|
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
|
|
|
|