summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Thompson <dthompson2@worcester.edu>2019-01-09 20:25:45 -0500
committerDavid Thompson <dthompson2@worcester.edu>2020-04-07 16:10:23 -0400
commitd5637f0c8d51eca4d7fe14d1e2b9c5c8bc0121b3 (patch)
treeb216d35d19b88e809706b478015348af30a17026
parent8bba92a619b00277e31f14276d78e8cd531d426f (diff)
audio: Add WAV module.
* chickadee/audio/wav.scm: New file. * Makefile.am (SOURCES): Add it.
-rw-r--r--Makefile.am1
-rw-r--r--chickadee/audio/wav.scm104
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))