#pragma once #include #include #include #include #include #include #include #include #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> 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 enumeratePlaybackDevices(); static QVector 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> m_currentPcmOwner; bool m_initialized = false; bool m_enabled = true; float m_volume = 0.8f; QHash m_eventSettings; QHash m_sounds; QString m_loopName; };