Mercurial > molko
changeset 203:d3ef968745f5
core: rework audio API
- sound: module to play sounds (several at once),
- music: loopable music that can only be played one at once.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 09 Nov 2020 21:18:41 +0100 |
parents | baf7e6575181 |
children | c9fbb822d269 |
files | examples/CMakeLists.txt examples/assets/musics/vabsounds-romance.ogg examples/assets/sounds/vabsounds-romance.ogg examples/battle/spell-fire.c examples/example-audio.c examples/example-sound.c libcore/CMakeLists.txt libcore/core/music.c libcore/core/music.h libcore/core/sound.c libcore/core/sound.h |
diffstat | 11 files changed, 480 insertions(+), 184 deletions(-) [+] |
line wrap: on
line diff
--- a/examples/CMakeLists.txt Mon Nov 09 19:28:08 2020 +0100 +++ b/examples/CMakeLists.txt Mon Nov 09 21:18:41 2020 +0100 @@ -56,6 +56,16 @@ ) molko_define_executable( + TARGET example-audio + SOURCES example-audio.c + FOLDER examples + ASSETS + ${examples_SOURCE_DIR}/assets/musics/vabsounds-romance.ogg + ${examples_SOURCE_DIR}/assets/sounds/fire.wav + LIBRARIES libui +) + +molko_define_executable( TARGET example-cursor SOURCES example-cursor.c FOLDER examples @@ -91,15 +101,6 @@ ) molko_define_executable( - TARGET example-sound - SOURCES example-sound.c - FOLDER examples - ASSETS - ${examples_SOURCE_DIR}/assets/sounds/vabsounds-romance.ogg - LIBRARIES libui -) - -molko_define_executable( TARGET example-sprite SOURCES example-sprite.c FOLDER examples
--- a/examples/battle/spell-fire.c Mon Nov 09 19:28:08 2020 +0100 +++ b/examples/battle/spell-fire.c Mon Nov 09 21:18:41 2020 +0100 @@ -101,7 +101,7 @@ animation_init(&data->animation, ®istry_sprites[REGISTRY_TEXTURE_EXPLOSION], 12); animation_start(&data->animation); - sound_play(®istry_sounds[REGISTRY_SOUND_FIRE]); + sound_play(®istry_sounds[REGISTRY_SOUND_FIRE], -1, 0); action_stack_add(&bt->actions[0], &data->action); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/example-audio.c Mon Nov 09 21:18:41 2020 +0100 @@ -0,0 +1,155 @@ +/* + * example-sound.c -- show how to use sounds + * + * Copyright (c) 2020 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <core/clock.h> +#include <core/core.h> +#include <core/event.h> +#include <core/music.h> +#include <core/painter.h> +#include <core/panic.h> +#include <core/sound.h> +#include <core/sys.h> +#include <core/util.h> +#include <core/window.h> + +#include <ui/label.h> +#include <ui/theme.h> +#include <ui/ui.h> + +/* https://freesound.org/people/VABsounds/sounds/423658 */ +#include <assets/musics/vabsounds-romance.h> +#include <assets/sounds/fire.h> + +#define W 1280 +#define H 720 + +static struct music music; +static struct sound sound; + +static struct label label_music = { + .text = "Music: <Space> play, <f> fade in, <s> fade out, <p> pause, <r> resume, <q> stop, <l> loop.", + .x = 10, + .y = 10, + .flags = LABEL_FLAGS_SHADOW +}; + +static struct label label_sound = { + .text = "Sound: click anywhere to pop a sound.", + .x = 10, + .y = 30, + .flags = LABEL_FLAGS_SHADOW +}; + +static void +init(void) +{ + if (!core_init() || !ui_init()) + panic(); + if (!window_open("Example - Audio", W, H)) + panic(); + if (!music_openmem(&music, musics_vabsounds_romance, sizeof (musics_vabsounds_romance))) + panic(); + if (!sound_openmem(&sound, sounds_fire, sizeof (sounds_fire))) + panic(); +} + +static void +quit(void) +{ + window_finish(); + ui_finish(); + core_finish(); +} + +static void +run(void) +{ + struct clock clock = {0}; + + clock_start(&clock); + + for (;;) { + union event ev; + unsigned int elapsed = clock_elapsed(&clock); + + clock_start(&clock); + + while (event_poll(&ev)) { + switch (ev.type) { + case EVENT_CLICKDOWN: + if (!sound_play(&sound, -1, 0)) + panic(); + break; + case EVENT_KEYDOWN: + switch (ev.key.key) { + case KEY_f: + music_play(&music, 0, 500); + break; + case KEY_s: + music_stop(500); + break; + case KEY_p: + music_pause(); + break; + case KEY_r: + music_resume(); + break; + case KEY_q: + music_stop(0); + break; + case KEY_l: + music_play(&music, MUSIC_LOOP, 0); + break; + case KEY_SPACE: + music_play(&music, 0, 0); + break; + default: + break; + } + break; + case EVENT_QUIT: + return; + default: + break; + } + } + + painter_set_color(0x006554ff); + painter_clear(); + label_draw(&label_music); + label_draw(&label_sound); + painter_present(); + + if ((elapsed = clock_elapsed(&clock)) < 20) + delay(20 - elapsed); + } + + music_finish(&music); + sound_finish(&sound); +} + +int +main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + init(); + run(); + quit(); +}
--- a/examples/example-sound.c Mon Nov 09 19:28:08 2020 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ -/* - * example-sound.c -- show how to use sounds - * - * Copyright (c) 2020 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <core/clock.h> -#include <core/core.h> -#include <core/event.h> -#include <core/painter.h> -#include <core/panic.h> -#include <core/sound.h> -#include <core/sys.h> -#include <core/util.h> -#include <core/window.h> - -#include <ui/label.h> -#include <ui/theme.h> -#include <ui/ui.h> - -/* https://freesound.org/people/VABsounds/sounds/423658 */ -#include <assets/sounds/vabsounds-romance.h> - -#define W 1280 -#define H 720 - -static struct sound sound; -static struct label label = { - .text = "Keys: <s> start, <p> pause, <r> resume, <q> stop, <l> loop", - .x = 10, - .y = 10, - .flags = LABEL_FLAGS_SHADOW -}; - -static void -init(void) -{ - if (!core_init() || !ui_init()) - panic(); - if (!window_open("Example - Sound", W, H)) - panic(); - if (!sound_openmem(&sound, sounds_vabsounds_romance, sizeof (sounds_vabsounds_romance))) - panic(); -} - -static void -quit(void) -{ - window_finish(); - ui_finish(); - core_finish(); -} - -static void -run(void) -{ - struct clock clock = {0}; - - clock_start(&clock); - - for (;;) { - union event ev; - unsigned int elapsed = clock_elapsed(&clock); - - clock_start(&clock); - - while (event_poll(&ev)) { - switch (ev.type) { - case EVENT_KEYDOWN: - switch (ev.key.key) { - case KEY_s: - sound_play(&sound); - break; - case KEY_p: - sound_pause(&sound); - break; - case KEY_r: - sound_resume(&sound); - break; - case KEY_q: - sound_stop(&sound); - break; - case KEY_l: - sound.flags ^= SOUND_LOOP; - sound_play(&sound); - break; - default: - break; - } - break; - case EVENT_QUIT: - return; - default: - break; - } - } - - painter_set_color(0x006554ff); - painter_clear(); - label_draw(&label); - painter_present(); - - if ((elapsed = clock_elapsed(&clock)) < 20) - delay(20 - elapsed); - } -} - -int -main(int argc, char **argv) -{ - (void)argc; - (void)argv; - - init(); - run(); - quit(); -}
--- a/libcore/CMakeLists.txt Mon Nov 09 19:28:08 2020 +0100 +++ b/libcore/CMakeLists.txt Mon Nov 09 21:18:41 2020 +0100 @@ -58,6 +58,8 @@ ${libcore_SOURCE_DIR}/core/maths.c ${libcore_SOURCE_DIR}/core/maths.h ${libcore_SOURCE_DIR}/core/mouse.h + ${libcore_SOURCE_DIR}/core/music.c + ${libcore_SOURCE_DIR}/core/music.h ${libcore_SOURCE_DIR}/core/painter.c ${libcore_SOURCE_DIR}/core/painter.h ${libcore_SOURCE_DIR}/core/panic.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcore/core/music.c Mon Nov 09 21:18:41 2020 +0100 @@ -0,0 +1,117 @@ +/* + * music.h -- music support + * + * Copyright (c) 2020 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <assert.h> +#include <string.h> + +#include <SDL_mixer.h> + +#include "error.h" +#include "music.h" + +bool +music_open(struct music *mus, const char *path) +{ + assert(mus); + assert(path); + + if (!(mus->handle = Mix_LoadMUS(path))) + return errorf("%s", SDL_GetError()); + + return true; +} + +bool +music_openmem(struct music *mus, const void *buffer, size_t buffersz) +{ + assert(mus); + assert(buffer); + + SDL_RWops *ops; + + if (!(ops = SDL_RWFromConstMem(buffer, buffersz)) || + !(mus->handle = Mix_LoadMUS_RW(ops, true))) + return errorf("%s", SDL_GetError()); + + return true; +} + +bool +music_ok(const struct music *mus) +{ + return mus && mus->handle; +} + +bool +music_play(struct music *mus, enum music_flags flags, unsigned int fadein) +{ + assert(mus); + + int loops = flags & MUSIC_LOOP ? -1 : 1; + int ret; + + if (fadein > 0) + ret = Mix_FadeInMusic(mus->handle, loops, fadein); + else + ret = Mix_PlayMusic(mus->handle, loops); + + if (ret < 0) + return errorf("%s", SDL_GetError()); + + return true; +} + +bool +music_playing(void) +{ + return Mix_PlayingMusic(); +} + +void +music_pause(void) +{ + Mix_PauseMusic(); +} + +void +music_resume(void) +{ + Mix_ResumeMusic(); +} + +void +music_stop(unsigned int fadeout) +{ + if (fadeout > 0) + Mix_FadeOutMusic(fadeout); + else + Mix_HaltMusic(); +} + +void +music_finish(struct music *mus) +{ + assert(mus); + + if (mus->handle) { + Mix_HaltMusic(); + Mix_FreeMusic(mus->handle); + } + + memset(mus, 0, sizeof (*mus)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcore/core/music.h Mon Nov 09 21:18:41 2020 +0100 @@ -0,0 +1,138 @@ +/* + * music.h -- music support + * + * Copyright (c) 2020 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MOLKO_CORE_MUSIC_H +#define MOLKO_CORE_MUSIC_H + +/** + * \file music.h + * \brief Music support. + * + * This module provide support for playing music. In contrast to sounds only one + * music can be played at a time. + */ + +#include <stdbool.h> +#include <stddef.h> + +#include "plat.h" + +/** + * \brief Music flags. + */ +enum music_flags { + MUSIC_NONE, /*!< No flags. */ + MUSIC_LOOP = (1 << 0) /*!< Loop the music. */ +}; + +/** + * \brief Music object handle. + */ +struct music { + void *handle; /*!< (*) Implementation handle. */ +}; + +/** + * Open a music file. + * + * \pre mus != NULL + * \pre path != NULL + * \param mus the music object to initialize + * \param path the path to the music file + * \return False on errors. + */ +bool +music_open(struct music *mus, const char *path) PLAT_NODISCARD; + +/** + * Open a music from a buffer. + * + * \pre mus != NULL + * \pre buffer != NULL + * \param mus the music object to initialize + * \param buffer the buffer + * \param buffersz the buffer size + * \return False on errors. + * \warning The buffer must exists until the sound object is closed. + */ +bool +music_openmem(struct music *mus, const void *buffer, size_t buffersz) PLAT_NODISCARD; + +/** + * Check if this music handle is properly loaded. + * + * \param mus the music to check (may be NULL) + */ +bool +music_ok(const struct music *mus); + +/** + * Start playing the given music + * + * This function will resume the playback since the beginning and will stop the + * current music. If the music playing is currently fading out this function + * will block until it has finished. + * + * \pre mus != NULL + * \param mus the music to start + * \param flags optional flags + * \param fadein a fade in delay in milliseconds (0 to disable) + * \return False on errors. + */ +bool +music_play(struct music *mus, enum music_flags flags, unsigned int fadein); + +/** + * Tells if music is playing. + * + * You can call this function with any music even one that is not currently + * playing. + */ +bool +music_playing(void); + +/** + * Pause the music playback. + */ +void +music_pause(void); + +/** + * Resume the music playback. + */ +void +music_resume(void); + +/** + * Stop the sound music. + * + * \param fadeout a fade out delay in milliseconds (0 to disable) + */ +void +music_stop(unsigned int fadeout); + +/** + * Close the associated resources. + * + * \pre snd != NULL + * \param snd the sound object + */ +void +music_finish(struct music *mus); + +#endif /* !MOLKO_CORE_MUSIC_H */
--- a/libcore/core/sound.c Mon Nov 09 19:28:08 2020 +0100 +++ b/libcore/core/sound.c Mon Nov 09 21:18:41 2020 +0100 @@ -30,7 +30,7 @@ assert(snd); assert(path); - if (!(snd->handle = Mix_LoadMUS(path))) + if (!(snd->handle = Mix_LoadWAV(path))) return errorf("%s", SDL_GetError()); return true; @@ -45,52 +45,57 @@ SDL_RWops *ops; if (!(ops = SDL_RWFromConstMem(buffer, buffersz)) || - !(snd->handle = Mix_LoadMUS_RW(ops, true))) + !(snd->handle = Mix_LoadWAV_RW(ops, true))) return errorf("%s", SDL_GetError()); return true; } bool -sound_play(struct sound *snd) +sound_ok(const struct sound *snd) { - assert(snd); + return snd && snd->handle; +} + +bool +sound_play(struct sound *snd, int channel, unsigned int fadein) +{ + assert(sound_ok(snd)); - int n = 1; + int ret; - if (snd->flags & SOUND_LOOP) - n = -1; - if (Mix_PlayMusic(snd->handle, n) < 0) + if (fadein > 0) + ret = Mix_FadeInChannel(channel, snd->handle, 0, fadein); + else + ret = Mix_PlayChannel(channel, snd->handle, 0); + + if (ret < 0) return errorf("%s", SDL_GetError()); + snd->channel = channel; + return true; } void sound_pause(struct sound *snd) { - /* Not needed yet. */ - (void)snd; - - Mix_PauseMusic(); + Mix_Pause(snd ? snd->channel : -1); } void sound_resume(struct sound *snd) { - /* Not needed yet. */ - (void)snd; - - Mix_ResumeMusic(); + Mix_Resume(snd ? snd->channel : -1); } void -sound_stop(struct sound *snd) +sound_stop(struct sound *snd, unsigned int fadeout) { - /* Not needed yet. */ - (void)snd; - - Mix_HaltMusic(); + if (fadeout > 0) + Mix_FadeOutChannel(snd ? snd->channel : -1, fadeout); + else + Mix_HaltChannel(snd ? snd->channel : -1); } void @@ -99,8 +104,8 @@ assert(snd); if (snd->handle) { - Mix_HaltMusic(); - Mix_FreeMusic(snd->handle); + Mix_HaltChannel(snd->channel); + Mix_FreeChunk(snd->handle); } memset(snd, 0, sizeof (*snd));
--- a/libcore/core/sound.h Mon Nov 09 19:28:08 2020 +0100 +++ b/libcore/core/sound.h Mon Nov 09 21:18:41 2020 +0100 @@ -16,8 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef MOLKO_SOUND_H -#define MOLKO_SOUND_H +#ifndef MOLKO_CORE_SOUND_H +#define MOLKO_CORE_SOUND_H /** * \file sound.h @@ -30,19 +30,16 @@ #include "plat.h" /** - * \brief Sound flags. + * \brief Number of channels allocated. */ -enum sound_flags { - SOUND_NONE, /*!< No flags. */ - SOUND_LOOP = (1 << 0) /*!< Loop the music. */ -}; +#define SOUND_MAX_CHANNELS (256) /** * \brief Sound chunk. */ struct sound { - enum sound_flags flags; /*!< (+) Flags. */ void *handle; /*!< (*) Native handle. */ + int channel; /*!< (*) Current channel. */ }; /** @@ -69,49 +66,59 @@ * \warning The buffer must exists until the sound object is closed. */ bool -sound_openmem(struct sound *snd, const void *buffer, size_t buffersz); +sound_openmem(struct sound *snd, const void *buffer, size_t buffersz) PLAT_NODISCARD; + +/** + * Check if this sound handle is properly loaded. + * + * \param snd the sound to check (may be NULL) + */ +bool +sound_ok(const struct sound *snd); /** * Start playing the sound. * * This function will resume the playback since the beginning. * - * \pre snd != NULL + * \pre sound_ok(snd) * \param snd the sound object + * \param channel the channel to use (-1 for a default) + * \param fadein a fade in delay in milliseconds (0 to disable) * \return False on errors. */ bool -sound_play(struct sound *snd); +sound_play(struct sound *snd, int channel, unsigned int fadein); /** - * Pause the sound music. + * Pause the given sound or all sounds currently playing. * - * \pre snd != NULL - * \param snd the sound object + * \param snd the sound object (or NULL to pause all) */ void sound_pause(struct sound *snd); /** - * Resume the sound music. + * Resume the current sound or all sounds currently paused. * - * \pre snd != NULL - * \param snd the sound object + * \param snd the sound object (or NULL to resume all) */ void sound_resume(struct sound *snd); /** - * Stop the sound music. + * Stop the sound music or all sounds currently playing. * - * \pre snd != NULL + * \pre sound_ok(snd) * \param snd the sound object + * \param fadeout a fade out delay in milliseconds (0 to disable) */ void -sound_stop(struct sound *snd); +sound_stop(struct sound *snd, unsigned int fadeout); /** - * Close the associated resources. + * Close the associated resources. This will also stop the playback of the + * given sound. * * \pre snd != NULL * \param snd the sound object @@ -119,4 +126,4 @@ void sound_finish(struct sound *snd); -#endif /* !MOLKO_SOUND_H */ +#endif /* !MOLKO_CORE_SOUND_H */