Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
RishiRecon
GitHub Repository: RishiRecon/exploits
Path: blob/main/misc/emulator/gba/user_scripts/XAudioJS/resampler.js
28798 views
1
//JavaScript Audio Resampler (c) 2011 - Grant Galitz
2
function Resampler(fromSampleRate, toSampleRate, channels, outputBufferSize, noReturn) {
3
this.fromSampleRate = fromSampleRate;
4
this.toSampleRate = toSampleRate;
5
this.channels = channels | 0;
6
this.outputBufferSize = outputBufferSize;
7
this.noReturn = !!noReturn;
8
this.initialize();
9
}
10
Resampler.prototype.initialize = function () {
11
//Perform some checks:
12
if (this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0) {
13
if (this.fromSampleRate == this.toSampleRate) {
14
//Setup a resampler bypass:
15
this.resampler = this.bypassResampler; //Resampler just returns what was passed through.
16
this.ratioWeight = 1;
17
}
18
else {
19
this.ratioWeight = this.fromSampleRate / this.toSampleRate;
20
if (this.fromSampleRate < this.toSampleRate) {
21
/*
22
Use generic linear interpolation if upsampling,
23
as linear interpolation produces a gradient that we want
24
and works fine with two input sample points per output in this case.
25
*/
26
this.compileLinearInterpolationFunction();
27
this.lastWeight = 1;
28
}
29
else {
30
/*
31
Custom resampler I wrote that doesn't skip samples
32
like standard linear interpolation in high downsampling.
33
This is more accurate than linear interpolation on downsampling.
34
*/
35
this.compileMultiTapFunction();
36
this.tailExists = false;
37
this.lastWeight = 0;
38
}
39
this.initializeBuffers();
40
}
41
}
42
else {
43
throw(new Error("Invalid settings specified for the resampler."));
44
}
45
}
46
Resampler.prototype.compileLinearInterpolationFunction = function () {
47
var toCompile = "var bufferLength = buffer.length;\
48
var outLength = this.outputBufferSize;\
49
if ((bufferLength % " + this.channels + ") == 0) {\
50
if (bufferLength > 0) {\
51
var weight = this.lastWeight;\
52
var firstWeight = 0;\
53
var secondWeight = 0;\
54
var sourceOffset = 0;\
55
var outputOffset = 0;\
56
var outputBuffer = this.outputBuffer;\
57
for (; weight < 1; weight += " + this.ratioWeight + ") {\
58
secondWeight = weight % 1;\
59
firstWeight = 1 - secondWeight;";
60
for (var channel = 0; channel < this.channels; ++channel) {
61
toCompile += "outputBuffer[outputOffset++] = (this.lastOutput[" + channel + "] * firstWeight) + (buffer[" + channel + "] * secondWeight);";
62
}
63
toCompile += "}\
64
weight -= 1;\
65
for (bufferLength -= " + this.channels + ", sourceOffset = Math.floor(weight) * " + this.channels + "; outputOffset < outLength && sourceOffset < bufferLength;) {\
66
secondWeight = weight % 1;\
67
firstWeight = 1 - secondWeight;";
68
for (var channel = 0; channel < this.channels; ++channel) {
69
toCompile += "outputBuffer[outputOffset++] = (buffer[sourceOffset" + ((channel > 0) ? (" + " + channel) : "") + "] * firstWeight) + (buffer[sourceOffset + " + (this.channels + channel) + "] * secondWeight);";
70
}
71
toCompile += "weight += " + this.ratioWeight + ";\
72
sourceOffset = Math.floor(weight) * " + this.channels + ";\
73
}";
74
for (var channel = 0; channel < this.channels; ++channel) {
75
toCompile += "this.lastOutput[" + channel + "] = buffer[sourceOffset++];";
76
}
77
toCompile += "this.lastWeight = weight % 1;\
78
return this.bufferSlice(outputOffset);\
79
}\
80
else {\
81
return (this.noReturn) ? 0 : [];\
82
}\
83
}\
84
else {\
85
throw(new Error(\"Buffer was of incorrect sample length.\"));\
86
}";
87
this.resampler = Function("buffer", toCompile);
88
}
89
Resampler.prototype.compileMultiTapFunction = function () {
90
var toCompile = "var bufferLength = buffer.length;\
91
var outLength = this.outputBufferSize;\
92
if ((bufferLength % " + this.channels + ") == 0) {\
93
if (bufferLength > 0) {\
94
var weight = 0;";
95
for (var channel = 0; channel < this.channels; ++channel) {
96
toCompile += "var output" + channel + " = 0;"
97
}
98
toCompile += "var actualPosition = 0;\
99
var amountToNext = 0;\
100
var alreadyProcessedTail = !this.tailExists;\
101
this.tailExists = false;\
102
var outputBuffer = this.outputBuffer;\
103
var outputOffset = 0;\
104
var currentPosition = 0;\
105
do {\
106
if (alreadyProcessedTail) {\
107
weight = " + this.ratioWeight + ";";
108
for (channel = 0; channel < this.channels; ++channel) {
109
toCompile += "output" + channel + " = 0;"
110
}
111
toCompile += "}\
112
else {\
113
weight = this.lastWeight;";
114
for (channel = 0; channel < this.channels; ++channel) {
115
toCompile += "output" + channel + " = this.lastOutput[" + channel + "];"
116
}
117
toCompile += "alreadyProcessedTail = true;\
118
}\
119
while (weight > 0 && actualPosition < bufferLength) {\
120
amountToNext = 1 + actualPosition - currentPosition;\
121
if (weight >= amountToNext) {";
122
for (channel = 0; channel < this.channels; ++channel) {
123
toCompile += "output" + channel + " += buffer[actualPosition++] * amountToNext;"
124
}
125
toCompile += "currentPosition = actualPosition;\
126
weight -= amountToNext;\
127
}\
128
else {";
129
for (channel = 0; channel < this.channels; ++channel) {
130
toCompile += "output" + channel + " += buffer[actualPosition" + ((channel > 0) ? (" + " + channel) : "") + "] * weight;"
131
}
132
toCompile += "currentPosition += weight;\
133
weight = 0;\
134
break;\
135
}\
136
}\
137
if (weight <= 0) {";
138
for (channel = 0; channel < this.channels; ++channel) {
139
toCompile += "outputBuffer[outputOffset++] = output" + channel + " / " + this.ratioWeight + ";"
140
}
141
toCompile += "}\
142
else {\
143
this.lastWeight = weight;";
144
for (channel = 0; channel < this.channels; ++channel) {
145
toCompile += "this.lastOutput[" + channel + "] = output" + channel + ";"
146
}
147
toCompile += "this.tailExists = true;\
148
break;\
149
}\
150
} while (actualPosition < bufferLength && outputOffset < outLength);\
151
return this.bufferSlice(outputOffset);\
152
}\
153
else {\
154
return (this.noReturn) ? 0 : [];\
155
}\
156
}\
157
else {\
158
throw(new Error(\"Buffer was of incorrect sample length.\"));\
159
}";
160
this.resampler = Function("buffer", toCompile);
161
}
162
Resampler.prototype.bypassResampler = function (buffer) {
163
if (this.noReturn) {
164
//Set the buffer passed as our own, as we don't need to resample it:
165
this.outputBuffer = buffer;
166
return buffer.length;
167
}
168
else {
169
//Just return the buffer passsed:
170
return buffer;
171
}
172
}
173
Resampler.prototype.bufferSlice = function (sliceAmount) {
174
if (this.noReturn) {
175
//If we're going to access the properties directly from this object:
176
return sliceAmount;
177
}
178
else {
179
//Typed array and normal array buffer section referencing:
180
try {
181
return this.outputBuffer.subarray(0, sliceAmount);
182
}
183
catch (error) {
184
try {
185
//Regular array pass:
186
this.outputBuffer.length = sliceAmount;
187
return this.outputBuffer;
188
}
189
catch (error) {
190
//Nightly Firefox 4 used to have the subarray function named as slice:
191
return this.outputBuffer.slice(0, sliceAmount);
192
}
193
}
194
}
195
}
196
Resampler.prototype.initializeBuffers = function () {
197
//Initialize the internal buffer:
198
try {
199
this.outputBuffer = new Float32Array(this.outputBufferSize);
200
this.lastOutput = new Float32Array(this.channels);
201
}
202
catch (error) {
203
this.outputBuffer = [];
204
this.lastOutput = [];
205
}
206
}
207