You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

109 lines
3.2 KiB

#pragma once
#include <QObject>
#include <QHash>
#include <QString>
#include <QByteArray>
#include <vector>
#include <memory>
#include <cstdint>
#include <QTimer>
#include "audio_device.h"
struct ma_engine;
struct ma_context;
struct ma_decoder;
struct ma_sound;
class SoundManager : public QObject {
Q_OBJECT
public:
struct EventSettings {
bool enabled = true;
float volume = 0.8f;
};
static SoundManager* instance();
bool init();
void shutdown();
void setEnabled(bool enabled);
bool isEnabled() const;
void setVolume(float volume);
float volume() const;
void setEvent(const QString& name, bool enabled, float volume);
EventSettings eventSettings(const QString& name) const;
bool loadSound(const QString& name, const QString& filePath);
void play(const QString& name);
void playLoop(const QString& name);
void stopLoop();
ma_context* context();
static bool deviceId(const QString& key, ma_device_id* id);
void setDevices(const QString& output, const QString& input);
QString playbackDevice() const { return m_playbackId; }
QString captureDevice() const { return m_captureId; }
/* owner удерживает готовый PCM без копии в GUI; без owner короткие буферы копируются. */
void playRawPcm(const int16_t* pcm, int sampleCount, int sampleRate, int channels,
std::shared_ptr<std::vector<int16_t>> owner = {});
void pauseCurrentPcm();
void resumeCurrentPcm();
void stopCurrentPcm();
void seekCurrentPcm(unsigned long long pcmFrame);
unsigned long long currentPcmCursor() const;
bool isCurrentPcmPlaying() const;
int currentPcmSampleRate() const { return m_currentPcmSampleRate; }
struct DeviceInfo {
QString name;
bool isDefault = false;
QString id;
};
static QVector<DeviceInfo> enumeratePlaybackDevices();
static QVector<DeviceInfo> enumerateCaptureDevices();
signals:
void contextAboutToReset(); // Все зависимые устройства закрываются синхронно перед uninit контекста.
void contextRestored();
void devicesChanged();
private:
SoundManager();
~SoundManager();
void clearCurrentPcm();
void recoverAudio();
bool openContext();
void openOutput();
struct LoadedSound {
QByteArray mp3Data;
ma_decoder* decoder = nullptr;
ma_sound* sound = nullptr;
};
ma_engine* m_engine = nullptr;
ma_context* m_context = nullptr;
AudioDevice m_output;
QTimer m_recoveryTimer;
QElapsedTimer m_clock;
qint64 m_contextRetryAt = 0;
int m_contextRetryDelay = 250;
QString m_playbackId, m_captureId;
ma_backend m_backend = ma_backend_null;
bool m_backendSelected = false;
ma_sound* m_currentPcmSound = nullptr;
void* m_currentPcmBuffer = nullptr;
int m_currentPcmSampleRate = 0;
std::shared_ptr<std::vector<int16_t>> m_currentPcmOwner;
bool m_initialized = false;
bool m_enabled = true;
float m_volume = 0.8f;
QHash<QString, EventSettings> m_eventSettings;
QHash<QString, LoadedSound> m_sounds;
QString m_loopName;
};