Skip to content

Commit bfdc749

Browse files
committed
feat: lots of small fixes + example video
1 parent d3fc094 commit bfdc749

File tree

3 files changed

+93
-83
lines changed

3 files changed

+93
-83
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
build
22
.vscode
33
*.sf2
4-
*.nu
4+
*.nu
5+
tmp.*
6+
*.sh

audioengine.cpp

Lines changed: 81 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -7,134 +7,138 @@
77
#define TSF_IMPLEMENTATION
88
#include "tsf/tsf.h"
99

10-
namespace emscrijpten {
11-
typedef std::function<void()> callback_t;
12-
void async_call_callback(void * arg) {
13-
callback_t * callback = (callback_t *)arg;
14-
(*callback)();
15-
delete callback;
16-
}
17-
void async_call(callback_t callback, int millis = 0) {
18-
callback_t * _callback = new callback_t(callback);
19-
emscripten_async_call(async_call_callback, _callback, millis);
20-
}
21-
22-
}
23-
24-
static tsf* g_TinySoundFont;
25-
10+
static tsf *g_TinySoundFont = NULL;
2611

2712
static bool run;
28-
static int volume = 100;
13+
int volume = 100;
2914

3015
static bool renderAudio(int numInputs, const AudioSampleFrame *inputs,
31-
int numOutputs, AudioSampleFrame *outputs,
32-
int numParams, const AudioParamFrame *params,
33-
void *userData)
16+
int numOutputs, AudioSampleFrame *outputs,
17+
int numParams, const AudioParamFrame *params,
18+
void *userData)
3419
{
35-
for(int i = 0; i < numOutputs; ++i) {
36-
tsf_render_float(g_TinySoundFont, outputs[i].data, outputs[i].samplesPerChannel*outputs[i].numberOfChannels, 0);
37-
}
20+
for (int i = 0; i < numOutputs; ++i)
21+
{
22+
tsf_render_float(g_TinySoundFont, outputs[i].data, outputs[i].samplesPerChannel * outputs[i].numberOfChannels, 0);
23+
}
3824

39-
return true;
25+
return true;
4026
}
4127

42-
void load(uintptr_t data, int length) {
43-
printf("hello world %d\n", length);
44-
auto array = reinterpret_cast<uint8_t *>( data );
45-
46-
g_TinySoundFont = tsf_load_memory(array, length);
28+
void load(uintptr_t data, int length)
29+
{
30+
printf("A file of length %d has been loaded into AudioEngine.\n", length);
31+
auto array = reinterpret_cast<uint8_t *>(data);
32+
bool recreating = false;
33+
if (g_TinySoundFont != NULL)
34+
{
35+
tsf_close(g_TinySoundFont);
36+
recreating = true;
37+
}
38+
g_TinySoundFont = tsf_load_memory(array, length);
39+
if (recreating)
40+
{
41+
tsf_set_output(g_TinySoundFont, TSF_MONO, 44100, 0);
42+
tsf_set_max_voices(g_TinySoundFont, 200);
43+
tsf_set_volume(g_TinySoundFont, volume / 100);
44+
}
4745
}
4846

49-
void destroy() {
50-
tsf_close(g_TinySoundFont);
47+
void destroy()
48+
{
49+
tsf_close(g_TinySoundFont);
5150
}
5251

53-
void end() {
54-
run = false;
52+
void end()
53+
{
54+
run = false;
5555
}
56-
void note_on(int presetId, int key, float velocity, int delay) {
56+
void note_on(int presetId, int key, float velocity, int delay)
57+
{
5758
tsf_note_on(g_TinySoundFont, presetId, key, velocity);
5859
}
5960

60-
void note_off(int presetId, int key, float velocity, int delay) {
61+
void note_off(int presetId, int key, float velocity, int delay)
62+
{
6163
tsf_note_off(g_TinySoundFont, presetId, key);
6264
}
6365

64-
void set_volume(int vol) {
65-
// nulled out
66-
}
67-
68-
int get_volume() {
69-
// always 100
70-
return 100;
71-
}
72-
7366
uint8_t audioThreadStack[4096];
7467

7568
bool OnClick(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
7669
{
77-
printf("canvas clicked\n");
78-
7970
EMSCRIPTEN_WEBAUDIO_T audioContext = (EMSCRIPTEN_WEBAUDIO_T)userData;
80-
if (emscripten_audio_context_state(audioContext) != AUDIO_CONTEXT_STATE_RUNNING) {
71+
if (emscripten_audio_context_state(audioContext) != AUDIO_CONTEXT_STATE_RUNNING)
72+
{
8173
emscripten_resume_audio_context_sync(audioContext);
8274
}
8375
return false;
8476
}
8577

8678
void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData)
8779
{
88-
if (!success) return; // Check browser console in a debug build for detailed errors
89-
printf("pressor created\n");
80+
if (!success)
81+
return; // Check browser console in a debug build for detailed errors
9082

91-
int outputChannelCounts[1] = { 1 };
83+
int outputChannelCounts[1] = {1};
9284
EmscriptenAudioWorkletNodeCreateOptions options = {
93-
.numberOfInputs = 0,
94-
.numberOfOutputs = 1,
95-
.outputChannelCounts = outputChannelCounts
96-
};
85+
.numberOfInputs = 0,
86+
.numberOfOutputs = 1,
87+
.outputChannelCounts = outputChannelCounts};
9788

9889
// Create node
9990
EMSCRIPTEN_AUDIO_WORKLET_NODE_T wasmAudioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext,
100-
"noise-generator", &options, &renderAudio, 0);
91+
"audioengine", &options, &renderAudio, 0);
10192

10293
// Connect it to audio context destination
10394
emscripten_audio_node_connect(wasmAudioWorklet, audioContext, 0, 0);
104-
emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, (void*)audioContext, 0, OnClick);
95+
emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, (void *)audioContext, 0, OnClick);
10596
}
10697

10798
void AudioThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, bool success, void *userData)
10899
{
109-
if (!success) return; // Check browser console in a debug build for detailed errors
110-
printf("thread initialized\n");
100+
if (!success)
101+
return; // Check browser console in a debug build for detailed errors
111102

112103
WebAudioWorkletProcessorCreateOptions opts = {
113-
.name = "noise-generator",
104+
.name = "audioengine",
114105
};
115106
emscripten_create_wasm_audio_worklet_processor_async(audioContext, &opts, &AudioWorkletProcessorCreated, 0);
116107
}
117108

