Fixed DevStudio 2003 build with memory check code.
[pwlib.git] / src / ptlib / unix / vxaudio.cxx
blobe4536a179a0736117dfcd294de589a14753f043a
1 #pragma implementation "sound.h"
3 #include <ptlib.h>
5 #include <time.h>
7 class SoundHandleEntry : public PObject {
9 PCLASSINFO(SoundHandleEntry, PObject)
11 public:
12 SoundHandleEntry();
14 int handle;
15 int direction;
17 unsigned numChannels;
18 unsigned sampleRate;
19 unsigned bitsPerSample;
20 unsigned fragmentValue;
21 BOOL isInitialised;
24 PDICTIONARY(SoundHandleDict, PString, SoundHandleEntry);
26 PMutex PSoundChannel::dictMutex;
28 static SoundHandleDict & handleDict()
30 static SoundHandleDict dict;
31 return 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];
38 int startptr, endptr;
41 PSound::PSound(unsigned channels,
42 unsigned samplesPerSecond,
43 unsigned bitsPerSample,
44 PINDEX bufferSize,
45 const BYTE * buffer)
47 encoding = 0;
48 numChannels = channels;
49 sampleRate = samplesPerSecond;
50 sampleSize = bitsPerSample;
51 SetSize(bufferSize);
52 if (buffer != NULL)
53 memcpy(GetPointer(), buffer, bufferSize);
57 PSound::PSound(const PFilePath & filename)
59 encoding = 0;
60 numChannels = 1;
61 sampleRate = 8000;
62 sampleSize = 16;
63 Load(filename);
67 PSound & PSound::operator=(const PBYTEArray & data)
69 PBYTEArray::operator=(data);
70 return *this;
74 void PSound::SetFormat(unsigned channels,
75 unsigned samplesPerSecond,
76 unsigned bitsPerSample)
78 encoding = 0;
79 numChannels = channels;
80 sampleRate = samplesPerSecond;
81 sampleSize = bitsPerSample;
82 formatInfo.SetSize(0);
86 BOOL PSound::Load(const PFilePath & /*filename*/)
88 return FALSE;
92 BOOL PSound::Save(const PFilePath & /*filename*/)
94 return FALSE;
97 ///////////////////////////////////////////////////////////////////////////////
99 SoundHandleEntry::SoundHandleEntry()
101 handle = -1;
102 direction = 0;
105 ///////////////////////////////////////////////////////////////////////////////
107 PSoundChannel::PSoundChannel()
109 Construct();
113 PSoundChannel::PSoundChannel(const PString & device,
114 Directions dir,
115 unsigned numChannels,
116 unsigned sampleRate,
117 unsigned bitsPerSample)
119 Construct();
120 Open(device, dir, numChannels, sampleRate, bitsPerSample);
124 void PSoundChannel::Construct()
126 os_handle = -1;
130 PSoundChannel::~PSoundChannel()
132 Close();
136 PStringArray PSoundChannel::GetDeviceNames(Directions /*dir*/)
138 static const char * const devices[] = {
139 "loopback"
142 return PStringArray(PARRAYSIZE(devices), devices);
146 PString PSoundChannel::GetDefaultDevice(Directions /*dir*/)
148 return "loopback";
152 BOOL PSoundChannel::Open(const PString & _device,
153 Directions _dir,
154 unsigned _numChannels,
155 unsigned _sampleRate,
156 unsigned _bitsPerSample)
158 Close();
160 // lock the dictionary
161 dictMutex.Wait();
163 // make the direction value 1 or 2
164 int dir = _dir + 1;
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) {
173 dictMutex.Signal();
174 return FALSE;
177 // flag this entry as open in this direction
178 entry.direction |= dir;
179 os_handle = entry.handle;
181 } else {
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!
189 else {
190 PAssertAlways(PUnimplementedFunction);
191 return FALSE;
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
210 dictMutex.Signal();
212 // save the direction and device
213 direction = _dir;
214 device = _device;
215 isInitialised = FALSE;
217 return TRUE;
220 BOOL PSoundChannel::Setup()
222 if (os_handle < 0)
223 return FALSE;
225 if (isInitialised)
226 return TRUE;
228 // lock the dictionary
229 dictMutex.Wait();
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];
237 BOOL stat = FALSE;
238 if (entry.isInitialised) {
239 isInitialised = TRUE;
240 stat = TRUE;
241 } else if (device == "loopback")
242 stat = TRUE;
243 else {
244 PAssertAlways(PUnimplementedFunction);
247 entry.isInitialised = TRUE;
248 isInitialised = TRUE;
250 dictMutex.Signal();
252 return stat;
255 BOOL PSoundChannel::Close()
257 // if the channel isn't open, do nothing
258 if (os_handle < 0)
259 return TRUE;
261 if (os_handle == 0) {
262 os_handle = -1;
263 return TRUE;
266 // the device must be in the dictionary
267 dictMutex.Wait();
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);
277 dictMutex.Signal();
278 return PChannel::Close();
281 // flag this channel as closed
282 dictMutex.Signal();
283 os_handle = -1;
284 return TRUE;
287 BOOL PSoundChannel::Write(const void * buf, PINDEX len)
289 static struct timespec ts = {0, 5000000};
291 if (!Setup()) {
292 return FALSE;
295 if (os_handle > 0) {
296 while (!ConvertOSError(::write(os_handle, (char *)buf, len)))
297 if (GetErrorCode() != Interrupted) {
298 return FALSE;
300 return TRUE;
303 int index = 0;
305 while (len > 0) {
306 len--;
307 soundbuffer[endptr++] = ((char *)buf)[index++];
308 if (endptr == LOOPBACK_BUFFER_SIZE)
309 endptr = 0;
310 while (((startptr - 1) == endptr) || ((endptr==LOOPBACK_BUFFER_SIZE - 1) && (startptr==0))) {
311 nanosleep(&ts, 0);
314 return TRUE;
317 BOOL PSoundChannel::Read(void * buf, PINDEX len)
319 static struct timespec ts = {0, 5000000};
321 if (!Setup())
322 return FALSE;
324 if (os_handle > 0) {
325 while (!ConvertOSError(::read(os_handle, (char *)buf, len)))
326 if (GetErrorCode() != Interrupted)
327 return FALSE;
328 return TRUE;
331 int index = 0;
333 while (len > 0) {
335 int i = 0;
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.
340 i++;
341 nanosleep(&ts, 0);
343 len--;
344 ((char *)buf)[index++]=soundbuffer[startptr++];
345 if (startptr == LOOPBACK_BUFFER_SIZE)
346 startptr = 0;
349 return TRUE;
353 BOOL PSoundChannel::SetFormat(unsigned numChannels,
354 unsigned sampleRate,
355 unsigned bitsPerSample)
357 if (os_handle < 0) {
358 return SetErrorValues(NotOpen, EBADF);
361 // check parameters
362 PAssert((bitsPerSample == 8) || (bitsPerSample == 16), PInvalidParameter);
363 PAssert(numChannels >= 1 && numChannels <= 2, PInvalidParameter);
365 Abort();
367 // lock the dictionary
368 dictMutex.Wait();
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;
381 // unlock dictionary
382 dictMutex.Signal();
384 // mark this channel as uninitialised
385 isInitialised = FALSE;
387 return TRUE;
391 BOOL PSoundChannel::SetBuffers(PINDEX size, PINDEX count)
393 if (os_handle < 0) {
394 return SetErrorValues(NotOpen, EBADF);
397 Abort();
399 PAssert(size > 0 && count > 0 && count < 65536, PInvalidParameter);
400 int arg = 1;
401 while (size > (PINDEX)(1 << arg))
402 arg++;
404 arg |= count << 16;
406 // lock the dictionary
407 dictMutex.Wait();
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;
422 dictMutex.Signal();
424 return TRUE;
428 BOOL PSoundChannel::GetBuffers(PINDEX & size, PINDEX & count)
430 if (os_handle < 0) {
431 return SetErrorValues(NotOpen, EBADF);
434 // lock the dictionary
435 dictMutex.Wait();
437 // the device must always be in the dictionary
438 PAssertOS(handleDict().Contains(device));
440 SoundHandleEntry & entry = handleDict()[device];
442 int arg = entry.fragmentValue;
444 dictMutex.Signal();
446 count = arg >> 16;
447 size = 1 << (arg&0xffff);
448 return TRUE;
452 BOOL PSoundChannel::PlaySound(const PSound & sound, BOOL wait)
454 if (os_handle < 0) {
455 return SetErrorValues(NotOpen, EBADF);
458 Abort();
460 if (!Write((const BYTE *)sound, sound.GetSize()))
461 return FALSE;
463 if (wait)
464 return WaitForPlayCompletion();
466 return TRUE;
470 BOOL PSoundChannel::PlayFile(const PFilePath & filename, BOOL wait)
472 if (os_handle < 0) {
473 return SetErrorValues(NotOpen, EBADF);
476 return FALSE;
480 BOOL PSoundChannel::HasPlayCompleted()
482 if (os_handle < 0) {
483 return SetErrorValues(NotOpen, EBADF);
486 if (os_handle == 0)
487 return BYTESINBUF <= 0;
489 PAssertAlways(PUnimplementedFunction);
490 return FALSE;
494 BOOL PSoundChannel::WaitForPlayCompletion()
496 static struct timespec ts = {0, 1000000};
498 if (os_handle < 0) {
499 return SetErrorValues(NotOpen, EBADF);
502 if (os_handle == 0) {
503 while (BYTESINBUF > 0)
504 nanosleep(&ts, 0);
505 return TRUE;
508 PAssertAlways(PUnimplementedFunction);
509 return FALSE;
513 BOOL PSoundChannel::RecordSound(PSound & sound)
515 if (os_handle < 0) {
516 return SetErrorValues(NotOpen, EBADF);
519 return FALSE;
523 BOOL PSoundChannel::RecordFile(const PFilePath & filename)
525 if (os_handle < 0) {
526 return SetErrorValues(NotOpen, EBADF);
529 return FALSE;
533 BOOL PSoundChannel::StartRecording()
535 if (os_handle < 0) {
536 return SetErrorValues(NotOpen, EBADF);
539 if (os_handle == 0)
540 return TRUE;
542 fd_set fds;
543 FD_ZERO(&fds);
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()
555 if (os_handle < 0) {
556 return SetErrorValues(NotOpen, EBADF);
559 if (os_handle == 0)
560 return (BYTESINBUF > 0);
562 PAssertAlways(PUnimplementedFunction);
563 return FALSE;
567 BOOL PSoundChannel::AreAllRecordBuffersFull()
569 if (os_handle < 0) {
570 return SetErrorValues(NotOpen, EBADF);
573 if (os_handle == 0)
574 return (BYTESINBUF == LOOPBACK_BUFFER_SIZE);
576 PAssertAlways(PUnimplementedFunction);
577 return FALSE;
581 BOOL PSoundChannel::WaitForRecordBufferFull()
583 if (os_handle < 0) {
584 return SetErrorValues(NotOpen, EBADF);
587 return PXSetIOBlock(PXReadBlock, readTimeout);
591 BOOL PSoundChannel::WaitForAllRecordBuffersFull()
593 return FALSE;
597 BOOL PSoundChannel::Abort()
599 if (os_handle == 0) {
600 startptr = endptr = 0;
601 return TRUE;
604 PAssertAlways(PUnimplementedFunction);
605 return FALSE;
609 // End of file