Passed
Branch v8.x (d6a5cf)
by Rafael S.
02:05
created

lib/wav-buffer-reader.js   A

Complexity

Total Complexity 42
Complexity/F 3

Size

Lines of Code 374
Function Count 14

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 0
eloc 206
c 1
b 0
f 0
nc 1
dl 0
loc 374
rs 9.0399
wmc 42
mnd 3
bc 45
fnc 14
bpm 3.2142
cpm 3
noi 10

14 Functions

Rating   Name   Duplication   Size   Complexity  
A wav-buffer-reader.js ➔ readLISTChunk_ 0 20 4
A wav-buffer-reader.js ➔ readJunkChunk_ 0 13 2
A wav-buffer-reader.js ➔ readFmtExtension_ 0 16 4
A wav-buffer-reader.js ➔ readDataChunk_ 0 15 3
A wav-buffer-reader.js ➔ readCueChunk_ 0 20 3
A wav-buffer-reader.js ➔ readSmplChunk_ 0 28 3
A wav-buffer-reader.js ➔ readFactChunk_ 0 10 2
A wav-buffer-reader.js ➔ readDs64Chunk_ 0 26 3
B wav-buffer-reader.js ➔ readLISTSubChunks_ 0 31 5
A wav-buffer-reader.js ➔ readFmtChunk_ 0 18 2
A wav-buffer-reader.js ➔ readBextChunk_ 0 27 2
A wav-buffer-reader.js ➔ readWavBuffer 0 16 1
A wav-buffer-reader.js ➔ bitDepthFromFmt_ 0 11 5
A wav-buffer-reader.js ➔ readRIFFChunk_ 0 14 3

How to fix   Complexity   

Complexity

Complex classes like lib/wav-buffer-reader.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*
2
 * Copyright (c) 2018 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview Get structured wav data out of buffers.
27
 * @see https://github.com/rochars/wavefile
28
 */
29
30
import {riffChunks, findChunk_} from '../vendor/riff-chunks.js';
31
import BufferIO from './bufferio.js';
32
33
let io = new BufferIO();
34
35
/**
36
 * Set up the WaveFile object from a byte buffer.
37
 * @param {!Uint8Array} buffer The buffer.
38
 * @param {boolean} samples True if the samples should be loaded.
39
 * @param {!Object} wav True if the samples should be loaded.
40
 * @param {!Object} uInt32_ True if the samples should be loaded.
41
 * @param {!Object} uInt16_ True if the samples should be loaded.
42
 * @throws {Error} If container is not RIFF, RIFX or RF64.
43
 * @throws {Error} If no 'fmt ' chunk is found.
44
 * @throws {Error} If no 'data' chunk is found.
45
 */
46
export default function readWavBuffer(buffer, samples, wav, uInt32_, uInt16_) {
47
  io.head_ = 0;
48
  readRIFFChunk_(buffer, wav, uInt32_, uInt16_);
49
  /** @type {!Object} */
50
  let chunk = riffChunks(buffer);
51
  readDs64Chunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
52
  readFmtChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
53
  readFactChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
54
  readBextChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
55
  readCueChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
56
  readSmplChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
57
  readDataChunk_(buffer, chunk.subChunks, samples, wav, uInt32_, uInt16_);
58
  readJunkChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
59
  readLISTChunk_(buffer, chunk.subChunks, wav, uInt32_, uInt16_);
60
  bitDepthFromFmt_(wav, uInt32_, uInt16_);
61
}
62
63
/**
64
 * Set the string code of the bit depth based on the 'fmt ' chunk.
65
 * @private
66
 */
67
function bitDepthFromFmt_(wav, uInt32_, uInt16_) {
0 ignored issues
show
Unused Code introduced by
The parameter uInt16_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter uInt32_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
68
  if (wav.fmt.audioFormat === 3 && wav.fmt.bitsPerSample === 32) {
69
    wav.bitDepth = '32f';
70
  } else if (wav.fmt.audioFormat === 6) {
71
    wav.bitDepth = '8a';
72
  } else if (wav.fmt.audioFormat === 7) {
73
    wav.bitDepth = '8m';
74
  } else {
75
    wav.bitDepth = wav.fmt.bitsPerSample.toString();
76
  }
77
}
78
79
/**
80
 * Read the RIFF chunk a wave file.
81
 * @param {!Uint8Array} bytes A wav buffer.
82
 * @throws {Error} If no 'RIFF' chunk is found.
83
 * @private
84
 */
