📄 sound.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): ______________________________________.
*
* $Log: sound.cxx,v $
* Revision 1.29 2001/10/23 02:49:48 robertj
* Fixed problem with Abort() not always breaking I/O blocked threads.
*
* Revision 1.28 2001/10/12 03:50:27 robertj
* Fixed race condition on using Abort() which Reading from another thread.
* Fixed failure to start recording if called WaitForXXXFull() functions.
*
* Revision 1.27 2001/10/10 03:29:34 yurik
* Added open with format other than PCM
*
* Revision 1.26 2001/09/22 03:36:56 yurik
* Put code to prevent audio channel disconnection
*
* Revision 1.25 2001/09/10 02:51:23 robertj
* Major change to fix problem with error codes being corrupted in a
* PChannel when have simultaneous reads and writes in threads.
*
* Revision 1.24 2001/09/10 02:48:51 robertj
* Removed previous change as breaks semantics of Read() function, moved test
* for zero buffer length to part that waits for buffer to be full.
*
* Revision 1.23 2001/09/09 17:37:49 yurik
* dwBytesRecorded in WAVEHDR could return 0. We should not close the channel in this case
*
* Revision 1.22 2001/09/09 02:17:11 yurik
* Returned to 1.20
*
* Revision 1.20 2001/07/01 02:45:01 yurik
* WinCE compiler wants implicit cast to format
*
* Revision 1.19 2001/05/04 09:38:07 robertj
* Fixed problem with some WAV files having small WAVEFORMATEX chunk.
*
* Revision 1.18 2001/04/10 00:51:11 robertj
* Fixed bug in using incorrect function to delete event handle, thanks Victor H.
*
* Revision 1.17 2001/03/15 23:39:29 robertj
* Fixed bug with trying to write block larger than one buffer, thanks Norbert Oertel
*
* Revision 1.16 2001/02/07 04:45:54 robertj
* Added functions to get current sound channel format parameters.
*
* Revision 1.15 2000/07/04 04:30:47 robertj
* Fixed shutdown issues with buffers in use, again.
*
* Revision 1.14 2000/07/01 09:39:31 robertj
* Fixed shutdown issues with buffers in use.
*
* Revision 1.13 2000/06/29 00:39:29 robertj
* Fixed bug when PWaveFormat is assigned to itself.
*
* Revision 1.12 2000/05/22 07:17:50 robertj
* Fixed missing initialisation of format data block in Win32 PSound::Load().
*
* Revision 1.11 2000/05/01 05:59:11 robertj
* Added mutex to PSoundChannel buffer structure.
*
* Revision 1.10 2000/03/04 10:15:32 robertj
* Added simple play functions for sound files.
*
* Revision 1.9 2000/02/17 11:33:33 robertj
* Changed PSoundChannel::Write so blocks instead of error if no buffers available.
*
* Revision 1.8 1999/10/09 01:22:07 robertj
* Fixed error display for sound channels.
*
* Revision 1.7 1999/09/23 04:28:44 robertj
* Allowed some Win32 only access to wave format in sound channel
*
* Revision 1.6 1999/07/08 08:39:53 robertj
* Fixed bug when breaking block by closing the PSoundChannel in other thread.
*
* Revision 1.5 1999/06/24 14:01:25 robertj
* Fixed bug in not returning correct default recorder (waveIn) device.
*
* Revision 1.4 1999/06/07 01:36:28 robertj
* Fixed incorrect;ly set block alignment in sound structure.
*
* Revision 1.3 1999/05/28 14:04:51 robertj
* Added function to get default audio device.
*
* Revision 1.2 1999/02/22 10:15:15 robertj
* Sound driver interface implementation to Linux OSS specification.
*
* Revision 1.1 1999/02/16 06:02:07 robertj
* Major implementation to Linux OSS model
*
*/
#include <ptlib.h>
#include <mmsystem.h>
#include <process.h>
class PMultiMediaFile
{
public:
PMultiMediaFile();
~PMultiMediaFile();
BOOL CreateWaveFile(const PFilePath & filename,
const PWaveFormat & waveFormat,
DWORD dataSize);
BOOL OpenWaveFile(const PFilePath & filename,
PWaveFormat & waveFormat,
DWORD & dataSize);
BOOL Open(const PFilePath & filename, DWORD dwOpenFlags, LPMMIOINFO lpmmioinfo = NULL);
BOOL Close(UINT wFlags = 0);
BOOL Ascend(MMCKINFO & ckinfo, UINT wFlags = 0);
BOOL Descend(UINT wFlags, MMCKINFO & ckinfo, LPMMCKINFO lpckParent = NULL);
BOOL Read(void * data, PINDEX len);
BOOL CreateChunk(MMCKINFO & ckinfo, UINT wFlags = 0);
BOOL 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();
}
BOOL PMultiMediaFile::CreateWaveFile(const PFilePath & filename,
const PWaveFormat & waveFormat,
DWORD dataSize)
{
if (!Open(filename, MMIO_CREATE|MMIO_WRITE))
return FALSE;
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 FALSE;
// Save the format sub-chunk
mmChunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
mmChunk.cksize = waveFormat.GetSize();
if (!CreateChunk(mmChunk))
return FALSE;
if (!Write(waveFormat, waveFormat.GetSize()))
return FALSE;
// Save the data sub-chunk
mmChunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
mmChunk.cksize = dataSize;
return CreateChunk(mmChunk);
}
BOOL PMultiMediaFile::OpenWaveFile(const PFilePath & filename,
PWaveFormat & waveFormat,
DWORD & dataSize)
{
// Open wave file
if (!Open(filename, MMIO_READ | MMIO_ALLOCBUF))
return FALSE;
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 FALSE;
// Find the format chunk
mmSubChunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (!Descend(MMIO_FINDCHUNK, mmSubChunk, &mmParentChunk))
return FALSE;
// Get the size of the format chunk, allocate memory for it
if (!waveFormat.SetSize(mmSubChunk.cksize))
return FALSE;
// Read the format chunk
if (!Read(waveFormat.GetPointer(), waveFormat.GetSize()))
return FALSE;
// 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 FALSE;
// Get the size of the data subchunk
if (mmSubChunk.cksize == 0) {
dwLastError = MMSYSERR_INVALPARAM;
return FALSE;
}
dataSize = mmSubChunk.cksize;
return TRUE;
}
BOOL 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;
}
BOOL PMultiMediaFile::Close(UINT wFlags)
{
if (hmmio == NULL)
return FALSE;
mmioClose(hmmio, wFlags);
hmmio = NULL;
return TRUE;
}
BOOL PMultiMediaFile::Ascend(MMCKINFO & ckinfo, UINT wFlags)
{
dwLastError = mmioAscend(hmmio, &ckinfo, wFlags);
return dwLastError == MMSYSERR_NOERROR;
}
BOOL PMultiMediaFile::Descend(UINT wFlags, MMCKINFO & ckinfo, LPMMCKINFO lpckParent)
{
dwLastError = mmioDescend(hmmio, &ckinfo, lpckParent, wFlags);
return dwLastError == MMSYSERR_NOERROR;
}
BOOL PMultiMediaFile::Read(void * data, PINDEX len)
{
return mmioRead(hmmio, (char *)data, len) == len;
}
BOOL PMultiMediaFile::CreateChunk(MMCKINFO & ckinfo, UINT wFlags)
{
dwLastError = mmioCreateChunk(hmmio, &ckinfo, wFlags);
return dwLastError == MMSYSERR_NOERROR;
}
BOOL 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);
}
BOOL 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -