-
Notifications
You must be signed in to change notification settings - Fork 0
audio: audio engine rework w/ miniaudio #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
20f7dfa
6161ba2
6872e48
d10a3d0
f803141
3690d6a
68a4377
fe2fb74
21a9c0a
b4f1f9e
132e066
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,4 @@ | ||
| #cmakedefine NEOTUX_BGFX | ||
| #cmakedefine NEOTUX_USE_MIXER | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No go
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made a comment explicity saying not to remove this bit but I mustve forgot to submit it; please add this logic back though.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic can be emulated by using the Null miniaudio backend. |
||
| #cmakedefine NEOTUX_PSP | ||
| #cmakedefine NEOTUX_UNITTESTS_DIR "@NEOTUX_UNITTESTS_DIR@" | ||
| #cmakedefine NEOTUX_DATA_DIR "@NEOTUX_DATA_DIR@" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,125 +1,177 @@ | ||
| // SuperTux | ||
| // Copyright (C) 2025 Hyland B. <[email protected]> | ||
| // | ||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
| // | ||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU General Public License | ||
| // SuperTux | ||
| // Copyright (C) 2025 Hyland B. <[email protected]> | ||
| // | ||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
| // | ||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| #ifdef NEOTUX_USE_MIXER | ||
| #include <SDL3_mixer/SDL_mixer.h> | ||
| #endif | ||
| #include <format> | ||
| #include <miniaudio.h> | ||
| #include <miniaudio_libvorbis.h> | ||
|
|
||
| #include "mixer.hpp" | ||
| #include "sdl_exception.hpp" | ||
| #include "util/filesystem.hpp" | ||
| #include "util/logger.hpp" | ||
| #include "mixer.hpp" | ||
| #include "audio/sound_manager.hpp" | ||
| #include "audio/music_reader.hpp" | ||
|
|
||
| MAException::MAException(const std::string& what, int result) : | ||
| std::runtime_error(std::format("{} (ma error: {})", what, result)) | ||
| {} | ||
|
|
||
| struct Mixer::Impl | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See my other comment. We can abstract it all at compile time... |
||
| { | ||
| ma_engine engine; | ||
| ma_resource_manager resource_manager; | ||
|
|
||
| ma_sound music; | ||
| MusicData music_data; | ||
| }; | ||
|
|
||
| Mixer g_mixer; | ||
|
|
||
| #ifdef NEOTUX_USE_MIXER | ||
| Mixer::Mixer() : | ||
| m_music(nullptr, Mix_FreeMusic), | ||
| m_cache(), | ||
| m_current_channel(0) | ||
| Mixer::Mixer(): | ||
| impl(std::make_unique<Impl>()) | ||
| { | ||
| SDL_AudioSpec spec; | ||
| spec.freq = MIX_DEFAULT_FREQUENCY; | ||
| spec.format = MIX_DEFAULT_FORMAT; | ||
| spec.channels = MIX_DEFAULT_CHANNELS; | ||
|
|
||
| SDL_Init(SDL_INIT_AUDIO); | ||
| Mix_Init(MIX_INIT_OGG | MIX_INIT_WAVPACK); | ||
|
|
||
| Mix_OpenAudio(0, &spec); | ||
| Logger::info(std::format("Opened audio at {}Hz, {} bit{}, {} audio buffer", | ||
| spec.freq, | ||
| spec.format & 0xFF, | ||
| SDL_AUDIO_ISFLOAT(spec.format) ? " (float)" : "", | ||
| (spec.channels > 2) ? "surround" : (spec.channels > 1) ? "stereo" : "mono")); | ||
| ma_result result; | ||
|
|
||
| ma_decoding_backend_vtable* decoders[] = { | ||
| ma_decoding_backend_libvorbis | ||
| }; | ||
|
|
||
| ma_resource_manager_config resource_manager_cfg = ma_resource_manager_config_init(); | ||
| resource_manager_cfg.pCustomDecodingBackendUserData = nullptr; | ||
| resource_manager_cfg.ppCustomDecodingBackendVTables = decoders; | ||
| resource_manager_cfg.customDecodingBackendCount = sizeof(decoders) / sizeof(decoders[0]); | ||
|
|
||
| result = ma_resource_manager_init(&resource_manager_cfg, &impl->resource_manager); | ||
| if (result != MA_SUCCESS) | ||
| { | ||
| throw MAException("Failed to initialize resource manager", result); | ||
| } | ||
|
|
||
| ma_engine_config engine_cfg; | ||
| engine_cfg = ma_engine_config_init(); | ||
| engine_cfg.pResourceManager = &impl->resource_manager; | ||
|
|
||
| result = ma_engine_init(&engine_cfg, &impl->engine); | ||
| if (result != MA_SUCCESS) | ||
| { | ||
| throw MAException("Failed to initialize engine", result); | ||
| } | ||
|
|
||
| ma_device* dev; | ||
| ma_device_info dev_info; | ||
| dev = ma_engine_get_device(&impl->engine); | ||
| ma_device_get_info(dev, ma_device_type_playback, &dev_info); | ||
|
|
||
| Logger::info("Mixer", "Opened audio device:"); | ||
| Logger::info("Mixer", std::format("\tSample rate: {}Hz", | ||
| dev_info.nativeDataFormats[0].sampleRate)); | ||
| Logger::info("Mixer", std::format("\tChannels: {} sound ({})", | ||
| get_channels_name(dev_info.nativeDataFormats[0].channels), | ||
| dev_info.nativeDataFormats[0].channels)); | ||
| Logger::info("Mixer", std::format("\tFormat: {}", | ||
| ma_get_format_name(dev_info.nativeDataFormats[0].format))); | ||
| } | ||
| #else | ||
| Mixer::Mixer() { | ||
| Logger::info("Built without SDL3 Mixer support. Hope you enjoy crickets."); | ||
|
|
||
| std::string Mixer::get_channels_name(u32 channels) | ||
| { | ||
| if (channels > 2) | ||
| return "surround"; | ||
| else if (channels > 1) | ||
| return "stereo"; | ||
| else | ||
| return "mono"; | ||
| } | ||
| #endif | ||
|
|
||
| void | ||
| Mixer::shutdown() | ||
| { | ||
| #ifdef NEOTUX_USE_MIXER | ||
| m_music.reset(); | ||
| m_cache.clear(); | ||
| //m_soundcache.clear(); | ||
| #endif | ||
| ma_sound_uninit(&impl->music); | ||
| ma_engine_uninit(&impl->engine); | ||
| ma_resource_manager_uninit(&impl->resource_manager); | ||
| } | ||
|
|
||
| bool | ||
| Mixer::is_playing_music() | ||
| { | ||
| #ifdef NEOTUX_USE_MIXER | ||
| return Mix_PlayingMusic(); | ||
| #else | ||
| return true; | ||
| #endif | ||
| return ma_sound_is_playing(&impl->music); | ||
| } | ||
|
|
||
| void | ||
| Mixer::stop_playing_music() | ||
| { | ||
| #ifdef NEOTUX_USE_MIXER | ||
| Mix_HaltMusic(); | ||
| #endif | ||
| ma_sound_stop(&impl->music); | ||
| } | ||
|
|
||
| ma_engine* | ||
| Mixer::engine() | ||
| { | ||
| return &impl->engine; | ||
| } | ||
|
|
||
| // TODO Cache sounds | ||
| void | ||
| Mixer::play_sound(const std::string &filename) | ||
| { | ||
| #ifdef NEOTUX_USE_MIXER | ||
| Mix_Chunk *chunk; | ||
| if (m_cache.contains(filename)) | ||
| { | ||
| chunk = m_cache.at(filename).get(); | ||
| } | ||
| else { | ||
| chunk = Mix_LoadWAV(FS::path(filename).c_str()); | ||
| m_cache.insert({filename, std::unique_ptr<Mix_Chunk, decltype(&Mix_FreeChunk)>(chunk, Mix_FreeChunk)}); | ||
| ma_sound* sound = g_sound_manager.load(filename); | ||
| play_sound(sound); | ||
| } | ||
|
|
||
| void | ||
| Mixer::play_sound(ma_sound* sound) | ||
| { | ||
| if (ma_sound_is_playing(sound)) | ||
| ma_sound_stop(sound); | ||
|
|
||
| ma_result result = ma_sound_start(sound); | ||
| if (result != MA_SUCCESS) { | ||
| throw MAException("Failed to play sound", result); | ||
| } | ||
|
|
||
| if (!chunk) | ||
| throw SDLException("Couldn't load chunk"); | ||
|
|
||
| ++m_current_channel; | ||
| if (m_current_channel == 4) | ||
| m_current_channel = 0; | ||
| Mix_PlayChannel(m_current_channel, chunk, false); | ||
| #endif | ||
| } | ||
|
|
||
| void | ||
| Mixer::play_music(const std::string &filename) | ||
| Mixer::play_music(std::string filename) | ||
| { | ||
| #ifdef NEOTUX_USE_MIXER | ||
| Mix_Music *music; | ||
| music = Mix_LoadMUS(FS::path(filename).c_str()); | ||
|
|
||
| if (!music) | ||
| if (filename.ends_with(".music")) | ||
| { | ||
| throw SDLException("Couldn't load music"); | ||
| MusicReader reader; | ||
| impl->music_data = reader.open(FS::path(filename)); | ||
| filename = FS::join(FS::parent_dir(filename), impl->music_data.file); | ||
| } else { | ||
| impl->music_data = {}; | ||
| } | ||
|
|
||
| Mix_FadeInMusic(music, true, 2000); | ||
|
|
||
| m_music.reset(music); | ||
| #endif | ||
| } | ||
|
|
||
| ma_result result; | ||
| ma_sound_uninit(&impl->music); | ||
| result = ma_sound_init_from_file(&impl->engine, FS::path(filename).c_str(), | ||
| MA_SOUND_FLAG_STREAM, nullptr, nullptr, &impl->music); | ||
|
|
||
| if (result != MA_SUCCESS) { | ||
| throw MAException(std::format("Failed to load music {}", FS::path(filename)), result); | ||
| } | ||
|
|
||
| ma_data_source* music_source = ma_sound_get_data_source(&impl->music); | ||
| ma_uint32 samplerate; | ||
| ma_data_source_get_data_format(music_source, nullptr, nullptr, &samplerate, nullptr, 67); | ||
|
|
||
| ma_data_source_set_loop_point_in_pcm_frames(music_source, impl->music_data.loop_begin * samplerate, | ||
| impl->music_data.loop_at * samplerate); | ||
| ma_sound_set_looping(&impl->music, MA_TRUE); | ||
|
|
||
| result = ma_sound_start(&impl->music); | ||
| if (result != MA_SUCCESS) { | ||
| throw MAException(std::format("Failed to play music {}", FS::path(filename)), result); | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when merging, try to rebase, I moved this stuff now to psp.cmake