📄 sound_win32.cxx
字号:
PSoundChannel::Player);
if (!channel.IsOpen())
return PFalse;
return channel.PlaySound(*this, PTrue);
}
PBoolean PSound::PlayFile(const PFilePath & file, PBoolean wait)
{
PVarString filename = file;
return ::PlaySound(filename, 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, PFalse, PFalse, 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;
}
static PString GetWaveOutDeviceName(unsigned id)
{
PString str;
WAVEOUTCAPS caps;
if (waveOutGetDevCaps(id, &caps, sizeof(caps)) == 0)
str = caps.szPname;
return str.Trim();
}
static PString GetWaveInDeviceName(unsigned id)
{
PString str;
WAVEINCAPS caps;
if (waveInGetDevCaps(id, &caps, sizeof(caps)) == 0)
str = caps.szPname;
return str.Trim();
}
PStringArray PSoundChannelWin32::GetDeviceNames(Directions dir)
{
PStringArray devices;
unsigned numDevs, id;
switch (dir) {
case Player :
numDevs = waveOutGetNumDevs();
for (id = 0; id < numDevs; id++)
devices.AppendString(GetWaveOutDeviceName(id));
break;
case Recorder :
numDevs = waveInGetNumDevs();
for (id = 0; id < numDevs; id++)
devices.AppendString(GetWaveInDeviceName(id));
break;
}
return devices;
}
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))
str = GetWaveOutDeviceName(0);
}
else {
if (!registry.QueryValue("Record", str))
str = GetWaveInDeviceName(0);
}
return str.Trim();
}
PBoolean PSoundChannelWin32::GetDeviceID(const PString & device, Directions dir, unsigned& id)
{
if (device[0] == '#') {
id = device.Mid(1).AsUnsigned();
switch (dir) {
case Player :
if (id < waveOutGetNumDevs())
deviceName = GetWaveOutDeviceName(id);
break;
case Recorder :
if (id < waveInGetNumDevs())
deviceName = GetWaveInDeviceName(id);
break;
}
}
else {
switch (dir) {
case Player :
for (id = 0; id < waveOutGetNumDevs(); id++) {
PCaselessString str = GetWaveOutDeviceName(id);
if (str == device) {
deviceName = str;
break;
}
}
break;
case Recorder :
for (id = 0; id < waveInGetNumDevs(); id++) {
PCaselessString str = GetWaveInDeviceName(id);
if (str == device) {
deviceName = str;
break;
}
}
break;
}
}
if (deviceName.IsEmpty())
return SetErrorValues(NotFound, MMSYSERR_BADDEVICEID|PWIN32ErrorFlag);
return PTrue;
}
PBoolean PSoundChannelWin32::Open(const PString & device,
Directions dir,
unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample)
{
Close();
unsigned id = 0;
if( !GetDeviceID(device, dir, id) )
return PFalse;
waveFormat.SetFormat(numChannels, sampleRate, bitsPerSample);
direction = dir;
return OpenDevice(id);
}
PBoolean PSoundChannelWin32::Open(const PString & device,
Directions dir,
const PWaveFormat& format)
{
Close();
unsigned id = 0;
if( !GetDeviceID(device, dir, id) )
return PFalse;
waveFormat = format;
direction = dir;
return OpenDevice(id);
}
PBoolean PSoundChannelWin32::OpenDevice(unsigned id)
{
Close();
PWaitAndSignal mutex(bufferMutex);
bufferByteOffset = P_MAX_INDEX;
bufferIndex = 0;
WAVEFORMATEX* format = (WAVEFORMATEX*) waveFormat;
MIXERLINE line;
DWORD osError = MMSYSERR_BADDEVICEID;
switch (direction) {
case Player :
osError = waveOutOpen(&hWaveOut, id, format, (DWORD)hEventDone, 0, CALLBACK_EVENT);
if (osError == MMSYSERR_NOERROR) {
mixerOpen(&hMixer, (UINT)hWaveOut, NULL, NULL, MIXER_OBJECTF_HWAVEOUT);
line.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
}
break;
case Recorder :
osError = waveInOpen(&hWaveIn, id, format, (DWORD)hEventDone, 0, CALLBACK_EVENT);
if (osError == MMSYSERR_NOERROR) {
mixerOpen(&hMixer, (UINT)hWaveIn, NULL, NULL, MIXER_OBJECTF_HWAVEIN);
line.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
}
break;
}
if (osError != MMSYSERR_NOERROR)
return SetErrorValues(NotFound, osError|PWIN32ErrorFlag);
if (hMixer != NULL) {
line.cbStruct = sizeof(line);
if (mixerGetLineInfo((HMIXEROBJ)hMixer, &line, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR) {
mixerClose(hMixer);
hMixer = NULL;
}
else {
volumeControl.cbStruct = sizeof(volumeControl);
MIXERLINECONTROLS controls;
controls.cbStruct = sizeof(controls);
controls.dwLineID = line.dwLineID&0xffff;
controls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
controls.cControls = 1;
controls.pamxctrl = &volumeControl;
controls.cbmxctrl = volumeControl.cbStruct;
if (mixerGetLineControls((HMIXEROBJ)hMixer, &controls, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR) {
mixerClose(hMixer);
hMixer = NULL;
}
}
}
os_handle = id;
return PTrue;
}
PBoolean PSoundChannelWin32::IsOpen() const
{
return os_handle >= 0;
}
PBoolean PSoundChannelWin32::SetFormat(unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample)
{
Abort();
waveFormat.SetFormat(numChannels, sampleRate, bitsPerSample);
return OpenDevice(os_handle);
}
PBoolean 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;
}
PBoolean 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();
if (hMixer != NULL) {
mixerClose(hMixer);
hMixer = NULL;
}
os_handle = -1;
return PTrue;
}
PBoolean PSoundChannelWin32::SetBuffers(PINDEX size, PINDEX count)
{
Abort();
PAssert(size > 0 && count > 0, PInvalidParameter);
PBoolean ok = PTrue;
PWaitAndSignal mutex(bufferMutex);
if (!buffers.SetSize(count))
ok = PFalse;
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 = PFalse;
}
}
bufferByteOffset = P_MAX_INDEX;
bufferIndex = 0;
return ok;
}
PBoolean PSoundChannelWin32::GetBuffers(PINDEX & size, PINDEX & count)
{
PWaitAndSignal mutex(bufferMutex);
count = buffers.GetSize();
if (count == 0)
size = 0;
else
size = buffers[0].GetSize();
return PTrue;
}
PBoolean PSoundChannelWin32::Write(const void * data, PINDEX size)
{
lastWriteCount = 0;
if (hWaveOut == NULL)
return SetErrorValues(NotOpen, EBADF, LastWriteError);
const BYTE * ptr = (const BYTE *)data;
bufferMutex.Wait();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -