| #include "SkOSSound.h" |
| |
| #ifdef SK_BUILD_FOR_WIN |
| |
| ////////////////////////////////////////////////////////////////////// |
| // Construction/Destruction |
| ////////////////////////////////////////////////////////////////////// |
| |
| #include <Mmreg.h> |
| #if defined _WIN32 && _MSC_VER >= 1300 // disable nameless struct/union |
| #pragma warning ( push ) |
| #pragma warning ( disable : 4201 ) |
| #endif |
| #include <Mmsystem.h> |
| #if defined _WIN32 && _MSC_VER >= 1300 |
| #pragma warning ( pop ) |
| #endif |
| #include <stdio.h> |
| |
| class CWaveFile { |
| public: |
| BOOL Open(const char path[]); |
| void Close(); |
| |
| long Read(char* pData, long nLength); |
| |
| long GetLength() const {return m_nLength;} |
| WAVEFORMATEX* GetWaveFormat() {return (&m_Format);} |
| |
| protected: |
| FILE* m_pFile; |
| long m_nLength; |
| WAVEFORMATEX m_Format; |
| |
| private: |
| enum { |
| WF_OFFSET_FORMATTAG = 20, |
| WF_OFFSET_CHANNELS = 22, |
| WF_OFFSET_SAMPLESPERSEC = 24, |
| WF_OFFSET_AVGBYTESPERSEC = 28, |
| WF_OFFSET_BLOCKALIGN = 32, |
| WF_OFFSET_BITSPERSAMPLE = 34, |
| WF_OFFSET_DATASIZE = 40, |
| WF_OFFSET_DATA = 44, |
| WF_HEADER_SIZE = WF_OFFSET_DATA |
| }; |
| }; |
| |
| BOOL CWaveFile::Open(const char path[]) |
| { |
| BYTE aHeader[WF_HEADER_SIZE]; |
| |
| /* hResInfo = FindResource (hInst, lpName, "WAVE"); |
| |
| if (hResInfo == NULL) |
| return FALSE; |
| |
| // Load the wave resource. |
| hRes = LoadResource (hInst, hResInfo); |
| |
| if (hRes == NULL) |
| return FALSE; |
| |
| // Lock the wave resource and play it. |
| lpRes = LockResource (0); |
| */ |
| |
| |
| // open file |
| // m_pFile = _tfopen(szFileName, TEXT("rb")); |
| m_pFile = fopen(path, "rb"); |
| if (!m_pFile) { |
| return FALSE; |
| } |
| |
| // set file length |
| fseek(m_pFile, 0, SEEK_END); |
| m_nLength = ftell(m_pFile) - WF_HEADER_SIZE; |
| |
| // set the format attribute members |
| fseek(m_pFile, 0, SEEK_SET); |
| fread(aHeader, 1, WF_HEADER_SIZE, m_pFile); |
| m_Format.wFormatTag = *((WORD*) (aHeader + WF_OFFSET_FORMATTAG)); |
| m_Format.nChannels = *((WORD*) (aHeader + WF_OFFSET_CHANNELS)); |
| m_Format.nSamplesPerSec = *((DWORD*) (aHeader + WF_OFFSET_SAMPLESPERSEC)); |
| m_Format.nAvgBytesPerSec = *((DWORD*) (aHeader + WF_OFFSET_AVGBYTESPERSEC)); |
| m_Format.nBlockAlign = *((WORD*) (aHeader + WF_OFFSET_BLOCKALIGN)); |
| m_Format.wBitsPerSample = *((WORD*) (aHeader + WF_OFFSET_BITSPERSAMPLE)); |
| |
| return TRUE; |
| } |
| |
| void CWaveFile::Close() |
| { |
| fclose(m_pFile); |
| } |
| |
| long CWaveFile::Read(char* pData, long nLength) |
| { |
| return fread(pData, 1, nLength, m_pFile); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////////////// |
| |
| struct SkOSSoundWave { |
| HWAVEOUT hwo; |
| WAVEHDR whdr; |
| DWORD dwOldVolume; |
| CWaveFile waveFile; |
| HANDLE hDoneEvent; |
| }; |
| |
| static SkOSSoundWave gWave; |
| static bool gWavePaused; |
| static U8 gVolume; |
| static bool gInited = false; |
| |
| static void init_wave() |
| { |
| if (gInited == false) |
| { |
| gWave.hwo = nil; |
| gWavePaused = false; |
| gVolume = 0x80; |
| gInited = true; |
| } |
| } |
| |
| MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol); |
| MMRESULT EndWave(SkOSSoundWave* wave); |
| |
| #define MAX_ERRMSG 256 |
| |
| //#include "SkOSFile.h" // for utf16 |
| |
| void SkOSSound::Play(const char path[]) |
| { |
| init_wave(); |
| |
| if (gWave.hwo != nil) |
| SkOSSound::Stop(); |
| |
| U32 v32 = (gVolume << 8) | gVolume; // fill it out to 16bits |
| v32 |= v32 << 16; // set the left and right channels |
| |
| StartWave(path, &gWave, v32); |
| gWavePaused = false; |
| } |
| |
| bool SkOSSound::TogglePause() |
| { |
| init_wave(); |
| |
| if (gWavePaused) |
| SkOSSound::Resume(); |
| else |
| SkOSSound::Pause(); |
| return !gWavePaused; |
| } |
| |
| |
| void SkOSSound::Pause() |
| { |
| init_wave(); |
| |
| if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE)) |
| return; |
| waveOutPause(gWave.hwo); |
| gWavePaused = true; |
| } |
| |
| void SkOSSound::Resume() |
| { |
| init_wave(); |
| |
| if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE)) |
| return; |
| waveOutRestart(gWave.hwo); |
| gWavePaused = false; |
| } |
| |
| void SkOSSound::Stop() |
| { |
| init_wave(); |
| |
| // if (gWave.hwo == nil || (gWave.whdr.dwFlags & WHDR_DONE)) |
| if (gWave.hwo == nil) |
| return; |
| waveOutReset(gWave.hwo); |
| EndWave(&gWave); |
| gWavePaused = false; |
| gWave.hwo = nil; |
| } |
| |
| U8 SkOSSound::GetVolume() |
| { |
| init_wave(); |
| return gVolume; |
| } |
| |
| void SkOSSound::SetVolume(U8CPU vol) |
| { |
| if ((int)vol < 0) |
| vol = 0; |
| else if (vol > 255) |
| vol = 255; |
| |
| init_wave(); |
| gVolume = SkToU8(vol); |
| |
| if (gWave.hwo) |
| { |
| unsigned long v32 = (vol << 8) | vol; // fill it out to 16bits |
| v32 |= v32 << 16; // set the left and right channels |
| waveOutSetVolume(gWave.hwo, v32); |
| } |
| } |
| |
| #if 0 |
| unsigned long SoundManager::GetPosition() |
| { |
| if (fWave.hwo == nil) |
| return 0; |
| MMTIME time; |
| time.wType = TIME_MS; |
| if (waveOutGetPosition(fWave.hwo, &time, sizeof(time)) == MMSYSERR_NOERROR && |
| time.wType == TIME_MS) |
| { |
| return time.u.ms; |
| } |
| return 0; |
| } |
| #endif |
| |
| MMRESULT StartWave(const char path[], SkOSSoundWave* wave, U32 vol) |
| { |
| HWAVEOUT hwo = nil; |
| // WAVEHDR whdr; |
| MMRESULT mmres = 0; |
| // CWaveFile waveFile; |
| // HANDLE hDoneEvent = wave.hDoneEvent = |
| // CreateEvent(NULL, FALSE, FALSE, TEXT("DONE_EVENT")); |
| UINT devId; |
| // DWORD dwOldVolume; |
| |
| // Open wave file |
| if (!wave->waveFile.Open(path)) { |
| // TCHAR szErrMsg[MAX_ERRMSG]; |
| // _stprintf(szErrMsg, TEXT("Unable to open file: %s\n"), szWavFile); |
| // MessageBox(NULL, szErrMsg, TEXT("File I/O Error"), MB_OK); |
| return MMSYSERR_NOERROR; |
| } |
| |
| // Open audio device |
| for (devId = 0; devId < waveOutGetNumDevs(); devId++) |
| { |
| mmres = waveOutOpen(&hwo, devId, wave->waveFile.GetWaveFormat(), 0, 0, CALLBACK_NULL); |
| if (mmres == MMSYSERR_NOERROR) |
| { |
| wave->hwo = hwo; |
| break; |
| } |
| } |
| if (mmres != MMSYSERR_NOERROR) |
| { |
| SkDEBUGCODE(SkDebugf("waveOutOpen(%s) -> %d\n", path, mmres);) |
| return mmres; |
| } |
| |
| // Set volume |
| mmres = waveOutGetVolume(hwo, &wave->dwOldVolume); |
| if (mmres != MMSYSERR_NOERROR) { |
| return mmres; |
| } |
| |
| waveOutSetVolume(hwo, vol); |
| if (mmres != MMSYSERR_NOERROR) { |
| return mmres; |
| } |
| |
| // Initialize wave header |
| ZeroMemory(&wave->whdr, sizeof(WAVEHDR)); |
| wave->whdr.lpData = new char[wave->waveFile.GetLength()]; |
| wave->whdr.dwBufferLength = wave->waveFile.GetLength(); |
| wave->whdr.dwUser = 0; |
| wave->whdr.dwFlags = 0; |
| wave->whdr.dwLoops = 0; |
| wave->whdr.dwBytesRecorded = 0; |
| wave->whdr.lpNext = 0; |
| wave->whdr.reserved = 0; |
| |
| // Play buffer |
| wave->waveFile.Read(wave->whdr.lpData, wave->whdr.dwBufferLength); |
| |
| mmres = waveOutPrepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR)); |
| if (mmres != MMSYSERR_NOERROR) { |
| return mmres; |
| } |
| |
| mmres = waveOutWrite(hwo, &wave->whdr, sizeof(WAVEHDR)); |
| // if (mmres != MMSYSERR_NOERROR) { |
| return mmres; |
| // } |
| } |
| |
| #if 0 |
| void IdleWave(Wave& wave) |
| { |
| // Wait for audio to finish playing |
| while (!(wave.whdr.dwFlags & WHDR_DONE)) { |
| WaitForSingleObject(wave.hDoneEvent, INFINITE); |
| } |
| } |
| #endif |
| |
| MMRESULT EndWave(SkOSSoundWave* wave) |
| { |
| HWAVEOUT hwo = wave->hwo; |
| MMRESULT mmres; |
| // Clean up |
| mmres = waveOutUnprepareHeader(hwo, &wave->whdr, sizeof(WAVEHDR)); |
| if (mmres != MMSYSERR_NOERROR) { |
| return mmres; |
| } |
| |
| waveOutSetVolume(hwo, wave->dwOldVolume); |
| if (mmres != MMSYSERR_NOERROR) { |
| return mmres; |
| } |
| |
| mmres = waveOutClose(hwo); |
| if (mmres != MMSYSERR_NOERROR) { |
| return mmres; |
| } |
| |
| delete [] wave->whdr.lpData; |
| wave->waveFile.Close(); |
| |
| return MMSYSERR_NOERROR; |
| } |
| |
| #endif /* SK_BUILD_FOR_WIN */ |
| |