#include "SoundsEngine.h"
#include "..\..\common_h\corecmds.h"
#ifdef _XBOX
#include
#endif
SoundsEngine * SoundsEngine::engine = null;
//-----------------------------------------------------------------------------------------------
//Мастер сцены, применяющий к её звукам эффекты
//-----------------------------------------------------------------------------------------------
SoundsEngine::SceneMaster::SceneMaster()
{
Assert(SoundsEngine::engine != null);
voice = null;
silence = null;
flags = 0;
}
SoundsEngine::SceneMaster::~SceneMaster()
{
//Удаляем объекты
if(silence)
{
silence->DestroyVoice();
silence = null;
}
if(voice)
{
voice->DestroyVoice();
voice = null;
}
}
HRESULT SoundsEngine::SceneMaster::Init()
{
//Создать войс для применения эффектов сцены
Assert(!voice);
Assert(!silence);
//Войс
HRESULT hr;
XAUDIO2_VOICE_SENDS sendList;
sendList.SendCount = 0;
sendList.pSends = null;
hr = SoundsEngine::engine->pXAudio2->CreateSubmixVoice(&voice, 2, GetFxMixFreq(), 0, 15, &sendList, null);
if(hr != S_OK) return hr;
//Создаём источник тишины, чтобы эффект работал постоянно
XAUDIO2_SEND_DESCRIPTOR sendDesc;
sendDesc.Flags = 0;
sendDesc.pOutputVoice = voice;
sendList.SendCount = 1;
sendList.pSends = &sendDesc;
WAVEFORMATEX format;
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = 2;
format.nSamplesPerSec = SoundsEngine::GetFxMixFreq();
format.wBitsPerSample = 16;
format.nBlockAlign = (format.nChannels*format.wBitsPerSample)/8;
format.nAvgBytesPerSec = (format.nChannels*format.wBitsPerSample*format.nSamplesPerSec)/8;
format.cbSize = 0;
Assert(SoundsEngine::engine->pXAudio2);
hr = SoundsEngine::engine->pXAudio2->CreateSourceVoice(&silence, &format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, null, &sendList, null);
if(hr != S_OK) return hr;
Assert(silence);
hr = silence->SubmitSourceBuffer(&SoundsEngine::engine->silenceBuffer, null);
if(hr != S_OK) return hr;
return hr;
}
//Использовать этот мастер сцены, если возможно
bool SoundsEngine::SceneMaster::Alloc()
{
if(flags & flags_isActive)
{
return false;
}
flags |= flags_isActive;
Assert(voice);
XAUDIO2_SEND_DESCRIPTOR sendDesc;
sendDesc.Flags = 0;
sendDesc.pOutputVoice = SoundsEngine::engine->fxPremasteringVoice;
XAUDIO2_VOICE_SENDS sendList;
sendList.SendCount = 1;
sendList.pSends = &sendDesc;
voice->SetOutputVoices(&sendList);
return true;
}
//Удалить
void SoundsEngine::SceneMaster::Release()
{
if((flags & flags_isActive) == 0)
{
return;
}
//Сбрасываем параметры эффект-процессора сцены
fxScene.Clear();
//Остановим звук тишины
if(silence)
{
silence->Stop();
}
//Остановим отсоединим мастер сцены
if(voice)
{
XAUDIO2_VOICE_SENDS sends;
sends.SendCount = 0;
sends.pSends = null;
voice->SetEffectChain(null);
voice->SetOutputVoices(&sends);
}
//Сбрасываем флажки
flags = 0;
}
//Запустить работу мастера
void SoundsEngine::SceneMaster::Resume()
{
if((flags & flags_isPause) == 0)
{
return;
}
if(flags & flags_isInitFx)
{
Assert(silence);
silence->Start();
fxScene.Reset();
}
flags &= ~flags_isPause;
}
//Остановить работу мастера
void SoundsEngine::SceneMaster::Stop()
{
if(flags & flags_isPause)
{
return;
}
if(flags & flags_isInitFx)
{
Assert(silence);
silence->Stop();
fxScene.Reset();
}
flags |= flags_isPause;
}
//Установить параметры серды окружения
void SoundsEngine::SceneMaster::SetEnvironment(const FxScene::EnvParams & params)
{
if((flags & flags_isInitFx) == 0)
{
//Описание эффекта
XAUDIO2_EFFECT_DESCRIPTOR fxDesc;
fxDesc.pEffect = (IXAPO *)&fxScene;
fxDesc.InitialState = true;
fxDesc.OutputChannels = 2;
XAUDIO2_EFFECT_CHAIN fxChain;
fxChain.EffectCount = 1;
fxChain.pEffectDescriptors = &fxDesc;
voice->SetEffectChain(&fxChain);
silence->Start();
flags |= flags_isInitFx;
}
fxScene.SetParams(params);
}
//-----------------------------------------------------------------------------------------------
//Источник звука
//-----------------------------------------------------------------------------------------------
SoundsEngine::SoundChannel::SoundChannel() : xWmaBuffer(_FL_, 1)
{
voice = null;
fxVoice = null;
states = 0;
lowPrtCounter = 0;
mode = 0;
format = 0;
waveVolume = 0.0f;
time = 0;
fadeOutTime = 0.0f;
samplesCount = 0;
startPlayPosition = 0;
continuePosition = 0;
currentUniqPtr = null;
}
SoundsEngine::SoundChannel::~SoundChannel()
{
Assert(!voice);
Assert(!fxVoice);
Assert(!currentUniqPtr);
}
void SoundsEngine::SoundChannel::Release()
{
if(voice)
{
voice->DestroyVoice();
voice = null;
}
if(fxVoice)
{
fxVoice->DestroyVoice();
fxVoice = null;
}
currentUniqPtr = null;
}
//Запустить источник звука на проигрывание
void SoundsEngine::SoundChannel::Play()
{
if(!(states & state_isPlay))
{
Assert(SoundsEngine::engine);
bool isNeedPause = (SoundsEngine::engine->isPause != 0);
Assert(voice);
XAUDIO2_VOICE_SENDS sendList;
sendList.SendCount = 1;
XAUDIO2_SEND_DESCRIPTOR sendDesc;
sendDesc.Flags = 0;
sendDesc.pOutputVoice = fxVoice;
sendList.pSends = &sendDesc;
voice->SetOutputVoices(&sendList);
if((states & state_isPause) == 0 && !isNeedPause)
{
voice->Start(0);
}
lowPrtCounter = 0;
states &= ~state_fadeoutProcess;
states |= state_isPlay;
if(isNeedPause)
{
Pause(true);
}
}
}
//Приостоновить буффер не освобождая канала
void SoundsEngine::SoundChannel::Pause(bool isPause)
{
if(((states & state_isPause) != 0) == isPause)
{
return;
}
if(isPause)
{
states |= state_isPause;
if((states & state_isPlay) != 0 && voice)
{
voice->Stop();
}
}else{
states &= ~state_isPause;
if((states & state_isPlay) != 0 && voice)
{
voice->Start(0);
}
}
}
//Плавно увеличить громкость с текущего значения до максимума
void SoundsEngine::SoundChannel::FadeIn(float time)
{
if(!(states & state_isPlay))
{
Assert(voice);
//Сколько надо шагов
float steps = time*SoundsEngine::GetFxMixFreq();
if(steps < 16)
{
//Очень мало шагов, сразу проигрываем
Play();
return;
}
//Шаг с которым надо увеличивать громкость с 0 до 1
float fadeDelta = 1.0f/steps;
fxProcessor.FadeIn(fadeDelta);
Play();
}
}
//Плавно уменьшить громкость с текущего значения до нуля
void SoundsEngine::SoundChannel::FadeOut(float time)
{
if((states & state_isPlay) && voice)
{
states |= state_fadeoutProcess;
//Сколько надо шагов
float steps = time*SoundsEngine::GetFxMixFreq();
if(steps < 16)
{
//Очень мало шагов, просто останавливаем
Stop();
return;
}
//Шаг с которым надо уменьшать громкость с 1 до 0
float fadeDelta = -1.0f/steps;
fxProcessor.FadeOut(fadeDelta);
fadeOutTime = time;
}
}
//Установить позицию в 3D (включает позиционирование звука)
void SoundsEngine::SoundChannel::SetLocators(const FxVoice::Locator * locs, dword count)
{
fxProcessor.SetLocators(locs, count);
}
//Установить громкость канала
void SoundsEngine::SoundChannel::SetVolume(float volume)
{
fxProcessor.SetVolume(waveVolume*volume);
}
//Установить режим низчайшего приоритета, при котором можно сразу вытеснять канал
void SoundsEngine::SoundChannel::SetLowPriority(bool isLowPriority)
{
if(!isLowPriority)
{
lowPrtCounter = 0;
}else{
lowPrtCounter++;
if(lowPrtCounter > 0x0fffffff)
{
lowPrtCounter = 0x0fffffff;
}
}
}
//Установить громкость волны
void SoundsEngine::SoundChannel::FixWaveVolume(float volume)
{
waveVolume = volume;
}
//Получить текущую громкость канала
float SoundsEngine::SoundChannel::DebugGetCurrentVolume()
{
return fxProcessor.GetCurrentVolume();
}
//Активен ли канал для данного владельца
bool SoundsEngine::SoundChannel::IsLost(void * uniqPtr)
{
return (currentUniqPtr != uniqPtr);
}
//На паузе ли буфер
bool SoundsEngine::SoundChannel::IsPause()
{
return (states & state_isPause) != 0;
}
//Играет ли звук
bool SoundsEngine::SoundChannel::IsPlay()
{
if(states & state_isPlay)
{
if(voice)
{
XAUDIO2_VOICE_STATE vstate;
voice->GetState(&vstate);
if(vstate.BuffersQueued > (states & state_buffersForStopMask))
{
if(!(states & state_fadeoutProcess))
{
return true;
}else{
float vol = fxProcessor.GetFadeVolume();
if(vol > (0.1f/65536.0f))
{
return true;
}
}
}
}
Stop();
}
return false;
}
//Получить позицию проигрывания в сэмплах
bool SoundsEngine::SoundChannel::GetPlayPosition(dword & playPosition)
{
if(states & state_isPlay)
{
if(voice)
{
XAUDIO2_VOICE_STATE vstate;
voice->GetState(&vstate);
if(vstate.BuffersQueued)
{
//Позиция проигрывания в текущем буфере
UINT64 currentPlayPos = vstate.SamplesPlayed + continuePosition - startPlayPosition;
if((mode & SoundBankFileSetup::mode_loop_mask) != SoundBankFileSetup::mode_loop_diasble)
{
Assert(samplesCount > 0);
currentPlayPos %= (UINT64)samplesCount;
}
if(currentPlayPos > 0xffffffff)
{
currentPlayPos = 0xffffffff;
}
playPosition = (dword)currentPlayPos;
return true;
}
}
Stop();
}
return false;
}
//Остановить канал
void SoundsEngine::SoundChannel::Stop()
{
if(states & state_isPlay)
{
if(voice)
{
XAUDIO2_VOICE_SENDS sends;
sends.SendCount = 0;
sends.pSends = null;
voice->SetOutputVoices(&sends);
fxVoice->SetOutputVoices(&sends);
fxProcessor.Reset();
samplesCount = 0;
startPlayPosition = 0;
voice->DestroyVoice();
voice = null;
}
states &= ~(state_isPlay | state_fadeoutProcess | state_isPause);
lowPrtCounter = 0;
}
}
//Получить логическое время
long SoundsEngine::SoundChannel::GetTime()
{
if(states & state_fadeoutProcess)
{
//Если идёт процесс фейда, то возвращаем эмпирическое значение, зависящее от текущей громкости
float vol = fxProcessor.GetCurrentVolume();
vol = 1.0f - Clampf(vol);
return long(vol*-1000000.0f);
}
return time;
}
//-----------------------------------------------------------------------------------------------
//SoundsEngine
//-----------------------------------------------------------------------------------------------
SoundsEngine::SoundsEngine() : sceneMasters(_FL_, 256)
{
Assert(!engine);
engine = this;
//Системное
pXAudio2 = null;
masteringVoice = null;
fxPremasteringVoice = null;
musicPremasteringVoice = null;
FxVoice::StaticInit(GetFxMixFreq());
isPause = 0;
notificationListenerForXbox = null;
//Буфер тишины
memset(silenceLoop, 0, sizeof(silenceLoop));
memset(&silenceBuffer, 0, sizeof(silenceBuffer));
silenceBuffer.Flags = XAUDIO2_END_OF_STREAM;
silenceBuffer.AudioBytes = sizeof(silenceBuffer);
silenceBuffer.pAudioData = (BYTE *)silenceLoop;
silenceBuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
//Звуки
dword c = 0;
for(dword i = 0; i < c_soundChannels; i++)
{
sound[i] = &soundChannels[c++];
}
soundCount = 0;
for(dword i = 0; i < c_reserveChannels; i++)
{
reserved[i] = &soundChannels[c++];
}
reservedCount = 0;
Assert(c == c_totalSoundChannels);
Assert(ARRSIZE(soundChannels) == c_totalSoundChannels);
creationCounter = 0;
#ifndef _XBOX
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif
#ifndef NO_TOOLS
previewMaster = null;
previewMasterVolume = 1.0f;
previewVoice = null;
previewVoiceMirror = null;
previewUserId[0] = previewUserId[1] = previewUserId[2] = previewUserId[3] = 0;
#endif
}
SoundsEngine::~SoundsEngine()
{
Release();
#ifndef _XBOX
CoUninitialize();
#endif
Assert(engine == this);
engine = null;
}
//Инициализировать
bool SoundsEngine::Init()
{
HRESULT hr;
dword flags = 0;
XAUDIO2_PROCESSOR processor = XAUDIO2_DEFAULT_PROCESSOR;
#ifdef _DEBUG
//flags = XAUDIO2_DEBUG_ENGINE;
#endif
#ifdef _XBOX
processor = XboxThread4;
notificationListenerForXbox = XNotifyCreateListener(XNOTIFY_XMP);
if(notificationListenerForXbox == null)
{
api->Trace("SoundService error: Can't create XNotifyCreateListener object!");
}
#endif
if(FAILED(hr = XAudio2Create(&pXAudio2, flags, processor)))
{
api->Trace("SoundService error: Can't create XAudio2 object, error code: 0x%x", hr);
return false;
}
//Мастер
if(FAILED(hr = pXAudio2->CreateMasteringVoice(&masteringVoice)))
{
api->Trace("SoundService error: Can't create mastering voice object, error code: 0x%x", hr);
return false;
}
Assert(masteringVoice);
XAUDIO2_VOICE_SENDS sendToMaster;
sendToMaster.SendCount = 1;
XAUDIO2_SEND_DESCRIPTOR sendDesc;
sendDesc.Flags = 0;
sendDesc.pOutputVoice = masteringVoice;
sendToMaster.pSends = &sendDesc;
XAUDIO2_EFFECT_DESCRIPTOR fxDesc;
fxDesc.pEffect = (IXAPO *)&fxPremaster;
fxDesc.InitialState = true;
fxDesc.OutputChannels = 2;
XAUDIO2_EFFECT_CHAIN fxChain;
fxChain.EffectCount = 1;
fxChain.pEffectDescriptors = &fxDesc;
//Премастер канала эффектов
if(FAILED(hr = pXAudio2->CreateSubmixVoice(&fxPremasteringVoice, 2, GetFxMixFreq(), 0, 20, &sendToMaster, &fxChain)))
{
api->Trace("SoundService error: Can't create fx premastering voice object, error code: 0x%x", hr);
return false;
}
Assert(fxPremasteringVoice);
api->ExecuteCoreCommand(CoreCommand_CheckMemory());
// float output4to2[2][4] = {{1.0f, 1.0f, 1.0f, 1.0f},{1.0f, 1.0f, 1.0f, 1.0f}};
// hr = fxPremasteringVoice->SetOutputMatrix(masteringVoice, 4, 2, (float *)output4to2);
//Премастер музыкального канала
if(FAILED(hr = pXAudio2->CreateSubmixVoice(&musicPremasteringVoice, 2, GetMusMixFreq(), 0, 20, &sendToMaster)))
{
api->Trace("SoundService error: Can't create music premastering voice object, error code: 0x%x", hr);
return false;
}
Assert(musicPremasteringVoice);
//Инициализация каналов
for(dword i = 0; i < ARRSIZE(soundChannels); i++)
{
SoundChannel & channel = soundChannels[i];
XAUDIO2_EFFECT_DESCRIPTOR fxDesc;
fxDesc.pEffect = (IXAPO *)&channel.fxProcessor;
fxDesc.InitialState = true;
fxDesc.OutputChannels = 2;
XAUDIO2_EFFECT_CHAIN fxChain;
fxChain.EffectCount = 1;
fxChain.pEffectDescriptors = &fxDesc;
HRESULT hr = pXAudio2->CreateSubmixVoice(&channel.fxVoice, 2, GetFxMixFreq(), 0, 10, null, &fxChain);
if(hr != S_OK)
{
api->Trace("SoundsEngine::Init() -> CreateSubmixVoice: result failed: hr = 0x%x", hr);
return false;
}
Assert(channel.fxVoice);
}
//Запускаем звуковую библиотеку
pXAudio2->StartEngine();
UpdateMusicState();
return true;
}
//Освободить ресурсы и перейди в неинициализированное состояние
void SoundsEngine::Release()
{
for(dword i = 0; i < ARRSIZE(soundChannels); i++)
{
soundChannels[i].Release();
}
for(dword i = 0; i < sceneMasters.Size(); i++)
{
sceneMasters[i]->Release();
}
Sleep(100);
#ifndef NO_TOOLS
if(previewVoice)
{
previewVoice->DestroyVoice();
previewVoice = null;
}
if(previewVoiceMirror)
{
previewVoiceMirror->DestroyVoice();
previewVoiceMirror = null;
}
if(previewMaster)
{
previewMaster->DestroyVoice();
previewMaster = null;
}
#endif
for(dword i = 0; i < sceneMasters.Size(); i++)
{
delete sceneMasters[i];
sceneMasters[i] = null;
}
sceneMasters.Empty();
if(musicPremasteringVoice)
{
musicPremasteringVoice->DestroyVoice();
musicPremasteringVoice = null;
}
if(fxPremasteringVoice)
{
fxPremasteringVoice->DestroyVoice();
fxPremasteringVoice = null;
}
if(masteringVoice)
{
masteringVoice->DestroyVoice();
masteringVoice = null;
}
if(pXAudio2)
{
pXAudio2->StopEngine();
Sleep(100);
pXAudio2->Release();
pXAudio2 = null;
}
dword c = 0;
for(dword i = 0; i < c_soundChannels; i++)
{
sound[i] = &soundChannels[c++];
}
soundCount = 0;
for(dword i = 0; i < c_reserveChannels; i++)
{
reserved[i] = &soundChannels[c++];
}
reservedCount = 0;
creationCounter = 0;
#ifdef _XBOX
if(notificationListenerForXbox != null)
{
XCloseHandle(notificationListenerForXbox);
}
#endif
}
//Обновить состояние
void SoundsEngine::Update()
{
//Глушим доигрываемые каналы
for(dword i = 0; i < reservedCount; i++)
{
if(!reserved[i]->IsPlay())
{
reserved[i]->Stop();
ExchangeChannels(reserved[i], reserved[reservedCount - 1]);
reservedCount--;
}
}
//Глушим зависшие каналы (PCM fix)
for(dword i = 0; i < soundCount; i++)
{
dword pos = 0;
if(sound[i]->GetPlayPosition(pos))
{
if(pos > sound[i]->samplesCount*2)
{
//Принудительно останавливаем (1 канал за кадр)
ReleaseSoundChannel(sound[i], sound[i]->currentUniqPtr);
break;
}
}
}
#ifdef _XBOX
if(notificationListenerForXbox)
{
//Смотрим состояние проигрывателя
DWORD msgFilter;
ULONG_PTR param;
if(XNotifyGetNext(notificationListenerForXbox, 0, &msgFilter, ¶m))
{
if(msgFilter == XN_XMP_STATECHANGED)
{
UpdateMusicState();
}
}
}
#endif
}
//Приостановить проигрывание звуков
void SoundsEngine::SetPause(bool isSetPause)
{
// api->Trace("SoundsEngine> Set pause: %s, current flag is %i", isSetPause ? "true" : "false", isSetPause);
if((isPause != 0) == isSetPause)
{
return;
}
isSetPause ? 1 : 0;
//Глушим доигрываемые каналы
if(isSetPause)
{
// api->Trace("SoundsEngine> Set to pause");
//Отмечаем, что на паузе
isPause = 1;
//Останавливаем хвосты
for(dword i = 0; i < reservedCount; i++)
{
reserved[i]->Stop();
}
reservedCount = 0;
//Останавливаем звуки
for(dword i = 0; i < soundCount; i++)
{
sound[i]->Pause(true);
}
//Останавливаем мастера сцен
for(dword i = 0; i < sceneMasters.Size(); i++)
{
sceneMasters[i]->Stop();
}
}else{
// api->Trace("SoundsEngine> Resume from pause");
//Отмечаем, что работаем
isPause = 0;
//Запускаем мастера сцен
for(dword i = 0; i < sceneMasters.Size(); i++)
{
sceneMasters[i]->Resume();
}
//Востанавливаем звуки
for(dword i = 0; i < soundCount; i++)
{
sound[i]->Pause(false);
}
}
}
//Создать мастер сцены
SoundsEngine::SceneMaster * SoundsEngine::CreateSceneMaster()
{
//Пробуем выделить среди созданных
for(dword i = 0; i < sceneMasters.Size(); i++)
{
if(sceneMasters[i]->Alloc())
{
return sceneMasters[i];
}
}
//Добавляем объект эффекта окружения
SceneMaster * sm = NEW SceneMaster();
HRESULT hr = sm->Init();
if(hr == S_OK)
{
Verify(sm->Alloc());
sceneMasters.Add(sm);
return sm;
}
delete sm;
return null;
}
//Попытаться получить доступный звуковой канал
SoundsEngine::SoundChannel * SoundsEngine::GetSoundChannel(SoundBankFileSound & sbfs, SoundBankFileWaveInfo * selWave, dword startPosition, SceneMaster & scene, void * uniqPtr)
{
//Получаем волну, которую будем проигрывать
Assert(selWave);
SoundBankFileWave * wave = selWave->wave;
if(!wave)
{
//Проигрывание тишины
return null;
}
//Контроль логического времени создания
if(creationCounter > 0xfffffff)
{
for(dword i = 0; i < c_totalSoundChannels; i++)
{
soundChannels[i].time -= 0xff00000;
if(soundChannels[i].time < 0)
{
soundChannels[i].time = 0;
}
}
}
//Если всё занято, пробуем освободить канал
if(soundCount >= c_soundChannels)
{
//Надо освободить какой нибудь, поскольку свободных уже нет
dword soundPriority = sbfs.setup.mode & SoundBankFileSetup::mode_priority_mask;
long bestIndex = ChannelSelector(sound, soundCount, soundPriority, creationCounter);
if(bestIndex < 0)
{
return null; //С текущим или более низким приоритетом каналов нет, поэтому ничего не можем сделать
}
//Освобождаем 1 канал в конце массива
sound[bestIndex]->currentUniqPtr = null;
soundCount = c_soundChannels - 1;
ExchangeChannels(sound[bestIndex], sound[c_soundChannels - 1]);
//Теперь решаем, отправить выбраный на фейд или убить сразу
if(reservedCount < c_reserveChannels)
{
bestIndex = reservedCount++;
}else{
//Поступаем аналогично с доигрываемыми звуками
bestIndex = ChannelSelector(reserved, reservedCount, soundPriority, sound[c_soundChannels - 1]->GetTime());
}
if(bestIndex >= 0)
{
//Есть резервные каналы, оставляем звук фейдиться
sound[c_soundChannels - 1]->FadeOut(c_replaceFadeTimeInMs*0.001f);
ExchangeChannels(sound[c_soundChannels - 1], reserved[bestIndex]);
}
}
//Формат волны
dword waveFormat = wave->format;
if((sbfs.setup.mode & SoundBankFileSetup::mode_fx_mask) == SoundBankFileSetup::mode_fx_music)
{
waveFormat |= SoundBankFileWave::f_tmp_music;
}
//Смотрим канал с подходящим форматом
Assert(soundCount < c_soundChannels);
#ifndef STOP_DEBUG
/*
//!!! Проверка, что свободные каналы не играют
for(dword i = soundCount; i < c_soundChannels; i++)
{
if(sound[i]->voice)
{
XAUDIO2_VOICE_STATE vstate;
sound[i]->voice->GetState(&vstate);
Assert(vstate.BuffersQueued == 0);
}
}
//*/
#endif
/*
for(dword i = soundCount; i < c_soundChannels; i++)
{
if(sound[i]->format == waveFormat)
{
//Есть нужный формат, поместим его в конец активных
ExchangeChannels(sound[soundCount], sound[i]);
break;
}
}
*/
SoundChannel * channel = sound[soundCount];
//Гарантированно останавливаем канал
channel->Stop();
channel->states = 0;
//Подготавливаем канал к использованию
XAUDIO2_VOICE_SENDS sendList;
sendList.SendCount = 1;
XAUDIO2_SEND_DESCRIPTOR sendDesc;
sendDesc.Flags = 0;
sendDesc.pOutputVoice = null;
sendList.pSends = &sendDesc;
//if(channel->format != waveFormat/* || ((channel->mode & SoundBankFileSetup::mode_loop_mask) != SoundBankFileSetup::mode_loop_diasble)*/)
{
//Необходимо пересоздать голос под нужный формат
HRESULT hr;
IXAudio2SourceVoice * sourceVoice = null;
sendDesc.pOutputVoice = channel->fxVoice;
dword workFreq = GetFxMixFreq();
dword flags = XAUDIO2_VOICE_NOPITCH;
if(workFreq == (waveFormat & SoundBankFileWave::f_freq_mask))
{
flags |= XAUDIO2_VOICE_NOSRC;
}
/*if(sbfs.setup.GetFxMode() == SoundBankFileSetup::mode_fx_music)
{
flags |= XAUDIO2_VOICE_MUSIC;
}*/
hr = pXAudio2->CreateSourceVoice(&sourceVoice, (const WAVEFORMATEX *)wave->waveFormatInfo, 0, XAUDIO2_DEFAULT_FREQ_RATIO, null, &sendList, null);
if(hr != S_OK)
{
//Фига се, чегото не создалось... пока проскипаем
api->Trace("SoundsEngine::GetSoundChannel -> CreateSourceVoice return error code 0x%x", hr);
Assert(sourceVoice);
return null;
}
Assert(sourceVoice);
if(channel->voice)
{
channel->voice->DestroyVoice();
}
channel->voice = sourceVoice;
channel->format = waveFormat;
}/*else{
channel->voice->Stop();
}*/
soundCount++;
//Определяем куда подключать звук
switch(sbfs.setup.GetFxMode())
{
case SoundBankFileSetup::mode_fx_full:
Assert(scene.voice);
sendDesc.pOutputVoice = scene.voice;
channel->fxProcessor.EnableEnvironment(true);
break;
case SoundBankFileSetup::mode_fx_premaster:
sendDesc.pOutputVoice = fxPremasteringVoice;
channel->fxProcessor.EnableEnvironment(false);
break;
case SoundBankFileSetup::mode_fx_master:
sendDesc.pOutputVoice = masteringVoice;
channel->fxProcessor.EnableEnvironment(false);
break;
case SoundBankFileSetup::mode_fx_music:
sendDesc.pOutputVoice = musicPremasteringVoice;
channel->fxProcessor.EnableEnvironment(false);
break;
default:
Assert(false);
}
channel->fxVoice->SetOutputVoices(&sendList);
//Сохраняем атрибуты текущего звука
channel->mode = sbfs.setup.mode;
channel->currentUniqPtr = uniqPtr;
channel->waveVolume = selWave->volume;
channel->pickAmp = wave->maxNormalizedAmp;
channel->tailTime = wave->unimportantTime;
channel->fadeOutTime = 0.0f;
channel->time = creationCounter++;
channel->samplesCount = wave->samplesCount;
//Фикс для незацикленных PCM, которые выходят за пределы буфера. Начало
UINT32 flagsForMainBuffer = XAUDIO2_END_OF_STREAM;
bool isPCM_fix_on = false;
if((wave->format & SoundBankFileWave::f_format_mask) == SoundBankFileWave::f_format_pcm)
{
isPCM_fix_on = true;
flagsForMainBuffer = 0;
}
//Заполняем буффер
bool isLoop = sbfs.setup.GetModeLoop() != SoundBankFileSetup::mode_loop_diasble;
XAUDIO2_BUFFER xbuffer;
xbuffer.Flags = flagsForMainBuffer;
xbuffer.AudioBytes = wave->dataSize;
xbuffer.pAudioData = (BYTE *)wave->data;
xbuffer.PlayBegin = startPosition & ~0x7f; //Для XMA надо выравнивать первый сэмпл по 128
xbuffer.PlayLength = 0;
//*
xbuffer.PlayLength = wave->samplesCount - xbuffer.PlayBegin;
if((waveFormat & SoundBankFileWave::f_format_mask) == SoundBankFileWave::f_format_xma)
{
Assert((xbuffer.PlayLength & 0x7f) == 0);
}
//*/
xbuffer.LoopBegin = isLoop ? 0 : XAUDIO2_NO_LOOP_REGION;
xbuffer.LoopLength = 0;
xbuffer.LoopCount = isLoop ? XAUDIO2_LOOP_INFINITE : 0;
xbuffer.pContext = null;
//Устанавливаем буфер
HRESULT hr = XAUDIO2_E_INVALID_CALL;
if((waveFormat & SoundBankFileWave::f_format_mask) != SoundBankFileWave::f_format_xvma)
{
hr = channel->voice->SubmitSourceBuffer(&xbuffer, null);
}else{
//Дополнительные плюшки для xWMA
const SoundBankFileWave::XWMAWAVEFORMAT * xwmaFormat = (const SoundBankFileWave::XWMAWAVEFORMAT *)wave->waveFormatInfo;
if(channel->xWmaBuffer.Size() < xwmaFormat->tableCount)
{
channel->xWmaBuffer.AddElements(xwmaFormat->tableCount - channel->xWmaBuffer.Size());
}
memcpy(channel->xWmaBuffer.GetBuffer(), xwmaFormat->table, xwmaFormat->tableCount*sizeof(dword));
XAUDIO2_BUFFER_WMA wma;
wma.PacketCount = xwmaFormat->tableCount;
wma.pDecodedPacketCumulativeBytes = channel->xWmaBuffer.GetBuffer();
hr = channel->voice->SubmitSourceBuffer(&xbuffer, &wma);
}
if(hr != S_OK)
{
//Не получилось подписать данный буфер на проигрывание, похоже баг в формате
ReleaseSoundChannel(channel, uniqPtr);
api->Trace("SoundsEngine::GetSoundChannel -> SubmitSourceBuffer return error code 0x%x (for sound %s, wave: %i)", hr, sbfs.name, sbfs.waves - selWave);
return null;
}
//Сохраняем стартовую позицию
Assert(channel->samplesCount > 0);
Assert(channel->voice);
XAUDIO2_VOICE_STATE vstate;
channel->voice->GetState(&vstate);
Assert(vstate.BuffersQueued > 0);
Assert(vstate.SamplesPlayed == 0);
channel->startPlayPosition = vstate.SamplesPlayed;
channel->continuePosition = xbuffer.PlayBegin;
//Фикс для незацикленных PCM, которые выходят за пределы буфера. Окончание
if(isPCM_fix_on)
{
hr = channel->voice->SubmitSourceBuffer(&silenceBuffer, null);
if(hr != S_OK)
{
//Не получилось подписать данный буфер на проигрывание, похоже баг в формате
ReleaseSoundChannel(channel, uniqPtr);
api->Trace("SoundsEngine::GetSoundChannel -> SubmitSourceBuffer (PCM fix loop buffer) return error code 0x%x (for sound %s, wave: %i)", hr, sbfs.name, sbfs.waves - selWave);
return null;
}
channel->states |= 1;
}
return channel;
}
//Освободить звуковой канал
void SoundsEngine::ReleaseSoundChannel(SoundChannel * ch, void * uniqPtr)
{
if(ch->currentUniqPtr != uniqPtr) return;
//Переводим канал к свободным
for(dword i = 0; i < soundCount; i++)
{
if(sound[i] == ch)
{
ch->Stop();
if(ch->voice)
{
ch->voice->DestroyVoice();
ch->voice = null;
}
ch->currentUniqPtr = null;
soundCount--;
ExchangeChannels(sound[i], sound[soundCount]);
return;
}
}
//Сюда попадать не должно
Assert(false);
}
//Получить количество активных каналов
dword SoundsEngine::GetPlayChannels()
{
return soundCount;
}
//Получить максимальное количество каналов
dword SoundsEngine::GetMaxChannels()
{
return c_soundChannels;
}
//Получить количество активных каналов для доигрывания
dword SoundsEngine::GetPlayReservedChannels()
{
return reservedCount;
}
//Получить максимальное количество каналов для доигрывания
dword SoundsEngine::GetMaxReservedChannels()
{
return c_reserveChannels;
}
#ifdef _XBOX
//Получить объект XAudio
IXAudio2 * SoundsEngine::GetXAudio()
{
return pXAudio2;
}
#endif
#ifndef NO_TOOLS
//Запустить волну на проигрывание 16бит (для звукового редактора)
bool SoundsEngine::EditPrewiewPlay(const dword userId[4], EditPrewiewWaveParams * mainWave, EditPrewiewWaveParams * mirrorWave, bool isMirror)
{
//-------------------------------------------------------------------
static IXAudio2SubmixVoice * testFxVoice = null;
//Отладка эффектов
#ifdef _DEBUG
if(false)
//if(!testFxVoice)
#else
if(false)
#endif
{
XAUDIO2_VOICE_SENDS sendList;
sendList.SendCount = 1;
XAUDIO2_SEND_DESCRIPTOR sendDesc;
sendDesc.Flags = 0;
sendDesc.pOutputVoice = masteringVoice;
sendList.pSends = &sendDesc;
static FxPremaster fxPremaster;
static FxScene fxScene;
XAUDIO2_EFFECT_DESCRIPTOR fxDesc[3];
fxDesc[0].pEffect = (IXAPO *)&fxScene;
fxDesc[0].InitialState = true;
fxDesc[0].OutputChannels = 2;
XAUDIO2_EFFECT_CHAIN fxChain;
fxChain.EffectCount = 1;
fxChain.pEffectDescriptors = &fxDesc[0];
HRESULT hr = pXAudio2->CreateSubmixVoice(&testFxVoice, 2, 44100, 0, 15, &sendList, &fxChain);
sendList.SendCount = 1;
sendDesc.Flags = 0;
sendDesc.pOutputVoice = testFxVoice;
WAVEFORMATEX format;
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = 2;
format.nSamplesPerSec = 44100/8;
format.wBitsPerSample = 16;
format.nBlockAlign = (format.nChannels*format.wBitsPerSample)/8;
format.nAvgBytesPerSec = (format.nChannels*format.wBitsPerSample*format.nSamplesPerSec)/8;
format.cbSize = 0;
Assert(pXAudio2);
hr = pXAudio2->CreateSourceVoice(&previewVoice, &format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, null, &sendList, null);
Assert(hr == S_OK)
XAUDIO2_BUFFER xbuffer;
xbuffer.Flags = XAUDIO2_END_OF_STREAM;
static short buffer[4096];
memset(buffer, 0, ARRSIZE(buffer));
xbuffer.AudioBytes = 4096;
xbuffer.pAudioData = (BYTE *)buffer;
xbuffer.PlayBegin = 0;
xbuffer.PlayLength = 0;
xbuffer.LoopBegin = 0;
xbuffer.LoopLength = 0;
xbuffer.LoopCount = XAUDIO2_LOOP_INFINITE;
xbuffer.pContext = null;
hr = previewVoice->SubmitSourceBuffer(&xbuffer, null);
previewVoice->Start();
previewVoice = null;
}
//-------------------------------------------------------------------
if(!previewMaster)
{
XAUDIO2_VOICE_SENDS sendToMaster;
sendToMaster.SendCount = 1;
XAUDIO2_SEND_DESCRIPTOR sendDesc;
sendDesc.Flags = 0;
sendDesc.pOutputVoice = masteringVoice;
sendToMaster.pSends = &sendDesc;
if(pXAudio2->CreateSubmixVoice(&previewMaster, 2, GetMusMixFreq(), 0, 25, &sendToMaster) != S_OK)
{
previewMaster = null;
}
EditPrewiewSetMasterVolume(previewMasterVolume);
}
//Если играло, то надо остановить проигрывание
static dword stop_userId[4] = {0, 0, 0, 0};
EditPrewiewStop(stop_userId);
Assert(!previewVoice);
Assert(!previewVoiceMirror);
Assert((previewUserId[0] | previewUserId[1] | previewUserId[2] | previewUserId[3]) == 0);
//Идентификатор
Assert((userId[0] | userId[1] | userId[2] | userId[3]) != 0);
Assert(mainWave != null);
previewUserId[0] = userId[0];
previewUserId[1] = userId[1];
previewUserId[2] = userId[2];
previewUserId[3] = userId[3];
//Куда выводить звук
Assert(masteringVoice);
XAUDIO2_VOICE_SENDS sendList;
sendList.SendCount = 1;
XAUDIO2_SEND_DESCRIPTOR sendDesc;
sendDesc.Flags = 0;
if(testFxVoice)
{
sendDesc.pOutputVoice = testFxVoice;
}else{
if(!previewMaster)
{
sendDesc.pOutputVoice = masteringVoice;
}else{
sendDesc.pOutputVoice = previewMaster;
}
}
sendList.pSends = &sendDesc;
//Формат волны
WAVEFORMATEX format;
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = mainWave->isStereo ? 2 : 1;
format.nSamplesPerSec = mainWave->sampleRate;
format.wBitsPerSample = 16;
format.nBlockAlign = (format.nChannels*format.wBitsPerSample)/8;
format.nAvgBytesPerSec = (format.nChannels*format.wBitsPerSample*format.nSamplesPerSec)/8;
format.cbSize = 0;
//Создаём войс для воспроизведения
Assert(pXAudio2);
HRESULT hr = pXAudio2->CreateSourceVoice(&previewVoice, &format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, null, &sendList, null);
if(hr != S_OK)
{
Assert(!previewVoice);
return false;
}
Assert(previewVoice);
XAUDIO2_BUFFER xbuffer;
xbuffer.Flags = XAUDIO2_END_OF_STREAM;
xbuffer.AudioBytes = mainWave->dataSize;
xbuffer.pAudioData = (BYTE *)mainWave->data;
xbuffer.PlayBegin = 0;
xbuffer.PlayLength = 0;
xbuffer.LoopBegin = 0;
xbuffer.LoopLength = 0;
xbuffer.LoopCount = 0;
xbuffer.pContext = null;
hr = previewVoice->SubmitSourceBuffer(&xbuffer, null);
if(hr != S_OK)
{
EditPrewiewStop(stop_userId);
return false;
}
if(mirrorWave)
{
format.nChannels = mirrorWave->isStereo ? 2 : 1;
format.nSamplesPerSec = mirrorWave->sampleRate;
format.nBlockAlign = (format.nChannels*format.wBitsPerSample)/8;
format.nAvgBytesPerSec = (format.nChannels*format.wBitsPerSample*format.nSamplesPerSec)/8;
HRESULT hr = pXAudio2->CreateSourceVoice(&previewVoiceMirror, &format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, null, &sendList, null);
if(hr != S_OK)
{
Assert(!previewVoiceMirror);
EditPrewiewStop(stop_userId);
return false;
}
xbuffer.AudioBytes = mirrorWave->dataSize;
xbuffer.pAudioData = (BYTE *)mirrorWave->data;
hr = previewVoiceMirror->SubmitSourceBuffer(&xbuffer, null);
if(hr != S_OK)
{
EditPrewiewStop(stop_userId);
return false;
}
if(isMirror)
{
previewVoice->SetVolume(0.0f);
previewVoiceMirror->SetVolume(1.0f);
}else{
previewVoice->SetVolume(1.0f);
previewVoiceMirror->SetVolume(0.0f);
}
previewVoice->Start();
previewVoiceMirror->Start();
}else{
previewVoice->SetVolume(1.0f);
previewVoice->Start();
}
return true;
}
//Переключить волну на зеркальную или оригинальную
bool SoundsEngine::EditPrewiewSwitch(const dword userId[4], bool isMirror)
{
if(previewUserId != userId)
{
return false;
}
if(!EditPrewiewIsPlay(userId, null))
{
return false;
}
if(!previewVoice || !previewVoiceMirror)
{
return false;
}
if(!isMirror)
{
previewVoice->SetVolume(1.0f);
previewVoiceMirror->SetVolume(0.0f);
}else{
previewVoice->SetVolume(0.0f);
previewVoiceMirror->SetVolume(1.0f);
}
return true;
}
//Играет ли волна на прослушивании (для звукового редактора)
bool SoundsEngine::EditPrewiewIsPlay(const dword userId[4], dword * samplesCount, bool * playWithMirror)
{
static dword stop_userId[4] = {0, 0, 0, 0};
if(previewVoice)
{
if((userId[0] | userId[1] | userId[2] | userId[3]) != 0)
{
if(((previewUserId[0] ^ userId[0]) | (previewUserId[1] ^ userId[1]) | (previewUserId[2] ^ userId[2]) | (previewUserId[3] ^ userId[3])) != 0)
{
return false;
}
}
XAUDIO2_VOICE_STATE state;
previewVoice->GetState(&state);
if(state.BuffersQueued > 0)
{
if(samplesCount)
{
*samplesCount = (dword)state.SamplesPlayed;
}
if(playWithMirror)
{
*playWithMirror = (previewVoiceMirror != null);
}
return true;
}
EditPrewiewStop(stop_userId);
}else{
if(previewVoiceMirror)
{
EditPrewiewStop(stop_userId);
}
}
return false;
}
//Остановить прослушивание волны
void SoundsEngine::EditPrewiewStop(const dword userId[4])
{
if(previewVoice || previewVoiceMirror)
{
if((userId[0] | userId[1] | userId[2] | userId[3]) != 0)
{
if(((previewUserId[0] ^ userId[0]) | (previewUserId[1] ^ userId[1]) | (previewUserId[2] ^ userId[2]) | (previewUserId[3] ^ userId[3])) != 0)
{
return;
}
}
if(previewVoice)
{
previewVoice->DestroyVoice();
previewVoice = null;
}
if(previewVoiceMirror)
{
previewVoiceMirror->DestroyVoice();
previewVoiceMirror = null;
}
}
previewUserId[0] = previewUserId[1] = previewUserId[2] = previewUserId[3] = 0;
}
//Установить громкость прослушиваемой волны
void SoundsEngine::EditPrewiewSetVolume(float volume, const dword userId[4])
{
if(previewVoice)
{
if((userId[0] | userId[1] | userId[2] | userId[3]) != 0)
{
if(((previewUserId[0] ^ userId[0]) | (previewUserId[1] ^ userId[1]) | (previewUserId[2] ^ userId[2]) | (previewUserId[3] ^ userId[3])) != 0)
{
return;
}
}
float setVol = Clampf(volume, 0.0f, 1.0f);
previewVoice->SetVolume(volume);
if(previewVoiceMirror)
{
previewVoiceMirror->DestroyVoice();
previewVoiceMirror = null;
}
}
}
//Установить громкость мастера предпрослушивания
void SoundsEngine::EditPrewiewSetMasterVolume(float volume)
{
previewMasterVolume = volume;
if(previewMaster)
{
previewMaster->SetVolume(volume);
}
}
#endif
//Обновить состояние музыкального канала в зависимости от состояния проигрывателя
void SoundsEngine::UpdateMusicState()
{
#ifdef _XBOX
XMP_STATE state;
XMPGetStatus(&state);
if(state == XMP_STATE_IDLE)
{
musicPremasteringVoice->SetVolume(1.0f);
}else{
musicPremasteringVoice->SetVolume(0.0f);
}
#endif
}
//Выбрать канал для замещения
long SoundsEngine::ChannelSelector(SoundChannel ** ch, long count, long priority, long time)
{
long bestIndex = -1;
dword bestPriority = priority;
long bestTime = time;
dword bestLowPrt = 0;
bool bestIsLoop = false;
bool bestIsTail = false;
float bestTailTime = 1000000.0f;
float bestPickVolume = 1.0f;
for(long i = 0; i < count; i++)
{
SoundChannel * channel = ch[i];
bool isLoop = (channel->mode & SoundBankFileSetup::mode_loop_mask) != SoundBankFileSetup::mode_loop_diasble;
//Проверяем режим низкоприоритетной работы (3D звук за пределами радиуса)
if(channel->lowPrtCounter > 0)
{
//Если переход с зацикленного звука на незацикленный, то не смотрим остальное
if(channel->lowPrtCounter > bestLowPrt || (bestIsLoop && !isLoop))
{
bestIndex = i;
bestLowPrt = channel->lowPrtCounter;
bestIsLoop = isLoop;
continue;
}
//Возможно будут ещё условия
continue;
}else
if(bestLowPrt > 0)
{
continue;
}
//Пропускаем волны с большим приоритетом
dword channelPriority = channel->mode & SoundBankFileSetup::mode_priority_mask;
if(channelPriority > bestPriority)
{
continue;
}
//Расширеные параметры канала
float pickVolume = channel->waveVolume*channel->pickAmp;
float tailTime = 1000000.0f;
bool isTail = false;
if((channel->states & SoundChannel::state_fadeoutProcess) == 0)
{
if(!isLoop)
{
dword pos = 0;
if(!channel->GetPlayPosition(pos)) pos = 0;
dword tailSamples = channel->samplesCount - pos;
float freq = (float)(channel->format & SoundBankFileWave::f_freq_mask);
if(freq < 1.0f) freq = 1.0f;
tailTime = (float)tailSamples/freq;
isTail = (tailTime < channel->tailTime);
}
}else{
//Если идёт процесс фейда, то возвращаем эмпирическое значение, зависящее от текущей громкости
isLoop = false;
float vol = Clampf(channel->fxProcessor.GetCurrentVolume());
pickVolume *= vol;
tailTime = vol*channel->fadeOutTime;
}
if(pickVolume < 0.00001f) pickVolume = 0.00001f;
//Сначала проверияем приоритет
if(bestIndex >= 0 && channelPriority == priority)
{
//Среди звуков с одинаковым приоритетом просматриваем остальные критерии
if(bestIsTail == isTail)
{
if(bestTailTime*bestPickVolume < tailTime*pickVolume)
{
continue;
}
}else
if(!isTail)
{
continue;
}
}
//Звук с меньшим преоритетом всегда вытесняеться нисмотря на другие критерии
bestIndex = i;
bestPriority = channelPriority;
bestTime = channel->GetTime();
bestIsLoop = isLoop;
bestIsTail = isTail;
bestTailTime = tailTime;
bestPickVolume = pickVolume;
}
return bestIndex;
}
//Поменять местами каналы
__forceinline void SoundsEngine::ExchangeChannels(SoundChannel * & ch1, SoundChannel * & ch2)
{
SoundChannel * tmp = ch1;
ch1 = ch2;
ch2 = tmp;
}
//Получить частоту для канала эфектов
dword SoundsEngine::GetFxMixFreq()
{
#ifdef _XBOX
//static const dword fxSubMixFreq = dword(48000/XAUDIO2_QUANTUM_DENOMINATOR)*XAUDIO2_QUANTUM_DENOMINATOR;
static const dword fxSubMixFreq = dword(44100/XAUDIO2_QUANTUM_DENOMINATOR)*XAUDIO2_QUANTUM_DENOMINATOR;
#else
static const dword fxSubMixFreq = dword(44100/XAUDIO2_QUANTUM_DENOMINATOR)*XAUDIO2_QUANTUM_DENOMINATOR;
#endif
return fxSubMixFreq;
}
//Получить частоту для канала музыки
dword SoundsEngine::GetMusMixFreq()
{
#ifdef _XBOX
//static const dword musSubMixFreq = dword(48000/XAUDIO2_QUANTUM_DENOMINATOR)*XAUDIO2_QUANTUM_DENOMINATOR;
static const dword musSubMixFreq = dword(44100/XAUDIO2_QUANTUM_DENOMINATOR)*XAUDIO2_QUANTUM_DENOMINATOR;
#else
static const dword musSubMixFreq = dword(44100/XAUDIO2_QUANTUM_DENOMINATOR)*XAUDIO2_QUANTUM_DENOMINATOR;
#endif
return musSubMixFreq;
}