Passed
Push — master ( 8ed4b0...19921a )
by Rafael S.
01:30
created

index.js ➔ truncateSamples   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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