首页 > 代码库 > 游戏架构其七:音效管理
游戏架构其七:音效管理
大型游戏中的音效相对比较复杂,如声音的淡入淡出,爆炸音效,声音的播放进度等。下面就看看一个音效管理系统都有些什么吧~~
1. 音效的基本操作:
#pragma once //======================================================================== // Audio.cpp : Defines a simple sound system. 参照: // <a target=_blank href=http://www.mamicode.com/"http://blog.csdn.net/wanghexu09008126/article/details/39462377">http://blog.csdn.net/wanghexu09008126/article/details/39462377>//======================================================================== // Audio.cpp : Defines a simple sound system. //======================================================================== #include "GameCodeStd.h" #include <mmsystem.h> #include <mmreg.h> #include "Audio.h" #include "SoundResource.h" //参照 http://www.cnitblog.com/ictfly/archive/2011/06/27/74454.html #pragma comment( lib, "dsound" ) // Globals Audio *g_pAudio = NULL; char *gSoundExtentions[] = { ".mp3", ".wav", ".midi", ".ogg" }; // Construction/Destruction Audio::Audio(): m_Initialized(false), m_AllPaused(false) { } // // Audio::VShutdown // void Audio::VShutdown() { AudioBufferList::iterator i=m_AllSamples.begin(); while (i!=m_AllSamples.end()) { IAudioBuffer *audioBuffer = (*i); audioBuffer->VStop(); //子类实现 m_AllSamples.pop_front(); } } // // Audio::VPauseAllSounds // Pause all active sounds, including music // void Audio::VPauseAllSounds() { AudioBufferList::iterator i; AudioBufferList::iterator end; for(i=m_AllSamples.begin(), end=m_AllSamples.end(); i!=end; ++i) { IAudioBuffer *audioBuffer = (*i); audioBuffer->VPause(); //子类实现 } m_AllPaused=true; } // // Audio::VResumeAllSounds // void Audio::VResumeAllSounds() { AudioBufferList::iterator i; AudioBufferList::iterator end; for(i=m_AllSamples.begin(), end=m_AllSamples.end(); i!=end; ++i) { IAudioBuffer *audioBuffer = (*i); audioBuffer->VResume(); <span style="color:#ff6666;">//子类实现</span> } m_AllPaused=false; } // // Audio::VStopAllSounds // void Audio::VStopAllSounds() { IAudioBuffer *audioBuffer = NULL; AudioBufferList::iterator i; AudioBufferList::iterator end; for(i=m_AllSamples.begin(), end=m_AllSamples.end(); i!=end; ++i) { audioBuffer = (*i); audioBuffer->VStop(); } m_AllPaused=false; } // // Audio::HasSoundCard // // A bit of an anachronism in name - but it simply returns true if the sound system is active. // bool Audio::HasSoundCard(void) { return (g_pAudio && g_pAudio->VActive()); }2. 音效类别和资源下载等#pragma once #include <mmsystem.h> #include "ResCache.h" typedef unsigned int DWORD; typedef unsigned short WORD; typedef unsigned char BYTE; typedef struct { WORD wFormatTag; WORD nChannels; DWORD nSamplesPerSec; DWORD nAvgBytesPerSec; WORD nBlockAlign; WORD wBitsPerSample; WORD cbSize; } WAVEFORMATEX; // // class SoundResourceExtraData // class IResourceExtraData { public: virtual std::string VToString()=0; }; class SoundResourceExtraData : public IResourceExtraData { friend class WaveResourceLoader; friend class OggResourceLoader; public: SoundResourceExtraData(); virtual ~SoundResourceExtraData() { } virtual std::string VToString() { return "SoundResourceExtraData"; } enum SoundType GetSoundType() { return m_SoundType; } WAVEFORMATEX const *GetFormat() { return &m_WavFormatEx; } int GetLengthMilli() const { return m_LengthMilli; } protected: enum SoundType m_SoundType; // is this an Ogg, WAV, etc.? bool m_bInitialized; // has the sound been initialized WAVEFORMATEX m_WavFormatEx; // description of the PCM format int m_LengthMilli; // how long the sound is in milliseconds }; // // class WaveResourceLoader // class IResourceLoader { public: virtual std::string VGetPattern()=0; virtual bool VUseRawFile()=0; virtual bool VDiscardRawBufferAfterLoad()=0; virtual bool VAddNullZero() { return false; } virtual unsigned int VGetLoadedResourceSize(char *rawBuffer, unsigned int rawSize)=0; virtual bool VLoadResource(char *rawBuffer, unsigned int rawSize, shared_ptr<ResHandle> handle)=0; }; class WaveResourceLoader : public IResourceLoader { public: virtual bool VUseRawFile() { return false; } virtual bool VDiscardRawBufferAfterLoad() { return true; } virtual unsigned int VGetLoadedResourceSize(char *rawBuffer, unsigned int rawSize); virtual bool VLoadResource(char *rawBuffer, unsigned int rawSize, shared_ptr<ResHandle> handle); virtual std::string VGetPattern() { return "*.wav"; } protected: bool ParseWave(char *wavStream, size_t length, shared_ptr<ResHandle> handle); }; // // class OggResourceLoader // class OggResourceLoader : public IResourceLoader { public: virtual bool VUseRawFile() { return false; } virtual bool VDiscardRawBufferAfterLoad() { return true; } virtual unsigned int VGetLoadedResourceSize(char *rawBuffer, unsigned int rawSize); virtual bool VLoadResource(char *rawBuffer, unsigned int rawSize, shared_ptr<ResHandle> handle); virtual std::string VGetPattern() { return "*.ogg"; } protected: bool ParseOgg(char *oggStream, size_t length, shared_ptr<ResHandle> handle); };#include "GameCodeStd.h" #include <io.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <vorbis/codec.h> // from the vorbis sdk #include <vorbis/vorbisfile.h> // also :) #include "GameCode.h" #include "SoundResource.h" #include "Audio.h" // // SoundResource::SoundResource // SoundResourceExtraData::SoundResourceExtraData() : m_SoundType(SOUND_TYPE_UNKNOWN), m_bInitialized(false), m_LengthMilli(0) { // don't do anything yet - timing sound Initialization is important! } unsigned int WaveResourceLoader::VGetLoadedResourceSize(char *rawBuffer, unsigned int rawSize) { DWORD file = 0; DWORD fileEnd = 0; DWORD length = 0; DWORD type = 0; DWORD pos = 0; /** mmioFOURCC -- converts four chars into a 4 byte integer code. The first 4 bytes of a valid .wav file is 'R','I','F','F' */ #define mmioFOURCC(ch0, ch1, ch2, ch3) MAKEFOURCC(ch0, ch1, ch2, ch3) #define MAKEFOURCC(ch0, ch1, ch2, ch3) ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 )) type = *((DWORD *)(rawBuffer+pos)); pos+=sizeof(DWORD); bool b = type != mmioFOURCC('R', 'I', 'F', 'F'); if( b ) { return false; } length = *((DWORD *)(rawBuffer+pos)); pos+=sizeof(DWORD); type = *((DWORD *)(rawBuffer+pos)); pos+=sizeof(DWORD); // 'W','A','V','E' for a legal .wav file b = type != mmioFOURCC('W', 'A', 'V', 'E'); if( b ) return false; //not a WAV // Find the end of the file fileEnd = length - 4; bool copiedBuffer = false; // Load the .wav format and the .wav data // Note that these blocks can be in either order. while(file < fileEnd) { type = *((DWORD *)(rawBuffer+pos)); pos+=sizeof(DWORD); file += sizeof(DWORD); length = *((DWORD *)(rawBuffer+pos)); pos+=sizeof(DWORD); file += sizeof(DWORD); switch(type) { case mmioFOURCC('f', 'a', 'c', 't'): { GCC_ASSERT(false && "This wav file is compressed. We don't handle compressed wav at this time"); break; } case mmioFOURCC('f', 'm', 't', ' '): { pos+=length; break; } case mmioFOURCC('d', 'a', 't', 'a'): { return length; } } file += length; // Increment the pointer past the block we just read, // and make sure the pointer is word aliged. if (length & 1) { ++pos; ++file; } } // If we get to here, the .wav file didn't contain all the right pieces. return false; } bool WaveResourceLoader::VLoadResource(char *rawBuffer, unsigned int rawSize, shared_ptr<ResHandle> handle) { shared_ptr<SoundResourceExtraData> extra = shared_ptr<SoundResourceExtraData>(GCC_NEW SoundResourceExtraData()); extra->m_SoundType = SOUND_TYPE_WAVE; handle->SetExtra(shared_ptr<SoundResourceExtraData>(extra)); if (!ParseWave(rawBuffer, rawSize, handle)) { return false; } return true; } // // WaveResourceLoader::ParseWave // bool WaveResourceLoader::ParseWave(char *wavStream, size_t bufferLength, shared_ptr<ResHandle> handle) { shared_ptr<SoundResourceExtraData> extra = static_pointer_cast<SoundResourceExtraData>(handle->GetExtra()); DWORD file = 0; DWORD fileEnd = 0; DWORD length = 0; DWORD type = 0; DWORD pos = 0; // mmioFOURCC -- converts four chars into a 4 byte integer code. // The first 4 bytes of a valid .wav file is 'R','I','F','F' type = *((DWORD *)(wavStream+pos)); pos+=sizeof(DWORD); if(type != mmioFOURCC('R', 'I', 'F', 'F')) return false; length = *((DWORD *)(wavStream+pos)); pos+=sizeof(DWORD); type = *((DWORD *)(wavStream+pos)); pos+=sizeof(DWORD); // 'W','A','V','E' for a legal .wav file if(type != mmioFOURCC('W', 'A', 'V', 'E')) return false; //not a WAV // Find the end of the file fileEnd = length - 4; memset(&extra->m_WavFormatEx, 0, sizeof(WAVEFORMATEX)); bool copiedBuffer = false; // Load the .wav format and the .wav data // Note that these blocks can be in either order. while(file < fileEnd) { type = *((DWORD *)(wavStream+pos)); pos+=sizeof(DWORD); file += sizeof(DWORD); length = *((DWORD *)(wavStream+pos)); pos+=sizeof(DWORD); file += sizeof(DWORD); switch(type) { case mmioFOURCC('f', 'a', 'c', 't'): { GCC_ASSERT(false && "This wav file is compressed. We don't handle compressed wav at this time"); break; } case mmioFOURCC('f', 'm', 't', ' '): { memcpy(&extra->m_WavFormatEx, wavStream+pos, length); pos+=length; extra->m_WavFormatEx.cbSize = (WORD)length; break; } case mmioFOURCC('d', 'a', 't', 'a'): { copiedBuffer = true; if (length != handle->Size()) { GCC_ASSERT(0 && _T("Wav resource size does not equal the buffer size")); return 0; } memcpy(handle->WritableBuffer(), wavStream+pos, length); pos+=length; break; } } file += length; // If both blocks have been seen, we can return true. if( copiedBuffer ) { extra->m_LengthMilli = ( handle->Size() * 1000 ) / extra->GetFormat()->nAvgBytesPerSec; return true; } // Increment the pointer past the block we just read, // and make sure the pointer is word aliged. if (length & 1) { ++pos; ++file; } } // If we get to here, the .wav file didn't contain all the right pieces. return false; } // // struct OggMemoryFile // struct OggMemoryFile { unsigned char* dataPtr;// Pointer to the data in memory size_t dataSize; // Size of the data size_t dataRead; // Bytes read so far OggMemoryFile(void) { dataPtr = NULL; dataSize = 0; dataRead = 0; } }; // // VorbisRead 音频压缩格式读 // size_t VorbisRead(void* data_ptr, size_t byteSize, size_t sizeToRead, void* data_src) { OggMemoryFile *pVorbisData = http://www.mamicode.com/static_cast(data_src);>上面有的函数变量是基于Microsoft的框架。 3. 音效的不同效果包括爆炸、渐入渐出
#pragma once #include "Process.h" #include "Audio.h" #include "SoundResource.h" #include "ResCache.h" ///////////////////////////////////////////////////////////////////////////// // class SoundProcess // // A Sound Process, not to be confused with a Sound Resource (SoundResource) // manages a sound as it is being played. You can use this class to manage // timing between sounds & animations. // ///////////////////////////////////////////////////////////////////////////// class SoundProcess : public Process { protected: shared_ptr<ResHandle> m_handle; // this is the raw sound data shared_ptr<IAudioBuffer> m_AudioBuffer; // handle to the implementation dependent audio buffer (DSound, Miles) int m_Volume; // these hold the initial setting until the sound is actually launched. bool m_isLooping; public: SoundProcess(shared_ptr<ResHandle> soundResource, int volume=100, bool looping=false); virtual ~SoundProcess(); void Play(const int volume, const bool looping); void Stop(); void SetVolume(int volume); int GetVolume(); int GetLengthMilli(); bool IsSoundValid() { return m_handle != NULL; } bool IsPlaying(); bool IsLooping() { return m_AudioBuffer && m_AudioBuffer->VIsLooping(); } float GetProgress(); void PauseSound(void); protected: virtual void VOnInit(); virtual void VOnUpdate(unsigned long deltaMs); void InitializeVolume(); protected: SoundProcess(); // Disable Default Construction }; ///////////////////////////////////////////////////////////////////////////// // class ExplosionProcess // // This is an example of a process that uses a simple state machine // to control itself. // ///////////////////////////////////////////////////////////////////////////// class <span style="color:#ff0000;">ExplosionProcess</span> : public Process { protected: int m_Stage; shared_ptr<SoundProcess> m_Sound; public: ExplosionProcess() { m_Stage=0; } protected: virtual void VOnInit(); virtual void VOnUpdate(unsigned long deltaMs); }; ///////////////////////////////////////////////////////////////////////////// // class FadeProcess // // Fades sound volume in or out over time and then kills itself. // This should be useful for groups of sound effects, too - such as when // an AI barks and it must be heard above the other effects like too much freaking thunder. // ///////////////////////////////////////////////////////////////////////////// class <span style="color:#ff0000;">FadeProcess</span> : public Process { protected: shared_ptr<SoundProcess> m_Sound; int m_TotalFadeTime; int m_ElapsedTime; int m_StartVolume; int m_EndVolume; public: FadeProcess(shared_ptr<SoundProcess> sound, int fadeTime, int endVolume); virtual void VOnUpdate(unsigned long deltaMs); };//======================================================================== // SoundProcess.cpp : Defines sound processes. //======================================================================== #include "GameCodeStd.h" #include "GameCode.h" #include "Audio.h" #include "SoundProcess.h" #include "SoundResource.h" ////////////////////////////////////////////////////////////////////// // SoundProcess Implementation ////////////////////////////////////////////////////////////////////// // // SoundProcess::SoundProcess // SoundProcess::SoundProcess(shared_ptr<ResHandle> resource, int volume, bool looping) : m_handle(resource), m_Volume(volume), m_isLooping(looping) { InitializeVolume(); } // // SoundProcess::~SoundProcess // SoundProcess::~SoundProcess() { if (IsPlaying()) Stop(); if (m_AudioBuffer) g_pAudio->VReleaseAudioBuffer(m_AudioBuffer.get()); } void SoundProcess::InitializeVolume() { // FUTURE WORK: Somewhere set an adjusted volume based on game options // m_volume = g_GraphicalApp->GetVolume(typeOfSound); } // // SoundProcess::GetLengthMilli // int SoundProcess::GetLengthMilli() { if ( m_handle && m_handle->GetExtra()) { shared_ptr<SoundResourceExtraData> extra = static_pointer_cast<SoundResourceExtraData>(m_handle->GetExtra()); return extra->GetLengthMilli(); } else { return 0; } } // // SoundProcess::VOnInitialize // note that the book incorrectly names this SoundProcess::OnInitialize void SoundProcess::VOnInit() { Process::VOnInit(); //If the sound has never been... you know... then Play it for the very first time if ( m_handle == NULL || m_handle->GetExtra() == NULL) return; //This sound will manage it's own handle in the other thread IAudioBuffer *buffer = g_pAudio->VInitAudioBuffer(m_handle); if (!buffer) { Fail(); return; } m_AudioBuffer.reset(buffer); Play(m_Volume, m_isLooping); } // // SoundProcess::OnUpdate // void SoundProcess::VOnUpdate(unsigned long deltaMs) { if (!IsPlaying()) { Succeed(); } } // // SoundProcess::IsPlaying // bool SoundProcess::IsPlaying() { if ( ! m_handle || ! m_AudioBuffer ) return false; return m_AudioBuffer->VIsPlaying(); } // // SoundProcess::SetVolume // void SoundProcess::SetVolume(int volume) { if(m_AudioBuffer==NULL) { return; } GCC_ASSERT(volume>=0 && volume<=100 && "Volume must be a number between 0 and 100"); m_Volume = volume; m_AudioBuffer->VSetVolume(volume); } // // SoundProcess::GetVolume // int SoundProcess::GetVolume() { if(m_AudioBuffer==NULL) { return 0; } m_Volume = m_AudioBuffer->VGetVolume(); return m_Volume; } // // SoundProcess::PauseSound // NOTE: This is called TogglePause in te book // void SoundProcess::PauseSound() { if (m_AudioBuffer) m_AudioBuffer->VTogglePause(); } // // SoundProcess::Play // void SoundProcess::Play(const int volume, const bool looping) { GCC_ASSERT(volume>=0 && volume<=100 && "Volume must be a number between 0 and 100"); if(!m_AudioBuffer) { return; } m_AudioBuffer->VPlay(volume, looping); } // // SoundProcess::Stop // void SoundProcess::Stop() { if (m_AudioBuffer) { m_AudioBuffer->VStop(); } } // // SoundProcess::GetProgress // float SoundProcess::GetProgress() { if (m_AudioBuffer) { return m_AudioBuffer->VGetProgress(); } return 0.0f; } // // ExplosionProcess::VOnInit // void ExplosionProcess::VOnInit() { Process::VOnInit(); Resource resource("explosion.wav"); shared_ptr<ResHandle> srh = g_pApp->m_ResCache->GetHandle(&resource); m_Sound.reset(GCC_NEW SoundProcess(srh)); // Imagine cool explosion graphics setup code here!!!! // // // } // // ExplosionProcess::OnUpdate // void ExplosionProcess::VOnUpdate(unsigned long deltaMs) { // Since the sound is the real pacing mechanism - we ignore deltaMilliseconds float progress = m_Sound->GetProgress(); switch (m_Stage) { case 0: { if (progress > 0.55f) { ++m_Stage; // Imagine secondary explosion effect launch right here! } break; } case 1: { if (progress > 0.75f) { ++m_Stage; // Imagine tertiary explosion effect launch right here! } break; } default: { break; } } } // // FadeProcess::FadeProcess // FadeProcess::FadeProcess(shared_ptr<SoundProcess> sound, int fadeTime, int endVolume) { m_Sound = sound; m_TotalFadeTime = fadeTime; m_StartVolume = sound->GetVolume(); m_EndVolume = endVolume; m_ElapsedTime = 0; VOnUpdate(0); } // // FadeProcess::OnUpdate // void FadeProcess::VOnUpdate(unsigned long deltaMs) { m_ElapsedTime += deltaMs; if (m_Sound->IsDead()) Succeed(); float cooef = (float)m_ElapsedTime / m_TotalFadeTime; if (cooef>1.0f) cooef = 1.0f; if (cooef<0.0f) cooef = 0.0f; int newVolume = m_StartVolume + (int)( float(m_EndVolume - m_StartVolume) * cooef); if (m_ElapsedTime >= m_TotalFadeTime) { newVolume = m_EndVolume; Succeed(); } m_Sound->SetVolume(newVolume); }4. 基于DirectSound声卡的音效管理实现#pragma once //======================================================================== // DirectSoundAudio.h : Implements audio interfaces for DirectSound //======================================================================== #include "Audio.h" // DirectSound includes #include <dsound.h> #include <mmsystem.h> // DirectSoundAudioBuffer // // Implements the rest of the IAudioBuffer interface left out by AudioBuffer. // If you are interested in implementing a sound system using OpenAL // you'd create a class OpenALAudioBuffer from AudioBuffer. // typedef struct IDirectSoundBuffer *LPDIRECTSOUNDBUFFER; enum HRESULT { E_INVALIDARG, E_FAIL, S_OK, }; class DirectSoundAudioBuffer : public AudioBuffer { protected: LPDIRECTSOUNDBUFFER m_Sample; public: DirectSoundAudioBuffer(LPDIRECTSOUNDBUFFER sample, shared_ptr<ResHandle> resource); virtual void *VGet(); virtual bool VOnRestore(); virtual bool VPlay(int volume, bool looping); virtual bool VPause(); virtual bool VStop(); virtual bool VResume(); virtual bool VTogglePause(); virtual bool VIsPlaying(); virtual void VSetVolume(int volume); virtual void VSetPosition(unsigned long newPosition); virtual float VGetProgress(); private: HRESULT FillBufferWithSound( ); HRESULT RestoreBuffer( BOOL* pbWasRestored ); }; // class DirectSoundAudio // // Implements the rest of the IAudio interface left out by Audio. // If you are interested in implementing a sound system using OpenAL // you'd create a class OpenALAudioBuffer from AudioBuffer. // class DirectSoundAudio : public Audio { public: DirectSoundAudio() { m_pDS = NULL; } virtual bool VActive() { return m_pDS!=NULL; } virtual IAudioBuffer *VInitAudioBuffer(shared_ptr<ResHandle> handle); virtual void VReleaseAudioBuffer(IAudioBuffer* audioBuffer); virtual void VShutdown(); virtual bool VInitialize(HWND hWnd); protected: //参照 http://msdn.microsoft.com/en-us/library/windows/desktop/ee418035(v=vs.85).aspx IDirectSound8* m_pDS; HRESULT SetPrimaryBufferFormat( DWORD dwPrimaryChannels, DWORD dwPrimaryFreq, DWORD dwPrimaryBitRate ); };//===========================================================================// DirectSoundAudio.cpp : Defines a simple sound system that uses DirectSound //=========================================================================== #include "GameCodeStd.h" #include "GameCode.h" #include "SoundResource.h" #include "DirectSoundAudio.h" #include <cguid.h> // // DirectSoundAudio::VInitialize // bool DirectSoundAudio::VInitialize(HWND hWnd) { if(m_Initialized) return true; m_Initialized=false; m_AllSamples.clear(); SAFE_RELEASE( m_pDS ); HRESULT hr; // Create IDirectSound using the primary sound device if( FAILED( hr = DirectSoundCreate8( NULL, &m_pDS, NULL ) ) ) return false; // Set DirectSound coop level if( FAILED( hr = m_pDS->SetCooperativeLevel( hWnd, DSSCL_PRIORITY) ) ) return false; if( FAILED( hr = SetPrimaryBufferFormat( 8, 44100, 16 ) ) ) return false; m_Initialized = true; return true; } // // DirectSoundAudio::SetPrimaryBufferFormat // HRESULT DirectSoundAudio::SetPrimaryBufferFormat( DWORD dwPrimaryChannels, DWORD dwPrimaryFreq, DWORD dwPrimaryBitRate ) { // !WARNING! - Setting the primary buffer format and then using this // it for DirectMusic messes up DirectMusic! // // If you want your primary buffer format to be 22kHz stereo, 16-bit // call with these parameters: SetPrimaryBufferFormat(2, 22050, 16); HRESULT hr; LPDIRECTSOUNDBUFFER pDSBPrimary = NULL; if( ! m_pDS ) return CO_E_NOTINITIALIZED; // Get the primary buffer DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwBufferBytes = 0; dsbd.lpwfxFormat = NULL; if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) ) return DXUT_ERR( L"CreateSoundBuffer", hr ); WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); wfx.wFormatTag = (WORD) WAVE_FORMAT_PCM; wfx.nChannels = (WORD) dwPrimaryChannels; wfx.nSamplesPerSec = (DWORD) dwPrimaryFreq; wfx.wBitsPerSample = (WORD) dwPrimaryBitRate; wfx.nBlockAlign = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels); wfx.nAvgBytesPerSec = (DWORD) (wfx.nSamplesPerSec * wfx.nBlockAlign); if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) ) return DXUT_ERR( L"SetFormat", hr ); SAFE_RELEASE( pDSBPrimary ); return S_OK; } // // DirectSoundAudio::VShutdown // void DirectSoundAudio::VShutdown() { if(m_Initialized) { Audio::VShutdown(); SAFE_RELEASE(m_pDS); m_Initialized = false; } } // // DirectSoundAudio::VInitAudioBuffer // Allocate a sample handle for the newborn sound (used by SoundResource) and tell you it's length // IAudioBuffer *DirectSoundAudio::VInitAudioBuffer(shared_ptr<ResHandle> resHandle)//const { shared_ptr<SoundResourceExtraData> extra = static_pointer_cast<SoundResourceExtraData>(resHandle->GetExtra()); if( ! m_pDS ) return NULL; switch(extra->GetSoundType()) { case SOUND_TYPE_OGG: case SOUND_TYPE_WAVE: // We support WAVs and OGGs break; case SOUND_TYPE_MP3: case SOUND_TYPE_MIDI: //If it's a midi file, then do nothin at this time... maybe we will support this in the future GCC_ASSERT(false && "MP3s and MIDI are not supported"); return NULL; break; default: GCC_ASSERT(false && "Unknown sound type"); return NULL; }//end switch LPDIRECTSOUNDBUFFER sampleHandle; // Create the direct sound buffer, and only request the flags needed // since each requires some overhead and limits if the buffer can // be hardware accelerated DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_CTRLVOLUME; dsbd.dwBufferBytes = resHandle->Size(); dsbd.guid3DAlgorithm = GUID_NULL; dsbd.lpwfxFormat = const_cast<WAVEFORMATEX *>(extra->GetFormat()); HRESULT hr; if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &sampleHandle, NULL ) ) ) { return NULL; } // Add handle to the list IAudioBuffer *audioBuffer = GCC_NEW DirectSoundAudioBuffer(sampleHandle, resHandle); m_AllSamples.push_front( audioBuffer); return audioBuffer; } // // DirectSoundAudio::VReleaseAudioBuffer // Allocate a sample handle for the newborn sound (used by SoundResource) // void DirectSoundAudio::VReleaseAudioBuffer(IAudioBuffer *sampleHandle)//const { sampleHandle->VStop(); m_AllSamples.remove(sampleHandle); } // // DirectSoundAudioBuffer::DirectSoundAudioBuffer // DirectSoundAudioBuffer::DirectSoundAudioBuffer( LPDIRECTSOUNDBUFFER sample, shared_ptr<ResHandle> resource) : AudioBuffer(resource) { m_Sample = sample; FillBufferWithSound(); } // // DirectSoundAudioBuffer::VGet // void *DirectSoundAudioBuffer::VGet() { if (!VOnRestore()) return NULL; return m_Sample; } // // DirectSoundAudioBuffer::VPlay // Play a sound // bool DirectSoundAudioBuffer::VPlay(int volume, bool looping) { if(!g_pAudio->VActive()) return false; VStop(); m_Volume = volume; m_isLooping = looping; LPDIRECTSOUNDBUFFER pDSB = (LPDIRECTSOUNDBUFFER)VGet(); if (!pDSB) return false; pDSB->SetVolume( volume ); DWORD dwFlags = looping ? DSBPLAY_LOOPING : 0L; return (S_OK==pDSB->Play( 0, 0, dwFlags ) ); }//end Play // // DirectSoundAudioBuffer::VStop - // Stop a sound and rewind play position to the beginning. // bool DirectSoundAudioBuffer::VStop() { if(!g_pAudio->VActive()) return false; LPDIRECTSOUNDBUFFER pDSB = (LPDIRECTSOUNDBUFFER)VGet(); if( ! pDSB ) return false; m_isPaused=true; pDSB->Stop(); return true; } // // DirectSoundAudioBuffer::VPause // Pause a sound // bool DirectSoundAudioBuffer::VPause() { LPDIRECTSOUNDBUFFER pDSB = (LPDIRECTSOUNDBUFFER)VGet(); if(!g_pAudio->VActive()) return false; if( pDSB ) return false; m_isPaused=true; pDSB->Stop(); pDSB->SetCurrentPosition(0); // rewinds buffer to beginning. return true; } // // DirectSoundAudioBuffer::VResume // Resume a sound bool DirectSoundAudioBuffer::VResume() { m_isPaused=false; return VPlay(VGetVolume(), VIsLooping()); } // // DirectSoundAudioBuffer::VTogglePause // Pause a sound or Resume a Paused sound // bool DirectSoundAudioBuffer::VTogglePause() { if(!g_pAudio->VActive()) return false; if(m_isPaused) { VResume(); } else { VPause(); // note that the book code calls VStop(). // It's better to call VPause() instead. } return true; }//end TogglePause // // DirectSoundAudioBuffer::VIsPlaying // bool DirectSoundAudioBuffer::VIsPlaying() { if(!g_pAudio->VActive()) return false; DWORD dwStatus = 0; LPDIRECTSOUNDBUFFER pDSB = (LPDIRECTSOUNDBUFFER)VGet(); pDSB->GetStatus( &dwStatus ); bool bIsPlaying = ( ( dwStatus & DSBSTATUS_PLAYING ) != 0 ); return bIsPlaying; } // // DirectSoundAudioBuffer::VSetVolume // void DirectSoundAudioBuffer::VSetVolume(int volume) { // DSBVOLUME_MIN, defined in dsound.h is set to as -10000, which is just way too silent for a // lower bound and many programmers find -5000 to be a better minimum bound for the volume // range to avoid an annoying silence for the lower 50% of a volume slider that uses a logarithmic scale. // This was contributed by BystanderKain! int gccDSBVolumeMin = DSBVOLUME_MIN; if(!g_pAudio->VActive()) return; LPDIRECTSOUNDBUFFER pDSB = (LPDIRECTSOUNDBUFFER)VGet(); GCC_ASSERT(volume>=0 && volume<=100 && "Volume must be a number between 0 and 100"); // convert volume from 0-100 into range for DirectX - don't forget to use a logarithmic scale! float coeff = (float)volume / 100.0f; float logarithmicProportion = coeff >0.1f ? 1+log10(coeff) : 0; float range = float(DSBVOLUME_MAX - gccDSBVolumeMin); float fvolume = ( range * logarithmicProportion ) + gccDSBVolumeMin; GCC_ASSERT(fvolume>=gccDSBVolumeMin && fvolume<=DSBVOLUME_MAX); HRESULT hr = pDSB->SetVolume( LONG(fvolume) ); GCC_ASSERT(hr==S_OK); } void DirectSoundAudioBuffer::VSetPosition(unsigned long newPosition) { m_Sample->SetCurrentPosition(newPosition); } // // DirectSoundAudioBuffer::VOnRestore // NOTE: Renamed from DirectSoundAudioBuffer::VRestore in the book bool DirectSoundAudioBuffer::VOnRestore() { HRESULT hr; BOOL bRestored; // Restore the buffer if it was lost if( FAILED( hr = RestoreBuffer( &bRestored ) ) ) return NULL; if( bRestored ) { // The buffer was restored, so we need to fill it with new data if( FAILED( hr = FillBufferWithSound( ) ) ) return NULL; } return true; } // // DirectSoundAudioBuffer::RestoreBuffer // // Restores the lost buffer. *pbWasRestored returns TRUE if the buffer was // restored. It can also NULL if the information is not needed. // HRESULT DirectSoundAudioBuffer::RestoreBuffer( BOOL* pbWasRestored ) { HRESULT hr; if( ! m_Sample ) return CO_E_NOTINITIALIZED; if( pbWasRestored ) *pbWasRestored = FALSE; DWORD dwStatus; if( FAILED( hr = m_Sample->GetStatus( &dwStatus ) ) ) return DXUT_ERR( L"GetStatus", hr ); if( dwStatus & DSBSTATUS_BUFFERLOST ) { // Since the app could have just been activated, then // DirectSound may not be giving us control yet, so // the restoring the buffer may fail. // If it does, sleep until DirectSound gives us control but fail if // if it goes on for more than 1 second int count = 0; do { hr = m_Sample->Restore(); if( hr == DSERR_BUFFERLOST ) Sleep( 10 ); } while( ( hr = m_Sample->Restore() ) == DSERR_BUFFERLOST && ++count < 100 ); if( pbWasRestored != NULL ) *pbWasRestored = TRUE; return S_OK; } else { return S_FALSE; } } // // DirectSoundAudioBuffer::FillBufferWithSound // HRESULT DirectSoundAudioBuffer::FillBufferWithSound( void ) { HRESULT hr; VOID *pDSLockedBuffer = NULL; // a pointer to the DirectSound buffer DWORD dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer DWORD dwWavDataRead = 0; // Amount of data read from the wav file if( ! m_Sample ) return CO_E_NOTINITIALIZED; // Make sure we have focus, and we didn't just switch in from // an app which had a DirectSound device if( FAILED( hr = RestoreBuffer( NULL ) ) ) return DXUT_ERR( L"RestoreBuffer", hr ); int pcmBufferSize = m_Resource->Size(); shared_ptr<SoundResourceExtraData> extra = static_pointer_cast<SoundResourceExtraData>(m_Resource->GetExtra()); // Lock the buffer down if( FAILED( hr = m_Sample->Lock( 0, pcmBufferSize, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, NULL, 0L ) ) ) return DXUT_ERR( L"Lock", hr ); if( pcmBufferSize == 0 ) { // Wav is blank, so just fill with silence FillMemory( (BYTE*) pDSLockedBuffer, dwDSLockedBufferSize, (BYTE)(extra->GetFormat()->wBitsPerSample == 8 ? 128 : 0 ) ); } else { CopyMemory(pDSLockedBuffer, m_Resource->Buffer(), pcmBufferSize); if( pcmBufferSize < (int)dwDSLockedBufferSize ) { // If the buffer sizes are different fill in the rest with silence FillMemory( (BYTE*) pDSLockedBuffer + pcmBufferSize, dwDSLockedBufferSize - pcmBufferSize, (BYTE)(extra->GetFormat()->wBitsPerSample == 8 ? 128 : 0 ) ); } } // Unlock the buffer, we don't need it anymore. m_Sample->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 ); return S_OK; } // // DirectSoundAudioBuffer::VGetProgress // float DirectSoundAudioBuffer::VGetProgress() { LPDIRECTSOUNDBUFFER pDSB = (LPDIRECTSOUNDBUFFER)VGet(); DWORD progress = 0; pDSB->GetCurrentPosition(&progress, NULL); float length = (float)m_Resource->Size(); return (float)progress / length; }以上实现的音效管理方法,都是基于DirectSound硬件。在Mac上不行,Mac基于OpenAL。上面是音效系统管理的代码实现了,包括音效的各种控制。下一篇是关于游戏中的2D图像实现~~~~
游戏架构其七:音效管理
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。