Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 081c9a0

Browse files
Version 2.4.0
- New Compressor2 class with optional sidechain input, adjustable knee and attack/HOLD/release. - Destruct method for the audio node.
1 parent 308c384 commit 081c9a0

File tree

10 files changed

+127
-21
lines changed

10 files changed

+127
-21
lines changed

‎docs.html‎

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ <h3>Web Audio</h3>
180180
// message is a standard JavaScript object.
181181
}
182182
);
183+
184+
// Will return false from the process() method of the AudioWorkletNode and run onDestruct to clean up WebAssembly and more.
185+
audioNode.destruct();
183186
</code></pre>
184187

185188
<h3>AudioWorkletProcessor</h3>
@@ -192,6 +195,10 @@ <h3>AudioWorkletProcessor</h3>
192195
onReady() {
193196
// Runs after the constructor. This is "your" constructor basically.
194197
}
198+
onDestruct() {
199+
// Runs before the node is destroyed.
200+
// Clean up memory and objects here (such as free allocated linear memory or destruct Superpowered objects).
201+
}
195202
onMessageFromMainScope(message) {
196203
// Runs when a message (data) is received from the main scope (main thread).
197204

@@ -1145,11 +1152,62 @@ <h3>Compressor</h3>
11451152
// It's never blocking for real-time usage. You can change all properties and call getGainReductionDb() on any thread, concurrently with process().
11461153
// If process() returns with true, the contents of output are replaced with the audio output. If process() returns with false, the contents of output are not changed.
11471154
let changedOutput = compressor.process(
1148-
input // Pointer to floating point numbers. 32-bit interleaved stereo input.
1155+
input, // Pointer to floating point numbers. 32-bit interleaved stereo input.
1156+
output, // Pointer to floating point numbers. 32-bit interleaved stereo output. Can point to the same location with input (in-place processing).
1157+
128 // Number of frames to process. Recommendations for best performance: multiply of 4, minimum 64.
1158+
);
1159+
1160+
// Destructor (to free up memory).
1161+
compressor.destruct();
1162+
</code></pre>
1163+
1164+
<h3>Compressor2</h3>
1165+
1166+
<p>Compressor with 0 latency, adjustable knee and optional sidechain input. It doesn't allocate any internal buffers and needs less than 1 kb of memory.</p>
1167+
1168+
<pre><code class="language-js">
1169+
// Constructor. Enabled is false by default.
1170+
let compressor = new Superpowered.Compressor2(
1171+
44100 // The initial sample rate in Hz.
1172+
);
1173+
1174+
// Do this when the sample rate changes.
1175+
compressor.samplerate = 48000;
1176+
1177+
// Turns the effect on/off. False by default. The actual switch will happen on the next process() call for smooth, audio-artifact free operation.
1178+
compressor.enabled = true;
1179+
1180+
compressor.attackSec = 0.01; // Attack in seconds (not milliseconds!). Limited between 0.00001 and 10. Default: 0.05 (50 ms).
1181+
compressor.holdSec = 0; // Hold segment before release starts, useful to limit unwanted noise with fast rates. 0 to 1 second (not millisecond). Default: 0.005 (5 ms).
1182+
compressor.releaseSec = 0.2; // Release in seconds (not milliseconds!). Limited between 0.00001 and 10. Default: 0.05 (50 ms).
1183+
compressor.ratio = 5; // Ratio, 1 to 1000. Default: 4.
1184+
compressor.thresholdDb = -16; // Threshold in decibels, limited between 0 and -60. Default: -6.
1185+
compressor.softKneeDb = 3 // Width of soft knee in decibels, 0 to 12. Default: 6.
1186+
compressor.outputGainDb = 3; // Output gain in decibels, limited between -24 and 24. Default: 0.
1187+
compressor.automaticGain = false; // If true, gain is set relative to compressor output at 0dB. Useful in digital environments. Default: true.
1188+
1189+
// Returns the maximum gain reduction in decibels since the last getGainReductionDb() call.
1190+
let gain_reduction_db = compressor.getGainReductionDb();
1191+
1192+
// Processes the audio. Always call it in the audio processing callback, regardless if the effect is enabled or not for smooth, audio-artifact free operation.
1193+
// It's never blocking for real-time usage. You can change all properties and call getGainReductionDb() on any thread, concurrently with process().
1194+
// If process() returns with true, the contents of output are replaced with the audio output. If process() returns with false, the contents of output are not changed.
1195+
let changedOutput = compressor.process(
1196+
input, // Pointer to floating point numbers. 32-bit interleaved stereo input.
11491197
output, // Pointer to floating point numbers. 32-bit interleaved stereo output. Can point to the same location with input (in-place processing).
11501198
128 // Number of frames to process. Recommendations for best performance: multiply of 4, minimum 64.
11511199
);
11521200

1201+
// Processes the audio. Always call it in the audio processing callback, regardless if the effect is enabled or not for smooth, audio-artifact free operation.
1202+
// It's never blocking for real-time usage. You can change all properties and call getGainReductionDb() on any thread, concurrently with process().
1203+
// If process() returns with true, the contents of output are replaced with the audio output. If process() returns with false, the contents of output are not changed.
1204+
let changedOutput = compressor.processWithSidechain(
1205+
input, // Pointer to floating point numbers. 32-bit interleaved stereo input.
1206+
sidechain, // Pointer to floating point numbers. 32-bit interleaved stereo sidechain input.
1207+
output, // Pointer to floating point numbers. 32-bit interleaved stereo output. Can point to the same location with input (in-place processing).
1208+
128 // Number of frames to process. Recommendations for best performance: multiply of 4, minimum 64.
1209+
);
1210+
11531211
// Destructor (to free up memory).
11541212
compressor.destruct();
11551213
</code></pre>

‎example_effects/superpowered/SuperpoweredWebAudio.js‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { SuperpoweredTrackLoader } from './SuperpoweredTrackLoaderModule.js';
44
var AudioWorkletHasBrokenModuleImplementation = false;
55

66
class SuperpoweredWebAudio {
7-
constructor(minimumSamplerate, superpowered) {
7+
constructor(minimumSamplerate, superpowered) {
88
AudioWorkletHasBrokenModuleImplementation = (navigator.userAgent.indexOf('AppleWebKit') > -1) || (navigator.userAgent.indexOf('Firefox') > -1);
99
if (AudioWorkletHasBrokenModuleImplementation && (navigator.userAgent.indexOf('Chrome') > -1)) AudioWorkletHasBrokenModuleImplementation = false;
1010
this.Superpowered = superpowered;
@@ -95,6 +95,7 @@ class SuperpoweredWebAudio {
9595
});
9696
}
9797
sendMessageToAudioScope(message, transfer = []) { this.port.postMessage(message, transfer); }
98+
destruct() { this.port.postMessage('___superpowered___destruct___'); }
9899
}
99100

100101
let node = new SuperpoweredNode(this, className);
@@ -115,6 +116,9 @@ class SuperpoweredWebAudio {
115116
node.outputBuffer = this.Superpowered.createFloatArray(1024 * 2);
116117
node.processor = new processorModule.default(this.Superpowered, onMessageFromAudioScope, node.samplerate);
117118
node.sendMessageToAudioScope = function(message, transfer = 0) { node.processor.onMessageFromMainScope(message); }
119+
node.destruct = function() {
120+
node.processor.onDestruct();
121+
}
118122
node.onaudioprocess = function(e) {
119123
node.processor.Superpowered.bufferToWASM(node.inputBuffer, e.inputBuffer);
120124
node.processor.processAudio(node.inputBuffer, node.outputBuffer, node.inputBuffer.array.length / 2);
@@ -131,8 +135,13 @@ if (!AudioWorkletHasBrokenModuleImplementation && (typeof AudioWorkletProcessor
131135
constructor(options) {
132136
super();
133137
SuperpoweredGlue.__uint_max__sp__ = options.processorOptions.maxChannels;
134-
this.port.onmessage = (event) => { this.onMessageFromMainScope(event.data); };
135-
this.ok = false;
138+
this.state = 0;
139+
this.port.onmessage = (event) => {
140+
if (event.data == '___superpowered___destruct___') {
141+
this.state = -1;
142+
this.onDestruct();
143+
} else this.onMessageFromMainScope(event.data);
144+
};
136145
this.samplerate = options.processorOptions.samplerate;
137146
this.Superpowered = new SuperpoweredGlue();
138147
this.Superpowered.loadFromArrayBuffer(options.processorOptions.wasmCode, this);
@@ -143,14 +152,16 @@ if (!AudioWorkletHasBrokenModuleImplementation && (typeof AudioWorkletProcessor
143152
this.outputBuffer = this.Superpowered.createFloatArray(128 * 2);
144153
this.onReady();
145154
this.port.postMessage('___superpowered___onready___');
146-
this.ok = true;
155+
this.state = 1;
147156
}
148157
onReady() {}
158+
onDestruct() {}
149159
onMessageFromMainScope(message) {}
150160
sendMessageToMainScope(message) { this.port.postMessage(message); }
151161
processAudio(buffer, parameters) {}
152162
process(inputs, outputs, parameters) {
153-
if (this.ok) {
163+
if (this.state < 0) return false;
164+
if (this.state == 1) {
154165
if (inputs[0].length > 1) this.Superpowered.bufferToWASM(this.inputBuffer, inputs);
155166
this.processAudio(this.inputBuffer, this.outputBuffer, this.inputBuffer.array.length / 2, parameters);
156167
if (outputs[0].length > 1) this.Superpowered.bufferToJS(this.outputBuffer, outputs);
@@ -170,6 +181,7 @@ if (!AudioWorkletHasBrokenModuleImplementation && (typeof AudioWorkletProcessor
170181
}
171182
onMessageFromAudioScope = null;
172183
onReady() {}
184+
onDestruct() {}
173185
onMessageFromMainScope(message) {}
174186
sendMessageToMainScope(message) { if (!this.loader.onmessage({ data: message })) this.onMessageFromAudioScope(message); }
175187
postMessage(message, transfer = []) { this.onMessageFromMainScope(message); }
7.83 KB
Binary file not shown.

‎example_guitardistortion/superpowered/SuperpoweredWebAudio.js‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { SuperpoweredTrackLoader } from './SuperpoweredTrackLoaderModule.js';
44
var AudioWorkletHasBrokenModuleImplementation = false;
55

66
class SuperpoweredWebAudio {
7-
constructor(minimumSamplerate, superpowered) {
7+
constructor(minimumSamplerate, superpowered) {
88
AudioWorkletHasBrokenModuleImplementation = (navigator.userAgent.indexOf('AppleWebKit') > -1) || (navigator.userAgent.indexOf('Firefox') > -1);
99
if (AudioWorkletHasBrokenModuleImplementation && (navigator.userAgent.indexOf('Chrome') > -1)) AudioWorkletHasBrokenModuleImplementation = false;
1010
this.Superpowered = superpowered;
@@ -95,6 +95,7 @@ class SuperpoweredWebAudio {
9595
});
9696
}
9797
sendMessageToAudioScope(message, transfer = []) { this.port.postMessage(message, transfer); }
98+
destruct() { this.port.postMessage('___superpowered___destruct___'); }
9899
}
99100

100101
let node = new SuperpoweredNode(this, className);
@@ -115,6 +116,9 @@ class SuperpoweredWebAudio {
115116
node.outputBuffer = this.Superpowered.createFloatArray(1024 * 2);
116117
node.processor = new processorModule.default(this.Superpowered, onMessageFromAudioScope, node.samplerate);
117118
node.sendMessageToAudioScope = function(message, transfer = 0) { node.processor.onMessageFromMainScope(message); }
119+
node.destruct = function() {
120+
node.processor.onDestruct();
121+
}
118122
node.onaudioprocess = function(e) {
119123
node.processor.Superpowered.bufferToWASM(node.inputBuffer, e.inputBuffer);
120124
node.processor.processAudio(node.inputBuffer, node.outputBuffer, node.inputBuffer.array.length / 2);
@@ -131,8 +135,13 @@ if (!AudioWorkletHasBrokenModuleImplementation && (typeof AudioWorkletProcessor
131135
constructor(options) {
132136
super();
133137
SuperpoweredGlue.__uint_max__sp__ = options.processorOptions.maxChannels;
134-
this.port.onmessage = (event) => { this.onMessageFromMainScope(event.data); };
135-
this.ok = false;
138+
this.state = 0;
139+
this.port.onmessage = (event) => {
140+
if (event.data == '___superpowered___destruct___') {
141+
this.state = -1;
142+
this.onDestruct();
143+
} else this.onMessageFromMainScope(event.data);
144+
};
136145
this.samplerate = options.processorOptions.samplerate;
137146
this.Superpowered = new SuperpoweredGlue();
138147
this.Superpowered.loadFromArrayBuffer(options.processorOptions.wasmCode, this);
@@ -143,14 +152,16 @@ if (!AudioWorkletHasBrokenModuleImplementation && (typeof AudioWorkletProcessor
143152
this.outputBuffer = this.Superpowered.createFloatArray(128 * 2);
144153
this.onReady();
145154
this.port.postMessage('___superpowered___onready___');
146-
this.ok = true;
155+
this.state = 1;
147156
}
148157
onReady() {}
158+
onDestruct() {}
149159
onMessageFromMainScope(message) {}
150160
sendMessageToMainScope(message) { this.port.postMessage(message); }
151161
processAudio(buffer, parameters) {}
152162
process(inputs, outputs, parameters) {
153-
if (this.ok) {
163+
if (this.state < 0) return false;
164+
if (this.state == 1) {
154165
if (inputs[0].length > 1) this.Superpowered.bufferToWASM(this.inputBuffer, inputs);
155166
this.processAudio(this.inputBuffer, this.outputBuffer, this.inputBuffer.array.length / 2, parameters);
156167
if (outputs[0].length > 1) this.Superpowered.bufferToJS(this.outputBuffer, outputs);
@@ -170,6 +181,7 @@ if (!AudioWorkletHasBrokenModuleImplementation && (typeof AudioWorkletProcessor
170181
}
171182
onMessageFromAudioScope = null;
172183
onReady() {}
184+
onDestruct() {}
173185
onMessageFromMainScope(message) {}
174186
sendMessageToMainScope(message) { if (!this.loader.onmessage({ data: message })) this.onMessageFromAudioScope(message); }
175187
postMessage(message, transfer = []) { this.onMessageFromMainScope(message); }
7.83 KB
Binary file not shown.

‎example_timestretching/superpowered/SuperpoweredWebAudio.js‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { SuperpoweredTrackLoader } from './SuperpoweredTrackLoaderModule.js';
44
var AudioWorkletHasBrokenModuleImplementation = false;
55

66
class SuperpoweredWebAudio {
7-
constructor(minimumSamplerate, superpowered) {
7+
constructor(minimumSamplerate, superpowered) {
88
AudioWorkletHasBrokenModuleImplementation = (navigator.userAgent.indexOf('AppleWebKit') > -1) || (navigator.userAgent.indexOf('Firefox') > -1);
99
if (AudioWorkletHasBrokenModuleImplementation && (navigator.userAgent.indexOf('Chrome') > -1)) AudioWorkletHasBrokenModuleImplementation = false;
1010
this.Superpowered = superpowered;
@@ -95,6 +95,7 @@ class SuperpoweredWebAudio {
9595
});
9696
}
9797
sendMessageToAudioScope(message, transfer = []) { this.port.postMessage(message, transfer); }
98+
destruct() { this.port.postMessage('___superpowered___destruct___'); }
9899
}
99100

100101
let node = new SuperpoweredNode(this, className);
@@ -115,6 +116,9 @@ class SuperpoweredWebAudio {
115116
node.outputBuffer = this.Superpowered.createFloatArray(1024 * 2);
116117
node.processor = new processorModule.default(this.Superpowered, onMessageFromAudioScope, node.samplerate);
117118
node.sendMessageToAudioScope = function(message, transfer = 0) { node.processor.onMessageFromMainScope(message); }
119+
node.destruct = function() {
120+
node.processor.onDestruct();
121+
}
118122
node.onaudioprocess = function(e) {
119123
node.processor.Superpowered.bufferToWASM(node.inputBuffer, e.inputBuffer);
120124
node.processor.processAudio(node.inputBuffer, node.outputBuffer, node.inputBuffer.array.length / 2);
@@ -131,8 +135,13 @@ if (!AudioWorkletHasBrokenModuleImplementation && (typeof AudioWorkletProcessor
131135
constructor(options) {
132136
super();
133137
SuperpoweredGlue.__uint_max__sp__ = options.processorOptions.maxChannels;
134-
this.port.onmessage = (event) => { this.onMessageFromMainScope(event.data); };
135-
this.ok = false;
138+
this.state = 0;
139+
this.port.onmessage = (event) => {
140+
if (event.data == '___superpowered___destruct___') {
141+
this.state = -1;
142+
this.onDestruct();
143+
} else this.onMessageFromMainScope(event.data);
144+
};
136145
this.samplerate = options.processorOptions.samplerate;
137146
this.Superpowered = new SuperpoweredGlue();
138147
this.Superpowered.loadFromArrayBuffer(options.processorOptions.wasmCode, this);
@@ -143,14 +152,16 @@ if (!AudioWorkletHasBrokenModuleImplementation && (typeof AudioWorkletProcessor
143152
this.outputBuffer = this.Superpowered.createFloatArray(128 * 2);
144153
this.onReady();
145154
this.port.postMessage('___superpowered___onready___');
146-
this.ok = true;
155+
this.state = 1;
147156
}
148157
onReady() {}
158+
onDestruct() {}
149159
onMessageFromMainScope(message) {}
150160
sendMessageToMainScope(message) { this.port.postMessage(message); }
151161
processAudio(buffer, parameters) {}
152162
process(inputs, outputs, parameters) {
153-
if (this.ok) {
163+
if (this.state < 0) return false;
164+
if (this.state == 1) {
154165
if (inputs[0].length > 1) this.Superpowered.bufferToWASM(this.inputBuffer, inputs);
155166
this.processAudio(this.inputBuffer, this.outputBuffer, this.inputBuffer.array.length / 2, parameters);
156167
if (outputs[0].length > 1) this.Superpowered.bufferToJS(this.outputBuffer, outputs);
@@ -170,6 +181,7 @@ if (!AudioWorkletHasBrokenModuleImplementation && (typeof AudioWorkletProcessor
170181
}
171182
onMessageFromAudioScope = null;
172183
onReady() {}
184+
onDestruct() {}
173185
onMessageFromMainScope(message) {}
174186
sendMessageToMainScope(message) { if (!this.loader.onmessage({ data: message })) this.onMessageFromAudioScope(message); }
175187
postMessage(message, transfer = []) { this.onMessageFromMainScope(message); }
7.83 KB
Binary file not shown.

‎superpowered.bc‎

9.5 KB
Binary file not shown.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /