📄 sound.cxx
字号:
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::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;
}
///////////////////////////////////////////////////////////////////////////////
PSoundChannel::PSoundChannel()
{
Construct();
}
PSoundChannel::PSoundChannel(const PString & device,
Directions dir,
unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample)
{
Construct();
Open(device, dir, numChannels, sampleRate, bitsPerSample);
}
void PSoundChannel::Construct()
{
direction = Player;
hWaveOut = NULL;
hWaveIn = NULL;
hEventDone = CreateEvent(NULL, FALSE, FALSE, NULL);
waveFormat.SetFormat(1, 8000, 16);
bufferByteOffset = P_MAX_INDEX;
SetBuffers(32768, 2);
}
PSoundChannel::~PSoundChannel()
{
Close();
if (hEventDone != NULL)
CloseHandle(hEventDone);
}
PString PSoundChannel::GetName() const
{
return deviceName;
}
PStringArray PSoundChannel::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 PSoundChannel::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 PSoundChannel::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 PSoundChannel::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 PSoundChannel::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 PSoundChannel::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 PSoundChannel::SetFormat(unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample)
{
Abort();
waveFormat.SetFormat(numChannels, sampleRate, bitsPerSample);
return OpenDevice(os_handle);
}
BOOL PSoundChannel::SetFormat(const PWaveFormat & format)
{
Abort();
waveFormat = format;
return OpenDevice(os_handle);
}
unsigned PSoundChannel::GetChannels() const
{
return waveFormat->nChannels;
}
unsigned PSoundChannel::GetSampleRate() const
{
return waveFormat->nSamplesPerSec;
}
unsigned PSoundChannel::GetSampleSize() const
{
return waveFormat->wBitsPerSample;
}
BOOL PSoundChannel::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 PSoundChannel::SetBuffers(PINDEX size, PINDEX count)
{
Abort();
PAssert(size > 0 && count > 0, PInvalidParameter);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -