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
Binary file examples/assets/musics/vabsounds-romance.ogg has changed
Binary file examples/assets/sounds/vabsounds-romance.ogg has changed
--- 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, &registry_sprites[REGISTRY_TEXTURE_EXPLOSION], 12);
 	animation_start(&data->animation);
 
-	sound_play(&registry_sounds[REGISTRY_SOUND_FIRE]);
+	sound_play(&registry_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 */