mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-03-31 01:26:49 +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:
@@ -1,8 +1,10 @@
|
|||||||
cmake_minimum_required (VERSION 3.0)
|
cmake_minimum_required (VERSION 3.0)
|
||||||
project(ggwave VERSION 0.1.4)
|
project(ggwave VERSION 0.1.5)
|
||||||
|
|
||||||
|
# configure project version
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/README.md.tmpl ${CMAKE_SOURCE_DIR}/README.md @ONLY)
|
configure_file(${CMAKE_SOURCE_DIR}/README.md.tmpl ${CMAKE_SOURCE_DIR}/README.md @ONLY)
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/bindings/python/setup.py.tmpl ${CMAKE_SOURCE_DIR}/bindings/python/setup.py @ONLY)
|
configure_file(${CMAKE_SOURCE_DIR}/bindings/python/setup.py.tmpl ${CMAKE_SOURCE_DIR}/bindings/python/setup.py @ONLY)
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/bindings/javascript/package-tmpl.json ${CMAKE_SOURCE_DIR}/bindings/javascript/package.json @ONLY)
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS "on")
|
set(CMAKE_EXPORT_COMPILE_COMMANDS "on")
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
@@ -75,48 +77,7 @@ if (GGWAVE_ALL_WARNINGS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
add_subdirectory(bindings)
|
||||||
if (GGWAVE_SUPPORT_PYTHON)
|
|
||||||
file(GLOB_RECURSE GGWAVE_SOURCES "include/*" "src/*")
|
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${CMAKE_SOURCE_DIR}/bindings/python/ggwave.bycython.cpp
|
|
||||||
OUTPUT ${CMAKE_SOURCE_DIR}/bindings/python/ggwave
|
|
||||||
DEPENDS ${CMAKE_SOURCE_DIR}/bindings/python/Makefile
|
|
||||||
DEPENDS ${CMAKE_SOURCE_DIR}/bindings/python/ggwave.pyx
|
|
||||||
DEPENDS ${CMAKE_SOURCE_DIR}/bindings/python/cggwave.pxd
|
|
||||||
DEPENDS ${CMAKE_SOURCE_DIR}/bindings/python/setup.py
|
|
||||||
DEPENDS ${GGWAVE_SOURCES}
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bindings/python
|
|
||||||
COMMAND make clean
|
|
||||||
COMMAND make
|
|
||||||
COMMENT "Compiling Python module"
|
|
||||||
VERBATIM
|
|
||||||
)
|
|
||||||
|
|
||||||
add_custom_target(ggwave-py ALL
|
|
||||||
DEPENDS bindings/python/ggwave.bycython.cpp
|
|
||||||
DEPENDS bindings/python/ggwave
|
|
||||||
)
|
|
||||||
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${CMAKE_SOURCE_DIR}/bindings/python/dist
|
|
||||||
DEPENDS ${CMAKE_SOURCE_DIR}/bindings/python/Makefile
|
|
||||||
DEPENDS ${CMAKE_SOURCE_DIR}/bindings/python/ggwave.pyx
|
|
||||||
DEPENDS ${CMAKE_SOURCE_DIR}/bindings/python/cggwave.pxd
|
|
||||||
DEPENDS ${CMAKE_SOURCE_DIR}/bindings/python/setup.py
|
|
||||||
DEPENDS ${GGWAVE_SOURCES}
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/bindings/python
|
|
||||||
COMMAND make publish
|
|
||||||
COMMENT "Publishing Python module v${PROJECT_VERSION}"
|
|
||||||
VERBATIM
|
|
||||||
)
|
|
||||||
|
|
||||||
add_custom_target(pypi-publish
|
|
||||||
DEPENDS bindings/python/dist
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
if (GGWAVE_BUILD_TESTS)
|
if (GGWAVE_BUILD_TESTS)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -4,6 +4,7 @@
|
|||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[![ggwave badge][changelog-badge]][changelog]
|
[![ggwave badge][changelog-badge]][changelog]
|
||||||
[](https://pypi.org/project/ggwave/)
|
[](https://pypi.org/project/ggwave/)
|
||||||
|
[](https://www.npmjs.com/package/ggwave/)
|
||||||
|
|
||||||
Tiny data-over-sound library.
|
Tiny data-over-sound library.
|
||||||
|
|
||||||
@@ -88,14 +89,15 @@ Reed-Solomon decoding is finally performed to obtain the original data.
|
|||||||
The [examples](https://github.com/ggerganov/ggwave/blob/master/examples/) folder contains several sample applications of the library:
|
The [examples](https://github.com/ggerganov/ggwave/blob/master/examples/) folder contains several sample applications of the library:
|
||||||
|
|
||||||
|
|
||||||
| Example | Description | Backend |
|
| Example | Description | Audio |
|
||||||
| ------- | ----------- | ------- |
|
| ------- | ----------- | ----- |
|
||||||
| [ggwave-rx](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-rx) | Very basic receive-only program | SDL |
|
| [ggwave-rx](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-rx) | Very basic receive-only program | SDL |
|
||||||
| [ggwave-cli](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-cli) | Command line tool for sending/receiving data through sound | SDL |
|
| [ggwave-cli](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-cli) | Command line tool for sending/receiving data through sound | SDL |
|
||||||
| [ggwave-wasm](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-wasm) | WebAssembly module for web applications | SDL |
|
| [ggwave-wasm](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-wasm) | WebAssembly module for web applications | SDL |
|
||||||
| [ggwave-to-file](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-to-file) | Output a generated waveform to an uncompressed WAV file | - |
|
| [ggwave-to-file](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-to-file) | Output a generated waveform to an uncompressed WAV file | - |
|
||||||
| [waver](https://github.com/ggerganov/ggwave/blob/master/examples/waver) | GUI application for sending/receiving data through sound | SDL |
|
| [waver](https://github.com/ggerganov/ggwave/blob/master/examples/waver) | GUI application for sending/receiving data through sound | SDL |
|
||||||
| [ggwave-py](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-py) | Python examples | - |
|
| [ggwave-py](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-py) | Python examples | PortAudio |
|
||||||
|
| [ggwave-js](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-js) | Javascript example | Web Audio API |
|
||||||
|
|
||||||
Other projects using **ggwave** or one of its prototypes:
|
Other projects using **ggwave** or one of its prototypes:
|
||||||
|
|
||||||
@@ -147,6 +149,14 @@ pip install ggwave
|
|||||||
|
|
||||||
More info: https://pypi.org/project/ggwave/
|
More info: https://pypi.org/project/ggwave/
|
||||||
|
|
||||||
|
### Node.js
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install ggwave
|
||||||
|
```
|
||||||
|
|
||||||
|
More info: https://www.npmjs.com/package/ggwave
|
||||||
|
|
||||||
## Installing the Waver application
|
## Installing the Waver application
|
||||||
|
|
||||||
[](https://snapcraft.io/waver)
|
[](https://snapcraft.io/waver)
|
||||||
@@ -165,5 +175,5 @@ sudo snap connect waver:audio-record :audio-record
|
|||||||
```
|
```
|
||||||
|
|
||||||
[changelog]: ./CHANGELOG.md
|
[changelog]: ./CHANGELOG.md
|
||||||
[changelog-badge]: https://img.shields.io/badge/changelog-ggwave%20v0.1.4-dummy
|
[changelog-badge]: https://img.shields.io/badge/changelog-ggwave%20v0.1.5-dummy
|
||||||
[license]: ./LICENSE
|
[license]: ./LICENSE
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[![ggwave badge][changelog-badge]][changelog]
|
[![ggwave badge][changelog-badge]][changelog]
|
||||||
[](https://pypi.org/project/ggwave/)
|
[](https://pypi.org/project/ggwave/)
|
||||||
|
[](https://www.npmjs.com/package/ggwave/)
|
||||||
|
|
||||||
Tiny data-over-sound library.
|
Tiny data-over-sound library.
|
||||||
|
|
||||||
@@ -88,14 +89,15 @@ Reed-Solomon decoding is finally performed to obtain the original data.
|
|||||||
The [examples](https://github.com/ggerganov/ggwave/blob/master/examples/) folder contains several sample applications of the library:
|
The [examples](https://github.com/ggerganov/ggwave/blob/master/examples/) folder contains several sample applications of the library:
|
||||||
|
|
||||||
|
|
||||||
| Example | Description | Backend |
|
| Example | Description | Audio |
|
||||||
| ------- | ----------- | ------- |
|
| ------- | ----------- | ----- |
|
||||||
| [ggwave-rx](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-rx) | Very basic receive-only program | SDL |
|
| [ggwave-rx](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-rx) | Very basic receive-only program | SDL |
|
||||||
| [ggwave-cli](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-cli) | Command line tool for sending/receiving data through sound | SDL |
|
| [ggwave-cli](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-cli) | Command line tool for sending/receiving data through sound | SDL |
|
||||||
| [ggwave-wasm](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-wasm) | WebAssembly module for web applications | SDL |
|
| [ggwave-wasm](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-wasm) | WebAssembly module for web applications | SDL |
|
||||||
| [ggwave-to-file](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-to-file) | Output a generated waveform to an uncompressed WAV file | - |
|
| [ggwave-to-file](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-to-file) | Output a generated waveform to an uncompressed WAV file | - |
|
||||||
| [waver](https://github.com/ggerganov/ggwave/blob/master/examples/waver) | GUI application for sending/receiving data through sound | SDL |
|
| [waver](https://github.com/ggerganov/ggwave/blob/master/examples/waver) | GUI application for sending/receiving data through sound | SDL |
|
||||||
| [ggwave-py](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-py) | Python examples | - |
|
| [ggwave-py](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-py) | Python examples | PortAudio |
|
||||||
|
| [ggwave-js](https://github.com/ggerganov/ggwave/blob/master/examples/ggwave-js) | Javascript example | Web Audio API |
|
||||||
|
|
||||||
Other projects using **ggwave** or one of its prototypes:
|
Other projects using **ggwave** or one of its prototypes:
|
||||||
|
|
||||||
@@ -147,6 +149,14 @@ pip install ggwave
|
|||||||
|
|
||||||
More info: https://pypi.org/project/ggwave/
|
More info: https://pypi.org/project/ggwave/
|
||||||
|
|
||||||
|
### Node.js
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install ggwave
|
||||||
|
```
|
||||||
|
|
||||||
|
More info: https://www.npmjs.com/package/ggwave
|
||||||
|
|
||||||
## Installing the Waver application
|
## Installing the Waver application
|
||||||
|
|
||||||
[](https://snapcraft.io/waver)
|
[](https://snapcraft.io/waver)
|
||||||
|
|||||||
60
bindings/CMakeLists.txt
Normal file
60
bindings/CMakeLists.txt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
if (EMSCRIPTEN)
|
||||||
|
add_subdirectory(javascript)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/javascript/publish.log
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/javascript/ggwave.js
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/javascript/package.json
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/javascript
|
||||||
|
COMMAND npm publish
|
||||||
|
COMMAND touch publish.log
|
||||||
|
COMMENT "Publishing npm module v${PROJECT_VERSION}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(npm-publish
|
||||||
|
DEPENDS javascript/publish.log
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (GGWAVE_SUPPORT_PYTHON)
|
||||||
|
file(GLOB_RECURSE GGWAVE_SOURCES "../include/*" "../src/*")
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/python/ggwave.bycython.cpp
|
||||||
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/python/ggwave
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/Makefile
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/ggwave.pyx
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/cggwave.pxd
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/setup.py
|
||||||
|
DEPENDS ${GGWAVE_SOURCES}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/python
|
||||||
|
COMMAND make clean
|
||||||
|
COMMAND make
|
||||||
|
COMMENT "Compiling Python module"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(ggwave-py ALL
|
||||||
|
DEPENDS python/ggwave.bycython.cpp
|
||||||
|
DEPENDS python/ggwave
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/python/dist
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/Makefile
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/ggwave.pyx
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/cggwave.pxd
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/python/setup.py
|
||||||
|
DEPENDS ${GGWAVE_SOURCES}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/python
|
||||||
|
COMMAND make publish
|
||||||
|
COMMENT "Publishing Python module v${PROJECT_VERSION}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(pypi-publish
|
||||||
|
DEPENDS python/dist
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
1
bindings/javascript/.gitignore
vendored
Normal file
1
bindings/javascript/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
publish.log
|
||||||
24
bindings/javascript/CMakeLists.txt
Normal file
24
bindings/javascript/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
set(TARGET libggwave)
|
||||||
|
|
||||||
|
add_executable(${TARGET}
|
||||||
|
emscripten.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${TARGET} PRIVATE
|
||||||
|
ggwave
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(${TARGET} PROPERTIES LINK_FLAGS " \
|
||||||
|
--bind \
|
||||||
|
-s MODULARIZE=1 \
|
||||||
|
-s SINGLE_FILE=1 \
|
||||||
|
-s ALLOW_MEMORY_GROWTH=1 \
|
||||||
|
-s EXPORT_NAME=\"'ggwave_factory'\" \
|
||||||
|
")
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
TARGET libggwave POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy
|
||||||
|
${CMAKE_BINARY_DIR}/bin/libggwave.js
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ggwave.js
|
||||||
|
)
|
||||||
65
bindings/javascript/emscripten.cpp
Normal file
65
bindings/javascript/emscripten.cpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#include "ggwave/ggwave.h"
|
||||||
|
|
||||||
|
#include <emscripten.h>
|
||||||
|
#include <emscripten/bind.h>
|
||||||
|
|
||||||
|
EMSCRIPTEN_BINDINGS(ggwave) {
|
||||||
|
emscripten::enum_<ggwave_SampleFormat>("SampleFormat")
|
||||||
|
.value("GGWAVE_SAMPLE_FORMAT_UNDEFINED", GGWAVE_SAMPLE_FORMAT_UNDEFINED)
|
||||||
|
.value("GGWAVE_SAMPLE_FORMAT_U8", GGWAVE_SAMPLE_FORMAT_U8)
|
||||||
|
.value("GGWAVE_SAMPLE_FORMAT_I8", GGWAVE_SAMPLE_FORMAT_I8)
|
||||||
|
.value("GGWAVE_SAMPLE_FORMAT_U16", GGWAVE_SAMPLE_FORMAT_U16)
|
||||||
|
.value("GGWAVE_SAMPLE_FORMAT_I16", GGWAVE_SAMPLE_FORMAT_I16)
|
||||||
|
.value("GGWAVE_SAMPLE_FORMAT_F32", GGWAVE_SAMPLE_FORMAT_F32)
|
||||||
|
;
|
||||||
|
|
||||||
|
emscripten::enum_<ggwave_TxProtocolId>("TxProtocolId")
|
||||||
|
.value("GGWAVE_TX_PROTOCOL_AUDIBLE_NORMAL", GGWAVE_TX_PROTOCOL_AUDIBLE_NORMAL)
|
||||||
|
.value("GGWAVE_TX_PROTOCOL_AUDIBLE_FAST", GGWAVE_TX_PROTOCOL_AUDIBLE_FAST)
|
||||||
|
.value("GGWAVE_TX_PROTOCOL_AUDIBLE_FASTEST", GGWAVE_TX_PROTOCOL_AUDIBLE_FASTEST)
|
||||||
|
.value("GGWAVE_TX_PROTOCOL_ULTRASOUND_NORMAL", GGWAVE_TX_PROTOCOL_ULTRASOUND_NORMAL)
|
||||||
|
.value("GGWAVE_TX_PROTOCOL_ULTRASOUND_FAST", GGWAVE_TX_PROTOCOL_ULTRASOUND_FAST)
|
||||||
|
.value("GGWAVE_TX_PROTOCOL_ULTRASOUND_FASTEST", GGWAVE_TX_PROTOCOL_ULTRASOUND_FASTEST)
|
||||||
|
;
|
||||||
|
|
||||||
|
emscripten::class_<ggwave_Parameters>("Parameters")
|
||||||
|
.constructor<>()
|
||||||
|
.property("sampleRateInp", &ggwave_Parameters::sampleRateInp)
|
||||||
|
.property("sampleRateOut", &ggwave_Parameters::sampleRateOut)
|
||||||
|
.property("samplesPerFrame", &ggwave_Parameters::samplesPerFrame)
|
||||||
|
.property("sampleFormatInp", &ggwave_Parameters::sampleFormatInp)
|
||||||
|
.property("sampleFormatOut", &ggwave_Parameters::sampleFormatOut)
|
||||||
|
;
|
||||||
|
|
||||||
|
emscripten::function("getDefaultParameters", &ggwave_getDefaultParameters);
|
||||||
|
emscripten::function("init", &ggwave_init);
|
||||||
|
emscripten::function("free", &ggwave_free);
|
||||||
|
|
||||||
|
emscripten::function("encode", emscripten::optional_override(
|
||||||
|
[](ggwave_Instance instance,
|
||||||
|
const std::string & data,
|
||||||
|
ggwave_TxProtocolId txProtocolId,
|
||||||
|
int volume) {
|
||||||
|
auto n = ggwave_encode(instance, data.data(), data.size(), txProtocolId, volume, nullptr, 1);
|
||||||
|
std::vector<char> result(n);
|
||||||
|
result.resize(n);
|
||||||
|
ggwave_encode(instance, data.data(), data.size(), txProtocolId, volume, result.data(), 0);
|
||||||
|
|
||||||
|
return emscripten::val(
|
||||||
|
emscripten::typed_memory_view(result.size(),
|
||||||
|
result.data()));
|
||||||
|
}));
|
||||||
|
|
||||||
|
emscripten::function("decode", emscripten::optional_override(
|
||||||
|
[](ggwave_Instance instance,
|
||||||
|
const std::string & data) {
|
||||||
|
char output[256];
|
||||||
|
auto n = ggwave_decode(instance, data.data(), data.size(), output);
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
return std::string(output, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}));
|
||||||
|
}
|
||||||
21
bindings/javascript/ggwave.js
Normal file
21
bindings/javascript/ggwave.js
Normal file
File diff suppressed because one or more lines are too long
26
bindings/javascript/package-tmpl.json
Normal file
26
bindings/javascript/package-tmpl.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "ggwave",
|
||||||
|
"version": "@PROJECT_VERSION@",
|
||||||
|
"description": "Tiny data-over-sound library",
|
||||||
|
"main": "ggwave.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"todo: add tests\" && exit 0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/ggerganov/ggwave.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"data-over-sound",
|
||||||
|
"fsk",
|
||||||
|
"sound-library",
|
||||||
|
"ultrasound",
|
||||||
|
"ecc"
|
||||||
|
],
|
||||||
|
"author": "Georgi Gerganov",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/ggerganov/ggwave/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/ggerganov/ggwave#readme"
|
||||||
|
}
|
||||||
26
bindings/javascript/package.json
Normal file
26
bindings/javascript/package.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "ggwave",
|
||||||
|
"version": "0.1.5",
|
||||||
|
"description": "Tiny data-over-sound library",
|
||||||
|
"main": "ggwave.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"todo: add tests\" && exit 0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/ggerganov/ggwave.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"data-over-sound",
|
||||||
|
"fsk",
|
||||||
|
"sound-library",
|
||||||
|
"ultrasound",
|
||||||
|
"ecc"
|
||||||
|
],
|
||||||
|
"author": "Georgi Gerganov",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/ggerganov/ggwave/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/ggerganov/ggwave#readme"
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ setup(
|
|||||||
name = "ggwave",
|
name = "ggwave",
|
||||||
description = "Tiny data-over-sound library.",
|
description = "Tiny data-over-sound library.",
|
||||||
long_description = long_description,
|
long_description = long_description,
|
||||||
version = "0.1.4",
|
version = "0.1.5",
|
||||||
url = "https://github.com/ggerganov/ggwave",
|
url = "https://github.com/ggerganov/ggwave",
|
||||||
author = "Georgi Gerganov",
|
author = "Georgi Gerganov",
|
||||||
author_email = "ggerganov@gmail.com",
|
author_email = "ggerganov@gmail.com",
|
||||||
|
|||||||
@@ -6,7 +6,14 @@ if (GGWAVE_SUPPORT_SDL2)
|
|||||||
# SDL2
|
# SDL2
|
||||||
|
|
||||||
if (EMSCRIPTEN)
|
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_INCLUDE_DIRS)
|
||||||
unset(SDL2_LIBRARIES)
|
unset(SDL2_LIBRARIES)
|
||||||
@@ -65,19 +72,23 @@ endif()
|
|||||||
|
|
||||||
# examples
|
# examples
|
||||||
|
|
||||||
add_subdirectory(ggwave-to-file)
|
if (EMSCRIPTEN)
|
||||||
|
add_subdirectory(ggwave-js)
|
||||||
|
else()
|
||||||
|
add_subdirectory(ggwave-to-file)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (GGWAVE_SUPPORT_SDL2)
|
if (GGWAVE_SUPPORT_SDL2)
|
||||||
if (EMSCRIPTEN)
|
if (EMSCRIPTEN)
|
||||||
# emscripten sdl2 examples
|
# emscripten sdl2 examples
|
||||||
|
|
||||||
add_subdirectory(ggwave-wasm)
|
add_subdirectory(ggwave-wasm)
|
||||||
add_subdirectory(waver)
|
|
||||||
else()
|
else()
|
||||||
# non-emscripten sdl2 examples
|
# non-emscripten sdl2 examples
|
||||||
|
|
||||||
add_subdirectory(ggwave-rx)
|
add_subdirectory(ggwave-rx)
|
||||||
add_subdirectory(ggwave-cli)
|
add_subdirectory(ggwave-cli)
|
||||||
add_subdirectory(waver)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(waver)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
#include "emscripten/emscripten.h"
|
#include <emscripten.h>
|
||||||
#else
|
#else
|
||||||
#define EMSCRIPTEN_KEEPALIVE
|
#define EMSCRIPTEN_KEEPALIVE
|
||||||
#endif
|
#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;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fprintf(stderr, "Generating waveform for message '%s' ...\n", message.c_str());
|
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 });
|
GGWave ggWave({ GGWave::kBaseSampleRate, sampleRateOut, 1024, GGWAVE_SAMPLE_FORMAT_F32, GGWAVE_SAMPLE_FORMAT_I16 });
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
if (EMSCRIPTEN)
|
if (EMSCRIPTEN)
|
||||||
|
#
|
||||||
|
# test-ggwave-js
|
||||||
|
|
||||||
|
set(TEST_TARGET test-ggwave-js)
|
||||||
|
|
||||||
|
add_test(NAME ${TEST_TARGET}
|
||||||
|
COMMAND node test-ggwave.js
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#
|
#
|
||||||
# test-ggwave-c
|
# test-ggwave-c
|
||||||
|
|
||||||
|
|||||||
19
tests/test-ggwave.js
Normal file
19
tests/test-ggwave.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
var factory = require('../bindings/javascript/ggwave.js')
|
||||||
|
|
||||||
|
factory().then(function(ggwave) {
|
||||||
|
// create ggwave instance with default parameters
|
||||||
|
var parameters = ggwave.getDefaultParameters();
|
||||||
|
var instance = ggwave.init(parameters);
|
||||||
|
|
||||||
|
var payload = 'hello js';
|
||||||
|
|
||||||
|
// generate audio waveform for string "hello js"
|
||||||
|
var waveform = ggwave.encode(instance, payload, ggwave.TxProtocolId.GGWAVE_TX_PROTOCOL_AUDIBLE_FAST, 10);
|
||||||
|
|
||||||
|
// decode the audio waveform back to text
|
||||||
|
var res = ggwave.decode(instance, waveform);
|
||||||
|
|
||||||
|
if (res != payload) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import ggwave
|
import ggwave
|
||||||
|
|
||||||
|
# create ggwave instance with default parameters
|
||||||
instance = ggwave.init()
|
instance = ggwave.init()
|
||||||
|
|
||||||
payload = 'hello python'
|
payload = 'hello python'
|
||||||
|
|||||||
Reference in New Issue
Block a user