#include "StreamBuffer.h" #include "SoundFile.h" #include #include extern bool g_b3DSound; ///////////////////////////////////////////////////////////////////////////////////////// // CStreamBuffer::CStreamBuffer() : m_dwLastPlayPos( 0 ) , m_dwPlayProgress( 0 ) , m_dwNotifySize( 0 ) , m_dwNextWriteOffset( 0 ) , m_hNotificationEvent( NULL ) , m_bFillNextNotificationWithSilence( false ) { } ///////////////////////////////////////////////////////////////////////////////////////// // CStreamBuffer::CStreamBuffer( IDirectSound8 * pDSound, ISoundFile * pSoundFile, bool b3DSound, DWORD dwNumBuffers ) : m_dwLastPlayPos( 0 ) , m_dwPlayProgress( 0 ) , m_dwNotifySize( 0 ) , m_dwNextWriteOffset( 0 ) , m_hNotificationEvent( NULL ) , m_bFillNextNotificationWithSilence( false ) , m_bLoopPlay( false ) { Create( pDSound, pSoundFile, b3DSound, dwNumBuffers, 0 ); } ///////////////////////////////////////////////////////////////////////////////////////// // CStreamBuffer::CStreamBuffer( IDirectSound8 * pDSound, const char * szFilename, bool b3DSound, DWORD dwNumBuffers ) : m_dwLastPlayPos( 0 ) , m_dwPlayProgress( 0 ) , m_dwNotifySize( 0 ) , m_dwNextWriteOffset( 0 ) , m_hNotificationEvent( NULL ) , m_bFillNextNotificationWithSilence( false ) , m_bLoopPlay( false ) { Create( pDSound, szFilename, b3DSound, dwNumBuffers, 0 ); } ///////////////////////////////////////////////////////////////////////////////////////// // CStreamBuffer::~CStreamBuffer() { Destroy(); } ///////////////////////////////////////////////////////////////////////////////////////// // //( DWORD dwNotifyCount, DWORD dwNotifySize, HANDLE hNotifyEvent ) void CStreamBuffer::Create( IDirectSound8 * pDSound, ISoundFile * pSoundFile, bool b3DSound, DWORD dwNumBuffers, DWORD dwAddFlag ) { Destroy(); if( g_b3DSound == false ) b3DSound = false; m_hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); m_pSoundFile = pSoundFile; assert( pDSound != NULL && m_pSoundFile != NULL ); WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); wfx.nChannels = m_pSoundFile->GetChannelCount(); wfx.nSamplesPerSec = m_pSoundFile->GetSamplePerSec(); wfx.wBitsPerSample = m_pSoundFile->GetBitsPerSample(); if( b3DSound ) { if( wfx.nChannels != 1 ) //À̰ÍÀÌ Stereo(2)·Î µÇ¾î ÀÖÀ¸¸é DSBCAPS_CTRL3D¸¦ ¼¼ÆÃÇÒ ¼ö ¾øÀ½ { MessageBox( NULL, "3D »ç¿îµå¸¦ »ç¿ëÇϱâ À§Çؼ­´Â »ç¿îµå È­ÀÏÀÌ mono·Î ¼³Á¤ÀÌ µÇ¾î ÀÖ¾î¾ß ÇÕ´Ï´Ù. ±×·¸Áö ¾ÊÀ¸¸é »ç¿îµå°¡ ÀÌ»óÇÏ°Ô Ç÷¹ÀÌ µÉ °ÍÀÔ´Ï´Ù.", pSoundFile->GetFilename(), MB_OK ); wfx.nChannels = 1; } } wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nBlockAlign = ( wfx.wBitsPerSample * wfx.nChannels ) / 8; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; DWORD dwNotifyCount = 32; DWORD dwTotalSize = wfx.nSamplesPerSec * 3 * wfx.nBlockAlign; //3ÃÊ µ¿¾ÈÀÇ »ç¿îµå¸¦ ´ãÀ» ¼ö ÀÖµµ·Ï ¹öÆÛÀÇ Å©±â ¼³Á¤ m_dwNotifySize = dwTotalSize / dwNotifyCount; m_dwNotifySize -= m_dwNotifySize % wfx.nBlockAlign; m_dwDSBufferSize = m_dwNotifySize * dwNotifyCount; // Set up the direct sound buffer. Request the NOTIFY flag, so // that we are notified as the sound buffer plays. Note, that using this flag // may limit the amount of hardware acceleration that can occur. DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | dwAddFlag; if( b3DSound ) dsbd.dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE; dsbd.dwBufferBytes = m_dwDSBufferSize; dsbd.guid3DAlgorithm = GUID_NULL; dsbd.lpwfxFormat = &wfx; LPDIRECTSOUNDBUFFER tempBuffer = 0; HRESULT hr = pDSound->CreateSoundBuffer( &dsbd, &tempBuffer, NULL ); if( FAILED( hr ) ) { return; /* if( hr == E_INVALIDARG ) { SNDError( "Create Streaming Buffer Failed!! ErrorCode : 0x%x, ExtraInfo : %d, %d, %d, %d, %d, %d, %d, SoundFileName : %s " , hr, wfx.nChannels, wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nBlockAlign , wfx.nAvgBytesPerSec, dsbd.dwFlags, dsbd.dwBufferBytes, pSoundFile->GetFilename() ); } else SNDError( "Create Streaming Buffer Failed!! ErrorCode : 0x%x", hr ); */ } hr = tempBuffer->QueryInterface( IID_IDirectSoundBuffer8, (void**)&m_pDSBuffer8 ); ULONG refCount = tempBuffer->Release(); if( FAILED( hr ) ) { SNDError( "QueryInterface Failed!! (at CStreamBuffer::Create)... ErrorCode : 0x%x", hr ); } m_dwNumBuffers = dwNumBuffers; m_apDSBuffer = new LPDIRECTSOUNDBUFFER[m_dwNumBuffers]; ZeroMemory( m_apDSBuffer, sizeof(LPDIRECTSOUNDBUFFER) * m_dwNumBuffers ); for( DWORD i = 0; i < m_dwNumBuffers; i++ ) { if( FAILED( hr = pDSound->DuplicateSoundBuffer( m_pDSBuffer8, &m_apDSBuffer[i] ) ) ) { delete [] m_apDSBuffer; SNDError( "Duplicate SoundBuffer Failed!! ErrorCode : 0x%x", hr ); } } LPDIRECTSOUNDNOTIFY pDSNotify = NULL; // Create the notification events, so that we know when to fill // the buffer as the sound plays. if( FAILED( hr = m_apDSBuffer[0]->QueryInterface( IID_IDirectSoundNotify, (VOID**)&pDSNotify ) ) ) SNDError( "Query DirectSoundNotify Object Failed!! ErrorCode : 0x%x", hr ); DSBPOSITIONNOTIFY* aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ]; for( i = 0; i < dwNotifyCount; i++ ) { aPosNotify[i].dwOffset = (m_dwNotifySize * i) + m_dwNotifySize - 1; aPosNotify[i].hEventNotify = m_hNotificationEvent; #ifdef DEBUG_LOG log_file << "[NotificationPositions:" << m_pSoundFile->GetFilename() << "] Offset: " << aPosNotify[i].dwOffset << endl; #endif } // Tell DirectSound when to notify us. The notification will come in the from // of signaled events that are handled in WinMain() if( FAILED( hr = pDSNotify->SetNotificationPositions( dwNotifyCount, aPosNotify ) ) ) { pDSNotify->Release(); delete [] aPosNotify; SNDError( "SetNotificationPositions Failed!! ErrorCode : 0x%x", hr ); } if( pDSNotify ) pDSNotify->Release(); delete [] aPosNotify; if( b3DSound ) { m_ap3DBuffer = new IDirectSound3DBuffer*[ dwNumBuffers ]; for( i = 0; i < dwNumBuffers; i++ ) { m_apDSBuffer[i]->SetCurrentPosition(0); m_ap3DBuffer[i] = Get3DBufferInterface( i ); } } FillBufferWithSound( m_apDSBuffer[0] ); m_dwLastPlayPos = 0; m_dwPlayProgress = 0; m_dwNextWriteOffset = 0; m_bFillNextNotificationWithSilence = false; #ifdef DEBUG_LOG if( log_file.is_open() && m_pSoundFile != NULL ) log_file << "StreamBuffer Created! (" << m_pSoundFile->GetFilename() << ")\n"; #endif } ///////////////////////////////////////////////////////////////////////////////////////// // void CStreamBuffer::Create( IDirectSound8 * pDSound, const char * szFilename, bool b3DSound, DWORD dwNumBuffers, DWORD dwAddFlag ) { if( !IsFileExist2( szFilename ) ) { SNDError( "%s : È­ÀÏÀ» ¿­ ¼ö ¾ø½À´Ï´Ù.", szFilename ); } ISoundFile * pSoundFile = ISoundFile::CreateSoundFileInstance( szFilename ); Create( pDSound, pSoundFile, b3DSound, dwNumBuffers, dwAddFlag ); } ///////////////////////////////////////////////////////////////////////////////////////// // void CStreamBuffer::Destroy() { CSoundBuffer::Destroy(); if( m_hNotificationEvent ) { CloseHandle( m_hNotificationEvent ); m_hNotificationEvent = NULL; } } ///////////////////////////////////////////////////////////////////////////////////////// // int CStreamBuffer::Play( bool bLoopPlay ) { m_bLoopPlay = bLoopPlay; unsigned index = CSoundBuffer::Play( true ); if( index >= m_dwNumBuffers ) return index; DWORD Pos; m_apDSBuffer[0]->GetCurrentPosition( &Pos, NULL ); m_apDSBuffer[index]->SetCurrentPosition( Pos ); return index; } void CStreamBuffer::Play( DWORD dwIndex, bool bLoopPlay ) { if( dwIndex >= m_dwNumBuffers ) return; m_bLoopPlay = bLoopPlay; CSoundBuffer::Play( dwIndex, true ); DWORD Pos; m_apDSBuffer[dwIndex]->GetCurrentPosition( &Pos, NULL ); m_apDSBuffer[dwIndex]->SetCurrentPosition( Pos ); } ///////////////////////////////////////////////////////////////////////////////////////// // void CStreamBuffer::Reset( unsigned bufIndex ) { ResetAll(); } ///////////////////////////////////////////////////////////////////////////////////////// // void CStreamBuffer::ResetAll() { assert( m_apDSBuffer[0] != NULL && m_pSoundFile != NULL ); m_dwLastPlayPos = 0; m_dwPlayProgress = 0; m_dwNextWriteOffset = 0; m_bFillNextNotificationWithSilence = false; if( Restore( m_apDSBuffer[0] ) ) { FillBufferWithSound( m_apDSBuffer[0] ); } m_pSoundFile->Reset(); HRESULT hr = m_apDSBuffer[0]->SetCurrentPosition( 0L ); if( FAILED( hr ) ) SNDError( "SetCurrentPosition(at CStreamBuffer::Reset) Failed!! ErrorCode : 0x%x", hr ); } ///////////////////////////////////////////////////////////////////////////////////////// // void CStreamBuffer::HandleNotification() { assert( m_apDSBuffer != NULL && m_pSoundFile != NULL ); //PlayµÇ°í ÀÖÁö ¾ÊÀº °ÍÀº ½ºÅµÇÑ´Ù. DWORD dwStatus = 0; m_apDSBuffer[0]->GetStatus( &dwStatus ); if( ( dwStatus & DSBSTATUS_PLAYING ) == 0 ) return; if( Restore( m_apDSBuffer[0] ) ) { FillBufferWithSound( m_apDSBuffer[0] ); return; } DWORD dwDSLockedBufferSize, dwDSLockedBufferSize2; void *pDSLockedBuffer = NULL, *pDSLockedBuffer2 = NULL; #ifdef DEBUG_LOG DWORD dwPos; m_apDSBuffer[0]->GetCurrentPosition( &dwPos, NULL ); char szBuf[1024]; sprintf( szBuf, "[HandleNotification:%s] NextWriteOffset : %u, Pos : %u, LastPlayPos : %u, NotifySize : %u\n", m_pSoundFile->GetFilename(), m_dwNextWriteOffset, dwPos, m_dwLastPlayPos, m_dwNotifySize ); log_file << szBuf; #endif HRESULT hr = m_apDSBuffer[0]->Lock( m_dwNextWriteOffset, m_dwNotifySize, &pDSLockedBuffer, &dwDSLockedBufferSize, &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L ); if( FAILED( hr ) ) SNDError( "Stream SoundBuffer Lock Failed!! ErrorCode : 0x%x", hr ); if( pDSLockedBuffer2 != NULL ) SNDError( "Stream SoundBuffer Lock Failed2!! ErrorCode : 0x%x", E_UNEXPECTED ); DWORD dwBytesWrittenToBuffer = 0; DWORD dwBitsPerSample = m_pSoundFile->GetBitsPerSample(); if( m_bFillNextNotificationWithSilence ) { FillMemory( pDSLockedBuffer, dwDSLockedBufferSize, (BYTE)( dwBitsPerSample == 8 ? 128 : 0 ) ); dwBytesWrittenToBuffer = dwDSLockedBufferSize; } else { dwBytesWrittenToBuffer = m_pSoundFile->Read( (BYTE*) pDSLockedBuffer, dwDSLockedBufferSize ); } if( dwBytesWrittenToBuffer < dwDSLockedBufferSize ) { if( m_bLoopPlay ) { //¹Ýº¹À» ÇØ¾ß Çϱ⠶§¹®¿¡ ³ª¸ÓÁö ¹öÆÛ¸¦ ¿þÀÌºê µ¥ÀÌÅÍ·Î °è¼Ó ä¿î´Ù. DWORD dwReadSoFar = dwBytesWrittenToBuffer; while( dwReadSoFar < dwDSLockedBufferSize ) { //¸Å¿ì ªÀº È­ÀÏÀÏ °æ¿ì ¹öÆÛ°¡ Âû¶§±îÁö ¹Ýº¹Çϸç ÀоîµéÀδÙ. m_pSoundFile->Reset(); dwBytesWrittenToBuffer = m_pSoundFile->Read( (BYTE*)pDSLockedBuffer + dwReadSoFar, dwDSLockedBufferSize - dwReadSoFar ); dwReadSoFar += dwBytesWrittenToBuffer; } } else { //³ª¸ÓÁö¸¦ ¹«À½(silence)À¸·Î ä¿î´Ù. FillMemory( (BYTE*) pDSLockedBuffer + dwBytesWrittenToBuffer, dwDSLockedBufferSize - dwBytesWrittenToBuffer, (BYTE)( dwBitsPerSample == 8 ? 128 : 0 ) ); //ÀÌ ÀÌÈÄ¿¡´Â ÀüºÎ ¹«À½À¸·Î ó¸® m_bFillNextNotificationWithSilence = true; } } m_apDSBuffer[0]->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 ); //ÀÌÁ¦±îÁö ¾ó¸¶³ª Ç÷¹ÀÌ µÇ¾ú´ÂÁö º¸°í, Ç÷¹ÀÌ ÁöÁ¡ÀÌ È­ÀÏÀÇ ³¡À» Áö³µÀ» ¶§ //m_bLoopPlay Ç÷¡±×¿¡ µû¶ó ¹öÆÛ¸¦ ¹«À½À¸·Î ä¿ì´øÁö È­ÀÏÀÇ Ã³À½ºÎÅÍ ´Ù½Ã Àо //º¹»ç¸¦ ÇÏ´øÁö ÇÑ´Ù. DWORD dwCurrentPlayPos; DWORD dwPlayDelta; if( FAILED( hr = m_apDSBuffer[0]->GetCurrentPosition( &dwCurrentPlayPos, NULL ) ) ) SNDError( "GetCurrentPosition(at CStreamBuffer::HandleNotification) Failed!! ErrorCode : 0x%x", hr ); // Check to see if the position counter looped if( dwCurrentPlayPos < m_dwLastPlayPos ) dwPlayDelta = ( m_dwDSBufferSize - m_dwLastPlayPos ) + dwCurrentPlayPos; else dwPlayDelta = dwCurrentPlayPos - m_dwLastPlayPos; m_dwPlayProgress += dwPlayDelta; m_dwLastPlayPos = dwCurrentPlayPos; if( m_bFillNextNotificationWithSilence ) { //³¡¿¡ µµ´ÞÇϸé stopÇÑ´Ù. if( m_dwPlayProgress >= m_pSoundFile->GetSize() ) { m_apDSBuffer[0]->Stop(); ResetAll(); } } m_dwNextWriteOffset += dwDSLockedBufferSize; m_dwNextWriteOffset %= m_dwDSBufferSize; //¹öÆÛ°¡ ¼øÈ¯Çϵµ·Ï.. if( m_dwDSBufferSize - m_dwNotifySize < m_dwNextWriteOffset ) SNDError( "WriteOffset°ªÀÌ À߸ø °è»ê µÇ¾ú½À´Ï´Ù.(CStreamBuffer::HandleNotification)" ); } /////////////////////////////////////////////////////////////////////////////////////////