/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" #include "Sound.h" #include "Globals.h" #include "Logs.h" #include "Usefull.h" #include "mczapkie/mctools.h" #include "WavRead.h" #define SAFE_RELEASE(p) \ { \ if (p) \ { \ (p)->Release(); \ (p) = NULL; \ } \ } LPDIRECTSOUND TSoundsManager::pDS; LPDIRECTSOUNDNOTIFY TSoundsManager::pDSNotify; int TSoundsManager::Count = 0; TSoundContainer *TSoundsManager::First = NULL; TSoundContainer::TSoundContainer(LPDIRECTSOUND pDS, std::string const &Directory, std::string const &Filename, int NConcurrent) { // wczytanie pliku dźwiękowego int hr = 111; DSBuffer = nullptr; // na początek, gdyby uruchomić dźwięków się nie udało Concurrent = NConcurrent; Oldest = 0; m_name = Directory + Filename; // Create a new wave file class std::shared_ptr pWaveSoundRead = std::make_shared(); // Load the wave file if( ( FAILED( pWaveSoundRead->Open( m_name ) ) ) && ( FAILED( pWaveSoundRead->Open( Filename ) ) ) ) { ErrorLog( "Missed sound: " + Filename ); return; } m_name = ToLower( Filename ); // Set up 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_STATIC | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; if (!Global::bInactivePause) // jeśli przełączony w tło ma nadal działać dsbd.dwFlags |= DSBCAPS_GLOBALFOCUS; // to dźwięki mają być również słyszalne dsbd.dwBufferBytes = pWaveSoundRead->m_ckIn.cksize; dsbd.lpwfxFormat = pWaveSoundRead->m_pwfx; fSamplingRate = pWaveSoundRead->m_pwfx->nSamplesPerSec; iBitsPerSample = pWaveSoundRead->m_pwfx->wBitsPerSample; // pDSBuffer= (LPDIRECTSOUNDBUFFER*) malloc(Concurrent*sizeof(LPDIRECTSOUNDBUFFER)); // for (int i=0; iCreateSoundBuffer(&dsbd, &(DSBuffer), NULL))) // if (FAILED(pDS->CreateSoundBuffer(&dsbd,&(DSBuffer),NULL))) return; // Remember how big the buffer is DWORD dwBufferBytes; dwBufferBytes = dsbd.dwBufferBytes; //---------------------------------------------------------- BYTE *pbWavData; // Pointer to actual wav data UINT cbWavSize; // Size of data VOID *pbData = NULL; VOID *pbData2 = NULL; DWORD dwLength; DWORD dwLength2; // The size of wave data is in pWaveFileSound->m_ckIn INT nWaveFileSize = pWaveSoundRead->m_ckIn.cksize; // Allocate that buffer. pbWavData = new BYTE[nWaveFileSize]; if (NULL == pbWavData) return; // E_OUTOFMEMORY; // if (FAILED(hr=pWaveSoundRead->Read( nWaveFileSize,pbWavData,&cbWavSize))) if( FAILED( hr = pWaveSoundRead->Read( nWaveFileSize, pbWavData, &cbWavSize ) ) ) { delete[] pbWavData; return; } // Reset the file to the beginning pWaveSoundRead->Reset(); // Lock the buffer down // if (FAILED(hr=DSBuffer->Lock(0,dwBufferBytes,&pbData,&dwLength,&pbData2,&dwLength2,0))) if( FAILED( hr = DSBuffer->Lock( 0, dwBufferBytes, &pbData, &dwLength, &pbData2, &dwLength2, 0L ) ) ) { delete[] pbWavData; return; } // Copy the memory to it. memcpy(pbData, pbWavData, dwBufferBytes); // Unlock the buffer, we don't need it anymore. DSBuffer->Unlock(pbData, dwBufferBytes, NULL, 0); pbData = NULL; // We dont need the wav file data buffer anymore, so delete it delete[] pbWavData; DSBuffers.push(DSBuffer); }; TSoundContainer::~TSoundContainer() { while (!DSBuffers.empty()) { SAFE_RELEASE(DSBuffers.top()); DSBuffers.pop(); } SafeDelete(Next); }; LPDIRECTSOUNDBUFFER TSoundContainer::GetUnique(LPDIRECTSOUND pDS) { if (!DSBuffer) return NULL; // jeśli się dobrze zainicjowało LPDIRECTSOUNDBUFFER buff = nullptr; pDS->DuplicateSoundBuffer(DSBuffer, &buff); if (buff) DSBuffers.push(buff); return DSBuffers.top(); }; TSoundsManager::~TSoundsManager() { Free(); }; void TSoundsManager::Free() { SafeDelete(First); SAFE_RELEASE(pDS); }; TSoundContainer * TSoundsManager::LoadFromFile( std::string const &Dir, std::string const &Filename, int Concurrent) { TSoundContainer *tmp = First; First = new TSoundContainer(pDS, Dir, Filename, Concurrent); First->Next = tmp; Count++; return First; // albo NULL, jak nie wyjdzie (na razie zawsze wychodzi) }; LPDIRECTSOUNDBUFFER TSoundsManager::GetFromName(std::string const &Name, bool Dynamic, float *fSamplingRate) { // wyszukanie dźwięku w pamięci albo wczytanie z pliku std::string name{ ToLower(Name) }; std::string file; if (Dynamic) { // próba wczytania z katalogu pojazdu file = Global::asCurrentDynamicPath + name; if (FileExists(file)) name = file; // nowa nazwa else Dynamic = false; // wczytanie z "sounds/" } TSoundContainer *Next = First; for (int i = 0; i < Count; i++) { if( name == Next->m_name ) { if (fSamplingRate) *fSamplingRate = Next->fSamplingRate; // częstotliwość return (Next->GetUnique(pDS)); } else Next = Next->Next; }; if (Dynamic) // wczytanie z katalogu pojazdu Next = LoadFromFile("", name, 1); else Next = LoadFromFile(szSoundPath, name, 1); if (Next) { // if (fSamplingRate) *fSamplingRate = Next->fSamplingRate; // częstotliwość return Next->GetUnique(pDS); } ErrorLog("Missed sound: " + Name ); return (nullptr); }; void TSoundsManager::RestoreAll() { TSoundContainer *Next = First; HRESULT hr; DWORD dwStatus = 0; for (int i = 0; i < Count; i++) { 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. do { hr = Next->DSBuffer->Restore(); if (hr == DSERR_BUFFERLOST) Sleep(10); } while ((hr = Next->DSBuffer->Restore()) != 0); // char *Name= Next->Name; // int cc= Next->Concurrent; // delete Next; // Next= new TSoundContainer(pDS, Directory, Name, cc); // if( FAILED( hr = FillBuffer() ) ); // return hr; }; Next = Next->Next; }; }; bool TSoundsManager::Init(HWND hWnd) { First = nullptr; Count = 0; pDS = nullptr; pDSNotify = nullptr; // Create IDirectSound using the primary sound device auto hr = ::DirectSoundCreate(NULL, &pDS, NULL); if (hr != DS_OK) { return false; }; pDS->SetCooperativeLevel(hWnd, DSSCL_NORMAL); return true; };