④
gitlab.com/fioravera/synth
Realtime Keyboard Instrument
Project synth implements a real-time keyboard instrument in C, with a WebAssembly/JS wrapper for browser usage, and keeps a legacy version for reference.
Project Structure
/realtime_keyboard_instrument/ <- Root folder of the project
├─ /legacy/ <- Historical / initial prototypes
│ └─ pianola.c <- First prototype C code (no libraries, basic realtime keyboard)
├─ /src/ <- Official C library implementation
│ ├─ instrument.h <- Public API header
│ ├─ instrument.c <- Implementation of the fast realtime synth
│ └─ native_example.c <- Minimal example for native compilation (stdout -> aplay)
├─ /emscripten/ <- Emscripten wrapper + WASM example
│ └─ emscripten_wrapper.c <- Exports functions to JS (ems_init, ems_generate, etc.)
└─ synth.xhtml <- Browser-ready offline demo with clickable keyboard
Notes
/legacy/: keeps your first working version (pianola.c) intact. Useful for historical reference and experimentation./src/: production-ready C library, optimized for real-time performance and easy to compile both natively and for WebAssembly./emscripten/: contains the wrapper that exposes the C library to JS, allowing playback in browsers via WASM./web/: fully offline HTML/XHTML demo, where users can click keys to play sounds and see color changes.
Compilation Tips
Playback in the terminal through ./native_synth (a..k notes, z/x octave, space=bellows, q quit).
Playback in the browser through synth.xhtml.
Compiling and Running the Native Synth
Note: Requires gcc installed and configured in your environment.
Compile the C sources into a native executable:
gcc src/native_example.c src/instrument.c -O3 -lm -o native_synth
Explanation:
- gcc → GNU C compiler.
src/native\_example.c src/instrument.c→ source files;native\_example.ccontains main(),instrument.cgenerates the audio.- -O3 → maximum optimization for speed.
- -lm → links the math library (sin(), pow()).
- -o native_synth → sets the output executable name.
Run the synth and pipe audio to a player:
./native_synth | aplay -f S16\_LE -r 48000
Breakdown:
./native_synth
Runs the compiled executable. This program:- Reads keyboard input in real time.
- Generates PCM audio data (raw 16-bit samples, stereo) to stdout.
- Handles note-on/note-off events internally using the instrument library.
|(pipe)
Takes the raw audio output fromnative_synthand passes it as input to the next command (aplay).aplay
A Linux utility for playing raw or WAV audio files via ALSA. It reads PCM data from stdin (provided by the pipe).-f S16_LE
Specifies the audio format: signed 16-bit little-endian samples. This matches the format produced bynative_synth.-r 48000
Specifies the sample rate: 48 kHz. Again, this matches the native synth output.
Summary:
- This command streams real-time audio from the C synth to your speakers.
- The pipe allows the program to produce audio continuously without writing to a file.
- If you don’t have
aplay(e.g., on Termux), you can useffplayinstead:
./native_synth | ffplay -f s16le -ar 48000 -ac 2 -
This achieves the same effect, playing the PCM audio directly in real time.
Compiling the Synth for WebAssembly with Emscripten
Note: Requires Emscripten installed and configured in your environment.
The following command compiles the C library and wrapper into WebAssembly (.wasm) and JavaScript (synth.js) for use in a browser:
emcc -I./src emscripten/emscripten_wrapper.c src/instrument.c -O3 -s WASM=1 \
-s EXPORTED_FUNCTIONS='["_ems_init","_ems_shutdown","_ems_set_wave","_ems_set_bellows","_ems_note_on","_ems_note_off","_ems_generate"]' \
-s EXPORTED_RUNTIME_METHODS='["cwrap","getValue","setValue","HEAP16"]' \
-o synth.js
Breakdown:
- emcc The Emscripten compiler, used to compile C/C++ code into WebAssembly and JavaScript.
-I./src→ include path forinstrument.h.emscripten/emscripten\_wrapper.c src/instrument.cSource files:instrument.c→ the core C library generating the audio.- emscripten_wrapper.c → wrapper exposing functions to JavaScript (e.g., _ems_generate(), _ems_note_on()).
-O3Maximum optimization for speed. Important for real-time audio performance in the browser.-s WASM=1Instructs Emscripten to generate WebAssembly (.wasm) rather than just asm.js.-s EXPORTED\_FUNCTIONS='[...]'Specifies which C functions are exported to JavaScript, so you can call them from your web code. Example exported functions:- _ems_init → initialize the synth
- _ems_shutdown → cleanup
- _ems_set_wave → select waveform
- _ems_set_bellows → toggle bellows mode
- _ems_note_on / _ems_note_off → play or stop a note
- _ems_generate → fill a buffer with PCM samples
-s EXPORTED\_RUNTIME\_METHODS='[...]'Exports runtime helpers to JS, such as:- cwrap → call C functions from JS
- getValue / setValue → read/write memory in the WASM heap
- HEAP16 → access the buffer as 16-bit PCM
- -o synth.js
Output file:
synth.js(plussynth.wasm), which can be loaded in a browser.
Result:
- Produces
synth.jsandsynth.wasm. - The functions listed in EXPORTED_FUNCTIONS are callable from JavaScript.
- _ems_generate() can then be used to fill a Web Audio buffer for real-time playback in the browser through
synth.xhtml.