Passed
Push — master ( a1a007...6a0db7 )
by Rafael S.
01:04
created

index.js   B

Complexity

Total Complexity 37
Complexity/F 4.11

Size

Lines of Code 238
Function Count 9

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 0
c 3
b 0
f 0
nc 1
dl 0
loc 238
rs 8.6
wmc 37
mnd 2
bc 35
fnc 9
bpm 3.8888
cpm 4.1111
noi 5

9 Functions

Rating   Name   Duplication   Size   Complexity  
A ➔ sign_ 0 3 2
A ➔ decode 0 12 4
A ➔ encodeBlock 0 12 3
B ➔ setEncoderInfo_ 0 18 6
D ➔ decodeSample_ 0 30 9
A ➔ decodeBlock 0 17 2
B ➔ encode 0 12 5
A ➔ blockHead_ 0 9 1
B ➔ encodeSample_ 0 30 5
1
/*!
2
 * imaadpcm
3
 * JavaScript IMA ADPCM codec.
4
 * Copyright (c) 2018 Rafael da Silva Rocha.
5
 * https://github.com/rochars/imaadpcm
6
 *
7
 * References:
8
 * http://www.cs.columbia.edu/~hgs/audio/dvi/
9
 * https://github.com/acida/pyima
10
 * https://wiki.multimedia.cx/index.php/IMA_ADPCM
11
 * 
12
 */
13
14
/** @private */
15
const INDEX_TABLE = [
16
    -1, -1, -1, -1, 2, 4, 6, 8,
17
    -1, -1, -1, -1, 2, 4, 6, 8];
18
/** @private */
19
const STEP_TABLE = [
20
    7, 8, 9, 10, 11, 12, 13, 14,
21
    16, 17, 19, 21, 23, 25, 28, 31,
22
    34, 37, 41, 45, 50, 55, 60, 66,
23
    73, 80, 88, 97, 107, 118, 130, 143,
24
    157, 173, 190, 209, 230, 253, 279, 307,
25
    337, 371, 408, 449, 494, 544, 598, 658,
26
    724, 796, 876, 963, 1060, 1166, 1282, 1411,
27
    1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
28
    3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
29
    7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
30
    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
31
    32767];
32
/** @private */
33
var encoderPredicted_ = 0;
34
/** @private */
35
var encoderIndex_ = 0;
36
/** @private */
37
var encoderStep_ = 7;
38
/** @private */
39
var decoderPredicted_ = 0;
40
/** @private */
41
var decoderIndex_ = 0;
42
/** @private */
43
var decoderStep_ = 7;
44
45
/**
46
 * Sign a 16-bit integer.
47
 * @param {number} num A 16-bit integer.
48
 * @return {number}
49
 * @private
50
 */
51
function sign_(num) {
52
    return num > 32768 ? num - 65536 : num;
53
}
54
55
/**
56
 * Set the value for encoderPredicted_ and encoderIndex_
57
 * after each sample is compressed.
58
 * @param {number} value The compressed ADPCM sample
59
 * @param {number} diff The calculated difference
60
 * @private
61
 */
62
function setEncoderInfo_(value, diff) {
63
    if (value & 8) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
64
        encoderPredicted_ -= diff;
65
    } else {
66
        encoderPredicted_ += diff;
67
    }
68
    if (encoderPredicted_ < -0x8000) {
69
        encoderPredicted_ = -0x8000;
70
    } else if (encoderPredicted_ > 0x7fff) {
71
        encoderPredicted_ = 0x7fff;
72
    }
73
    encoderIndex_ += INDEX_TABLE[value & 7];
74
    if (encoderIndex_ < 0) {
75
        encoderIndex_ = 0;
76
    } else if (encoderIndex_ > 88) {
77
        encoderIndex_ = 88;
78
    }
79
}
80
81
/**
82
 * Compress a 16-bit PCM sample into a 4-bit ADPCM sample.
83
 * @param {number} sample The sample.
84
 * @return {number}
85
 * @private
86
 */
87
function encodeSample_(sample) {
88
    let delta = sample - encoderPredicted_;
89
    let value = 0;
90
    if (delta >= 0) {
91
        value = 0;
92
    } else {
93
        value = 8;
94
        delta = -delta;
95
    }
96
    let step = STEP_TABLE[encoderIndex_];
97
    let diff = step >> 3;
98
    if (delta > step) {
99
        value |= 4;
100
        delta -= step;
101
        diff += step;
102
    }
103
    step >>= 1;
104
    if (delta > step) {
105
        value |= 2;
106
        delta -= step;
107
        diff += step;
108
    }
109
    step >>= 1;
110
    if (delta > step) {
111
        value |= 1;
112
        diff += step;
113
    }
114
    setEncoderInfo_(value, diff);
115
    return value;
116
}
117
118
/**
119
 * Decode a 4-bit ADPCM sample into a 16-bit PCM sample.
120
 * @param {number} nibble A 4-bit adpcm sample.
121
 * @return {number}
122
 * @private
123
 */
