1 /*
2 * sound.cxx
3 *
4 * Code for pluigns sound device
5 *
6 * Portable Windows Library
7 *
8 * Copyright (c) 2003 Post Increment
9 *
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/
14 *
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.
19 *
20 * The Original Code is Portable Windows Library.
21 *
22 * The Initial Developer of the Original Code is Post Increment
23 *
24 * Contributor(s): Craig Southeren
25 * Snark at GnomeMeeting
26 *
27 * $Revision: 28580 $
28 * $Author: rjongbloed $
29 * $Date: 2012-11-26 01:31:08 -0600 (Mon, 26 Nov 2012) $
30 */
31
32 #ifdef __GNUC__
33 #pragma implementation "sound.h"
34 #endif
35
36 #include <ptlib.h>
37
38 #include <ptlib/sound.h>
39 #include <ptlib/pluginmgr.h>
40 #include <ptclib/delaychan.h>
41
42
43 static const char soundPluginBaseClass[] = "PSoundChannel";
44
45
Create(const PString & type) const46 template <> PSoundChannel * PDevicePluginFactory<PSoundChannel>::Worker::Create(const PString & type) const
47 {
48 return PSoundChannel::CreateChannel(type);
49 }
50
51 typedef PDevicePluginAdapter<PSoundChannel> PDevicePluginSoundChannel;
52 PFACTORY_CREATE(PFactory<PDevicePluginAdapterBase>, PDevicePluginSoundChannel, "PSoundChannel", true);
53
54
GetDriverNames(PPluginManager * pluginMgr)55 PStringArray PSoundChannel::GetDriverNames(PPluginManager * pluginMgr)
56 {
57 if (pluginMgr == NULL)
58 pluginMgr = &PPluginManager::GetPluginManager();
59
60 return pluginMgr->GetPluginsProviding(soundPluginBaseClass);
61 }
62
63
GetDriversDeviceNames(const PString & driverName,PSoundChannel::Directions dir,PPluginManager * pluginMgr)64 PStringArray PSoundChannel::GetDriversDeviceNames(const PString & driverName,
65 PSoundChannel::Directions dir,
66 PPluginManager * pluginMgr)
67 {
68 if (pluginMgr == NULL)
69 pluginMgr = &PPluginManager::GetPluginManager();
70
71 return pluginMgr->GetPluginsDeviceNames(driverName, soundPluginBaseClass, dir);
72 }
73
74
CreateChannel(const PString & driverName,PPluginManager * pluginMgr)75 PSoundChannel * PSoundChannel::CreateChannel(const PString & driverName, PPluginManager * pluginMgr)
76 {
77 if (pluginMgr == NULL)
78 pluginMgr = &PPluginManager::GetPluginManager();
79
80 return (PSoundChannel *)pluginMgr->CreatePluginsDevice(driverName, soundPluginBaseClass, 0);
81 }
82
83
CreateChannelByName(const PString & deviceName,PSoundChannel::Directions dir,PPluginManager * pluginMgr)84 PSoundChannel * PSoundChannel::CreateChannelByName(const PString & deviceName,
85 PSoundChannel::Directions dir,
86 PPluginManager * pluginMgr)
87 {
88 if (pluginMgr == NULL)
89 pluginMgr = &PPluginManager::GetPluginManager();
90
91 return (PSoundChannel *)pluginMgr->CreatePluginsDeviceByName(deviceName, soundPluginBaseClass, dir);
92 }
93
94
CreateOpenedChannel(const PString & driverName,const PString & deviceName,PSoundChannel::Directions dir,unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample,PPluginManager * pluginMgr)95 PSoundChannel * PSoundChannel::CreateOpenedChannel(const PString & driverName,
96 const PString & deviceName,
97 PSoundChannel::Directions dir,
98 unsigned numChannels,
99 unsigned sampleRate,
100 unsigned bitsPerSample,
101 PPluginManager * pluginMgr)
102 {
103 PString adjustedDeviceName = deviceName;
104 PSoundChannel * sndChan;
105 if (driverName.IsEmpty() || driverName == "*") {
106 if (deviceName.IsEmpty() || deviceName == "*")
107 adjustedDeviceName = PSoundChannel::GetDefaultDevice(dir);
108 sndChan = CreateChannelByName(adjustedDeviceName, dir, pluginMgr);
109 }
110 else {
111 if (deviceName.IsEmpty() || deviceName == "*") {
112 PStringArray devices = PSoundChannel::GetDriversDeviceNames(driverName, PSoundChannel::Player);
113 if (devices.IsEmpty())
114 return NULL;
115 adjustedDeviceName = devices[0];
116 }
117 sndChan = CreateChannel(driverName, pluginMgr);
118 }
119
120 if (sndChan != NULL && sndChan->Open(adjustedDeviceName, dir, numChannels, sampleRate, bitsPerSample))
121 return sndChan;
122
123 delete sndChan;
124 return NULL;
125 }
126
127
GetDeviceNames(PSoundChannel::Directions dir,PPluginManager * pluginMgr)128 PStringArray PSoundChannel::GetDeviceNames(PSoundChannel::Directions dir, PPluginManager * pluginMgr)
129 {
130 if (pluginMgr == NULL)
131 pluginMgr = &PPluginManager::GetPluginManager();
132
133 return pluginMgr->GetPluginsDeviceNames("*", soundPluginBaseClass, dir);
134 }
135
136
GetDefaultDevice(Directions dir)137 PString PSoundChannel::GetDefaultDevice(Directions dir)
138 {
139 #ifdef _WIN32
140 RegistryKey registry("HKEY_CURRENT_USER\\Software\\Microsoft\\Multimedia\\Sound Mapper",
141 RegistryKey::ReadOnly);
142
143 PString str;
144
145 if (dir == Player) {
146 if (registry.QueryValue("ConsoleVoiceComPlayback", str) )
147 return str;
148 if (registry.QueryValue("Playback", str))
149 return str;
150 }
151 else {
152 if (registry.QueryValue("ConsoleVoiceComRecord", str))
153 return str;
154 if (registry.QueryValue("Record", str))
155 return str;
156 }
157 #endif
158
159 PStringArray devices = GetDeviceNames(dir);
160
161 if (devices.GetSize() == 0)
162 return PString::Empty();
163
164 for (PINDEX i = 0; i < devices.GetSize(); ++i) {
165 if (!(devices[i] *= "NULL"))
166 return devices[i];
167 }
168
169 return devices[0];
170 }
171
172
PSoundChannel()173 PSoundChannel::PSoundChannel()
174 : m_baseChannel(NULL)
175 , activeDirection(Closed)
176 {
177 }
178
~PSoundChannel()179 PSoundChannel::~PSoundChannel()
180 {
181 delete m_baseChannel;
182 }
183
184
PSoundChannel(const PString & device,Directions dir,unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample)185 PSoundChannel::PSoundChannel(const PString & device,
186 Directions dir,
187 unsigned numChannels,
188 unsigned sampleRate,
189 unsigned bitsPerSample)
190 : m_baseChannel(NULL)
191 , activeDirection(dir)
192 {
193 Open(device, dir, numChannels, sampleRate, bitsPerSample);
194 }
195
196
Open(const PString & devSpec,Directions dir,unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample)197 PBoolean PSoundChannel::Open(const PString & devSpec,
198 Directions dir,
199 unsigned numChannels,
200 unsigned sampleRate,
201 unsigned bitsPerSample)
202 {
203 PString driver, device;
204 PINDEX colon = devSpec.Find(':');
205 if (colon == P_MAX_INDEX)
206 device = devSpec;
207 else {
208 driver = devSpec.Left(colon);
209 device = devSpec.Mid(colon+1).Trim();
210 }
211
212 m_baseMutex.StartWrite();
213
214 delete m_baseChannel;
215 activeDirection = dir;
216
217 m_baseChannel = CreateOpenedChannel(driver, device, dir, numChannels, sampleRate, bitsPerSample);
218 if (m_baseChannel == NULL && !driver.IsEmpty())
219 m_baseChannel = CreateOpenedChannel(PString::Empty(), devSpec, dir, numChannels, sampleRate, bitsPerSample);
220
221 m_baseMutex.EndWrite();
222
223 return m_baseChannel != NULL;
224 }
225
226
GetName() const227 PString PSoundChannel::GetName() const
228 {
229 PReadWaitAndSignal mutex(m_baseMutex);
230 return m_baseChannel != NULL ? m_baseChannel->GetName() : PString::Empty();
231 }
232
233
IsOpen() const234 PBoolean PSoundChannel::IsOpen() const
235 {
236 PReadWaitAndSignal mutex(m_baseMutex);
237 return m_baseChannel != NULL && m_baseChannel->PChannel::IsOpen();
238 }
239
240
Close()241 PBoolean PSoundChannel::Close()
242 {
243 PReadWaitAndSignal mutex(m_baseMutex);
244 return m_baseChannel == NULL || m_baseChannel->Close();
245 }
246
247
GetHandle() const248 int PSoundChannel::GetHandle() const
249 {
250 PReadWaitAndSignal mutex(m_baseMutex);
251 return m_baseChannel == NULL ? -1 : m_baseChannel->PChannel::GetHandle();
252 }
253
254
Abort()255 PBoolean PSoundChannel::Abort()
256 {
257 PReadWaitAndSignal mutex(m_baseMutex);
258 return m_baseChannel == NULL || m_baseChannel->Abort();
259 }
260
261
SetFormat(unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample)262 PBoolean PSoundChannel::SetFormat(unsigned numChannels, unsigned sampleRate, unsigned bitsPerSample)
263 {
264 PReadWaitAndSignal mutex(m_baseMutex);
265 return m_baseChannel != NULL && m_baseChannel->SetFormat(numChannels, sampleRate, bitsPerSample);
266 }
267
268
GetChannels() const269 unsigned PSoundChannel::GetChannels() const
270 {
271 PReadWaitAndSignal mutex(m_baseMutex);
272 return m_baseChannel == NULL ? 0 : m_baseChannel->GetChannels();
273 }
274
275
GetSampleRate() const276 unsigned PSoundChannel::GetSampleRate() const
277 {
278 PReadWaitAndSignal mutex(m_baseMutex);
279 return m_baseChannel == NULL ? 0 : m_baseChannel->GetSampleRate();
280 }
281
282
GetSampleSize() const283 unsigned PSoundChannel::GetSampleSize() const
284 {
285 PReadWaitAndSignal mutex(m_baseMutex);
286 return m_baseChannel == NULL ? 0 : m_baseChannel->GetSampleSize();
287 }
288
289
SetBuffers(PINDEX size,PINDEX count)290 PBoolean PSoundChannel::SetBuffers(PINDEX size, PINDEX count)
291 {
292 PReadWaitAndSignal mutex(m_baseMutex);
293 return m_baseChannel != NULL && m_baseChannel->SetBuffers(size, count);
294 }
295
296
GetBuffers(PINDEX & size,PINDEX & count)297 PBoolean PSoundChannel::GetBuffers(PINDEX & size, PINDEX & count)
298 {
299 PReadWaitAndSignal mutex(m_baseMutex);
300 return m_baseChannel != NULL && m_baseChannel->GetBuffers(size, count);
301 }
302
303
SetVolume(unsigned volume)304 PBoolean PSoundChannel::SetVolume(unsigned volume)
305 {
306 PReadWaitAndSignal mutex(m_baseMutex);
307 return m_baseChannel != NULL && m_baseChannel->SetVolume(volume);
308 }
309
310
GetMute(bool & mute)311 PBoolean PSoundChannel::GetMute(bool & mute)
312 {
313 PReadWaitAndSignal mutex(m_baseMutex);
314 return m_baseChannel != NULL && m_baseChannel->GetMute(mute);
315 }
316
317
SetMute(bool mute)318 PBoolean PSoundChannel::SetMute(bool mute)
319 {
320 PReadWaitAndSignal mutex(m_baseMutex);
321 return m_baseChannel != NULL && m_baseChannel->SetMute(mute);
322 }
323
324
GetVolume(unsigned & volume)325 PBoolean PSoundChannel::GetVolume(unsigned & volume)
326 {
327 PReadWaitAndSignal mutex(m_baseMutex);
328 return m_baseChannel != NULL && m_baseChannel->GetVolume(volume);
329 }
330
331
Write(const void * buf,PINDEX len)332 PBoolean PSoundChannel::Write(const void * buf, PINDEX len)
333 {
334 PAssert(activeDirection == Player, PLogicError);
335
336 if (len == 0)
337 return IsOpen();
338
339 PReadWaitAndSignal mutex(m_baseMutex);
340 return m_baseChannel != NULL && m_baseChannel->Write(buf, len);
341 }
342
Write(const void * buf,PINDEX len,const void *)343 PBoolean PSoundChannel::Write(const void * buf, PINDEX len, const void * /*mark*/)
344 {
345 return Write(buf, len);
346 }
347
GetLastWriteCount() const348 PINDEX PSoundChannel::GetLastWriteCount() const
349 {
350 PReadWaitAndSignal mutex(m_baseMutex);
351 return m_baseChannel != NULL ? m_baseChannel->GetLastWriteCount() : PChannel::GetLastWriteCount();
352 }
353
354
PlaySound(const PSound & sound,PBoolean wait)355 PBoolean PSoundChannel::PlaySound(const PSound & sound, PBoolean wait)
356 {
357 PAssert(activeDirection == Player, PLogicError);
358 PReadWaitAndSignal mutex(m_baseMutex);
359 return m_baseChannel != NULL && m_baseChannel->PlaySound(sound, wait);
360 }
361
362
PlayFile(const PFilePath & file,PBoolean wait)363 PBoolean PSoundChannel::PlayFile(const PFilePath & file, PBoolean wait)
364 {
365 PAssert(activeDirection == Player, PLogicError);
366 PReadWaitAndSignal mutex(m_baseMutex);
367 return m_baseChannel != NULL && m_baseChannel->PlayFile(file, wait);
368 }
369
370
HasPlayCompleted()371 PBoolean PSoundChannel::HasPlayCompleted()
372 {
373 PAssert(activeDirection == Player, PLogicError);
374 PReadWaitAndSignal mutex(m_baseMutex);
375 return m_baseChannel != NULL && m_baseChannel->HasPlayCompleted();
376 }
377
378
WaitForPlayCompletion()379 PBoolean PSoundChannel::WaitForPlayCompletion()
380 {
381 PAssert(activeDirection == Player, PLogicError);
382 PReadWaitAndSignal mutex(m_baseMutex);
383 return m_baseChannel != NULL && m_baseChannel->WaitForPlayCompletion();
384 }
385
386
Read(void * buf,PINDEX len)387 PBoolean PSoundChannel::Read(void * buf, PINDEX len)
388 {
389 PAssert(activeDirection == Recorder, PLogicError);
390
391 if (len == 0)
392 return IsOpen();
393
394 PReadWaitAndSignal mutex(m_baseMutex);
395 return m_baseChannel != NULL && m_baseChannel->Read(buf, len);
396 }
397
398
GetLastReadCount() const399 PINDEX PSoundChannel::GetLastReadCount() const
400 {
401 PReadWaitAndSignal mutex(m_baseMutex);
402 return m_baseChannel != NULL ? m_baseChannel->GetLastReadCount() : PChannel::GetLastReadCount();
403 }
404
405
RecordSound(PSound & sound)406 PBoolean PSoundChannel::RecordSound(PSound & sound)
407 {
408 PAssert(activeDirection == Recorder, PLogicError);
409 PReadWaitAndSignal mutex(m_baseMutex);
410 return m_baseChannel != NULL && m_baseChannel->RecordSound(sound);
411 }
412
413
RecordFile(const PFilePath & file)414 PBoolean PSoundChannel::RecordFile(const PFilePath & file)
415 {
416 PAssert(activeDirection == Recorder, PLogicError);
417 PReadWaitAndSignal mutex(m_baseMutex);
418 return m_baseChannel != NULL && m_baseChannel->RecordFile(file);
419 }
420
421
StartRecording()422 PBoolean PSoundChannel::StartRecording()
423 {
424 PAssert(activeDirection == Recorder, PLogicError);
425 PReadWaitAndSignal mutex(m_baseMutex);
426 return m_baseChannel != NULL && m_baseChannel->StartRecording();
427 }
428
429
IsRecordBufferFull()430 PBoolean PSoundChannel::IsRecordBufferFull()
431 {
432 PAssert(activeDirection == Recorder, PLogicError);
433 PReadWaitAndSignal mutex(m_baseMutex);
434 return m_baseChannel != NULL && m_baseChannel->IsRecordBufferFull();
435 }
436
437
AreAllRecordBuffersFull()438 PBoolean PSoundChannel::AreAllRecordBuffersFull()
439 {
440 PAssert(activeDirection == Recorder, PLogicError);
441 PReadWaitAndSignal mutex(m_baseMutex);
442 return m_baseChannel != NULL && m_baseChannel->AreAllRecordBuffersFull();
443 }
444
445
WaitForRecordBufferFull()446 PBoolean PSoundChannel::WaitForRecordBufferFull()
447 {
448 PAssert(activeDirection == Recorder, PLogicError);
449 PReadWaitAndSignal mutex(m_baseMutex);
450 return m_baseChannel != NULL && m_baseChannel->WaitForRecordBufferFull();
451 }
452
453
WaitForAllRecordBuffersFull()454 PBoolean PSoundChannel::WaitForAllRecordBuffersFull()
455 {
456 PAssert(activeDirection == Recorder, PLogicError);
457 PReadWaitAndSignal mutex(m_baseMutex);
458 return m_baseChannel != NULL && m_baseChannel->WaitForAllRecordBuffersFull();
459 }
460
461
GetDirectionText(Directions dir)462 const char * PSoundChannel::GetDirectionText(Directions dir)
463 {
464 switch (dir) {
465 case Player :
466 return "Playback";
467 case Recorder :
468 return "Recording";
469 case Closed :
470 return "Closed";
471 }
472
473 return "<Unknown>";
474 }
475
476
477 ///////////////////////////////////////////////////////////////////////////
478
479 #if !defined(_WIN32) && !defined(__BEOS__) && !defined(__APPLE__)
480
PSound(unsigned channels,unsigned samplesPerSecond,unsigned bitsPerSample,PINDEX bufferSize,const BYTE * buffer)481 PSound::PSound(unsigned channels,
482 unsigned samplesPerSecond,
483 unsigned bitsPerSample,
484 PINDEX bufferSize,
485 const BYTE * buffer)
486 {
487 encoding = 0;
488 numChannels = channels;
489 sampleRate = samplesPerSecond;
490 sampleSize = bitsPerSample;
491 SetSize(bufferSize);
492 if (buffer != NULL)
493 memcpy(GetPointer(), buffer, bufferSize);
494 }
495
496
PSound(const PFilePath & filename)497 PSound::PSound(const PFilePath & filename)
498 {
499 encoding = 0;
500 numChannels = 1;
501 sampleRate = 8000;
502 sampleSize = 16;
503 Load(filename);
504 }
505
506
operator =(const PBYTEArray & data)507 PSound & PSound::operator=(const PBYTEArray & data)
508 {
509 PBYTEArray::operator=(data);
510 return *this;
511 }
512
513
SetFormat(unsigned channels,unsigned samplesPerSecond,unsigned bitsPerSample)514 void PSound::SetFormat(unsigned channels,
515 unsigned samplesPerSecond,
516 unsigned bitsPerSample)
517 {
518 encoding = 0;
519 numChannels = channels;
520 sampleRate = samplesPerSecond;
521 sampleSize = bitsPerSample;
522 formatInfo.SetSize(0);
523 }
524
525
Load(const PFilePath &)526 PBoolean PSound::Load(const PFilePath & /*filename*/)
527 {
528 return PFalse;
529 }
530
531
Save(const PFilePath &)532 PBoolean PSound::Save(const PFilePath & /*filename*/)
533 {
534 return PFalse;
535 }
536
537
Play()538 PBoolean PSound::Play()
539 {
540 return Play(PSoundChannel::GetDefaultDevice(PSoundChannel::Player));
541 }
542
543
Play(const PString & device)544 PBoolean PSound::Play(const PString & device)
545 {
546
547 PSoundChannel channel(device, PSoundChannel::Player);
548 if (!channel.IsOpen())
549 return PFalse;
550
551 return channel.PlaySound(*this, PTrue);
552 }
553
554
PlayFile(const PFilePath & file,PBoolean wait)555 PBoolean PSound::PlayFile(const PFilePath & file, PBoolean wait)
556 {
557 PSoundChannel channel(PSoundChannel::GetDefaultDevice(PSoundChannel::Player),
558 PSoundChannel::Player);
559 if (!channel.IsOpen())
560 return PFalse;
561
562 return channel.PlayFile(file, wait);
563 }
564
565
566 #endif //_WIN32
567
568 ///////////////////////////////////////////////////////////////////////////
569
570 static const PConstString NullAudio("Null Audio");
571
572 class PSoundChannelNull : public PSoundChannel
573 {
574 PCLASSINFO(PSoundChannelNull, PSoundChannel);
575 public:
PSoundChannelNull()576 PSoundChannelNull()
577 : m_sampleRate(0)
578 {
579 }
580
PSoundChannelNull(const PString & device,PSoundChannel::Directions dir,unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample)581 PSoundChannelNull(
582 const PString &device,
583 PSoundChannel::Directions dir,
584 unsigned numChannels,
585 unsigned sampleRate,
586 unsigned bitsPerSample
587 ) : m_sampleRate(0)
588 {
589 Open(device, dir, numChannels, sampleRate, bitsPerSample);
590 }
591
GetDeviceNames(PSoundChannel::Directions=Player)592 static PStringArray GetDeviceNames(PSoundChannel::Directions = Player)
593 {
594 return NullAudio;
595 }
596
Open(const PString &,Directions dir,unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample)597 PBoolean Open(const PString &,
598 Directions dir,
599 unsigned numChannels,
600 unsigned sampleRate,
601 unsigned bitsPerSample)
602 {
603 activeDirection = dir;
604 return SetFormat(numChannels, sampleRate, bitsPerSample);
605 }
606
GetName() const607 virtual PString GetName() const
608 {
609 return NullAudio;
610 }
611
Close()612 PBoolean Close()
613 {
614 m_sampleRate = 0;
615 return true;
616 }
617
IsOpen() const618 PBoolean IsOpen() const
619 {
620 return m_sampleRate > 0;
621 }
622
Write(const void *,PINDEX len)623 PBoolean Write(const void *, PINDEX len)
624 {
625 if (m_sampleRate <= 0)
626 return false;
627
628 lastWriteCount = len;
629 m_Pacing.Delay(len/2*1000/m_sampleRate);
630 return true;
631 }
632
Read(void * buf,PINDEX len)633 PBoolean Read(void * buf, PINDEX len)
634 {
635 if (m_sampleRate <= 0)
636 return false;
637
638 memset(buf, 0, len);
639 lastReadCount = len;
640 m_Pacing.Delay(len/2*1000/m_sampleRate);
641 return true;
642 }
643
SetFormat(unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample)644 PBoolean SetFormat(unsigned numChannels,
645 unsigned sampleRate,
646 unsigned bitsPerSample)
647 {
648 m_sampleRate = sampleRate;
649 return numChannels == 1 && bitsPerSample == 16;
650 }
651
GetChannels() const652 unsigned GetChannels() const
653 {
654 return 1;
655 }
656
GetSampleRate() const657 unsigned GetSampleRate() const
658 {
659 return m_sampleRate;
660 }
661
GetSampleSize() const662 unsigned GetSampleSize() const
663 {
664 return 16;
665 }
666
SetBuffers(PINDEX,PINDEX)667 PBoolean SetBuffers(PINDEX, PINDEX)
668 {
669 return true;
670 }
671
GetBuffers(PINDEX & size,PINDEX & count)672 PBoolean GetBuffers(PINDEX & size, PINDEX & count)
673 {
674 size = 2;
675 count = 1;
676 return true;
677 }
678
679 protected:
680 unsigned m_sampleRate;
681 PAdaptiveDelay m_Pacing;
682 };
683
684
685 PCREATE_SOUND_PLUGIN(NullAudio, PSoundChannelNull)
686