Index: fhandler.h =================================================================== RCS file: /cvs/src/src/winsup/cygwin/fhandler.h,v retrieving revision 1.210 diff -u -p -r1.210 fhandler.h --- fhandler.h 15 Jul 2004 14:56:05 -0000 1.210 +++ fhandler.h 14 Aug 2004 03:30:18 -0000 @@ -1069,12 +1069,10 @@ class fhandler_dev_dsp: public fhandler_ int audiofreq_; int audiobits_; int audiochannels_; - static int open_count; // per process Audio_out *audio_out_; Audio_in *audio_in_; public: fhandler_dev_dsp (); - ~fhandler_dev_dsp(); int open (int flags, mode_t mode = 0); int write (const void *ptr, size_t len); @@ -1086,6 +1084,9 @@ class fhandler_dev_dsp: public fhandler_ void dump (void); void fixup_after_fork (HANDLE parent); void fixup_after_exec (); + private: + void close_audio_in (); + void close_audio_out (bool immediately = false); }; class fhandler_virtual : public fhandler_base Index: fhandler_dsp.cc =================================================================== RCS file: /cvs/src/src/winsup/cygwin/fhandler_dsp.cc,v retrieving revision 1.38 diff -u -p -r1.38 fhandler_dsp.cc --- fhandler_dsp.cc 19 Jul 2004 13:13:48 -0000 1.38 +++ fhandler_dsp.cc 16 Aug 2004 23:47:11 -0000 @@ -1,4 +1,4 @@ -/* fhandler_dev_dsp: code to emulate OSS sound model /dev/dsp +/* Fhandler_dev_dsp: code to emulate OSS sound model /dev/dsp Copyright 2001, 2002, 2003, 2004 Red Hat, Inc @@ -21,12 +21,14 @@ details. */ #include "security.h" #include "path.h" #include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" /*------------------------------------------------------------------------ Simple encapsulation of the win32 audio device. Implementation Notes - 1. Audio buffers are created dynamically just before the first read or + 1. Audio structures are malloced just before the first read or write to /dev/dsp. The actual buffer size is determined at that time, such that one buffer holds about 125ms of audio data. At the time of this writing, 12 buffers are allocated, @@ -35,38 +37,28 @@ details. */ but for this implementation only returns meaningful results if sampling rate, number of channels and number of bits per sample are not changed afterwards. + The audio structures are freed when the device is reset or closed, + and they are not passed to exec'ed processes. + The dev_ member is cleared after a fork. This forces the child + to reopen the audio device._ - 2. Every open call creates a new instance of the handler. To cope - with the fact that only a single wave device exists, the static - variable open_count tracks opens for one process. After a + 2. Every open call creates a new instance of the handler. After a successful open, every subsequent open from the same process to the device fails with EBUSY. - If different processes open the audio device simultaneously, - the results are unpredictable - usually the first one wins. - - 3. The wave device is reserved within a process from the time that - the first read or write call has been successful until /dev/dsp - has been closed by that process. During this reservation period - child processes that use the same file descriptor cannot - do read, write or ioctls that change the device properties. - This means that a parent can open the device, do some ioctl, - spawn children, and any one of them can do the data read/write + The structures are shared between duped handles, but not with + children. They only inherit the settings from the parent. */ class fhandler_dev_dsp::Audio { // This class contains functionality common to Audio_in and Audio_out public: Audio (); - ~Audio (); + ~Audio (); class queue; - bool denyAccess (); - void fork_fixup (HANDLE parent); - inline DWORD getOwner () { return owner_; } - void setOwner () { owner_ = GetCurrentProcessId (); } - inline void clearOwner () { owner_ = 0L; } - void setformat (int format); + bool isvalid (); + void setconvert (int format); void convert_none (unsigned char *buffer, int size_bytes) { } void convert_U8_S8 (unsigned char *buffer, int size_bytes); void convert_S16LE_U16LE (unsigned char *buffer, int size_bytes); @@ -75,14 +67,16 @@ class fhandler_dev_dsp::Audio void fillFormat (WAVEFORMATEX * format, int rate, int bits, int channels); unsigned blockSize (int rate, int bits, int channels); - void (fhandler_dev_dsp::Audio::*convert_) (unsigned char *buffer, int size_bytes); - inline void lock () { EnterCriticalSection (&lock_); } - inline void unlock () { LeaveCriticalSection (&lock_); } - private: - DWORD owner_; /* Process ID when wave operation started, else 0 */ - CRITICAL_SECTION lock_; + + enum { MAX_BLOCKS = 12 }; + int bufferIndex_; // offset into pHdr_->lpData + WAVEHDR *pHdr_; // data to be filled by write + WAVEHDR wavehdr_[MAX_BLOCKS]; + char *bigwavebuffer_; // audio samples only + // Member variables below must be locked + queue *Qisr2app_; // blocks passed from wave callback }; class fhandler_dev_dsp::Audio::queue @@ -93,12 +87,17 @@ class fhandler_dev_dsp::Audio::queue bool send (WAVEHDR *); // queue an item, returns true if successful bool recv (WAVEHDR **); // retrieve an item, returns true if successful - int query (); // return number of items queued - + void reset (); + int query (); // return number of items queued + inline void lock () { EnterCriticalSection (&lock_); } + inline void unlock () { LeaveCriticalSection (&lock_); } + inline void dellock () { debug_printf ("Deleting Critical Section"); DeleteCriticalSection (&lock_); } + bool isvalid () { return storage_; } private: + CRITICAL_SECTION lock_; int head_; int tail_; - int depth_, depth1_; + int depth_; WAVEHDR **storage_; }; @@ -108,35 +107,29 @@ static void CALLBACK waveOut_callback (H class fhandler_dev_dsp::Audio_out: public Audio { public: - Audio_out (); - ~Audio_out (); - + void fork_fixup (HANDLE parent); bool query (int rate, int bits, int channels); - bool start (int rate, int bits, int channels); + bool start (); void stop (bool immediately = false); bool write (const char *pSampleData, int nBytes); void buf_info (audio_buf_info *p, int rate, int bits, int channels); void callback_sampledone (WAVEHDR *pHdr); bool parsewav (const char *&pData, int &nBytes, - int &rate, int &bits, int &channels); + int rate, int bits, int channels); private: void init (unsigned blockSize); void waitforallsent (); void waitforspace (); bool sendcurrent (); - int emptyblocks (); enum { MAX_BLOCKS = 12 }; - queue *Qapp2app_; // empty and unprepared blocks HWAVEOUT dev_; // The wave device - int bufferIndex_; // offset into pHdr_->lpData - WAVEHDR *pHdr_; // data to be filled by write - WAVEHDR wavehdr_[MAX_BLOCKS]; - char *bigwavebuffer_; // audio samples only - - // Member variables below must be locked - queue *Qisr2app_; // empty blocks passed from wave callback + /* Private copies of audiofreq_, audiobits_, audiochannels_, + possibly set from wave file */ + int freq_; + int bits_; + int channels_; }; static void CALLBACK waveIn_callback (HWAVEIN hWave, UINT msg, DWORD instance, @@ -145,9 +138,7 @@ static void CALLBACK waveIn_callback (HW class fhandler_dev_dsp::Audio_in: public Audio { public: - Audio_in (); - ~Audio_in (); - + void fork_fixup (HANDLE parent); bool query (int rate, int bits, int channels); bool start (int rate, int bits, int channels); void stop (); @@ -160,16 +151,7 @@ private: bool queueblock (WAVEHDR *pHdr); void waitfordata (); // blocks until we have a good pHdr_ - enum { MAX_BLOCKS = 12 }; // read ahead of 1.5 seconds - queue *Qapp2app_; // filled and unprepared blocks HWAVEIN dev_; - int bufferIndex_; // offset into pHdr_->lpData - WAVEHDR *pHdr_; // successfully recorded data - WAVEHDR wavehdr_[MAX_BLOCKS]; - char *bigwavebuffer_; // audio samples - - // Member variables below must be locked - queue *Qisr2app_; // filled blocks passed from wave callback }; /* -------------------------------------------------------------------- @@ -178,13 +160,10 @@ private: // Simple fixed length FIFO queue implementation for audio buffer management fhandler_dev_dsp::Audio::queue::queue (int depth) { - head_ = 0; - tail_ = 0; - depth_ = depth; - depth1_ = depth + 1; // allow space for one extra object in the queue // so we can distinguish full and empty status - storage_ = new WAVEHDR *[depth1_]; + depth_ = depth; + storage_ = new WAVEHDR *[depth_ + 1]; } fhandler_dev_dsp::Audio::queue::~queue () @@ -192,28 +171,48 @@ fhandler_dev_dsp::Audio::queue::~queue ( delete[] storage_; } +void +fhandler_dev_dsp::Audio::queue::reset () + { + /* When starting, after reset and after fork */ + head_ = tail_ = 0; + debug_printf ("InitializeCriticalSection"); + memset (&lock_, 0, sizeof (lock_)); + InitializeCriticalSection (&lock_); + } + bool fhandler_dev_dsp::Audio::queue::send (WAVEHDR *x) { + bool res = false; + lock (); if (query () == depth_) - return false; - storage_[tail_] = x; - tail_++; - if (tail_ == depth1_) - tail_ = 0; - return true; + system_printf ("Queue overflow"); + else + { + storage_[tail_] = x; + if (++tail_ > depth_) + tail_ = 0; + res = true; + } + unlock (); + return res; } bool fhandler_dev_dsp::Audio::queue::recv (WAVEHDR **x) { - if (query () == 0) - return false; - *x = storage_[head_]; - head_++; - if (head_ == depth1_) - head_ = 0; - return true; + bool res = false; + lock (); + if (query () != 0) + { + *x = storage_[head_]; + if (++head_ > depth_) + head_ = 0; + res = true; + } + unlock (); + return res; } int @@ -221,40 +220,33 @@ fhandler_dev_dsp::Audio::queue::query () { int n = tail_ - head_; if (n < 0) - n += depth1_; + n += depth_ + 1; return n; } // Audio class implements functionality need for both read and write fhandler_dev_dsp::Audio::Audio () { - InitializeCriticalSection (&lock_); + bigwavebuffer_ = NULL; + Qisr2app_ = new queue (MAX_BLOCKS); convert_ = &fhandler_dev_dsp::Audio::convert_none; - owner_ = 0L; } fhandler_dev_dsp::Audio::~Audio () { - DeleteCriticalSection (&lock_); + debug_printf(""); + delete Qisr2app_; + delete[] bigwavebuffer_; } -void -fhandler_dev_dsp::Audio::fork_fixup (HANDLE parent) -{ - debug_printf ("parent=0x%08x", parent); - InitializeCriticalSection (&lock_); -} - -bool -fhandler_dev_dsp::Audio::denyAccess () -{ - if (owner_ == 0L) - return false; - return (GetCurrentProcessId () != owner_); +inline bool +fhandler_dev_dsp::Audio::isvalid () +{ + return bigwavebuffer_ && Qisr2app_ && Qisr2app_->isvalid (); } void -fhandler_dev_dsp::Audio::setformat (int format) +fhandler_dev_dsp::Audio::setconvert (int format) { switch (format) { @@ -361,19 +353,16 @@ fhandler_dev_dsp::Audio::blockSize (int } //======================================================================= -fhandler_dev_dsp::Audio_out::Audio_out (): Audio () +void +fhandler_dev_dsp::Audio_out::fork_fixup (HANDLE parent) { - bigwavebuffer_ = NULL; - Qisr2app_ = new queue (MAX_BLOCKS); - Qapp2app_ = new queue (MAX_BLOCKS); + /* Null dev_. + It will be necessary to reset the queue, open the device + and create a lock when writing */ + debug_printf ("parent=0x%08x", parent); + dev_ = NULL; } -fhandler_dev_dsp::Audio_out::~Audio_out () -{ - stop (); - delete Qapp2app_; - delete Qisr2app_; -} bool fhandler_dev_dsp::Audio_out::query (int rate, int bits, int channels) @@ -383,37 +372,34 @@ fhandler_dev_dsp::Audio_out::query (int fillFormat (&format, rate, bits, channels); rc = waveOutOpen (NULL, WAVE_MAPPER, &format, 0L, 0L, WAVE_FORMAT_QUERY); - debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels, - (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK"); + debug_printf ("%d = waveOutOpen (freq=%d bits=%d channels=%d)", rc, rate, bits, channels); return (rc == MMSYSERR_NOERROR); } bool -fhandler_dev_dsp::Audio_out::start (int rate, int bits, int channels) +fhandler_dev_dsp::Audio_out::start () { WAVEFORMATEX format; MMRESULT rc; - unsigned bSize = blockSize (rate, bits, channels); - bigwavebuffer_ = new char[MAX_BLOCKS * bSize]; - if (bigwavebuffer_ == NULL) - return false; + unsigned bSize = blockSize (freq_, bits_, channels_); + + if (dev_) + return true; + + /* In case of fork bigwavebuffer may already exist */ + if (!bigwavebuffer_) + bigwavebuffer_ = new char[MAX_BLOCKS * bSize]; - int nDevices = waveOutGetNumDevs (); - debug_printf ("number devices=%d, blocksize=%d", nDevices, bSize); - if (nDevices <= 0) + if (!isvalid ()) return false; - fillFormat (&format, rate, bits, channels); + fillFormat (&format, freq_, bits_, channels_); rc = waveOutOpen (&dev_, WAVE_MAPPER, &format, (DWORD) waveOut_callback, (DWORD) this, CALLBACK_FUNCTION); if (rc == MMSYSERR_NOERROR) - { - setOwner (); - init (bSize); - } + init (bSize); - debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels, - (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK"); + debug_printf ("%d = waveOutOpen (freq=%d bits=%d channels=%d)", rc, freq_, bits_, channels_); return (rc == MMSYSERR_NOERROR); } @@ -423,11 +409,9 @@ fhandler_dev_dsp::Audio_out::stop (bool { MMRESULT rc; WAVEHDR *pHdr; - bool gotblock; - debug_printf ("dev_=%08x pid=%d owner=%d", (int)dev_, - GetCurrentProcessId (), getOwner ()); - if (getOwner () && !denyAccess ()) + debug_printf ("dev_=%08x", (int)dev_); + if (dev_) { if (!immediately) { @@ -436,33 +420,17 @@ fhandler_dev_dsp::Audio_out::stop (bool } rc = waveOutReset (dev_); - debug_printf ("waveOutReset rc=%d", rc); - do + debug_printf ("%d = waveOutReset ()", rc); + while (Qisr2app_->recv (&pHdr)) { - lock (); - gotblock = Qisr2app_->recv (&pHdr); - unlock (); - if (gotblock) - { - rc = waveOutUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR)); - debug_printf ("waveOutUnprepareHeader Block 0x%08x %s", pHdr, - (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK"); - } + rc = waveOutUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR)); + debug_printf ("%d = waveOutUnprepareHeader (0x%08x)", rc, pHdr); } - while (gotblock); - while (Qapp2app_->recv (&pHdr)) - /* flush queue */; rc = waveOutClose (dev_); - debug_printf ("waveOutClose rc=%d", rc); + debug_printf ("%d = waveOutClose ()", rc); - clearOwner (); - } - - if (bigwavebuffer_) - { - delete[] bigwavebuffer_; - bigwavebuffer_ = NULL; + Qisr2app_->dellock (); } } @@ -472,13 +440,15 @@ fhandler_dev_dsp::Audio_out::init (unsig int i; // internally queue all of our buffer for later use by write + Qisr2app_->reset (); for (i = 0; i < MAX_BLOCKS; i++) { wavehdr_[i].lpData = &bigwavebuffer_[i * blockSize]; wavehdr_[i].dwUser = (int) blockSize; - if (!Qapp2app_->send (&wavehdr_[i])) + wavehdr_[i].dwFlags = 0; + if (!Qisr2app_->send (&wavehdr_[i])) { - debug_printf ("Internal Error i=%d", i); + system_printf ("Internal Error i=%d", i); break; // should not happen } } @@ -511,27 +481,17 @@ fhandler_dev_dsp::Audio_out::write (cons return true; } -// return number of (completely) empty blocks back. -int -fhandler_dev_dsp::Audio_out::emptyblocks () -{ - int n; - lock (); - n = Qisr2app_->query (); - unlock (); - n += Qapp2app_->query (); - return n; -} - void fhandler_dev_dsp::Audio_out::buf_info (audio_buf_info *p, int rate, int bits, int channels) { p->fragstotal = MAX_BLOCKS; - p->fragsize = blockSize (rate, bits, channels); - if (getOwner ()) + if (this && dev_) { - p->fragments = emptyblocks (); + /* If the device is running we use the internal values, + possibly set from the wave file. */ + p->fragsize = blockSize (freq_, bits_, channels_); + p->fragments = Qisr2app_->query (); if (pHdr_ != NULL) p->bytes = (int)pHdr_->dwUser - bufferIndex_ + p->fragsize * p->fragments; @@ -540,6 +500,7 @@ fhandler_dev_dsp::Audio_out::buf_info (a } else { + p->fragsize = blockSize (rate, bits, channels); p->fragments = MAX_BLOCKS; p->bytes = p->fragsize * p->fragments; } @@ -547,51 +508,31 @@ fhandler_dev_dsp::Audio_out::buf_info (a /* This is called on an interupt so use locking.. Note Qisr2app_ is used so we should wrap all references to it in locks. */ -void +inline void fhandler_dev_dsp::Audio_out::callback_sampledone (WAVEHDR *pHdr) { - lock (); Qisr2app_->send (pHdr); - unlock (); } void fhandler_dev_dsp::Audio_out::waitforspace () { WAVEHDR *pHdr; - bool gotblock; MMRESULT rc = WAVERR_STILLPLAYING; if (pHdr_ != NULL) return; - while (Qapp2app_->recv (&pHdr) == false) + while (!Qisr2app_->recv (&pHdr)) { - lock (); - gotblock = Qisr2app_->recv (&pHdr); - unlock (); - if (gotblock) - { - if ((pHdr->dwFlags & WHDR_DONE) - && ((rc = waveOutUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR))) - == MMSYSERR_NOERROR)) - { - Qapp2app_->send (pHdr); - } - else - { - debug_printf ("error UnprepareHeader 0x%08x, rc=%d, 100ms", - pHdr, rc); - lock (); - Qisr2app_->send (pHdr); - unlock (); - Sleep (100); - } - } - else - { - debug_printf ("100ms"); - Sleep (100); - } + debug_printf ("100ms"); + Sleep (100); + } + if (pHdr->dwFlags) + { + /* Errors are ignored here. They will probbaly cause a failure + in the subsequent PrepareHeader */ + rc = waveOutUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR)); + debug_printf ("%d = waveOutUnprepareHeader (0x%08x)", rc, pHdr); } pHdr_ = pHdr; bufferIndex_ = 0; @@ -600,10 +541,9 @@ fhandler_dev_dsp::Audio_out::waitforspac void fhandler_dev_dsp::Audio_out::waitforallsent () { - while (emptyblocks () != MAX_BLOCKS) + while (Qisr2app_->query () != MAX_BLOCKS) { - debug_printf ("100ms Qisr=%d Qapp=%d", - Qisr2app_->query (), Qapp2app_->query ()); + debug_printf ("%d blocks in Qisr2app", Qisr2app_->query ()); Sleep (100); } } @@ -613,6 +553,9 @@ bool fhandler_dev_dsp::Audio_out::sendcurrent () { WAVEHDR *pHdr = pHdr_; + MMRESULT rc; + debug_printf ("pHdr=0x%08x bytes=%d", pHdr, bufferIndex_); + if (pHdr_ == NULL) return false; pHdr_ = NULL; @@ -622,27 +565,19 @@ fhandler_dev_dsp::Audio_out::sendcurrent // Send internal buffer out to the soundcard pHdr->dwBufferLength = bufferIndex_; - pHdr->dwFlags = 0; - if (waveOutPrepareHeader (dev_, pHdr, sizeof (WAVEHDR)) == MMSYSERR_NOERROR) - { - if (waveOutWrite (dev_, pHdr, sizeof (WAVEHDR)) == MMSYSERR_NOERROR) - { - debug_printf ("waveOutWrite bytes=%d", bufferIndex_); - return true; - } - else - { - debug_printf ("waveOutWrite failed"); - lock (); - Qisr2app_->send (pHdr); - unlock (); - } - } - else + rc = waveOutPrepareHeader (dev_, pHdr, sizeof (WAVEHDR)); + debug_printf ("%d = waveOutPrepareHeader (0x%08x)", rc, pHdr); + if (rc == MMSYSERR_NOERROR) { - debug_printf ("waveOutPrepareHeader failed"); - Qapp2app_->send (pHdr); + rc = waveOutWrite (dev_, pHdr, sizeof (WAVEHDR)); + debug_printf ("%d = waveOutWrite (0x%08x)", rc, pHdr); } + if (rc == MMSYSERR_NOERROR) + return true; + + /* FIXME: Should we return an error instead ?*/ + pHdr->dwFlags = 0; /* avoid calling UnprepareHeader again */ + Qisr2app_->send (pHdr); return false; } @@ -681,12 +616,19 @@ struct wavformat bool fhandler_dev_dsp::Audio_out::parsewav (const char * &pData, int &nBytes, - int &rate, int &bits, int &channels) + int dev_freq, int dev_bits, int dev_channels) { int len; const char *end = pData + nBytes; const char *pDat; int skip = 0; + + /* Start with default values from the device handler */ + freq_ = dev_freq; + bits_ = dev_bits; + channels_ = dev_channels; + setconvert (bits_ == 8 ? AFMT_U8 : AFMT_S16_LE); + // Check alignment first: A lot of the code below depends on it if (((int)pData & 0x3) != 0) return false; @@ -719,9 +661,9 @@ fhandler_dev_dsp::Audio_out::parsewav (c if (query (format->dwSamplesPerSec, format->wBitsPerSample, format->wChannels)) { // return the parameters we found - rate = format->dwSamplesPerSec; - bits = format->wBitsPerSample; - channels = format->wChannels; + freq_ = format->dwSamplesPerSec; + bits_ = format->wBitsPerSample; + channels_ = format->wChannels; } } } @@ -734,6 +676,7 @@ fhandler_dev_dsp::Audio_out::parsewav (c debug_printf ("Discard %d bytes wave header", skip); pData += skip; nBytes -= skip; + setconvert (bits_ == 8 ? AFMT_U8 : AFMT_S16_LE); return true; } } @@ -750,9 +693,7 @@ fhandler_dev_dsp::Audio_out::parsewav (c for reception and start the wave-in device. We manage queues of pointers to WAVEHDR When a block has been filled, the callback puts the corresponding - WAVEHDR pointer into a queue. We need a second queue to distinguish - blocks with data from blocks that have been unprepared and are ready - to be used by read(). + WAVEHDR pointer into a queue. The function read() blocks (polled, sigh) until at least one good buffer has arrived, then the data is copied into the buffer provided to read(). After a buffer has been fully used by read(), it is queued again @@ -760,18 +701,14 @@ fhandler_dev_dsp::Audio_out::parsewav (c The function read() iterates until all data requested has been received, there is no way to interrupt it */ -fhandler_dev_dsp::Audio_in::Audio_in () : Audio () -{ - bigwavebuffer_ = NULL; - Qisr2app_ = new queue (MAX_BLOCKS); - Qapp2app_ = new queue (MAX_BLOCKS); -} - -fhandler_dev_dsp::Audio_in::~Audio_in () +void +fhandler_dev_dsp::Audio_in::fork_fixup (HANDLE parent) { - stop (); - delete Qapp2app_; - delete Qisr2app_; + /* Null dev_. + It will be necessary to reset the queue, open the device + and create a lock when reading */ + debug_printf ("parent=0x%08x", parent); + dev_ = NULL; } bool @@ -782,8 +719,7 @@ fhandler_dev_dsp::Audio_in::query (int r fillFormat (&format, rate, bits, channels); rc = waveInOpen (NULL, WAVE_MAPPER, &format, 0L, 0L, WAVE_FORMAT_QUERY); - debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels, - (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK"); + debug_printf ("%d = waveInOpen (freq=%d bits=%d channels=%d)", rc, rate, bits, channels); return (rc == MMSYSERR_NOERROR); } @@ -793,31 +729,27 @@ fhandler_dev_dsp::Audio_in::start (int r WAVEFORMATEX format; MMRESULT rc; unsigned bSize = blockSize (rate, bits, channels); - bigwavebuffer_ = new char[MAX_BLOCKS * bSize]; - if (bigwavebuffer_ == NULL) - return false; - int nDevices = waveInGetNumDevs (); - debug_printf ("number devices=%d, blocksize=%d", nDevices, bSize); - if (nDevices <= 0) + if (dev_) + return true; + + /* In case of fork bigwavebuffer may already exist */ + if (!bigwavebuffer_) + bigwavebuffer_ = new char[MAX_BLOCKS * bSize]; + + if (!isvalid ()) return false; fillFormat (&format, rate, bits, channels); rc = waveInOpen (&dev_, WAVE_MAPPER, &format, (DWORD) waveIn_callback, (DWORD) this, CALLBACK_FUNCTION); + debug_printf ("%d = waveInOpen (rate=%d bits=%d channels=%d)", rc, rate, bits, channels); + if (rc == MMSYSERR_NOERROR) { - setOwner (); if (!init (bSize)) - { - stop (); - return false; - } + return false; } - - debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels, - (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK"); - return (rc == MMSYSERR_NOERROR); } @@ -826,45 +758,27 @@ fhandler_dev_dsp::Audio_in::stop () { MMRESULT rc; WAVEHDR *pHdr; - bool gotblock; - debug_printf ("dev_=%08x pid=%d owner=%d", (int)dev_, - GetCurrentProcessId (), getOwner ()); - if (getOwner () && !denyAccess ()) + debug_printf ("dev_=%08x", (int)dev_); + if (dev_) { - rc = waveInReset (dev_); /* Note that waveInReset calls our callback for all incomplete buffers. Since all the win32 wave functions appear to use a common lock, we must not call into the wave API from the callback. Otherwise we end up in a deadlock. */ - debug_printf ("waveInReset rc=%d", rc); + rc = waveInReset (dev_); + debug_printf ("%d = waveInReset ()", rc); - do + while (Qisr2app_->recv (&pHdr)) { - lock (); - gotblock = Qisr2app_->recv (&pHdr); - unlock (); - if (gotblock) - { - rc = waveInUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR)); - debug_printf ("waveInUnprepareHeader Block 0x%08x %s", pHdr, - (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK"); - } + rc = waveInUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR)); + debug_printf ("%d = waveInUnprepareHeader (0x%08x)", rc, pHdr); } - while (gotblock); - while (Qapp2app_->recv (&pHdr)) - /* flush queue */; rc = waveInClose (dev_); - debug_printf ("waveInClose rc=%d", rc); + debug_printf ("%d = waveInClose ()", rc); - clearOwner (); - } - - if (bigwavebuffer_) - { - delete[] bigwavebuffer_; - bigwavebuffer_ = NULL; + Qisr2app_->dellock (); } } @@ -872,13 +786,21 @@ bool fhandler_dev_dsp::Audio_in::queueblock (WAVEHDR *pHdr) { MMRESULT rc; - pHdr->dwFlags = 0; rc = waveInPrepareHeader (dev_, pHdr, sizeof (WAVEHDR)); + debug_printf ("%d = waveInPrepareHeader (0x%08x)", rc, pHdr); if (rc == MMSYSERR_NOERROR) - rc = waveInAddBuffer (dev_, pHdr, sizeof (WAVEHDR)); - debug_printf ("waveInAddBuffer Block 0x%08x %s", pHdr, - (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK"); - return (rc == MMSYSERR_NOERROR); + { + rc = waveInAddBuffer (dev_, pHdr, sizeof (WAVEHDR)); + debug_printf ("%d = waveInAddBuffer (0x%08x)", rc, pHdr); + } + if (rc == MMSYSERR_NOERROR) + return true; + + /* FIXME: Should the calling function return an error instead ?*/ + pHdr->dwFlags = 0; /* avoid calling UnprepareHeader again */ + pHdr->dwBytesRecorded = 0; /* no data will have been read */ + Qisr2app_->send (pHdr); + return false; } bool @@ -888,17 +810,18 @@ fhandler_dev_dsp::Audio_in::init (unsign int i; // try to queue all of our buffer for reception + Qisr2app_->reset (); for (i = 0; i < MAX_BLOCKS; i++) { wavehdr_[i].lpData = &bigwavebuffer_[i * blockSize]; wavehdr_[i].dwBufferLength = blockSize; + wavehdr_[i].dwFlags = 0; if (!queueblock (&wavehdr_[i])) break; } pHdr_ = NULL; rc = waveInStart (dev_); - debug_printf ("waveInStart=%d %s queued=%d", - rc, (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK", i); + debug_printf ("%d = waveInStart (), queued=%d", rc, i); return (rc == MMSYSERR_NOERROR); } @@ -947,29 +870,21 @@ void fhandler_dev_dsp::Audio_in::waitfordata () { WAVEHDR *pHdr; - bool gotblock; MMRESULT rc; if (pHdr_ != NULL) return; - while (Qapp2app_->recv (&pHdr) == false) + while (!Qisr2app_->recv (&pHdr)) { - lock (); - gotblock = Qisr2app_->recv (&pHdr); - unlock (); - if (gotblock) - { - rc = waveInUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR)); - if (rc == MMSYSERR_NOERROR) - Qapp2app_->send (pHdr); - else - debug_printf ("error UnprepareHeader 0x%08x", pHdr); - } - else - { - debug_printf ("100ms"); - Sleep (100); - } + debug_printf ("100ms"); + Sleep (100); + } + if (pHdr->dwFlags) /* Zero if queued following error in queueblock */ + { + /* Errors are ignored here. They will probbaly cause a failure + in the subsequent PrepareHeader */ + rc = waveInUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR)); + debug_printf ("%d = waveInUnprepareHeader (0x%08x)", rc, pHdr); } pHdr_ = pHdr; bufferIndex_ = 0; @@ -981,12 +896,9 @@ fhandler_dev_dsp::Audio_in::buf_info (au { p->fragstotal = MAX_BLOCKS; p->fragsize = blockSize (rate, bits, channels); - if (getOwner ()) + if (this && dev_) { - lock (); p->fragments = Qisr2app_->query (); - unlock (); - p->fragments += Qapp2app_->query (); if (pHdr_ != NULL) p->bytes = pHdr_->dwBytesRecorded - bufferIndex_ + p->fragsize * p->fragments; @@ -1000,13 +912,10 @@ fhandler_dev_dsp::Audio_in::buf_info (au } } -// This is called on an interrupt so use locking.. -void +inline void fhandler_dev_dsp::Audio_in::callback_blockfull (WAVEHDR *pHdr) { - lock (); Qisr2app_->send (pHdr); - unlock (); } static void CALLBACK @@ -1024,10 +933,7 @@ waveIn_callback (HWAVEIN hWave, UINT msg /* ------------------------------------------------------------------------ /dev/dsp handler - ------------------------------------------------------------------------ - instances of the handler statics */ -int fhandler_dev_dsp::open_count = 0; - + ------------------------------------------------------------------------ */ fhandler_dev_dsp::fhandler_dev_dsp (): fhandler_base () { @@ -1036,21 +942,16 @@ fhandler_dev_dsp::fhandler_dev_dsp (): audio_out_ = NULL; } -fhandler_dev_dsp::~fhandler_dev_dsp () -{ - close (); - debug_printf ("0x%08x end", (int)this); -} - int fhandler_dev_dsp::open (int flags, mode_t mode) { - open_count++; - if (open_count > 1) + if (cygheap->fdtab.find_archetype (pc.dev)) { set_errno (EBUSY); return 0; } + int err = 0; + UINT num_in = 0, num_out = 0; set_flags ((flags & ~O_TEXT) | O_BINARY); // Work out initial sample format & frequency, /dev/dsp defaults audioformat_ = AFMT_U8; @@ -1059,102 +960,84 @@ fhandler_dev_dsp::open (int flags, mode_ audiochannels_ = 1; switch (flags & O_ACCMODE) { + case O_RDWR: + if ((num_in = waveInGetNumDevs ()) == 0) + err = ENXIO; + /* Fall through */ case O_WRONLY: - audio_out_ = new Audio_out; - if (!audio_out_->query (audiofreq_, audiobits_, audiochannels_)) - { - delete audio_out_; - audio_out_ = NULL; - } + if ((num_out = waveOutGetNumDevs ()) == 0) + err = ENXIO; break; case O_RDONLY: - audio_in_ = new Audio_in; - if (!audio_in_->query (audiofreq_, audiobits_, audiochannels_)) - { - delete audio_in_; - audio_in_ = NULL; - } - break; - case O_RDWR: - audio_out_ = new Audio_out; - if (audio_out_->query (audiofreq_, audiobits_, audiochannels_)) - { - audio_in_ = new Audio_in; - if (!audio_in_->query (audiofreq_, audiobits_, audiochannels_)) - { - delete audio_in_; - audio_in_ = NULL; - audio_out_->stop (); - delete audio_out_; - audio_out_ = NULL; - } - } - else - { - delete audio_out_; - audio_out_ = NULL; - } + if ((num_in = waveInGetNumDevs ()) == 0) + err = ENXIO; break; default: - set_errno (EINVAL); - return 0; - } // switch (flags & O_ACCMODE) - int rc; - if (audio_in_ || audio_out_) - { /* All tried query () succeeded */ - rc = 1; + err = EINVAL; + } + + if (!err) + { set_open_status (); need_fork_fixup (true); - close_on_exec (true); + nohandle (true); + + // FIXME: Do this better someday + fhandler_dev_dsp *arch = (fhandler_dev_dsp *) cmalloc (HEAP_ARCHETYPES, sizeof (*this)); + archetype = arch; + *((fhandler_dev_dsp **) cygheap->fdtab.add_archetype ()) = arch; + *arch = *this; + archetype->usecount = 1; } else - { /* One of the tried query () failed */ - rc = 0; - set_errno (EIO); - } - debug_printf ("ACCMODE=0x%08x audio_in=%08x audio_out=%08x, rc=%d", - flags & O_ACCMODE, (int)audio_in_, (int)audio_out_, rc); - return rc; + set_errno (err); + + debug_printf ("ACCMODE=0x%08x audio_in=%d audio_out=%d, err=%d", + flags & O_ACCMODE, num_in, num_out, err); + return !err; } -#define RETURN_ERROR_WHEN_BUSY(audio)\ - if ((audio)->denyAccess ()) \ - {\ - set_errno (EBUSY);\ - return -1;\ - } +#define IS_WRITE() ((get_flags() & O_ACCMODE) != O_RDONLY) +#define IS_READ() ((get_flags() & O_ACCMODE) != O_WRONLY) int fhandler_dev_dsp::write (const void *ptr, size_t len) { + debug_printf ("ptr=%08x len=%d", ptr, len); + if ((fhandler_dev_dsp *) archetype != this) + return ((fhandler_dev_dsp *)archetype)->write(ptr, len); + int len_s = len; const char *ptr_s = static_cast (ptr); - debug_printf ("ptr=%08x len=%d", ptr, len); if (!audio_out_) + if (IS_WRITE ()) + { + debug_printf ("Allocating"); + if (!(audio_out_ = new Audio_out)) + return -1; + + /* check for wave file & get parameters & skip header if possible. */ + + if (audio_out_->parsewav (ptr_s, len_s, + audiofreq_, audiobits_, audiochannels_)) + debug_printf ("=> ptr_s=%08x len_s=%d", ptr_s, len_s); + } + else + { + set_errno (EBADF); // device was opened for read? + return -1; + } + + /* Open audio device properly with callbacks. + Private parameters were set in call to parsewav. + This is a no-op when there are successive writes in the same process */ + if (!audio_out_->start ()) { - set_errno (EACCES); // device was opened for read? + set_errno (EIO); return -1; } - RETURN_ERROR_WHEN_BUSY (audio_out_); - if (audio_out_->getOwner () == 0L) - { // No owner yet, lets do it - // check for wave file & get parameters & skip header if possible. - if (audio_out_->parsewav (ptr_s, len_s, - audiofreq_, audiobits_, audiochannels_)) - { // update our format conversion - debug_printf ("=> ptr_s=%08x len_s=%d", ptr_s, len_s); - audioformat_ = ((audiobits_ == 8) ? AFMT_U8 : AFMT_S16_LE); - audio_out_->setformat (audioformat_); - } - // Open audio device properly with callbacks. - if (!audio_out_->start (audiofreq_, audiobits_, audiochannels_)) - { - set_errno (EIO); - return -1; - } - } - + audio_out_->write (ptr_s, len_s); return len; } @@ -1163,28 +1046,36 @@ void __stdcall fhandler_dev_dsp::read (void *ptr, size_t& len) { debug_printf ("ptr=%08x len=%d", ptr, len); + if ((fhandler_dev_dsp *) archetype != this) + return ((fhandler_dev_dsp *)archetype)->read(ptr, len); + if (!audio_in_) + if (IS_READ ()) + { + debug_printf ("Allocating"); + if (!(audio_in_ = new Audio_in)) + { + len = (size_t)-1; + return; + } + audio_in_->setconvert (audioformat_); + } + else + { + len = (size_t)-1; + set_errno (EBADF); // device was opened for write? + return; + } + + /* Open audio device properly with callbacks. + This is a noop when there are successive reads in the same process */ + if (!audio_in_->start (audiofreq_, audiobits_, audiochannels_)) { len = (size_t)-1; - set_errno (EACCES); // device was opened for write? - return; - } - if (audio_in_->denyAccess ()) - { - len = (size_t)-1; - set_errno (EBUSY); + set_errno (EIO); return; } - if (audio_in_->getOwner () == 0L) - { // No owner yet. Let's take it - // Open audio device properly with callbacks. - if (!audio_in_->start (audiofreq_, audiobits_, audiochannels_)) - { - len = (size_t)-1; - set_errno (EIO); - return; - } - } + audio_in_->read ((char *)ptr, (int&)len); return; } @@ -1195,28 +1086,41 @@ fhandler_dev_dsp::lseek (_off64_t offset return 0; } -int -fhandler_dev_dsp::close (void) +void +fhandler_dev_dsp::close_audio_in () { - debug_printf ("audio_in=%08x audio_out=%08x", - (int)audio_in_, (int)audio_out_); if (audio_in_) { + audio_in_->stop (); delete audio_in_; audio_in_ = NULL; } +} + +void +fhandler_dev_dsp::close_audio_out (bool immediately) +{ if (audio_out_) { - if (exit_state != ES_NOT_EXITING) - { // emergency close due to call to exit() or Ctrl-C: - // do not wait for all pending audio to be played - audio_out_->stop (true); - } + audio_out_->stop (immediately); delete audio_out_; audio_out_ = NULL; } - if (open_count > 0) - open_count--; +} + +int +fhandler_dev_dsp::close (void) +{ + debug_printf ("audio_in=%08x audio_out=%08x", + (int)audio_in_, (int)audio_out_); + if ((fhandler_dev_dsp *) archetype != this) + return ((fhandler_dev_dsp *)archetype)->close (); + + if (--usecount == 0) + { + close_audio_in (); + close_audio_out (exit_state != ES_NOT_EXITING); + } return 0; } @@ -1224,55 +1128,45 @@ int fhandler_dev_dsp::dup (fhandler_base * child) { debug_printf (""); - fhandler_dev_dsp *fhc = (fhandler_dev_dsp *) child; - - fhc->set_flags (get_flags ()); - fhc->audiochannels_ = audiochannels_; - fhc->audiobits_ = audiobits_; - fhc->audiofreq_ = audiofreq_; - fhc->audioformat_ = audioformat_; + child->archetype = archetype; + archetype->usecount++; return 0; } int fhandler_dev_dsp::ioctl (unsigned int cmd, void *ptr) { - int *intptr = (int *) ptr; debug_printf ("audio_in=%08x audio_out=%08x", (int)audio_in_, (int)audio_out_); + if ((fhandler_dev_dsp *) archetype != this) + return ((fhandler_dev_dsp *)archetype)->ioctl(cmd, ptr); + + int *intptr = (int *) ptr; switch (cmd) { #define CASE(a) case a : debug_printf ("/dev/dsp: ioctl %s", #a); CASE (SNDCTL_DSP_RESET) - if (audio_out_) - { - RETURN_ERROR_WHEN_BUSY (audio_out_); - audio_out_->stop (true); - } - if (audio_in_) - { - RETURN_ERROR_WHEN_BUSY (audio_in_); - audio_in_->stop (); - } - return 0; + close_audio_in (); + close_audio_out (true); + return 0; break; CASE (SNDCTL_DSP_GETBLKSIZE) - if (audio_out_) + /* This is valid even if audio_X is NULL */ + if (IS_WRITE ()) { *intptr = audio_out_->blockSize (audiofreq_, audiobits_, audiochannels_); } else - { // I am very sure that audio_in_ is valid + { // I am very sure that IS_READ is valid *intptr = audio_in_->blockSize (audiofreq_, audiobits_, audiochannels_); } return 0; - break; CASE (SNDCTL_DSP_SETFMT) { @@ -1296,11 +1190,9 @@ fhandler_dev_dsp::ioctl (unsigned int cm default: nBits = 0; } - if (nBits && audio_out_) + if (nBits && IS_WRITE ()) { - RETURN_ERROR_WHEN_BUSY (audio_out_); - audio_out_->stop (); - audio_out_->setformat (*intptr); + close_audio_out (); if (audio_out_->query (audiofreq_, nBits, audiochannels_)) { audiobits_ = nBits; @@ -1312,11 +1204,9 @@ fhandler_dev_dsp::ioctl (unsigned int cm return -1; } } - if (nBits && audio_in_) + if (nBits && IS_READ ()) { - RETURN_ERROR_WHEN_BUSY (audio_in_); - audio_in_->stop (); - audio_in_->setformat (*intptr); + close_audio_in (); if (audio_in_->query (audiofreq_, nBits, audiochannels_)) { audiobits_ = nBits; @@ -1330,14 +1220,11 @@ fhandler_dev_dsp::ioctl (unsigned int cm } return 0; } - break; CASE (SNDCTL_DSP_SPEED) - { - if (audio_out_) + if (IS_WRITE ()) { - RETURN_ERROR_WHEN_BUSY (audio_out_); - audio_out_->stop (); + close_audio_out (); if (audio_out_->query (*intptr, audiobits_, audiochannels_)) audiofreq_ = *intptr; else @@ -1346,10 +1233,9 @@ fhandler_dev_dsp::ioctl (unsigned int cm return -1; } } - if (audio_in_) + if (IS_READ ()) { - RETURN_ERROR_WHEN_BUSY (audio_in_); - audio_in_->stop (); + close_audio_in (); if (audio_in_->query (*intptr, audiobits_, audiochannels_)) audiofreq_ = *intptr; else @@ -1359,49 +1245,22 @@ fhandler_dev_dsp::ioctl (unsigned int cm } } return 0; - } - break; CASE (SNDCTL_DSP_STEREO) { int nChannels = *intptr + 1; - - if (audio_out_) - { - RETURN_ERROR_WHEN_BUSY (audio_out_); - audio_out_->stop (); - if (audio_out_->query (audiofreq_, audiobits_, nChannels)) - audiochannels_ = nChannels; - else - { - *intptr = audiochannels_ - 1; - return -1; - } - } - if (audio_in_) - { - RETURN_ERROR_WHEN_BUSY (audio_in_); - audio_in_->stop (); - if (audio_in_->query (audiofreq_, audiobits_, nChannels)) - audiochannels_ = nChannels; - else - { - *intptr = audiochannels_ - 1; - return -1; - } - } - return 0; + int res = ioctl (SNDCTL_DSP_CHANNELS, &nChannels); + *intptr = nChannels - 1; + return res; } - break; CASE (SNDCTL_DSP_CHANNELS) { int nChannels = *intptr; - if (audio_out_) + if (IS_WRITE ()) { - RETURN_ERROR_WHEN_BUSY (audio_out_); - audio_out_->stop (); + close_audio_out (); if (audio_out_->query (audiofreq_, audiobits_, nChannels)) audiochannels_ = nChannels; else @@ -1410,10 +1269,9 @@ fhandler_dev_dsp::ioctl (unsigned int cm return -1; } } - if (audio_in_) + if (IS_READ ()) { - RETURN_ERROR_WHEN_BUSY (audio_in_); - audio_in_->stop (); + close_audio_in (); if (audio_in_->query (audiofreq_, audiobits_, nChannels)) audiochannels_ = nChannels; else @@ -1424,76 +1282,55 @@ fhandler_dev_dsp::ioctl (unsigned int cm } return 0; } - break; CASE (SNDCTL_DSP_GETOSPACE) { - audio_buf_info *p = (audio_buf_info *) ptr; - if (audio_out_) + if (!IS_WRITE ()) { - RETURN_ERROR_WHEN_BUSY (audio_out_); - audio_out_->buf_info (p, audiofreq_, audiobits_, audiochannels_); - debug_printf ("ptr=%p frags=%d fragsize=%d bytes=%d", - ptr, p->fragments, p->fragsize, p->bytes); + set_errno(EBADF); + return -1; } + audio_buf_info *p = (audio_buf_info *) ptr; + audio_out_->buf_info (p, audiofreq_, audiobits_, audiochannels_); + debug_printf ("ptr=%p frags=%d fragsize=%d bytes=%d", + ptr, p->fragments, p->fragsize, p->bytes); return 0; } - break; CASE (SNDCTL_DSP_GETISPACE) { - audio_buf_info *p = (audio_buf_info *) ptr; - if (audio_in_) + if (!IS_READ ()) { - RETURN_ERROR_WHEN_BUSY (audio_in_); - audio_in_->buf_info (p, audiofreq_, audiobits_, audiochannels_); - debug_printf ("ptr=%p frags=%d fragsize=%d bytes=%d", - ptr, p->fragments, p->fragsize, p->bytes); + set_errno(EBADF); + return -1; } + audio_buf_info *p = (audio_buf_info *) ptr; + audio_in_->buf_info (p, audiofreq_, audiobits_, audiochannels_); + debug_printf ("ptr=%p frags=%d fragsize=%d bytes=%d", + ptr, p->fragments, p->fragsize, p->bytes); return 0; } - break; CASE (SNDCTL_DSP_SETFRAGMENT) - { // Fake!! esound & mikmod require this on non PowerPC platforms. // return 0; - } - break; CASE (SNDCTL_DSP_GETFMTS) - { *intptr = AFMT_S16_LE | AFMT_U8; // only native formats returned here return 0; - } - break; CASE (SNDCTL_DSP_GETCAPS) - { *intptr = DSP_CAP_BATCH | DSP_CAP_DUPLEX; return 0; - } - break; CASE (SNDCTL_DSP_POST) CASE (SNDCTL_DSP_SYNC) - { - if (audio_out_) - { - // Stop audio out device - RETURN_ERROR_WHEN_BUSY (audio_out_); - audio_out_->stop (); - } - if (audio_in_) - { - // Stop audio in device - RETURN_ERROR_WHEN_BUSY (audio_in_); - audio_in_->stop (); - } + // Stop audio out device + close_audio_out (); + // Stop audio in device + close_audio_in (); return 0; - } - break; default: debug_printf ("/dev/dsp: ioctl 0x%08x not handled yet! FIXME:", cmd); @@ -1516,7 +1353,10 @@ fhandler_dev_dsp::fixup_after_fork (HAND { // called from new child process debug_printf ("audio_in=%08x audio_out=%08x", (int)audio_in_, (int)audio_out_); - if (audio_in_ ) + if (archetype != this) + return ((fhandler_dev_dsp *)archetype)->fixup_after_fork (parent); + + if (audio_in_) audio_in_ ->fork_fixup (parent); if (audio_out_) audio_out_->fork_fixup (parent); @@ -1527,6 +1367,9 @@ fhandler_dev_dsp::fixup_after_exec () { debug_printf ("audio_in=%08x audio_out=%08x", (int)audio_in_, (int)audio_out_); -} - + if (archetype != this) + return ((fhandler_dev_dsp *)archetype)->fixup_after_exec (); + audio_in_ = NULL; + audio_out_ = NULL; +}