mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-02-07 01:11:22 +08:00
Javascript bindings (#14)
* Initial version ready - bindings are in `bindings/emscripten.cpp` - minimal Javascript example is in `examples/ggwave-js` * add npm package + add test-ggwave.js * js : rename export name to "ggwave_factory" * update to v0.1.5 * Update README.md * npm : add npm-publish target
This commit is contained in:
@@ -6,7 +6,14 @@ if (GGWAVE_SUPPORT_SDL2)
|
||||
# SDL2
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
set(CMAKE_CXX_FLAGS "-s TOTAL_MEMORY=67108864 -s USE_SDL=2 -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0 -s 'EXTRA_EXPORTED_RUNTIME_METHODS=[\"writeArrayToMemory\"]'")
|
||||
set(CMAKE_CXX_FLAGS " \
|
||||
--bind \
|
||||
-s TOTAL_MEMORY=67108864 \
|
||||
-s USE_SDL=2 \
|
||||
-s ASSERTIONS=1 \
|
||||
-s DISABLE_EXCEPTION_CATCHING=0 \
|
||||
-s 'EXTRA_EXPORTED_RUNTIME_METHODS=[\"writeArrayToMemory\"]' \
|
||||
")
|
||||
|
||||
unset(SDL2_INCLUDE_DIRS)
|
||||
unset(SDL2_LIBRARIES)
|
||||
@@ -65,19 +72,23 @@ endif()
|
||||
|
||||
# examples
|
||||
|
||||
add_subdirectory(ggwave-to-file)
|
||||
if (EMSCRIPTEN)
|
||||
add_subdirectory(ggwave-js)
|
||||
else()
|
||||
add_subdirectory(ggwave-to-file)
|
||||
endif()
|
||||
|
||||
if (GGWAVE_SUPPORT_SDL2)
|
||||
if (EMSCRIPTEN)
|
||||
# emscripten sdl2 examples
|
||||
|
||||
add_subdirectory(ggwave-wasm)
|
||||
add_subdirectory(waver)
|
||||
else()
|
||||
# non-emscripten sdl2 examples
|
||||
|
||||
add_subdirectory(ggwave-rx)
|
||||
add_subdirectory(ggwave-cli)
|
||||
add_subdirectory(waver)
|
||||
endif()
|
||||
|
||||
add_subdirectory(waver)
|
||||
endif()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <chrono>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include "emscripten/emscripten.h"
|
||||
#include <emscripten.h>
|
||||
#else
|
||||
#define EMSCRIPTEN_KEEPALIVE
|
||||
#endif
|
||||
|
||||
3
examples/ggwave-js/CMakeLists.txt
Normal file
3
examples/ggwave-js/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
set(TARGET ggwave-js)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/index-tmpl.html ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET}/index.html @ONLY)
|
||||
180
examples/ggwave-js/index-tmpl.html
Normal file
180
examples/ggwave-js/index-tmpl.html
Normal file
@@ -0,0 +1,180 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<title>ggwave : javascript example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-container">
|
||||
Minimal <b>ggwave</b> example using Javascript bindings
|
||||
|
||||
<br><br>
|
||||
|
||||
<div>Tx Data:</div> <textarea name="textarea" id="txData" style="width:300px;height:100px;">Hello javascript</textarea><br>
|
||||
|
||||
<button onclick="onSend();">Send</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<div>Rx data:</div> <textarea name="textarea" id="rxData" style="width:300px;height:100px;" disabled></textarea><br>
|
||||
|
||||
<button id="captureStart">Start capturing</button>
|
||||
<button id="captureStop" hidden>Stop capturing</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<div class="cell-version">
|
||||
<span>
|
||||
|
|
||||
Build time: <span class="nav-link">@GIT_DATE@</span> |
|
||||
Commit hash: <a class="nav-link" href="https://github.com/ggerganov/ggwave/commit/@GIT_SHA1@">@GIT_SHA1@</a> |
|
||||
Commit subject: <span class="nav-link">@GIT_COMMIT_SUBJECT@</span> |
|
||||
<a class="nav-link" href="https://github.com/ggerganov/ggwave/tree/master/examples/ggwave-js">Source Code</a> |
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="ggwave.js"></script>
|
||||
<script type='text/javascript'>
|
||||
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
window.OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
|
||||
|
||||
var context = null;
|
||||
var recorder = null;
|
||||
|
||||
// the ggwave module instance
|
||||
var ggwave = null;
|
||||
var parameters = null;
|
||||
var instance = null;
|
||||
|
||||
// instantiate the ggwave instance
|
||||
// ggwave_factory comes from the ggwave.js module
|
||||
ggwave_factory().then(function(obj) {
|
||||
ggwave = obj;
|
||||
});
|
||||
|
||||
var txData = document.getElementById("txData");
|
||||
var rxData = document.getElementById("rxData");
|
||||
var captureStart = document.getElementById("captureStart");
|
||||
var captureStop = document.getElementById("captureStop");
|
||||
|
||||
// helper function
|
||||
function convertTypedArray(src, type) {
|
||||
var buffer = new ArrayBuffer(src.byteLength);
|
||||
var baseView = new src.constructor(buffer).set(src);
|
||||
return new type(buffer);
|
||||
}
|
||||
|
||||
// initialize audio context and ggwave
|
||||
function init() {
|
||||
if (!context) {
|
||||
// todo : query ggwave's base sample rate
|
||||
context = new AudioContext({sampleRate: 48000});
|
||||
|
||||
parameters = ggwave.getDefaultParameters();
|
||||
parameters.sampleRateOut = context.sampleRate;
|
||||
instance = ggwave.init(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Tx
|
||||
//
|
||||
|
||||
function onSend() {
|
||||
init();
|
||||
|
||||
// pause audio capture during transmission
|
||||
captureStop.click();
|
||||
|
||||
// generate audio waveform
|
||||
var waveform = ggwave.encode(instance, txData.value, ggwave.TxProtocolId.GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, 10)
|
||||
|
||||
// play audio
|
||||
var buf = convertTypedArray(waveform, Float32Array);
|
||||
var buffer = context.createBuffer(1, buf.length, context.sampleRate);
|
||||
buffer.getChannelData(0).set(buf);
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.connect(context.destination);
|
||||
source.start(0);
|
||||
}
|
||||
|
||||
//
|
||||
// Rx
|
||||
//
|
||||
|
||||
captureStart.addEventListener("click", function () {
|
||||
init();
|
||||
|
||||
let constraints = {
|
||||
audio: {
|
||||
// not sure if these are necessary to have
|
||||
echoCancellation: false,
|
||||
autoGainControl: false,
|
||||
noiseSuppression: false
|
||||
}
|
||||
};
|
||||
|
||||
navigator.mediaDevices.getUserMedia(constraints).then(function (e) {
|
||||
mediaStream = context.createMediaStreamSource(e);
|
||||
|
||||
var bufferSize = 16*1024;
|
||||
var numberOfInputChannels = 1;
|
||||
var numberOfOutputChannels = 1;
|
||||
|
||||
if (context.createScriptProcessor) {
|
||||
recorder = context.createScriptProcessor(
|
||||
bufferSize,
|
||||
numberOfInputChannels,
|
||||
numberOfOutputChannels);
|
||||
} else {
|
||||
recorder = context.createJavaScriptNode(
|
||||
bufferSize,
|
||||
numberOfInputChannels,
|
||||
numberOfOutputChannels);
|
||||
}
|
||||
|
||||
recorder.onaudioprocess = function (e) {
|
||||
var source = e.inputBuffer;
|
||||
var offlineCtx = new OfflineAudioContext(source.numberOfChannels, 48000*source.duration, 48000);
|
||||
var offlineSource = offlineCtx.createBufferSource();
|
||||
|
||||
offlineSource.buffer = source;
|
||||
offlineSource.connect(offlineCtx.destination);
|
||||
offlineSource.start();
|
||||
offlineCtx.startRendering();
|
||||
offlineCtx.oncomplete = function(e) {
|
||||
var resampled = e.renderedBuffer.getChannelData(0);
|
||||
var res = ggwave.decode(instance, convertTypedArray(new Float32Array(resampled), Int8Array));
|
||||
if (res) {
|
||||
rxData.value = res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mediaStream.connect(recorder);
|
||||
recorder.connect(context.destination);
|
||||
}).catch(function (e) {
|
||||
console.error(e);
|
||||
});
|
||||
|
||||
rxData.value = 'Listening ...';
|
||||
captureStart.hidden = true;
|
||||
captureStop.hidden = false;
|
||||
});
|
||||
|
||||
captureStop.addEventListener("click", function () {
|
||||
if (recorder) {
|
||||
recorder.disconnect(context.destination);
|
||||
mediaStream.disconnect(recorder);
|
||||
}
|
||||
|
||||
rxData.value = 'Audio capture is paused! Press the "Start capturing" button to analyze audio from the microphone';
|
||||
captureStart.hidden = false;
|
||||
captureStop.hidden = true;
|
||||
});
|
||||
|
||||
captureStop.click();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -67,7 +67,6 @@ int main(int argc, char** argv) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
|
||||
fprintf(stderr, "Generating waveform for message '%s' ...\n", message.c_str());
|
||||
|
||||
GGWave ggWave({ GGWave::kBaseSampleRate, sampleRateOut, 1024, GGWAVE_SAMPLE_FORMAT_F32, GGWAVE_SAMPLE_FORMAT_I16 });
|
||||
|
||||
Reference in New Issue
Block a user