Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 23 additions & 18 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
cmake_policy(SET CMP0135 NEW)
endif()

set(MINIAUDIO_VERSION "0.11.23" CACHE STRING "Version of miniaudio to use")
set(MINIAUDIO_NO_MP3 YES)
set(MINIAUDIO_NO_FLAC YES)
set(MINIAUDIO_NO_LIBVORBIS NO)
FetchContent_Declare(
miniaudio
URL https://github.com/mackron/miniaudio/archive/refs/tags/${MINIAUDIO_VERSION}.tar.gz
)
FetchContent_MakeAvailable(miniaudio)

set(SFSEXP_VERSION "1.4.1" CACHE STRING "Version of SfSexp to use")
FetchContent_Declare(
sfsexp
Expand All @@ -39,18 +49,11 @@ FetchContent_Declare(
FetchContent_MakeAvailable(sfsexp)
include("${PROJECT_SOURCE_DIR}/cmake/sfsexp.cmake")

option(NEOTUX_USE_MIXER "Build with SDL3 Mixer support." ON)

if (NEOTUX_USE_MIXER)
message(STATUS "Using SDL3 Mixer...")
FetchContent_Declare(
SDL3_mixer
URL https://github.com/libsdl-org/SDL_mixer/archive/68764f35899e133b402336843c046d75136eaf08.tar.gz
)
FetchContent_MakeAvailable(SDL3_mixer)
else()
message(STATUS "NOT using SDL3 Mixer...")
endif (NEOTUX_USE_MIXER)
if (PSP)
message(STATUS "Building PSP version...")
Copy link
Member

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

set(NEOTUX_PSP ON)
set(NEOTUX_DATA_DIR "data")
endif()

set(SSQ_BUILD_STATIC_ONLY Off)

Expand Down Expand Up @@ -116,16 +119,18 @@ endif()
add_executable(NeoTux ${NEOTUX_SRC} )
target_include_directories(NeoTux PRIVATE src tests/game_tests)
target_link_libraries(NeoTux PUBLIC
SFSEXP
SDL3::SDL3
SDL3_ttf::SDL3_ttf
SDL3_image::SDL3_image
# PkgConfig::SDL3_image
# PkgConfig::SDL3_ttf

SFSEXP

miniaudio
miniaudio_libvorbis
)
if(NEOTUX_USE_MIXER)
target_link_libraries(NeoTux PUBLIC SDL3_mixer::SDL3_mixer)
endif()

# NOTE: They seem to have forgotten to add this include directory to the miniaudio_libvorbis target.
target_include_directories(NeoTux SYSTEM PUBLIC ${miniaudio_SOURCE_DIR}/extras/decoders/libvorbis)

if(NEOTUX_BGFX)
target_sources(NeoTux PRIVATE "${NEOTUX_DATA_DIR}/shaders/frag.glsl" "${NEOTUX_DATA_DIR}/shaders/vert.glsl")
Expand Down
1 change: 0 additions & 1 deletion config.h.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#cmakedefine NEOTUX_BGFX
#cmakedefine NEOTUX_USE_MIXER
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No go

Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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@"
175 changes: 108 additions & 67 deletions src/audio/mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,112 +14,153 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include "mixer.hpp"
#ifdef NEOTUX_USE_MIXER
#include <SDL3_mixer/SDL_mixer.h>
#endif
#include <format>
#include "sdl_exception.hpp"
#include "util/filesystem.hpp"
#include "util/logger.hpp"
#include "mixer.hpp"
#include "audio/sound_manager.hpp"

#include <miniaudio_libvorbis.h>

Mixer g_mixer;

#ifdef NEOTUX_USE_MIXER
Mixer::Mixer() :
m_music(nullptr, Mix_FreeMusic),
m_cache(),
m_current_channel(0)
Mixer::Mixer()
{
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, &m_resource_manager);
if (result != MA_SUCCESS)
{
Logger::error("Mixer", "Oops! ded.");
std::exit(-1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nein :) for now throw an exception, but try to handle it instead of panicking, we want the game to keep running but display an error

}

ma_engine_config engine_cfg;
engine_cfg = ma_engine_config_init();
engine_cfg.pResourceManager = &m_resource_manager;

result = ma_engine_init(&engine_cfg, &m_engine);
if (result != MA_SUCCESS)
{
Logger::error("Mixer", "Oops! ded.");
std::exit(-1);
}

ma_device* dev;
ma_device_info dev_info;
dev = ma_engine_get_device(&m_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();
ma_sound_uninit(&m_music);
ma_engine_uninit(&m_engine);
ma_resource_manager_uninit(&m_resource_manager);
// m_music.reset();
// m_cache.clear();
//m_soundcache.clear();
#endif
}

bool
Mixer::is_playing_music()
{
#ifdef NEOTUX_USE_MIXER
return Mix_PlayingMusic();
#else
return true;
#endif
return ma_sound_is_playing(&m_music);
}

void
Mixer::stop_playing_music()
{
#ifdef NEOTUX_USE_MIXER
Mix_HaltMusic();
#endif
ma_sound_stop(&m_music);
}

// 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 std::runtime_error(std::format("Failed to play sound (ma error: {})",
(int)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;
m_music_data = reader.open(FS::path(filename));
filename = FS::join(FS::parent_dir(filename), m_music_data.file);
} else {
m_music_data = {};
}

Mix_FadeInMusic(music, true, 2000);

m_music.reset(music);
#endif

ma_sound new_music;
ma_result result;
result = ma_sound_init_from_file(&m_engine, FS::path(filename).c_str(),
MA_SOUND_FLAG_STREAM, nullptr, nullptr, &new_music);

if (result != MA_SUCCESS) {
throw std::runtime_error(std::format("Failed to load music {} (ma error: {})",
FS::path(filename),
(int)result));
}

ma_sound_uninit(&m_music);
m_music = new_music;

ma_data_source* music_source = ma_sound_get_data_source(&m_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, m_music_data.loop_begin * samplerate,
m_music_data.loop_at * samplerate);
ma_sound_set_looping(&m_music, MA_TRUE);

ma_sound_start(&m_music);
}