85
function readRIFFChunk_(bytes, wav, uInt32_, uInt16_) {
86
  io.head_ = 0;
87
  wav.container = io.readString_(bytes, 4);
88
  if (['RIFF', 'RIFX', 'RF64'].indexOf(wav.container) === -1) {
89
    throw Error('Not a supported format.');
90
  }
91
  uInt16_.be = wav.container === 'RIFX';
92
  uInt32_.be = uInt16_.be;
93
  wav.chunkSize = io.read_(bytes, uInt32_);
94
  wav.format = io.readString_(bytes, 4);
95
  if (wav.format != 'WAVE') {
96
    throw Error('Could not find the "WAVE" format identifier');
97
  }
98
}
99
100
/**
101
 * Read the 'fmt ' chunk of a wave file.
102
 * @param {!Uint8Array} buffer The wav file buffer.
103
 * @param {!Object} signature The file signature.
104
 * @throws {Error} If no 'fmt ' chunk is found.
105
 * @private
106
 */
107
function readFmtChunk_(buffer, signature, wav, uInt32_, uInt16_) {
108
  /** @type {?Object} */
109
  let chunk = findChunk_(signature, 'fmt ');
110
  if (chunk) {
111
    io.head_ = chunk.chunkData.start;
112
    wav.fmt.chunkId = chunk.chunkId;
113
    wav.fmt.chunkSize = chunk.chunkSize;
114
    wav.fmt.audioFormat = io.read_(buffer, uInt16_);
115
    wav.fmt.numChannels = io.read_(buffer, uInt16_);
116
    wav.fmt.sampleRate = io.read_(buffer, uInt32_);
117
    wav.fmt.byteRate = io.read_(buffer, uInt32_);
118
    wav.fmt.blockAlign = io.read_(buffer, uInt16_);
119
    wav.fmt.bitsPerSample = io.read_(buffer, uInt16_);
120
    readFmtExtension_(buffer, wav, uInt32_, uInt16_);
121
  } else {
122
    throw Error('Could not find the "fmt " chunk');
123
  }
124
}
125
126
/**
127
 * Read the 'fmt ' chunk extension.
128
 * @param {!Uint8Array} buffer The wav file buffer.
129
 * @private
130
 */
131
function readFmtExtension_(buffer, wav, uInt32_, uInt16_) {
132
  if (wav.fmt.chunkSize > 16) {
133
    wav.fmt.cbSize = io.read_(buffer, uInt16_);
134
    if (wav.fmt.chunkSize > 18) {
135
      wav.fmt.validBitsPerSample = io.read_(buffer, uInt16_);
136
      if (wav.fmt.chunkSize > 20) {
137
        wav.fmt.dwChannelMask = io.read_(buffer, uInt32_);
138
        wav.fmt.subformat = [
139
          io.read_(buffer, uInt32_),
140
          io.read_(buffer, uInt32_),
141
          io.read_(buffer, uInt32_),
142
          io.read_(buffer, uInt32_)];
143
      }
144
    }
145
  }
146
}
147
148
/**
149
 * Read the 'fact' chunk of a wav file.
150
 * @param {!Uint8Array} buffer The wav file buffer.
151
 * @param {!Object} signature The file signature.
152
 * @private
153
 */
