blob: 9b39ad4550295c625a841a90439ebfb18d3eb69a (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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))
|