124
function decodeSample_(nibble) {
125
    let difference = 0;
126
    if (nibble & 4) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
127
        difference += decoderStep_;
128
    }
129
    if (nibble & 2) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
130
        difference += decoderStep_ >> 1;
131
    }
132
    if (nibble & 1) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
133
        difference += decoderStep_ >> 2;
134
    }
135
    difference += decoderStep_ >> 3;
136
    if (nibble & 8) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
137
        difference = -difference;
138
    }
139
    decoderPredicted_ += difference;
140
    if (decoderPredicted_ > 32767) {
141
        decoderPredicted_ = 32767;
142
    } else if (decoderPredicted_ < -32767) {
143
        decoderPredicted_ = -32767;
144
    }
145
    decoderIndex_ += INDEX_TABLE[nibble];
146
    if (decoderIndex_ < 0) {
147
        decoderIndex_ = 0;
148
    } else if (decoderIndex_ > 88) {
149
        decoderIndex_ = 88;
150
    }
151
    decoderStep_ = STEP_TABLE[decoderIndex_];
152
    return decoderPredicted_;
153
}
154
155
/**
156
 * Return the head of a ADPCM sample block.
157
 * @param {number} sample The first sample of the block.
158
 * @return {!Array<number>}
159
 * @private
160
 */
161
function blockHead_(sample) {
162
    encodeSample_(sample);
163
    let adpcmSamples = [];
164
    adpcmSamples.push(sample & 0xFF);
165
    adpcmSamples.push((sample >> 8) & 0xFF);
166
    adpcmSamples.push(encoderIndex_);
167
    adpcmSamples.push(0);
168
    return adpcmSamples;
169
}
170
171
/**
172
 * Encode a block of 505 16-bit samples as 4-bit ADPCM samples.
173
 * @param {!Array<number>} block A sample block of 505 samples.
174
 * @return {!Array<number>}
175
 */
176
function encodeBlock(block) {
177
    let adpcmSamples = blockHead_(block[0]);
178
    for (let i=3; i<block.length; i+=2) {
179
        let sample2 = encodeSample_(block[i]);
180
        let sample = encodeSample_(block[i + 1]);
181
        adpcmSamples.push((sample << 4) | sample2);
182
    }
183
    while (adpcmSamples.length < 256) {
184
        adpcmSamples.push(0);
185
    }
186
    return adpcmSamples;
187
}
188
189
/**
190
 * Decode a block of 256 ADPCM samples into 16-bit PCM samples.
191
 * @param {!Array<number>} block A adpcm sample block of 256 samples.
192
 * @return {!Array<number>}
193
 */
194
function decodeBlock(block) {
195
    decoderPredicted_ = sign_((block[1] << 8) | block[0]);
196
    decoderIndex_ = block[2];
197
    decoderStep_ = STEP_TABLE[decoderIndex_];
198
    let result = [
199
            decoderPredicted_,
200
            sign_((block[3] << 8) | block[2])
201
        ];
202
    for (let i=4; i<block.length; i++) {
203
        let original_sample = block[i];
204
        let second_sample = original_sample >> 4;
205
        let first_sample = (second_sample << 4) ^ original_sample;
206
        result.push(decodeSample_(first_sample));
207
        result.push(decodeSample_(second_sample));
208
    }
209
    return result;
210
}
211
212
/**
213
 * Encode 16-bit PCM samples into 4-bit IMA ADPCM samples.
214
 * @param {!Array<number>} samples A array of samples.
215
 * @return {!Array<number>}
216
 */
217
function encode(samples) {
218
    let adpcmSamples = [];
219
    let block = [];
220
    for (let i=0; i<samples.length; i++) {
221
        block.push(samples[i]);
222
        if ((i % 505 == 0 && i != 0) || i == samples.length - 1) {
223
            adpcmSamples = adpcmSamples.concat(encodeBlock(block));
224
            block = [];
225
        }
226
    }
227
    return adpcmSamples;
228
}
229
230
/**
231
 * Decode IMA ADPCM samples into 16-bit PCM samples.
232
 * @param {!Array<number>} adpcmSamples A array of ADPCM samples.
233
 * @param {number} blockAlign The block size.
234
 * @return {!Array<number>}
235
 */
236
function decode(adpcmSamples, blockAlign=256) {
237
    let samples = [];
238
    let block = [];
239
    for (let i=0; i<adpcmSamples.length; i++) {
240
        if (i % blockAlign == 0 && i != 0) {            
241
            samples = samples.concat(decodeBlock(block));
242
            block = [];
243
        }
244
        block.push(adpcmSamples[i]);
245
    }
246
    return samples;
247
}
248
249
module.exports.encode = encode;
250
module.exports.decode = decode;
251
module.exports.encodeBlock = encodeBlock;
252
module.exports.decodeBlock = decodeBlock;
253