154
function readFactChunk_(buffer, signature, wav, uInt32_, uInt16_) {
0 ignored issues
show
Unused Code introduced by
The parameter uInt16_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
155
  /** @type {?Object} */
156
  let chunk = findChunk_(signature, 'fact');
157
  if (chunk) {
158
    io.head_ = chunk.chunkData.start;
159
    wav.fact.chunkId = chunk.chunkId;
160
    wav.fact.chunkSize = chunk.chunkSize;
161
    wav.fact.dwSampleLength = io.read_(buffer, uInt32_);
162
  }
163
}
164
165
/**
166
 * Read the 'cue ' chunk of a wave file.
167
 * @param {!Uint8Array} buffer The wav file buffer.
168
 * @param {!Object} signature The file signature.
169
 * @private
170
 */
171
function readCueChunk_(buffer, signature, wav, uInt32_, uInt16_) {
0 ignored issues
show
Unused Code introduced by
The parameter uInt16_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
172
  /** @type {?Object} */
173
  let chunk = findChunk_(signature, 'cue ');
174
  if (chunk) {
175
    io.head_ = chunk.chunkData.start;
176
    wav.cue.chunkId = chunk.chunkId;
177
    wav.cue.chunkSize = chunk.chunkSize;
178
    wav.cue.dwCuePoints = io.read_(buffer, uInt32_);
179
    for (let i=0; i<wav.cue.dwCuePoints; i++) {
180
      wav.cue.points.push({
181
        dwName: io.read_(buffer, uInt32_),
182
        dwPosition: io.read_(buffer, uInt32_),
183
        fccChunk: io.readString_(buffer, 4),
184
        dwChunkStart: io.read_(buffer, uInt32_),
185
        dwBlockStart: io.read_(buffer, uInt32_),
186
        dwSampleOffset: io.read_(buffer, uInt32_),
187
      });
188
    }
189
  }
190
}
191
192
/**
193
 * Read the 'smpl' chunk of a wave file.
194
 * @param {!Uint8Array} buffer The wav file buffer.
195
 * @param {!Object} signature The file signature.
196
 * @private
197
 */
198
function readSmplChunk_(buffer, signature, wav, uInt32_, uInt16_) {
0 ignored issues
show
Unused Code introduced by
The parameter uInt16_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
199
  /** @type {?Object} */
200
  let chunk = findChunk_(signature, 'smpl');
201
  if (chunk) {
202
    io.head_ = chunk.chunkData.start;
203
    wav.smpl.chunkId = chunk.chunkId;
204
    wav.smpl.chunkSize = chunk.chunkSize;
205
    wav.smpl.dwManufacturer = io.read_(buffer, uInt32_);
206
    wav.smpl.dwProduct = io.read_(buffer, uInt32_);
207
    wav.smpl.dwSamplePeriod = io.read_(buffer, uInt32_);
208
    wav.smpl.dwMIDIUnityNote = io.read_(buffer, uInt32_);
209
    wav.smpl.dwMIDIPitchFraction = io.read_(buffer, uInt32_);
210
    wav.smpl.dwSMPTEFormat = io.read_(buffer, uInt32_);
211
    wav.smpl.dwSMPTEOffset = io.read_(buffer, uInt32_);
212
    wav.smpl.dwNumSampleLoops = io.read_(buffer, uInt32_);
213
    wav.smpl.dwSamplerData = io.read_(buffer, uInt32_);
214
    for (let i=0; i<wav.smpl.dwNumSampleLoops; i++) {
215
      wav.smpl.loops.push({
216
        dwName: io.read_(buffer, uInt32_),
217
        dwType: io.read_(buffer, uInt32_),
218
        dwStart: io.read_(buffer, uInt32_),
219
        dwEnd: io.read_(buffer, uInt32_),
220
        dwFraction: io.read_(buffer, uInt32_),
221
        dwPlayCount: io.read_(buffer, uInt32_),
222
      });
223
    }
224
  }
225
}
226
227
/**
228
 * Read the 'data' chunk of a wave file.
229
 * @param {!Uint8Array} buffer The wav file buffer.
230
 * @param {!Object} signature The file signature.
231
 * @param {boolean} samples True if the samples should be loaded.
232
 * @throws {Error} If no 'data' chunk is found.
233
 * @private
234
 */
