-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
118 lines (114 loc) · 4.34 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
* Copyright (c) 2018-2019 Rafael da Silva Rocha.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/**
* @fileoverview Function to create multi-stage frequency sweeps.
* @see https://github.com/rochars/frequency-sweep
*/
/**
* Waveform functions.
* @private
*/
const WAVES = {
noise: function() {
return Math.random() * (1 - (-1)) + (-1);
},
sine: function(t, delta, phase, start, end, phi0) {
phase = 2 * Math.PI * t * (start + (end - start) * delta / 2);
return Math.sin(phase + phi0);
},
triangle: function(t, delta, phase, start, end, phi0) {
phase = 2 * Math.PI * t * (start + (end - start) * delta / 2);
return (2 / Math.PI) * Math.asin(Math.sin(phase + phi0));
},
sawtooth: function(t, delta, phase, start, end, phi0) {
phase = 2 * Math.PI * t * (start + (end - start) * (delta / 2)) / 2;
return (2 / Math.PI) * Math.atan(Math.tan(phase + phi0));
},
square: function(t, delta, phase, start, end, phi0) {
phase = 2 * Math.PI * t * (start + (end - start) * delta / 2);
return Math.sign(Math.sin(phase + phi0));
}
};
/**
* Return the samples of a frequency sweep. The sweep may be divided
* in segments, each using a different waveform (or noise), and each with
* a start and end frequency.
* @param {!Array<Object<string, string|number>>} sequence The sequence.
* Each item in the array must have the properties:
* start: Integer value, the start frequency of the segment.
* end: Integer value, the end frequency of the segment.
* time: Float value, the duration of the segment. 1 = 1 second.
* wave: String, "sine", "square", "triangle", "sawtooth" or "noise".
* @param {number} sampleRate The sample rate.
* @param {?boolean} outputTyped True to return Float64Array, false for Array.
* @return {!Array<number>|!Float64Array}
*/
export function sweep(sequence, sampleRate, outputTyped=false) {
/** @type {number} */
let phi0 = 0;
/** @type {number} */
let phase = 0;
/** @type {number} */
let numSamples = 0;
/** @type {number} */
let duration = 0;
/** @type {number} */
let delta = 0;
/** @type {!Array<number>|!Float64Array} */
let samples = getOutputObject_(sequence, sampleRate, outputTyped);
for (let i = 0, x = 0; i < sequence.length; i++) {
numSamples = Math.round(sampleRate * sequence[i].time);
duration = numSamples / sampleRate;
/** @type {number} */
let limit = numSamples + x;
for (; x < limit; x++) {
delta = x / numSamples;
samples[x] = WAVES[sequence[i].wave](
duration*delta, delta, phase, sequence[i].start, sequence[i].end, phi0);
}
phase = 2 * Math.PI * duration *
(sequence[i].start + (sequence[i].end - sequence[i].start) / 2);
phi0 = phi0 + phase;
}
return samples;
}
/**
* Return the output object, Array or Float64Array.
* @param {!Array<Object<string, string|number>>} sequence The sequence.
* @param {number} sampleRate The sample rate.
* @param {boolean|null} outputTyped True for Float64Array, false for Array.
* @return {!Array<number>|!Float64Array}
* @private
*/
function getOutputObject_(sequence, sampleRate, outputTyped) {
if (outputTyped) {
/** @type {number} */
let numSamples = 0;
for (let i = 0; i < sequence.length; i++) {
numSamples += Math.round(sampleRate * sequence[i].time);
}
return new Float64Array(numSamples);
}
return [];
}