Passed
Push — master ( 45c9ce...ea3e0e )
by Rafael S.
53s
created

index.js ➔ sign8Bit   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
/*!
2
 * bitdepth
3
 * Change the bit depth of audio samples to and from 8, 16, 24, 32, 32 IEEE & 64-bit.
4
 * Copyright (c) 2017 Rafael da Silva Rocha. MIT License.
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>} data The data.
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(data, originalBitDepth, targetBitDepth) {
30
    if (originalBitDepth == targetBitDepth) {
31
        return;
32
    }
33
    validateBitDepths(originalBitDepth, targetBitDepth);
34
    let len = data.length;
35
36
    for (let i=0; i<len; i++) {        
37
        // 8-bit samples are unsigned;
38
        // They are signed here before conversion
39
        // (other bit depths are all signed)
40
        data[i] = sign8Bit(data[i], originalBitDepth);
41
42
        // If it is a float-to-float or int-to-float conversion then
43
        // the samples in the target bit depth will be normalized in the
44
        // -1.0 to 1.0 range; there is no need to multiply
45
        if (targetBitDepth == "32f" || targetBitDepth == "64") {
46
            data[i] = toFloat(data[i], originalBitDepth);
47
48
        // If it is a float-to-int or int-to-int conversion then the
49
        // samples will be de-normalized according to the bit depth
50
        }else {
51
            data[i] = toInt(data[i], originalBitDepth, targetBitDepth);
52
        }  
53
    }
54
}
55
56
/**
57
 * Sign unsigned 8-bit data.
58
 * @param {number} sample The sample.
59
 * @param {string} originalBitDepth The original bit depth of the data.
60
 *      One of "8", "16", "24", "32", "32f", "64"
61
 */
62
function sign8Bit(sample, originalBitDepth) {
63
    if (originalBitDepth == "8") {
64
        sample -= 128;
65
    }
66
    return sample;
67
}
68
69
/**
70
 * Unsign signed 8-bit data.
71
 * @param {number} sample The sample.
72
 * @param {string} targetBitDepth The target bit depth of the data.
73
 *      One of "8", "16", "24", "32", "32f", "64"
74
 */
75
function unsign8Bit(sample, targetBitDepth) {
76
    if (targetBitDepth == "8") {
77
        sample += 128;
78
    }
79
    return sample;
80
}
81
82
/**
83
 * Change the bit depth from int to float.
84
 * The input array is modified in-place.
85
 * @param {number} sample The sample.
86
 * @param {string} originalBitDepth The original bit depth of the data.
87
 *      One of "8", "16", "24", "32", "32f", "64"
88
 */
89
function toFloat(sample, originalBitDepth) {
90
    let oldMaxValue = parseInt((BitDepthMaxValues[originalBitDepth]) / 2, 10);
91
    if (originalBitDepth != "32f" && originalBitDepth != "64") {
92
        if (sample > 0) {
93
            sample = sample / (oldMaxValue - 1);
94
        } else {
95
            sample = sample / oldMaxValue;
96
        }
97
    }
98
    return sample;
99
}
100
101
/**
102
 * Change the bit depth of the data.
103
 * The input array is modified in-place.
104
 * @param {number} sample The sample.
105
 * @param {string} originalBitDepth The original bit depth of the data.
106
 *      One of "8", "16", "24", "32", "32f", "64"
107
 * @param {string} targetBitDepth The new bit depth of the data.
108
 *      One of "8", "16", "24", "32", "32f", "64"
109
 */
110
function toInt(sample, originalBitDepth, targetBitDepth) {
111
    // If the original samples are float, then they are already
112
    // normalized between -1.0 and 1.0; All that is need is to
113
    // multiply the sample values by the new bit depth max value
114
    let oldMaxValue = parseInt((BitDepthMaxValues[originalBitDepth]) / 2, 10);
115
    let newMaxValue = parseInt((BitDepthMaxValues[targetBitDepth]) / 2, 10);
116
    if (originalBitDepth == "32f" || originalBitDepth == "64" ) {
117
        if (sample > 0) {
118
            sample = sample * (newMaxValue - 1);
119
        } else {
120
            sample = sample * newMaxValue;
121
        }
122
123
    // If the original samples are integers, then they need to be
124
    // divided by the maximum values of its original bit depth
125
    // (to normalize them between -1.0 and .10) and then multiply
126
    // them by the new bit depth max value
127
    } else {
128
        if (sample > 0) {
129
            sample =
130
                parseInt((sample / (oldMaxValue - 1)) * newMaxValue - 1, 10);
131
        } else {
132
            sample = parseInt((sample / oldMaxValue) * newMaxValue, 10);
133
        }
134
    }
135
    
136
    // Make the samples unsigned if the target bit depth is "8"
137
    return unsign8Bit(sample, targetBitDepth);
138
}
139
140
/**
141
 * Validate the bit depth.
142
 * @param {string} originalBitDepth The original bit depth.
143
 *     Should be one of "8", "16", "24", "32", "32f", "64".
144
 * @param {string} targetBitDepth The target bit depth.
145
 *     Should be one of "8", "16", "24", "32", "32f", "64".
146
 * @throws {Error} If any argument does not meet the criteria.
147
 */
148
function validateBitDepths(originalBitDepth, targetBitDepth) {
149
    let validBitDepths = ["8", "16", "24", "32", "32f", "64"];
150
    if (validBitDepths.indexOf(originalBitDepth) == -1 ||
151
        validBitDepths.indexOf(targetBitDepth) == -1) {
152
        throw new Error("Invalid bit depth.");
153
    }
154
    return true;
155
}
156
157
module.exports.toBitDepth = toBitDepth;
158
module.exports.BitDepthMaxValues = BitDepthMaxValues;
159