235
function readDataChunk_(buffer, signature, samples, wav, uInt32_, uInt16_) {
0 ignored issues
show
Unused Code introduced by
The parameter uInt32_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter uInt16_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
236
  /** @type {?Object} */
237
  let chunk = findChunk_(signature, 'data');
238
  if (chunk) {
239
    wav.data.chunkId = 'data';
240
    wav.data.chunkSize = chunk.chunkSize;
241
    if (samples) {
242
      wav.data.samples = buffer.slice(
243
        chunk.chunkData.start,
244
        chunk.chunkData.end);
245
    }
246
  } else {
247
    throw Error('Could not find the "data" chunk');
248
  }
249
}
250
251
/**
252
 * Read the 'bext' chunk of a wav file.
253
 * @param {!Uint8Array} buffer The wav file buffer.
254
 * @param {!Object} signature The file signature.
255
 * @private
256
 */
257
function readBextChunk_(buffer, signature, wav, uInt32_, uInt16_) {
258
  /** @type {?Object} */
259
  let chunk = findChunk_(signature, 'bext');
260
  if (chunk) {
261
    io.head_ = chunk.chunkData.start;
262
    wav.bext.chunkId = chunk.chunkId;
263
    wav.bext.chunkSize = chunk.chunkSize;
264
    wav.bext.description = io.readString_(buffer, 256);
265
    wav.bext.originator = io.readString_(buffer, 32);
266
    wav.bext.originatorReference = io.readString_(buffer, 32);
267
    wav.bext.originationDate = io.readString_(buffer, 10);
268
    wav.bext.originationTime = io.readString_(buffer, 8);
269
    wav.bext.timeReference = [
270
      io.read_(buffer, uInt32_),
271
      io.read_(buffer, uInt32_)];
272
    wav.bext.version = io.read_(buffer, uInt16_);
273
    wav.bext.UMID = io.readString_(buffer, 64);
274
    wav.bext.loudnessValue = io.read_(buffer, uInt16_);
275
    wav.bext.loudnessRange = io.read_(buffer, uInt16_);
276
    wav.bext.maxTruePeakLevel = io.read_(buffer, uInt16_);
277
    wav.bext.maxMomentaryLoudness = io.read_(buffer, uInt16_);
278
    wav.bext.maxShortTermLoudness = io.read_(buffer, uInt16_);
279
    wav.bext.reserved = io.readString_(buffer, 180);
280
    wav.bext.codingHistory = io.readString_(
281
      buffer, wav.bext.chunkSize - 602);
282
  }
283
}
284
285
/**
286
 * Read the 'ds64' chunk of a wave file.
287
 * @param {!Uint8Array} buffer The wav file buffer.
288
 * @param {!Object} signature The file signature.
289
 * @throws {Error} If no 'ds64' chunk is found and the file is RF64.
290
 * @private
291
 */
292
function readDs64Chunk_(buffer, signature, wav, uInt32_, uInt16_) {
0 ignored issues
show
Unused Code introduced by
The parameter uInt16_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
293
  /** @type {?Object} */
294
  let chunk = findChunk_(signature, 'ds64');
295
  if (chunk) {
296
    io.head_ = chunk.chunkData.start;
297
    wav.ds64.chunkId = chunk.chunkId;
298
    wav.ds64.chunkSize = chunk.chunkSize;
299
    wav.ds64.riffSizeHigh = io.read_(buffer, uInt32_);
300
    wav.ds64.riffSizeLow = io.read_(buffer, uInt32_);
301
    wav.ds64.dataSizeHigh = io.read_(buffer, uInt32_);
302
    wav.ds64.dataSizeLow = io.read_(buffer, uInt32_);
303
    wav.ds64.originationTime = io.read_(buffer, uInt32_);
304
    wav.ds64.sampleCountHigh = io.read_(buffer, uInt32_);
305
    wav.ds64.sampleCountLow = io.read_(buffer, uInt32_);
306
    //if (wav.ds64.chunkSize > 28) {
307
    //  wav.ds64.tableLength = unpack(
308
    //    chunkData.slice(28, 32), uInt32_);
309
    //  wav.ds64.table = chunkData.slice(
310
    //     32, 32 + wav.ds64.tableLength);
311
    //}
312
  } else {
313
    if (wav.container == 'RF64') {
314
      throw Error('Could not find the "ds64" chunk');
315
    }
316
  }
317
}
318
319
/**
320
 * Read the 'LIST' chunks of a wave file.
321
 * @param {!Uint8Array} buffer The wav file buffer.
322
 * @param {!Object} signature The file signature.
323
 * @private
324
 */
