#include "DMusic_OggLoader.h" #include "OggFile.h" #include #include #include #include #ifndef RELEASE #define RELEASE(x) { if(x) (x)->Release(); x = NULL; } #endif /////////////////////////////////////////////////////////////////////////////////////// // struct SWaveHeader { char ChunkID[4]; DWORD ChunkSize; char Format[4]; char SubChunk1ID[4]; DWORD SubChunk1Size; WORD AudioFormat; WORD NumChannels; DWORD SampleRate; DWORD ByteRate; WORD BlockAlign; WORD BitsPerSample; char SubChunk2ID[4]; DWORD SubChunk2Size; }; /////////////////////////////////////////////////////////////////////////////////////// // COggStream::COggStream() : m_cRef( 1 ) , m_pLoader( NULL ) , m_pOggFile( new COGGFile ) , m_pWaveHeader( new SWaveHeader ) , m_TotalWaveBytes( 0 ) , m_SeekPos( -sizeof( SWaveHeader ) ) { } /////////////////////////////////////////////////////////////////////////////////////// // COggStream::~COggStream() { Detach(); delete m_pOggFile; delete m_pWaveHeader; } /////////////////////////////////////////////////////////////////////////////////////// // HRESULT COggStream::Attach( LPCTSTR tzFile, IDirectMusicLoader * pLoader ) { Detach(); m_pOggFile->Create( tzFile ); m_pLoader = pLoader; pLoader->AddRef(); //Wave Çì´õ¸¦ ä¿ö³ÖÀ½. m_TotalWaveBytes = m_pOggFile->SeekWaveData( 0, ISoundFile::seek_end ); m_pOggFile->SeekWaveData( 0, ISoundFile::seek_begin ); memcpy( m_pWaveHeader->ChunkID, "RIFF", 4 ); //null ¹®ÀÚ¸¦ ³ÖÁö ¾Ê±â À§ÇØ strcpy ´ë½Å memcpy·Î º¹»çÇÔ. m_pWaveHeader->ChunkSize = m_TotalWaveBytes + 36; //36 == sizeof( SWaveHeader ) - sizeof( m_pWaveHeader->m_ChunkID ) - sizeof( m_pWaveHeader->m_ChunkSize ) memcpy( m_pWaveHeader->Format, "WAVE", 4 ); memcpy( m_pWaveHeader->SubChunk1ID, "fmt ", 4 ); m_pWaveHeader->SubChunk1Size = 16; m_pWaveHeader->AudioFormat = WAVE_FORMAT_PCM; m_pWaveHeader->NumChannels = m_pOggFile->GetChannelCount(); m_pWaveHeader->SampleRate = m_pOggFile->GetSamplePerSec(); m_pWaveHeader->BlockAlign = m_pOggFile->GetBitsPerSample() * m_pOggFile->GetChannelCount() / 8; m_pWaveHeader->ByteRate = m_pWaveHeader->BlockAlign * m_pWaveHeader->SampleRate; m_pWaveHeader->BitsPerSample = m_pOggFile->GetBitsPerSample(); memcpy( m_pWaveHeader->SubChunk2ID, "data", 4 ); m_pWaveHeader->SubChunk2Size = m_TotalWaveBytes; m_SeekPos = -sizeof( SWaveHeader ); return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// // void COggStream::Detach() { m_pOggFile->Destroy(); RELEASE( m_pLoader ); } /////////////////////////////////////////////////////////////////////////////////////// // MUSIC_TIME COggStream::GetMusicTime() { DWORD cbSamples = m_TotalWaveBytes / ( m_pWaveHeader->NumChannels * ( m_pWaveHeader->BitsPerSample / 8 ) ); // At 120 bpm (2 beats per second) and 768 music-time ticks per beat, // we have 1536 ticks per second. // return (cbSamples * 2 * DMUS_PPQ) / m_pWaveHeader->SampleRate; } /////////////////////////////////////////////////////////////////////////////////////// // //IUnknown methods STDMETHODIMP COggStream::QueryInterface( REFIID iid, void ** ppv ) { *ppv = NULL; if (iid == IID_IUnknown || iid == IID_ISequentialStream) { // ISequentialStream is the base class for IStream, which // provides only sequential file I/O. // *ppv = (void*)static_cast(this); } else if (iid == IID_IStream) { // IStream adds the concept of a file pointer to the // sequential stream. // *ppv = (void*)static_cast(this); } else if (iid == IID_IDirectMusicGetLoader) { // This is a DirectMusic specific interface to get back // the loader that created this IStream. // *ppv = (void*)static_cast(this); } else { return E_NOINTERFACE; } AddRef(); return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP_(ULONG) COggStream::AddRef() { return InterlockedIncrement( &m_cRef ); } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP_(ULONG) COggStream::Release() { if( InterlockedDecrement( &m_cRef ) == 0 ) { delete this; return 0; } return m_cRef; } /////////////////////////////////////////////////////////////////////////////////////// // //IDirectMusicGetLoader STDMETHODIMP COggStream::GetLoader( IDirectMusicLoader ** ppLoader ) { m_pLoader->AddRef(); *ppLoader = m_pLoader; return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// // //IStream methods STDMETHODIMP COggStream::Read( void * pv, ULONG cb, ULONG * pcb ) { if( m_pLoader == NULL ) return E_FAIL; if( m_SeekPos < 0 ) { char * pWaveHeader = (char*)m_pWaveHeader; if( m_SeekPos + long(cb) >= 0 ) { //WaveHeader¿Í Ogg µ¥ÀÌÅÍ »çÀÌ¿¡ m_SeekPos°¡ ³¢¾î ÀÖÀ¸¹Ç·Î.... //WaveHeaderÀÇ µ¥ÀÌÅ͸¦ ¸ÕÀú º¹»ç... DWORD writeSize1 = -m_SeekPos; memcpy( pv, pWaveHeader + m_SeekPos + sizeof( SWaveHeader ), writeSize1 ); pv = (char*)pv + writeSize1; DWORD writeSize2 = cb - writeSize1; m_pOggFile->SeekWaveData( 0, ISoundFile::seek_begin ); size_t BytesWritten2 = m_pOggFile->Read( pv, writeSize2 ); m_SeekPos += cb; if( pcb ) { *pcb = writeSize1 + BytesWritten2; } } else { memcpy( pv, pWaveHeader + m_SeekPos + sizeof( SWaveHeader ), cb ); m_SeekPos += cb; if( pcb ) { *pcb = cb; } } } else { size_t BytesWritten = m_pOggFile->Read( pv, cb ); m_SeekPos += BytesWritten; if( pcb ) { *pcb = BytesWritten; } } return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP COggStream::Seek( LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER * plibNewPosition ) { if( m_pLoader == NULL ) return E_FAIL; long offset = dlibMove.LowPart; if( dwOrigin == FILE_BEGIN ) { m_SeekPos = offset - sizeof( SWaveHeader ); } else if( dwOrigin == FILE_END ) { m_SeekPos = m_TotalWaveBytes + offset - sizeof( SWaveHeader ); } else { m_SeekPos += offset; } if( m_SeekPos >= 0 ) { m_pOggFile->SeekWaveData( m_SeekPos, ISoundFile::seek_begin ); } if( plibNewPosition ) { plibNewPosition->HighPart = dlibMove.HighPart; plibNewPosition->LowPart = m_SeekPos + sizeof( SWaveHeader ); } return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP COggStream::Clone( IStream ** ppstm ) { COggStream * pOther = new COggStream; if( pOther == NULL ) { return E_OUTOFMEMORY; } if( m_pOggFile ) { HRESULT hr = pOther->Attach( m_pOggFile->GetFilename(), m_pLoader ); if( SUCCEEDED( hr ) ) { LARGE_INTEGER liNewPosition; liNewPosition.QuadPart = m_SeekPos + sizeof( SWaveHeader ); hr = pOther->Seek( liNewPosition, STREAM_SEEK_SET, NULL ); } if( FAILED( hr ) ) { RELEASE( pOther ); return hr; } } *ppstm = static_cast( pOther ); return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// // struct SObjRef { wstring m_strFilename; GUID m_guidObject; IDirectMusicObject* m_pObject; }; /////////////////////////////////////////////////////////////////////////////////////// // CDMusic_OggLoader::CDMusic_OggLoader() : m_cRef( 1 ) , m_pstrSearchPath( new std::wstring( L".\\" ) ) , m_pObjList( new LIST_OBJREF ) { } /////////////////////////////////////////////////////////////////////////////////////// // CDMusic_OggLoader::~CDMusic_OggLoader() { delete m_pstrSearchPath; delete m_pObjList; } /////////////////////////////////////////////////////////////////////////////////////// // HRESULT CDMusic_OggLoader::Init() { USES_CONVERSION; HRESULT hr = S_OK; IDirectMusicLoader * pLoader; hr = CoCreateInstance( CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, IID_IDirectMusicLoader, (void**)&pLoader ); if( SUCCEEDED( hr ) ) { DMUS_OBJECTDESC ObjDesc; IDirectMusicObject * pGMSet = NULL; ObjDesc.guidClass = CLSID_DirectMusicCollection; ObjDesc.guidObject = GUID_DefaultGMCollection; ObjDesc.dwSize = sizeof( DMUS_OBJECTDESC ); ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_OBJECT; hr = pLoader->GetObject( &ObjDesc, IID_IDirectMusicObject, (void**) &pGMSet ); if( SUCCEEDED( hr ) ) { SObjRef objRef; objRef.m_guidObject = GUID_DefaultGMCollection; objRef.m_pObject = pGMSet; m_pObjList->push_back( objRef ); pGMSet->AddRef(); pGMSet->Release(); } pLoader->Release(); } return hr; } /////////////////////////////////////////////////////////////////////////////////////// // void CDMusic_OggLoader::UnInit() { LIST_OBJREF::iterator it, it_end; it = m_pObjList->begin(); it_end = m_pObjList->end(); for( ; it != it_end; it++ ) { RELEASE( it->m_pObject ); } } /////////////////////////////////////////////////////////////////////////////////////// // //IUnknown methods STDMETHODIMP CDMusic_OggLoader::QueryInterface( REFIID iid, void ** ppv ) { *ppv = NULL; if( iid == IID_IUnknown || iid == IID_IDirectMusicLoader ) { *ppv = (void*)static_cast( this ); } else { return E_NOINTERFACE; } AddRef(); return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP_(ULONG) CDMusic_OggLoader::AddRef() { return InterlockedIncrement( &m_cRef ); } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP_(ULONG) CDMusic_OggLoader::Release() { LONG ulCount = InterlockedDecrement( &m_cRef ); if( ulCount <= 0 ) { delete this; } return (ULONG) ulCount; } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CDMusic_OggLoader::SetSearchDirectory( REFGUID rguidClass, WCHAR * pwzPath, BOOL fClear ) { *m_pstrSearchPath = pwzPath; return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// // SObjRef * CDMusic_OggLoader::FindCacheObject( LPDMUS_OBJECTDESC pDesc ) { LIST_OBJREF::iterator it, it_end; it = m_pObjList->begin(); it_end = m_pObjList->end(); if( pDesc->dwValidData & DMUS_OBJ_OBJECT ) //°°Àº GUID¸¦ °¡Áø °ÍÀÌ ÀÖ´ÂÁö ã´Â´Ù. { for( ; it != it_end; it++ ) { if( it->m_guidObject == pDesc->guidObject ) { return &(*it); } } } else if( pDesc->dwValidData & DMUS_OBJ_FILENAME ) //°°Àº È­ÀÏÀ̸§À» °¡Áø °ÍÀ» ã´Â´Ù. { for( ; it != it_end; it++ ) { if( it->m_strFilename == pDesc->wszFileName ) { return &(*it); } } } return NULL; //¸ø ãÀ¸¸é NULL ¸®ÅÏ } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CDMusic_OggLoader::CreateFromFile( IDirectMusicObject * pObject, WCHAR * wszFileName, MUSIC_TIME * pMusicTime ) { USES_CONVERSION; HRESULT hr = S_OK; //È­ÀϷκÎÅÍ ÀоîµéÀδÙ. È­ÀϷκÎÅÍ ½ºÆ®¸²À» ¸¸µé°í, ±×°ÍÀ¸·ÎºÎÅÍ ÀоîµéÀδÙ. WCHAR wzFileName1[MAX_PATH]; WCHAR wzExt[_MAX_EXT]; WCHAR wzDrive[_MAX_DRIVE] = L""; WCHAR wzDir[_MAX_DIR] = L""; _wsplitpath( wszFileName, wzDrive, wzDir, NULL, NULL ); //µå¶óÀÌºê ¹®ÀÚ°¡ Àְųª Path°¡ \\·Î ½ÃÀÛÇÒ ´ë if( wzDrive[0] != L'\0' || wzDir[0] == L'\\' ) { wcscpy( wzFileName1, wszFileName ); } else { _wmakepath( wzFileName1, NULL, m_pstrSearchPath->c_str(), wszFileName, NULL ); _wsplitpath( wzFileName1, NULL, NULL, NULL, wzExt ); } COggStream * pOggStream = new COggStream; if( pOggStream == NULL ) { hr = E_OUTOFMEMORY; } if( SUCCEEDED( hr ) ) { hr = pOggStream->Attach( W2CT( wzFileName1 ), this ); } IPersistStream * pPersistStream = NULL; if( SUCCEEDED( hr ) ) { hr = pObject->QueryInterface( IID_IPersistStream, (void**)&pPersistStream ); } if( SUCCEEDED( hr ) ) { hr = pPersistStream->Load( pOggStream ); } if( pMusicTime ) { *pMusicTime = pOggStream->GetMusicTime(); } RELEASE( pOggStream ); RELEASE( pPersistStream ); return hr; } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CDMusic_OggLoader::CreateFromStream( IDirectMusicObject * pObject, IStream * pStream ) { IStream * pClonedStream = NULL; IPersistStream * pPersistStream = NULL; HRESULT hr = pObject->QueryInterface( IID_IPersistStream, (void**)&pPersistStream ); if( SUCCEEDED( hr ) ) { hr = pStream->Clone( &pClonedStream ); } if( SUCCEEDED( hr ) ) { hr = pPersistStream->Load( pClonedStream ); } RELEASE( pPersistStream ); RELEASE( pClonedStream ); return hr; } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CDMusic_OggLoader::AddToList( IDirectMusicObject * pObject, LPDMUS_OBJECTDESC pDesc ) { DMUS_OBJECTDESC DESC; memset( &DESC, 0, sizeof( DESC ) ); DESC.dwSize = sizeof( DMUS_OBJECTDESC ); pObject->GetDescriptor( &DESC ); if( ( DESC.dwValidData & DMUS_OBJ_OBJECT ) || ( pDesc->dwValidData & DMUS_OBJ_FILENAME ) ) { SObjRef objRef; objRef.m_guidObject = DESC.guidObject; if( pDesc->dwValidData & DMUS_OBJ_FILENAME ) { objRef.m_strFilename = pDesc->wszFileName; } objRef.m_pObject = pObject; m_pObjList->push_back( objRef ); pObject->AddRef(); } return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// // //IDirectMusicLoader methods STDMETHODIMP CDMusic_OggLoader::GetObject( LPDMUS_OBJECTDESC pDesc, REFIID riid, LPVOID FAR * ppv ) { USES_CONVERSION; SObjRef * pFindedObj = FindCacheObject( pDesc ); //¿ÀºêÁ§Æ®¸¦ ã¾ÒÀ¸¸é ±×°ÍÀÌ ¿äûÇÑ ÀÎÅÍÆäÀ̽º¸¦ Áö¿øÇÏ´ÂÁö È®ÀÎÇϰí, ·¹ÆÛ·±½º¸¦ Ãß°¡ÇÑ´Ù. if( pFindedObj ) { IDirectMusicObject * pObject = pFindedObj->m_pObject; if( pObject ) { HRESULT hr = pObject->QueryInterface( riid, ppv ); return hr; } } else //ãÁö ¸øÇßÀ¸¸é ´Ù½Ã ¸¸µç´Ù. { MUSIC_TIME musicTime = 0; IDirectMusicObject * pObject = NULL; //ÁÖ¾îÁø Ŭ·¡½º´Â IDirectMusicObject¸¦ Áö¿øÇؼ­ »ý¼º °¡´ÉÇØ¾ß ÇÑ´Ù. HRESULT hr = CoCreateInstance( pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, IID_IDirectMusicObject, (void**)&pObject ); if( FAILED( hr ) ) { return hr; } if( pDesc->dwValidData & DMUS_OBJ_FILENAME ) { hr = CreateFromFile( pObject, pDesc->wszFileName, &musicTime ); } else if( pDesc->dwValidData & DMUS_OBJ_STREAM ) { hr = CreateFromStream( pObject, pDesc->pStream ); } else { hr = E_FAIL; } if( SUCCEEDED( hr ) ) { //·Îµù¿¡ ¼º°øÇßÀ¸¸é AddRef¸¦ ÇÏ°í ³ªÁßÀ» À§ÇØ GUID¸¦ ÀúÀåÇØ ³õ´Â´Ù. AddToList( pObject, pDesc ); hr = pObject->QueryInterface( riid, ppv ); } if( SUCCEEDED( hr ) && riid == IID_IDirectMusicSegment8 && musicTime ) { //Length ¼³Á¤.. ´ëºÎºÐÀÇ °æ¿ì´Â ¼³Á¤À» ¾ÈÇØÁ൵ ¹®Á¦°¡ ¾È »ý±âÁö¸¸, //¿©·¯ Á¾·ùÀÇ MusicBuffer¸¦ ¾µ ¶§´Â ¹®Á¦°¡ »ý±æ °¡´É¼ºÀÌ ÀÖÀ¸¹Ç·Î //¼³Á¤À» ÇØÁÜ. //ÇöÀç´Â ÅÛÆ÷ 120 bpmÀ» ±âÁØÀ¸·Î Music TimeÀ» °è»êÇØ¼­ ¼³Á¤Çϵµ·Ï ÇßÀ½. IDirectMusicSegment * pSegment = (IDirectMusicSegment8*)(*ppv); hr = pSegment->SetLength( musicTime ); } RELEASE( pObject ); return hr; } return E_FAIL; } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP_(void) CDMusic_OggLoader::CollectGarbage() { } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CDMusic_OggLoader::ReleaseObjectByUnknown( IUnknown * pObject ) { LIST_OBJREF::iterator it, it_end; it = m_pObjList->begin(); it_end = m_pObjList->end(); for( ; it != it_end; it++ ) { if( it->m_pObject == pObject ) { if( it->m_pObject->Release() == 0 ) { m_pObjList->erase( it ); break; } } } return S_OK; } /////////////////////////////////////////////////////////////////////////////////////// // STDMETHODIMP CDMusic_OggLoader::LoadObjectFromFile( REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR * pwzFilePath, void ** ppObject ) { DMUS_OBJECTDESC dmod; memset(&dmod, 0, sizeof(dmod)); dmod.dwSize = sizeof(dmod); dmod.dwValidData = DMUS_OBJ_FILENAME; dmod.guidClass = CLSID_DirectMusicSegment; wcscpy( dmod.wszFileName, pwzFilePath ); HRESULT hr = GetObject( &dmod, IID_IDirectMusicSegment8, ppObject ); return hr; } ///////////////////////////////////////////////////////////////////////////////////////