#include #pragma managed(push, off) // ************************************************************************************************************************************************************ // ************************************************************************************************************************************************************ struct FMODSoundDescriptor { FMOD::Sound* m_sound; FMOD::Channel* m_channel; FMOD::System* m_system; REAL m_fadeClock; REAL m_fadeTime; INT32 m_soundType; bool m_enable3DPositioning; bool m_pinToListener; CRuVector3 m_3dPosition; FMODSoundDescriptor(); ~FMODSoundDescriptor(); void Initialize(FMOD::System *fmod, FMOD::Channel *fmodChannel, FMOD::Sound *fmodSound, INT32 soundType); void Update(REAL elapsedTime); int IsPlaying(); int Stop(); void Release(); int FadeAndStop(REAL fadeTime); int SetVolume(REAL volume); REAL GetVolume(); void FMODError(const char *format, ...); }; MySoud::MySoud() { m_defaults_3D_MinDist = 1.0f; m_defaults_3D_MaxDist = 480.0f; m_defaults_3D_Rolloff = 0.10f; m_activeDescriptors.Clear(); m_fmodSystem = NULL; m_masterVolume = 1.0f; } MySoud::~MySoud() { } CRuEvent& MySoud::Event_PlaybackFinished() { return m_event_PlaybackFinished; } BOOL MySoud::Startup() { FMODError("MySoud::Startup\n"); FMOD_RESULT result; result = FMOD::System_Create(&m_fmodSystem); if (result != FMOD_OK) { FMODError("Startup FMOD 1 error! (%d) %s\n", result, FMOD_ErrorString(result)); return FALSE; } result = m_fmodSystem->init(512, FMOD_INIT_NORMAL, 0); if (result != FMOD_OK) { FMODError("Startup FMOD 2 error! (%d) %s\n", result, FMOD_ErrorString(result)); return FALSE; } return TRUE; } BOOL MySoud::Shutdown() { FMODError("MySoud::Shutdown\n"); if (m_fmodSystem == NULL) return FALSE; for(INT32 i = 0; i < m_activeDescriptors.Count(); ++i) { FMODSoundDescriptor* descriptor = m_activeDescriptors[i]; descriptor->Stop(); ruSAFE_RELEASE(descriptor); delete descriptor; } m_activeDescriptors.Clear(); FMOD_RESULT result = m_fmodSystem->release(); if (result != FMOD_OK) { FMODError("Shutdown FMOD 1 error! (%d) %s\n", result, FMOD_ErrorString(result)); return FALSE; } return TRUE; } const CRuVector3& MySoud::GetListenerPosition() { return m_listenerPosition; } BOOL MySoud::SetListenerPosition(const CRuVector3& listenerPosition) { if (m_fmodSystem == NULL) return FALSE; CRuVector3 oldListenerPosition = m_listenerPosition; m_listenerPosition = listenerPosition; for(INT32 i = 0; i < m_activeDescriptors.Count(); ++i) { if(m_activeDescriptors[i]->m_enable3DPositioning && m_activeDescriptors[i]->m_pinToListener) { if (m_activeDescriptors[i]->IsPlaying()) { CRuVector3 relativePosition = m_activeDescriptors[i]->m_3dPosition - oldListenerPosition; m_activeDescriptors[i]->m_3dPosition = relativePosition + m_listenerPosition; } } } FMOD_VECTOR fmodPos = { m_listenerPosition.m_x, m_listenerPosition.m_y, m_listenerPosition.m_z }; FMOD_VECTOR fmodForward = { m_listenerForward.m_x, m_listenerForward.m_y, m_listenerForward.m_z }; FMOD_VECTOR fmodUp = { m_listenerUp.m_x, m_listenerUp.m_y, m_listenerUp.m_z }; FMOD_RESULT result = m_fmodSystem->set3DListenerAttributes(0, &fmodPos, 0, &fmodForward, &fmodUp); //FMOD_RESULT result = m_fmodSystem->set3DListenerAttributes(0, &fmodPos, 0, 0, 0); if (result != FMOD_OK) { FMODError("SetListenerPosition FMOD 1 error! code:%d error:%s\n", result, FMOD_ErrorString(result)); return NULL; } result = m_fmodSystem->set3DNumListeners(1); if (result != FMOD_OK) { FMODError("SetListenerPosition FMOD 2 error! code:%d error:%s\n", result, FMOD_ErrorString(result)); return NULL; } return TRUE; } BOOL MySoud::SetListenerOrientation(const CRuVector3& listenerForward, const CRuVector3& listenerUp) { m_listenerForward = listenerForward; m_listenerUp = listenerUp; return TRUE; } BOOL MySoud::SetDefault3DSoundDistances(REAL minDist, REAL maxDist) { OutputDebugString("MySoud::SetDefault3DSoundDistances\n"); if (m_fmodSystem == NULL) return FALSE; m_defaults_3D_MinDist = minDist; m_defaults_3D_MaxDist = maxDist; return TRUE; } BOOL MySoud::SetDefault3DSoundRolloff(REAL rolloff) { OutputDebugString("MySoud::SetDefault3DSoundRolloff\n"); if (m_fmodSystem == NULL) return FALSE; m_defaults_3D_Rolloff = rolloff; return FALSE; } REAL MySoud::GetDefault3DMaxDist() { //OutputDebugString("MySoud::GetDefault3DMaxDist\n"); return m_defaults_3D_MaxDist; } PTRVALUE MySoud::PlaySoundByPath(const char* soundPath, BOOL loop, REAL volumeLevel, INT32 soundType) { return PlaySound(soundPath, soundType, volumeLevel, loop); } PTRVALUE MySoud::PlaySound(const char* soundPath, INT32 soundType, REAL volumeLevel, BOOL loop) { if (m_fmodSystem == NULL) return NULL; IRuStream* sampleStream = resourceManager->LoadStream(soundPath); if (sampleStream == NULL) return NULL; FMOD_CREATESOUNDEXINFO exInfo; memset(&exInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); exInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); exInfo.length = sampleStream->GetStreamSize(); char* soundData = new char[sampleStream->GetStreamSize()]; sampleStream->Read(soundData, sampleStream->GetStreamSize()); delete sampleStream; FMOD::Sound* sound = NULL; FMOD_MODE mode = FMOD_2D | FMOD_OPENMEMORY; if (loop) { FMODError("PlaySound loop not realised file:%s\n", soundPath); mode |= FMOD_LOOP_NORMAL; } else mode |= FMOD_LOOP_OFF; FMOD_RESULT result = m_fmodSystem->createSound(soundData, mode, &exInfo, &sound); delete []soundData; if (result != FMOD_OK) { FMODError("PlaySound FMOD 1 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } FMOD::Channel* channel; result = m_fmodSystem->playSound(sound, NULL, true, &channel); if (result != FMOD_OK) { FMODError("PlaySound FMOD 2 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } channel->setVolumeRamp(true); if (result != FMOD_OK) { FMODError("PlaySound FMOD 3 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } channel->setVolume(GetVolume(volumeLevel)); if (result != FMOD_OK) { FMODError("PlaySound FMOD 4 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } // TODO: Предлагается установить рампу от нуля. channel->setPaused(false); if (result != FMOD_OK) { FMODError("PlaySound FMOD 5 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } FMODSoundDescriptor* descriptor = new FMODSoundDescriptor; descriptor->Initialize(m_fmodSystem, channel, sound, soundType); m_activeDescriptors.Add(descriptor); return (PTRVALUE)sound; } PTRVALUE MySoud::Play3DSound(const char* soundPath, INT32 soundType, REAL volumeLevel, BOOL loop, const CRuVector3& position, BOOL pinToListener) { if (m_fmodSystem == NULL) return NULL; REAL maxThreshold = m_defaults_3D_MaxDist * 1.10f; REAL distance = (position - m_listenerPosition).Magnitude(); if(distance > maxThreshold) return NULL; IRuStream* sampleStream = resourceManager->LoadStream(soundPath); if (sampleStream == NULL) return NULL; FMOD_CREATESOUNDEXINFO exInfo; memset(&exInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); exInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); exInfo.length = sampleStream->GetStreamSize(); char* soundData = new char[sampleStream->GetStreamSize()]; sampleStream->Read(soundData, sampleStream->GetStreamSize()); delete sampleStream; FMOD::Sound* sound = NULL; FMOD_MODE mode = FMOD_3D | FMOD_OPENMEMORY; if (loop) { FMODError("Play3DSound loop not realised file:%s\n", soundPath); mode |= FMOD_LOOP_NORMAL; } else mode |= FMOD_LOOP_OFF; FMOD_RESULT result = m_fmodSystem->createSound(soundData, mode, &exInfo, &sound); delete []soundData; if (result != FMOD_OK) { FMODError("Play3DSound FMOD 1 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } FMOD::Channel* channel; result = m_fmodSystem->playSound(sound, NULL, true, &channel); if (result != FMOD_OK) { FMODError("Play3DSound FMOD 2 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } channel->setVolumeRamp(true); if (result != FMOD_OK) { FMODError("Play3DSound FMOD 3 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } channel->setVolume(GetVolume(volumeLevel)); if (result != FMOD_OK) { FMODError("Play3DSound FMOD 4 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } // TODO: Предлагается установить рампу от нуля. channel->setPaused(false); if (result != FMOD_OK) { FMODError("Play3DSound FMOD 5 error! code:%d error:%s file:%s\n", result, FMOD_ErrorString(result), soundPath); return NULL; } FMODSoundDescriptor* descriptor = new FMODSoundDescriptor; descriptor->Initialize(m_fmodSystem, channel, sound, soundType); descriptor->m_enable3DPositioning = TRUE; descriptor->m_pinToListener = pinToListener; descriptor->m_3dPosition = position; m_activeDescriptors.Add(descriptor); Update3DSoundChannelLevels(descriptor); return (PTRVALUE)sound; } BOOL MySoud::Set3DSoundPosition(PTRVALUE soundHandle, const CRuVector3& position) { FMODError("MySoud::Set3DSoundPosition\n"); if (m_fmodSystem == NULL) return FALSE; for(INT32 i = 0; i < m_activeDescriptors.Count(); ++i) { if ((PTRVALUE)m_activeDescriptors[i]->m_sound == soundHandle) { m_activeDescriptors[i]->m_3dPosition = position; return TRUE; } } return FALSE; } BOOL MySoud::SetMasterSoundVolume(REAL volumeLevel) { //FMODError("MySoud::SetMasterSoundVolume\n"); if (m_fmodSystem == NULL) return FALSE; m_masterVolume = volumeLevel; for(INT32 i = 0; i < m_activeDescriptors.Count(); ++i) { m_activeDescriptors[i]->SetVolume(GetVolume(m_activeDescriptors[i]->GetVolume())); } return TRUE; } BOOL MySoud::AdjustSoundVolumeByType(INT32 soundType, REAL volumeLevel) { //FMODError("MySoud::AdjustSoundVolumeByType\n"); if (m_fmodSystem == NULL) return FALSE; for(INT32 i = 0; i < m_activeDescriptors.Count(); ++i) { if (m_activeDescriptors[i]->m_soundType == soundType) { if (m_activeDescriptors[i]->IsPlaying()) { m_activeDescriptors[i]->SetVolume(GetVolume(volumeLevel)); } } } return TRUE; } BOOL MySoud::FadeOutSound(PTRVALUE soundHandle, REAL fadeOutTimeMS) { //FMODError("MySoud::FadeOutSound\n"); if (m_fmodSystem == NULL) return FALSE; for(INT32 i = 0; i < m_activeDescriptors.Count(); ++i) { if ((PTRVALUE)m_activeDescriptors[i]->m_sound == soundHandle) { m_activeDescriptors[i]->FadeAndStop(fadeOutTimeMS / 1000.0f); return TRUE; } } return FALSE; } BOOL MySoud::StopSound(PTRVALUE soundHandle) { FMODError("MySoud::StopSound\n"); if (m_fmodSystem == NULL) return FALSE; for(INT32 i = 0; i < m_activeDescriptors.Count(); ++i) { if ((PTRVALUE)m_activeDescriptors[i]->m_sound == soundHandle) { m_activeDescriptors[i]->Stop(); return TRUE; } } return FALSE; } BOOL MySoud::Update(REAL elapsedTime) { //FMODError("MySoud::Update\n"); if (m_fmodSystem == NULL) return FALSE; m_fmodSystem->update(); for(INT32 i = 0; i < m_activeDescriptors.Count(); ++i) { // Update channel m_activeDescriptors[i]->Update(elapsedTime); // Update 3D sound channel level Update3DSoundChannelLevels(m_activeDescriptors[i]); // If channel is no longer active, free it if(m_activeDescriptors[i]->IsPlaying() == FALSE) { FMODSoundDescriptor* descriptor = m_activeDescriptors[i]; // TODO: Вызываем калбэк для закончевшейся музыки RuSymphonyEvent_PlaybackFinished_Args playbackFinishedArgs(reinterpret_cast(descriptor->m_sound)); m_event_PlaybackFinished.Trigger(&playbackFinishedArgs); // TODO: Возможно нужна реализация Release ruSAFE_RELEASE(descriptor); m_activeDescriptors.RemoveAt(i); delete descriptor; --i; } } return TRUE; } BOOL MySoud::ClearUnusedSoundDescriptors() { OutputDebugString("MySoud::ClearUnusedSoundDescriptors\n"); return FALSE; } BOOL MySoud::VolumeRamp(PTRVALUE sampleHandle, REAL level0, REAL level1, REAL duration) { OutputDebugString("MySoud::VolumeRamp\n"); return FALSE; } BOOL MySoud::SoundVolumeRampIsZero(PTRVALUE soundHandle) { OutputDebugString("MySoud::SoundVolumeRampIsZero\n"); return FALSE; } BOOL MySoud::Update3DSoundChannelLevels(FMODSoundDescriptor* descriptor) { //FMODError("MySoud::Update3DSoundChannelLevels\n"); if (m_fmodSystem == NULL) return FALSE; if (!descriptor->m_enable3DPositioning) return FALSE; if (!descriptor->IsPlaying()) return FALSE; FMOD_VECTOR fmodPos = { descriptor->m_3dPosition.m_x, descriptor->m_3dPosition.m_y, descriptor->m_3dPosition.m_z }; descriptor->m_channel->set3DAttributes(&fmodPos, 0); descriptor->m_channel->set3DMinMaxDistance(m_defaults_3D_MinDist, m_defaults_3D_MaxDist); return FALSE; } BOOL MySoud::ClearUnusedSoundDescriptorsCallback(const void* key, void* data) { OutputDebugString("MySoud::ClearUnusedSoundDescriptorsCallback\n"); return FALSE; } BOOL MySoud::GCSoundDescriptors() { OutputDebugString("MySoud::GCSoundDescriptors\n"); return FALSE; } BOOL MySoud::GCSoundDescriptorsCallback(const void* key, void* data) { OutputDebugString("MySoud::GCSoundDescriptorsCallback\n"); return FALSE; } BOOL MySoud::ClearSoundDescriptors() { OutputDebugString("MySoud::ClearSoundDescriptors\n"); return FALSE; } BOOL MySoud::ClearSoundDescriptorsCallback(const void* key, void* data) { OutputDebugString("MySoud::ClearSoundDescriptorsCallback\n"); return FALSE; } FMODSoundDescriptor::FMODSoundDescriptor() { m_system = NULL; m_sound = NULL; m_channel = NULL; m_fadeTime = -1.0f; m_fadeClock = 0.0f; m_soundType = 0; m_enable3DPositioning = false; m_pinToListener = false; } FMODSoundDescriptor::~FMODSoundDescriptor() { m_system = NULL; m_sound = NULL; m_channel = NULL; } void FMODSoundDescriptor::Initialize(FMOD::System *fmod, FMOD::Channel *fmodChannel, FMOD::Sound *fmodSound, INT32 soundType) { m_system = fmod; m_channel = fmodChannel; m_sound = fmodSound; m_soundType = soundType; } int FMODSoundDescriptor::IsPlaying() { bool isPlaying = false; FMOD_RESULT fmodResult = m_channel->isPlaying(&isPlaying); return fmodResult != FMOD_ERR_INVALID_HANDLE && isPlaying != false; } void FMODSoundDescriptor::Update(REAL elapsedTime) { if(m_fadeTime > 0.0f) { // Increment fade clock m_fadeClock += elapsedTime; if(m_fadeClock >= m_fadeTime) { // Force stop this->Stop(); } else { // Calculate volume as a linear function of time REAL volume = (m_fadeTime - m_fadeClock) / m_fadeTime; // Set volume FMOD_RESULT fmodResult = m_channel->setVolume(volume); if (fmodResult != FMOD_OK) { FMODError("Update FMOD 1 error! code:%d error:%s\n", fmodResult, FMOD_ErrorString(fmodResult)); } } } } int FMODSoundDescriptor::Stop() { FMOD_RESULT fmodResult = m_channel->stop(); if (fmodResult != FMOD_OK) { FMODError("Stop FMOD 1 error! code:%d error:%s\n", fmodResult, FMOD_ErrorString(fmodResult)); } return fmodResult == FMOD_OK; } void FMODSoundDescriptor::Release() { m_sound->release(); } int FMODSoundDescriptor::FadeAndStop(REAL fadeTime) { m_fadeClock = 0.0f; m_fadeTime = fadeTime; // Adjust fade clock based on current volume REAL curVolume = 1.0f; m_channel->getVolume(&curVolume); m_fadeClock = curVolume > ruEPSILON ? m_fadeTime * (1.0f - curVolume) : m_fadeTime; return TRUE; } int FMODSoundDescriptor::SetVolume(REAL volume) { FMOD_RESULT fmodResult = m_channel->setVolume(volume); if (fmodResult != FMOD_OK) { FMODError("SetVolume FMOD 1 error! code:%d error:%s\n", fmodResult, FMOD_ErrorString(fmodResult)); } return fmodResult == FMOD_OK; } REAL FMODSoundDescriptor::GetVolume() { float volume = 1.0f; FMOD_RESULT fmodResult = m_channel->getVolume(&volume); if (fmodResult != FMOD_OK) { FMODError("GetVolume FMOD 1 error! code:%d error:%s\n", fmodResult, FMOD_ErrorString(fmodResult)); } return fmodResult == FMOD_OK ? volume : 1.0f; } void MySoud::FMODError(const char *format, ...) { static char buf[4096]; if (format == NULL) return; va_list args; va_start(args, format); vsprintf(buf, format, args); va_end(args); OutputDebugString(buf); } REAL MySoud::GetVolume(REAL volume) { if (m_fmodSystem == NULL) return 0.0f; return m_masterVolume / 1.0f * volume; } void FMODSoundDescriptor::FMODError(const char *format, ...) { static char buf[4096]; if (format == NULL) return; va_list args; va_start(args, format); vsprintf(buf, format, args); va_end(args); OutputDebugString(buf); } // ************************************************************************************************************************************************************ MySoud* g_ruSymphony = NULL; // ************************************************************************************************************************************************************ #pragma managed(pop)