📄 sound_win32.cxx
字号:
PSound & PSound::operator=(const PBYTEArray & data){ PBYTEArray::operator=(data); return *this;}void PSound::SetFormat(unsigned channels, unsigned samplesPerSecond, unsigned bitsPerSample){ encoding = 0; numChannels = channels; sampleRate = samplesPerSecond; sampleSize = bitsPerSample; formatInfo.SetSize(0);}BOOL PSound::Load(const PFilePath & filename){ // Open wave file PMultiMediaFile mmio; PWaveFormat waveFormat; DWORD dataSize; if (!mmio.OpenWaveFile(filename, waveFormat, dataSize)) { dwLastError = mmio.GetLastError(); return FALSE; } encoding = waveFormat->wFormatTag; numChannels = waveFormat->nChannels; sampleRate = waveFormat->nSamplesPerSec; sampleSize = waveFormat->wBitsPerSample; if (encoding != 0) { PINDEX formatSize = waveFormat->cbSize + sizeof(WAVEFORMATEX); memcpy(formatInfo.GetPointer(formatSize), waveFormat, formatSize); } // Allocate and lock memory for the waveform data. if (!SetSize(dataSize)) { dwLastError = MMSYSERR_NOMEM; return FALSE; } // Read the waveform data subchunk if (!mmio.Read(GetPointer(), GetSize())) { dwLastError = mmio.GetLastError(); return FALSE; } return TRUE;}BOOL PSound::Save(const PFilePath & filename){ PWaveFormat waveFormat; if (encoding == 0) waveFormat.SetFormat(numChannels, sampleRate, sampleSize); else { waveFormat.SetSize(GetFormatInfoSize()); memcpy(waveFormat.GetPointer(), GetFormatInfoData(), GetFormatInfoSize()); } // Open wave file PMultiMediaFile mmio; if (!mmio.CreateWaveFile(filename, waveFormat, GetSize())) { dwLastError = mmio.GetLastError(); return FALSE; } if (!mmio.Write(GetPointer(), GetSize())) { dwLastError = mmio.GetLastError(); return FALSE; } return TRUE;}BOOL PSound::Play(){ PSoundChannel channel(PSoundChannel::GetDefaultDevice(PSoundChannel::Player), PSoundChannel::Player); if (!channel.IsOpen()) return FALSE; return channel.PlaySound(*this, TRUE);}BOOL PSound::Play(const PString & device){ PSoundChannel channel(device, PSoundChannel::Player); if (!channel.IsOpen()) return FALSE; return channel.PlaySound(*this, TRUE);}BOOL PSound::PlayFile(const PFilePath & file, BOOL wait){ return ::PlaySound(file, NULL, SND_FILENAME|(wait ? SND_SYNC : SND_ASYNC));}///////////////////////////////////////////////////////////////////////////////PWaveBuffer::PWaveBuffer(PINDEX sz) : PBYTEArray(sz){ hWaveOut = NULL; hWaveIn = NULL; header.dwFlags = WHDR_DONE;}PWaveBuffer::~PWaveBuffer(){ Release();}PWaveBuffer & PWaveBuffer::operator=(const PSound & sound){ PBYTEArray::operator=(sound); return *this;}void PWaveBuffer::PrepareCommon(PINDEX count){ Release(); memset(&header, 0, sizeof(header)); header.lpData = (char *)GetPointer(); header.dwBufferLength = count; header.dwUser = (DWORD)this;}DWORD PWaveBuffer::Prepare(HWAVEOUT hOut, PINDEX & count){ // Set up WAVEHDR structure and prepare it to be written to wave device if (count > GetSize()) count = GetSize(); PrepareCommon(count); hWaveOut = hOut; return waveOutPrepareHeader(hWaveOut, &header, sizeof(header));}DWORD PWaveBuffer::Prepare(HWAVEIN hIn){ // Set up WAVEHDR structure and prepare it to be read from wave device PrepareCommon(GetSize()); hWaveIn = hIn; return waveInPrepareHeader(hWaveIn, &header, sizeof(header));}DWORD PWaveBuffer::Release(){ DWORD err = MMSYSERR_NOERROR; // There seems to be some pathalogical cases where on an Abort() call the buffers // still are "in use", even though waveOutReset() was called. So wait until the // sound driver has finished with the buffer before releasing it. if (hWaveOut != NULL) { if ((err = waveOutUnprepareHeader(hWaveOut, &header, sizeof(header))) == WAVERR_STILLPLAYING) return err; hWaveOut = NULL; } if (hWaveIn != NULL) { if ((err = waveInUnprepareHeader(hWaveIn, &header, sizeof(header))) == WAVERR_STILLPLAYING) return err; hWaveIn = NULL; } header.dwFlags |= WHDR_DONE; return err;}///////////////////////////////////////////////////////////////////////////////PSoundChannelWin32::PSoundChannelWin32(){ Construct();}PSoundChannelWin32::PSoundChannelWin32(const PString & device, Directions dir, unsigned numChannels, unsigned sampleRate, unsigned bitsPerSample){ Construct(); Open(device, dir, numChannels, sampleRate, bitsPerSample);}void PSoundChannelWin32::Construct(){ direction = Player; hWaveOut = NULL; hWaveIn = NULL; hEventDone = CreateEvent(NULL, FALSE, FALSE, NULL); waveFormat.SetFormat(1, 8000, 16); bufferByteOffset = P_MAX_INDEX; SetBuffers(32768, 3);}PSoundChannelWin32::~PSoundChannelWin32(){ Close(); if (hEventDone != NULL) CloseHandle(hEventDone);}PString PSoundChannelWin32::GetName() const{ return deviceName;}PStringArray PSoundChannelWin32::GetDeviceNames(Directions dir){ PStringArray array; unsigned numDevs, id; switch (dir) { case Player : numDevs = waveOutGetNumDevs(); for (id = 0; id < numDevs; id++) { WAVEOUTCAPS caps; if (waveOutGetDevCaps(id, &caps, sizeof(caps)) == 0) array[array.GetSize()] = caps.szPname; } break; case Recorder : numDevs = waveInGetNumDevs(); for (id = 0; id < numDevs; id++) { WAVEINCAPS caps; if (waveInGetDevCaps(id, &caps, sizeof(caps)) == 0) array[array.GetSize()] = caps.szPname; } break; } return array;}PString PSoundChannelWin32::GetDefaultDevice(Directions dir){ RegistryKey registry("HKEY_CURRENT_USER\\Software\\Microsoft\\Multimedia\\Sound Mapper", RegistryKey::ReadOnly); PString str; if (dir == Player) { if (!registry.QueryValue("Playback", str)) { WAVEOUTCAPS caps; if (waveOutGetDevCaps(0, &caps, sizeof(caps)) == 0) str = caps.szPname; } } else { if (!registry.QueryValue("Record", str)) { WAVEINCAPS caps; if (waveInGetDevCaps(0, &caps, sizeof(caps)) == 0) str = caps.szPname; } } return str;}BOOL PSoundChannelWin32::GetDeviceID(const PString & device, Directions dir, unsigned& id){ BOOL bad = TRUE; if (device[0] == '#') { id = device.Mid(1).AsUnsigned(); switch (dir) { case Player : if (id < waveOutGetNumDevs()) { WAVEOUTCAPS caps; if (waveOutGetDevCaps(id, &caps, sizeof(caps)) == 0) { deviceName = caps.szPname; bad = FALSE; } } break; case Recorder : if (id < waveInGetNumDevs()) { WAVEINCAPS caps; if (waveInGetDevCaps(id, &caps, sizeof(caps)) == 0) { deviceName = caps.szPname; bad = FALSE; } } break; } } else { switch (dir) { case Player : for (id = 0; id < waveOutGetNumDevs(); id++) { WAVEOUTCAPS caps; if (waveOutGetDevCaps(id, &caps, sizeof(caps)) == 0 && stricmp(caps.szPname, device) == 0) { deviceName = caps.szPname; bad = FALSE; break; } } break; case Recorder : for (id = 0; id < waveInGetNumDevs(); id++) { WAVEINCAPS caps; if (waveInGetDevCaps(id, &caps, sizeof(caps)) == 0 && stricmp(caps.szPname, device) == 0) { deviceName = caps.szPname; bad = FALSE; break; } } break; } } if (bad) return SetErrorValues(NotFound, MMSYSERR_BADDEVICEID|PWIN32ErrorFlag); return TRUE;}BOOL PSoundChannelWin32::Open(const PString & device, Directions dir, unsigned numChannels, unsigned sampleRate, unsigned bitsPerSample){ Close(); unsigned id = 0; if( !GetDeviceID(device, dir, id) ) return FALSE; waveFormat.SetFormat(numChannels, sampleRate, bitsPerSample); direction = dir; return OpenDevice(id);}BOOL PSoundChannelWin32::Open(const PString & device, Directions dir, const PWaveFormat& format){ Close(); unsigned id = 0; if( !GetDeviceID(device, dir, id) ) return FALSE; waveFormat = format; direction = dir; return OpenDevice(id);}BOOL PSoundChannelWin32::OpenDevice(unsigned id){ Close(); PWaitAndSignal mutex(bufferMutex); bufferByteOffset = P_MAX_INDEX; bufferIndex = 0; WAVEFORMATEX* format = (WAVEFORMATEX*) waveFormat; DWORD osError = MMSYSERR_BADDEVICEID; switch (direction) { case Player : osError = waveOutOpen(&hWaveOut, id, format, (DWORD)hEventDone, 0, CALLBACK_EVENT); break; case Recorder : osError = waveInOpen(&hWaveIn, id, format, (DWORD)hEventDone, 0, CALLBACK_EVENT); break; } if (osError != MMSYSERR_NOERROR) return SetErrorValues(NotFound, osError|PWIN32ErrorFlag); os_handle = id; return TRUE;}BOOL PSoundChannelWin32::IsOpen() const{ return os_handle >= 0;}BOOL PSoundChannelWin32::SetFormat(unsigned numChannels, unsigned sampleRate, unsigned bitsPerSample){ Abort(); waveFormat.SetFormat(numChannels, sampleRate, bitsPerSample); return OpenDevice(os_handle);}BOOL PSoundChannelWin32::SetFormat(const PWaveFormat & format){ Abort(); waveFormat = format; return OpenDevice(os_handle);}unsigned PSoundChannelWin32::GetChannels() const{ return waveFormat->nChannels;}unsigned PSoundChannelWin32::GetSampleRate() const{ return waveFormat->nSamplesPerSec;}unsigned PSoundChannelWin32::GetSampleSize() const{ return waveFormat->wBitsPerSample;}BOOL PSoundChannelWin32::Close(){ if (!IsOpen()) return SetErrorValues(NotOpen, EBADF); Abort(); if (hWaveOut != NULL) { while (waveOutClose(hWaveOut) == WAVERR_STILLPLAYING) waveOutReset(hWaveOut); hWaveOut = NULL; } if (hWaveIn != NULL) { while (waveInClose(hWaveIn) == WAVERR_STILLPLAYING) waveInReset(hWaveIn); hWaveIn = NULL; } Abort(); os_handle = -1; return TRUE;}BOOL PSoundChannelWin32::SetBuffers(PINDEX size, PINDEX count){ Abort(); PAssert(size > 0 && count > 0, PInvalidParameter); BOOL ok = TRUE; PWaitAndSignal mutex(bufferMutex); if (!buffers.SetSize(count)) ok = FALSE; else { for (PINDEX i = 0; i < count; i++) { if (buffers.GetAt(i) == NULL) buffers.SetAt(i, new PWaveBuffer(size)); if (!buffers[i].SetSize(size)) ok = FALSE; } } bufferByteOffset = P_MAX_INDEX; bufferIndex = 0; return ok;}BOOL PSoundChannelWin32::GetBuffers(PINDEX & size, PINDEX & count){ PWaitAndSignal mutex(bufferMutex); count = buffers.GetSize(); if (count == 0) size = 0; else size = buffers[0].GetSize(); return TRUE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -