diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | chickadee/audio/vorbis.scm | 304 | ||||
-rw-r--r-- | chickadee/config.scm.in | 2 | ||||
-rw-r--r-- | configure.ac | 10 |
4 files changed, 317 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 765a488..54839f1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,6 +56,7 @@ SOURCES = \ chickadee/math/easings.scm \ chickadee/math/path-finding.scm \ chickadee/audio/openal.scm \ + chickadee/audio/vorbis.scm \ chickadee/audio/wav.scm \ chickadee/render/color.scm \ chickadee/render/gl.scm \ diff --git a/chickadee/audio/vorbis.scm b/chickadee/audio/vorbis.scm new file mode 100644 index 0000000..e54e943 --- /dev/null +++ b/chickadee/audio/vorbis.scm @@ -0,0 +1,304 @@ +;;; Chickadee Game Toolkit +;;; Copyright © 2019 David Thompson <davet@gnu.org> +;;; +;;; Chickadee is free software: you can redistribute it and/or modify +;;; it under the terms of the GNU General Public License as published +;;; by the Free Software Foundation, either version 3 of the License, +;;; or (at your option) any later version. +;;; +;;; Chickadee is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;;; General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with this program. If not, see +;;; <http://www.gnu.org/licenses/>. + +;;; Commentary: +;; +;; libvorbis bindings. +;; +;;; Code: + +(define-module (chickadee audio vorbis) + #:use-module (chickadee config) + #:use-module (ice-9 format) + #:use-module (ice-9 match) + #:use-module (rnrs bytevectors) + #:use-module (srfi srfi-9) + #:use-module (srfi srfi-9 gnu) + #:use-module (system foreign) + #:export (vorbis-open + vorbis-clear + vorbis-info + vorbis-time-total + vorbis-time-seek + vorbis-read + vorbis-fill-buffer + + vorbis-info? + vorbis-info-version + vorbis-info-channels + vorbis-info-sample-rate)) + + +;;; +;;; Low-level Bindings +;;; + +(define vorbis-func + (let ((lib (dynamic-link %libvorbisfile))) + (lambda (return-type function-name arg-types) + (pointer->procedure return-type + (dynamic-func function-name lib) + arg-types)))) + +(define-syntax-rule (define-foreign name return-type func-name arg-types) + (define name + (vorbis-func return-type func-name arg-types))) + +(define ogg-sync-state + (list '* ; data + int ; storage + int ; fill + int ; returned + int ; unsynced + int ; headerbytes + int ; bodybytes + )) + +(define ogg-stream-state + (list '* ; body_data + long ; body_storage + long ; body_fill + long ; body_returned + '* ; lacing_vals + '* ; granule_vals + long ; lacing_storage + long ; lacing_fill + long ; lacing_packet + long ; lacing_returned + (make-list 282 uint8) ; header + int ; header_fill + int ; e_o_s + int ; b_o_s + long ; serialno + long ; pageno + int64 ; packetno + int64 ; granulepos + )) + +(define oggpack-buffer + (list long ; endbyte + int ; endbit + '* ; buffer + '* ; ptr + long ; storage + )) + +(define vorbis-dsp-state + (list int ; analysisp + '* ; vi + '* ; pcm + '* ; pcmret + int ; pcm_storage + int ; pcm_current + int ; pcm_returned + int ; preextrapolate + int ; eofflag + long ; lW + long ; W + long ; nW + long ; centerW + int64 ; granulepos + int64 ; sequence + int64 ; glue_bits + int64 ; time_bits + int64 ; floor_bits + int64 ; res_bits + '* ; backend_state + )) + +(define vorbis-block + (list '* ; pcm + oggpack-buffer ; opb + long ; lW + long ; W + long ; nW + int ; pcmend + int ; mode + int ; eofflag + int64 ; granulepos + int64 ; sequence + '* ; vd + '* ; localstore + long ; localtop + long ; localalloc + long ; totaluse + '* ; reap + long ; glue_bits + long ; time_bits + long ; floor_bits + long ; res_bits + '* ; internal + )) + +(define %vorbis-info + (list int ; version + int ; channels + long ; rate + long ; bitrate_upper + long ; bitrate_nominal + long ; bitrate_lower + long ; bitrate_window + '* ; codec_setup + )) + +(define ov-callbacks + (list '* ; read_func + '* ; seek_func + '* ; close_func + '* ; tell_func + )) + +(define ogg-vorbis-file + (list '* ; datasource + int ; seekable + int64 ; offset + int64 ; end + ogg-sync-state ; oy + int ; links + '* ; offsets + '* ; dataoffsets + '* ; serialnos + '* ; pcmlengths + '* ; vi + '* ; vc + int64 ; pcm_offset + int ; ready_state + long ; current_serialno + int ; current_link + double ; bittrack + double ; samptrack + ogg-stream-state ; os + vorbis-dsp-state ; vd + vorbis-block ; vb + ov-callbacks ; callbacks + )) + +(define OV_FALSE -1) +(define OV_EOF -2) +(define OV_HOLE -3) +(define OV_EREAD -128) +(define OV_EFAULT -129) +(define OV_EIMPL -130) +(define OV_EINVAL -131) +(define OV_ENOTVORBIS -132) +(define OV_EBADHEADER -133) +(define OV_EVERSION -134) +(define OV_ENOTAUDIO -135) +(define OV_EBADPACKET -136) +(define OV_EBADLINK -137) +(define OV_ENOSEEK -138) + +(define-foreign ov-fopen + int "ov_fopen" '(* *)) + +(define-foreign ov-open-callbacks + int "ov_open_callbacks" (list '* '* '* long '*)) + +(define-foreign ov-clear + int "ov_clear" '(*)) + +(define-foreign ov-info + '* "ov_info" (list '* int)) + +(define-foreign ov-read + long "ov_read" (list '* '* int int int int '*)) + +(define-foreign ov-comment + '* "ov_comment" (list '* int)) + +(define-foreign ov-raw-seek + int "ov_raw_seek" (list '* long)) + +(define-foreign ov-time-seek + int "ov_time_seek" (list '* double)) + +(define-foreign ov-seekable + long "ov_seekable" '(*)) + +(define-foreign ov-time-total + double "ov_time_total" (list '* int)) + + +;;; +;;; High-level public API +;;; + +(define-record-type <vorbis-file> + (wrap-vorbis-file bv ptr) + vorbis-file? + (bv vorbis-file-bv) + (ptr unwrap-vorbis-file)) + +(define (display-vorbis-file vf port) + (format port "#<vorbis-file ~x>" (pointer-address (unwrap-vorbis-file vf)))) + +(set-record-type-printer! <vorbis-file> display-vorbis-file) + +(define (vorbis-open file-name) + "Open the OGG Vorbis audio file located at FILE-NAME." + (let* ((bv (make-bytevector (sizeof ogg-vorbis-file))) + (ptr (bytevector->pointer bv))) + (ov-fopen (string->pointer file-name) ptr) + (wrap-vorbis-file bv ptr))) + +(define (vorbis-clear vf) + "Clear the buffers of the Vorbis file VF." + (ov-clear (unwrap-vorbis-file vf))) + +(define-record-type <vorbis-info> + (make-vorbis-info version channels sample-rate) + vorbis-info? + (version vorbis-info-version) + (channels vorbis-info-channels) + (sample-rate vorbis-info-sample-rate)) + +(define (vorbis-info vf) + (match (parse-c-struct (ov-info (unwrap-vorbis-file vf) -1) + %vorbis-info) + ((version channels sample-rate _ _ _ _ _) + (make-vorbis-info version channels sample-rate)))) + +(define (vorbis-time-total vf) + (ov-time-total (unwrap-vorbis-file vf) -1)) + +(define (vorbis-time-seek vf t) + (ov-time-seek (unwrap-vorbis-file vf) t)) + +(define* (vorbis-read vf buffer #:optional (start 0)) + (ov-read (unwrap-vorbis-file vf) + (bytevector->pointer buffer start) + (- (bytevector-length buffer) start) + 0 ; little endian + 2 ; 16 bits per sample + 1 ; signed data + %null-pointer)) + +(define (vorbis-fill-buffer vf buffer) + (let ((capacity (bytevector-length buffer))) + (let loop ((size 0)) + (if (< size capacity) + (let ((result (vorbis-read vf buffer size))) + (cond + ((zero? result) + size) + ((= result OV_HOLE) + (loop size)) + ((or (= result OV_EBADLINK) (= result OV_EINVAL)) + -1) + (else + (loop (+ size result))))) + size)))) diff --git a/chickadee/config.scm.in b/chickadee/config.scm.in index 083d7ba..ab88dbb 100644 --- a/chickadee/config.scm.in +++ b/chickadee/config.scm.in @@ -25,6 +25,7 @@ #:export (%datadir %chickadee-version %libopenal + %libvorbisfile scope-datadir)) (define %datadir @@ -33,6 +34,7 @@ (define %chickadee-version "@PACKAGE_VERSION@") (define %libopenal "@OPENAL_LIBDIR@/libopenal") +(define %libvorbisfile "@VORBIS_LIBDIR@/libvorbisfile") (define (scope-datadir file) "Append the Chickadee data directory to FILE." (string-append %datadir "/" file)) diff --git a/configure.ac b/configure.ac index 66be3c0..2bc4ad7 100644 --- a/configure.ac +++ b/configure.ac @@ -33,4 +33,14 @@ AS_IF([test "x$OPENAL_LIBDIR" = "x"], [ ]) AC_SUBST([OPENAL_LIBDIR]) +PKG_CHECK_MODULES([Vorbis], [vorbis]) +PKG_CHECK_VAR([VORBIS_LIBDIR], [vorbis], [libdir]) +AC_MSG_CHECKING([Vorbis library path]) +AS_IF([test "VORBIS_LIBDIR" = "x"], [ + AC_MSG_FAILURE([Unable to identify Vorbis lib path.]) +], [ + AC_MSG_RESULT([$VORBIS_LIBDIR]) +]) +AC_SUBST([VORBIS_LIBDIR]) + AC_OUTPUT |