31 changes: 19 additions & 12 deletions src/audio/mixer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,36 @@
#include <vector>
#include <unordered_map>
#include <string>
#include "config.h"
#ifdef NEOTUX_USE_MIXER
#include <SDL3_mixer/SDL_mixer.h>
#endif

#include <miniaudio.h>

#include "audio/music_reader.hpp"
#include "types.hpp"

class Mixer
{
friend class SoundManager;

public:
static std::string get_channels_name(u32 channels);

public:
Mixer();
~Mixer() = default;

void shutdown();
void play_sound(const std::string &filename);
void play_music(const std::string &filename);
void play_sound(const std::string& filename);
void play_sound(ma_sound* sound);
void play_music(std::string filename);
bool is_playing_music();
void stop_playing_music();

private:
#ifdef NEOTUX_USE_MIXER
std::unordered_map<std::string, std::unique_ptr<Mix_Chunk, decltype(&Mix_FreeChunk)>> m_cache;
std::unique_ptr<Mix_Music, decltype(&Mix_FreeMusic)> m_music;
int m_current_channel;
#endif
//std::vector<std::unique_ptr<Mix_Chunk, decltype(&Mix_FreeChunk)>> m_soundcache;
ma_engine m_engine;
ma_resource_manager m_resource_manager;

ma_sound m_music;
MusicData m_music_data;
};

extern Mixer g_mixer;
Expand Down
29 changes: 29 additions & 0 deletions src/audio/music_data.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SuperTux
// Copyright (C) 2025 MatusGuy <[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/>.
#ifndef SUPERTUX_SRC_AUDIO_MUSIC_DATA_HPP
#define SUPERTUX_SRC_AUDIO_MUSIC_DATA_HPP

#include <string>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spacing

#include "types.hpp"

struct MusicData {
std::string file;
u32 loop_begin = 0;
u32 loop_at = UINT_MAX;
};

#endif // SUPERTUX_SRC_AUDIO_MUSIC_DATA_HPP
Loading