325
function readLISTChunk_(buffer, signature, wav, uInt32_, uInt16_) {
326
  /** @type {?Object} */
327
  let listChunks = findChunk_(signature, 'LIST', true);
328
  if (listChunks === null) {
329
    return;
330
  }
331
  for (let j=0; j < listChunks.length; j++) {
332
    /** @type {!Object} */
333
    let subChunk = listChunks[j];
334
    wav.LIST.push({
335
      chunkId: subChunk.chunkId,
336
      chunkSize: subChunk.chunkSize,
337
      format: subChunk.format,
338
      subChunks: []});
339
    for (let x=0; x<subChunk.subChunks.length; x++) {
340
      readLISTSubChunks_(subChunk.subChunks[x],
341
        subChunk.format, buffer, wav, uInt32_, uInt16_);
342
    }
343
  }
344
}
345
346
/**
347
 * Read the sub chunks of a 'LIST' chunk.
348
 * @param {!Object} subChunk The 'LIST' subchunks.
349
 * @param {string} format The 'LIST' format, 'adtl' or 'INFO'.
350
 * @param {!Uint8Array} buffer The wav file buffer.
351
 * @private
352
 */
353
function readLISTSubChunks_(subChunk, format, buffer, wav, uInt32_, uInt16_) {
354
  if (format == 'adtl') {
355
    if (['labl', 'note','ltxt'].indexOf(subChunk.chunkId) > -1) {
356
      io.head_ = subChunk.chunkData.start;
357
      /** @type {!Object<string, string|number>} */
358
      let item = {
359
        chunkId: subChunk.chunkId,
360
        chunkSize: subChunk.chunkSize,
361
        dwName: io.read_(buffer, uInt32_)
362
      };
363
      if (subChunk.chunkId == 'ltxt') {
364
        item.dwSampleLength = io.read_(buffer, uInt32_);
365
        item.dwPurposeID = io.read_(buffer, uInt32_);
366
        item.dwCountry = io.read_(buffer, uInt16_);
367
        item.dwLanguage = io.read_(buffer, uInt16_);
368
        item.dwDialect = io.read_(buffer, uInt16_);
369
        item.dwCodePage = io.read_(buffer, uInt16_);
370
      }
371
      item.value = io.readZSTR_(buffer, io.head_);
372
      wav.LIST[wav.LIST.length - 1].subChunks.push(item);
373
    }
374
  // RIFF INFO tags like ICRD, ISFT, ICMT
375
  } else if(format == 'INFO') {
376
    io.head_ = subChunk.chunkData.start;
377
    wav.LIST[wav.LIST.length - 1].subChunks.push({
378
      chunkId: subChunk.chunkId,
379
      chunkSize: subChunk.chunkSize,
380
      value: io.readZSTR_(buffer, io.head_)
381
    });
382
  }
383
}
384
385
/**
386
 * Read the 'junk' chunk of a wave file.
387
 * @param {!Uint8Array} buffer The wav file buffer.
388
 * @param {!Object} signature The file signature.
389
 * @private
390
 */
391
function readJunkChunk_(buffer, signature, wav, uInt32_, uInt16_) {
0 ignored issues
show
Unused Code introduced by
The parameter uInt16_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter uInt32_ is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
392
  /** @type {?Object} */
393
  let chunk = findChunk_(signature, 'junk');
394
  if (chunk) {
395
    wav.junk = {
396
      chunkId: chunk.chunkId,
397
      chunkSize: chunk.chunkSize,
398
      chunkData: [].slice.call(buffer.slice(
399
        chunk.chunkData.start,
400
        chunk.chunkData.end))
401
    };
402
  }
403
}
404