diff --git a/src/dusk/audio/CMakeLists.txt b/src/dusk/audio/CMakeLists.txt index 18ee2b45..2b9098d8 100644 --- a/src/dusk/audio/CMakeLists.txt +++ b/src/dusk/audio/CMakeLists.txt @@ -7,7 +7,8 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC audio.c - audiostream.c - audiostreampcm.c - audiostreammp3.c -) \ No newline at end of file +) + +# Subdirs +add_subdirectory(mixer) +add_subdirectory(stream) \ No newline at end of file diff --git a/src/dusk/audio/audio.h b/src/dusk/audio/audio.h index def8bd4f..73e23aac 100644 --- a/src/dusk/audio/audio.h +++ b/src/dusk/audio/audio.h @@ -7,7 +7,7 @@ #pragma once #include "dusk.h" -#include "audio/audiostream.h" +#include "audio/stream/audiostream.h" #include "audio/audioplatform.h" #ifndef audioPlatformInit diff --git a/src/dusk/audio/mixer/CMakeLists.txt b/src/dusk/audio/mixer/CMakeLists.txt new file mode 100644 index 00000000..edfdb04b --- /dev/null +++ b/src/dusk/audio/mixer/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + audiomixer.c +) \ No newline at end of file diff --git a/src/dusk/audio/mixer/audiomixer.c b/src/dusk/audio/mixer/audiomixer.c new file mode 100644 index 00000000..778dba03 --- /dev/null +++ b/src/dusk/audio/mixer/audiomixer.c @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "audiomixer.h" +#include "util/memory.h" +#include "util/string.h" +#include "assert/assert.h" + +audiomixer_t AUDIO_MIXER; + +errorret_t audioMixerInit() { + memoryZero(&AUDIO_MIXER, sizeof(audiomixer_t)); + + errorOk(); +} + +errorret_t audioMixerUpdate() { + // For each channel. + audiomixerchannel_t channel = 0; + do { + audiomixerchanneldata_t *queue = &AUDIO_MIXER.queue[channel]; + + // File queued? + if(queue->file[0] == '\0') { + channel++; + continue; + } + + // Stop this channel + audioMixerStop(channel); + + // Get the file + // TODO: Loading? + // assetentry_t *entry = assetLock(queue->file, ASSET_LOADER_TYPE_WAV, NULL); + + printf("Playing %s on channel %d\n", queue->file, channel); + } while(channel < AUDIO_MIXER_CHANNEL_COUNT); + + errorOk(); +} + +void audioMixerPlay( + const char_t *file, + const audiomixerchannel_t channel, + const uint8_t volume, + const int8_t pan +) { + audioMixerPlayLooped(file, channel, volume, pan, 0, 0.0f, 0.0f); +} + +void audioMixerPlayLooped( + const char_t *file, + const audiomixerchannel_t channel, + const uint8_t volume, + const int8_t pan, + const uint8_t loopCount, + const float_t loopStart, + const float_t loopTo +) { + assertStrLenMin(file, 1, "File is required, to stop use audioMixerStop"); + assertTrue(channel < AUDIO_MIXER_CHANNEL_COUNT, "Channel is out of range"); + assertTrue(volume <= 0xFF, "Volume is out of range"); + assertTrue(volume > 0, "Volume must be greater than 0"); + assertTrue(loopStart >= 0.0f, "Loop start incorrect"); + assertTrue(loopTo >= 0.0f, "Loop to incorrect"); + + audiomixerchanneldata_t *queue = &AUDIO_MIXER.queue[channel]; + + stringCopy(queue->file, file, AUDIO_PATH_MAX); + queue->volume = volume; + queue->pan = pan; + queue->loopCount = loopCount; + queue->loopStart = loopStart; + queue->loopTo = loopTo; +} + +void audioMixerStop(const audiomixerchannel_t channel) { + assertTrue(channel < AUDIO_MIXER_CHANNEL_COUNT, "Channel is out of range"); + printf("Stopping channel %d\n", channel); +} + +errorret_t audioMixerDispose() { + audiomixerchannel_t channel = 0; + do { + audioMixerStop(channel); + channel++; + } while(channel < AUDIO_MIXER_CHANNEL_COUNT); + + errorOk(); +} \ No newline at end of file diff --git a/src/dusk/audio/mixer/audiomixer.h b/src/dusk/audio/mixer/audiomixer.h new file mode 100644 index 00000000..29d560d1 --- /dev/null +++ b/src/dusk/audio/mixer/audiomixer.h @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "asset/asset.h" + +typedef enum { + AUDIO_MIXER_CHANNEL_BGM_0, + AUDIO_MIXER_CHANNEL_VOICE_0, + AUDIO_MIXER_CHANNEL_VOICE_1, + AUDIO_MIXER_CHANNEL_SFX_0, + AUDIO_MIXER_CHANNEL_SFX_1, + AUDIO_MIXER_CHANNEL_SFX_2, + AUDIO_MIXER_CHANNEL_SFX_3, + AUDIO_MIXER_CHANNEL_COUNT +} audiomixerchannel_t; + +#define AUDIO_PATH_MAX 256 + +typedef struct { + assetentry_t *asset; + + char_t file[AUDIO_PATH_MAX]; + uint8_t volume; + int8_t pan; + uint8_t loopCount; + float_t loopStart; + float_t loopTo; +} audiomixerchanneldata_t; + +typedef struct { + audiomixerchanneldata_t queue[AUDIO_MIXER_CHANNEL_COUNT]; + uint8_t fade[AUDIO_MIXER_CHANNEL_COUNT]; +} audiomixer_t; + +extern audiomixer_t AUDIO_MIXER; + +/** + * Initializes the audio mixer. + */ +errorret_t audioMixerInit(); + +/** + * Updates the audio mixer. + */ +errorret_t audioMixerUpdate(); + +/** + * Same as audioMixerPlayLooped() but with no looping. + * + * @param file File to be played. + * @param channel Channel you want to play this sound on. + * @param volume Volume of the sound, from 0 (silent) to 0xFF (loudest). + * @param pan Stereo panning of the sound, from AUDIO_STREAM_LEFT to + * AUDIO_STREAM_RIGHT. + */ +void audioMixerPlay( + const char_t *file, + const audiomixerchannel_t channel, + const uint8_t volume, + const int8_t pan +); + +/** + * Queues a sound to be played on the next audio mixer update, which occurs at + * the end of each frame. + * + * Mixer channels can only hold one voice (audio stream) at a time. If you try + * to queue multiple sounds on the same channel only the last one will be played + * and will stop any actively playing on that channel currently. + * + * Volume is also mixed based on the channel, so supplying volume 0xFF is then + * multiplied by the channel's volume, e.g. 0xDD*0xFF = 0xDD. + * + * @param file File to be played. + * @param channel Channel you want to play this sound on. + * @param volume Volume of the sound, from 0 (silent) to 0xFF (loudest). + * @param pan Stereo panning of the sound, from AUDIO_STREAM_LEFT to + * AUDIO_STREAM_RIGHT. + * @param loopCount How many times to loop the sound, 0 for infinite looping. + * @param loopStart Where the loop segment ends, in seconds, or -1 to loop + * the whole stream (the default). + * @param loopTo Where the loop segment starts, in seconds. + */ +void audioMixerPlayLooped( + const char_t *file, + const audiomixerchannel_t channel, + const uint8_t volume, + const int8_t pan, + const uint8_t loopCount, + const float_t loopStart, + const float_t loopTo +); + +/** + * Stops playing sound on the given channel. + * + * @param channel Channel to stop. + */ +void audioMixerStop(const audiomixerchannel_t channel); + +/** + * Disposes the audio mixer. + */ +errorret_t audioMixerDispose(); \ No newline at end of file diff --git a/src/dusk/audio/stream/CMakeLists.txt b/src/dusk/audio/stream/CMakeLists.txt new file mode 100644 index 00000000..00e2a660 --- /dev/null +++ b/src/dusk/audio/stream/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + audiostream.c + audiostreampcm.c + audiostreammp3.c +) \ No newline at end of file diff --git a/src/dusk/audio/audiostream.c b/src/dusk/audio/stream/audiostream.c similarity index 100% rename from src/dusk/audio/audiostream.c rename to src/dusk/audio/stream/audiostream.c diff --git a/src/dusk/audio/audiostream.h b/src/dusk/audio/stream/audiostream.h similarity index 99% rename from src/dusk/audio/audiostream.h rename to src/dusk/audio/stream/audiostream.h index f92bb287..060e7467 100644 --- a/src/dusk/audio/audiostream.h +++ b/src/dusk/audio/stream/audiostream.h @@ -6,8 +6,8 @@ */ #pragma once -#include "audio/audiostreampcm.h" -#include "audio/audiostreammp3.h" +#include "audio/stream/audiostreampcm.h" +#include "audio/stream/audiostreammp3.h" #include "audio/audiostreamplatform.h" #include "asset/loader/assetentry.h" diff --git a/src/dusk/audio/audiostreammp3.c b/src/dusk/audio/stream/audiostreammp3.c similarity index 100% rename from src/dusk/audio/audiostreammp3.c rename to src/dusk/audio/stream/audiostreammp3.c diff --git a/src/dusk/audio/audiostreammp3.h b/src/dusk/audio/stream/audiostreammp3.h similarity index 100% rename from src/dusk/audio/audiostreammp3.h rename to src/dusk/audio/stream/audiostreammp3.h diff --git a/src/dusk/audio/audiostreammp3decodersw.c b/src/dusk/audio/stream/audiostreammp3decodersw.c similarity index 99% rename from src/dusk/audio/audiostreammp3decodersw.c rename to src/dusk/audio/stream/audiostreammp3decodersw.c index 42a85d08..5f5d62b8 100644 --- a/src/dusk/audio/audiostreammp3decodersw.c +++ b/src/dusk/audio/stream/audiostreammp3decodersw.c @@ -7,7 +7,7 @@ #define MINIMP3_IMPLEMENTATION #include "audiostreammp3decodersw.h" -#include "audio/audiostream.h" +#include "audiostream.h" #include "assert/assert.h" #include "util/memory.h" diff --git a/src/dusk/audio/audiostreammp3decodersw.h b/src/dusk/audio/stream/audiostreammp3decodersw.h similarity index 100% rename from src/dusk/audio/audiostreammp3decodersw.h rename to src/dusk/audio/stream/audiostreammp3decodersw.h diff --git a/src/dusk/audio/audiostreampcm.c b/src/dusk/audio/stream/audiostreampcm.c similarity index 100% rename from src/dusk/audio/audiostreampcm.c rename to src/dusk/audio/stream/audiostreampcm.c diff --git a/src/dusk/audio/audiostreampcm.h b/src/dusk/audio/stream/audiostreampcm.h similarity index 100% rename from src/dusk/audio/audiostreampcm.h rename to src/dusk/audio/stream/audiostreampcm.h diff --git a/src/duskdolphin/audio/CMakeLists.txt b/src/duskdolphin/audio/CMakeLists.txt index e576967d..af649f1c 100644 --- a/src/duskdolphin/audio/CMakeLists.txt +++ b/src/duskdolphin/audio/CMakeLists.txt @@ -25,5 +25,5 @@ endif() target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE minimp3) target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - "${DUSK_SOURCES_DIR}/dusk/audio/audiostreammp3decodersw.c" + "${DUSK_SOURCES_DIR}/dusk/audio/stream/audiostreammp3decodersw.c" ) diff --git a/src/duskdolphin/audio/audiostreamdolphin.c b/src/duskdolphin/audio/audiostreamdolphin.c index 5fd0696f..49c76dfa 100644 --- a/src/duskdolphin/audio/audiostreamdolphin.c +++ b/src/duskdolphin/audio/audiostreamdolphin.c @@ -6,7 +6,7 @@ */ #include "audiostreamdolphin.h" -#include "audio/audiostream.h" +#include "audio/stream/audiostream.h" #include "assert/assert.h" #include "util/memory.h" #include "util/math.h" diff --git a/src/duskdolphin/audio/audiostreammp3decoder.h b/src/duskdolphin/audio/audiostreammp3decoder.h index 4cd741a8..a3e48f4f 100644 --- a/src/duskdolphin/audio/audiostreammp3decoder.h +++ b/src/duskdolphin/audio/audiostreammp3decoder.h @@ -13,4 +13,4 @@ // audiostreamdolphin.c already owns - not a fit here), so Dolphin uses the // same shared minimp3-based software backend as Linux - see that header's // own documentation for the actual struct/interface. -#include "audio/audiostreammp3decodersw.h" +#include "audio/stream/audiostreammp3decodersw.h" diff --git a/src/dusklinux/audio/CMakeLists.txt b/src/dusklinux/audio/CMakeLists.txt index 1fd2e8fc..f7ae6cca 100644 --- a/src/dusklinux/audio/CMakeLists.txt +++ b/src/dusklinux/audio/CMakeLists.txt @@ -20,5 +20,5 @@ endif() target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC minimp3) target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - "${DUSK_SOURCES_DIR}/dusk/audio/audiostreammp3decodersw.c" + "${DUSK_SOURCES_DIR}/dusk/audio/stream/audiostreammp3decodersw.c" ) diff --git a/src/dusklinux/audio/audiostreamlinux.c b/src/dusklinux/audio/audiostreamlinux.c index 0a0bb45f..61960b51 100644 --- a/src/dusklinux/audio/audiostreamlinux.c +++ b/src/dusklinux/audio/audiostreamlinux.c @@ -6,7 +6,7 @@ */ #include "audiostreamlinux.h" -#include "audio/audiostream.h" +#include "audio/stream/audiostream.h" #include "assert/assert.h" #include "util/memory.h" #include "util/math.h" diff --git a/src/dusklinux/audio/audiostreammp3decoder.h b/src/dusklinux/audio/audiostreammp3decoder.h index 8371ccf4..06dad99d 100644 --- a/src/dusklinux/audio/audiostreammp3decoder.h +++ b/src/dusklinux/audio/audiostreammp3decoder.h @@ -10,4 +10,4 @@ // Linux has no hardware MP3 decoder to prefer, so it uses the shared // minimp3-based software backend (also used by Dolphin) - see that // header's own documentation for the actual struct/interface. -#include "audio/audiostreammp3decodersw.h" +#include "audio/stream/audiostreammp3decodersw.h" diff --git a/src/duskpsp/audio/audiostreammp3decoder.c b/src/duskpsp/audio/audiostreammp3decoder.c index 745d5f50..93e9e04f 100644 --- a/src/duskpsp/audio/audiostreammp3decoder.c +++ b/src/duskpsp/audio/audiostreammp3decoder.c @@ -6,7 +6,7 @@ */ #include "audiostreammp3decoder.h" -#include "audio/audiostream.h" +#include "audio/stream/audiostream.h" #include "assert/assert.h" #include "util/memory.h" #include diff --git a/src/duskpsp/audio/audiostreampsp.c b/src/duskpsp/audio/audiostreampsp.c index 8af9ac67..ecc83681 100644 --- a/src/duskpsp/audio/audiostreampsp.c +++ b/src/duskpsp/audio/audiostreampsp.c @@ -6,7 +6,7 @@ */ #include "audiostreampsp.h" -#include "audio/audiostream.h" +#include "audio/stream/audiostream.h" #include "assert/assert.h" #include "util/memory.h" #include "util/math.h"