-/* 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
#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,
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);
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
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_;
};
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,
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 ();
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
};
/* --------------------------------------------------------------------
// 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 ()
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
{
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)
+inline bool
+fhandler_dev_dsp::Audio::isvalid ()
{
- debug_printf ("parent=0x%08x", parent);
- InitializeCriticalSection (&lock_);
-}
-
-bool
-fhandler_dev_dsp::Audio::denyAccess ()
-{
- if (owner_ == 0L)
- return false;
- return (GetCurrentProcessId () != owner_);
+ return bigwavebuffer_ && Qisr2app_ && Qisr2app_->isvalid ();
}
void
-fhandler_dev_dsp::Audio::setformat (int format)
+fhandler_dev_dsp::Audio::setconvert (int format)
{
switch (format)
{
}
//=======================================================================
-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)
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);
}
{
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)
{
}
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);
-
- clearOwner ();
- }
+ debug_printf ("%d = waveOutClose ()", rc);
- if (bigwavebuffer_)
- {
- delete[] bigwavebuffer_;
- bigwavebuffer_ = NULL;
+ Qisr2app_->dellock ();
}
}
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
}
}
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;
}
else
{
+ p->fragsize = blockSize (rate, bits, channels);
p->fragments = MAX_BLOCKS;
p->bytes = p->fragsize * p->fragments;
}
/* 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;
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);
}
}
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;
// 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;
}
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;
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;
}
}
}
debug_printf ("Discard %d bytes wave header", skip);
pData += skip;
nBytes -= skip;
+ setconvert (bits_ == 8 ? AFMT_U8 : AFMT_S16_LE);
return true;
}
}
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
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
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);
}
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);
}
{
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 ();
}
}
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
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);
}
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;
{
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;
}
}
-// 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
/* ------------------------------------------------------------------------
/dev/dsp handler
- ------------------------------------------------------------------------
- instances of the handler statics */
-int fhandler_dev_dsp::open_count = 0;
-
+ ------------------------------------------------------------------------ */
fhandler_dev_dsp::fhandler_dev_dsp ():
fhandler_base ()
{
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;
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 <const char *> (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;
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;
}
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;
}
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)
{
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;
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;
}
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
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
}
}
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
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
}
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);
{ // 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);
{
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;
+}