1
|
|
|
/* |
2
|
|
|
* WaveFileReaderWriter |
3
|
|
|
* Class to read and write wav files to and from buffers. |
4
|
|
|
* Copyright (c) 2017-2018 Rafael da Silva Rocha. |
5
|
|
|
* https://github.com/rochars/wavefile |
6
|
|
|
* |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
/** @private */ |
10
|
|
|
const WAVE_ERRORS = require("../src/wave-errors"); |
11
|
|
|
/** @private */ |
12
|
|
|
const WaveFileHeader = require("../src/wavefile-header"); |
13
|
|
|
/** @private */ |
14
|
|
|
const byteData_ = require("byte-data"); |
15
|
|
|
/** @private */ |
16
|
|
|
let uInt8_ = byteData_.uInt8; |
17
|
|
|
/** @private */ |
18
|
|
|
let uInt16_ = byteData_.uInt16; |
19
|
|
|
/** @private */ |
20
|
|
|
let uInt32_ = byteData_.uInt32; |
21
|
|
|
/** @private */ |
22
|
|
|
const chr_ = byteData_.chr; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Class to read and write wav files to and from buffers. |
26
|
|
|
* @extends WaveFileHeader |
27
|
|
|
*/ |
28
|
|
|
class WaveFileReaderWriter extends WaveFileHeader { |
29
|
|
|
|
30
|
|
|
constructor() { |
31
|
|
|
super(); |
32
|
|
|
/** |
33
|
|
|
* @type {Array<number>} |
34
|
|
|
*/ |
35
|
|
|
this.samples = []; |
36
|
|
|
/** |
37
|
|
|
* Header formats. |
38
|
|
|
* @enum {number} |
39
|
|
|
* @private |
40
|
|
|
*/ |
41
|
|
|
this.headerFormats_ = { |
42
|
|
|
"4": 17, |
43
|
|
|
"8": 1, |
44
|
|
|
"8a": 6, |
45
|
|
|
"8m": 7, |
46
|
|
|
"16": 1, |
47
|
|
|
"24": 1, |
48
|
|
|
"32": 1, |
49
|
|
|
"32f": 3, |
50
|
|
|
"64": 3 |
51
|
|
|
}; |
52
|
|
|
/** |
53
|
|
|
* @type {number} |
54
|
|
|
* @private |
55
|
|
|
*/ |
56
|
|
|
this.head_ = 0; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Read the RIFF chunk a wave file. |
61
|
|
|
* @param {Uint8Array} bytes A wav buffer. |
62
|
|
|
* @throws {Error} If no "RIFF" chunk is found. |
63
|
|
|
* @private |
64
|
|
|
*/ |
65
|
|
|
readRIFFChunk_(bytes) { |
66
|
|
|
this.chunkId = byteData_.unpackArray(bytes.slice(0, 4), chr_); |
67
|
|
|
if (this.chunkId != "RIFF" && this.chunkId != "RIFX") { |
68
|
|
|
throw Error(WAVE_ERRORS.format); |
69
|
|
|
} |
70
|
|
|
this.LEorBE_(); |
71
|
|
|
this.chunkSize = byteData_.unpack(bytes.slice(4, 8), uInt32_); |
72
|
|
|
this.format = byteData_.unpackArray(bytes.slice(8, 12), chr_); |
73
|
|
|
if (this.format != "WAVE") { |
74
|
|
|
throw Error(WAVE_ERRORS.wave); |
75
|
|
|
} |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Set up to work wih big-endian or little-endian files. |
80
|
|
|
* The types used are changed from LE or BE. If the |
81
|
|
|
* the file is big-endian (RIFX), true is returned. |
82
|
|
|
* @private |
83
|
|
|
*/ |
84
|
|
|
LEorBE_() { |
85
|
|
|
let bigEndian = this.chunkId == "RIFX"; |
86
|
|
|
uInt8_.be = bigEndian; |
87
|
|
|
uInt16_.be = bigEndian; |
88
|
|
|
uInt32_.be = bigEndian; |
89
|
|
|
return bigEndian; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Read the "fmt " chunk of a wave file. |
94
|
|
|
* @param {Object} chunks The wav file chunks. |
95
|
|
|
* @throws {Error} If no "fmt " chunk is found. |
96
|
|
|
* @private |
97
|
|
|
*/ |
98
|
|
|
readFmtChunk_(chunks) { |
99
|
|
|
let chunk = this.findChunk_(chunks, "fmt "); |
100
|
|
|
if (chunk) { |
101
|
|
|
this.fmtChunkId = "fmt "; |
102
|
|
|
this.fmtChunkSize = chunk.chunkSize; |
103
|
|
|
this.audioFormat = byteData_.unpack( |
104
|
|
|
chunk.chunkData.slice(0, 2), uInt16_); |
105
|
|
|
this.numChannels = byteData_.unpack( |
106
|
|
|
chunk.chunkData.slice(2, 4), uInt16_); |
107
|
|
|
this.sampleRate = byteData_.unpack( |
108
|
|
|
chunk.chunkData.slice(4, 8), uInt32_); |
109
|
|
|
this.byteRate = byteData_.unpack( |
110
|
|
|
chunk.chunkData.slice(8, 12), uInt32_); |
111
|
|
|
this.blockAlign = byteData_.unpack( |
112
|
|
|
chunk.chunkData.slice(12, 14), uInt16_); |
113
|
|
|
this.bitsPerSample = byteData_.unpack( |
114
|
|
|
chunk.chunkData.slice(14, 16), uInt16_); |
115
|
|
|
this.readFmtExtension_(chunk); |
116
|
|
|
} else { |
117
|
|
|
throw Error(WAVE_ERRORS["fmt "]); |
118
|
|
|
} |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Read the "fmt " chunk extension. |
123
|
|
|
* @param {Object} chunk The "fmt " chunk. |
124
|
|
|
* @private |
125
|
|
|
*/ |
126
|
|
|
readFmtExtension_(chunk) { |
127
|
|
|
if (this.fmtChunkSize > 16) { |
128
|
|
|
this.cbSize = byteData_.unpack( |
129
|
|
|
chunk.chunkData.slice(16, 18), uInt16_); |
130
|
|
|
if (this.fmtChunkSize > 18) { |
131
|
|
|
this.validBitsPerSample = byteData_.unpack( |
132
|
|
|
chunk.chunkData.slice(18, 20), uInt16_); |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Read the "fact" chunk of a wav file. |
139
|
|
|
* @param {Object} chunks The wav file chunks. |
140
|
|
|
* @throws {Error} If no "fact" chunk is found. |
141
|
|
|
* @private |
142
|
|
|
*/ |
143
|
|
|
readFactChunk_(chunks) { |
144
|
|
|
let chunk = this.findChunk_(chunks, "fact"); |
145
|
|
|
if (chunk) { |
146
|
|
|
this.factChunkId = "fact"; |
147
|
|
|
this.factChunkSize = chunk.chunkSize; |
148
|
|
|
this.dwSampleLength = byteData_.unpack( |
149
|
|
|
chunk.chunkData.slice(0, 4), uInt32_); |
150
|
|
|
} else if (this.enforceFact) { |
151
|
|
|
throw Error(WAVE_ERRORS["fact"]); |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Read the "bext" chunk of a wav file. |
157
|
|
|
* @param {Object} chunks The wav file chunks. |
158
|
|
|
* @throws {Error} If no "bext" chunk is found. |
159
|
|
|
* @private |
160
|
|
|
*/ |
161
|
|
|
readBextChunk_(chunks) { |
162
|
|
|
let chunk = this.findChunk_(chunks, "bext"); |
163
|
|
|
if (chunk) { |
164
|
|
|
this.bextChunkId = "bext"; |
165
|
|
|
this.bextChunkSize = chunk.chunkSize; |
166
|
|
|
this.bextChunkData = chunk.chunkData; |
167
|
|
|
this.readBextChunkFields_(); |
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Read the fields of the "bext" chunk. |
173
|
|
|
* @private |
174
|
|
|
*/ |
175
|
|
|
readBextChunkFields_() { |
176
|
|
|
this.head_ = 0; |
177
|
|
|
this.bextChunkFields = { |
178
|
|
|
"description": this.readVariableSizeString_( |
179
|
|
|
this.bextChunkData, 256), |
180
|
|
|
"originator": this.readVariableSizeString_( |
181
|
|
|
this.bextChunkData, 32), |
182
|
|
|
"originatorReference": this.readVariableSizeString_( |
183
|
|
|
this.bextChunkData, 32), |
184
|
|
|
"originationDate": this.readVariableSizeString_( |
185
|
|
|
this.bextChunkData, 10), |
186
|
|
|
"originationTime": this.readVariableSizeString_( |
187
|
|
|
this.bextChunkData, 8), |
188
|
|
|
// timeReference is a 64-bit value |
189
|
|
|
"timeReference": this.readBytes_( |
190
|
|
|
this.bextChunkData, 8), |
191
|
|
|
"version": this.readFromChunk_( |
192
|
|
|
this.bextChunkData, uInt16_), |
193
|
|
|
"UMID": this.readVariableSizeString_( |
194
|
|
|
this.bextChunkData, 64), |
195
|
|
|
"loudnessValue": this.readFromChunk_( |
196
|
|
|
this.bextChunkData, uInt16_), |
197
|
|
|
"loudnessRange": this.readFromChunk_( |
198
|
|
|
this.bextChunkData, uInt16_), |
199
|
|
|
"maxTruePeakLevel": this.readFromChunk_( |
200
|
|
|
this.bextChunkData, uInt16_), |
201
|
|
|
"maxMomentaryLoudness": this.readFromChunk_( |
202
|
|
|
this.bextChunkData, uInt16_), |
203
|
|
|
"maxShortTermLoudness": this.readFromChunk_( |
204
|
|
|
this.bextChunkData, uInt16_), |
205
|
|
|
"reserved": this.readVariableSizeString_( |
206
|
|
|
this.bextChunkData, 180), |
207
|
|
|
"codingHistory": this.readVariableSizeString_( |
208
|
|
|
this.bextChunkData, this.bextChunkData.length - 602), |
209
|
|
|
} |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Return a slice of the byte array while moving the reading head. |
214
|
|
|
* @param {Array<number>|Uint8Array} bytes The bytes. |
215
|
|
|
* @param {number} size the number of bytes to read. |
216
|
|
|
* @private |
217
|
|
|
*/ |
218
|
|
|
readBytes_(bytes, size) { |
219
|
|
|
let v = this.head_; |
220
|
|
|
this.head_ += size; |
221
|
|
|
return bytes.slice(v, this.head_); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Read bytes as a string from a RIFF chunk. |
226
|
|
|
* @param {Array<number>|Uint8Array} bytes The bytes. |
227
|
|
|
* @param {number} maxSize the max size of the string. |
228
|
|
|
* @private |
229
|
|
|
*/ |
230
|
|
|
readVariableSizeString_(bytes, maxSize) { |
231
|
|
|
let str = ""; |
232
|
|
|
for (let i=0; i<maxSize; i++) { |
233
|
|
|
str += byteData_.unpack([bytes[this.head_]], chr_); |
234
|
|
|
this.head_++; |
235
|
|
|
} |
236
|
|
|
return str; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Read a number from a chunk. |
241
|
|
|
* @param {Array<number>|Uint8Array} bytes The chunk bytes. |
242
|
|
|
* @param {Object} bdType The byte-data corresponding type. |
243
|
|
|
* @private |
244
|
|
|
*/ |
245
|
|
|
readFromChunk_(bytes, bdType) { |
246
|
|
|
let size = bdType.bits / 8; |
247
|
|
|
let value = byteData_.unpack( |
248
|
|
|
bytes.slice(this.head_, this.head_ + size), bdType); |
249
|
|
|
this.head_ += size; |
250
|
|
|
return value; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Write a variable size string as bytes. |
255
|
|
|
* If the string is smaller than the max size it |
256
|
|
|
* is filled with 0s. |
257
|
|
|
* @param {string} str The string to be written as bytes. |
258
|
|
|
* @param {number} maxSize the max size of the string. |
259
|
|
|
* @private |
260
|
|
|
*/ |
261
|
|
|
writeVariableSizeString_(str, maxSize) { |
262
|
|
|
let bytes = byteData_.packArray(str, chr_); |
263
|
|
|
for (let i=bytes.length; i<maxSize; i++) { |
264
|
|
|
bytes.push(0); |
265
|
|
|
} |
266
|
|
|
return bytes; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Read the "cue " chunk of a wave file. |
271
|
|
|
* @param {Object} chunks The RIFF file chunks. |
272
|
|
|
* @throws {Error} If no "cue" chunk is found. |
273
|
|
|
* @private |
274
|
|
|
*/ |
275
|
|
|
readCueChunk_(chunks) { |
276
|
|
|
let chunk = this.findChunk_(chunks, "cue "); |
277
|
|
|
if (chunk) { |
278
|
|
|
this.cueChunkId = "cue "; |
279
|
|
|
this.cueChunkSize = chunk.chunkSize; |
280
|
|
|
this.cueChunkData = chunk.chunkData; |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* Read the "data" chunk of a wave file. |
286
|
|
|
* @param {Object} chunks The RIFF file chunks. |
287
|
|
|
* @param {Object} options Type options. |
288
|
|
|
* @throws {Error} If no "data" chunk is found. |
289
|
|
|
* @private |
290
|
|
|
*/ |
291
|
|
|
readDataChunk_(chunks, options) { |
292
|
|
|
let chunk = this.findChunk_(chunks, "data"); |
293
|
|
|
if (chunk) { |
294
|
|
|
this.dataChunkId = "data"; |
295
|
|
|
this.dataChunkSize = chunk.chunkSize; |
296
|
|
|
this.samplesFromBytes_(chunk.chunkData, options); |
297
|
|
|
} else { |
298
|
|
|
throw Error(WAVE_ERRORS["data"]); |
299
|
|
|
} |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Find and return the start offset of the data chunk on a wave file. |
304
|
|
|
* @param {Array<number>|Uint8Array} bytes A wav file buffer. |
305
|
|
|
* @param {Object} options Type options. |
306
|
|
|
* @private |
307
|
|
|
*/ |
308
|
|
|
samplesFromBytes_(bytes, options) { |
309
|
|
|
options.bits = this.bitsPerSample == 4 ? 8 : this.bitsPerSample; |
310
|
|
|
options.signed = options.bits == 8 ? false : true; |
311
|
|
|
options.float = (this.audioFormat == 3 || |
312
|
|
|
this.bitsPerSample == 64) ? true : false; |
313
|
|
|
options.single = false; |
314
|
|
|
this.samples = byteData_.unpackArray( |
315
|
|
|
bytes, new byteData_.Type(options)); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Find a chunk by its FourCC in a array of RIFF chunks. |
320
|
|
|
* @param {Object} chunks The wav file chunks. |
321
|
|
|
* @param {string} fourCC The chunk fourCC. |
322
|
|
|
* @return {Object|null} |
323
|
|
|
* @private |
324
|
|
|
*/ |
325
|
|
|
findChunk_(chunks, fourCC) { |
326
|
|
|
for (let i = 0; i<chunks.length; i++) { |
327
|
|
|
if (chunks[i].chunkId == fourCC) { |
328
|
|
|
return chunks[i]; |
329
|
|
|
} |
330
|
|
|
} |
331
|
|
|
return null; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* Turn samples to bytes. |
336
|
|
|
* @param {Object} options Type options. |
337
|
|
|
* @private |
338
|
|
|
*/ |
339
|
|
|
samplesToBytes_(options) { |
340
|
|
|
options.bits = this.bitsPerSample == 4 ? 8 : this.bitsPerSample; |
341
|
|
|
options.signed = options.bits == 8 ? false : true; |
342
|
|
|
options.float = (this.audioFormat == 3 || |
343
|
|
|
this.bitsPerSample == 64) ? true : false; |
344
|
|
|
let bytes = byteData_.packArray( |
345
|
|
|
this.samples, new byteData_.Type(options)); |
346
|
|
|
if (bytes.length % 2) { |
347
|
|
|
bytes.push(0); |
348
|
|
|
} |
349
|
|
|
return bytes; |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* Get the bytes of the "bext" chunk. |
354
|
|
|
* @return {Array<number>} The "bext" chunk bytes. |
355
|
|
|
* @private |
356
|
|
|
*/ |
357
|
|
|
getBextBytes_() { |
358
|
|
|
if (this.bextChunkId) { |
359
|
|
|
let bextBytes = [].concat(this.writeVariableSizeString_( |
360
|
|
|
this.bextChunkFields["description"], 256)); |
361
|
|
|
bextBytes = bextBytes.concat(this.writeVariableSizeString_( |
362
|
|
|
this.bextChunkFields["originator"], 32)); |
363
|
|
|
bextBytes = bextBytes.concat(this.writeVariableSizeString_( |
364
|
|
|
this.bextChunkFields["originatorReference"], 32)); |
365
|
|
|
bextBytes = bextBytes.concat(this.writeVariableSizeString_( |
366
|
|
|
this.bextChunkFields["originationDate"], 10)); |
367
|
|
|
bextBytes = bextBytes.concat(this.writeVariableSizeString_( |
368
|
|
|
this.bextChunkFields["originationTime"], 8)); |
369
|
|
|
// 64-bit value rw as bytes |
370
|
|
|
bextBytes = bextBytes.concat( |
371
|
|
|
this.bextChunkFields["timeReference"]); |
372
|
|
|
bextBytes = bextBytes.concat(byteData_.pack( |
373
|
|
|
this.bextChunkFields["version"], uInt16_)); |
374
|
|
|
bextBytes = bextBytes.concat(this.writeVariableSizeString_( |
375
|
|
|
this.bextChunkFields["UMID"], 64)); |
376
|
|
|
bextBytes = bextBytes.concat(byteData_.pack( |
377
|
|
|
this.bextChunkFields["loudnessValue"], uInt16_)); |
378
|
|
|
bextBytes = bextBytes.concat(byteData_.pack( |
379
|
|
|
this.bextChunkFields["loudnessRange"], uInt16_)); |
380
|
|
|
bextBytes = bextBytes.concat(byteData_.pack( |
381
|
|
|
this.bextChunkFields["maxTruePeakLevel"], uInt16_)); |
382
|
|
|
bextBytes = bextBytes.concat(byteData_.pack( |
383
|
|
|
this.bextChunkFields["maxMomentaryLoudness"], uInt16_)); |
384
|
|
|
bextBytes = bextBytes.concat(byteData_.pack( |
385
|
|
|
this.bextChunkFields["maxShortTermLoudness"], uInt16_)); |
386
|
|
|
bextBytes = bextBytes.concat(this.writeVariableSizeString_( |
387
|
|
|
this.bextChunkFields["reserved"], 180)); |
388
|
|
|
bextBytes = bextBytes.concat(this.writeVariableSizeString_( |
389
|
|
|
this.bextChunkFields["codingHistory"], |
390
|
|
|
this.bextChunkData.length - 602)); |
391
|
|
|
return [].concat( |
392
|
|
|
byteData_.packArray(this.bextChunkId, chr_), |
393
|
|
|
byteData_.pack(bextBytes.length, uInt32_), |
394
|
|
|
bextBytes |
395
|
|
|
); |
396
|
|
|
} |
397
|
|
|
return []; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
/** |
401
|
|
|
* Get the bytes of the "cue " chunk. |
402
|
|
|
* @return {Array<number>} The "cue " chunk bytes. |
403
|
|
|
* @private |
404
|
|
|
*/ |
405
|
|
|
getCueBytes_() { |
406
|
|
|
if (this.cueChunkId) { |
407
|
|
|
return [].concat( |
408
|
|
|
byteData_.packArray(this.cueChunkId, chr_), |
409
|
|
|
byteData_.pack(this.cueChunkSize, uInt32_), |
410
|
|
|
this.cueChunkData |
411
|
|
|
); |
412
|
|
|
} |
413
|
|
|
return []; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* Get the bytes of the "fact" chunk. |
418
|
|
|
* @return {Array<number>} The "fact" chunk bytes. |
419
|
|
|
* @private |
420
|
|
|
*/ |
421
|
|
|
getFactBytes_() { |
422
|
|
|
if (this.factChunkId) { |
423
|
|
|
return [].concat( |
424
|
|
|
byteData_.packArray(this.factChunkId, chr_), |
425
|
|
|
byteData_.pack(this.factChunkSize, uInt32_), |
426
|
|
|
byteData_.pack(this.dwSampleLength, uInt32_) |
427
|
|
|
); |
428
|
|
|
} |
429
|
|
|
return []; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
/** |
433
|
|
|
* Get the bytes of the cbSize field. |
434
|
|
|
* @return {Array<number>} The cbSize bytes. |
435
|
|
|
* @private |
436
|
|
|
*/ |
437
|
|
|
getCbSizeBytes_() { |
438
|
|
|
if (this.fmtChunkSize > 16) { |
439
|
|
|
return byteData_.pack(this.cbSize, uInt16_); |
440
|
|
|
} |
441
|
|
|
return []; |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
/** |
445
|
|
|
* Get the bytes of the validBitsPerSample field. |
446
|
|
|
* @return {Array<number>} The validBitsPerSample bytes. |
447
|
|
|
* @private |
448
|
|
|
*/ |
449
|
|
|
getValidBitsPerSampleBytes_() { |
450
|
|
|
if (this.fmtChunkSize > 18) { |
451
|
|
|
return byteData_.pack(this.validBitsPerSample, uInt16_); |
452
|
|
|
} |
453
|
|
|
return []; |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
/** |
457
|
|
|
* Turn a WaveFile object into a file. |
458
|
|
|
* @return {Array<number>} The wav file bytes. |
459
|
|
|
* @private |
460
|
|
|
*/ |
461
|
|
|
createWaveFile_() { |
462
|
|
|
let options = {"be": this.LEorBE_()}; |
463
|
|
|
return byteData_.packArray(this.chunkId, chr_).concat( |
464
|
|
|
byteData_.pack(this.chunkSize, uInt32_), |
465
|
|
|
byteData_.packArray(this.format, chr_), |
466
|
|
|
this.getBextBytes_(), |
467
|
|
|
byteData_.packArray(this.fmtChunkId, chr_), |
468
|
|
|
byteData_.pack(this.fmtChunkSize, uInt32_), |
469
|
|
|
byteData_.pack(this.audioFormat, uInt16_), |
470
|
|
|
byteData_.pack(this.numChannels, uInt16_), |
471
|
|
|
byteData_.pack(this.sampleRate, uInt32_), |
472
|
|
|
byteData_.pack(this.byteRate, uInt32_), |
473
|
|
|
byteData_.pack(this.blockAlign, uInt16_), |
474
|
|
|
byteData_.pack(this.bitsPerSample, uInt16_), |
475
|
|
|
this.getCbSizeBytes_(), |
476
|
|
|
this.getValidBitsPerSampleBytes_(), |
477
|
|
|
this.getFactBytes_(), |
478
|
|
|
byteData_.packArray(this.dataChunkId, chr_), |
479
|
|
|
byteData_.pack(this.dataChunkSize, uInt32_), |
480
|
|
|
this.samplesToBytes_(options), |
481
|
|
|
this.getCueBytes_() |
482
|
|
|
); |
483
|
|
|
} |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
module.exports = WaveFileReaderWriter; |
487
|
|
|
|