Passed
Push — master ( 19921a...7df886 )
by Rafael S.
02:41
created

index.js ➔ changeBitDepth   B

Complexity

Conditions 7

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 20
dl 0
loc 34
rs 8
c 0
b 0
f 0
1
/*
2
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview The changeBitdepth() function.
27
 * @see https://github.com/rochars/bitdepth
28
 */
29
30
/** @module bitdepth */
31
32
/** @private */
33
const f64f32_ = new Float32Array(1);
34
35
/**
36
 * Change the bit depth of samples.
37
 * @param {!TypedArray} input The samples.
38
 * @param {string} original The original bit depth of the data.
39
 *      One of "8" ... "53", "32f", "64"
40
 * @param {string} target The desired bit depth for the data.
41
 *      One of "8" ... "53", "32f", "64"
42
 * @param {!TypedArray} output The output array.
43
 */
44
export function changeBitDepth(input, original, target, output) {
45
  validateBitDepth_(original);
46
  validateBitDepth_(target);
47
  /** @type {!Function} */
48
  let toFunction = getBitDepthFunction_(original, target);
49
  /** @type {!Object<string, number>} */
50
  let options = {
51
    oldMin: Math.pow(2, parseInt(original, 10)) / 2,
52
    newMin: Math.pow(2, parseInt(target, 10)) / 2,
53
    oldMax: (Math.pow(2, parseInt(original, 10)) / 2) - 1,
54
    newMax: (Math.pow(2, parseInt(target, 10)) / 2) - 1,
55
  };
56
  /** @type {number} */
57
  const len = input.length;
58
  // sign the samples if original is 8-bit
59
  if (original == "8") {
60
    for (let i=0; i<len; i++) {
61
      output[i] = input[i] -= 128;
62
    }
63
  }
64
  if (original == "32f" || original == "64") {
65
    truncateSamples(input);
66
  }
67
  // change the resolution of the samples
68
  for (let i=0; i<len; i++) {        
69
    output[i] = toFunction(input[i], options);
70
  }
71
  // unsign the samples if target is 8-bit
72
  if (target == "8") {
73
    for (let i=0; i<len; i++) {
74
      output[i] = output[i] += 128;
75
    }
76
  }
77
}
78
79
/**
80
 * Change the bit depth from int to int.
81
 * @param {number} sample The sample.
82
 * @param {!Object<string, number>} args Data about the original and target bit depths.
83
 * @return {number}
84
 * @private
85
 */
86
function intToInt_(sample, args) {
87
  if (sample > 0) {
88
    sample = parseInt((sample / args.oldMax) * args.newMax, 10);
89
  } else {
90
    sample = parseInt((sample / args.oldMin) * args.newMin, 10);
91
  }
92
  return sample;
93
}
94
95
/**
96
 * Change the bit depth from float to int.
97
 * @param {number} sample The sample.
98
 * @param {!Object<string, number>} args Data about the original and target bit depths.
99
 * @return {number}
100
 * @private
101
 */
102
function floatToInt_(sample, args) {
103
  return parseInt(
104
    sample > 0 ? sample * args.newMax : sample * args.newMin, 10);
105
}
106
107
/**
108
 * Change the bit depth from int to float.
109
 * @param {number} sample The sample.
110
 * @param {!Object<string, number>} args Data about the original and target bit depths.
111
 * @return {number}
112
 * @private
113
 */
114
function intToFloat_(sample, args) {
115
  return sample > 0 ? sample / args.oldMax : sample / args.oldMin;
116
}
117
118
/**
119
 * Change the bit depth from float to float.
120
 * @param {number} sample The sample.
121
 * @return {number}
122
 * @private
123
 */
124
function floatToFloat_(sample) {
125
  f64f32_[0] = sample;
126
  return f64f32_[0];
127
}
128
129
/**
130
 * Return the function to change the bit depth of a sample.
131
 * @param {string} original The original bit depth of the data.
132
 *      One of "8" ... "53", "32f", "64"
133
 * @param {string} target The new bit depth of the data.
134
 *      One of "8" ... "53", "32f", "64"
135
 * @return {!Function}
136
 * @private
137
 */
138
function getBitDepthFunction_(original, target) {
139
  /** @type {!Function} */
140
  let func = function(x) {return x;};
141
  if (original != target) {
142
    if (["32f", "64"].includes(original)) {
143
      if (["32f", "64"].includes(target)) {
144
        func = floatToFloat_;
145
      } else {
146
        func = floatToInt_;
147
      }
148
    } else {
149
      if (["32f", "64"].includes(target)) {
150
        func = intToFloat_;
151
      } else {
152
        func = intToInt_;
153
      }
154
    }
155
  }
156
  return func;
157
}
158
159
/**
160
 * Validate the bit depth.
161
 * @param {string} bitDepth The original bit depth.
162
 *     Should be one of "8" ... "53", "32f" or "64".
163
 * @throws {Error} If any argument does not meet the criteria.
164
 * @private
165
 */
166
function validateBitDepth_(bitDepth) {
167
  if ((bitDepth != "32f" && bitDepth != "64") &&
168
      (parseInt(bitDepth, 10) < "8" || parseInt(bitDepth, 10) > "53")) {
169
    throw new Error("Invalid bit depth.");
170
  }
171
}
172
173
/**
174
 * Truncate float samples on overflow.
175
 * @private
176
 */
177
function truncateSamples(samples) {
178
  /** @type {number} */   
179
  let len = samples.length;
180
  for (let i=0; i<len; i++) {
181
    if (samples[i] > 1) {
182
      samples[i] = 1;
183
    } else if (samples[i] < -1) {
184
      samples[i] = -1;
185
    }
186
  }
187
}
188