AtlatestRepositorysigil-audio

sigil-audio / tree / src / caudio.c

1/*
2 * audio.c - Sigil Audio Module
3 *
4 * Audio playback: a mixer of loaded sounds and one streamed music track over
5 * the playback device in miniaudio-impl.c (miniaudio).
6 * Uses stb_vorbis for OGG decoding.
7 *
8 * Sound effects are loaded entirely into memory.
9 * Music is streamed from disk via stb_vorbis.
10 */
12#include "sigil/sigil.h"
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <math.h>
19/* The playback device (miniaudio, in miniaudio-impl.c) */
20#include "audio-device.h"
22/* stb_vorbis for OGG decoding (implementation in stb_impl.c) */
23#include "stb_vorbis.c"
25/* Streaming audio sink (SPSC ring) */
26#include "audio-stream.h"
28/* ============================================================
29 * CONSTANTS
30 * ============================================================ */
32#define MAX_SOUNDS 64
33#define MAX_PLAYING_SOUNDS 16
34#define STREAM_BUFFER_SAMPLES 4096
36/* ============================================================
37 * DATA STRUCTURES
38 * ============================================================ */
40/* Sound effect - fully loaded into memory */
41typedef struct {
42 float *samples; /* Interleaved stereo samples */
43 int num_samples; /* Total samples (frames * channels) */
44 int sample_rate;
45 int channels;
46} StudioSound;
48/* Playing sound instance */
49typedef struct {
50 StudioSound *sound;
51 int position; /* Current playback position */
52 float volume;
53 float pan; /* -1.0 left, 0.0 center, 1.0 right */
54 bool playing;
55 bool loop;
56} PlayingSound;
58/* Music stream - decoded on the fly */
59typedef struct {
60 stb_vorbis *vorbis;
61 char *filepath; /* For reopening if looping */
62 float volume;
63 bool playing;
64 bool loop;
65 bool paused;
66} MusicStream;
68/* ============================================================
69 * GLOBAL STATE
70 * ============================================================ */
72static PlayingSound g_playing_sounds[MAX_PLAYING_SOUNDS];
73static MusicStream g_music = {0};
74static float g_master_volume = 1.0f;
75static float g_sounds_volume = 1.0f; /* the sounds' own gain (cues), under the master */
76static bool g_muted = false;
77static bool g_audio_initialized = false;
79/* Type tags for foreign objects */
80static Value sound_type_tag = SIGIL_UNDEFINED;
82/* ============================================================
83 * AUDIO CALLBACK
84 * ============================================================ */
86static void audio_callback(float *buffer, int num_frames, int num_channels)
88 /* Clear buffer */
89 memset(buffer, 0, num_frames * num_channels * sizeof(float));
91 if (g_muted) return;
93 float master = g_master_volume;
95 /* Mix playing sounds */
96 for (int i = 0; i < MAX_PLAYING_SOUNDS; i++) {
97 PlayingSound *ps = &g_playing_sounds[i];
98 /* acquire: the slot's fields were written before playing was set */
99 if (!__atomic_load_n(&ps->playing, __ATOMIC_ACQUIRE) || !ps->sound) continue;
101 StudioSound *snd = ps->sound;
102 float vol = ps->volume * master * g_sounds_volume;
104 /* Calculate pan gains */
105 float pan = ps->pan;
106 float left_gain = vol * (pan <= 0 ? 1.0f : 1.0f - pan);
107 float right_gain = vol * (pan >= 0 ? 1.0f : 1.0f + pan);
109 for (int f = 0; f < num_frames; f++) {
110 if (ps->position >= snd->num_samples / snd->channels) {
111 if (ps->loop) {
112 ps->position = 0;
113 } else {
114 ps->playing = false;
115 break;
116 }
117 }
119 float left, right;
120 if (snd->channels == 1) {
121 /* Mono */
122 left = right = snd->samples[ps->position];
123 } else {
124 /* Stereo */
125 left = snd->samples[ps->position * 2];
126 right = snd->samples[ps->position * 2 + 1];
127 }
129 if (num_channels >= 2) {
130 buffer[f * num_channels] += left * left_gain;
131 buffer[f * num_channels + 1] += right * right_gain;
132 } else {
133 buffer[f] += (left + right) * 0.5f * vol;
134 }
136 ps->position++;
137 }
138 }
140 /* Mix music stream */
141 if (g_music.playing && !g_music.paused && g_music.vorbis) {
142 float vol = g_music.volume * master;
143 float temp[STREAM_BUFFER_SAMPLES * 2];
144 int samples_needed = num_frames;
145 int offset = 0;
147 while (samples_needed > 0) {
148 int to_decode = samples_needed < STREAM_BUFFER_SAMPLES ?
149 samples_needed : STREAM_BUFFER_SAMPLES;
151 int decoded = stb_vorbis_get_samples_float_interleaved(
152 g_music.vorbis, 2, temp, to_decode * 2);
154 if (decoded == 0) {
155 /* End of file */
156 if (g_music.loop && g_music.filepath) {
157 /* Reopen and continue */
158 stb_vorbis_close(g_music.vorbis);
159 int error;
160 g_music.vorbis = stb_vorbis_open_filename(
161 g_music.filepath, &error, NULL);
162 if (!g_music.vorbis) {
163 g_music.playing = false;
164 break;
165 }
166 continue;
167 } else {
168 g_music.playing = false;
169 break;
170 }
171 }
173 /* Mix decoded samples */
174 for (int f = 0; f < decoded; f++) {
175 int buf_idx = (offset + f) * num_channels;
176 if (num_channels >= 2) {
177 buffer[buf_idx] += temp[f * 2] * vol;
178 buffer[buf_idx + 1] += temp[f * 2 + 1] * vol;
179 } else {
180 buffer[buf_idx] += (temp[f * 2] + temp[f * 2 + 1]) * 0.5f * vol;
181 }
182 }
184 samples_needed -= decoded;
185 offset += decoded;
186 }
187 }
189 /* Mix streaming sinks (SPSC ring sources). Additive, silence on under-run. */
190 sigil_audio_stream_mix_all(buffer, num_frames, num_channels);
192 /* Clamp output */
193 for (int i = 0; i < num_frames * num_channels; i++) {
194 if (buffer[i] > 1.0f) buffer[i] = 1.0f;
195 if (buffer[i] < -1.0f) buffer[i] = -1.0f;
196 }
199/* ============================================================
200 * HELPER FUNCTIONS
201 * ============================================================ */
203static void ensure_sound_type(SigilVM *vm)
205 if (sigil_is_undefined(sound_type_tag)) {
206 sound_type_tag = sigil_intern_symbol(vm, "sigil-audio-sound", 17);
207 }
210static StudioSound *get_sound(SigilVM *vm, Value v)
212 if (!sigil_is_foreign(v)) return NULL;
213 ensure_sound_type(vm);
214 if (sigil_foreign_type(v) != sound_type_tag) return NULL;
215 return (StudioSound *)sigil_foreign_data(v);
218static void sound_destructor(void *data)
220 StudioSound *snd = (StudioSound *)data;
221 if (snd) {
222 free(snd->samples);
223 free(snd);
224 }
227static PlayingSound *find_free_slot(void)
229 for (int i = 0; i < MAX_PLAYING_SOUNDS; i++) {
230 if (!g_playing_sounds[i].playing) {
231 return &g_playing_sounds[i];
232 }
233 }
234 return NULL;
237/* ============================================================
238 * NATIVE FUNCTIONS - SETUP
239 * ============================================================ */
241/*
242 * (audio-setup) - Initialize audio subsystem
243 */
244static Value native_audio_setup(SigilVM *vm, int argc, Value *args)
246 (void)vm; (void)argc; (void)args;
248 if (g_audio_initialized) {
249 return SIGIL_NIL;
250 }
252 /* No device (a headless runner, no sound server, no ALSA card) is not
253 * an error, as it was not under sokol_audio: the mixer runs with no
254 * output, every play is a no-op, and (audio-initialized?) is still
255 * #t. miniaudio-impl.c has already said why on stderr. */
256 if (sigil_audio_device_open(SIGIL_AUDIO_DEVICE_RATE, 2, 2048, audio_callback) != 0) {
257 fprintf(stderr, "sigil-audio: continuing without an audio device\n");
258 }
260 /* Clear playing sounds */
261 memset(g_playing_sounds, 0, sizeof(g_playing_sounds));
263 /* Clear music */
264 memset(&g_music, 0, sizeof(g_music));
265 g_music.volume = 1.0f;
267 g_master_volume = 1.0f;
268 g_muted = false;
269 g_audio_initialized = true;
271 return SIGIL_NIL;
274/*
275 * (audio-shutdown) - Shutdown audio subsystem
276 */
277static Value native_audio_shutdown(SigilVM *vm, int argc, Value *args)
279 (void)vm; (void)argc; (void)args;
281 if (g_audio_initialized) {
282 /* Detach any active streaming sinks before tearing down the device */
283 sigil_audio_stream_shutdown_all();
285 /* Stop music */
286 if (g_music.vorbis) {
287 stb_vorbis_close(g_music.vorbis);
288 g_music.vorbis = NULL;
289 }
290 free(g_music.filepath);
291 g_music.filepath = NULL;
293 sigil_audio_device_close();
294 g_audio_initialized = false;
295 }
297 return SIGIL_NIL;
300/*
301 * (audio-initialized?) -> boolean
302 */
303static Value native_audio_initialized(SigilVM *vm, int argc, Value *args)
305 (void)vm; (void)argc; (void)args;
306 return g_audio_initialized ? SIGIL_TRUE : SIGIL_FALSE;
309/*
310 * (audio-backend) -> symbol or #f
311 *
312 * The backend the device runs on: 'pulseaudio (also on a PipeWire desktop,
313 * through pipewire-pulse), 'alsa, 'jack, 'wasapi, 'dsound, 'winmm,
314 * 'coreaudio, or 'null when no backend library or server was usable and
315 * frames are consumed in silence; #f before audio-setup.
316 */
317static Value native_audio_backend(SigilVM *vm, int argc, Value *args)
319 (void)argc; (void)args;
320 const char *name = sigil_audio_device_backend_symbol();
321 if (!name) return SIGIL_FALSE;
322 return sigil_intern_symbol(vm, name, strlen(name));
325/*
326 * (audio-device?) -> boolean
327 *
328 * #t when sound can reach a listener: a device is open on a real backend.
329 * (audio-initialized?) stays #t after audio-setup whatever the backend,
330 * because the mixer and the streams work either way (as under sokol_audio,
331 * where an invalid device was silent but initialized); this predicate
332 * names the new fact a game may want: whether to bother with sound.
333 */
334static Value native_audio_device(SigilVM *vm, int argc, Value *args)
336 (void)vm; (void)argc; (void)args;
337 return sigil_audio_device_real() ? SIGIL_TRUE : SIGIL_FALSE;
340/* ============================================================
341 * NATIVE FUNCTIONS - SOUNDS
342 * ============================================================ */
344/*
345 * (load-sound path) -> <sound> or #f
346 *
347 * Load an OGG file entirely into memory.
348 */
349static Value native_load_sound(SigilVM *vm, int argc, Value *args)
351 if (argc < 1 || !sigil_is_string(args[0])) {
352 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "load-sound: expected string path");
353 return SIGIL_FALSE;
354 }
356 SigilString *path_str = (SigilString *)sigil_as_ptr(args[0]);
357 const char *path = path_str->data;
359 int channels, sample_rate;
360 short *raw_samples;
361 int num_samples = stb_vorbis_decode_filename(path, &channels, &sample_rate,
362 &raw_samples);
363 if (num_samples < 0) {
364 return SIGIL_FALSE;
365 }
367 /* Convert to float */
368 int total_samples = num_samples * channels;
369 float *samples = malloc(total_samples * sizeof(float));
370 if (!samples) {
371 free(raw_samples);
372 return SIGIL_FALSE;
373 }
375 for (int i = 0; i < total_samples; i++) {
376 samples[i] = raw_samples[i] / 32768.0f;
377 }
378 free(raw_samples);
380 StudioSound *snd = malloc(sizeof(StudioSound));
381 if (!snd) {
382 free(samples);
383 return SIGIL_FALSE;
384 }
386 snd->samples = samples;
387 snd->num_samples = total_samples;
388 snd->sample_rate = sample_rate;
389 snd->channels = channels;
391 ensure_sound_type(vm);
392 return sigil_make_foreign(vm, sound_type_tag, snd, sound_destructor,
393 sizeof(StudioSound) + total_samples * sizeof(float));
396/*
397 * (sound? obj) -> boolean
398 */
399static Value native_sound_p(SigilVM *vm, int argc, Value *args)
401 (void)argc;
402 return get_sound(vm, args[0]) ? SIGIL_TRUE : SIGIL_FALSE;
405/*
406 * (play-sound sound [volume] [pan] [loop?]) -> boolean
407 *
408 * Play a sound effect. Returns #t if started, #f if no slots available.
409 */
410static Value native_play_sound(SigilVM *vm, int argc, Value *args)
412 if (argc < 1) {
413 sigil__vm_set_error(vm, SIGIL_ERR_ARITY, "play-sound: requires sound argument");
414 return SIGIL_FALSE;
415 }
417 StudioSound *snd = get_sound(vm, args[0]);
418 if (!snd) {
419 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "play-sound: expected sound");
420 return SIGIL_FALSE;
421 }
423 PlayingSound *ps = find_free_slot();
424 if (!ps) {
425 return SIGIL_FALSE; /* No slots available */
426 }
428 ps->sound = snd;
429 ps->position = 0;
430 ps->volume = argc > 1 ? (float)sigil_as_flonum(args[1]) : 1.0f;
431 ps->pan = argc > 2 ? (float)sigil_as_flonum(args[2]) : 0.0f;
432 ps->loop = argc > 3 ? sigil_is_true(args[3]) : false;
433 /* release: the audio thread sees the fields above once it sees playing */
434 __atomic_store_n(&ps->playing, true, __ATOMIC_RELEASE);
436 return SIGIL_TRUE;
439/*
440 * (%sound-from-buffer bv channels) -> sound | #f
441 *
442 * A sound from interleaved float32 PCM in memory (channels 1 or 2) at the
443 * device's rate, copied: the cues of (sigil audio sink) on native. play-sound
444 * mixes it in the device callback, so it is heard one device period after
445 * the call, whatever any stream's ring holds.
446 */
447static Value native_sound_from_buffer(SigilVM *vm, int argc, Value *args)
449 if (argc < 2 || !sigil_is_bytevector(args[0]) || !sigil_is_fixnum(args[1])) {
450 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "%sound-from-buffer: bytevector channels");
451 return SIGIL_FALSE;
452 }
453 int channels = sigil_as_fixnum(args[1]) == 1 ? 1 : 2;
454 size_t bytes = sigil_bytevector_length(args[0]);
455 int frames = (int)(bytes / (sizeof(float) * (size_t)channels));
456 if (frames <= 0) return SIGIL_FALSE;
457 int total = frames * channels;
458 float *samples = malloc((size_t)total * sizeof(float));
459 if (!samples) return SIGIL_FALSE;
460 memcpy(samples, sigil_bytevector_data(args[0]), (size_t)total * sizeof(float));
461 StudioSound *snd = malloc(sizeof(StudioSound));
462 if (!snd) {
463 free(samples);
464 return SIGIL_FALSE;
465 }
466 snd->samples = samples;
467 snd->num_samples = total;
468 snd->sample_rate = SIGIL_AUDIO_DEVICE_RATE;
469 snd->channels = channels;
470 ensure_sound_type(vm);
471 return sigil_make_foreign(vm, sound_type_tag, snd, sound_destructor,
472 sizeof(StudioSound) + (size_t)total * sizeof(float));
475/*
476 * (%set-sounds-volume volume) - the gain of every play-sound sound, under
477 * the master volume (the cues' volume on native)
478 */
479static Value native_set_sounds_volume(SigilVM *vm, int argc, Value *args)
481 (void)vm; (void)argc;
482 float v = 1.0f;
483 if (sigil_is_flonum(args[0])) v = (float)sigil_as_flonum(args[0]);
484 else if (sigil_is_fixnum(args[0])) v = (float)sigil_as_fixnum(args[0]);
485 if (v < 0.0f) v = 0.0f;
486 if (v > 1.0f) v = 1.0f;
487 g_sounds_volume = v;
488 return SIGIL_NIL;
491/*
492 * (stop-all-sounds) - Stop all playing sound effects
493 */
494static Value native_stop_all_sounds(SigilVM *vm, int argc, Value *args)
496 (void)vm; (void)argc; (void)args;
498 for (int i = 0; i < MAX_PLAYING_SOUNDS; i++) {
499 g_playing_sounds[i].playing = false;
500 }
502 return SIGIL_NIL;
505/* ============================================================
506 * NATIVE FUNCTIONS - MUSIC
507 * ============================================================ */
509/*
510 * (play-music path [loop?]) -> boolean
511 *
512 * Start streaming music from an OGG file.
513 */
514static Value native_play_music(SigilVM *vm, int argc, Value *args)
516 if (argc < 1 || !sigil_is_string(args[0])) {
517 sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "play-music: expected string path");
518 return SIGIL_FALSE;
519 }
521 /* Stop any existing music */
522 if (g_music.vorbis) {
523 stb_vorbis_close(g_music.vorbis);
524 g_music.vorbis = NULL;
525 }
526 free(g_music.filepath);
527 g_music.filepath = NULL;
529 SigilString *path_str = (SigilString *)sigil_as_ptr(args[0]);
530 const char *path = path_str->data;
532 int error;
533 g_music.vorbis = stb_vorbis_open_filename(path, &error, NULL);
534 if (!g_music.vorbis) {
535 return SIGIL_FALSE;
536 }
538 g_music.filepath = strdup(path);
539 g_music.loop = argc > 1 ? sigil_is_true(args[1]) : true;
540 g_music.playing = true;
541 g_music.paused = false;
543 return SIGIL_TRUE;
546/*
547 * (stop-music) - Stop music playback
548 */
549static Value native_stop_music(SigilVM *vm, int argc, Value *args)
551 (void)vm; (void)argc; (void)args;
553 if (g_music.vorbis) {
554 stb_vorbis_close(g_music.vorbis);
555 g_music.vorbis = NULL;
556 }
557 free(g_music.filepath);
558 g_music.filepath = NULL;
559 g_music.playing = false;
561 return SIGIL_NIL;
564/*
565 * (pause-music) - Pause music playback
566 */
567static Value native_pause_music(SigilVM *vm, int argc, Value *args)
569 (void)vm; (void)argc; (void)args;
570 g_music.paused = true;
571 return SIGIL_NIL;
574/*
575 * (resume-music) - Resume music playback
576 */
577static Value native_resume_music(SigilVM *vm, int argc, Value *args)
579 (void)vm; (void)argc; (void)args;
580 g_music.paused = false;
581 return SIGIL_NIL;
584/*
585 * (music-playing?) -> boolean
586 */
587static Value native_music_playing(SigilVM *vm, int argc, Value *args)
589 (void)vm; (void)argc; (void)args;
590 return (g_music.playing && !g_music.paused) ? SIGIL_TRUE : SIGIL_FALSE;
593/*
594 * (set-music-volume volume) - Set music volume (0.0 to 1.0)
595 */
596static Value native_set_music_volume(SigilVM *vm, int argc, Value *args)
598 if (argc < 1) {
599 sigil__vm_set_error(vm, SIGIL_ERR_ARITY, "set-music-volume: requires volume");
600 return SIGIL_NIL;
601 }
603 float vol = (float)sigil_as_flonum(args[0]);
604 if (vol < 0.0f) vol = 0.0f;
605 if (vol > 1.0f) vol = 1.0f;
606 g_music.volume = vol;
608 return SIGIL_NIL;
611/* ============================================================
612 * NATIVE FUNCTIONS - GLOBAL CONTROL
613 * ============================================================ */
615/*
616 * (set-master-volume volume) - Set master volume (0.0 to 1.0)
617 */
618static Value native_set_master_volume(SigilVM *vm, int argc, Value *args)
620 if (argc < 1) {
621 sigil__vm_set_error(vm, SIGIL_ERR_ARITY, "set-master-volume: requires volume");
622 return SIGIL_NIL;
623 }
625 float vol = (float)sigil_as_flonum(args[0]);
626 if (vol < 0.0f) vol = 0.0f;
627 if (vol > 1.0f) vol = 1.0f;
628 g_master_volume = vol;
630 return SIGIL_NIL;
633/*
634 * (mute-audio) - Mute all audio
635 */
636static Value native_mute_audio(SigilVM *vm, int argc, Value *args)
638 (void)vm; (void)argc; (void)args;
639 g_muted = true;
640 return SIGIL_NIL;
643/*
644 * (unmute-audio) - Unmute audio
645 */
646static Value native_unmute_audio(SigilVM *vm, int argc, Value *args)
648 (void)vm; (void)argc; (void)args;
649 g_muted = false;
650 return SIGIL_NIL;
653/*
654 * (audio-muted?) -> boolean
655 */
656static Value native_audio_muted(SigilVM *vm, int argc, Value *args)
658 (void)vm; (void)argc; (void)args;
659 return g_muted ? SIGIL_TRUE : SIGIL_FALSE;
662/* ============================================================
663 * MODULE INITIALIZATION
664 * ============================================================ */
666/* OGG encoding (ogg-encode.c) */
667extern void sigil__register_ogg_encode(SigilVM *vm);
669void sigil__init_sigil_audio_module(SigilVM *vm)
671 SigilModule *module = sigil_begin_module(vm, "(sigil audio)");
672 if (!module) return;
674 /* Setup/shutdown */
675 sigil_module_register_native(vm, "audio-setup", native_audio_setup,
676 SIGIL_ARITY_EXACT(0), "Initialize audio");
677 sigil_module_register_native(vm, "audio-shutdown", native_audio_shutdown,
678 SIGIL_ARITY_EXACT(0), "Shutdown audio");
679 sigil_module_register_native(vm, "audio-backend", native_audio_backend,
680 SIGIL_ARITY_EXACT(0), "The audio backend in use, as a symbol, or #f");
681 sigil_module_register_native(vm, "audio-device?", native_audio_device,
682 SIGIL_ARITY_EXACT(0), "Is a real (non-null) audio device open?");
683 sigil_module_register_native(vm, "audio-initialized?", native_audio_initialized,
684 SIGIL_ARITY_EXACT(0), "Is audio initialized?");
686 /* Sound effects */
687 sigil_module_register_native(vm, "load-sound", native_load_sound,
688 SIGIL_ARITY_EXACT(1), "Load OGG sound into memory");
689 sigil_module_register_native(vm, "sound?", native_sound_p,
690 SIGIL_ARITY_EXACT(1), "Check if object is a sound");
691 sigil_module_register_native(vm, "play-sound", native_play_sound,
692 SIGIL_ARITY_RANGE(1, 4), "Play sound effect");
693 sigil_module_register_native(vm, "stop-all-sounds", native_stop_all_sounds,
694 SIGIL_ARITY_EXACT(0), "Stop all sound effects");
695 sigil_module_register_native(vm, "%sound-from-buffer", native_sound_from_buffer,
696 SIGIL_ARITY_EXACT(2), "A sound from float32 PCM in memory (internal)");
697 sigil_module_register_native(vm, "%set-sounds-volume", native_set_sounds_volume,
698 SIGIL_ARITY_EXACT(1), "The gain of play-sound sounds (internal)");
700 /* Music streaming */
701 sigil_module_register_native(vm, "play-music", native_play_music,
702 SIGIL_ARITY_RANGE(1, 2), "Stream music from file");
703 sigil_module_register_native(vm, "stop-music", native_stop_music,
704 SIGIL_ARITY_EXACT(0), "Stop music");
705 sigil_module_register_native(vm, "pause-music", native_pause_music,
706 SIGIL_ARITY_EXACT(0), "Pause music");
707 sigil_module_register_native(vm, "resume-music", native_resume_music,
708 SIGIL_ARITY_EXACT(0), "Resume music");
709 sigil_module_register_native(vm, "music-playing?", native_music_playing,
710 SIGIL_ARITY_EXACT(0), "Is music playing?");
711 sigil_module_register_native(vm, "set-music-volume", native_set_music_volume,
712 SIGIL_ARITY_EXACT(1), "Set music volume");
714 /* Global control */
715 sigil_module_register_native(vm, "set-master-volume", native_set_master_volume,
716 SIGIL_ARITY_EXACT(1), "Set master volume");
717 sigil_module_register_native(vm, "mute-audio", native_mute_audio,
718 SIGIL_ARITY_EXACT(0), "Mute all audio");
719 sigil_module_register_native(vm, "unmute-audio", native_unmute_audio,
720 SIGIL_ARITY_EXACT(0), "Unmute audio");
721 sigil_module_register_native(vm, "audio-muted?", native_audio_muted,
722 SIGIL_ARITY_EXACT(0), "Is audio muted?");
724 /* Export all */
725 sigil_module_export(vm, "audio-setup");
726 sigil_module_export(vm, "audio-shutdown");
727 sigil_module_export(vm, "audio-initialized?");
728 sigil_module_export(vm, "audio-backend");
729 sigil_module_export(vm, "audio-device?");
730 sigil_module_export(vm, "load-sound");
731 sigil_module_export(vm, "sound?");
732 sigil_module_export(vm, "play-sound");
733 sigil_module_export(vm, "stop-all-sounds");
734 sigil_module_export(vm, "%sound-from-buffer");
735 sigil_module_export(vm, "%set-sounds-volume");
736 sigil_module_export(vm, "play-music");
737 sigil_module_export(vm, "stop-music");
738 sigil_module_export(vm, "pause-music");
739 sigil_module_export(vm, "resume-music");
740 sigil_module_export(vm, "music-playing?");
741 sigil_module_export(vm, "set-music-volume");
742 sigil_module_export(vm, "set-master-volume");
743 sigil_module_export(vm, "mute-audio");
744 sigil_module_export(vm, "unmute-audio");
745 sigil_module_export(vm, "audio-muted?");
747 /* OGG encoding */
748 sigil__register_ogg_encode(vm);
750 /* Streaming sink */
751 sigil__register_audio_stream(vm);
753 sigil_end_module(vm);