Changed output and intermediate directories, tuned up compile parameters for Windows...
[pwlib.git] / src / ptlib / msos / sound_win32.cxx
blobef739e07d847e54a3390aa694c80b13a5bc1d953
1 /*
2 * sound.cxx
4 * Implementation of sound classes for Win32
6 * Portable Windows Library
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
27 * Contributor(s): ______________________________________.
29 * $Log$
30 * Revision 1.18 2006/07/31 12:08:28 rjongbloed
31 * Fixed problem with WAV file asynchronous play back
33 * Revision 1.17 2006/06/21 04:20:07 csoutheren
34 * Fixes for VS.net
36 * Revision 1.16 2006/04/09 11:03:59 csoutheren
37 * Remove warnings on VS.net 2005
39 * Revision 1.15 2005/11/30 12:47:42 csoutheren
40 * Removed tabs, reformatted some code, and changed tags for Doxygen
42 * Revision 1.14 2005/10/06 08:14:55 csoutheren
43 * Fixed race condition in sound driver when shutting down with driver that is not open
45 * Revision 1.13 2005/09/18 13:01:43 dominance
46 * fixed pragma warnings when building with gcc.
48 * Revision 1.12 2005/07/03 13:48:58 shorne
49 * Add the ability to play sound to specified device.
51 * Revision 1.11 2005/04/21 05:27:04 csoutheren
52 * Prevent weird deadlocks when using record-only or play-only sound channels
54 * Revision 1.10 2005/01/04 07:44:04 csoutheren
55 * More changes to implement the new configuration methodology, and also to
56 * attack the global static problem
58 * Revision 1.9 2004/10/23 11:16:17 ykiryanov
59 * Added ifdef _WIN32_WCE for PocketPC 2003 SDK port
61 * Revision 1.8 2004/08/16 06:41:00 csoutheren
62 * Added adapters template to make device plugins available via the abstract factory interface
64 * Revision 1.7 2004/04/09 06:52:18 rjongbloed
65 * Removed #pargma linker command for /delayload of DLL as documentations sais that
66 * you cannot do this.
68 * Revision 1.6 2004/02/23 23:52:20 csoutheren
69 * Added pragmas to avoid every Windows application needing to include libs explicitly
71 * Revision 1.5 2004/02/15 03:59:20 rjongbloed
72 * Fixed the default number of buffer to be the value determined emprirically in
73 * OpenH323, thanks Ted Szoczei
75 * Revision 1.4 2003/12/29 03:29:26 csoutheren
76 * Allowed access to Windows sound channel declaration, just in case it is required
78 * Revision 1.3 2003/12/29 02:00:40 csoutheren
79 * Moved some declarations to sound_win32.h to allow access
81 * Revision 1.2 2003/11/18 10:50:44 csoutheren
82 * Changed name of Windows sound device
84 * Revision 1.1 2003/11/12 04:39:56 csoutheren
85 * Changed to work with new plugin system
87 * Revision 1.37 2003/11/05 05:57:58 csoutheren
88 * Added #pragma to include required libs
90 * Revision 1.36 2003/09/17 05:45:10 csoutheren
91 * Removed recursive includes
93 * Revision 1.35 2003/06/05 05:20:35 rjongbloed
94 * Fixed WinCE compatibility, thanks Yuri Kiryanov
96 * Revision 1.34 2003/05/29 08:57:38 rjongbloed
97 * Futher changes to not alter balance when changing volume setting, also fixed
98 * correct return of volume level if balance not centred, thanks Diego Tártara
100 * Revision 1.33 2003/05/01 00:17:40 robertj
101 * Fixed setting of stereo volume levels, thanks Diego Tártara
103 * Revision 1.32 2002/08/05 01:22:59 robertj
104 * Fixed possible range error on SetVolume(), thanks Sonya Cooper-Hull
106 * Revision 1.31 2002/02/08 09:59:45 robertj
107 * Slight adjustment to API and documentation for volume functions.
108 * Added implementation for volume function on play, still needs recording.
110 * Revision 1.30 2002/02/07 20:57:21 dereks
111 * add SetVolume and GetVolume methods to PSoundChannel
113 * Revision 1.29 2001/10/23 02:49:48 robertj
114 * Fixed problem with Abort() not always breaking I/O blocked threads.
116 * Revision 1.28 2001/10/12 03:50:27 robertj
117 * Fixed race condition on using Abort() which Reading from another thread.
118 * Fixed failure to start recording if called WaitForXXXFull() functions.
120 * Revision 1.27 2001/10/10 03:29:34 yurik
121 * Added open with format other than PCM
123 * Revision 1.26 2001/09/22 03:36:56 yurik
124 * Put code to prevent audio channel disconnection
126 * Revision 1.25 2001/09/10 02:51:23 robertj
127 * Major change to fix problem with error codes being corrupted in a
128 * PChannel when have simultaneous reads and writes in threads.
130 * Revision 1.24 2001/09/10 02:48:51 robertj
131 * Removed previous change as breaks semantics of Read() function, moved test
132 * for zero buffer length to part that waits for buffer to be full.
134 * Revision 1.23 2001/09/09 17:37:49 yurik
135 * dwBytesRecorded in WAVEHDR could return 0. We should not close the channel in this case
137 * Revision 1.22 2001/09/09 02:17:11 yurik
138 * Returned to 1.20
140 * Revision 1.20 2001/07/01 02:45:01 yurik
141 * WinCE compiler wants implicit cast to format
143 * Revision 1.19 2001/05/04 09:38:07 robertj
144 * Fixed problem with some WAV files having small WAVEFORMATEX chunk.
146 * Revision 1.18 2001/04/10 00:51:11 robertj
147 * Fixed bug in using incorrect function to delete event handle, thanks Victor H.
149 * Revision 1.17 2001/03/15 23:39:29 robertj
150 * Fixed bug with trying to write block larger than one buffer, thanks Norbert Oertel
152 * Revision 1.16 2001/02/07 04:45:54 robertj
153 * Added functions to get current sound channel format parameters.
155 * Revision 1.15 2000/07/04 04:30:47 robertj
156 * Fixed shutdown issues with buffers in use, again.
158 * Revision 1.14 2000/07/01 09:39:31 robertj
159 * Fixed shutdown issues with buffers in use.
161 * Revision 1.13 2000/06/29 00:39:29 robertj
162 * Fixed bug when PWaveFormat is assigned to itself.
164 * Revision 1.12 2000/05/22 07:17:50 robertj
165 * Fixed missing initialisation of format data block in Win32 PSound::Load().
167 * Revision 1.11 2000/05/01 05:59:11 robertj
168 * Added mutex to PSoundChannel buffer structure.
170 * Revision 1.10 2000/03/04 10:15:32 robertj
171 * Added simple play functions for sound files.
173 * Revision 1.9 2000/02/17 11:33:33 robertj
174 * Changed PSoundChannel::Write so blocks instead of error if no buffers available.
176 * Revision 1.8 1999/10/09 01:22:07 robertj
177 * Fixed error display for sound channels.
179 * Revision 1.7 1999/09/23 04:28:44 robertj
180 * Allowed some Win32 only access to wave format in sound channel
182 * Revision 1.6 1999/07/08 08:39:53 robertj
183 * Fixed bug when breaking block by closing the PSoundChannel in other thread.
185 * Revision 1.5 1999/06/24 14:01:25 robertj
186 * Fixed bug in not returning correct default recorder (waveIn) device.
188 * Revision 1.4 1999/06/07 01:36:28 robertj
189 * Fixed incorrect;ly set block alignment in sound structure.
191 * Revision 1.3 1999/05/28 14:04:51 robertj
192 * Added function to get default audio device.
194 * Revision 1.2 1999/02/22 10:15:15 robertj
195 * Sound driver interface implementation to Linux OSS specification.
197 * Revision 1.1 1999/02/16 06:02:07 robertj
198 * Major implementation to Linux OSS model
202 #define P_FORCE_STATIC_PLUGIN
204 #include <ptlib.h>
205 #include <ptlib/sound.h>
207 #if defined(_WIN32) && !defined(P_FORCE_STATIC_PLUGIN)
208 #error "sound_win32.cxx must be compiled without precompiled headers"
209 #endif
211 #include <process.h>
213 #include <ptlib/plugin.h>
214 #include <ptlib/msos/ptlib/sound_win32.h>
216 #ifndef _WIN32_WCE
217 #ifdef _MSC_VER
218 #pragma comment(lib, "winmm.lib")
219 #endif
220 #endif
222 class PSound;
224 PCREATE_SOUND_PLUGIN(WindowsMultimedia, PSoundChannelWin32);
226 class PMultiMediaFile
228 public:
229 PMultiMediaFile();
230 ~PMultiMediaFile();
232 BOOL CreateWaveFile(const PFilePath & filename,
233 const PWaveFormat & waveFormat,
234 DWORD dataSize);
235 BOOL OpenWaveFile(const PFilePath & filename,
236 PWaveFormat & waveFormat,
237 DWORD & dataSize);
239 BOOL Open(const PFilePath & filename, DWORD dwOpenFlags, LPMMIOINFO lpmmioinfo = NULL);
240 BOOL Close(UINT wFlags = 0);
241 BOOL Ascend(MMCKINFO & ckinfo, UINT wFlags = 0);
242 BOOL Descend(UINT wFlags, MMCKINFO & ckinfo, LPMMCKINFO lpckParent = NULL);
243 BOOL Read(void * data, PINDEX len);
244 BOOL CreateChunk(MMCKINFO & ckinfo, UINT wFlags = 0);
245 BOOL Write(const void * data, PINDEX len);
247 DWORD GetLastError() const { return dwLastError; }
249 protected:
250 HMMIO hmmio;
251 DWORD dwLastError;
255 #define new PNEW
258 ///////////////////////////////////////////////////////////////////////////////
260 PMultiMediaFile::PMultiMediaFile()
262 hmmio = NULL;
266 PMultiMediaFile::~PMultiMediaFile()
268 Close();
272 BOOL PMultiMediaFile::CreateWaveFile(const PFilePath & filename,
273 const PWaveFormat & waveFormat,
274 DWORD dataSize)
276 if (!Open(filename, MMIO_CREATE|MMIO_WRITE))
277 return FALSE;
279 MMCKINFO mmChunk;
280 mmChunk.fccType = mmioFOURCC('W', 'A', 'V', 'E');
281 mmChunk.cksize = 4 + // Form type
282 4 + sizeof(DWORD) + waveFormat.GetSize() + // fmt chunk
283 4 + sizeof(DWORD) + dataSize; // data chunk
285 // Create a RIFF chunk
286 if (!CreateChunk(mmChunk, MMIO_CREATERIFF))
287 return FALSE;
289 // Save the format sub-chunk
290 mmChunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
291 mmChunk.cksize = waveFormat.GetSize();
292 if (!CreateChunk(mmChunk))
293 return FALSE;
295 if (!Write(waveFormat, waveFormat.GetSize()))
296 return FALSE;
298 // Save the data sub-chunk
299 mmChunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
300 mmChunk.cksize = dataSize;
301 return CreateChunk(mmChunk);
305 BOOL PMultiMediaFile::OpenWaveFile(const PFilePath & filename,
306 PWaveFormat & waveFormat,
307 DWORD & dataSize)
309 // Open wave file
310 if (!Open(filename, MMIO_READ | MMIO_ALLOCBUF))
311 return FALSE;
313 MMCKINFO mmParentChunk, mmSubChunk;
314 dwLastError = MMSYSERR_NOERROR;
316 // Locate a 'RIFF' chunk with a 'WAVE' form type
317 mmParentChunk.fccType = mmioFOURCC('W', 'A', 'V', 'E');
318 if (!Descend(MMIO_FINDRIFF, mmParentChunk))
319 return FALSE;
321 // Find the format chunk
322 mmSubChunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
323 if (!Descend(MMIO_FINDCHUNK, mmSubChunk, &mmParentChunk))
324 return FALSE;
326 // Get the size of the format chunk, allocate memory for it
327 if (!waveFormat.SetSize(mmSubChunk.cksize))
328 return FALSE;
330 // Read the format chunk
331 if (!Read(waveFormat.GetPointer(), waveFormat.GetSize()))
332 return FALSE;
334 // Ascend out of the format subchunk
335 Ascend(mmSubChunk);
337 // Find the data subchunk
338 mmSubChunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
339 if (!Descend(MMIO_FINDCHUNK, mmSubChunk, &mmParentChunk))
340 return FALSE;
342 // Get the size of the data subchunk
343 if (mmSubChunk.cksize == 0) {
344 dwLastError = MMSYSERR_INVALPARAM;
345 return FALSE;
348 dataSize = mmSubChunk.cksize;
349 return TRUE;
353 BOOL PMultiMediaFile::Open(const PFilePath & filename,
354 DWORD dwOpenFlags,
355 LPMMIOINFO lpmmioinfo)
357 MMIOINFO local_mmioinfo;
358 if (lpmmioinfo == NULL) {
359 lpmmioinfo = &local_mmioinfo;
360 memset(lpmmioinfo, 0, sizeof(local_mmioinfo));
363 hmmio = mmioOpen((char *)(const char *)filename, lpmmioinfo, dwOpenFlags);
365 dwLastError = lpmmioinfo->wErrorRet;
367 return hmmio != NULL;
371 BOOL PMultiMediaFile::Close(UINT wFlags)
373 if (hmmio == NULL)
374 return FALSE;
376 mmioClose(hmmio, wFlags);
377 hmmio = NULL;
378 return TRUE;
382 BOOL PMultiMediaFile::Ascend(MMCKINFO & ckinfo, UINT wFlags)
384 dwLastError = mmioAscend(hmmio, &ckinfo, wFlags);
385 return dwLastError == MMSYSERR_NOERROR;
389 BOOL PMultiMediaFile::Descend(UINT wFlags, MMCKINFO & ckinfo, LPMMCKINFO lpckParent)
391 dwLastError = mmioDescend(hmmio, &ckinfo, lpckParent, wFlags);
392 return dwLastError == MMSYSERR_NOERROR;
396 BOOL PMultiMediaFile::Read(void * data, PINDEX len)
398 return mmioRead(hmmio, (char *)data, len) == len;
402 BOOL PMultiMediaFile::CreateChunk(MMCKINFO & ckinfo, UINT wFlags)
404 dwLastError = mmioCreateChunk(hmmio, &ckinfo, wFlags);
405 return dwLastError == MMSYSERR_NOERROR;
409 BOOL PMultiMediaFile::Write(const void * data, PINDEX len)
411 return mmioWrite(hmmio, (char *)data, len) == len;
415 ///////////////////////////////////////////////////////////////////////////////
417 PWaveFormat::PWaveFormat()
419 size = 0;
420 waveFormat = NULL;
424 PWaveFormat::~PWaveFormat()
426 if (waveFormat != NULL)
427 free(waveFormat);
431 PWaveFormat::PWaveFormat(const PWaveFormat & fmt)
433 size = fmt.size;
434 waveFormat = (WAVEFORMATEX *)malloc(size);
435 PAssert(waveFormat != NULL, POutOfMemory);
437 memcpy(waveFormat, fmt.waveFormat, size);
441 PWaveFormat & PWaveFormat::operator=(const PWaveFormat & fmt)
443 if (this == &fmt)
444 return *this;
446 if (waveFormat != NULL)
447 free(waveFormat);
449 size = fmt.size;
450 waveFormat = (WAVEFORMATEX *)malloc(size);
451 PAssert(waveFormat != NULL, POutOfMemory);
453 memcpy(waveFormat, fmt.waveFormat, size);
454 return *this;
458 void PWaveFormat::PrintOn(ostream & out) const
460 if (waveFormat == NULL)
461 out << "<null>";
462 else {
463 out << waveFormat->wFormatTag << ','
464 << waveFormat->nChannels << ','
465 << waveFormat->nSamplesPerSec << ','
466 << waveFormat->nAvgBytesPerSec << ','
467 << waveFormat->nBlockAlign << ','
468 << waveFormat->wBitsPerSample;
469 if (waveFormat->cbSize > 0) {
470 out << hex << setfill('0');
471 const BYTE * ptr = (const BYTE *)&waveFormat[1];
472 for (PINDEX i = 0; i < waveFormat->cbSize; i++)
473 out << ',' << setw(2) << (unsigned)*ptr++;
474 out << dec << setfill(' ');
480 void PWaveFormat::ReadFrom(istream &)
485 void PWaveFormat::SetFormat(unsigned numChannels,
486 unsigned sampleRate,
487 unsigned bitsPerSample)
489 PAssert(numChannels == 1 || numChannels == 2, PInvalidParameter);
490 PAssert(bitsPerSample == 8 || bitsPerSample == 16, PInvalidParameter);
492 if (waveFormat != NULL)
493 free(waveFormat);
495 size = sizeof(WAVEFORMATEX);
496 waveFormat = (WAVEFORMATEX *)malloc(sizeof(WAVEFORMATEX));
497 PAssert(waveFormat != NULL, POutOfMemory);
499 waveFormat->wFormatTag = WAVE_FORMAT_PCM;
500 waveFormat->nChannels = (WORD)numChannels;
501 waveFormat->nSamplesPerSec = sampleRate;
502 waveFormat->wBitsPerSample = (WORD)bitsPerSample;
503 waveFormat->nBlockAlign = (WORD)(numChannels*(bitsPerSample+7)/8);
504 waveFormat->nAvgBytesPerSec = waveFormat->nSamplesPerSec*waveFormat->nBlockAlign;
505 waveFormat->cbSize = 0;
509 void PWaveFormat::SetFormat(const void * data, PINDEX size)
511 SetSize(size);
512 memcpy(waveFormat, data, size);
516 BOOL PWaveFormat::SetSize(PINDEX sz)
518 if (waveFormat != NULL)
519 free(waveFormat);
521 size = sz;
522 if (sz == 0)
523 waveFormat = NULL;
524 else {
525 if (sz < sizeof(WAVEFORMATEX))
526 sz = sizeof(WAVEFORMATEX);
527 waveFormat = (WAVEFORMATEX *)calloc(sz, 1);
528 waveFormat->cbSize = (WORD)(sz - sizeof(WAVEFORMATEX));
531 return waveFormat != NULL;
535 ///////////////////////////////////////////////////////////////////////////////
537 PSound::PSound(unsigned channels,
538 unsigned samplesPerSecond,
539 unsigned bitsPerSample,
540 PINDEX bufferSize,
541 const BYTE * buffer)
543 encoding = 0;
544 numChannels = channels;
545 sampleRate = samplesPerSecond;
546 sampleSize = bitsPerSample;
547 SetSize(bufferSize);
548 if (buffer != NULL)
549 memcpy(GetPointer(), buffer, bufferSize);
553 PSound::PSound(const PFilePath & filename)
555 encoding = 0;
556 numChannels = 1;
557 sampleRate = 8000;
558 sampleSize = 16;
559 Load(filename);
563 PSound & PSound::operator=(const PBYTEArray & data)
565 PBYTEArray::operator=(data);
566 return *this;
570 void PSound::SetFormat(unsigned channels,
571 unsigned samplesPerSecond,
572 unsigned bitsPerSample)
574 encoding = 0;
575 numChannels = channels;
576 sampleRate = samplesPerSecond;
577 sampleSize = bitsPerSample;
578 formatInfo.SetSize(0);
582 BOOL PSound::Load(const PFilePath & filename)
584 // Open wave file
585 PMultiMediaFile mmio;
586 PWaveFormat waveFormat;
587 DWORD dataSize;
588 if (!mmio.OpenWaveFile(filename, waveFormat, dataSize)) {
589 dwLastError = mmio.GetLastError();
590 return FALSE;
593 encoding = waveFormat->wFormatTag;
594 numChannels = waveFormat->nChannels;
595 sampleRate = waveFormat->nSamplesPerSec;
596 sampleSize = waveFormat->wBitsPerSample;
598 if (encoding != 0) {
599 PINDEX formatSize = waveFormat->cbSize + sizeof(WAVEFORMATEX);
600 memcpy(formatInfo.GetPointer(formatSize), waveFormat, formatSize);
603 // Allocate and lock memory for the waveform data.
604 if (!SetSize(dataSize)) {
605 dwLastError = MMSYSERR_NOMEM;
606 return FALSE;
609 // Read the waveform data subchunk
610 if (!mmio.Read(GetPointer(), GetSize())) {
611 dwLastError = mmio.GetLastError();
612 return FALSE;
615 return TRUE;
619 BOOL PSound::Save(const PFilePath & filename)
621 PWaveFormat waveFormat;
622 if (encoding == 0)
623 waveFormat.SetFormat(numChannels, sampleRate, sampleSize);
624 else {
625 waveFormat.SetSize(GetFormatInfoSize());
626 memcpy(waveFormat.GetPointer(), GetFormatInfoData(), GetFormatInfoSize());
629 // Open wave file
630 PMultiMediaFile mmio;
631 if (!mmio.CreateWaveFile(filename, waveFormat, GetSize())) {
632 dwLastError = mmio.GetLastError();
633 return FALSE;
636 if (!mmio.Write(GetPointer(), GetSize())) {
637 dwLastError = mmio.GetLastError();
638 return FALSE;
641 return TRUE;
645 BOOL PSound::Play()
647 PSoundChannel channel(PSoundChannel::GetDefaultDevice(PSoundChannel::Player),
648 PSoundChannel::Player);
649 if (!channel.IsOpen())
650 return FALSE;
652 return channel.PlaySound(*this, TRUE);
655 BOOL PSound::Play(const PString & device)
658 PSoundChannel channel(device,
659 PSoundChannel::Player);
660 if (!channel.IsOpen())
661 return FALSE;
663 return channel.PlaySound(*this, TRUE);
666 BOOL PSound::PlayFile(const PFilePath & file, BOOL wait)
668 return ::PlaySound(file, NULL, SND_FILENAME|(wait ? SND_SYNC : SND_ASYNC));
672 ///////////////////////////////////////////////////////////////////////////////
674 PWaveBuffer::PWaveBuffer(PINDEX sz)
675 : PBYTEArray(sz)
677 hWaveOut = NULL;
678 hWaveIn = NULL;
679 header.dwFlags = WHDR_DONE;
683 PWaveBuffer::~PWaveBuffer()
685 Release();
689 PWaveBuffer & PWaveBuffer::operator=(const PSound & sound)
691 PBYTEArray::operator=(sound);
692 return *this;
696 void PWaveBuffer::PrepareCommon(PINDEX count)
698 Release();
700 memset(&header, 0, sizeof(header));
701 header.lpData = (char *)GetPointer();
702 header.dwBufferLength = count;
703 header.dwUser = (DWORD)this;
707 DWORD PWaveBuffer::Prepare(HWAVEOUT hOut, PINDEX & count)
709 // Set up WAVEHDR structure and prepare it to be written to wave device
710 if (count > GetSize())
711 count = GetSize();
713 PrepareCommon(count);
714 hWaveOut = hOut;
715 return waveOutPrepareHeader(hWaveOut, &header, sizeof(header));
719 DWORD PWaveBuffer::Prepare(HWAVEIN hIn)
721 // Set up WAVEHDR structure and prepare it to be read from wave device
722 PrepareCommon(GetSize());
723 hWaveIn = hIn;
724 return waveInPrepareHeader(hWaveIn, &header, sizeof(header));
728 DWORD PWaveBuffer::Release()
730 DWORD err = MMSYSERR_NOERROR;
732 // There seems to be some pathalogical cases where on an Abort() call the buffers
733 // still are "in use", even though waveOutReset() was called. So wait until the
734 // sound driver has finished with the buffer before releasing it.
736 if (hWaveOut != NULL) {
737 if ((err = waveOutUnprepareHeader(hWaveOut, &header, sizeof(header))) == WAVERR_STILLPLAYING)
738 return err;
739 hWaveOut = NULL;
742 if (hWaveIn != NULL) {
743 if ((err = waveInUnprepareHeader(hWaveIn, &header, sizeof(header))) == WAVERR_STILLPLAYING)
744 return err;
745 hWaveIn = NULL;
748 header.dwFlags |= WHDR_DONE;
749 return err;
753 ///////////////////////////////////////////////////////////////////////////////
759 PSoundChannelWin32::PSoundChannelWin32()
761 Construct();
765 PSoundChannelWin32::PSoundChannelWin32(const PString & device,
766 Directions dir,
767 unsigned numChannels,
768 unsigned sampleRate,
769 unsigned bitsPerSample)
771 Construct();
772 Open(device, dir, numChannels, sampleRate, bitsPerSample);
776 void PSoundChannelWin32::Construct()
778 direction = Player;
779 hWaveOut = NULL;
780 hWaveIn = NULL;
781 hEventDone = CreateEvent(NULL, FALSE, FALSE, NULL);
783 waveFormat.SetFormat(1, 8000, 16);
785 bufferByteOffset = P_MAX_INDEX;
787 SetBuffers(32768, 3);
791 PSoundChannelWin32::~PSoundChannelWin32()
793 Close();
795 if (hEventDone != NULL)
796 CloseHandle(hEventDone);
800 PString PSoundChannelWin32::GetName() const
802 return deviceName;
806 PStringArray PSoundChannelWin32::GetDeviceNames(Directions dir)
808 PStringArray array;
810 unsigned numDevs, id;
812 switch (dir) {
813 case Player :
814 numDevs = waveOutGetNumDevs();
815 for (id = 0; id < numDevs; id++) {
816 WAVEOUTCAPS caps;
817 if (waveOutGetDevCaps(id, &caps, sizeof(caps)) == 0)
818 array[array.GetSize()] = caps.szPname;
820 break;
822 case Recorder :
823 numDevs = waveInGetNumDevs();
824 for (id = 0; id < numDevs; id++) {
825 WAVEINCAPS caps;
826 if (waveInGetDevCaps(id, &caps, sizeof(caps)) == 0)
827 array[array.GetSize()] = caps.szPname;
829 break;
832 return array;
836 PString PSoundChannelWin32::GetDefaultDevice(Directions dir)
838 RegistryKey registry("HKEY_CURRENT_USER\\Software\\Microsoft\\Multimedia\\Sound Mapper",
839 RegistryKey::ReadOnly);
841 PString str;
843 if (dir == Player) {
844 if (!registry.QueryValue("Playback", str)) {
845 WAVEOUTCAPS caps;
846 if (waveOutGetDevCaps(0, &caps, sizeof(caps)) == 0)
847 str = caps.szPname;
850 else {
851 if (!registry.QueryValue("Record", str)) {
852 WAVEINCAPS caps;
853 if (waveInGetDevCaps(0, &caps, sizeof(caps)) == 0)
854 str = caps.szPname;
858 return str;
861 BOOL PSoundChannelWin32::GetDeviceID(const PString & device, Directions dir, unsigned& id)
863 BOOL bad = TRUE;
865 if (device[0] == '#') {
866 id = device.Mid(1).AsUnsigned();
867 switch (dir) {
868 case Player :
869 if (id < waveOutGetNumDevs()) {
870 WAVEOUTCAPS caps;
871 if (waveOutGetDevCaps(id, &caps, sizeof(caps)) == 0) {
872 deviceName = caps.szPname;
873 bad = FALSE;
876 break;
878 case Recorder :
879 if (id < waveInGetNumDevs()) {
880 WAVEINCAPS caps;
881 if (waveInGetDevCaps(id, &caps, sizeof(caps)) == 0) {
882 deviceName = caps.szPname;
883 bad = FALSE;
886 break;
889 else {
890 switch (dir) {
891 case Player :
892 for (id = 0; id < waveOutGetNumDevs(); id++) {
893 WAVEOUTCAPS caps;
894 if (waveOutGetDevCaps(id, &caps, sizeof(caps)) == 0 &&
895 strcasecmp(caps.szPname, device) == 0) {
896 deviceName = caps.szPname;
897 bad = FALSE;
898 break;
901 break;
903 case Recorder :
904 for (id = 0; id < waveInGetNumDevs(); id++) {
905 WAVEINCAPS caps;
906 if (waveInGetDevCaps(id, &caps, sizeof(caps)) == 0 &&
907 strcasecmp(caps.szPname, device) == 0) {
908 deviceName = caps.szPname;
909 bad = FALSE;
910 break;
913 break;
917 if (bad)
918 return SetErrorValues(NotFound, MMSYSERR_BADDEVICEID|PWIN32ErrorFlag);
920 return TRUE;
923 BOOL PSoundChannelWin32::Open(const PString & device,
924 Directions dir,
925 unsigned numChannels,
926 unsigned sampleRate,
927 unsigned bitsPerSample)
929 Close();
930 unsigned id = 0;
932 if( !GetDeviceID(device, dir, id) )
933 return FALSE;
935 waveFormat.SetFormat(numChannels, sampleRate, bitsPerSample);
937 direction = dir;
938 return OpenDevice(id);
941 BOOL PSoundChannelWin32::Open(const PString & device,
942 Directions dir,
943 const PWaveFormat& format)
945 Close();
946 unsigned id = 0;
948 if( !GetDeviceID(device, dir, id) )
949 return FALSE;
951 waveFormat = format;
953 direction = dir;
954 return OpenDevice(id);
957 BOOL PSoundChannelWin32::OpenDevice(unsigned id)
959 Close();
961 PWaitAndSignal mutex(bufferMutex);
963 bufferByteOffset = P_MAX_INDEX;
964 bufferIndex = 0;
966 WAVEFORMATEX* format = (WAVEFORMATEX*) waveFormat;
968 DWORD osError = MMSYSERR_BADDEVICEID;
969 switch (direction) {
970 case Player :
971 osError = waveOutOpen(&hWaveOut, id, format,
972 (DWORD)hEventDone, 0, CALLBACK_EVENT);
973 break;
975 case Recorder :
976 osError = waveInOpen(&hWaveIn, id, format,
977 (DWORD)hEventDone, 0, CALLBACK_EVENT);
978 break;
981 if (osError != MMSYSERR_NOERROR)
982 return SetErrorValues(NotFound, osError|PWIN32ErrorFlag);
984 os_handle = id;
985 return TRUE;
988 BOOL PSoundChannelWin32::IsOpen() const
990 return os_handle >= 0;
993 BOOL PSoundChannelWin32::SetFormat(unsigned numChannels,
994 unsigned sampleRate,
995 unsigned bitsPerSample)
997 Abort();
999 waveFormat.SetFormat(numChannels, sampleRate, bitsPerSample);
1001 return OpenDevice(os_handle);
1005 BOOL PSoundChannelWin32::SetFormat(const PWaveFormat & format)
1007 Abort();
1009 waveFormat = format;
1011 return OpenDevice(os_handle);
1015 unsigned PSoundChannelWin32::GetChannels() const
1017 return waveFormat->nChannels;
1021 unsigned PSoundChannelWin32::GetSampleRate() const
1023 return waveFormat->nSamplesPerSec;
1027 unsigned PSoundChannelWin32::GetSampleSize() const
1029 return waveFormat->wBitsPerSample;
1033 BOOL PSoundChannelWin32::Close()
1035 if (!IsOpen())
1036 return SetErrorValues(NotOpen, EBADF);
1038 Abort();
1040 if (hWaveOut != NULL) {
1041 while (waveOutClose(hWaveOut) == WAVERR_STILLPLAYING)
1042 waveOutReset(hWaveOut);
1043 hWaveOut = NULL;
1046 if (hWaveIn != NULL) {
1047 while (waveInClose(hWaveIn) == WAVERR_STILLPLAYING)
1048 waveInReset(hWaveIn);
1049 hWaveIn = NULL;
1052 Abort();
1054 os_handle = -1;
1055 return TRUE;
1059 BOOL PSoundChannelWin32::SetBuffers(PINDEX size, PINDEX count)
1061 Abort();
1063 PAssert(size > 0 && count > 0, PInvalidParameter);
1065 BOOL ok = TRUE;
1067 PWaitAndSignal mutex(bufferMutex);
1069 if (!buffers.SetSize(count))
1070 ok = FALSE;
1071 else {
1072 for (PINDEX i = 0; i < count; i++) {
1073 if (buffers.GetAt(i) == NULL)
1074 buffers.SetAt(i, new PWaveBuffer(size));
1075 if (!buffers[i].SetSize(size))
1076 ok = FALSE;
1080 bufferByteOffset = P_MAX_INDEX;
1081 bufferIndex = 0;
1083 return ok;
1087 BOOL PSoundChannelWin32::GetBuffers(PINDEX & size, PINDEX & count)
1089 PWaitAndSignal mutex(bufferMutex);
1091 count = buffers.GetSize();
1093 if (count == 0)
1094 size = 0;
1095 else
1096 size = buffers[0].GetSize();
1098 return TRUE;
1102 BOOL PSoundChannelWin32::Write(const void * data, PINDEX size)
1104 lastWriteCount = 0;
1106 if (hWaveOut == NULL)
1107 return SetErrorValues(NotOpen, EBADF, LastWriteError);
1109 const BYTE * ptr = (const BYTE *)data;
1111 bufferMutex.Wait();
1113 DWORD osError = MMSYSERR_NOERROR;
1114 while (size > 0) {
1115 PWaveBuffer & buffer = buffers[bufferIndex];
1116 while ((buffer.header.dwFlags&WHDR_DONE) == 0) {
1117 bufferMutex.Signal();
1118 // No free buffers, so wait for one
1119 if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
1120 return SetErrorValues(Miscellaneous, ::GetLastError()|PWIN32ErrorFlag, LastWriteError);
1121 bufferMutex.Wait();
1124 // Can't write more than a buffer full
1125 PINDEX count = size;
1126 if ((osError = buffer.Prepare(hWaveOut, count)) != MMSYSERR_NOERROR)
1127 break;
1129 memcpy(buffer.GetPointer(), ptr, count);
1131 if ((osError = waveOutWrite(hWaveOut, &buffer.header, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
1132 break;
1134 bufferIndex = (bufferIndex+1)%buffers.GetSize();
1135 lastWriteCount += count;
1136 size -= count;
1137 ptr += count;
1140 bufferMutex.Signal();
1142 if (size != 0)
1143 return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastWriteError);
1145 return TRUE;
1149 BOOL PSoundChannelWin32::PlaySound(const PSound & sound, BOOL wait)
1151 Abort();
1153 BOOL ok = FALSE;
1155 PINDEX bufferSize;
1156 PINDEX bufferCount;
1157 GetBuffers(bufferSize, bufferCount);
1159 unsigned numChannels = waveFormat->nChannels;
1160 unsigned sampleRate = waveFormat->nSamplesPerSec;
1161 unsigned bitsPerSample = waveFormat->wBitsPerSample;
1162 if (sound.GetEncoding() == 0)
1163 ok = SetFormat(sound.GetChannels(), sound.GetSampleRate(), sound.GetSampleSize());
1164 else {
1165 waveFormat.SetFormat(sound.GetFormatInfoData(), sound.GetFormatInfoSize());
1166 ok = OpenDevice(os_handle);
1169 if (ok) {
1170 bufferMutex.Wait();
1172 // To avoid lots of copying of sound data, we fake the PSound buffer into
1173 // the internal buffers and play directly from the PSound object.
1174 buffers.SetSize(1);
1175 PWaveBuffer & buffer = buffers[0];
1176 buffer = sound;
1178 DWORD osError;
1179 PINDEX count = sound.GetSize();
1180 if ((osError = buffer.Prepare(hWaveOut, count)) == MMSYSERR_NOERROR &&
1181 (osError = waveOutWrite(hWaveOut, &buffer.header, sizeof(WAVEHDR))) == MMSYSERR_NOERROR) {
1182 if (wait)
1183 ok = WaitForPlayCompletion();
1185 else {
1186 SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastWriteError);
1187 ok = FALSE;
1190 bufferMutex.Signal();
1193 SetFormat(numChannels, sampleRate, bitsPerSample);
1194 SetBuffers(bufferSize, bufferCount);
1195 return ok;
1199 BOOL PSoundChannelWin32::PlayFile(const PFilePath & filename, BOOL wait)
1201 Abort();
1203 PMultiMediaFile mmio;
1204 PWaveFormat fileFormat;
1205 DWORD dataSize;
1206 if (!mmio.OpenWaveFile(filename, fileFormat, dataSize))
1207 return SetErrorValues(NotOpen, mmio.GetLastError()|PWIN32ErrorFlag, LastWriteError);
1209 // Save old format and set to one loaded from file.
1210 unsigned numChannels = waveFormat->nChannels;
1211 unsigned sampleRate = waveFormat->nSamplesPerSec;
1212 unsigned bitsPerSample = waveFormat->wBitsPerSample;
1213 waveFormat = fileFormat;
1214 if (!OpenDevice(os_handle)) {
1215 SetFormat(numChannels, sampleRate, bitsPerSample);
1216 return FALSE;
1219 bufferMutex.Wait();
1221 DWORD osError = MMSYSERR_NOERROR;
1222 while (dataSize > 0) {
1223 PWaveBuffer & buffer = buffers[bufferIndex];
1224 while ((buffer.header.dwFlags&WHDR_DONE) == 0) {
1225 bufferMutex.Signal();
1226 // No free buffers, so wait for one
1227 if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0) {
1228 osError = ::GetLastError();
1229 SetFormat(numChannels, sampleRate, bitsPerSample);
1230 return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastWriteError);
1232 bufferMutex.Wait();
1235 // Can't write more than a buffer full
1236 PINDEX count = dataSize;
1237 if ((osError = buffer.Prepare(hWaveOut, count)) != MMSYSERR_NOERROR)
1238 break;
1240 // Read the waveform data subchunk
1241 if (!mmio.Read(buffer.GetPointer(), count)) {
1242 osError = mmio.GetLastError();
1243 break;
1246 if ((osError = waveOutWrite(hWaveOut, &buffer.header, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
1247 break;
1249 bufferIndex = (bufferIndex+1)%buffers.GetSize();
1250 dataSize -= count;
1253 bufferMutex.Signal();
1255 if (osError != MMSYSERR_NOERROR) {
1256 SetFormat(numChannels, sampleRate, bitsPerSample);
1257 return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastWriteError);
1260 if (dataSize == 0 && wait) {
1261 WaitForPlayCompletion();
1262 SetFormat(numChannels, sampleRate, bitsPerSample);
1265 return TRUE;
1269 BOOL PSoundChannelWin32::HasPlayCompleted()
1271 PWaitAndSignal mutex(bufferMutex);
1273 for (PINDEX i = 0; i < buffers.GetSize(); i++) {
1274 if ((buffers[i].header.dwFlags&WHDR_DONE) == 0)
1275 return FALSE;
1278 return TRUE;
1282 BOOL PSoundChannelWin32::WaitForPlayCompletion()
1284 while (!HasPlayCompleted()) {
1285 if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
1286 return FALSE;
1289 return TRUE;
1293 BOOL PSoundChannelWin32::StartRecording()
1295 PWaitAndSignal mutex(bufferMutex);
1297 // See if has started already.
1298 if (bufferByteOffset != P_MAX_INDEX)
1299 return TRUE;
1301 DWORD osError;
1303 // Start the first read, queue all the buffers
1304 for (PINDEX i = 0; i < buffers.GetSize(); i++) {
1305 PWaveBuffer & buffer = buffers[i];
1306 if ((osError = buffer.Prepare(hWaveIn)) != MMSYSERR_NOERROR)
1307 return FALSE;
1308 if ((osError = waveInAddBuffer(hWaveIn, &buffer.header, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
1309 return FALSE;
1312 bufferByteOffset = 0;
1314 if ((osError = waveInStart(hWaveIn)) == MMSYSERR_NOERROR) // start recording
1315 return TRUE;
1317 bufferByteOffset = P_MAX_INDEX;
1318 return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastReadError);
1322 BOOL PSoundChannelWin32::Read(void * data, PINDEX size)
1324 lastReadCount = 0;
1326 if (hWaveIn == NULL)
1327 return SetErrorValues(NotOpen, EBADF, LastReadError);
1329 if (!WaitForRecordBufferFull())
1330 return FALSE;
1332 PWaitAndSignal mutex(bufferMutex);
1334 // Check to see if Abort() was called in another thread
1335 if (bufferByteOffset == P_MAX_INDEX)
1336 return FALSE;
1338 PWaveBuffer & buffer = buffers[bufferIndex];
1340 lastReadCount = buffer.header.dwBytesRecorded - bufferByteOffset;
1341 if (lastReadCount > size)
1342 lastReadCount = size;
1344 memcpy(data, &buffer[bufferByteOffset], lastReadCount);
1346 bufferByteOffset += lastReadCount;
1347 if (bufferByteOffset >= (PINDEX)buffer.header.dwBytesRecorded) {
1348 DWORD osError;
1349 if ((osError = buffer.Prepare(hWaveIn)) != MMSYSERR_NOERROR)
1350 return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastReadError);
1351 if ((osError = waveInAddBuffer(hWaveIn, &buffer.header, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
1352 return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag, LastReadError);
1354 bufferIndex = (bufferIndex+1)%buffers.GetSize();
1355 bufferByteOffset = 0;
1358 return TRUE;
1362 BOOL PSoundChannelWin32::RecordSound(PSound & sound)
1364 if (!WaitForAllRecordBuffersFull())
1365 return FALSE;
1367 sound.SetFormat(waveFormat->nChannels,
1368 waveFormat->nSamplesPerSec,
1369 waveFormat->wBitsPerSample);
1371 PWaitAndSignal mutex(bufferMutex);
1373 if (buffers.GetSize() == 1 &&
1374 (PINDEX)buffers[0].header.dwBytesRecorded == buffers[0].GetSize())
1375 sound = buffers[0];
1376 else {
1377 PINDEX totalSize = 0;
1378 PINDEX i;
1379 for (i = 0; i < buffers.GetSize(); i++)
1380 totalSize += buffers[i].header.dwBytesRecorded;
1382 if (!sound.SetSize(totalSize))
1383 return SetErrorValues(NoMemory, ENOMEM, LastReadError);
1385 BYTE * ptr = sound.GetPointer();
1386 for (i = 0; i < buffers.GetSize(); i++) {
1387 PINDEX sz = buffers[i].header.dwBytesRecorded;
1388 memcpy(ptr, buffers[i], sz);
1389 ptr += sz;
1393 return TRUE;
1397 BOOL PSoundChannelWin32::RecordFile(const PFilePath & filename)
1399 if (!WaitForAllRecordBuffersFull())
1400 return FALSE;
1402 PWaitAndSignal mutex(bufferMutex);
1404 PINDEX dataSize = 0;
1405 PINDEX i;
1406 for (i = 0; i < buffers.GetSize(); i++)
1407 dataSize += buffers[i].header.dwBytesRecorded;
1409 PMultiMediaFile mmio;
1410 if (!mmio.CreateWaveFile(filename, waveFormat, dataSize))
1411 return SetErrorValues(Miscellaneous, mmio.GetLastError()|PWIN32ErrorFlag, LastReadError);
1413 for (i = 0; i < buffers.GetSize(); i++) {
1414 if (!mmio.Write(buffers[i], buffers[i].header.dwBytesRecorded))
1415 return SetErrorValues(Miscellaneous, mmio.GetLastError()|PWIN32ErrorFlag, LastReadError);
1418 return TRUE;
1422 BOOL PSoundChannelWin32::IsRecordBufferFull()
1424 PWaitAndSignal mutex(bufferMutex);
1426 return (buffers[bufferIndex].header.dwFlags&WHDR_DONE) != 0 &&
1427 buffers[bufferIndex].header.dwBytesRecorded > 0;
1431 BOOL PSoundChannelWin32::AreAllRecordBuffersFull()
1433 PWaitAndSignal mutex(bufferMutex);
1435 for (PINDEX i = 0; i < buffers.GetSize(); i++) {
1436 if ((buffers[i].header.dwFlags&WHDR_DONE) == 0 ||
1437 buffers[i].header.dwBytesRecorded == 0)
1438 return FALSE;
1441 return TRUE;
1445 BOOL PSoundChannelWin32::WaitForRecordBufferFull()
1447 if (!StartRecording()) // Start the first read, queue all the buffers
1448 return FALSE;
1450 while (!IsRecordBufferFull()) {
1451 if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
1452 return FALSE;
1454 PWaitAndSignal mutex(bufferMutex);
1455 if (bufferByteOffset == P_MAX_INDEX)
1456 return FALSE;
1459 return TRUE;
1463 BOOL PSoundChannelWin32::WaitForAllRecordBuffersFull()
1465 if (!StartRecording()) // Start the first read, queue all the buffers
1466 return FALSE;
1468 while (!AreAllRecordBuffersFull()) {
1469 if (WaitForSingleObject(hEventDone, INFINITE) != WAIT_OBJECT_0)
1470 return FALSE;
1472 PWaitAndSignal mutex(bufferMutex);
1473 if (bufferByteOffset == P_MAX_INDEX)
1474 return FALSE;
1477 return TRUE;
1481 BOOL PSoundChannelWin32::Abort()
1483 DWORD osError = MMSYSERR_NOERROR;
1485 if (hWaveOut != NULL)
1486 osError = waveOutReset(hWaveOut);
1488 if (hWaveIn != NULL)
1489 osError = waveInReset(hWaveIn);
1492 PWaitAndSignal mutex(bufferMutex);
1494 if (hWaveOut != NULL || hWaveIn != NULL) {
1495 for (PINDEX i = 0; i < buffers.GetSize(); i++) {
1496 while (buffers[i].Release() == WAVERR_STILLPLAYING) {
1497 if (hWaveOut != NULL)
1498 waveOutReset(hWaveOut);
1499 if (hWaveIn != NULL)
1500 waveInReset(hWaveIn);
1505 bufferByteOffset = P_MAX_INDEX;
1506 bufferIndex = 0;
1508 // Signal any threads waiting on this event, they should then check
1509 // the bufferByteOffset variable for an abort.
1510 SetEvent(hEventDone);
1513 if (osError != MMSYSERR_NOERROR)
1514 return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag);
1516 return TRUE;
1520 PString PSoundChannelWin32::GetErrorText(ErrorGroup group) const
1522 PString str;
1524 if ((lastErrorNumber[group]&PWIN32ErrorFlag) == 0)
1525 return PChannel::GetErrorText(group);
1527 DWORD osError = lastErrorNumber[group]&~PWIN32ErrorFlag;
1528 if (direction == Recorder) {
1529 if (waveInGetErrorText(osError, str.GetPointer(256), 256) != MMSYSERR_NOERROR)
1530 return PChannel::GetErrorText(group);
1532 else {
1533 if (waveOutGetErrorText(osError, str.GetPointer(256), 256) != MMSYSERR_NOERROR)
1534 return PChannel::GetErrorText(group);
1537 return str;
1541 BOOL PSoundChannelWin32::SetVolume(unsigned newVolume)
1543 if (!IsOpen())
1544 return SetErrorValues(NotOpen, EBADF);
1546 DWORD rawVolume = newVolume*65536/100;
1547 if (rawVolume > 65535)
1548 rawVolume = 65535;
1550 WAVEOUTCAPS caps;
1551 if (waveOutGetDevCaps((UINT) hWaveOut, &caps, sizeof(caps)) == MMSYSERR_NOERROR) {
1552 // If the device does not support L/R volume only the low word matters
1553 if ((caps.dwSupport & WAVECAPS_LRVOLUME) != 0) {
1554 // Mantain balance
1555 DWORD oldVolume = 0;
1556 if (waveOutGetVolume(hWaveOut, &oldVolume) == MMSYSERR_NOERROR) {
1557 // GetVolume() is supposed to return the value we intended to set.
1558 // So do the proper calculations
1559 // 1. (L + R) / 2 = rawVolume -> GetVolume() formula
1560 // 2. L / R = oldL / oldR -> Unmodified balance
1561 // Being:
1562 // oldL = LOWORD(oldVolume)
1563 // oldR = HIWORD(oldVolume)
1565 DWORD rVol, lVol;
1566 DWORD oldL, oldR;
1568 // Old volume values
1569 oldL = LOWORD(oldVolume);
1570 oldR = HIWORD(oldVolume);
1572 lVol = rVol = 0;
1574 // First sort out extreme cases
1575 if ( oldL == oldR )
1576 rVol = lVol = rawVolume;
1577 else if ( oldL == 0 )
1578 rVol = rawVolume;
1579 else if ( oldR == 0 )
1580 lVol = rawVolume;
1581 else {
1582 #ifndef _WIN32_WCE
1583 rVol = ::MulDiv( 2 * rawVolume, oldR, oldL + oldR );
1584 lVol = ::MulDiv( rVol, oldL, oldR );
1585 #else
1586 rVol = 2 * rawVolume * oldR / ( oldL + oldR );
1587 lVol = rVol * oldL / oldR;
1588 #endif
1591 rawVolume = MAKELPARAM(lVol, rVol);
1593 else {
1594 // Couldn't get current volume. Assume centered balance
1595 rawVolume = MAKELPARAM(rawVolume, rawVolume);
1599 else {
1600 // Couldn't get device caps. Assume centered balance
1601 // If the device does not support independant L/R volume
1602 // the high-order word (R) is ignored
1603 rawVolume = MAKELPARAM(rawVolume, rawVolume);
1606 if (direction == Recorder) {
1607 // Does not appear to be an input volume!!
1609 else {
1610 DWORD osError = waveOutSetVolume(hWaveOut, rawVolume);
1611 if (osError != MMSYSERR_NOERROR)
1612 return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag);
1615 return TRUE;
1620 BOOL PSoundChannelWin32::GetVolume(unsigned & oldVolume)
1622 if (!IsOpen())
1623 return SetErrorValues(NotOpen, EBADF);
1625 DWORD rawVolume = 0;
1627 if (direction == Recorder) {
1628 // Does not appear to be an input volume!!
1630 else {
1631 DWORD osError = waveOutGetVolume(hWaveOut, &rawVolume);
1632 if (osError != MMSYSERR_NOERROR)
1633 return SetErrorValues(Miscellaneous, osError|PWIN32ErrorFlag);
1636 WAVEOUTCAPS caps;
1637 if (waveOutGetDevCaps((UINT) hWaveOut, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
1638 (caps.dwSupport & WAVECAPS_LRVOLUME) != 0)
1639 rawVolume = (HIWORD(rawVolume) + LOWORD(rawVolume)) / 2;
1641 oldVolume = rawVolume*100/65536;
1642 return TRUE;
1645 // End of File ///////////////////////////////////////////////////////////////