mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
switched audio from miniaudio to SDL_mixer
This commit is contained in:
parent
9accbbeea7
commit
be16a68e85
10 changed files with 231 additions and 324 deletions
|
|
@ -1,301 +1,63 @@
|
||||||
/*
|
|
||||||
Demonstrates how to load a sound file and play it back using the low-level API.
|
|
||||||
|
|
||||||
The low-level API uses a callback to deliver audio between the application and miniaudio for playback or recording. When
|
|
||||||
in playback mode, as in this example, the application sends raw audio data to miniaudio which is then played back through
|
|
||||||
the default playback device as defined by the operating system.
|
|
||||||
|
|
||||||
This example uses the `ma_decoder` API to load a sound and play it back. The decoder is entirely decoupled from the
|
|
||||||
device and can be used independently of it. This example only plays back a single sound file, but it's possible to play
|
|
||||||
back multiple files by simple loading multiple decoders and mixing them (do not create multiple devices to do this). See
|
|
||||||
the simple_mixing example for how best to do this.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define STB_VORBIS_HEADER_ONLY
|
|
||||||
#include "stb_vorbis.c"
|
|
||||||
|
|
||||||
|
|
||||||
#define MINIAUDIO_IMPLEMENTATION
|
|
||||||
#include "miniaudio.h"
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "audio_player.h"
|
#include "audio_player.h"
|
||||||
|
|
||||||
// #define MA_NO_LIBOPUS
|
#include <SDL3_mixer/SDL_mixer.h>
|
||||||
// #include "miniaudio_libvorbis.h"
|
|
||||||
// #include "miniaudio_libopus.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
static int audio_open = 0;
|
||||||
{
|
static Mix_Music *music = NULL;
|
||||||
ma_result result;
|
static int next_track = 0;
|
||||||
ma_libvorbis* pVorbis;
|
|
||||||
|
|
||||||
(void)pUserData;
|
|
||||||
|
|
||||||
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
|
||||||
if (pVorbis == NULL) {
|
|
||||||
return MA_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
ma_free(pVorbis, pAllocationCallbacks);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ppBackend = pVorbis;
|
|
||||||
|
|
||||||
return MA_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
||||||
{
|
|
||||||
ma_result result;
|
|
||||||
ma_libvorbis* pVorbis;
|
|
||||||
|
|
||||||
(void)pUserData;
|
|
||||||
|
|
||||||
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
|
||||||
if (pVorbis == NULL) {
|
|
||||||
return MA_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
ma_free(pVorbis, pAllocationCallbacks);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ppBackend = pVorbis;
|
|
||||||
|
|
||||||
return MA_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
||||||
{
|
|
||||||
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
|
|
||||||
|
|
||||||
(void)pUserData;
|
|
||||||
|
|
||||||
ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
|
|
||||||
ma_free(pVorbis, pAllocationCallbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_decoding_backend_get_channel_map__libvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
|
|
||||||
{
|
|
||||||
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
|
|
||||||
|
|
||||||
(void)pUserData;
|
|
||||||
|
|
||||||
return ma_libvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis =
|
|
||||||
{
|
|
||||||
ma_decoding_backend_init__libvorbis,
|
|
||||||
ma_decoding_backend_init_file__libvorbis,
|
|
||||||
NULL, /* onInitFileW() */
|
|
||||||
NULL, /* onInitMemory() */
|
|
||||||
ma_decoding_backend_uninit__libvorbis
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
||||||
{
|
|
||||||
ma_result result;
|
|
||||||
ma_libopus* pOpus;
|
|
||||||
|
|
||||||
(void)pUserData;
|
|
||||||
|
|
||||||
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
|
|
||||||
if (pOpus == NULL) {
|
|
||||||
return MA_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
ma_free(pOpus, pAllocationCallbacks);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ppBackend = pOpus;
|
|
||||||
|
|
||||||
return MA_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
||||||
{
|
|
||||||
ma_result result;
|
|
||||||
ma_libopus* pOpus;
|
|
||||||
|
|
||||||
(void)pUserData;
|
|
||||||
|
|
||||||
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
|
|
||||||
if (pOpus == NULL) {
|
|
||||||
return MA_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
ma_free(pOpus, pAllocationCallbacks);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ppBackend = pOpus;
|
|
||||||
|
|
||||||
return MA_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
||||||
{
|
|
||||||
ma_libopus* pOpus = (ma_libopus*)pBackend;
|
|
||||||
|
|
||||||
(void)pUserData;
|
|
||||||
|
|
||||||
ma_libopus_uninit(pOpus, pAllocationCallbacks);
|
|
||||||
ma_free(pOpus, pAllocationCallbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_decoding_backend_get_channel_map__libopus(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
|
|
||||||
{
|
|
||||||
ma_libopus* pOpus = (ma_libopus*)pBackend;
|
|
||||||
|
|
||||||
(void)pUserData;
|
|
||||||
|
|
||||||
return ma_libopus_get_data_format(pOpus, NULL, NULL, NULL, pChannelMap, channelMapCap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libopus =
|
|
||||||
{
|
|
||||||
ma_decoding_backend_init__libopus,
|
|
||||||
ma_decoding_backend_init_file__libopus,
|
|
||||||
NULL, /* onInitFileW() */
|
|
||||||
NULL, /* onInitMemory() */
|
|
||||||
ma_decoding_backend_uninit__libopus
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
|
|
||||||
// {
|
|
||||||
// ma_data_source* pDataSource = (ma_data_source*)pDevice->pUserData;
|
|
||||||
// if (pDataSource == NULL) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ma_data_source_read_pcm_frames(pDataSource, pOutput, frameCount, NULL);
|
|
||||||
|
|
||||||
// (void)pInput;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
SDL_AudioSpec spec;
|
||||||
|
|
||||||
|
|
||||||
static ThreadSafeQueue<AudioCommand> g_audioQueue;
|
static ThreadSafeQueue<AudioCommand> g_audioQueue;
|
||||||
static ma_decoder decoder;
|
|
||||||
static ma_device device;
|
|
||||||
|
|
||||||
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
|
|
||||||
|
void music_finished()
|
||||||
{
|
{
|
||||||
ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData;
|
Mix_FreeMusic(music);
|
||||||
if (pDecoder == NULL) {
|
music = NULL;
|
||||||
return;
|
g_audioQueue.push({"end", ""});
|
||||||
}
|
|
||||||
|
|
||||||
ma_uint64 framesRead;
|
|
||||||
ma_result result = ma_decoder_read_pcm_frames(pDecoder, pOutput, frameCount, &framesRead);
|
|
||||||
|
|
||||||
|
|
||||||
if (result == MA_AT_END) {
|
|
||||||
g_audioQueue.push({"end", ""});
|
|
||||||
}
|
|
||||||
// if (framesRead < frameCount) {
|
|
||||||
// // Reached the end.
|
|
||||||
// ma_event_signal(&g_stopEvent);
|
|
||||||
// }
|
|
||||||
|
|
||||||
(void)pInput;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int miniaudio_play(const char* filename)
|
|
||||||
{
|
|
||||||
ma_result result;
|
|
||||||
ma_device_config deviceConfig;
|
|
||||||
short *output = NULL;
|
|
||||||
|
|
||||||
result = ma_decoder_init_file(filename, NULL, &decoder);
|
|
||||||
if (result != MA_SUCCESS)
|
|
||||||
{
|
|
||||||
printf("Could not load file: %s\n", filename);
|
|
||||||
|
|
||||||
// Try OGG (FIXME: plus tard, utiliser l'extension pour choisir le décodeur)
|
|
||||||
int channels = 0, sample_rate = 0;
|
|
||||||
|
|
||||||
int sample_count = stb_vorbis_decode_filename("chemin/vers/fichier.ogg", &channels, &sample_rate, &output);
|
|
||||||
if (sample_count == -1)
|
|
||||||
{
|
|
||||||
printf("Erreur de décodage du fichier Vorbis\n");
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback);
|
|
||||||
deviceConfig.playback.format = ma_format_s16;
|
|
||||||
deviceConfig.playback.channels = channels;
|
|
||||||
deviceConfig.sampleRate = sample_rate;
|
|
||||||
deviceConfig.dataCallback = data_callback;
|
|
||||||
deviceConfig.pUserData = &decoder;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
deviceConfig = ma_device_config_init(ma_device_type_playback);
|
|
||||||
deviceConfig.playback.format = decoder.outputFormat;
|
|
||||||
deviceConfig.playback.channels = decoder.outputChannels;
|
|
||||||
deviceConfig.sampleRate = decoder.outputSampleRate;
|
|
||||||
deviceConfig.dataCallback = data_callback;
|
|
||||||
deviceConfig.pUserData = &decoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) {
|
|
||||||
printf("Failed to open playback device.\n");
|
|
||||||
ma_decoder_uninit(&decoder);
|
|
||||||
if (output != NULL)
|
|
||||||
{
|
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ma_device_start(&device) != MA_SUCCESS) {
|
|
||||||
printf("Failed to start playback device.\n");
|
|
||||||
ma_device_uninit(&device);
|
|
||||||
ma_decoder_uninit(&decoder);
|
|
||||||
if (output != NULL)
|
|
||||||
{
|
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output != NULL)
|
|
||||||
{
|
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioPlayer::AudioPlayer(IAudioEvent &event)
|
AudioPlayer::AudioPlayer(IAudioEvent &event)
|
||||||
: m_event(event)
|
: m_event(event)
|
||||||
{
|
{
|
||||||
m_audioThread = std::thread( std::bind(&AudioPlayer::AudioThread, this) );
|
m_audioThread = std::thread( std::bind(&AudioPlayer::AudioThread, this) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::Initialize()
|
||||||
|
{
|
||||||
|
/* Initialize variables */
|
||||||
|
spec.freq = MIX_DEFAULT_FREQUENCY;
|
||||||
|
spec.format = MIX_DEFAULT_FORMAT;
|
||||||
|
spec.channels = MIX_DEFAULT_CHANNELS;
|
||||||
|
|
||||||
|
if (Mix_Init(MIX_INIT_FLAC | MIX_INIT_MP3 | MIX_INIT_OGG) < 0) {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mix_OpenAudio(0, &spec) < 0) {
|
||||||
|
SDL_Log("Couldn't open audio: %s\n", SDL_GetError());
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Mix_QuerySpec(&spec.freq, &spec.format, &spec.channels);
|
||||||
|
SDL_Log("Opened audio at %d Hz %d bit%s %s audio buffer\n", spec.freq,
|
||||||
|
(spec.format&0xFF),
|
||||||
|
(SDL_AUDIO_ISFLOAT(spec.format) ? " (float)" : ""),
|
||||||
|
(spec.channels > 2) ? "surround" : (spec.channels > 1) ? "stereo" : "mono");
|
||||||
|
}
|
||||||
|
audio_open = 1;
|
||||||
|
|
||||||
|
Mix_HookMusicFinished(music_finished);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void AudioPlayer::Play(const std::string &filename)
|
void AudioPlayer::Play(const std::string &filename)
|
||||||
{
|
{
|
||||||
g_audioQueue.push({"play", filename});
|
g_audioQueue.push({"play", filename});
|
||||||
|
|
@ -304,6 +66,7 @@ void AudioPlayer::Play(const std::string &filename)
|
||||||
AudioPlayer::~AudioPlayer()
|
AudioPlayer::~AudioPlayer()
|
||||||
{
|
{
|
||||||
// Quit audio thread
|
// Quit audio thread
|
||||||
|
g_audioQueue.clear();
|
||||||
g_audioQueue.push({"quit", ""});
|
g_audioQueue.push({"quit", ""});
|
||||||
if (m_audioThread.joinable())
|
if (m_audioThread.joinable())
|
||||||
{
|
{
|
||||||
|
|
@ -311,11 +74,12 @@ AudioPlayer::~AudioPlayer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioPlayer::CloseAudio()
|
void AudioPlayer::Stop()
|
||||||
{
|
{
|
||||||
ma_device_uninit(&device);
|
g_audioQueue.clear();
|
||||||
ma_decoder_uninit(&decoder);
|
g_audioQueue.push({"end", ""});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define AUDIO_STATE_WAIT_PLAY 1
|
#define AUDIO_STATE_WAIT_PLAY 1
|
||||||
#define AUDIO_STATE_WAIT_END 2
|
#define AUDIO_STATE_WAIT_END 2
|
||||||
|
|
@ -328,7 +92,8 @@ void AudioPlayer::AudioThread()
|
||||||
auto cmd = g_audioQueue.front();
|
auto cmd = g_audioQueue.front();
|
||||||
g_audioQueue.pop();
|
g_audioQueue.pop();
|
||||||
|
|
||||||
if (cmd.order == "quit") {
|
if (cmd.order == "quit")
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (cmd.order == "play")
|
else if (cmd.order == "play")
|
||||||
|
|
@ -336,7 +101,12 @@ void AudioPlayer::AudioThread()
|
||||||
if (state == AUDIO_STATE_WAIT_PLAY)
|
if (state == AUDIO_STATE_WAIT_PLAY)
|
||||||
{
|
{
|
||||||
state = AUDIO_STATE_WAIT_END;
|
state = AUDIO_STATE_WAIT_END;
|
||||||
miniaudio_play(cmd.filename.c_str());
|
music = Mix_LoadMUS(cmd.filename.c_str());
|
||||||
|
|
||||||
|
if (music)
|
||||||
|
{
|
||||||
|
Mix_PlayMusic(music, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cmd.order == "end")
|
else if (cmd.order == "end")
|
||||||
|
|
@ -344,7 +114,8 @@ void AudioPlayer::AudioThread()
|
||||||
if (state == AUDIO_STATE_WAIT_END)
|
if (state == AUDIO_STATE_WAIT_END)
|
||||||
{
|
{
|
||||||
state = AUDIO_STATE_WAIT_PLAY;
|
state = AUDIO_STATE_WAIT_PLAY;
|
||||||
CloseAudio();
|
Mix_HaltMusic();
|
||||||
|
Mix_FreeMusic(music);
|
||||||
m_event.EndOfAudio();
|
m_event.EndOfAudio();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#ifndef AUDIO_PLAYER_H
|
#ifndef AUDIO_PLAYER_H
|
||||||
#define AUDIO_PLAYER_H
|
#define AUDIO_PLAYER_H
|
||||||
|
|
||||||
#include "miniaudio.h"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
@ -34,6 +33,8 @@ public:
|
||||||
~AudioPlayer();
|
~AudioPlayer();
|
||||||
|
|
||||||
void Play(const std::string &filename);
|
void Play(const std::string &filename);
|
||||||
|
void Stop();
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IAudioEvent &m_event;
|
IAudioEvent &m_event;
|
||||||
|
|
@ -41,7 +42,7 @@ private:
|
||||||
std::thread m_audioThread;
|
std::thread m_audioThread;
|
||||||
|
|
||||||
void AudioThread();
|
void AudioThread();
|
||||||
void CloseAudio();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,13 @@ public:
|
||||||
queue.pop();
|
queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
while(!queue.empty()) {
|
||||||
|
queue.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool try_pop(T& popped_item) {
|
bool try_pop(T& popped_item) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock(mutex);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,25 @@ FetchContent_MakeAvailable(sdl3)
|
||||||
include_directories(${sdl3_SOURCE_DIR}/include)
|
include_directories(${sdl3_SOURCE_DIR}/include)
|
||||||
# add_subdirectory(${sdl3_SOURCE_DIR})
|
# add_subdirectory(${sdl3_SOURCE_DIR})
|
||||||
|
|
||||||
|
# =========================================================================================================================
|
||||||
|
# SDL3 MIXER
|
||||||
|
# =========================================================================================================================
|
||||||
|
FetchContent_Declare(
|
||||||
|
sdl3_mixer
|
||||||
|
GIT_REPOSITORY https://github.com/libsdl-org/SDL_mixer.git
|
||||||
|
GIT_TAG ace2d37796d2541123cdcbc06dce0b716a0416ad
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
GIT_SUBMODULES ""
|
||||||
|
)
|
||||||
|
|
||||||
|
set(BUILD_SHARED_LIBS TRUE)
|
||||||
|
set(SDL_PULSEAUDIO_SHARED TRUE)
|
||||||
|
set(SDL_PIPEWIRE_SHARED TRUE)
|
||||||
|
FetchContent_MakeAvailable(sdl3_mixer)
|
||||||
|
include_directories(${sdl3_mixer_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
|
||||||
# =========================================================================================================================
|
# =========================================================================================================================
|
||||||
# SDL3-Image
|
# SDL3-Image
|
||||||
# =========================================================================================================================
|
# =========================================================================================================================
|
||||||
|
|
@ -326,6 +345,7 @@ if(UNIX)
|
||||||
dl
|
dl
|
||||||
SDL3::SDL3
|
SDL3::SDL3
|
||||||
SDL3_image::SDL3_image
|
SDL3_image::SDL3_image
|
||||||
|
SDL3_mixer::SDL3_mixer
|
||||||
libcurl_static
|
libcurl_static
|
||||||
# OpenSSL::SSL OpenSSL::Crypto
|
# OpenSSL::SSL OpenSSL::Crypto
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,18 @@ Gui::Gui()
|
||||||
std::cout << "PATH: " << m_executablePath << std::endl;
|
std::cout << "PATH: " << m_executablePath << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ImFont* normalFont;
|
||||||
|
// static ImFont* bigFont;
|
||||||
|
|
||||||
|
void Gui::PushBigFont()
|
||||||
|
{
|
||||||
|
// ImGui::PushFont(bigFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gui::PopBigFont()
|
||||||
|
{
|
||||||
|
// ImGui::PopFont();
|
||||||
|
}
|
||||||
|
|
||||||
bool Gui::Initialize()
|
bool Gui::Initialize()
|
||||||
{
|
{
|
||||||
|
|
@ -112,11 +124,29 @@ bool Gui::Initialize()
|
||||||
// if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
|
// if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
|
||||||
|
|
||||||
// Setup SDL3
|
// Setup SDL3
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD) != 0)
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
|
||||||
{
|
{
|
||||||
printf("Error: SDL_Init(): %s\n", SDL_GetError());
|
printf("Error: SDL_Init(): %s\n", SDL_GetError());
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0)
|
||||||
|
{
|
||||||
|
int i, num_devices;
|
||||||
|
SDL_AudioDeviceID *devices = SDL_GetAudioOutputDevices(&num_devices);
|
||||||
|
if (devices) {
|
||||||
|
for (i = 0; i < num_devices; ++i) {
|
||||||
|
SDL_AudioDeviceID instance_id = devices[i];
|
||||||
|
char *name = SDL_GetAudioDeviceName(instance_id);
|
||||||
|
SDL_Log("AudioDevice %" SDL_PRIu32 ": %s\n", instance_id, name);
|
||||||
|
SDL_free(name);
|
||||||
|
}
|
||||||
|
SDL_free(devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Error: SDL_InitSubSystem(): %s\n", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}*/
|
||||||
|
|
||||||
// Enable native IME.
|
// Enable native IME.
|
||||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||||
|
|
@ -133,13 +163,13 @@ bool Gui::Initialize()
|
||||||
if (window == nullptr)
|
if (window == nullptr)
|
||||||
{
|
{
|
||||||
printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
|
printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
renderer = SDL_CreateRenderer(window, nullptr, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
renderer = SDL_CreateRenderer(window, nullptr, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
||||||
if (renderer == nullptr)
|
if (renderer == nullptr)
|
||||||
{
|
{
|
||||||
SDL_Log("Error: SDL_CreateRenderer(): %s\n", SDL_GetError());
|
SDL_Log("Error: SDL_CreateRenderer(): %s\n", SDL_GetError());
|
||||||
return -1;
|
return false;
|
||||||
}
|
}
|
||||||
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
||||||
SDL_ShowWindow(window);
|
SDL_ShowWindow(window);
|
||||||
|
|
@ -155,7 +185,9 @@ bool Gui::Initialize()
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
||||||
|
|
||||||
|
|
||||||
io.Fonts->AddFontFromFileTTF( std::string(m_executablePath + "/fonts/roboto.ttf").c_str(), 20);
|
// bigFont = io.Fonts->AddFontFromFileTTF( std::string(m_executablePath + "/fonts/roboto.ttf").c_str(), 40);
|
||||||
|
// io.Fonts->Build();
|
||||||
|
normalFont = io.Fonts->AddFontFromFileTTF( std::string(m_executablePath + "/fonts/roboto.ttf").c_str(), 20);
|
||||||
|
|
||||||
{
|
{
|
||||||
ImFontConfig config;
|
ImFontConfig config;
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,12 @@ public:
|
||||||
static bool LoadRawImage(const std::string &filename, Image &image);
|
static bool LoadRawImage(const std::string &filename, Image &image);
|
||||||
static Size GetWindowSize();
|
static Size GetWindowSize();
|
||||||
|
|
||||||
|
static void PushBigFont();
|
||||||
|
static void PopBigFont();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_executablePath;
|
std::string m_executablePath;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// namespace ImGui {
|
// namespace ImGui {
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@ int main(int, char**)
|
||||||
{
|
{
|
||||||
|
|
||||||
MainWindow w;
|
MainWindow w;
|
||||||
w.Initialize();
|
if (w.Initialize())
|
||||||
|
{
|
||||||
w.Loop();
|
w.Loop();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -256,13 +256,15 @@ void MainWindow::DrawStatusBar()
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::DrawMainMenuBar()
|
float MainWindow::DrawMainMenuBar()
|
||||||
{
|
{
|
||||||
bool showAboutPopup = false;
|
bool showAboutPopup = false;
|
||||||
bool showParameters = false;
|
bool showParameters = false;
|
||||||
bool showNewProject = false;
|
bool showNewProject = false;
|
||||||
bool showOpenProject = false;
|
bool showOpenProject = false;
|
||||||
|
|
||||||
|
float height = 60;
|
||||||
|
|
||||||
if (ImGui::BeginMainMenuBar())
|
if (ImGui::BeginMainMenuBar())
|
||||||
{
|
{
|
||||||
if (ImGui::BeginMenu("File"))
|
if (ImGui::BeginMenu("File"))
|
||||||
|
|
@ -333,6 +335,8 @@ void MainWindow::DrawMainMenuBar()
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
height = ImGui::GetFrameHeight();
|
||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -380,21 +384,31 @@ void MainWindow::DrawMainMenuBar()
|
||||||
}
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::Initialize()
|
bool MainWindow::Initialize()
|
||||||
{
|
{
|
||||||
|
bool success = false;
|
||||||
LoadParams();
|
LoadParams();
|
||||||
|
|
||||||
// GUI Init
|
// GUI Init
|
||||||
m_gui.Initialize();
|
if (m_gui.Initialize())
|
||||||
// gui.ApplyTheme();
|
{
|
||||||
|
m_player.Initialize(); // Initialize audio after GUI (uses SDL)
|
||||||
|
// gui.ApplyTheme();
|
||||||
|
|
||||||
m_editorWindow.Initialize();
|
m_editorWindow.Initialize();
|
||||||
m_emulatorWindow.Initialize();
|
m_emulatorWindow.Initialize();
|
||||||
m_nodeEditorWindow.Initialize();
|
m_nodeEditorWindow.Initialize();
|
||||||
m_PropertiesWindow.Initialize();
|
m_PropertiesWindow.Initialize();
|
||||||
m_libraryWindow.Initialize();
|
m_libraryWindow.Initialize();
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -591,20 +605,20 @@ void MainWindow::ProjectPropertiesPopup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto GetImageFormat = [](int idx) -> StoryProject::ImageFormat
|
auto GetImageFormat = [](int idx) -> Resource::ImageFormat
|
||||||
{
|
{
|
||||||
StoryProject::ImageFormat img{StoryProject::IMG_SAME_FORMAT};
|
Resource::ImageFormat img{Resource::IMG_SAME_FORMAT};
|
||||||
if (idx < StoryProject::IMG_FORMAT_COUNT) {
|
if (idx < Resource::IMG_FORMAT_COUNT) {
|
||||||
img = static_cast<StoryProject::ImageFormat>(idx);
|
img = static_cast<Resource::ImageFormat>(idx);
|
||||||
}
|
}
|
||||||
return img;
|
return img;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto GetSoundFormat = [](int idx) -> StoryProject::SoundFormat {
|
auto GetSoundFormat = [](int idx) -> Resource::SoundFormat {
|
||||||
|
|
||||||
StoryProject::SoundFormat img{StoryProject::SND_FORMAT_WAV};
|
Resource::SoundFormat img{Resource::SND_FORMAT_WAV};
|
||||||
if (idx < StoryProject::IMG_FORMAT_COUNT) {
|
if (idx < Resource::IMG_FORMAT_COUNT) {
|
||||||
img = static_cast<StoryProject::SoundFormat>(idx);
|
img = static_cast<Resource::SoundFormat>(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
|
|
@ -652,6 +666,11 @@ void MainWindow::OpenProject(const std::string &uuid)
|
||||||
CloseProject();
|
CloseProject();
|
||||||
m_story = m_libraryManager.GetStory(uuid);
|
m_story = m_libraryManager.GetStory(uuid);
|
||||||
|
|
||||||
|
// DEBUG CODE !!!!!!!!!!!!! Permet si décommenter de forcer l'import, permet de tester plus facilement l'algo en ouvrant le projet
|
||||||
|
// PackArchive arch(*this);
|
||||||
|
// std::string basePath = m_libraryManager.LibraryPath() + "/" + uuid;
|
||||||
|
// arch.ConvertJsonStudioToOst(basePath, uuid, m_libraryManager.LibraryPath());
|
||||||
|
|
||||||
if (!m_story)
|
if (!m_story)
|
||||||
{
|
{
|
||||||
Log("Cannot find story: " + uuid);
|
Log("Cannot find story: " + uuid);
|
||||||
|
|
@ -712,11 +731,11 @@ void MainWindow::RefreshProjectInformation()
|
||||||
|
|
||||||
void MainWindow::CloseProject()
|
void MainWindow::CloseProject()
|
||||||
{
|
{
|
||||||
if (m_story)
|
// if (m_story)
|
||||||
{
|
// {
|
||||||
m_story->Clear();
|
// m_story->Clear();
|
||||||
m_story.reset();
|
// m_story.reset();
|
||||||
}
|
// }
|
||||||
|
|
||||||
m_resources.Clear();
|
m_resources.Clear();
|
||||||
|
|
||||||
|
|
@ -736,7 +755,43 @@ void MainWindow::CloseProject()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::DrawToolBar(float topPadding)
|
||||||
|
{
|
||||||
|
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration |
|
||||||
|
ImGuiWindowFlags_NoMove |
|
||||||
|
ImGuiWindowFlags_NoScrollbar |
|
||||||
|
ImGuiWindowFlags_NoScrollWithMouse |
|
||||||
|
ImGuiWindowFlags_NoDocking;
|
||||||
|
|
||||||
|
// Définit la taille et la position de la barre d'outils
|
||||||
|
ImVec2 size = ImVec2(60, ImGui::GetIO().DisplaySize.y - topPadding); // Largeur de 60 pixels et hauteur égale à celle de l'écran
|
||||||
|
ImGui::SetNextWindowSize(size);
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(0, topPadding)); // Positionné à gauche et en haut
|
||||||
|
|
||||||
|
// Création de la fenêtre pour la barre d'outils
|
||||||
|
ImGui::Begin("ToolBar", nullptr, window_flags);
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); // rouge
|
||||||
|
float old_size = ImGui::GetFont()->Scale;
|
||||||
|
ImGui::GetFont()->Scale *= 2.5;
|
||||||
|
|
||||||
|
ImGui::PushFont(ImGui::GetFont());
|
||||||
|
|
||||||
|
// Ajouter des boutons à la barre d'outils
|
||||||
|
if (ImGui::Button(ICON_MDI_SPEAKER_STOP "##stop_sound", ImVec2(-1, 50))) { // Le bouton prend toute la largeur de la fenêtre et a une hauteur de 50 pixels
|
||||||
|
m_player.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::GetFont()->Scale = old_size;
|
||||||
|
ImGui::PopFont();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
|
||||||
|
// Fermeture de la fenêtre ImGui
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "imgui_internal.h"
|
||||||
void MainWindow::Loop()
|
void MainWindow::Loop()
|
||||||
{
|
{
|
||||||
// Main loop
|
// Main loop
|
||||||
|
|
@ -749,14 +804,21 @@ void MainWindow::Loop()
|
||||||
|
|
||||||
m_gui.StartFrame();
|
m_gui.StartFrame();
|
||||||
|
|
||||||
ImGui::DockSpaceOverViewport(ImGui::GetMainViewport());
|
auto vp = ImGui::GetMainViewport();
|
||||||
DrawMainMenuBar();
|
|
||||||
|
auto pos = vp->WorkPos;
|
||||||
|
auto size = vp->WorkSize;
|
||||||
|
pos.x += 60;
|
||||||
|
size.x -= 60;
|
||||||
|
vp->WorkPos = pos;
|
||||||
|
vp->WorkSize = size;
|
||||||
|
ImGui::DockSpaceOverViewport(vp);
|
||||||
|
float height = DrawMainMenuBar();
|
||||||
|
|
||||||
// DrawStatusBar();
|
// DrawStatusBar();
|
||||||
|
|
||||||
|
|
||||||
ProcessStory();
|
ProcessStory();
|
||||||
|
|
||||||
|
|
||||||
// ------------ Draw all windows
|
// ------------ Draw all windows
|
||||||
m_libraryWindow.Draw();
|
m_libraryWindow.Draw();
|
||||||
|
|
||||||
|
|
@ -771,6 +833,12 @@ void MainWindow::Loop()
|
||||||
|
|
||||||
m_PropertiesWindow.SetSelectedNode(m_nodeEditorWindow.GetSelectedNode());
|
m_PropertiesWindow.SetSelectedNode(m_nodeEditorWindow.GetSelectedNode());
|
||||||
m_PropertiesWindow.Draw();
|
m_PropertiesWindow.Draw();
|
||||||
|
|
||||||
|
|
||||||
|
// static ImGuiAxis toolbar2_axis = ImGuiAxis_Y;
|
||||||
|
// DockingToolbar("Toolbar2", &toolbar2_axis);
|
||||||
|
|
||||||
|
DrawToolBar(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectPropertiesPopup();
|
ProjectPropertiesPopup();
|
||||||
|
|
@ -863,7 +931,7 @@ void MainWindow::Build(bool compileonly)
|
||||||
if (!compileonly)
|
if (!compileonly)
|
||||||
{
|
{
|
||||||
// 3. Convert all media to desired type format
|
// 3. Convert all media to desired type format
|
||||||
m_resources.ConvertResources(m_story->AssetsPath(), ""); // pas de répertoire de destination
|
m_resources.ConvertResources(m_story->AssetsPath(), "", m_story->GetImageFormat(), m_story->GetSoundFormat()); // pas de répertoire de destination
|
||||||
}
|
}
|
||||||
|
|
||||||
Chip32::Assembler::Error err;
|
Chip32::Assembler::Error err;
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ public:
|
||||||
MainWindow();
|
MainWindow();
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
void Initialize();
|
bool Initialize();
|
||||||
void Loop();
|
void Loop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -155,8 +155,9 @@ private:
|
||||||
void SaveParams();
|
void SaveParams();
|
||||||
void LoadParams();
|
void LoadParams();
|
||||||
|
|
||||||
void DrawMainMenuBar();
|
float DrawMainMenuBar();
|
||||||
bool ShowQuitConfirm();
|
bool ShowQuitConfirm();
|
||||||
|
void DrawToolBar(float topPadding);
|
||||||
|
|
||||||
void SaveProject();
|
void SaveProject();
|
||||||
void CloseProject();
|
void CloseProject();
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@ NodeEditorWindow::~NodeEditorWindow()
|
||||||
void NodeEditorWindow::Initialize()
|
void NodeEditorWindow::Initialize()
|
||||||
{
|
{
|
||||||
ed::Config config;
|
ed::Config config;
|
||||||
config.SettingsFile = "Widgets.json";
|
config.SettingsFile = nullptr;
|
||||||
|
config.SaveSettings = nullptr;
|
||||||
|
config.LoadSettings = nullptr;
|
||||||
m_context = ed::CreateEditor(&config);
|
m_context = ed::CreateEditor(&config);
|
||||||
|
|
||||||
ed::SetCurrentEditor(m_context);
|
ed::SetCurrentEditor(m_context);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue