Completed
Push — master ( 1923f9...12bd6d )
by Rafael S.
01:13
created

index.js (6 issues)

Severity
1
/*
2
 * imaadpcm: IMA ADPCM codec in JavaScript.
3
 * Derived from https://github.com/acida/pyima  
4
 * Copyright (c) 2016 acida. MIT License.  
5
 * Copyright (c) 2018 Rafael da Silva Rocha.
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining
8
 * a copy of this software and associated documentation files (the
9
 * "Software"), to deal in the Software without restriction, including
10
 * without limitation the rights to use, copy, modify, merge, publish,
11
 * distribute, sublicense, and/or sell copies of the Software, and to
12
 * permit persons to whom the Software is furnished to do so, subject to
13
 * the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be
16
 * included in all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
 *
26
 */
27
28
/**
29
 * @fileoverview imaadpcm API and private methods.
30
 */
31
32
/** @module imaadpcm */
33
34
/**
35
 * @type {!Array<number>}
36
 * @private
37
 */
38
const INDEX_TABLE = [
39
    -1, -1, -1, -1, 2, 4, 6, 8,
40
    -1, -1, -1, -1, 2, 4, 6, 8];
41
/**
42
 * @type {!Array<number>}
43
 * @private
44
 */
45
const STEP_TABLE = [
46
    7, 8, 9, 10, 11, 12, 13, 14,
47
    16, 17, 19, 21, 23, 25, 28, 31,
48
    34, 37, 41, 45, 50, 55, 60, 66,
49
    73, 80, 88, 97, 107, 118, 130, 143,
50
    157, 173, 190, 209, 230, 253, 279, 307,
51
    337, 371, 408, 449, 494, 544, 598, 658,
52
    724, 796, 876, 963, 1060, 1166, 1282, 1411,
53
    1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
54
    3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
55
    7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
56
    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
57
    32767];
58
/**
59
 * @type {number}
60
 * @private
61
 */
62
let encoderPredicted_ = 0;
63
/**
64
 * @type {number}
65
 * @private
66
 */
67
let encoderIndex_ = 0;
68
/**
69
 * @type {number}
70
 * @private
71
 */
72
let encoderStep_ = 7;
0 ignored issues
show
The variable encoderStep_ seems to be never used. Consider removing it.
Loading history...
73
/**
74
 * @type {number}
75
 * @private
76
 */
77
let decoderPredicted_ = 0;
78
/**
79
 * @type {number}
80
 * @private
81
 */
82
let decoderIndex_ = 0;
83
/**
84
 * @type {number}
85
 * @private
86
 */
87
let decoderStep_ = 7;
88
89
/**
90
 * Encode 16-bit PCM samples into 4-bit IMA ADPCM samples.
91
 * @param {!Int16Array} samples A array of samples.
92
 * @return {!Uint8Array}
93
 */
94
export function encode(samples) {
95
  /** @type {!Uint8Array} */
96
  let adpcmSamples = new Uint8Array((samples.length));
97
  /** @type {!Array<number>} */
98
  let block = [];
99
  /** @type {number} */
100
  let fileIndex = 0;
101
  for (let i=0; i<samples.length; i++) {
102
    if ((i % 505 == 0 && i != 0)) {
103
      adpcmSamples.set(encodeBlock(block), fileIndex);
104
      fileIndex += 256;
105
      block = [];
106
    }
107
    block.push(samples[i]);
108
  }
109
  return adpcmSamples;
110
}
111
112
/**
113
 * Decode IMA ADPCM samples into 16-bit PCM samples.
114
 * @param {!Uint8Array} adpcmSamples A array of ADPCM samples.
115
 * @param {number} blockAlign The block size.
116
 * @return {!Int16Array}
117
 */
118
export function decode(adpcmSamples, blockAlign=256) {
119
  /** @type {!Int16Array} */
120
  let samples = new Int16Array(adpcmSamples.length * 2);
121
  /** @type {!Array<number>} */
122
  let block = [];
123
  /** @type {number} */
124
  let fileIndex = 0;
125
  for (let i=0; i<adpcmSamples.length; i++) {
126
    if (i % blockAlign == 0 && i != 0) {            
127
      samples.set(decodeBlock(block), fileIndex);
128
      fileIndex += blockAlign * 2;
129
      block = [];
130
    }
131
    block.push(adpcmSamples[i]);
132
  }
133
  return samples;
134
}
135
136
/**
137
 * Encode a block of 505 16-bit samples as 4-bit ADPCM samples.
138
 * @param {!Array<number>} block A sample block of 505 samples.
139
 * @return {!Array<number>}
140
 */
141
export function encodeBlock(block) {
142
  /** @type {!Array<number>} */
143
  let adpcmSamples = blockHead_(block[0]);
144
  for (let i=3; i<block.length; i+=2) {
145
    /** @type {number} */
146
    let sample2 = encodeSample_(block[i]);
147
    /** @type {number} */
148
    let sample = encodeSample_(block[i + 1]);
149
    adpcmSamples.push((sample << 4) | sample2);
150
  }
151
  while (adpcmSamples.length < 256) {
152
    adpcmSamples.push(0);
153
  }
154
  return adpcmSamples;
155
}
156
157
/**
158
 * Decode a block of ADPCM samples into 16-bit PCM samples.
159
 * @param {!Array<number>} block A adpcm sample block.
160
 * @return {!Array<number>}
161
 */
