Passed
Push — master ( 1f8f52...707aa7 )
by Rafael S.
01:06
created

index.js ➔ intToInt   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 3
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
/*!
2
 * bitdepth
3
 * Change the bit depth of samples to and from 8, 16, 24, 32 & 64-bit..
4
 * Copyright (c) 2017 Rafael da Silva Rocha.
5
 * https://github.com/rochars/bitdepth
6
 *
7
 */
8
9
/**
10
 * Max number of different values for each bit depth.
11
 * @enum {number}
12
 */
13
const BitDepthMaxValues = {
14
    8: 256,
15
    16: 65536,
16
    24: 16777216,
17
    32: 4294967296
18
};
19
20
/**
21
 * Change the bit depth of the data.
22
 * The input array is modified in-place.
23
 * @param {!Array<number>} samples The samples.
24
 * @param {string} originalBitDepth The original bit depth of the data.
25
 *      One of "8", "16", "24", "32", "32f", "64"
26
 * @param {string} targetBitDepth The new bit depth of the data.
27
 *      One of "8", "16", "24", "32", "32f", "64"
28
 */
29
function toBitDepth(samples, originalBitDepth, targetBitDepth) {
30
    validateBitDepths(originalBitDepth, targetBitDepth);
31
    let len = samples.length;
32
    for (let i=0; i<len; i++) {        
33
        let sample = samples[i];
34
        
35
        // 8-bit samples are unsigned;
36
        // They are signed here before conversion
37
        // (other bit depths are all signed)
38
        sample = sign8Bit(sample, originalBitDepth);
39
40
        // If it is a float-to-float or int-to-float conversion then
41
        // the samples in the target bit depth need to be normalized in the
42
        // -1.0 to 1.0 range; there is no need to multiply
43
        if (targetBitDepth == "32f" || targetBitDepth == "64") {
44
            sample = toFloat(sample, originalBitDepth);
45
46
        // If it is a float-to-int or int-to-int conversion then the
47
        // samples need to be de-normalized according to the bit depth
48
        } else {
49
            sample = toInt(sample, originalBitDepth, targetBitDepth);
50
        }
51
        samples[i] = sample;
52
    }
53
}
54
55
/**
56
 * Sign unsigned 8-bit data.
57
 * @param {number} sample The sample.
58
 * @param {string} originalBitDepth The original bit depth of the data.
59
 *      One of "8", "16", "24", "32", "32f", "64"
60
 */
61
function sign8Bit(sample, originalBitDepth) {
62
    if (originalBitDepth == "8") {
63
        sample -= 128;
64
    }
65
    return sample;
66
}
67
68
/**
69
 * Unsign signed 8-bit data.
70
 * @param {number} sample The sample.
71
 * @param {string} targetBitDepth The target bit depth of the data.
72
 *      One of "8", "16", "24", "32", "32f", "64"
73
 */
74
function unsign8Bit(sample, targetBitDepth) {
75
    if (targetBitDepth == "8") {
76
        sample += 128;
77
    }
78
    return sample;
79
}
80
81
/**
82
 * Change the bit depth of a sample to a new floating point bit depth.
83
 * The input array is modified in-place.
84
 * @param {number} sample The sample.
85
 * @param {string} originalBitDepth The original bit depth of the data.
86
 *      One of "8", "16", "24", "32", "32f", "64"
87
 */
88
function toFloat(sample, originalBitDepth) {
89
    let oldMaxValue = parseInt((BitDepthMaxValues[originalBitDepth]) / 2, 10);
90
    if (originalBitDepth != "32f" && originalBitDepth != "64") {
91
        if (sample > 0) {
92
            sample = sample / (oldMaxValue - 1);
93
        } else {
94
            sample = sample / oldMaxValue;
95
        }
96
    }
97
    return sample;
98
}
99
100
/**
101
 * Change the bit depth of a sample to a new integer bit depth.
102
 * @param {number} sample The sample.
103
 * @param {string} originalBitDepth The original bit depth of the data.
104
 *      One of "8", "16", "24", "32", "32f", "64"
105
 * @param {string} targetBitDepth The new bit depth of the data.
106
 *      One of "8", "16", "24", "32", "32f", "64"
107
 */
108
function toInt(sample, originalBitDepth, targetBitDepth) {
109
    // If the original samples are float, then they are already
110
    // normalized between -1.0 and 1.0; All that is need is to
111
    // multiply the sample values by the new bit depth max value
112
    let newMaxValue = parseInt((BitDepthMaxValues[targetBitDepth]) / 2, 10);
113
    if (originalBitDepth == "32f" || originalBitDepth == "64" ) {
114
        sample = floatToInt(sample, newMaxValue);
115
    // If the original samples are integers, then they need to be
116
    // divided by the maximum values of its original bit depth
117
    // (to normalize them between -1.0 and .10) and then multiplied
118
    // by the new bit depth max value
119
    } else {
120
        sample = intToInt(
121
                sample,
122
                parseInt((BitDepthMaxValues[originalBitDepth]) / 2, 10),
123
                newMaxValue
124
            );
125
    }
126
    // Make the samples unsigned if the target bit depth is "8"
127
    return unsign8Bit(sample, targetBitDepth);
128
}
129
130
/**
131
 * Perform a int-to-int conversion.
132
 * @param {number} sample The sample.
133
 * @param {number} oldMaxValue The max value for the original bit depth.
134
 * @param {number} newMaxValue The max value for the target bit depth.
135
 * @return {number}
136
 */
137
function intToInt(sample, oldMaxValue, newMaxValue) {
138
    if (sample > 0) {
139
        sample =
140
            parseInt((sample / (oldMaxValue - 1)) * newMaxValue - 1, 10);
141
    } else {
142
        sample = parseInt((sample / oldMaxValue) * newMaxValue, 10);
143
    }
144
    return sample;
145
}
146
147
/**
148
 * Perform a float-to-int conversion.
149
 * @param {number} sample The sample.
150
 * @param {number} newMaxValue The max value for the target bit depth.
151
 * @return {number}
152
 */
153
function floatToInt(sample, newMaxValue) {
154
    if (sample > 0) {
155
        sample = sample * (newMaxValue - 1);
156
    } else {
157
        sample = sample * newMaxValue;
158
    }
159
    return sample;
160
}
161
162
/**
163
 * Validate the bit depth.
164
 * @param {string} originalBitDepth The original bit depth.
165
 *     Should be one of "8", "16", "24", "32", "32f", "64".
166
 * @param {string} targetBitDepth The target bit depth.
167
 *     Should be one of "8", "16", "24", "32", "32f", "64".
168
 * @throws {Error} If any argument does not meet the criteria.
169
 */
170
function validateBitDepths(originalBitDepth, targetBitDepth) {
171
    let validBitDepths = ["8", "16", "24", "32", "32f", "64"];
172
    if (validBitDepths.indexOf(originalBitDepth) == -1 ||
173
        validBitDepths.indexOf(targetBitDepth) == -1) {
174
        throw new Error("Invalid bit depth.");
175
    }
176
    return true;
177
}
178
179
module.exports.toBitDepth = toBitDepth;
180
module.exports.BitDepthMaxValues = BitDepthMaxValues;
181