📄 sound_win32.cxx
字号:
/*
* sound.cxx
*
* Implementation of sound classes for Win32
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Revision: 19482 $
* $Author: rjongbloed $
* $Date: 2008-02-13 02:47:50 +0000 (Wed, 13 Feb 2008) $
*/
#include <ptlib.h>
#include <ptlib/sound.h>
#include <ptlib/plugin.h>
#include <ptlib/msos/ptlib/sound_win32.h>
#ifndef _WIN32_WCE
#ifdef _MSC_VER
#pragma comment(lib, "winmm.lib")
#endif
#else
#include <ptlib/wm/mmsystemx.h>
#endif
class PSound;
PCREATE_SOUND_PLUGIN(WindowsMultimedia, PSoundChannelWin32);
class PMultiMediaFile
{
public:
PMultiMediaFile();
~PMultiMediaFile();
PBoolean CreateWaveFile(const PFilePath & filename,
const PWaveFormat & waveFormat,
DWORD dataSize);
PBoolean OpenWaveFile(const PFilePath & filename,
PWaveFormat & waveFormat,
DWORD & dataSize);
PBoolean Open(const PFilePath & filename, DWORD dwOpenFlags, LPMMIOINFO lpmmioinfo = NULL);
PBoolean Close(UINT wFlags = 0);
PBoolean Ascend(MMCKINFO & ckinfo, UINT wFlags = 0);
PBoolean Descend(UINT wFlags, MMCKINFO & ckinfo, LPMMCKINFO lpckParent = NULL);
PBoolean Read(void * data, PINDEX len);
PBoolean CreateChunk(MMCKINFO & ckinfo, UINT wFlags = 0);
PBoolean Write(const void * data, PINDEX len);
DWORD GetLastError() const { return dwLastError; }
protected:
HMMIO hmmio;
DWORD dwLastError;
};
#define new PNEW
///////////////////////////////////////////////////////////////////////////////
PMultiMediaFile::PMultiMediaFile()
{
hmmio = NULL;
}
PMultiMediaFile::~PMultiMediaFile()
{
Close();
}
PBoolean PMultiMediaFile::CreateWaveFile(const PFilePath & filename,
const PWaveFormat & waveFormat,
DWORD dataSize)
{
if (!Open(filename, MMIO_CREATE|MMIO_WRITE))
return PFalse;
MMCKINFO mmChunk;
mmChunk.fccType = mmioFOURCC('W', 'A', 'V', 'E');
mmChunk.cksize = 4 + // Form type
4 + sizeof(DWORD) + waveFormat.GetSize() + // fmt chunk
4 + sizeof(DWORD) + dataSize; // data chunk
// Create a RIFF chunk
if (!CreateChunk(mmChunk, MMIO_CREATERIFF))
return PFalse;
// Save the format sub-chunk
mmChunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
mmChunk.cksize = waveFormat.GetSize();
if (!CreateChunk(mmChunk))
return PFalse;
if (!Write(waveFormat, waveFormat.GetSize()))
return PFalse;
// Save the data sub-chunk
mmChunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
mmChunk.cksize = dataSize;
return CreateChunk(mmChunk);
}
PBoolean PMultiMediaFile::OpenWaveFile(const PFilePath & filename,
PWaveFormat & waveFormat,
DWORD & dataSize)
{
// Open wave file
if (!Open(filename, MMIO_READ | MMIO_ALLOCBUF))
return PFalse;
MMCKINFO mmParentChunk, mmSubChunk;
dwLastError = MMSYSERR_NOERROR;
// Locate a 'RIFF' chunk with a 'WAVE' form type
mmParentChunk.fccType = mmioFOURCC('W', 'A', 'V', 'E');
if (!Descend(MMIO_FINDRIFF, mmParentChunk))
return PFalse;
// Find the format chunk
mmSubChunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (!Descend(MMIO_FINDCHUNK, mmSubChunk, &mmParentChunk))
return PFalse;
// Get the size of the format chunk, allocate memory for it
if (!waveFormat.SetSize(mmSubChunk.cksize))
return PFalse;
// Read the format chunk
if (!Read(waveFormat.GetPointer(), waveFormat.GetSize()))
return PFalse;
// Ascend out of the format subchunk
Ascend(mmSubChunk);
// Find the data subchunk
mmSubChunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
if (!Descend(MMIO_FINDCHUNK, mmSubChunk, &mmParentChunk))
return PFalse;
// Get the size of the data subchunk
if (mmSubChunk.cksize == 0) {
dwLastError = MMSYSERR_INVALPARAM;
return PFalse;
}
dataSize = mmSubChunk.cksize;
return PTrue;
}
PBoolean PMultiMediaFile::Open(const PFilePath & filename,
DWORD dwOpenFlags,
LPMMIOINFO lpmmioinfo)
{
MMIOINFO local_mmioinfo;
if (lpmmioinfo == NULL) {
lpmmioinfo = &local_mmioinfo;
memset(lpmmioinfo, 0, sizeof(local_mmioinfo));
}
hmmio = mmioOpen((char *)(const char *)filename, lpmmioinfo, dwOpenFlags);
dwLastError = lpmmioinfo->wErrorRet;
return hmmio != NULL;
}
PBoolean PMultiMediaFile::Close(UINT wFlags)
{
if (hmmio == NULL)
return PFalse;
mmioClose(hmmio, wFlags);
hmmio = NULL;
return PTrue;
}
PBoolean PMultiMediaFile::Ascend(MMCKINFO & ckinfo, UINT wFlags)
{
dwLastError = mmioAscend(hmmio, &ckinfo, wFlags);
return dwLastError == MMSYSERR_NOERROR;
}
PBoolean PMultiMediaFile::Descend(UINT wFlags, MMCKINFO & ckinfo, LPMMCKINFO lpckParent)
{
dwLastError = mmioDescend(hmmio, &ckinfo, lpckParent, wFlags);
return dwLastError == MMSYSERR_NOERROR;
}
PBoolean PMultiMediaFile::Read(void * data, PINDEX len)
{
return mmioRead(hmmio, (char *)data, len) == len;
}
PBoolean PMultiMediaFile::CreateChunk(MMCKINFO & ckinfo, UINT wFlags)
{
dwLastError = mmioCreateChunk(hmmio, &ckinfo, wFlags);
return dwLastError == MMSYSERR_NOERROR;
}
PBoolean PMultiMediaFile::Write(const void * data, PINDEX len)
{
return mmioWrite(hmmio, (char *)data, len) == len;
}
///////////////////////////////////////////////////////////////////////////////
PWaveFormat::PWaveFormat()
{
size = 0;
waveFormat = NULL;
}
PWaveFormat::~PWaveFormat()
{
if (waveFormat != NULL)
free(waveFormat);
}
PWaveFormat::PWaveFormat(const PWaveFormat & fmt)
{
size = fmt.size;
waveFormat = (WAVEFORMATEX *)malloc(size);
PAssert(waveFormat != NULL, POutOfMemory);
memcpy(waveFormat, fmt.waveFormat, size);
}
PWaveFormat & PWaveFormat::operator=(const PWaveFormat & fmt)
{
if (this == &fmt)
return *this;
if (waveFormat != NULL)
free(waveFormat);
size = fmt.size;
waveFormat = (WAVEFORMATEX *)malloc(size);
PAssert(waveFormat != NULL, POutOfMemory);
memcpy(waveFormat, fmt.waveFormat, size);
return *this;
}
void PWaveFormat::PrintOn(ostream & out) const
{
if (waveFormat == NULL)
out << "<null>";
else {
out << waveFormat->wFormatTag << ','
<< waveFormat->nChannels << ','
<< waveFormat->nSamplesPerSec << ','
<< waveFormat->nAvgBytesPerSec << ','
<< waveFormat->nBlockAlign << ','
<< waveFormat->wBitsPerSample;
if (waveFormat->cbSize > 0) {
out << hex << setfill('0');
const BYTE * ptr = (const BYTE *)&waveFormat[1];
for (PINDEX i = 0; i < waveFormat->cbSize; i++)
out << ',' << setw(2) << (unsigned)*ptr++;
out << dec << setfill(' ');
}
}
}
void PWaveFormat::ReadFrom(istream &)
{
}
void PWaveFormat::SetFormat(unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample)
{
PAssert(numChannels == 1 || numChannels == 2, PInvalidParameter);
PAssert(bitsPerSample == 8 || bitsPerSample == 16, PInvalidParameter);
if (waveFormat != NULL)
free(waveFormat);
size = sizeof(WAVEFORMATEX);
waveFormat = (WAVEFORMATEX *)malloc(sizeof(WAVEFORMATEX));
PAssert(waveFormat != NULL, POutOfMemory);
waveFormat->wFormatTag = WAVE_FORMAT_PCM;
waveFormat->nChannels = (WORD)numChannels;
waveFormat->nSamplesPerSec = sampleRate;
waveFormat->wBitsPerSample = (WORD)bitsPerSample;
waveFormat->nBlockAlign = (WORD)(numChannels*(bitsPerSample+7)/8);
waveFormat->nAvgBytesPerSec = waveFormat->nSamplesPerSec*waveFormat->nBlockAlign;
waveFormat->cbSize = 0;
}
void PWaveFormat::SetFormat(const void * data, PINDEX size)
{
SetSize(size);
memcpy(waveFormat, data, size);
}
PBoolean PWaveFormat::SetSize(PINDEX sz)
{
if (waveFormat != NULL)
free(waveFormat);
size = sz;
if (sz == 0)
waveFormat = NULL;
else {
if (sz < sizeof(WAVEFORMATEX))
sz = sizeof(WAVEFORMATEX);
waveFormat = (WAVEFORMATEX *)calloc(sz, 1);
waveFormat->cbSize = (WORD)(sz - sizeof(WAVEFORMATEX));
}
return waveFormat != NULL;
}
///////////////////////////////////////////////////////////////////////////////
PSound::PSound(unsigned channels,
unsigned samplesPerSecond,
unsigned bitsPerSample,
PINDEX bufferSize,
const BYTE * buffer)
{
encoding = 0;
numChannels = channels;
sampleRate = samplesPerSecond;
sampleSize = bitsPerSample;
SetSize(bufferSize);
if (buffer != NULL)
memcpy(GetPointer(), buffer, bufferSize);
}
PSound::PSound(const PFilePath & filename)
{
encoding = 0;
numChannels = 1;
sampleRate = 8000;
sampleSize = 16;
Load(filename);
}
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);
}
PBoolean PSound::Load(const PFilePath & filename)
{
// Open wave file
PMultiMediaFile mmio;
PWaveFormat waveFormat;
DWORD dataSize;
if (!mmio.OpenWaveFile(filename, waveFormat, dataSize)) {
dwLastError = mmio.GetLastError();
return PFalse;
}
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 PFalse;
}
// Read the waveform data subchunk
if (!mmio.Read(GetPointer(), GetSize())) {
dwLastError = mmio.GetLastError();
return PFalse;
}
return PTrue;
}
PBoolean 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 PFalse;
}
if (!mmio.Write(GetPointer(), GetSize())) {
dwLastError = mmio.GetLastError();
return PFalse;
}
return PTrue;
}
PBoolean PSound::Play()
{
PSoundChannel channel(PSoundChannel::GetDefaultDevice(PSoundChannel::Player),
PSoundChannel::Player);
if (!channel.IsOpen())
return PFalse;
return channel.PlaySound(*this, PTrue);
}
PBoolean PSound::Play(const PString & device)
{
PSoundChannel channel(device,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -