// 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_NULL; ) 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_NULL; ) 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_NULL; ) JSC_CCALL(sound_filePlayerStop, if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized"); pd_sound->fileplayer->stop(js2fileplayer(js, argv[0])); return JS_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) // --- 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) // --- 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) JSC_CCALL(sound_synthStop, if (!pd_sound) return JS_ThrowInternalError(js, "sound not initialized"); pd_sound->synth->stop(js2synth(js, argv[0])); return JS_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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_NULL; ) 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; }