275 lines
7.9 KiB
C++
275 lines
7.9 KiB
C++
#include "soundmanager.h"
|
|
#include <QDebug>
|
|
#include <QFileInfo>
|
|
|
|
SoundManager::SoundManager(QObject *parent) : QObject(parent)
|
|
{
|
|
// Инициализация COM
|
|
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
|
|
// Инициализация каналов
|
|
for (int i = 0; i < 2; i++) {
|
|
m_channels[i].graphBuilder = NULL;
|
|
m_channels[i].mediaControl = NULL;
|
|
m_channels[i].mediaPosition = NULL;
|
|
m_channels[i].basicAudio = NULL;
|
|
m_channels[i].audioRenderer = NULL;
|
|
m_channels[i].sessionManager = NULL;
|
|
m_channels[i].sessionControl = NULL;
|
|
m_channels[i].isLoaded = false;
|
|
}
|
|
}
|
|
|
|
SoundManager::~SoundManager()
|
|
{
|
|
// Очистка ресурсов
|
|
for (int i = 0; i < 2; i++) {
|
|
cleanupChannel(static_cast<SoundChannel>(i));
|
|
}
|
|
CoUninitialize();
|
|
}
|
|
|
|
IBaseFilter* SoundManager::createAudioRenderer()
|
|
{
|
|
IBaseFilter* renderer = NULL;
|
|
|
|
// Создаем отдельный аудио рендерер для каждого канала
|
|
HRESULT hr = CoCreateInstance(CLSID_DSoundRender, NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IBaseFilter,
|
|
(void**)&renderer);
|
|
|
|
if (FAILED(hr)) {
|
|
return NULL;
|
|
}
|
|
|
|
return renderer;
|
|
}
|
|
|
|
bool SoundManager::initializeChannel(SoundChannel channel)
|
|
{
|
|
int idx = static_cast<int>(channel);
|
|
|
|
// Создаем GraphBuilder
|
|
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IGraphBuilder,
|
|
(void**)&m_channels[idx].graphBuilder);
|
|
|
|
if (FAILED(hr)) {
|
|
return false;
|
|
}
|
|
|
|
// Создаем отдельный аудио рендерер для этого канала
|
|
m_channels[idx].audioRenderer = createAudioRenderer();
|
|
if (!m_channels[idx].audioRenderer) {
|
|
cleanupChannel(channel);
|
|
return false;
|
|
}
|
|
|
|
// Добавляем рендерер в граф
|
|
WCHAR filterName[64];
|
|
swprintf_s(filterName, 64, L"Audio Renderer Channel %d", idx + 1);
|
|
|
|
hr = m_channels[idx].graphBuilder->AddFilter(m_channels[idx].audioRenderer, filterName);
|
|
if (FAILED(hr)) {
|
|
cleanupChannel(channel);
|
|
return false;
|
|
}
|
|
|
|
// Пытаемся получить интерфейс аудио сессии для отдельного управления
|
|
IAudioSessionManager2* pSessionManager = NULL;
|
|
hr = m_channels[idx].audioRenderer->QueryInterface(__uuidof(IAudioSessionManager2),
|
|
(void**)&m_channels[idx].sessionManager);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
// Получаем контроль над сессией
|
|
hr = m_channels[idx].sessionManager->GetAudioSessionControl(NULL, 0,
|
|
&m_channels[idx].sessionControl);
|
|
if (SUCCEEDED(hr)) {
|
|
// Устанавливаем уникальное отображаемое имя для сессии
|
|
WCHAR sessionName[64];
|
|
swprintf_s(sessionName, 64, L"Channel %d", idx + 1);
|
|
m_channels[idx].sessionControl->SetDisplayName(sessionName, NULL);
|
|
}
|
|
}
|
|
|
|
// Получаем интерфейсы управления
|
|
hr = m_channels[idx].graphBuilder->QueryInterface(IID_IMediaControl,
|
|
(void**)&m_channels[idx].mediaControl);
|
|
if (FAILED(hr)) {
|
|
cleanupChannel(channel);
|
|
return false;
|
|
}
|
|
|
|
hr = m_channels[idx].graphBuilder->QueryInterface(IID_IMediaPosition,
|
|
(void**)&m_channels[idx].mediaPosition);
|
|
if (FAILED(hr)) {
|
|
cleanupChannel(channel);
|
|
return false;
|
|
}
|
|
|
|
hr = m_channels[idx].graphBuilder->QueryInterface(IID_IBasicAudio,
|
|
(void**)&m_channels[idx].basicAudio);
|
|
if (FAILED(hr)) {
|
|
cleanupChannel(channel);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SoundManager::cleanupChannel(SoundChannel channel)
|
|
{
|
|
int idx = static_cast<int>(channel);
|
|
|
|
if (m_channels[idx].mediaControl) {
|
|
m_channels[idx].mediaControl->Stop();
|
|
m_channels[idx].mediaControl->Release();
|
|
m_channels[idx].mediaControl = NULL;
|
|
}
|
|
|
|
if (m_channels[idx].mediaPosition) {
|
|
m_channels[idx].mediaPosition->Release();
|
|
m_channels[idx].mediaPosition = NULL;
|
|
}
|
|
|
|
if (m_channels[idx].basicAudio) {
|
|
m_channels[idx].basicAudio->Release();
|
|
m_channels[idx].basicAudio = NULL;
|
|
}
|
|
|
|
if (m_channels[idx].sessionControl) {
|
|
m_channels[idx].sessionControl->Release();
|
|
m_channels[idx].sessionControl = NULL;
|
|
}
|
|
|
|
if (m_channels[idx].sessionManager) {
|
|
m_channels[idx].sessionManager->Release();
|
|
m_channels[idx].sessionManager = NULL;
|
|
}
|
|
|
|
if (m_channels[idx].audioRenderer) {
|
|
m_channels[idx].audioRenderer->Release();
|
|
m_channels[idx].audioRenderer = NULL;
|
|
}
|
|
|
|
if (m_channels[idx].graphBuilder) {
|
|
m_channels[idx].graphBuilder->Release();
|
|
m_channels[idx].graphBuilder = NULL;
|
|
}
|
|
|
|
m_channels[idx].isLoaded = false;
|
|
}
|
|
|
|
bool SoundManager::loadSound(SoundChannel channel, const QString &filePath)
|
|
{
|
|
int idx = static_cast<int>(channel);
|
|
|
|
// Проверяем существование файла
|
|
if (!QFileInfo(filePath).exists()) {
|
|
return false;
|
|
}
|
|
|
|
// Очищаем предыдущий звук на канале
|
|
cleanupChannel(channel);
|
|
|
|
// Инициализируем канал
|
|
if (!initializeChannel(channel)) {
|
|
return false;
|
|
}
|
|
|
|
// Конвертируем QString в WCHAR
|
|
std::wstring wideFilePath = filePath.toStdWString();
|
|
|
|
// Рендерим файл
|
|
HRESULT hr = m_channels[idx].graphBuilder->RenderFile(wideFilePath.c_str(), NULL);
|
|
|
|
if (FAILED(hr)) {
|
|
cleanupChannel(channel);
|
|
return false;
|
|
}
|
|
|
|
m_channels[idx].isLoaded = true;
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::playSound(SoundChannel channel)
|
|
{
|
|
int idx = static_cast<int>(channel);
|
|
|
|
if (!m_channels[idx].isLoaded || !m_channels[idx].mediaControl) {
|
|
return false;
|
|
}
|
|
|
|
HRESULT hr = m_channels[idx].mediaControl->Run();
|
|
|
|
if (FAILED(hr)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SoundManager::pauseSound(SoundChannel channel)
|
|
{
|
|
int idx = static_cast<int>(channel);
|
|
|
|
if (!m_channels[idx].isLoaded || !m_channels[idx].mediaControl) {
|
|
return false;
|
|
}
|
|
|
|
HRESULT hr = m_channels[idx].mediaControl->Pause();
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
bool SoundManager::stopSound(SoundChannel channel)
|
|
{
|
|
int idx = static_cast<int>(channel);
|
|
|
|
if (!m_channels[idx].isLoaded || !m_channels[idx].mediaControl) {
|
|
return false;
|
|
}
|
|
|
|
HRESULT hr = m_channels[idx].mediaControl->Stop();
|
|
|
|
// Сбрасываем позицию воспроизведения
|
|
if (m_channels[idx].mediaPosition) {
|
|
m_channels[idx].mediaPosition->put_CurrentPosition(0);
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
void SoundManager::setVolume(SoundChannel channel, int volume)
|
|
{
|
|
int idx = static_cast<int>(channel);
|
|
|
|
if (!m_channels[idx].isLoaded || !m_channels[idx].basicAudio) {
|
|
return;
|
|
}
|
|
|
|
// Конвертируем 0-100 в диапазон DirectShow (-10000 до 0)
|
|
long directShowVolume = (volume - 100) * 100; // 0 = -10000, 100 = 0
|
|
|
|
m_channels[idx].basicAudio->put_Volume(directShowVolume);
|
|
}
|
|
|
|
bool SoundManager::isPlaying(SoundChannel channel)
|
|
{
|
|
int idx = static_cast<int>(channel);
|
|
|
|
if (!m_channels[idx].isLoaded || !m_channels[idx].mediaControl) {
|
|
return false;
|
|
}
|
|
|
|
OAFilterState state;
|
|
HRESULT hr = m_channels[idx].mediaControl->GetState(10, &state);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
return (state == State_Running);
|
|
}
|
|
|
|
return false;
|
|
}
|