1 #pragma implementation "sound.h"
7 class SoundHandleEntry
: public PObject
{
9 PCLASSINFO(SoundHandleEntry
, PObject
)
19 unsigned bitsPerSample
;
20 unsigned fragmentValue
;
24 PDICTIONARY(SoundHandleDict
, PString
, SoundHandleEntry
);
26 PMutex
PSoundChannel::dictMutex
;
28 static SoundHandleDict
& handleDict()
30 static SoundHandleDict dict
;
34 #define LOOPBACK_BUFFER_SIZE 5000
35 #define BYTESINBUF ((startptr<endptr)?(endptr-startptr):(LOOPBACK_BUFFER_SIZE+endptr-startptr))
37 char soundbuffer
[LOOPBACK_BUFFER_SIZE
];
41 PSound::PSound(unsigned channels
,
42 unsigned samplesPerSecond
,
43 unsigned bitsPerSample
,
48 numChannels
= channels
;
49 sampleRate
= samplesPerSecond
;
50 sampleSize
= bitsPerSample
;
53 memcpy(GetPointer(), buffer
, bufferSize
);
57 PSound::PSound(const PFilePath
& filename
)
67 PSound
& PSound::operator=(const PBYTEArray
& data
)
69 PBYTEArray::operator=(data
);
74 void PSound::SetFormat(unsigned channels
,
75 unsigned samplesPerSecond
,
76 unsigned bitsPerSample
)
79 numChannels
= channels
;
80 sampleRate
= samplesPerSecond
;
81 sampleSize
= bitsPerSample
;
82 formatInfo
.SetSize(0);
86 BOOL
PSound::Load(const PFilePath
& /*filename*/)
92 BOOL
PSound::Save(const PFilePath
& /*filename*/)
97 ///////////////////////////////////////////////////////////////////////////////
99 SoundHandleEntry::SoundHandleEntry()
105 ///////////////////////////////////////////////////////////////////////////////
107 PSoundChannel::PSoundChannel()
113 PSoundChannel::PSoundChannel(const PString
& device
,
115 unsigned numChannels
,
117 unsigned bitsPerSample
)
120 Open(device
, dir
, numChannels
, sampleRate
, bitsPerSample
);
124 void PSoundChannel::Construct()
130 PSoundChannel::~PSoundChannel()
136 PStringArray
PSoundChannel::GetDeviceNames(Directions
/*dir*/)
138 static const char * const devices
[] = {
142 return PStringArray(PARRAYSIZE(devices
), devices
);
146 PString
PSoundChannel::GetDefaultDevice(Directions
/*dir*/)
152 BOOL
PSoundChannel::Open(const PString
& _device
,
154 unsigned _numChannels
,
155 unsigned _sampleRate
,
156 unsigned _bitsPerSample
)
160 // lock the dictionary
163 // make the direction value 1 or 2
166 // if this device in in the dictionary
167 if (handleDict().Contains(_device
)) {
169 SoundHandleEntry
& entry
= handleDict()[_device
];
171 // see if the sound channel is already open in this direction
172 if ((entry
.direction
& dir
) != 0) {
177 // flag this entry as open in this direction
178 entry
.direction
|= dir
;
179 os_handle
= entry
.handle
;
183 // this is the first time this device has been used
184 // open the device in read/write mode always
185 if (_device
== "loopback") {
186 startptr
= endptr
= 0;
187 os_handle
= 0; // Use os_handle value 0 to indicate loopback, cannot ever be stdin!
190 PAssertAlways(PUnimplementedFunction
);
194 // add the device to the dictionary
195 SoundHandleEntry
* entry
= PNEW SoundHandleEntry
;
196 handleDict().SetAt(_device
, entry
);
198 // save the information into the dictionary entry
199 entry
->handle
= os_handle
;
200 entry
->direction
= dir
;
201 entry
->numChannels
= _numChannels
;
202 entry
->sampleRate
= _sampleRate
;
203 entry
->bitsPerSample
= _bitsPerSample
;
204 entry
->isInitialised
= FALSE
;
205 entry
->fragmentValue
= 0x7fff0008;
209 // unlock the dictionary
212 // save the direction and device
215 isInitialised
= FALSE
;
220 BOOL
PSoundChannel::Setup()
228 // lock the dictionary
231 // the device must always be in the dictionary
232 PAssertOS(handleDict().Contains(device
));
234 // get record for the device
235 SoundHandleEntry
& entry
= handleDict()[device
];
238 if (entry
.isInitialised
) {
239 isInitialised
= TRUE
;
241 } else if (device
== "loopback")
244 PAssertAlways(PUnimplementedFunction
);
247 entry
.isInitialised
= TRUE
;
248 isInitialised
= TRUE
;
255 BOOL
PSoundChannel::Close()
257 // if the channel isn't open, do nothing
261 if (os_handle
== 0) {
266 // the device must be in the dictionary
268 SoundHandleEntry
* entry
;
269 PAssert((entry
= handleDict().GetAt(device
)) != NULL
, "Unknown sound device \"" + device
+ "\" found");
271 // modify the directions bit mask in the dictionary
272 entry
->direction
^= (direction
+1);
274 // if this is the last usage of this entry, then remove it
275 if (entry
->direction
== 0) {
276 handleDict().RemoveAt(device
);
278 return PChannel::Close();
281 // flag this channel as closed
287 BOOL
PSoundChannel::Write(const void * buf
, PINDEX len
)
289 static struct timespec ts
= {0, 5000000};
296 while (!ConvertOSError(::write(os_handle
, (char *)buf
, len
)))
297 if (GetErrorCode() != Interrupted
) {
307 soundbuffer
[endptr
++] = ((char *)buf
)[index
++];
308 if (endptr
== LOOPBACK_BUFFER_SIZE
)
310 while (((startptr
- 1) == endptr
) || ((endptr
==LOOPBACK_BUFFER_SIZE
- 1) && (startptr
==0))) {
317 BOOL
PSoundChannel::Read(void * buf
, PINDEX len
)
319 static struct timespec ts
= {0, 5000000};
325 while (!ConvertOSError(::read(os_handle
, (char *)buf
, len
)))
326 if (GetErrorCode() != Interrupted
)
336 while ((startptr
== endptr
) && (i
< 30)){ // int i is a very dirty hack to get the
337 // call-termination to work. Transmit thread
338 // will otherwise not end! (channels.cxx line 706
339 // while() will get stuck. This should be fixed.
344 ((char *)buf
)[index
++]=soundbuffer
[startptr
++];
345 if (startptr
== LOOPBACK_BUFFER_SIZE
)
353 BOOL
PSoundChannel::SetFormat(unsigned numChannels
,
355 unsigned bitsPerSample
)
358 return SetErrorValues(NotOpen
, EBADF
);
362 PAssert((bitsPerSample
== 8) || (bitsPerSample
== 16), PInvalidParameter
);
363 PAssert(numChannels
>= 1 && numChannels
<= 2, PInvalidParameter
);
367 // lock the dictionary
370 // the device must always be in the dictionary
371 PAssertOS(handleDict().Contains(device
));
373 // get record for the device
374 SoundHandleEntry
& entry
= handleDict()[device
];
376 entry
.numChannels
= numChannels
;
377 entry
.sampleRate
= sampleRate
;
378 entry
.bitsPerSample
= bitsPerSample
;
379 entry
.isInitialised
= FALSE
;
384 // mark this channel as uninitialised
385 isInitialised
= FALSE
;
391 BOOL
PSoundChannel::SetBuffers(PINDEX size
, PINDEX count
)
394 return SetErrorValues(NotOpen
, EBADF
);
399 PAssert(size
> 0 && count
> 0 && count
< 65536, PInvalidParameter
);
401 while (size
> (PINDEX
)(1 << arg
))
406 // lock the dictionary
409 // the device must always be in the dictionary
410 PAssertOS(handleDict().Contains(device
));
412 // get record for the device
413 SoundHandleEntry
& entry
= handleDict()[device
];
415 // set information in the common record
416 entry
.fragmentValue
= arg
;
417 entry
.isInitialised
= FALSE
;
419 // flag this channel as not initialised
420 isInitialised
= FALSE
;
428 BOOL
PSoundChannel::GetBuffers(PINDEX
& size
, PINDEX
& count
)
431 return SetErrorValues(NotOpen
, EBADF
);
434 // lock the dictionary
437 // the device must always be in the dictionary
438 PAssertOS(handleDict().Contains(device
));
440 SoundHandleEntry
& entry
= handleDict()[device
];
442 int arg
= entry
.fragmentValue
;
447 size
= 1 << (arg
&0xffff);
452 BOOL
PSoundChannel::PlaySound(const PSound
& sound
, BOOL wait
)
455 return SetErrorValues(NotOpen
, EBADF
);
460 if (!Write((const BYTE
*)sound
, sound
.GetSize()))
464 return WaitForPlayCompletion();
470 BOOL
PSoundChannel::PlayFile(const PFilePath
& filename
, BOOL wait
)
473 return SetErrorValues(NotOpen
, EBADF
);
480 BOOL
PSoundChannel::HasPlayCompleted()
483 return SetErrorValues(NotOpen
, EBADF
);
487 return BYTESINBUF
<= 0;
489 PAssertAlways(PUnimplementedFunction
);
494 BOOL
PSoundChannel::WaitForPlayCompletion()
496 static struct timespec ts
= {0, 1000000};
499 return SetErrorValues(NotOpen
, EBADF
);
502 if (os_handle
== 0) {
503 while (BYTESINBUF
> 0)
508 PAssertAlways(PUnimplementedFunction
);
513 BOOL
PSoundChannel::RecordSound(PSound
& sound
)
516 return SetErrorValues(NotOpen
, EBADF
);
523 BOOL
PSoundChannel::RecordFile(const PFilePath
& filename
)
526 return SetErrorValues(NotOpen
, EBADF
);
533 BOOL
PSoundChannel::StartRecording()
536 return SetErrorValues(NotOpen
, EBADF
);
544 FD_SET(os_handle
, &fds
);
546 struct timeval timeout
;
547 memset(&timeout
, 0, sizeof(timeout
));
549 return ConvertOSError(::select(1, &fds
, NULL
, NULL
, &timeout
));
553 BOOL
PSoundChannel::IsRecordBufferFull()
556 return SetErrorValues(NotOpen
, EBADF
);
560 return (BYTESINBUF
> 0);
562 PAssertAlways(PUnimplementedFunction
);
567 BOOL
PSoundChannel::AreAllRecordBuffersFull()
570 return SetErrorValues(NotOpen
, EBADF
);
574 return (BYTESINBUF
== LOOPBACK_BUFFER_SIZE
);
576 PAssertAlways(PUnimplementedFunction
);
581 BOOL
PSoundChannel::WaitForRecordBufferFull()
584 return SetErrorValues(NotOpen
, EBADF
);
587 return PXSetIOBlock(PXReadBlock
, readTimeout
);
591 BOOL
PSoundChannel::WaitForAllRecordBuffersFull()
597 BOOL
PSoundChannel::Abort()
599 if (os_handle
== 0) {
600 startptr
= endptr
= 0;
604 PAssertAlways(PUnimplementedFunction
);