Passed
Push — master ( 13b350...038bd4 )
by Rafael S.
03:46
created

bitdepth.js ➔ sign8Bit_   A

Complexity

Conditions 4

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
dl 0
loc 8
rs 10
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 A module to change the bit depth of PCM samples.
27
 * @see https://github.com/rochars/wavefile
28
 * @see https://github.com/rochars/bitdepth
29
 */
30
31
/**
32
 * Change the bit depth of PCM samples.
33
 * @param {!Array|!TypedArray} samples The original samples.
34
 * @param {string} bithDepth The original bit depth.
35
 * @param {!TypedArray} newSamples The output array.
36
 * @param {string} targetBitDepth The target bit depth.
37
 * @throws {Error} If original or target bit depths are not valid.
38
 */
39
export function changeBitDepth(samples, bithDepth, newSamples, targetBitDepth) {
40
  // float to float, just copy the values
41
  if (["32f","64"].indexOf(bithDepth) > -1 &&
42
    ["32f","64"].indexOf(targetBitDepth) > -1) {
43
    newSamples.set(samples);
44
    return;
45
  }
46
  validateBitDepth_(bithDepth);
47
  validateBitDepth_(targetBitDepth);
48
  /** @type {!Function} */
49
  let toFunction = getBitDepthFunction_(bithDepth, targetBitDepth);
50
  /** @type {!Object<string, number>} */
51
  let options = {
52
    oldMin: Math.pow(2, parseInt(bithDepth, 10)) / 2,
53
    newMin: Math.pow(2, parseInt(targetBitDepth, 10)) / 2,
54
    oldMax: (Math.pow(2, parseInt(bithDepth, 10)) / 2) - 1,
55
    newMax: (Math.pow(2, parseInt(targetBitDepth, 10)) / 2) - 1,
56
  };
57
  // sign the samples if original is 8-bit
58
  sign8Bit_(bithDepth, samples, true);
59
  // change the resolution of the samples
60
  for (let i = 0, len = samples.length; i < len; i++) {        
61
    newSamples[i] = toFunction(samples[i], options);
62
  }
63
  // unsign the samples if target is 8-bit
64
  sign8Bit_(targetBitDepth, newSamples, false);
65
}
66
67
/**
68
 * Change the bit depth from int to int.
69
 * @param {number} sample The sample.
70
 * @param {!Object<string, number>} args Data about the bit depths.
71
 * @return {number}
72
 * @private
73
 */
74
function intToInt_(sample, args) {
75
  if (sample > 0) {
76
    sample = parseInt((sample / args.oldMax) * args.newMax, 10);
77
  } else {
78
    sample = parseInt((sample / args.oldMin) * args.newMin, 10);
79
  }
80
  return sample;
81
}
82
83
/**
84
 * Change the bit depth from float to int.
85
 * @param {number} sample The sample.
86
 * @param {!Object<string, number>} args Data about the bit depths.
87
 * @return {number}
88
 * @private
89
 */
90
function floatToInt_(sample, args) {
91
  return parseInt(
92
    sample > 0 ? sample * args.newMax : sample * args.newMin, 10);
93
}
94
95
/**
96
 * Change the bit depth from int to float.
97
 * @param {number} sample The sample.
98
 * @param {!Object<string, number>} args Data about the bit depths.
99
 * @return {number}
100
 * @private
101
 */
102
function intToFloat_(sample, args) {
103
  return sample > 0 ? sample / args.oldMax : sample / args.oldMin;
104
}
105
106
/**
107
 * Return the function to change the bit depth of a sample.
108
 * @param {string} original The original bit depth of the data.
109
 * @param {string} target The new bit depth of the data.
110
 * @return {!Function}
111
 * @private
112
 */
113
function getBitDepthFunction_(original, target) {
114
  /** @type {!Function} */
115
  let func = function(x) {return x;};
116
  if (original != target) {
117
    if (["32f", "64"].includes(original)) {
118
      func = floatToInt_;
119
    } else {
120
      if (["32f", "64"].includes(target)) {
121
        func = intToFloat_;
122
      } else {
123
        func = intToInt_;
124
      }
125
    }
126
  }
127
  return func;
128
}
129
130
/**
131
 * Validate the bit depth.
132
 * @param {string} bitDepth The original bit depth.
133
 * @throws {Error} If bit depth is not valid.
134
 * @private
135
 */
136
function validateBitDepth_(bitDepth) {
137
  if ((bitDepth != "32f" && bitDepth != "64") &&
138
      (parseInt(bitDepth, 10) < "8" || parseInt(bitDepth, 10) > "53")) {
139
    throw new Error("Invalid bit depth.");
140
  }
141
}
142
143
/**
144
 * Sign samples if they are 8-bit.
145
 * @param {string} bitDepth The bit depth code.
146
 * @param {!Array|!TypedArray} samples The samples.
147
 * @param {boolean} sign True to sign, false to unsign.
148
 * @private
149
 */
150
function sign8Bit_(bitDepth, samples, sign) {
151
  if (bitDepth == "8") {
152
    let factor = sign ? -128 : 128;
153
    for (let i = 0, len = samples.length; i < len; i++) {
154
      samples[i] = samples[i] += factor;
155
    }
156
  }
157
}
158