162
export function decodeBlock(block) {
163
  decoderPredicted_ = sign_((block[1] << 8) | block[0]);
164
  decoderIndex_ = block[2];
165
  decoderStep_ = STEP_TABLE[decoderIndex_];
166
  /** @type {!Array<number>} */
167
  let result = [
168
      decoderPredicted_,
169
      sign_((block[3] << 8) | block[2])
170
    ];
171
  for (let i=4; i<block.length; i++) {
172
    /** @type {number} */
173
    let original_sample = block[i];
174
    /** @type {number} */
175
    let second_sample = original_sample >> 4;
176
    /** @type {number} */
177
    let first_sample = (second_sample << 4) ^ original_sample;
178
    result.push(decodeSample_(first_sample));
179
    result.push(decodeSample_(second_sample));
180
  }
181
  return result;
182
}
183
184
/**
185
 * Sign a 16-bit integer.
186
 * @param {number} num A 16-bit integer.
187
 * @return {number}
188
 * @private
189
 */
190
function sign_(num) {
191
  return num > 32768 ? num - 65536 : num;
192
}
193
194
/**
195
 * Compress a 16-bit PCM sample into a 4-bit ADPCM sample.
196
 * @param {number} sample The sample.
197
 * @return {number}
198
 * @private
199
 */
200
function encodeSample_(sample) {
201
  /** @type {number} */
202
  let delta = sample - encoderPredicted_;
203
  /** @type {number} */
204
  let value = 0;
205
  if (delta >= 0) {
206
    value = 0;
207
  } else {
208
    value = 8;
209
    delta = -delta;
210
  }
211
  /** @type {number} */
212
  let step = STEP_TABLE[encoderIndex_];
213
  /** @type {number} */
214
  let diff = step >> 3;
215
  if (delta > step) {
216
    value |= 4;
217
    delta -= step;
218
    diff += step;
219
  }
220
  step >>= 1;
221
  if (delta > step) {
222
    value |= 2;
223
    delta -= step;
224
    diff += step;
225
  }
226
  step >>= 1;
227
  if (delta > step) {
228
    value |= 1;
229
    diff += step;
230
  }
231
  updateEncoder_(value, diff);
232
  return value;
233
}
234
235
/**
236
 * Set the value for encoderPredicted_ and encoderIndex_
237
 * after each sample is compressed.
238
 * @param {number} value The compressed ADPCM sample
239
 * @param {number} diff The calculated difference
240
 * @private
241
 */
242
function updateEncoder_(value, diff) {
243
  if (value & 8) {
0 ignored issues
show
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
244
    encoderPredicted_ -= diff;
245
  } else {
246
    encoderPredicted_ += diff;
247
  }
248
  if (encoderPredicted_ < -0x8000) {
249
    encoderPredicted_ = -0x8000;
250
  } else if (encoderPredicted_ > 0x7fff) {
251
    encoderPredicted_ = 0x7fff;
252
  }
253
  encoderIndex_ += INDEX_TABLE[value & 7];
254
  if (encoderIndex_ < 0) {
255
    encoderIndex_ = 0;
256
  } else if (encoderIndex_ > 88) {
257
    encoderIndex_ = 88;
258
  }
259
}
260
261
/**
262
 * Decode a 4-bit ADPCM sample into a 16-bit PCM sample.
263
 * @param {number} nibble A 4-bit adpcm sample.
264
 * @return {number}
265
 * @private
266
 */
267
function decodeSample_(nibble) {
268
  /** @type {number} */
269
  let difference = 0;
270
  if (nibble & 4) {
0 ignored issues
show
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
271
    difference += decoderStep_;
272
  }
273
  if (nibble & 2) {
0 ignored issues
show
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
274
    difference += decoderStep_ >> 1;
275
  }
276
  if (nibble & 1) {
0 ignored issues
show
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
277
    difference += decoderStep_ >> 2;
278
  }
279
  difference += decoderStep_ >> 3;
280
  if (nibble & 8) {
0 ignored issues
show
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
281
    difference = -difference;
282
  }
283
  decoderPredicted_ += difference;
284
  if (decoderPredicted_ > 32767) {
285
    decoderPredicted_ = 32767;
286
  } else if (decoderPredicted_ < -32767) {
287
    decoderPredicted_ = -32767;
288
  }
289
  updateDecoder_(nibble);
290
  return decoderPredicted_;
291
}
292
293
/**
294
 * Update the index and step after decoding a sample.
295
 * @param {number} nibble A 4-bit adpcm sample.
296
 * @private
297
 */
298
function updateDecoder_(nibble) {
299
  decoderIndex_ += INDEX_TABLE[nibble];
300
  if (decoderIndex_ < 0) {
301
    decoderIndex_ = 0;
302
  } else if (decoderIndex_ > 88) {
303
    decoderIndex_ = 88;
304
  }
305
  decoderStep_ = STEP_TABLE[decoderIndex_];
306
}
307
308
/**
309
 * Return the head of a ADPCM sample block.
310
 * @param {number} sample The first sample of the block.
311
 * @return {!Array<number>}
312
 * @private
313
 */
314
function blockHead_(sample) {
315
  encodeSample_(sample);
316
  /** @type {!Array<number>} */
317
  let adpcmSamples = [];
318
  adpcmSamples.push(sample & 0xFF);
319
  adpcmSamples.push((sample >> 8) & 0xFF);
320
  adpcmSamples.push(encoderIndex_);
321
  adpcmSamples.push(0);
322
  return adpcmSamples;
323
}
324