109+
int get_volume()
110+
{
111+
return volume;
112+
}
113+
114+
void set_volume(int vol)
115+
{
116+
volume = vol;
117+
tsf_set_volume(g_TinySoundFont, vol / 100);
118+
}
119+
118120
int start()
119121
{
120-
printf("we started\n");
121-
122-
tsf_set_output(g_TinySoundFont, TSF_STEREO_INTERLEAVED, 22000, 0);
123-
tsf_set_max_voices(g_TinySoundFont, 40);
124-
EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(0);
125-
emscripten_start_wasm_audio_worklet_thread_async(context, audioThreadStack, sizeof(audioThreadStack),
126-
&AudioThreadInitialized, 0);
127-
128-
return 0;
122+
printf("AudioEngine has been started.\n");
123+
124+
tsf_set_output(g_TinySoundFont, TSF_MONO, 44100, 0);
125+
tsf_set_max_voices(g_TinySoundFont, 200);
126+
EmscriptenWebAudioCreateAttributes attr = {
127+
.latencyHint = "playback",
128+
.sampleRate = 44100};
129+
EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(&attr);
130+
emscripten_start_wasm_audio_worklet_thread_async(context, audioThreadStack, sizeof(audioThreadStack), &AudioThreadInitialized, 0);
131+
return 0;
129132
}
130133

131-
EMSCRIPTEN_BINDINGS(my_module) {
132-
emscripten::function("start", start);
133-
emscripten::function("load", load, emscripten::allow_raw_pointers());
134-
emscripten::function("end", end);
135-
emscripten::function("destroy", destroy);
136-
emscripten::function("note_off", note_off);
137-
emscripten::function("note_on", note_on);
138-
emscripten::function("set_volume", set_volume);
139-
emscripten::function("get_volume", get_volume);
134+
EMSCRIPTEN_BINDINGS(my_module)
135+
{
136+
emscripten::function("start", start);
137+
emscripten::function("load", load, emscripten::allow_raw_pointers());
138+
emscripten::function("end", end);
139+
emscripten::function("destroy", destroy);
140+
emscripten::function("note_off", note_off);
141+
emscripten::function("note_on", note_on);
142+
emscripten::function("set_volume", set_volume);
143+
emscripten::function("get_volume", get_volume);
140144
}

readme.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,25 @@
22

33
this is a c++ (compiled to wasm) application which is a supposed replacement for the current AudioEngineWeb that's in multiplayerpiano.
44

5-
it can reliably play most black midis and normal midis with resonable performance with absolutely no cracking or artifacting.
5+
it can reliably play most black midis and normal midis with resonable performance with absolutely no artifacting.
66

7-
this is very heavily in development still, and is not a final version at all.
8-
9-
we are mainly missing all Synth stuff, and audio playback is only mono.
7+
this is very heavily in development still, and is not a final version at all.
108

119
uses [TinySoundFont](https://github.com/schellingb/TinySoundFont) and [emscripten](https://emscripten.org).
1210

11+
[Video - freedom dive black midi](https://files.sad.ovh/public/2024-12-28%2017-40-26.mp4)
12+
13+
## bugs
14+
1. The only major bug is the fact that it's still mono! There's a ongoing TSF issue, which I've reported is still happening.
15+
2. Also, higher than 100-ish voice count crackles (we're using 200 voices for better blackmidi playback).. I have no clue how to fix this, maybe it's just a issue with how small the sample size for webaudio is (128 samples??).
16+
3. Volume control is broken. Created a issue for this on the TSF repo.
1317

1418
## build
1519
use (with emsdk activated and sourced)
1620
```
1721
mkdir -p build
1822
19-
emcc -std=c++11 -lembind -s ALLOW_MEMORY_GROWTH -s EXPORTED_FUNCTIONS="['_malloc', '_free']" -s AUDIO_WORKLET=1 -s WASM_WORKERS=1 -O3 -o build/audioengine.js audioengine.cpp
23+
emcc -std=c++11 -lembind -s ALLOW_MEMORY_GROWTH -s EXPORTED_FUNCTIONS="['_malloc', '_free']" -s AUDIO_WORKLET=1 -s WASM_WORKERS=1 -O3 --emit-tsd audioengine.ts.d -o build/audioengine.js audioengine.cpp
2024
```
2125

2226
to compile into `build`

0 commit comments

Comments
 (0)