#pragma implementation "sound.h" #include #include class SoundHandleEntry : public PObject { PCLASSINFO(SoundHandleEntry, PObject) public: SoundHandleEntry(); int handle; int direction; unsigned numChannels; unsigned sampleRate; unsigned bitsPerSample; unsigned fragmentValue; BOOL isInitialised; }; PDICTIONARY(SoundHandleDict, PString, SoundHandleEntry); PMutex PSoundChannel::dictMutex; static SoundHandleDict & handleDict() { static SoundHandleDict dict; return dict; } #define LOOPBACK_BUFFER_SIZE 5000 #define BYTESINBUF ((startptrhandle = os_handle; entry->direction = dir; entry->numChannels = _numChannels; entry->sampleRate = _sampleRate; entry->bitsPerSample = _bitsPerSample; entry->isInitialised = FALSE; entry->fragmentValue = 0x7fff0008; } // unlock the dictionary dictMutex.Signal(); // save the direction and device direction = _dir; device = _device; isInitialised = FALSE; return TRUE; } BOOL PSoundChannel::Setup() { if (os_handle < 0) return FALSE; if (isInitialised) return TRUE; // lock the dictionary dictMutex.Wait(); // the device must always be in the dictionary PAssertOS(handleDict().Contains(device)); // get record for the device SoundHandleEntry & entry = handleDict()[device]; BOOL stat = FALSE; if (entry.isInitialised) { isInitialised = TRUE; stat = TRUE; } else if (device == "loopback") stat = TRUE; else { PAssertAlways(PUnimplementedFunction); } entry.isInitialised = TRUE; isInitialised = TRUE; dictMutex.Signal(); return stat; } BOOL PSoundChannel::Close() { // if the channel isn't open, do nothing if (os_handle < 0) return TRUE; if (os_handle == 0) { os_handle = -1; return TRUE; } // the device must be in the dictionary dictMutex.Wait(); SoundHandleEntry * entry; PAssert((entry = handleDict().GetAt(device)) != NULL, "Unknown sound device \"" + device + "\" found"); // modify the directions bit mask in the dictionary entry->direction ^= (direction+1); // if this is the last usage of this entry, then remove it if (entry->direction == 0) { handleDict().RemoveAt(device); dictMutex.Signal(); return PChannel::Close(); } // flag this channel as closed dictMutex.Signal(); os_handle = -1; return TRUE; } BOOL PSoundChannel::Write(const void * buf, PINDEX len) { static struct timespec ts = {0, 5000000}; if (!Setup()) { return FALSE; } if (os_handle > 0) { while (!ConvertOSError(::write(os_handle, (char *)buf, len))) if (GetErrorCode() != Interrupted) { return FALSE; } return TRUE; } int index = 0; while (len > 0) { len--; soundbuffer[endptr++] = ((char *)buf)[index++]; if (endptr == LOOPBACK_BUFFER_SIZE) endptr = 0; while (((startptr - 1) == endptr) || ((endptr==LOOPBACK_BUFFER_SIZE - 1) && (startptr==0))) { nanosleep(&ts, 0); } } return TRUE; } BOOL PSoundChannel::Read(void * buf, PINDEX len) { static struct timespec ts = {0, 5000000}; if (!Setup()) return FALSE; if (os_handle > 0) { while (!ConvertOSError(::read(os_handle, (char *)buf, len))) if (GetErrorCode() != Interrupted) return FALSE; return TRUE; } int index = 0; while (len > 0) { int i = 0; while ((startptr == endptr) && (i < 30)){ // int i is a very dirty hack to get the // call-termination to work. Transmit thread // will otherwise not end! (channels.cxx line 706 // while() will get stuck. This should be fixed. i++; nanosleep(&ts, 0); } len--; ((char *)buf)[index++]=soundbuffer[startptr++]; if (startptr == LOOPBACK_BUFFER_SIZE) startptr = 0; } return TRUE; } BOOL PSoundChannel::SetFormat(unsigned numChannels, unsigned sampleRate, unsigned bitsPerSample) { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } // check parameters PAssert((bitsPerSample == 8) || (bitsPerSample == 16), PInvalidParameter); PAssert(numChannels >= 1 && numChannels <= 2, PInvalidParameter); Abort(); // lock the dictionary dictMutex.Wait(); // the device must always be in the dictionary PAssertOS(handleDict().Contains(device)); // get record for the device SoundHandleEntry & entry = handleDict()[device]; entry.numChannels = numChannels; entry.sampleRate = sampleRate; entry.bitsPerSample = bitsPerSample; entry.isInitialised = FALSE; // unlock dictionary dictMutex.Signal(); // mark this channel as uninitialised isInitialised = FALSE; return TRUE; } BOOL PSoundChannel::SetBuffers(PINDEX size, PINDEX count) { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } Abort(); PAssert(size > 0 && count > 0 && count < 65536, PInvalidParameter); int arg = 1; while (size > (PINDEX)(1 << arg)) arg++; arg |= count << 16; // lock the dictionary dictMutex.Wait(); // the device must always be in the dictionary PAssertOS(handleDict().Contains(device)); // get record for the device SoundHandleEntry & entry = handleDict()[device]; // set information in the common record entry.fragmentValue = arg; entry.isInitialised = FALSE; // flag this channel as not initialised isInitialised = FALSE; dictMutex.Signal(); return TRUE; } BOOL PSoundChannel::GetBuffers(PINDEX & size, PINDEX & count) { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } // lock the dictionary dictMutex.Wait(); // the device must always be in the dictionary PAssertOS(handleDict().Contains(device)); SoundHandleEntry & entry = handleDict()[device]; int arg = entry.fragmentValue; dictMutex.Signal(); count = arg >> 16; size = 1 << (arg&0xffff); return TRUE; } BOOL PSoundChannel::PlaySound(const PSound & sound, BOOL wait) { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } Abort(); if (!Write((const BYTE *)sound, sound.GetSize())) return FALSE; if (wait) return WaitForPlayCompletion(); return TRUE; } BOOL PSoundChannel::PlayFile(const PFilePath & filename, BOOL wait) { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } return FALSE; } BOOL PSoundChannel::HasPlayCompleted() { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } if (os_handle == 0) return BYTESINBUF <= 0; PAssertAlways(PUnimplementedFunction); return FALSE; } BOOL PSoundChannel::WaitForPlayCompletion() { static struct timespec ts = {0, 1000000}; if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } if (os_handle == 0) { while (BYTESINBUF > 0) nanosleep(&ts, 0); return TRUE; } PAssertAlways(PUnimplementedFunction); return FALSE; } BOOL PSoundChannel::RecordSound(PSound & sound) { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } return FALSE; } BOOL PSoundChannel::RecordFile(const PFilePath & filename) { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } return FALSE; } BOOL PSoundChannel::StartRecording() { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } if (os_handle == 0) return TRUE; fd_set fds; FD_ZERO(&fds); FD_SET(os_handle, &fds); struct timeval timeout; memset(&timeout, 0, sizeof(timeout)); return ConvertOSError(::select(1, &fds, NULL, NULL, &timeout)); } BOOL PSoundChannel::IsRecordBufferFull() { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } if (os_handle == 0) return (BYTESINBUF > 0); PAssertAlways(PUnimplementedFunction); return FALSE; } BOOL PSoundChannel::AreAllRecordBuffersFull() { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } if (os_handle == 0) return (BYTESINBUF == LOOPBACK_BUFFER_SIZE); PAssertAlways(PUnimplementedFunction); return FALSE; } BOOL PSoundChannel::WaitForRecordBufferFull() { if (os_handle < 0) { return SetErrorValues(NotOpen, EBADF); } return PXSetIOBlock(PXReadBlock, readTimeout); } BOOL PSoundChannel::WaitForAllRecordBuffersFull() { return FALSE; } BOOL PSoundChannel::Abort() { if (os_handle == 0) { startptr = endptr = 0; return TRUE; } PAssertAlways(PUnimplementedFunction); return FALSE; } // End of file