Skip to content

Superpowered v2.6.7 #43

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 3 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions dist/Superpowered.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* eslint-disable */
// @ts-nocheck

class SuperpoweredGlue {

static wasmCDNUrl = "https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@2.6.6/dist/superpowered-npm.wasm"
static wasmCDNUrl = "https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@2.6.7/dist/superpowered-npm.wasm"

niceSize(bytes) {
if (bytes == 0) return '0 byte'; else if (bytes == 1) return '1 byte';
Expand Down Expand Up @@ -748,7 +749,7 @@ class SuperpoweredWebAudio {
}.bind(node);
});
} else {
import(/* webpackIgnore: true */ url).then((processorModule) => {
import(/* webpackIgnore: true */ /* viteIgnore: true */ url).then((processorModule) => {
const node = this.audioContext.createScriptProcessor(1024, 2, 2);
node.trackLoaderID = this.Superpowered.registerTrackLoader(node);
node.samplerate = this.audioContext.sampleRate;
Expand Down
Binary file modified dist/superpowered-npm.wasm
Binary file not shown.
Binary file modified dist/superpowered.wasm
Binary file not shown.
5 changes: 3 additions & 2 deletions examples/example_effects/Superpowered.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* eslint-disable */
// @ts-nocheck

class SuperpoweredGlue {

static wasmCDNUrl = "https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@2.6.6/dist/superpowered-npm.wasm"
static wasmCDNUrl = "https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@2.6.7/dist/superpowered-npm.wasm"

niceSize(bytes) {
if (bytes == 0) return '0 byte'; else if (bytes == 1) return '1 byte';
Expand Down Expand Up @@ -748,7 +749,7 @@ class SuperpoweredWebAudio {
}.bind(node);
});
} else {
import(/* webpackIgnore: true */ url).then((processorModule) => {
import(/* webpackIgnore: true */ /* viteIgnore: true */ url).then((processorModule) => {
const node = this.audioContext.createScriptProcessor(1024, 2, 2);
node.trackLoaderID = this.Superpowered.registerTrackLoader(node);
node.samplerate = this.audioContext.sampleRate;
Expand Down
5 changes: 3 additions & 2 deletions examples/example_guitardistortion/Superpowered.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* eslint-disable */
// @ts-nocheck

class SuperpoweredGlue {

static wasmCDNUrl = "https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@2.6.6/dist/superpowered-npm.wasm"
static wasmCDNUrl = "https://cdn.jsdelivr.net/npm/@superpoweredsdk/web@2.6.7/dist/superpowered-npm.wasm"

niceSize(bytes) {
if (bytes == 0) return '0 byte'; else if (bytes == 1) return '1 byte';
Expand Down Expand Up @@ -748,7 +749,7 @@ class SuperpoweredWebAudio {
}.bind(node);
});
} else {
import(/* webpackIgnore: true */ url).then((processorModule) => {
import(/* webpackIgnore: true */ /* viteIgnore: true */ url).then((processorModule) => {
const node = this.audioContext.createScriptProcessor(1024, 2, 2);
node.trackLoaderID = this.Superpowered.registerTrackLoader(node);
node.samplerate = this.audioContext.sampleRate;
Expand Down
869 changes: 869 additions & 0 deletions examples/example_pitchbend/assets/Superpowered.js

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions examples/example_pitchbend/assets/processor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import "./Superpowered.js";

class MyProcessor extends SuperpoweredWebAudio.AudioWorkletProcessor {
cancelledPitchBend = true;

onReady() {
this.player = new this.Superpowered.AdvancedAudioPlayer(this.samplerate, 2, 2, 0, 0.501, 2, false);
}

onDestruct() {
this.player.destruct();
}

onMessageFromMainScope(message) {
if (message.SuperpoweredLoaded) {
this.player.openMemory(this.Superpowered.arrayBufferToWASM(message.SuperpoweredLoaded.buffer), false, false);
this.player.play();
this.sendMessageToMainScope({ loaded: true });
}
if (typeof message.rate !== 'undefined') this.player.playbackRate = message.rate / 10000.0;
if (typeof message.pitchShift !== 'undefined') this.player.pitchShiftCents = parseInt(message.pitchShift) * 100;
if (typeof message.requestPitchBend !== 'undefined') this.sendMessageToMainScope({ pitchBendDetails: {currentPitchBend: this.currentPitchBend, currentPitchBendMsOffset: this.currentPitchBendMsOffset} })
if (message.pitchBend) this.pitchBend = message.maxPercent !== 0 ? {
maxPercent: message.maxPercent,
bendStretch: message.bendStretch,
faster: message.faster,
holdMs: message.holdMs
} : undefined;
}

processAudio(inputBuffer, outputBuffer, buffersize, parameters) {
if (this.pitchBend) {
this.player.pitchBend(this.pitchBend.maxPercent, this.pitchBend.bendStretch, this.pitchBend.faster, this.pitchBend.holdMs);
} else if (!this.cancelledPitchBend) {
this.player.endContinuousPitchBend();
}
this.cancelledPitchBend = !this.cancelledPitchBend;
this.currentPitchBend = this.player.getCurrentPitchBendPercent();
this.currentPitchBendMsOffset = this.player.getBendOffsetMs();
if (!this.player.processStereo(outputBuffer.pointer, false, buffersize, 1)) this.Superpowered.memorySet(outputBuffer.pointer, 0, buffersize * 8);
}
}

if (typeof AudioWorkletProcessor === 'function') registerProcessor('MyProcessor', MyProcessor);
export default MyProcessor;
Binary file not shown.
14 changes: 14 additions & 0 deletions examples/example_pitchbend/assets/superpowered.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/example_pitchbend/assets/track.mp3
Binary file not shown.
20 changes: 20 additions & 0 deletions examples/example_pitchbend/assets/wave.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions examples/example_pitchbend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Superpowered WebAssembly Audio Player Time Stretching and Pitch Shifting Example</title>
<script type="module" src="./polyfill_worklet_import.js"></script>
<link href="/style.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<img class="logo" src="/assets/superpowered.svg" />
<div class="controls">
<h2>AAP pitch/speed controls</h2>
<p>A demonstration of pitch bend, pitch shift and playback rate changes with the Advanced Audio Player class</p>
<div id="content">Initializing...</div>
</div>
</div>

<script src="main.js" type="module"></script>
</body>
</html>
163 changes: 163 additions & 0 deletions examples/example_pitchbend/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import "./assets/Superpowered.js";

var webaudioManager = null; // The SuperpoweredWebAudio helper class managing Web Audio for us.
var Superpowered = null; // A Superpowered instance.
var audioNode = null; // This example uses one audio node only.
var content = null; // The <div> displaying everything.
var pitchShift = 0; // The current pitch shift value.
var currentPath = null;
var pbPerc = null;

function changePitchShift(e) {
// limiting the new pitch shift value
// let value = parseInt(e.target.value);
// pitchShift += value;

pitchShift = Math.min(12, Math.max(-12, pitchShift + parseInt(e.target.value)));

// if (pitchShift < -12) pitchShift = -12; else if (pitchShift > 12) pitchShift = 12;
// displaying the value
document.getElementById('pitch-shift-display').textContent = ' pitch shift: ' + ((pitchShift < 1) ? pitchShift : '+' + pitchShift) + ' ';
// sending the new value to the audio node
audioNode.sendMessageToAudioScope({ 'pitchShift': pitchShift });
}

// on change by the rate slider
function changeRate() {
// displaying the new rate
let value = document.getElementById('rateSlider').value, text;
if (value == 10000) text = 'original tempo';
else if (value < 10000) text = '-' + (100 - value / 100).toPrecision(2) + '%';
else text = '+' + (value / 100 - 100).toPrecision(2) + '%';
document.getElementById('rateDisplay').textContent = text;
// sending the new rate to the audio node
audioNode.sendMessageToAudioScope({ rate: value });
}

function changePitchBend(e) {
console.log(Number(document.getElementById('holdMsSelect').value));
const value = e.target.value;
audioNode.sendMessageToAudioScope({
'pitchBend': true,
maxPercent: Math.abs(value/100),
bendStretch: 0,
faster: value < 0 ? 0 : 1,
holdMs: Number(document.getElementById('holdMsSelect').value)
});
}

// double click on the rate slider
function changeRateDbl() {
document.getElementById('rateSlider').value = 10000;
changeRate();
}

// double click on the rate slider
function changeBendDbl() {
document.getElementById('pitchBend').value = 0;
audioNode.sendMessageToAudioScope({
'pitchBend': true,
maxPercent: 0,
bendStretch: 0,
faster: 0,
holdMs: Number(document.getElementById('holdMsSelect').value)
});
}

// click on play/pause
function togglePlayback(e) {
let button = document.getElementById('playPause');
if (button.value == 1) {
button.value = 0;
button.textContent = 'Play audio';
webaudioManager.audioContext.suspend();
} else {
button.value = 1;
button.textContent = 'Pause audio';
webaudioManager.audioContext.resume();
}
}

function onMessageFromAudioScope(message) {
if (message.loaded) {
// UI: innerHTML may be ugly but keeps this example small
content.innerHTML = '\
<button id="playPause" value="0">Play audio</button>\
<h3>Pitch bend holdMs</h3>\
<select id="holdMsSelect"><option>40</option><option>200</option><option>300</option><option>600</option><option>1000</option></select><span>ms</span>\
<h3>Pitch bend percentage</h3>\
<div style="width: 100%; display: flex; justify-content: space-between;"><span>-30%</span><span>0%</span><span>+30%</span></div>\
<input id="pitchBend" type="range" min="-30" max="30" value="0" style="width: 100%">\
<div style="overflow: hidden; border-radius: 5px; background: #909090; width: 100%; postion: relative;" id="bend-container"><div style="width: 50%; height: 10px; background: black;" id="bend-value"></div></div>\
<div style="text-align: center;"><span><span id="pitch-bend-percentage">100</span>%</span></div><br />\
<button id="reset-bend">Reset pitch bend</button>\
<h3>Playback rate</h3>\
<span id="rateDisplay">original tempo</span>\
<div style="width: 100%; display: flex; justify-content: space-between;"><span>-50%</span><span>+100%</span></div>\
<input id="rateSlider" type="range" min="5000" max="20000" value="10000" style="width: 100%">\
<button id="reset-rate">Reset playback rate</button> <br /><br />\
<div>\
<button id="pitchMinus" value="-1">-</button>\
<span id="pitch-shift-display"> pitch shift: 0 </span>\
<button id="pitchPlus" value="1">+</button>\
</div>\
';
document.getElementById('rateSlider').addEventListener('input', changeRate);
document.getElementById('pitchBend').addEventListener('input', changePitchBend);
document.getElementById('pitchBend').addEventListener('dblclick', changeBendDbl);
document.getElementById('rateSlider').addEventListener('dblclick', changeRateDbl);
document.getElementById('reset-bend').addEventListener('click', changeBendDbl);
document.getElementById('reset-rate').addEventListener('click', changeRateDbl);
document.getElementById('pitchMinus').addEventListener('click', changePitchShift);
document.getElementById('pitchPlus').addEventListener('click', changePitchShift);
document.getElementById('playPause').addEventListener('click', togglePlayback);
pbPerc = document.getElementById('pitch-bend-percentage');
}
if (message.pitchBendDetails && document.getElementById('bend-value')) {
if (pbPerc && (typeof message.pitchBendDetails.currentPitchBend !== 'undefined')) {
pbPerc.textContent = message.pitchBendDetails.currentPitchBend * 100;
document.getElementById('bend-value').style.width = convertRange(message.pitchBendDetails.currentPitchBend * 100, [70, 130], [0, 100]) + '%';
document.getElementById('bend-value').style.background = message.pitchBendDetails.currentPitchBend === 1 ? 'black' : message.pitchBendDetails.currentPitchBend < 1 ? 'red' : 'green';
}
}
}

function convertRange( value, r1, r2 ) {
return ( value - r1[ 0 ] ) * ( r2[ 1 ] - r2[ 0 ] ) / ( r1[ 1 ] - r1[ 0 ] ) + r2[ 0 ];
}

function requestPitchBendDetails() {
audioNode.sendMessageToAudioScope({ requestPitchBend: true });
requestAnimationFrame(requestPitchBendDetails)
}

// when the START button is clicked
async function start() {
webaudioManager = new SuperpoweredWebAudio(44100, Superpowered);
currentPath = window.location.href.substring(0, window.location.href.lastIndexOf('/'));
audioNode = await webaudioManager.createAudioNodeAsync(window.location.href + '/assets/processor.js?date=' + Date.now(), 'MyProcessor', onMessageFromAudioScope);
// audioNode -> audioContext.destination (audio output)
webaudioManager.audioContext.suspend();
audioNode.connect(webaudioManager.audioContext.destination);

// start polling of pitch bend details from audioworklet
requestAnimationFrame(requestPitchBendDetails)
}

async function loadFromMainThread() {
Superpowered.downloadAndDecode(window.location.href + '/assets/track.mp3', audioNode);
}

async function loadJS() {
Superpowered = await SuperpoweredGlue.Instantiate('ExampleLicenseKey-WillExpire-OnNextUpdate', `${window.location.href}/assets/superpowered-npm.wasm`);

// display the START button
content = document.getElementById('content');
content.innerHTML = `<div>
<button id="startApplication">Start</button>
</div>`;
document.getElementById('startApplication').addEventListener('click', loadFromMainThread);
start();
}

loadJS();
Loading