Files
cell/playdate/sound_playdate.c
2025-12-10 15:01:20 -06:00

449 lines
20 KiB
C

// sound_playdate.c - Cell integration for Playdate Sound API
// Wraps pd_api_sound.h functions for JavaScript access
#include "cell.h"
#include "common.h"
// --- Helpers ---
static FilePlayer* js2fileplayer(JSContext *js, JSValueConst val) {
int64_t ptr; if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
return (FilePlayer*)(intptr_t)ptr;
}
static SamplePlayer* js2sampleplayer(JSContext *js, JSValueConst val) {
int64_t ptr; if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
return (SamplePlayer*)(intptr_t)ptr;
}
static AudioSample* js2sample(JSContext *js, JSValueConst val) {
int64_t ptr; if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
return (AudioSample*)(intptr_t)ptr;
}
static PDSynth* js2synth(JSContext *js, JSValueConst val) {
int64_t ptr; if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
return (PDSynth*)(intptr_t)ptr;
}
static SoundChannel* js2channel(JSContext *js, JSValueConst val) {
int64_t ptr; if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
return (SoundChannel*)(intptr_t)ptr;
}
// --- Global ---
JSC_CCALL(sound_getCurrentTime,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewInt64(js, pd_sound->getCurrentTime());
)
JSC_CCALL(sound_getDefaultChannel,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
SoundChannel *ch = pd_sound->getDefaultChannel();
return ch ? JS_NewInt64(js, (int64_t)(intptr_t)ch) : JS_NULL;
)
JSC_CCALL(sound_addChannel,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewBool(js, pd_sound->addChannel(js2channel(js, argv[0])));
)
JSC_CCALL(sound_removeChannel,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewBool(js, pd_sound->removeChannel(js2channel(js, argv[0])));
)
JSC_CCALL(sound_setOutputsActive,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->setOutputsActive(JS_ToBool(js, argv[0]), JS_ToBool(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_getError,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
const char *err = pd_sound->getError();
return err ? JS_NewString(js, err) : JS_NULL;
)
// --- FilePlayer ---
JSC_CCALL(sound_newFilePlayer,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
FilePlayer *p = pd_sound->fileplayer->newPlayer();
return p ? JS_NewInt64(js, (int64_t)(intptr_t)p) : JS_NULL;
)
JSC_CCALL(sound_freeFilePlayer,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
FilePlayer *p = js2fileplayer(js, argv[0]);
if (p) pd_sound->fileplayer->freePlayer(p);
return JS_UNDEFINED;
)
JSC_SCALL(sound_loadIntoFilePlayer,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
FilePlayer *p = js2fileplayer(js, argv[0]);
ret = JS_NewBool(js, pd_sound->fileplayer->loadIntoPlayer(p, str));
)
JSC_CCALL(sound_filePlayerPlay,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
FilePlayer *p = js2fileplayer(js, argv[0]);
int repeat = argc > 1 ? (int)js2number(js, argv[1]) : 1;
return JS_NewBool(js, pd_sound->fileplayer->play(p, repeat));
)
JSC_CCALL(sound_filePlayerIsPlaying,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewBool(js, pd_sound->fileplayer->isPlaying(js2fileplayer(js, argv[0])));
)
JSC_CCALL(sound_filePlayerPause,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->fileplayer->pause(js2fileplayer(js, argv[0]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_filePlayerStop,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->fileplayer->stop(js2fileplayer(js, argv[0]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_filePlayerSetVolume,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->fileplayer->setVolume(js2fileplayer(js, argv[0]), (float)js2number(js, argv[1]), (float)js2number(js, argv[2]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_filePlayerGetVolume,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
float l, r; pd_sound->fileplayer->getVolume(js2fileplayer(js, argv[0]), &l, &r);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "left", JS_NewFloat64(js, l));
JS_SetPropertyStr(js, obj, "right", JS_NewFloat64(js, r));
return obj;
)
JSC_CCALL(sound_filePlayerGetLength,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewFloat64(js, pd_sound->fileplayer->getLength(js2fileplayer(js, argv[0])));
)
JSC_CCALL(sound_filePlayerSetOffset,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->fileplayer->setOffset(js2fileplayer(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_filePlayerGetOffset,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewFloat64(js, pd_sound->fileplayer->getOffset(js2fileplayer(js, argv[0])));
)
JSC_CCALL(sound_filePlayerSetRate,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->fileplayer->setRate(js2fileplayer(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_filePlayerGetRate,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewFloat64(js, pd_sound->fileplayer->getRate(js2fileplayer(js, argv[0])));
)
JSC_CCALL(sound_filePlayerSetLoopRange,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->fileplayer->setLoopRange(js2fileplayer(js, argv[0]), (float)js2number(js, argv[1]), (float)js2number(js, argv[2]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_filePlayerDidUnderrun,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewBool(js, pd_sound->fileplayer->didUnderrun(js2fileplayer(js, argv[0])));
)
JSC_CCALL(sound_filePlayerSetStopOnUnderrun,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->fileplayer->setStopOnUnderrun(js2fileplayer(js, argv[0]), JS_ToBool(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_filePlayerSetBufferLength,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->fileplayer->setBufferLength(js2fileplayer(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
// --- Sample ---
JSC_SCALL(sound_loadSample,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
AudioSample *s = pd_sound->sample->load(str);
ret = s ? JS_NewInt64(js, (int64_t)(intptr_t)s) : JS_NULL;
)
JSC_CCALL(sound_freeSample,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
AudioSample *s = js2sample(js, argv[0]);
if (s) pd_sound->sample->freeSample(s);
return JS_UNDEFINED;
)
JSC_CCALL(sound_getSampleLength,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewFloat64(js, pd_sound->sample->getLength(js2sample(js, argv[0])));
)
JSC_CCALL(sound_newSampleBuffer,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
AudioSample *s = pd_sound->sample->newSampleBuffer((int)js2number(js, argv[0]));
return s ? JS_NewInt64(js, (int64_t)(intptr_t)s) : JS_NULL;
)
// --- SamplePlayer ---
JSC_CCALL(sound_newSamplePlayer,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
SamplePlayer *p = pd_sound->sampleplayer->newPlayer();
return p ? JS_NewInt64(js, (int64_t)(intptr_t)p) : JS_NULL;
)
JSC_CCALL(sound_freeSamplePlayer,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
SamplePlayer *p = js2sampleplayer(js, argv[0]);
if (p) pd_sound->sampleplayer->freePlayer(p);
return JS_UNDEFINED;
)
JSC_CCALL(sound_samplePlayerSetSample,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->sampleplayer->setSample(js2sampleplayer(js, argv[0]), js2sample(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_samplePlayerPlay,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
SamplePlayer *p = js2sampleplayer(js, argv[0]);
int repeat = argc > 1 ? (int)js2number(js, argv[1]) : 1;
float rate = argc > 2 ? (float)js2number(js, argv[2]) : 1.0f;
return JS_NewBool(js, pd_sound->sampleplayer->play(p, repeat, rate));
)
JSC_CCALL(sound_samplePlayerIsPlaying,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewBool(js, pd_sound->sampleplayer->isPlaying(js2sampleplayer(js, argv[0])));
)
JSC_CCALL(sound_samplePlayerStop,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->sampleplayer->stop(js2sampleplayer(js, argv[0]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_samplePlayerSetVolume,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->sampleplayer->setVolume(js2sampleplayer(js, argv[0]), (float)js2number(js, argv[1]), (float)js2number(js, argv[2]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_samplePlayerGetVolume,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
float l, r; pd_sound->sampleplayer->getVolume(js2sampleplayer(js, argv[0]), &l, &r);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "left", JS_NewFloat64(js, l));
JS_SetPropertyStr(js, obj, "right", JS_NewFloat64(js, r));
return obj;
)
JSC_CCALL(sound_samplePlayerGetLength,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewFloat64(js, pd_sound->sampleplayer->getLength(js2sampleplayer(js, argv[0])));
)
JSC_CCALL(sound_samplePlayerSetOffset,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->sampleplayer->setOffset(js2sampleplayer(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_samplePlayerGetOffset,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewFloat64(js, pd_sound->sampleplayer->getOffset(js2sampleplayer(js, argv[0])));
)
JSC_CCALL(sound_samplePlayerSetRate,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->sampleplayer->setRate(js2sampleplayer(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_samplePlayerGetRate,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewFloat64(js, pd_sound->sampleplayer->getRate(js2sampleplayer(js, argv[0])));
)
JSC_CCALL(sound_samplePlayerSetPlayRange,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->sampleplayer->setPlayRange(js2sampleplayer(js, argv[0]), (int)js2number(js, argv[1]), (int)js2number(js, argv[2]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_samplePlayerSetPaused,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->sampleplayer->setPaused(js2sampleplayer(js, argv[0]), JS_ToBool(js, argv[1]));
return JS_UNDEFINED;
)
// --- Synth ---
JSC_CCALL(sound_newSynth,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
PDSynth *s = pd_sound->synth->newSynth();
return s ? JS_NewInt64(js, (int64_t)(intptr_t)s) : JS_NULL;
)
JSC_CCALL(sound_freeSynth,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
PDSynth *s = js2synth(js, argv[0]);
if (s) pd_sound->synth->freeSynth(s);
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthSetWaveform,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->synth->setWaveform(js2synth(js, argv[0]), (SoundWaveform)(int)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthSetAttackTime,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->synth->setAttackTime(js2synth(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthSetDecayTime,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->synth->setDecayTime(js2synth(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthSetSustainLevel,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->synth->setSustainLevel(js2synth(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthSetReleaseTime,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->synth->setReleaseTime(js2synth(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthSetTranspose,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->synth->setTranspose(js2synth(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthPlayNote,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
PDSynth *s = js2synth(js, argv[0]);
float freq = (float)js2number(js, argv[1]);
float vel = argc > 2 ? (float)js2number(js, argv[2]) : 1.0f;
float len = argc > 3 ? (float)js2number(js, argv[3]) : -1.0f;
uint32_t when = argc > 4 ? (uint32_t)js2number(js, argv[4]) : 0;
pd_sound->synth->playNote(s, freq, vel, len, when);
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthPlayMIDINote,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
PDSynth *s = js2synth(js, argv[0]);
MIDINote note = (MIDINote)js2number(js, argv[1]);
float vel = argc > 2 ? (float)js2number(js, argv[2]) : 1.0f;
float len = argc > 3 ? (float)js2number(js, argv[3]) : -1.0f;
uint32_t when = argc > 4 ? (uint32_t)js2number(js, argv[4]) : 0;
pd_sound->synth->playMIDINote(s, note, vel, len, when);
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthNoteOff,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->synth->noteOff(js2synth(js, argv[0]), argc > 1 ? (uint32_t)js2number(js, argv[1]) : 0);
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthStop,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->synth->stop(js2synth(js, argv[0]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthSetVolume,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->synth->setVolume(js2synth(js, argv[0]), (float)js2number(js, argv[1]), (float)js2number(js, argv[2]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_synthGetVolume,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
float l, r; pd_sound->synth->getVolume(js2synth(js, argv[0]), &l, &r);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "left", JS_NewFloat64(js, l));
JS_SetPropertyStr(js, obj, "right", JS_NewFloat64(js, r));
return obj;
)
JSC_CCALL(sound_synthIsPlaying,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewBool(js, pd_sound->synth->isPlaying(js2synth(js, argv[0])));
)
JSC_CCALL(sound_synthGetParameterCount,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewInt32(js, pd_sound->synth->getParameterCount(js2synth(js, argv[0])));
)
JSC_CCALL(sound_synthSetParameter,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewBool(js, pd_sound->synth->setParameter(js2synth(js, argv[0]), (int)js2number(js, argv[1]), (float)js2number(js, argv[2])));
)
// --- Channel ---
JSC_CCALL(sound_newChannel,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
SoundChannel *ch = pd_sound->channel->newChannel();
return ch ? JS_NewInt64(js, (int64_t)(intptr_t)ch) : JS_NULL;
)
JSC_CCALL(sound_freeChannel,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
SoundChannel *ch = js2channel(js, argv[0]);
if (ch) pd_sound->channel->freeChannel(ch);
return JS_UNDEFINED;
)
JSC_CCALL(sound_channelSetVolume,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->channel->setVolume(js2channel(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
JSC_CCALL(sound_channelGetVolume,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
return JS_NewFloat64(js, pd_sound->channel->getVolume(js2channel(js, argv[0])));
)
JSC_CCALL(sound_channelSetPan,
if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized");
pd_sound->channel->setPan(js2channel(js, argv[0]), (float)js2number(js, argv[1]));
return JS_UNDEFINED;
)
static const JSCFunctionListEntry js_sound_funcs[] = {
MIST_FUNC_DEF(sound, getCurrentTime, 0),
MIST_FUNC_DEF(sound, getDefaultChannel, 0),
MIST_FUNC_DEF(sound, addChannel, 1),
MIST_FUNC_DEF(sound, removeChannel, 1),
MIST_FUNC_DEF(sound, setOutputsActive, 2),
MIST_FUNC_DEF(sound, getError, 0),
MIST_FUNC_DEF(sound, newFilePlayer, 0),
MIST_FUNC_DEF(sound, freeFilePlayer, 1),
MIST_FUNC_DEF(sound, loadIntoFilePlayer, 2),
MIST_FUNC_DEF(sound, filePlayerPlay, 2),
MIST_FUNC_DEF(sound, filePlayerIsPlaying, 1),
MIST_FUNC_DEF(sound, filePlayerPause, 1),
MIST_FUNC_DEF(sound, filePlayerStop, 1),
MIST_FUNC_DEF(sound, filePlayerSetVolume, 3),
MIST_FUNC_DEF(sound, filePlayerGetVolume, 1),
MIST_FUNC_DEF(sound, filePlayerGetLength, 1),
MIST_FUNC_DEF(sound, filePlayerSetOffset, 2),
MIST_FUNC_DEF(sound, filePlayerGetOffset, 1),
MIST_FUNC_DEF(sound, filePlayerSetRate, 2),
MIST_FUNC_DEF(sound, filePlayerGetRate, 1),
MIST_FUNC_DEF(sound, filePlayerSetLoopRange, 3),
MIST_FUNC_DEF(sound, filePlayerDidUnderrun, 1),
MIST_FUNC_DEF(sound, filePlayerSetStopOnUnderrun, 2),
MIST_FUNC_DEF(sound, filePlayerSetBufferLength, 2),
MIST_FUNC_DEF(sound, loadSample, 1),
MIST_FUNC_DEF(sound, freeSample, 1),
MIST_FUNC_DEF(sound, getSampleLength, 1),
MIST_FUNC_DEF(sound, newSampleBuffer, 1),
MIST_FUNC_DEF(sound, newSamplePlayer, 0),
MIST_FUNC_DEF(sound, freeSamplePlayer, 1),
MIST_FUNC_DEF(sound, samplePlayerSetSample, 2),
MIST_FUNC_DEF(sound, samplePlayerPlay, 3),
MIST_FUNC_DEF(sound, samplePlayerIsPlaying, 1),
MIST_FUNC_DEF(sound, samplePlayerStop, 1),
MIST_FUNC_DEF(sound, samplePlayerSetVolume, 3),
MIST_FUNC_DEF(sound, samplePlayerGetVolume, 1),
MIST_FUNC_DEF(sound, samplePlayerGetLength, 1),
MIST_FUNC_DEF(sound, samplePlayerSetOffset, 2),
MIST_FUNC_DEF(sound, samplePlayerGetOffset, 1),
MIST_FUNC_DEF(sound, samplePlayerSetRate, 2),
MIST_FUNC_DEF(sound, samplePlayerGetRate, 1),
MIST_FUNC_DEF(sound, samplePlayerSetPlayRange, 3),
MIST_FUNC_DEF(sound, samplePlayerSetPaused, 2),
MIST_FUNC_DEF(sound, newSynth, 0),
MIST_FUNC_DEF(sound, freeSynth, 1),
MIST_FUNC_DEF(sound, synthSetWaveform, 2),
MIST_FUNC_DEF(sound, synthSetAttackTime, 2),
MIST_FUNC_DEF(sound, synthSetDecayTime, 2),
MIST_FUNC_DEF(sound, synthSetSustainLevel, 2),
MIST_FUNC_DEF(sound, synthSetReleaseTime, 2),
MIST_FUNC_DEF(sound, synthSetTranspose, 2),
MIST_FUNC_DEF(sound, synthPlayNote, 5),
MIST_FUNC_DEF(sound, synthPlayMIDINote, 5),
MIST_FUNC_DEF(sound, synthNoteOff, 2),
MIST_FUNC_DEF(sound, synthStop, 1),
MIST_FUNC_DEF(sound, synthSetVolume, 3),
MIST_FUNC_DEF(sound, synthGetVolume, 1),
MIST_FUNC_DEF(sound, synthIsPlaying, 1),
MIST_FUNC_DEF(sound, synthGetParameterCount, 1),
MIST_FUNC_DEF(sound, synthSetParameter, 3),
MIST_FUNC_DEF(sound, newChannel, 0),
MIST_FUNC_DEF(sound, freeChannel, 1),
MIST_FUNC_DEF(sound, channelSetVolume, 2),
MIST_FUNC_DEF(sound, channelGetVolume, 1),
MIST_FUNC_DEF(sound, channelSetPan, 2),
};
JSValue js_sound_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_sound_funcs, countof(js_sound_funcs));
return mod;
}