Passed
Push — master ( f46385...d7bb73 )
by Rafael S.
02:26
created

lib/make-wav-header.js (1 issue)

Severity
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 A tool to create wav file headers.
27
 * @see https://github.com/rochars/wavefile
28
 */
29
30
import WAV_AUDIO_FORMATS from './wav-audio-formats.js';
31
32
/**
33
 * Return the header for a wav file.
34
 * @param {string} bitDepthCode The audio bit depth
35
 * @param {number} numChannels The number of channels
36
 * @param {number} sampleRate The sample rate.
37
 * @param {number} numBytes The number of bytes each sample use.
38
 * @param {number} samplesLength The length of the samples in bytes.
39
 * @param {!Object} options The extra options, like container defintion.
40
 * @private
41
 */
42
export default function makeWavHeader(
43
  bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options) {
44
  /** @type {!Object} */
45
  let header = {};
0 ignored issues
show
The assignment to variable header seems to be never used. Consider removing it.
Loading history...
46
  if (bitDepthCode == '4') {
47
    header = createADPCMHeader_(
48
      bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options);
49
50
  } else if (bitDepthCode == '8a' || bitDepthCode == '8m') {
51
    header = createALawMulawHeader_(
52
      bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options);
53
54
  } else if(Object.keys(WAV_AUDIO_FORMATS).indexOf(bitDepthCode) == -1 ||
55
      numChannels > 2) {
56
    header = createExtensibleHeader_(
57
      bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options);
58
59
  } else {
60
    header = createPCMHeader_(
61
      bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options);      
62
  }
63
  return header;
64
}
65
66
/**
67
 * Create the header of a linear PCM wave file.
68
 * @param {string} bitDepthCode The audio bit depth
69
 * @param {number} numChannels The number of channels
70
 * @param {number} sampleRate The sample rate.
71
 * @param {number} numBytes The number of bytes each sample use.
72
 * @param {number} samplesLength The length of the samples in bytes.
73
 * @param {!Object} options The extra options, like container defintion.
74
 * @private
75
 */
76
function createPCMHeader_(
77
  bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options) {
78
  return {
79
    container: options.container,
80
    chunkSize: 36 + samplesLength,
81
    format: 'WAVE',
82
    bitDepth: bitDepthCode,
83
    fmt: {
84
      chunkId: 'fmt ',
85
      chunkSize: 16,
86
      audioFormat: WAV_AUDIO_FORMATS[bitDepthCode] || 65534,
87
      numChannels: numChannels,
88
      sampleRate: sampleRate,
89
      byteRate: (numChannels * numBytes) * sampleRate,
90
      blockAlign: numChannels * numBytes,
91
      bitsPerSample: parseInt(bitDepthCode, 10),
92
      cbSize: 0,
93
      validBitsPerSample: 0,
94
      dwChannelMask: 0,
95
      subformat: []
96
    }
97
  };
98
}
99
100
/**
101
 * Create the header of a ADPCM wave file.
102
 * @param {string} bitDepthCode The audio bit depth
103
 * @param {number} numChannels The number of channels
104
 * @param {number} sampleRate The sample rate.
105
 * @param {number} numBytes The number of bytes each sample use.
106
 * @param {number} samplesLength The length of the samples in bytes.
107
 * @param {!Object} options The extra options, like container defintion.
108
 * @private
109
 */
110
function createADPCMHeader_(
111
  bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options) {
112
  /** @type {!Object} */
113
  let header = createPCMHeader_(
114
    bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options);
115
  header.chunkSize = 40 + samplesLength;
116
  header.fmt.chunkSize = 20;
117
  header.fmt.byteRate = 4055;
118
  header.fmt.blockAlign = 256;
119
  header.fmt.bitsPerSample = 4;
120
  header.fmt.cbSize = 2;
121
  header.fmt.validBitsPerSample = 505;
122
  header.fact = {
123
    chunkId: 'fact',
124
    chunkSize: 4,
125
    dwSampleLength: samplesLength * 2
126
  };
127
  return header;
128
}
129
130
/**
131
 * Create the header of WAVE_FORMAT_EXTENSIBLE file.
132
 * @param {string} bitDepthCode The audio bit depth
133
 * @param {number} numChannels The number of channels
134
 * @param {number} sampleRate The sample rate.
135
 * @param {number} numBytes The number of bytes each sample use.
136
 * @param {number} samplesLength The length of the samples in bytes.
137
 * @param {!Object} options The extra options, like container defintion.
138
 * @private
139
 */
140
function createExtensibleHeader_(
141
    bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options) {
142
  /** @type {!Object} */
143
  let header = createPCMHeader_(
144
    bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options);
145
  header.chunkSize = 36 + 24 + samplesLength;
146
  header.fmt.chunkSize = 40;
147
  header.fmt.bitsPerSample = ((parseInt(bitDepthCode, 10) - 1) | 7) + 1;
148
  header.fmt.cbSize = 22;
149
  header.fmt.validBitsPerSample = parseInt(bitDepthCode, 10);
150
  header.fmt.dwChannelMask = getDwChannelMask_(numChannels);
151
  // subformat 128-bit GUID as 4 32-bit values
152
  // only supports uncompressed integer PCM samples
153
  header.fmt.subformat = [1, 1048576, 2852126848, 1905997824];
154
  return header;
155
}
156
157
/**
158
 * Create the header of mu-Law and A-Law wave files.
159
 * @param {string} bitDepthCode The audio bit depth
160
 * @param {number} numChannels The number of channels
161
 * @param {number} sampleRate The sample rate.
162
 * @param {number} numBytes The number of bytes each sample use.
163
 * @param {number} samplesLength The length of the samples in bytes.
164
 * @param {!Object} options The extra options, like container defintion.
165
 * @private
166
 */
167
function createALawMulawHeader_(
168
    bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options) {
169
  /** @type {!Object} */
170
  let header = createPCMHeader_(
171
    bitDepthCode, numChannels, sampleRate, numBytes, samplesLength, options);
172
  header.chunkSize = 40 + samplesLength;
173
  header.fmt.chunkSize = 20;
174
  header.fmt.cbSize = 2;
175
  header.fmt.validBitsPerSample = 8;
176
  header.fact = {
177
    chunkId: 'fact',
178
    chunkSize: 4,
179
    dwSampleLength: samplesLength
180
  };
181
  return header;
182
}
183
184
/**
185
 * Get the value for dwChannelMask according to the number of channels.
186
 * @return {number} the dwChannelMask value.
187
 * @private
188
 */
189
function getDwChannelMask_(numChannels) {
190
  /** @type {number} */
191
  let dwChannelMask = 0;
192
  // mono = FC
193
  if (numChannels === 1) {
194
    dwChannelMask = 0x4;
195
  // stereo = FL, FR
196
  } else if (numChannels === 2) {
197
    dwChannelMask = 0x3;
198
  // quad = FL, FR, BL, BR
199
  } else if (numChannels === 4) {
200
    dwChannelMask = 0x33;
201
  // 5.1 = FL, FR, FC, LF, BL, BR
202
  } else if (numChannels === 6) {
203
    dwChannelMask = 0x3F;
204
  // 7.1 = FL, FR, FC, LF, BL, BR, SL, SR
205
  } else if (numChannels === 8) {
206
    dwChannelMask = 0x63F;
207
  }
208
  return dwChannelMask;
209
}
210