diff options
author | David Thompson <dthompson2@worcester.edu> | 2019-01-09 20:25:45 -0500 |
---|---|---|
committer | David Thompson <dthompson2@worcester.edu> | 2020-04-07 16:10:23 -0400 |
commit | d5637f0c8d51eca4d7fe14d1e2b9c5c8bc0121b3 (patch) | |
tree | b216d35d19b88e809706b478015348af30a17026 | |
parent | 8bba92a619b00277e31f14276d78e8cd531d426f (diff) |
audio: Add WAV module.
* chickadee/audio/wav.scm: New file.
* Makefile.am (SOURCES): Add it.
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | chickadee/audio/wav.scm | 104 |
2 files changed, 105 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index f9776f8..765a488 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/wav.scm \ chickadee/render/color.scm \ chickadee/render/gl.scm \ chickadee/render/gpu.scm \ diff --git a/chickadee/audio/wav.scm b/chickadee/audio/wav.scm new file mode 100644 index 0000000..9b39ad4 --- /dev/null +++ b/chickadee/audio/wav.scm @@ -0,0 +1,104 @@ +;;; 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: +;; +;; WAV decoder. +;; +;;; Code: + +(define-module (chickadee audio wav) + #:use-module (ice-9 binary-ports) + #:use-module (ice-9 format) + #:use-module (rnrs bytevectors) + #:use-module (srfi srfi-9) + #:use-module (srfi srfi-9 gnu) + #:export (open-wav + close-wav + wav? + wav-num-channels + wav-sample-rate + wav-byte-rate + wav-bits-per-sample + wav-length + wav-read + wav-time-seek)) + +(define-record-type <wav> + (make-wav num-channels sample-rate byte-rate + block-align bits-per-sample length port) + wav? + (num-channels wav-num-channels) + (sample-rate wav-sample-rate) + (byte-rate wav-byte-rate) + (block-align wav-block-align) + (bits-per-sample wav-bits-per-sample) + (length wav-length) + (port wav-port)) + +(define (display-wav wav port) + (format port "#<wav num-channels: ~d sample-rate: ~d byte-rate: ~d block-align: ~d bits-per-sample: ~d length: ~d>" + (wav-num-channels wav) + (wav-sample-rate wav) + (wav-byte-rate wav) + (wav-block-align wav) + (wav-bits-per-sample wav) + (wav-length wav))) + +(set-record-type-printer! <wav> display-wav) + +(define (open-wav file-name) + (let* ((port (open-file file-name "r")) + (bv (get-bytevector-n port 44)) + (data-length (bytevector-uint-ref bv 40 'little 4))) + ;; Validate the magic values that must be present in the header. + (unless (and (= (bytevector-uint-ref bv 0 'big 4) #x52494646) ; RIFF + (= (bytevector-uint-ref bv 8 'big 4) #x57415645) ; WAVE + (= (bytevector-uint-ref bv 12 'big 4) #x666d7420) ; fmt + (= (bytevector-uint-ref bv 36 'big 4) #x64617461)) ; data + (error "invalid WAV header in file:" file-name)) + ;; Only PCM data is supported. + (unless (= (bytevector-uint-ref bv 20 'little 2) 1) + (error "WAV data not in PCM format:" file-name)) + (make-wav (bytevector-uint-ref bv 22 'little 2) ; num channels + (bytevector-uint-ref bv 24 'little 4) ; sample rate + (bytevector-uint-ref bv 28 'little 4) ; byte rate + (bytevector-uint-ref bv 32 'little 2) ; block align + (bytevector-uint-ref bv 34 'little 2) ; bits per sample + data-length + port))) + +(define (close-wav wav) + "Close the open file port for WAV." + (close-port (wav-port wav))) + +(define (wav-read wav bv) + "Fill BV with audio samples from WAV and return the number of bytes +read." + (let ((bytes-read (get-bytevector-n! (wav-port wav) bv 0 (bytevector-length bv)))) + (if (eof-object? bytes-read) 0 bytes-read))) + +(define (wav-time-seek wav time) + "Change the play head to TIME in WAV." + (seek (wav-port wav) + (+ 44 (inexact->exact + (ceiling + (* time + (wav-num-channels wav) + (wav-byte-rate wav) + (wav-sample-rate wav))))) + SEEK_SET)) |