/
nwafile.go
337 lines (311 loc) · 9.51 KB
/
nwafile.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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
package nwa
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"unsafe"
)
type NwaFile struct {
Channels int // channels
Bps int // bits per sample
Freq int // samples per second
complevel int // compression level
userunlength int // run length encoding
blocks int // block count
datasize int // all data size
compdatasize int // compressed data size
samplecount int // all samples
blocksize int // samples per block
restsize int // samples of the last block
curblock int
offsets []int
reader io.Reader
tmpdata bytes.Buffer
outdata bytes.Buffer
}
// NewNwaFile returns an initialized and decoded NWA file that is read from the provided io.Reader r.
func NewNwaFile(r io.Reader) (*NwaFile, error) {
if r == nil {
return nil, errors.New("Reader is nil.")
}
nf := &NwaFile{reader: r}
if err := nf.readHeader(); err != nil {
return nil, err
}
if err := nf.checkHeader(); err != nil {
return nil, err
}
ret, err := nf.decodeBlock()
for ret != 0 {
if err != nil {
return nil, err
}
ret, err = nf.decodeBlock()
}
return nf, nil
}
// Read implements the io.Reader interface
func (nf *NwaFile) Read(p []byte) (int, error) {
return nf.outdata.Read(p)
}
func (nf *NwaFile) readHeader() error {
nf.curblock = -1
buffer := new(bytes.Buffer)
if count, err := io.CopyN(buffer, nf.reader, 0x2c); count != 0x2c || err != nil {
if err == nil {
err = fmt.Errorf("Can't read the header. Read 0x%X bytes\n", count)
}
return err
}
var channels, bps int16
var freq, complevel, userunlength, blocks, datasize, compdatasize, samplecount, blocksize, restsize, dummy int32
binary.Read(buffer, binary.LittleEndian, &channels)
binary.Read(buffer, binary.LittleEndian, &bps)
binary.Read(buffer, binary.LittleEndian, &freq)
binary.Read(buffer, binary.LittleEndian, &complevel)
binary.Read(buffer, binary.LittleEndian, &userunlength)
binary.Read(buffer, binary.LittleEndian, &blocks)
binary.Read(buffer, binary.LittleEndian, &datasize)
binary.Read(buffer, binary.LittleEndian, &compdatasize)
binary.Read(buffer, binary.LittleEndian, &samplecount)
binary.Read(buffer, binary.LittleEndian, &blocksize)
binary.Read(buffer, binary.LittleEndian, &restsize)
binary.Read(buffer, binary.LittleEndian, &dummy)
nf.Channels = int(channels)
nf.Bps = int(bps)
nf.Freq = int(freq)
nf.complevel = int(complevel)
nf.userunlength = int(userunlength)
nf.blocks = int(blocks)
nf.datasize = int(datasize)
nf.compdatasize = int(compdatasize)
nf.samplecount = int(samplecount)
nf.blocksize = int(blocksize)
nf.restsize = int(restsize)
// Uncompressed wave
if nf.complevel == -1 {
nf.blocksize = 65536
nf.restsize = (nf.datasize % (nf.blocksize * (nf.Bps / 8))) / (nf.Bps / 8)
var rest int = 0
if nf.restsize > 0 {
rest = 1
}
nf.blocks = nf.datasize/(nf.blocksize*(nf.Bps/8)) + rest
}
if nf.blocks <= 0 || nf.blocks > 1000000 {
// There can't be a file with over 1hr music
return fmt.Errorf("Blocks are too large: %d\n", nf.blocks)
}
if nf.complevel == -1 {
return nil
}
// Read the offset infex
nf.offsets = make([]int, nf.blocks)
for i := 0; i < nf.blocks; i++ {
var tmp int32
if err := binary.Read(nf.reader, binary.LittleEndian, &tmp); err != nil {
return errors.New("Couldn't read the offset block")
}
nf.offsets[i] = int(tmp)
}
return nil
}
func (nf *NwaFile) checkHeader() error {
if nf.complevel != -1 && nf.offsets == nil {
return errors.New("No offsets set even thought they are needed")
}
if nf.Channels != 1 && nf.Channels != 2 {
return fmt.Errorf("This library only supports mono / stereo data: data has %d channels\n", nf.Channels)
}
if nf.Bps != 8 && nf.Bps != 16 {
return fmt.Errorf("This library only supports 8 / 16bit data: data is %d bits\n", nf.Bps)
}
if nf.complevel == -1 {
var byps int = nf.Bps / 8 // Bytes per sample
if nf.datasize != nf.samplecount*byps {
return fmt.Errorf("Invalid datasize: datasize %d != samplecount %d * samplesize %d\n", nf.datasize, nf.samplecount, byps)
}
if nf.samplecount != (nf.blocks-1)*nf.blocksize+nf.restsize {
return fmt.Errorf("Total sample count is invalid: samplecount %d != %d*%d+%d(block*blocksize+lastblocksize)\n", nf.samplecount, nf.blocks-1, nf.blocksize, nf.restsize)
}
return nil
}
if nf.complevel < -1 || nf.complevel > 5 {
return fmt.Errorf("This library supports only compression level from -1 to 5: the compression level of the data is %d\n", nf.complevel)
}
if nf.offsets[nf.blocks-1] >= nf.compdatasize {
return fmt.Errorf("The last offset overruns the file.\n")
}
var byps int = nf.Bps / 8 // Bytes per sample
if nf.datasize != nf.samplecount*byps {
return fmt.Errorf("Invalid datasize: datasize %d != samplecount %d * samplesize %d\n", nf.datasize, nf.samplecount, byps)
}
if nf.samplecount != (nf.blocks-1)*nf.blocksize+nf.restsize {
return fmt.Errorf("Total sample count is invalid: samplecount %d != %d*%d+%d(block*blocksize+lastblocksize).\n", nf.samplecount, nf.blocks-1, nf.blocksize, nf.restsize)
}
return nil
}
// decodeBlock decodes one block with each call. Returns the length of the
// written bytes and an error if there was one.
func (nf *NwaFile) decodeBlock() (int64, error) {
// Uncompressed wave data stream
if nf.complevel == -1 {
if nf.curblock == -1 {
// If it's the first block we have to write the wave header
written, _ := io.Copy(&nf.outdata, makeWavHeader(nf.datasize, nf.Channels, nf.Bps, nf.Freq))
nf.curblock++
return written, nil
}
if nf.curblock <= nf.blocks {
nf.curblock++
ret, err := io.CopyN(&nf.outdata, nf.reader, (int64)(nf.blocksize*(nf.Bps/8)))
if err != nil && err != io.EOF {
return -1, err
}
return ret, nil
}
return -1, errors.New("This shouldn't happen! Please report me")
}
// Compressed (NWA) wave data stream
if nf.offsets == nil {
return -1, errors.New("Offsets weren't set. Aborting")
}
if nf.blocks == nf.curblock {
// We are finished
return 0, nil
}
if nf.curblock == -1 {
// If it's the first block we have to write the wave header
written, _ := io.Copy(&nf.outdata, makeWavHeader(nf.datasize, nf.Channels, nf.Bps, nf.Freq))
nf.curblock++
return written, nil
}
// Calculate the size of the decoded block
var curblocksize, curcompsize int
if nf.curblock != nf.blocks-1 {
curblocksize = nf.blocksize * (nf.Bps / 8)
curcompsize = nf.offsets[nf.curblock+1] - nf.offsets[nf.curblock]
if curblocksize >= nf.blocksize*(nf.Bps/8)*2 {
return -1, errors.New("Current block exceeds the excepted count.")
} // Fatal error
} else {
curblocksize = nf.restsize * (nf.Bps / 8)
curcompsize = nf.blocksize * (nf.Bps / 8) * 2
}
// Read in the block data
nf.tmpdata.Reset()
io.CopyN(&nf.tmpdata, nf.reader, (int64)(curcompsize))
// Decode the compressed block
nf.decode(curblocksize)
nf.curblock++
return (int64)(curblocksize), nil
}
func (nf *NwaFile) decode(outsize int) {
var d [2]int
var flipflag, runlength int
// Read the first data (with full accuracy)
if nf.Bps == 8 {
var tmp uint8
binary.Read(&nf.tmpdata, binary.LittleEndian, &tmp)
d[0] = int(tmp)
} else { // bps == 16bit
var tmp uint16
binary.Read(&nf.tmpdata, binary.LittleEndian, &tmp)
d[0] = int(tmp)
}
// Stereo
if nf.Channels == 2 {
if nf.Bps == 8 {
var tmp uint8
binary.Read(&nf.tmpdata, binary.LittleEndian, &tmp)
d[1] = int(tmp)
} else { // bps == 16bit
var tmp uint16
binary.Read(&nf.tmpdata, binary.LittleEndian, &tmp)
d[1] = int(tmp)
}
}
ptr := uintptr(unsafe.Pointer(&nf.tmpdata.Bytes()[0]))
var shiftBits uint
dsize := outsize / (nf.Bps / 8)
for i := 0; i < dsize; i++ {
// If we are not in a copy loop (RLE), read in the data
if runlength == 0 {
exponent := getBits(&ptr, &shiftBits, 3)
// Branching according to the mantissa: 0, 1-6, 7
switch {
case exponent == 7:
{
// 7: big exponent
// In case we are using RLE (complevel==5) this is disabled
if getBits(&ptr, &shiftBits, 1) == 1 {
d[flipflag] = 0
} else {
var bits, shift uint
if nf.complevel >= 3 {
bits = 8
shift = 9
} else {
bits = 8 - uint(nf.complevel)
shift = 2 + 7 + uint(nf.complevel)
}
mask1 := uint(1 << (bits - 1))
mask2 := uint((1 << (bits - 1)) - 1)
b := getBits(&ptr, &shiftBits, bits)
if b&mask1 != 0 {
d[flipflag] -= int((b & mask2) << shift)
} else {
d[flipflag] += int((b & mask2) << shift)
}
}
}
case exponent != 0:
{
// 1-6 : normal differencial
var bits, shift uint
if nf.complevel >= 3 {
bits = uint(nf.complevel) + 3
shift = 1 + exponent
} else {
bits = 5 - uint(nf.complevel)
shift = 2 + exponent + uint(nf.complevel)
}
mask1 := uint(1 << (bits - 1))
mask2 := uint((1 << (bits - 1)) - 1)
b := getBits(&ptr, &shiftBits, bits)
if b&mask1 != 0 {
d[flipflag] -= int((b & mask2) << shift)
} else {
d[flipflag] += int((b & mask2) << shift)
}
}
case exponent == 0:
{
// Skips when not using RLE
if nf.userunlength == 1 {
runlength = int(getBits(&ptr, &shiftBits, 1))
if runlength == 1 {
runlength = int(getBits(&ptr, &shiftBits, 2))
if runlength == 3 {
runlength = int(getBits(&ptr, &shiftBits, 8))
}
}
}
}
}
} else {
runlength--
}
if nf.Bps == 8 {
binary.Write(&nf.outdata, binary.LittleEndian, uint8(d[flipflag]))
} else {
binary.Write(&nf.outdata, binary.LittleEndian, int16(d[flipflag]))
}
if nf.Channels == 2 {
// Changing the channel
flipflag = flipflag ^ 1
}
}
}