-
Notifications
You must be signed in to change notification settings - Fork 13
/
wave.go
206 lines (182 loc) · 3.78 KB
/
wave.go
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright 2012 Lawrence Kesteloot
package main
// Parse .WAV files for cassette support.
import (
"fmt"
"io"
"log"
"os"
)
// Holds information about the WAV file.
type wavFile struct {
io.ReadSeeker
channelCount uint16
samplesPerSecond uint32
bytesPerSample uint16
bitsPerSample uint16
isEof bool
}
// Parses .WAV file headers.
func openWav(filename string) (w *wavFile, err error) {
// Open the file.
f, err := os.Open(filename)
if err != nil {
return
}
w = &wavFile{f, 0, 0, 0, 0, false}
// Parse header.
err = w.parseChunkId("RIFF")
if err != nil {
return
}
// Length of the rest of the file.
_, err = w.parseInt()
if err != nil {
return
}
err = w.parseChunkId("WAVE")
if err != nil {
return
}
err = w.parseChunkId("fmt ")
if err != nil {
return
}
// Format chunk size.
chunkSize, err := w.parseInt()
if err != nil {
return
}
// Format.
format, err := w.parseShort()
if err != nil {
return
}
if format != 1 {
panic(fmt.Sprintf("We only handle PCM format (1), not %d", format))
}
// Number of channels.
w.channelCount, err = w.parseShort()
if err != nil {
return
}
// Sample rate.
w.samplesPerSecond, err = w.parseInt()
if err != nil {
return
}
// Ignore this int.
_, err = w.parseInt()
if err != nil {
return
}
// Bytes per sample.
w.bytesPerSample, err = w.parseShort()
if err != nil {
return
}
// Bits per sample.
w.bitsPerSample, err = w.parseShort()
if err != nil {
return
}
// Read rest of format chunk.
chunkSize -= 16 // Amount read already.
if chunkSize > 0 {
if wavDebug {
log.Printf("Skipping %d bytes in fmt chunk", chunkSize)
}
b := make([]byte, chunkSize)
_, err = io.ReadFull(w, b)
if err != nil {
return
}
}
// Start data chunk.
err = w.parseChunkId("data")
if err != nil {
return
}
// Size of data chunk.
_, err = w.parseInt()
if err != nil {
return
}
return
}
// Parse a 4-byte ASCII chunk ID and verify that it matches the given ID.
func (w *wavFile) parseChunkId(expectedChunkId string) error {
// Read four bytes.
b := make([]byte, 4)
_, err := io.ReadFull(w, b)
if err != nil {
return err
}
// Compare to expected chunk ID.
foundChunkId := string(b)
if foundChunkId != expectedChunkId {
return fmt.Errorf("Expected chunk ID \"%s\" but got \"%s\"", expectedChunkId, foundChunkId)
}
if wavDebug {
log.Printf("Found expected chunk ID \"%s\"", expectedChunkId)
}
return nil
}
// Loads a 4-byte little-endian int.
func (w *wavFile) parseInt() (uint32, error) {
// Read four bytes.
b := make([]byte, 4)
_, err := io.ReadFull(w, b)
if err != nil {
return 0, err
}
// Little-endian.
n := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
if wavDebug {
log.Printf("Found 4-byte integer %d (0x%08X)", n, n)
}
return n, nil
}
// Loads a 2-byte little-endian int.
func (w *wavFile) parseShort() (uint16, error) {
// Read two bytes.
b := make([]byte, 2)
_, err := io.ReadFull(w, b)
if err != nil {
return 0, err
}
// Little-endian.
n := uint16(b[0]) | uint16(b[1])<<8
if wavDebug {
log.Printf("Found 2-byte integer %d (0x%04X)", n, n)
}
return n, nil
}
// Loads a sample.
func (w *wavFile) readSample() (int16, error) {
// Only handle simple case.
if w.channelCount != 1 {
panic("We only handle mono WAV files")
}
if w.bytesPerSample != 2 {
panic("We only handle WAV files with two bytes per sample")
}
if w.bitsPerSample != 16 {
panic("We only handle 16-bit WAV files")
}
if w.isEof {
// Pretend that the tape stopped and that we're just
// reading silence. That's probably what the original
// computer did.
return 0, nil
}
s, err := w.parseShort()
if err == io.EOF {
log.Print("End of cassette")
w.isEof = true
return 0, nil
} else if err != nil {
return 0, err
}
return int16(s), nil
}