chatgui: add sound playback via miniaudio (MP3 notification sounds)
- SoundManager singleton wraps ma_engine + ma_sound (non-blocking, background thread) - MP3 files embedded in QRC, decoded to PCM at startup with MA_SOUND_FLAG_DECODE - miniaudio.h (MIT) in lib/, IMPLEMENTATION in separate miniaudio_impl.c - Sound settings page in SettingsDialog: enable/disable, volume slider (0-100%) - Config keys [gui] sound_enabled, sound_volume - Incoming messages trigger msg_incoming.mp3 sound
This commit is contained in:
1 parent
f6f7e12192
commit
c2ad2e78c3
25 files changed
+101647
-2
No files matched your search
@@ -0,0 +1,124 @@
|
||||
#include "miniaudio.h"
|
||||
|
||||
#include "sound_manager.h"
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
SoundManager* SoundManager::instance() {
|
||||
static SoundManager s;
|
||||
return &s;
|
||||
}
|
||||
|
||||
SoundManager::~SoundManager() { shutdown(); }
|
||||
|
||||
bool SoundManager::init() {
|
||||
if (m_initialized) return true;
|
||||
|
||||
ma_engine* engine = new ma_engine;
|
||||
ma_result result = ma_engine_init(NULL, engine);
|
||||
if (result != MA_SUCCESS) {
|
||||
qWarning("SoundManager: ma_engine_init failed (%d)", result);
|
||||
delete engine;
|
||||
return false;
|
||||
}
|
||||
m_engine = engine;
|
||||
m_initialized = true;
|
||||
qDebug("SoundManager: initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundManager::shutdown() {
|
||||
if (!m_initialized) return;
|
||||
|
||||
for (auto it = m_sounds.begin(); it != m_sounds.end(); ++it) {
|
||||
if (it->sound) {
|
||||
ma_sound_uninit(it->sound);
|
||||
delete it->sound;
|
||||
}
|
||||
}
|
||||
m_sounds.clear();
|
||||
|
||||
if (m_engine) {
|
||||
ma_engine_uninit(m_engine);
|
||||
delete m_engine;
|
||||
m_engine = nullptr;
|
||||
}
|
||||
m_initialized = false;
|
||||
qDebug("SoundManager: shutdown");
|
||||
}
|
||||
|
||||
void SoundManager::setEnabled(bool enabled) {
|
||||
m_enabled = enabled;
|
||||
qDebug("SoundManager: enabled=%d", enabled);
|
||||
}
|
||||
|
||||
bool SoundManager::isEnabled() const { return m_enabled; }
|
||||
|
||||
void SoundManager::setVolume(float volume) {
|
||||
if (volume < 0.0f) volume = 0.0f;
|
||||
if (volume > 1.0f) volume = 1.0f;
|
||||
m_volume = volume;
|
||||
for (auto it = m_sounds.begin(); it != m_sounds.end(); ++it) {
|
||||
if (it->sound)
|
||||
ma_sound_set_volume(it->sound, m_volume);
|
||||
}
|
||||
}
|
||||
|
||||
float SoundManager::volume() const { return m_volume; }
|
||||
|
||||
bool SoundManager::loadSound(const QString& name, const QString& qrcPath) {
|
||||
if (!m_initialized) {
|
||||
qWarning("SoundManager: not initialized, cannot load %s", qPrintable(name));
|
||||
return false;
|
||||
}
|
||||
if (m_sounds.contains(name)) {
|
||||
qWarning("SoundManager: sound '%s' already loaded", qPrintable(name));
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile f(qrcPath);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qWarning("SoundManager: cannot open %s", qPrintable(qrcPath));
|
||||
return false;
|
||||
}
|
||||
QByteArray mp3Data = f.readAll();
|
||||
f.close();
|
||||
|
||||
ma_decoder_config decConfig = ma_decoder_config_init_default();
|
||||
ma_decoder decoder;
|
||||
ma_result result = ma_decoder_init_memory(mp3Data.constData(), mp3Data.size(), &decConfig, &decoder);
|
||||
if (result != MA_SUCCESS) {
|
||||
qWarning("SoundManager: ma_decoder_init_memory failed for %s (%d)", qPrintable(name), result);
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadedSound ls;
|
||||
ls.sound = new ma_sound;
|
||||
result = ma_sound_init_from_data_source(m_engine, (ma_data_source*)&decoder.ds,
|
||||
MA_SOUND_FLAG_DECODE,
|
||||
NULL, ls.sound);
|
||||
ma_decoder_uninit(&decoder);
|
||||
|
||||
if (result != MA_SUCCESS) {
|
||||
qWarning("SoundManager: sound init failed for %s (%d)", qPrintable(name), result);
|
||||
delete ls.sound;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sounds.insert(name, ls);
|
||||
qDebug("SoundManager: loaded '%s' from %s (%lld bytes)",
|
||||
qPrintable(name), qPrintable(qrcPath), (long long)mp3Data.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundManager::play(const QString& name) {
|
||||
if (!m_enabled || !m_initialized) return;
|
||||
|
||||
auto it = m_sounds.find(name);
|
||||
if (it == m_sounds.end()) return;
|
||||
|
||||
ma_sound* s = it->sound;
|
||||
ma_sound_stop(s);
|
||||
ma_sound_seek_to_pcm_frame(s, 0);
|
||||
ma_sound_start(s);
|
||||
}
|
||||
Reference in new issue
Block a user