Last active
February 3, 2022 00:15
-
-
Save automata/5832104 to your computer and use it in GitHub Desktop.
emscripten + audio data api
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
to get it working, install Emscripten: | |
https://github.com/kripken/emscripten/wiki/Tutorial | |
(on Ubuntu: https://github.com/kripken/emscripten/wiki/Getting-Started-on-Ubuntu-12.10) | |
set all the env variables and run build.sh. | |
open sin_audiodata.html in your Firefox. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
emcc sin.cpp -o sin.js \ | |
-s EXPORTED_FUNCTIONS="['_Sin_constructor','_Sin_destructor','_Sin_setFrequency','_Sin_setAmplitude','_Sin_getFrequency','_Sin_computeBuffer']" | |
cat sin-proxy.js >> sin.js |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// get references to the exposed proxy functions | |
var Sin_constructor = Module.cwrap('Sin_constructor', 'number', ['number', 'number']); | |
var Sin_destructor = Module.cwrap('Sin_destructor', null, ['number']); | |
var Sin_setFrequency = Module.cwrap('Sin_setFrequency', null, ['number', 'number']); | |
var Sin_setAmplitude = Module.cwrap('Sin_setAmplitude', null, ['number', 'number']); | |
var Sin_getFrequency = Module.cwrap('Sin_getFrequency', 'number', ['number']); | |
var Sin_computeBuffer = Module.cwrap('Sin_computeBuffer', null, ['number', 'number', 'number', 'number']); | |
Sin = function (freq, amp) { | |
that = {}; | |
that.ptr = Sin_constructor(freq, amp); | |
that.destroy = function () { | |
Sin_destructor(that.ptr); | |
}; | |
that.setFrequency = function (x) { | |
Sin_setFrequency(that.ptr, x); | |
}; | |
that.setAmplitude = function (x) { | |
Sin_setAmplitude(that.ptr, x); | |
}; | |
that.getFrequency = function () { | |
return Sin_getFrequency(that.ptr); | |
}; | |
that.computeBuffer = function (buffer, size, samplePos) { | |
Sin_computeBuffer(that.ptr, buffer, size, samplePos); | |
}; | |
return that; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <new> | |
#include <cmath> | |
#include <cstdlib> | |
static const float PI = 3.14159265358979f; | |
using namespace std; | |
class Sin { | |
public: | |
float amplitude, frequency, sampleRate; | |
Sin (float frequencyV, float amplitudeV) { | |
this->frequency = frequencyV; | |
this->amplitude = amplitudeV; | |
this->sampleRate = 44100; | |
} | |
~Sin() { | |
} | |
void setFrequency(float v) { | |
this->frequency = v; | |
} | |
void setAmplitude(float v) { | |
this->amplitude = v; | |
} | |
float getFrequency() { | |
return this->frequency; | |
} | |
void computeBuffer(float *buffer, int size, int samplePos) { | |
int i; | |
float k = 2 * PI * this->frequency / this->sampleRate; | |
for (i=0; i<size; i++) { | |
buffer[i] = sin(k * samplePos++); | |
} | |
} | |
}; | |
//compile using "C" linkage to avoid name obfuscation | |
extern "C" { | |
//constructor, returns a pointer to the HelloWorld object | |
void *Sin_constructor(float freq, float amp) { | |
Sin* obj = new Sin(freq, amp); | |
return obj; | |
} | |
void Sin_setFrequency(Sin *s, float freq) { | |
s->setFrequency(freq); | |
} | |
void Sin_setAmplitude(Sin *s, float amp) { | |
s->setAmplitude(amp); | |
} | |
float Sin_getFrequency(Sin *s) { | |
return s->getFrequency(); | |
} | |
void Sin_computeBuffer(Sin *s, float *buffer, int size, int samplePos) { | |
s->computeBuffer(buffer, size, samplePos); | |
} | |
void Sin_destructor(Sin *s) { | |
delete s; | |
} | |
}; | |
// main() { | |
// float *buf; | |
// Sin s (220.0, 1.5); | |
// s.setFrequency(440.0); | |
// s.computeBuffer(); | |
// buf = s.buffer; | |
// cout << buf[1] << endl; | |
// return 0; | |
// } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title> sin </title> | |
<script src="sin.js"></script> | |
</head> | |
<body> | |
<input type="text" size="4" id="freq" value="220"><label for="hz">Hz</label> | |
<button onclick="start()">play</button> | |
<button onclick="stop()">stop</button> | |
<script type="text/javascript"> | |
function AudioDataDestination(sampleRate, readFn) { | |
var audio = new Audio(); | |
audio.mozSetup(1, sampleRate); | |
var currentWritePosition = 0; | |
var prebufferSize = sampleRate / 2; // buffer 500ms | |
var tail = null, tailPosition; | |
setInterval(function() { | |
var written; | |
// Check if some data was not written in previous attempts. | |
if(tail) { | |
written = audio.mozWriteAudio(tail.subarray(tailPosition)); | |
currentWritePosition += written; | |
tailPosition += written; | |
if(tailPosition < tail.length) { | |
// Not all the data was written, saving the tail... | |
return; // ... and exit the function. | |
} | |
tail = null; | |
} | |
// Check if we need add some data to the audio output. | |
var currentPosition = audio.mozCurrentSampleOffset(); | |
var available = currentPosition + prebufferSize - currentWritePosition; | |
if(available > 0) { | |
// Request some sound data from the callback function. | |
var soundData = new Float32Array(available); | |
readFn(soundData); | |
// Writting the data. | |
written = audio.mozWriteAudio(soundData); | |
if(written < soundData.length) { | |
// Not all the data was written, saving the tail. | |
tail = soundData; | |
tailPosition = written; | |
} | |
currentWritePosition += written; | |
} | |
}, 100); | |
} | |
// Control and generate the sound. | |
var frequency = 0, currentSoundSample; | |
var sampleRate = 44100; | |
function requestSoundData(soundData) { | |
if (!frequency) { | |
return; // no sound selected | |
} | |
// get the length of the data in bytes | |
var bufferBytes = soundData.length * soundData.BYTES_PER_ELEMENT; | |
// malloc enough space for the data | |
var bufferPtr = _malloc(bufferBytes); | |
// get a bytes-wise view on the newly allocated buffer | |
var heapBytes= new Uint8Array(Module.HEAPU8.buffer, bufferPtr, bufferBytes); | |
// copy data into heapBytes | |
heapBytes.set(new Uint8Array(soundData.buffer)); | |
// call the c function which should modify the vals | |
s = Sin(frequency, 1.0); | |
s.computeBuffer(heapBytes.byteOffset, soundData.length, currentSoundSample); | |
// print out the results of the c function | |
var heapFloats= new Float32Array(heapBytes.buffer, heapBytes.byteOffset, | |
soundData.length); | |
for (var i=0, size=soundData.length; i<size; i++) { | |
soundData[i] = heapFloats[i]; | |
} | |
currentSoundSample += soundData.length; | |
_free(heapBytes.byteOffset); | |
} | |
var audioDestination = new AudioDataDestination(sampleRate, requestSoundData); | |
function start() { | |
currentSoundSample = 0; | |
frequency = parseFloat(document.getElementById("freq").value); | |
} | |
function stop() { | |
frequency = 0; | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment