Passed
Push — master ( 6ac629...770d02 )
by Rafael S.
01:03
created

index.js ➔ decodeSample_   D

Complexity

Conditions 9
Paths 144

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
nc 144
nop 1
dl 0
loc 30
rs 4.6666
c 0
b 0
f 0
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} A 16-bit integer.
0 ignored issues
show
Documentation introduced by
The parameter A does not exist. Did you maybe forget to remove this comment?
Loading history...
48
 * @return {number}
49
 * @private
50
 */
51
function sign_(number) {
52
    return number > 32768 ? number - 65536 : number;
53
}
54
55
/**
56
 * Compress a 16-bit PCM sample into a 4-bit ADPCM sample.
57
 * @param {number} sample The sample.
58
 * @return {number}
59
 * @private
60
 */
61
function encodeSample_(sample) {
62
    let delta = sample - encoderPredicted_;
63
    let value = 0;
64
    if (delta >= 0) {
65
        value = 0;
66
    }
67
    else {
68
        value = 8;
69
        delta = -delta;
70
    }
71
    let step = STEP_TABLE[encoderIndex_];
72
    let diff = step >> 3;
73
    if (delta > step) {
74
        value |= 4;
75
        delta -= step;
76
        diff += step;
77
    }
78
    step >>= 1;
79
    if (delta > step) {
80
        value |= 2;
81
        delta -= step;
82
        diff += step;
83
    }
84
    step >>= 1;
85
    if (delta > step) {
86
        value |= 1;
87
        diff += step;
88
    }
89
    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...
90
        encoderPredicted_ -= diff;
91
    }
92
    else {
93
        encoderPredicted_ += diff;
94
    }
95
    if (encoderPredicted_ < -0x8000) {
96
        encoderPredicted_ = -0x8000;
97
    } else if (encoderPredicted_ > 0x7fff) {
98
        encoderPredicted_ = 0x7fff;
99
    }
100
    encoderIndex_ += INDEX_TABLE[value & 7];
101
    if (encoderIndex_ < 0) {
102
        encoderIndex_ = 0;
103
    } else if (encoderIndex_ > 88) {
104
        encoderIndex_ = 88;
105
    }
106
    return value;
107
}
108
109
/**
110
 * Decode a 4-bit ADPCM sample into a 16-bit PCM sample.
111
 * @param {number} nibble A 4-bit adpcm sample.
112
 * @return {number}
113
 * @private
114
 */
115
function decodeSample_(nibble) {
116
    let difference = 0;
117
    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...
118
        difference += decoderStep_;
119
    }
120
    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...
121
        difference += decoderStep_ >> 1;
122
    }
123
    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...
124
        difference += decoderStep_ >> 2;
125
    }
126
    difference += decoderStep_ >> 3;
127
    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...
128
        difference = -difference;
129
    }
130
    decoderPredicted_ += difference;
131
    if (decoderPredicted_ > 32767) {
132
        decoderPredicted_ = 32767;
133
    } else if (decoderPredicted_ < -32767) {
134
        decoderPredicted_ = -32767;
135
    }
136
    decoderIndex_ += INDEX_TABLE[nibble];
137
    if (decoderIndex_ < 0) {
138
        decoderIndex_ = 0;
139
    } else if (decoderIndex_ > 88) {
140
        decoderIndex_ = 88;
141
    }
142
    decoderStep_ = STEP_TABLE[decoderIndex_];
143
    return decoderPredicted_;
144
}
145
146
/**
147
 * Return the head of a ADPCM sample block.
148
 * @param {number} sample The first sample of the block.
149
 * @return {!Array<number>}
150
 */
151
function blockHead_(sample) {
152
    encodeSample_(sample);
153
    let adpcmSamples = [];
154
    adpcmSamples.push(sample & 0xFF);
155
    adpcmSamples.push((sample >> 8) & 0xFF);
156
    adpcmSamples.push(encoderIndex_);
157
    adpcmSamples.push(0);
158
    return adpcmSamples;
159
}
160
161
/**
162
 * Encode a block of 505 16-bit samples as 4-bit ADPCM samples.
163
 * @param {!Array<number>} block A sample block of 505 samples.
164
 * @return {!Array<number>}
165
 */
166
function encodeBlock(block) {
167
    let adpcmSamples = blockHead_(block[0]);
168
    for (let i=3; i<block.length; i+=2) {
169
        let sample2 = encodeSample_(block[i]);
170
        let sample = encodeSample_(block[i + 1]);
171
        adpcmSamples.push((sample << 4) | sample2);
172
    }
173
    while (adpcmSamples.length < 256) {
174
        adpcmSamples.push(0);
175
    }
176
    return adpcmSamples;
177
}
178
179
/**
180
 * Decode a block of 256 ADPCM samples into 16-bit PCM samples.
181
 * @param {!Array<number>} block A adpcm sample block of 256 samples.
182
 * @return {!Array<number>}
183
 */
184
function decodeBlock(block) {
185
    decoderPredicted_ = sign_((block[1] << 8) | block[0]);
186
    decoderIndex_ = block[2];
187
    decoderStep_ = STEP_TABLE[decoderIndex_];
188
    let result = [
189
            decoderPredicted_,
190
            sign_((block[3] << 8) | block[2])
191
        ];
192
    for (let i=4; i<block.length; i++) {
193
        let original_sample = block[i];
194
        let second_sample = original_sample >> 4;
195
        let first_sample = (second_sample << 4) ^ original_sample;
196
        result.push(decodeSample_(first_sample));
197
        result.push(decodeSample_(second_sample));
198
    }
199
    return result;
200
}
201
202
/**
203
 * Encode 16-bit PCM samples into 4-bit IMA ADPCM samples.
204
 * @param {!Array<number>} samples A array of samples.
205
 * @return {!Array<number>}
206
 */
207
function encode(samples) {
208
    let adpcmSamples = [];
209
    let block = [];
210
    for (let i=0; i<samples.length; i++) {
211
        block.push(samples[i]);
212
        if ((i % 505 == 0 && i != 0) || i == samples.length - 1) {
213
            adpcmSamples = adpcmSamples.concat(encodeBlock(block));
214
            block = [];
215
        }
216
    }
217
    return adpcmSamples;
218
}
219
220
/**
221
 * Decode IMA ADPCM samples into 16-bit PCM samples.
222
 * @param {!Array<number>} adpcmSamples A array of ADPCM samples.
223
 * @param {number} blockAlign The block size.
224
 * @return {!Array<number>}
225
 */
226
function decode(adpcmSamples, blockAlign=256) {
227
    let samples = [];
228
    let block = [];
229
    for (let i=0; i<adpcmSamples.length; i++) {
230
        if (i % blockAlign == 0 && i != 0) {            
231
            samples = samples.concat(decodeBlock(block));
232
            block = [];
233
        }
234
        block.push(adpcmSamples[i]);
235
    }
236
    return samples;
237
}
238
239
module.exports.encode = encode;
240
module.exports.decode = decode;
241
module.exports.encodeBlock = encodeBlock;
242
module.exports.decodeBlock = decodeBlock;
243