Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2004-03-23 14:05:56 +0300
committerCorinna Vinschen <corinna@vinschen.de>2004-03-23 14:05:56 +0300
commit6653af6cc2d118f50dc8b7a37ba24d9bb9865ef6 (patch)
tree054ba40418ade238893f0fb06bba09fdb1cc96ff /winsup/cygwin/fhandler_dsp.cc
parente437499bace8b40337198b96d62a742c0678ec57 (diff)
* autoload.cc: Load eight more functions for waveIn support.
* fhandler.h (class fhandler_dev_dsp): Add class Audio, class Audio_in and class Audio_out members and audio_in_, audio_out_ pointers so that future changes are restricted to file fhandler_dsp.cc. * fhandler_dsp.cc (fhandler_dev_dsp::Audio): Add this class to treat things common to audio recording and playback. Add more format conversions. (fhandler_dev_dsp::Audio::queue): New queues for buffer management to fix incomplete cleanup of buffers passed to the wave device. (fhandler_dev_dsp::Audio_in): New, added class to implement audio recording. (fhandler_dev_dsp::Audio_out): Rework to use functionality provided by fhandler_dev_dsp::Audio. Allocate memory audio buffers late, just before write. (fhandler_dev_dsp::Audio_out::start): Size of wave buffer allocated here depends on audio rate/bits/channels. (fhandler_dev_dsp::Audio_in::start): Ditto. (fhandler_dev_dsp::setupwav): Replaced by following function. (fhandler_dev_dsp::Audio_out::parsewav): Does not setup wave device any more. Discard wave header properly. (fhandler_dev_dsp::open): Add O_RDONLY and_RDWR as legal modes. Protect against re-open. Activate fork_fixup. (fhandler_dev_dsp::ioctl): Protect against actions when audio is active. SNDCTL_DSP_GETFMTS only returns formats supported by mmsystem wave API, not all supported formats. SNDCTL_DSP_GETBLKSIZE result now depends on current audio format. (fhandler_dev_dsp::fixup_after_fork): Call fork_fixup for the Audio classes to let them duplicate the CRITICAL_SECTION.
Diffstat (limited to 'winsup/cygwin/fhandler_dsp.cc')
-rw-r--r--winsup/cygwin/fhandler_dsp.cc1483
1 files changed, 1141 insertions, 342 deletions
diff --git a/winsup/cygwin/fhandler_dsp.cc b/winsup/cygwin/fhandler_dsp.cc
index 01e1ea6ff..8791a64a3 100644
--- a/winsup/cygwin/fhandler_dsp.cc
+++ b/winsup/cygwin/fhandler_dsp.cc
@@ -3,6 +3,8 @@
Copyright 2001, 2002, 2003, 2004 Red Hat, Inc
Written by Andy Younger (andy@snoogie.demon.co.uk)
+ Extended by Gerd Spalink (Gerd.Spalink@t-online.de)
+ to support recording from the audio input
This file is part of Cygwin.
@@ -20,314 +22,624 @@ details. */
#include "path.h"
#include "fhandler.h"
-//------------------------------------------------------------------------
-// Simple encapsulation of the win32 audio device.
-//
-static void CALLBACK wave_callback (HWAVE hWave, UINT msg, DWORD instance,
- DWORD param1, DWORD param2);
-class Audio
+/*------------------------------------------------------------------------
+ Simple encapsulation of the win32 audio device.
+
+ Implementation Notes
+ 1. Audio buffers are created dynamically 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,
+ so that up to 1.5 seconds can be buffered within Win32.
+ The buffer size can be queried with the ioctl SNDCTL_DSP_GETBLKSIZE,
+ but for this implementation only returns meaningful results if
+ sampling rate, number of channels and number of bits per sample
+ are not changed afterwards.
+
+ 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
+ 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
+ */
+
+class fhandler_dev_dsp::Audio
+{ // This class contains functionality common to Audio_in and Audio_out
+ public:
+ 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);
+ 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 convert_S16LE_U16BE (unsigned char *buffer, int size_bytes);
+ void convert_S16LE_S16BE (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_;
+};
+
+class fhandler_dev_dsp::Audio::queue
+{ // non-blocking fixed size queues for buffer management
+ public:
+ queue (int depth = 4);
+ ~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
+
+ private:
+ int head_;
+ int tail_;
+ int depth_, depth1_;
+ WAVEHDR **storage_;
+};
+
+static void CALLBACK waveOut_callback (HWAVEOUT hWave, UINT msg, DWORD instance,
+ DWORD param1, DWORD param2);
+
+class fhandler_dev_dsp::Audio_out: public Audio
+{
+ public:
+ Audio_out ();
+ ~Audio_out ();
+
+ bool query (int rate, int bits, int channels);
+ bool start (int rate, int bits, int channels);
+ void stop ();
+ 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);
+
+ 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
+};
+
+static void CALLBACK waveIn_callback (HWAVEIN hWave, UINT msg, DWORD instance,
+ DWORD param1, DWORD param2);
+
+class fhandler_dev_dsp::Audio_in: public Audio
{
public:
- enum
- {
- MAX_BLOCKS = 12,
- BLOCK_SIZE = 16384,
- TOT_BLOCK_SIZE = BLOCK_SIZE + sizeof (WAVEHDR)
- };
-
- Audio ();
- ~Audio ();
-
- bool open (int rate, int bits, int channels, bool bCallback = false);
- void close ();
- int getvolume ();
- void setvolume (int newVolume);
- bool write (const void *pSampleData, int nBytes);
- int blocks ();
- void callback_sampledone (void *pData);
- void setformat (int format) {formattype_ = format;}
- int numbytesoutput ();
-
- void *operator new (size_t, void *p) {return p;}
+ Audio_in ();
+ ~Audio_in ();
-private:
- char *initialisebuffer ();
- void waitforcallback ();
- bool flush ();
-
- HWAVEOUT dev_;
- volatile int nBlocksInQue_;
- int nBytesWritten_;
- char *buffer_;
- int bufferIndex_;
- CRITICAL_SECTION lock_;
- char *freeblocks_[MAX_BLOCKS];
- int formattype_;
+ bool query (int rate, int bits, int channels);
+ bool start (int rate, int bits, int channels);
+ void stop ();
+ bool read (char *pSampleData, int &nBytes);
+ void callback_blockfull (WAVEHDR *pHdr);
- char bigwavebuffer_[MAX_BLOCKS * TOT_BLOCK_SIZE];
+private:
+ bool init (unsigned blockSize);
+ 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
};
-static char audio_buf[sizeof (class Audio)];
+/* --------------------------------------------------------------------
+ Implementation */
+
+// 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_];
+}
+
+fhandler_dev_dsp::Audio::queue::~queue ()
+{
+ delete[] storage_;
+}
-Audio::Audio ()
+bool
+fhandler_dev_dsp::Audio::queue::send (WAVEHDR *x)
+{
+ if (query () == depth_)
+ return false;
+ storage_[tail_] = x;
+ tail_++;
+ if (tail_ == depth1_)
+ tail_ = 0;
+ return true;
+}
+
+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;
+}
+
+int
+fhandler_dev_dsp::Audio::queue::query ()
+{
+ int n = tail_ - head_;
+ if (n < 0)
+ n += depth1_;
+ return n;
+}
+
+// Audio class implements functionality need for both read and write
+fhandler_dev_dsp::Audio::Audio ()
{
InitializeCriticalSection (&lock_);
- memset (bigwavebuffer_, 0, sizeof (bigwavebuffer_));
- for (int i = 0; i < MAX_BLOCKS; i++)
- freeblocks_[i] = &bigwavebuffer_[i * TOT_BLOCK_SIZE];
+ convert_ = &fhandler_dev_dsp::Audio::convert_none;
+ owner_ = 0L;
}
-Audio::~Audio ()
+fhandler_dev_dsp::Audio::~Audio ()
{
- if (dev_)
- close ();
DeleteCriticalSection (&lock_);
}
-bool
-Audio::open (int rate, int bits, int channels, bool bCallback)
+void
+fhandler_dev_dsp::Audio::fork_fixup (HANDLE parent)
{
- WAVEFORMATEX format;
- int nDevices = waveOutGetNumDevs ();
+ debug_printf ("parent=0x%08x", parent);
+ InitializeCriticalSection (&lock_);
+}
- nBytesWritten_ = 0L;
- bufferIndex_ = 0;
- buffer_ = 0L;
- debug_printf ("number devices %d", nDevices);
- if (nDevices <= 0)
+bool
+fhandler_dev_dsp::Audio::denyAccess ()
+{
+ if (owner_ == 0L)
return false;
+ return (GetCurrentProcessId () != owner_);
+}
- debug_printf ("trying to map device freq %d, bits %d, "
- "channels %d, callback %d", rate, bits, channels,
- bCallback);
-
- int bytesperSample = bits / 8;
-
- memset (&format, 0, sizeof (format));
- format.wFormatTag = WAVE_FORMAT_PCM;
- format.wBitsPerSample = bits;
- format.nChannels = channels;
- format.nSamplesPerSec = rate;
- format.nAvgBytesPerSec = format.nSamplesPerSec * format.nChannels *
- bytesperSample;
- format.nBlockAlign = format.nChannels * bytesperSample;
-
- nBlocksInQue_ = 0;
- HRESULT res = waveOutOpen (&dev_, WAVE_MAPPER, &format, (DWORD) wave_callback,
- (DWORD) this, bCallback ? CALLBACK_FUNCTION : 0);
- if (res == S_OK)
+void
+fhandler_dev_dsp::Audio::setformat (int format)
+{
+ switch (format)
{
- debug_printf ("Sucessfully opened!");
- return true;
+ case AFMT_S8:
+ convert_ = &fhandler_dev_dsp::Audio::convert_U8_S8;
+ debug_printf ("U8_S8");
+ break;
+ case AFMT_U16_LE:
+ convert_ = &fhandler_dev_dsp::Audio::convert_S16LE_U16LE;
+ debug_printf ("S16LE_U16LE");
+ break;
+ case AFMT_U16_BE:
+ convert_ = &fhandler_dev_dsp::Audio::convert_S16LE_U16BE;
+ debug_printf ("S16LE_U16BE");
+ break;
+ case AFMT_S16_BE:
+ convert_ = &fhandler_dev_dsp::Audio::convert_S16LE_S16BE;
+ debug_printf ("S16LE_S16BE");
+ break;
+ default:
+ convert_ = &fhandler_dev_dsp::Audio::convert_none;
+ debug_printf ("none");
}
- else
+}
+
+void
+fhandler_dev_dsp::Audio::convert_U8_S8 (unsigned char *buffer,
+ int size_bytes)
+{
+ while (size_bytes-- > 0)
{
- debug_printf ("failed to open");
- return false;
+ *buffer ^= (unsigned char)0x80;
+ buffer++;
}
}
void
-Audio::close ()
+fhandler_dev_dsp::Audio::convert_S16LE_U16BE (unsigned char *buffer,
+ int size_bytes)
{
- if (dev_)
+ int size_samples = size_bytes / 2;
+ unsigned char hi, lo;
+ while (size_samples-- > 0)
{
- flush (); // force out last block whatever size..
+ hi = buffer[0];
+ lo = buffer[1];
+ *buffer++ = lo;
+ *buffer++ = hi ^ (unsigned char)0x80;
+ }
+}
- while (blocks ()) // block till finished..
- waitforcallback ();
+void
+fhandler_dev_dsp::Audio::convert_S16LE_U16LE (unsigned char *buffer,
+ int size_bytes)
+{
+ int size_samples = size_bytes / 2;
+ while (size_samples-- > 0)
+ {
+ buffer++;
+ *buffer ^= (unsigned char)0x80;
+ buffer++;
+ }
+}
- waveOutReset (dev_);
- waveOutClose (dev_);
- dev_ = 0L;
+void
+fhandler_dev_dsp::Audio::convert_S16LE_S16BE (unsigned char *buffer,
+ int size_bytes)
+{
+ int size_samples = size_bytes / 2;
+ unsigned char hi, lo;
+ while (size_samples-- > 0)
+ {
+ hi = buffer[0];
+ lo = buffer[1];
+ *buffer++ = lo;
+ *buffer++ = hi;
}
- nBytesWritten_ = 0L;
}
-int
-Audio::numbytesoutput ()
+void
+fhandler_dev_dsp::Audio::fillFormat (WAVEFORMATEX * format,
+ int rate, int bits, int channels)
{
- return nBytesWritten_;
+ memset (format, 0, sizeof (*format));
+ format->wFormatTag = WAVE_FORMAT_PCM;
+ format->wBitsPerSample = bits;
+ format->nChannels = channels;
+ format->nSamplesPerSec = rate;
+ format->nAvgBytesPerSec = format->nSamplesPerSec * format->nChannels
+ * (bits / 8);
+ format->nBlockAlign = format->nChannels * (bits / 8);
}
-int
-Audio::getvolume ()
+// calculate a good block size
+unsigned
+fhandler_dev_dsp::Audio::blockSize (int rate, int bits, int channels)
{
- DWORD volume;
+ unsigned blockSize;
+ blockSize = ((bits / 8) * channels * rate) / 8; // approx 125ms per block
+ // round up to multiple of 64
+ blockSize += 0x3f;
+ blockSize &= ~0x3f;
+ return blockSize;
+}
- waveOutGetVolume (dev_, &volume);
- return ((volume >> 16) + (volume & 0xffff)) >> 1;
+//=======================================================================
+fhandler_dev_dsp::Audio_out::Audio_out (): Audio ()
+{
+ bigwavebuffer_ = NULL;
+ Qisr2app_ = new queue (MAX_BLOCKS);
+ Qapp2app_ = new queue (MAX_BLOCKS);
}
-void
-Audio::setvolume (int newVolume)
+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)
{
- waveOutSetVolume (dev_, (newVolume << 16) | newVolume);
+ WAVEFORMATEX format;
+ MMRESULT rc;
+
+ 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");
+ return (rc == MMSYSERR_NOERROR);
+}
+
+bool
+fhandler_dev_dsp::Audio_out::start (int rate, int bits, int channels)
+{
+ WAVEFORMATEX format;
+ MMRESULT rc;
+ unsigned bSize = blockSize (rate, bits, channels);
+ bigwavebuffer_ = new char[MAX_BLOCKS * bSize];
+ if (bigwavebuffer_ == NULL)
+ return false;
+
+ int nDevices = waveOutGetNumDevs ();
+ debug_printf ("number devices=%d, blocksize=%d", nDevices, bSize);
+ if (nDevices <= 0)
+ return false;
+
+ fillFormat (&format, rate, bits, channels);
+ rc = waveOutOpen (&dev_, WAVE_MAPPER, &format, (DWORD) waveOut_callback,
+ (DWORD) this, CALLBACK_FUNCTION);
+ if (rc == MMSYSERR_NOERROR)
+ {
+ setOwner ();
+ init (bSize);
+ }
+
+ debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels,
+ (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
+
+ return (rc == MMSYSERR_NOERROR);
}
-char *
-Audio::initialisebuffer ()
+void
+fhandler_dev_dsp::Audio_out::stop ()
{
- EnterCriticalSection (&lock_);
- WAVEHDR *pHeader = 0L;
- for (int i = 0; i < MAX_BLOCKS; i++)
+ MMRESULT rc;
+ WAVEHDR *pHdr;
+ bool gotblock;
+
+ debug_printf ("dev_=%08x pid=%d owner=%d", (int)dev_,
+ GetCurrentProcessId (), getOwner ());
+ if (getOwner () && !denyAccess ())
{
- char *pData = freeblocks_[i];
- if (pData)
+ sendcurrent (); // force out last block whatever size..
+ waitforallsent (); // block till finished..
+
+ rc = waveOutReset (dev_);
+ debug_printf ("waveOutReset rc=%d", rc);
+ do
{
- pHeader = (WAVEHDR *) pData;
- if (pHeader->dwFlags & WHDR_DONE)
+ lock ();
+ gotblock = Qisr2app_->recv (&pHdr);
+ unlock ();
+ if (gotblock)
{
- waveOutUnprepareHeader (dev_, pHeader, sizeof (WAVEHDR));
+ rc = waveOutUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("waveOutUnprepareHeader Block 0x%08x %s", pHdr,
+ (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
}
- freeblocks_[i] = 0L;
- break;
+ }
+ while (gotblock);
+ while (Qapp2app_->recv (&pHdr))
+ /* flush queue */;
+
+ rc = waveOutClose (dev_);
+ debug_printf ("waveOutClose rc=%d", rc);
+
+ clearOwner ();
+
+ if (bigwavebuffer_)
+ {
+ delete[] bigwavebuffer_;
+ bigwavebuffer_ = NULL;
}
}
- LeaveCriticalSection (&lock_);
+}
+
+void
+fhandler_dev_dsp::Audio_out::init (unsigned blockSize)
+{
+ int i;
- if (pHeader)
+ // internally queue all of our buffer for later use by write
+ for (i = 0; i < MAX_BLOCKS; i++)
{
- memset (pHeader, 0, sizeof (WAVEHDR));
- pHeader->dwBufferLength = BLOCK_SIZE;
- pHeader->lpData = (LPSTR) (&pHeader[1]);
- return (char *) pHeader->lpData;
+ wavehdr_[i].lpData = &bigwavebuffer_[i * blockSize];
+ (int)wavehdr_[i].dwUser = blockSize;
+ if (!Qapp2app_->send (&wavehdr_[i]))
+ {
+ debug_printf ("Internal Error i=%d", i);
+ break; // should not happen
+ }
}
- return 0L;
+ pHdr_ = NULL;
}
bool
-Audio::write (const void *pSampleData, int nBytes)
+fhandler_dev_dsp::Audio_out::write (const char *pSampleData, int nBytes)
{
- // split up big blocks into smaller BLOCK_SIZE chunks
- while (nBytes > BLOCK_SIZE)
- {
- write (pSampleData, BLOCK_SIZE);
- nBytes -= BLOCK_SIZE;
- pSampleData = (void *) ((char *) pSampleData + BLOCK_SIZE);
+ while (nBytes != 0)
+ { // Block if all blocks used until at least one is free
+ waitforspace ();
+
+ int sizeleft = (int)pHdr_->dwUser - bufferIndex_;
+ if (nBytes < sizeleft)
+ { // all data fits into the current block, with some space left
+ memcpy (&pHdr_->lpData[bufferIndex_], pSampleData, nBytes);
+ bufferIndex_ += nBytes;
+ break;
+ }
+ else
+ { // data will fill up the current block
+ memcpy (&pHdr_->lpData[bufferIndex_], pSampleData, sizeleft);
+ bufferIndex_ += sizeleft;
+ sendcurrent ();
+ pSampleData += sizeleft;
+ nBytes -= sizeleft;
+ }
}
+ return true;
+}
- // Block till next sound is flushed
- if (blocks () == MAX_BLOCKS)
- waitforcallback ();
+// return number of (partially) empty blocks back.
+int
+fhandler_dev_dsp::Audio_out::emptyblocks ()
+{
+ int n;
+ lock ();
+ n = Qisr2app_->query ();
+ unlock ();
+ n += Qapp2app_->query ();
+ if (pHdr_ != NULL)
+ n++;
+ return n;
+}
- // Allocate new wave buffer if necessary
- if (buffer_ == 0L)
+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 ())
{
- buffer_ = initialisebuffer ();
- if (buffer_ == 0L)
- return false;
+ p->fragments = emptyblocks ();
+ if (pHdr_ != NULL)
+ p->bytes = p->fragsize - bufferIndex_ +
+ p->fragsize * (p->fragments - 1);
+ else
+ p->bytes = p->fragsize * p->fragments;
}
-
-
- // Handle gathering blocks into larger buffer
- int sizeleft = BLOCK_SIZE - bufferIndex_;
- if (nBytes < sizeleft)
+ else
{
- memcpy (&buffer_[bufferIndex_], pSampleData, nBytes);
- bufferIndex_ += nBytes;
- nBytesWritten_ += nBytes;
- return true;
+ p->fragments = MAX_BLOCKS;
+ p->bytes = p->fragsize * p->fragments;
}
-
- // flushing when we reach our limit of BLOCK_SIZE
- memcpy (&buffer_[bufferIndex_], pSampleData, sizeleft);
- bufferIndex_ += sizeleft;
- nBytesWritten_ += sizeleft;
- flush ();
-
- // change pointer to rest of sample, and size accordingly
- pSampleData = (void *) ((char *) pSampleData + sizeleft);
- nBytes -= sizeleft;
-
- // if we still have some sample left over write it out
- if (nBytes)
- return write (pSampleData, nBytes);
-
- return true;
}
-// return number of blocks back.
-int
-Audio::blocks ()
+/* This is called on an interupt so use locking.. Note Qisr2app_
+ is used so we should wrap all references to it in locks. */
+void
+fhandler_dev_dsp::Audio_out::callback_sampledone (WAVEHDR *pHdr)
{
- EnterCriticalSection (&lock_);
- int ret = nBlocksInQue_;
- LeaveCriticalSection (&lock_);
- return ret;
+ lock ();
+ Qisr2app_->send (pHdr);
+ unlock ();
}
-// This is called on an interupt so use locking.. Note nBlocksInQue_ is
-// modified by it so we should wrap all references to it in locks.
void
-Audio::callback_sampledone (void *pData)
+fhandler_dev_dsp::Audio_out::waitforspace ()
{
- EnterCriticalSection (&lock_);
-
- nBlocksInQue_--;
- for (int i = 0; i < MAX_BLOCKS; i++)
- if (!freeblocks_[i])
- {
- freeblocks_[i] = (char *) pData;
- break;
- }
+ WAVEHDR *pHdr;
+ bool gotblock;
+ MMRESULT rc = WAVERR_STILLPLAYING;
- LeaveCriticalSection (&lock_);
+ if (pHdr_ != NULL)
+ return;
+ while (Qapp2app_->recv (&pHdr) == false)
+ {
+ 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);
+ }
+ }
+ pHdr_ = pHdr;
+ bufferIndex_ = 0;
}
void
-Audio::waitforcallback ()
+fhandler_dev_dsp::Audio_out::waitforallsent ()
{
- int n = blocks ();
- if (!n)
- return;
- do
+ while (emptyblocks () != MAX_BLOCKS)
{
- Sleep (250);
+ debug_printf ("100ms Qisr=%d Qapp=%d",
+ Qisr2app_->query (), Qapp2app_->query ());
+ Sleep (100);
}
- while (n == blocks ());
}
+// send the block described by pHdr_ and bufferIndex_ to wave device
bool
-Audio::flush ()
+fhandler_dev_dsp::Audio_out::sendcurrent ()
{
- if (!buffer_)
+ WAVEHDR *pHdr = pHdr_;
+ if (pHdr_ == NULL)
return false;
+ pHdr_ = NULL;
- // Send internal buffer out to the soundcard
- WAVEHDR *pHeader = ((WAVEHDR *) buffer_) - 1;
- pHeader->dwBufferLength = bufferIndex_;
+ // Sample buffer conversion
+ (this->*convert_) ((unsigned char *)pHdr->lpData, bufferIndex_);
- // Quick bit of sample buffer conversion
- if (formattype_ == AFMT_S8)
+ // Send internal buffer out to the soundcard
+ pHdr->dwBufferLength = bufferIndex_;
+ pHdr->dwFlags = 0;
+ if (waveOutPrepareHeader (dev_, pHdr, sizeof (WAVEHDR)) == MMSYSERR_NOERROR)
{
- unsigned char *p = ((unsigned char *) buffer_);
- for (int i = 0; i < bufferIndex_; i++)
+ if (waveOutWrite (dev_, pHdr, sizeof (WAVEHDR)) == MMSYSERR_NOERROR)
{
- p[i] -= 0x7f;
+ debug_printf ("waveOutWrite bytes=%d", bufferIndex_);
+ return true;
+ }
+ else
+ {
+ debug_printf ("waveOutWrite failed");
+ lock ();
+ Qisr2app_->send (pHdr);
+ unlock ();
}
- }
-
- if (waveOutPrepareHeader (dev_, pHeader, sizeof (WAVEHDR)) == S_OK &&
- waveOutWrite (dev_, pHeader, sizeof (WAVEHDR)) == S_OK)
- {
- EnterCriticalSection (&lock_);
- nBlocksInQue_++;
- LeaveCriticalSection (&lock_);
- bufferIndex_ = 0;
- buffer_ = 0L;
- return true;
}
else
{
- EnterCriticalSection (&lock_);
- for (int i = 0; i < MAX_BLOCKS; i++)
- if (!freeblocks_[i])
- {
- freeblocks_[i] = (char *) pHeader;
- break;
- }
- LeaveCriticalSection (&lock_);
+ debug_printf ("waveOutPrepareHeader failed");
+ Qapp2app_->send (pHdr);
}
return false;
}
@@ -335,21 +647,18 @@ Audio::flush ()
//------------------------------------------------------------------------
// Call back routine
static void CALLBACK
-wave_callback (HWAVE hWave, UINT msg, DWORD instance, DWORD param1,
- DWORD param2)
+waveOut_callback (HWAVEOUT hWave, UINT msg, DWORD instance, DWORD param1,
+ DWORD param2)
{
if (msg == WOM_DONE)
{
- Audio *ptr = (Audio *) instance;
- ptr->callback_sampledone ((void *) param1);
+ fhandler_dev_dsp::Audio_out *ptr =
+ (fhandler_dev_dsp::Audio_out *) instance;
+ ptr->callback_sampledone ((WAVEHDR *) param1);
}
}
//------------------------------------------------------------------------
-// /dev/dsp handler
-static Audio *s_audio; // static instance of the Audio handler
-
-//------------------------------------------------------------------------
// wav file detection..
#pragma pack(1)
struct wavchunk
@@ -369,121 +678,482 @@ struct wavformat
#pragma pack()
bool
-fhandler_dev_dsp::setupwav (const char *pData, int nBytes)
+fhandler_dev_dsp::Audio_out::parsewav (const char * &pData, int &nBytes,
+ int &rate, int &bits, int &channels)
{
int len;
const char *end = pData + nBytes;
-
- if (!(pData[0] == 'R' && pData[1] == 'I' &&
- pData[2] == 'F' && pData[3] == 'F'))
+ const char *pDat;
+ int skip = 0;
+
+ if (!(pData[0] == 'R' && pData[1] == 'I'
+ && pData[2] == 'F' && pData[3] == 'F'))
return false;
- if (!(pData[8] == 'W' && pData[9] == 'A' &&
- pData[10] == 'V' && pData[11] == 'E'))
+ if (!(pData[8] == 'W' && pData[9] == 'A'
+ && pData[10] == 'V' && pData[11] == 'E'))
return false;
len = *(int *) &pData[4];
- pData += 12;
- while (len && pData < end)
- {
- wavchunk * pChunk = (wavchunk *) pData;
+ len -= 12;
+ pDat = pData + 12;
+ skip = 12;
+ while ((len > 0) && (pDat + sizeof (wavchunk) < end))
+ { /* We recognize two kinds of wavchunk:
+ "fmt " for the PCM parameters (only PCM supported here)
+ "data" for the start of PCM data */
+ wavchunk * pChunk = (wavchunk *) pDat;
int blklen = pChunk-> len;
- if (pChunk->id[0] == 'f' && pChunk->id[1] == 'm' &&
- pChunk->id[2] == 't' && pChunk->id[3] == ' ')
+ if (pChunk->id[0] == 'f' && pChunk->id[1] == 'm'
+ && pChunk->id[2] == 't' && pChunk->id[3] == ' ')
{
wavformat *format = (wavformat *) (pChunk + 1);
- if ((char *) (format + 1) > end)
+ if ((char *) (format + 1) >= end)
return false;
+ // We have found the parameter chunk
+ if (format->wFormatTag == 0x0001)
+ { // Micr*s*ft PCM; check if parameters work with our device
+ if (query (format->dwSamplesPerSec, format->wBitsPerSample,
+ format->wChannels))
+ { // return the parameters we found
+ rate = format->dwSamplesPerSec;
+ bits = format->wBitsPerSample;
+ channels = format->wChannels;
+ }
+ }
+ }
+ else
+ {
+ if (pChunk->id[0] == 'd' && pChunk->id[1] == 'a'
+ && pChunk->id[2] == 't' && pChunk->id[3] == 'a')
+ { // throw away all the header & not output it to the soundcard.
+ skip += sizeof (wavchunk);
+ debug_printf ("Discard %d bytes wave header", skip);
+ pData += skip;
+ nBytes -= skip;
+ return true;
+ }
+ }
+ pDat += blklen + sizeof (wavchunk);
+ skip += blklen + sizeof (wavchunk);
+ len -= blklen + sizeof (wavchunk);
+ }
+ return false;
+}
+
+/* ========================================================================
+ Buffering concept for Audio_in:
+ On the first read, we queue all blocks of our bigwavebuffer
+ 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().
+ 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
+ to the wave-in device immediately.
+ 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);
+}
- // Open up audio device with correct frequency for wav file
- //
- // FIXME: should through away all the header & not output
- // it to the soundcard.
- s_audio->close ();
- if (s_audio->open (format->dwSamplesPerSec, format->wBitsPerSample,
- format->wChannels) == false)
+fhandler_dev_dsp::Audio_in::~Audio_in ()
+{
+ stop ();
+ delete Qapp2app_;
+ delete Qisr2app_;
+}
+
+bool
+fhandler_dev_dsp::Audio_in::query (int rate, int bits, int channels)
+{
+ WAVEFORMATEX format;
+ MMRESULT rc;
+
+ 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");
+ return (rc == MMSYSERR_NOERROR);
+}
+
+bool
+fhandler_dev_dsp::Audio_in::start (int rate, int bits, int channels)
+{
+ 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)
+ return false;
+
+ fillFormat (&format, rate, bits, channels);
+ rc = waveInOpen (&dev_, WAVE_MAPPER, &format, (DWORD) waveIn_callback,
+ (DWORD) this, CALLBACK_FUNCTION);
+ if (rc == MMSYSERR_NOERROR)
+ {
+ setOwner ();
+ if (!init (bSize))
+ {
+ stop ();
+ return false;
+ }
+ }
+
+ debug_printf ("freq=%d bits=%d channels=%d %s", rate, bits, channels,
+ (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
+
+ return (rc == MMSYSERR_NOERROR);
+}
+
+void
+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 ())
+ {
+ 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);
+
+ do
+ {
+ lock ();
+ gotblock = Qisr2app_->recv (&pHdr);
+ unlock ();
+ if (gotblock)
{
- s_audio->open (audiofreq_, audiobits_, audiochannels_);
+ rc = waveInUnprepareHeader (dev_, pHdr, sizeof (WAVEHDR));
+ debug_printf ("waveInUnprepareHeader Block 0x%08x %s", pHdr,
+ (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK");
}
- else
- {
- audiofreq_ = format->dwSamplesPerSec;
- audiobits_ = format->wBitsPerSample;
- audiochannels_ = format->wChannels;
+ }
+ while (gotblock);
+ while (Qapp2app_->recv (&pHdr))
+ /* flush queue */;
+
+ rc = waveInClose (dev_);
+ debug_printf ("waveInClose rc=%d", rc);
+
+ clearOwner ();
+
+ if (bigwavebuffer_)
+ {
+ delete[] bigwavebuffer_;
+ bigwavebuffer_ = NULL;
+ }
+ }
+}
+
+bool
+fhandler_dev_dsp::Audio_in::queueblock (WAVEHDR *pHdr)
+{
+ MMRESULT rc;
+ pHdr->dwFlags = 0;
+ rc = waveInPrepareHeader (dev_, pHdr, sizeof (WAVEHDR));
+ 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);
+}
+
+bool
+fhandler_dev_dsp::Audio_in::init (unsigned blockSize)
+{
+ MMRESULT rc;
+ int i;
+
+ // try to queue all of our buffer for reception
+ for (i = 0; i < MAX_BLOCKS; i++)
+ {
+ wavehdr_[i].lpData = &bigwavebuffer_[i * blockSize];
+ wavehdr_[i].dwBufferLength = blockSize;
+ if (!queueblock (&wavehdr_[i]))
+ break;
+ }
+ pHdr_ = NULL;
+ rc = waveInStart (dev_);
+ debug_printf ("waveInStart=%d %s queued=%d",
+ rc, (rc != MMSYSERR_NOERROR) ? "FAIL" : "OK", i);
+ return (rc == MMSYSERR_NOERROR);
+}
+
+bool
+fhandler_dev_dsp::Audio_in::read (char *pSampleData, int &nBytes)
+{
+ int bytes_to_read = nBytes;
+ nBytes = 0;
+ debug_printf ("pSampleData=%08x nBytes=%d", pSampleData, bytes_to_read);
+ while (bytes_to_read != 0)
+ { // Block till next sound has been read
+ waitfordata ();
+
+ // Handle gathering our blocks into smaller or larger buffer
+ int sizeleft = pHdr_->dwBytesRecorded - bufferIndex_;
+ if (bytes_to_read < sizeleft)
+ { // The current buffer holds more data than requested
+ memcpy (pSampleData, &pHdr_->lpData[bufferIndex_], bytes_to_read);
+ (this->*convert_) ((unsigned char *)pSampleData, bytes_to_read);
+ nBytes += bytes_to_read;
+ bufferIndex_ += bytes_to_read;
+ debug_printf ("got %d", bytes_to_read);
+ break; // done; use remaining data in next call to read
+ }
+ else
+ { // not enough or exact amount in the current buffer
+ if (sizeleft)
+ { // use up what we have
+ memcpy (pSampleData, &pHdr_->lpData[bufferIndex_], sizeleft);
+ (this->*convert_) ((unsigned char *)pSampleData, sizeleft);
+ nBytes += sizeleft;
+ bytes_to_read -= sizeleft;
+ pSampleData += sizeleft;
+ debug_printf ("got %d", sizeleft);
}
- return true;
+ queueblock (pHdr_); // re-queue this block to ISR
+ pHdr_ = NULL; // need to wait for a new block
+ // if more samples are needed, we need a new block now
+ }
+ }
+ debug_printf ("end nBytes=%d", nBytes);
+ return true;
+}
+
+void
+fhandler_dev_dsp::Audio_in::waitfordata ()
+{
+ WAVEHDR *pHdr;
+ bool gotblock;
+ MMRESULT rc;
+
+ if (pHdr_ != NULL)
+ return;
+ while (Qapp2app_->recv (&pHdr) == false)
+ {
+ 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);
}
+ }
+ pHdr_ = pHdr;
+ bufferIndex_ = 0;
+}
- pData += blklen + sizeof (wavchunk);
+// This is called on an interrupt so use locking..
+void
+fhandler_dev_dsp::Audio_in::callback_blockfull (WAVEHDR *pHdr)
+{
+ lock ();
+ Qisr2app_->send (pHdr);
+ unlock ();
+}
+
+static void CALLBACK
+waveIn_callback (HWAVEIN hWave, UINT msg, DWORD instance, DWORD param1,
+ DWORD param2)
+{
+ if (msg == WIM_DATA)
+ {
+ fhandler_dev_dsp::Audio_in *ptr =
+ (fhandler_dev_dsp::Audio_in *) instance;
+ ptr->callback_blockfull ((WAVEHDR *) param1);
}
- return false;
}
-//------------------------------------------------------------------------
+
+/* ------------------------------------------------------------------------
+ /dev/dsp handler
+ ------------------------------------------------------------------------
+ instances of the handler statics */
+int fhandler_dev_dsp::open_count = 0;
+
fhandler_dev_dsp::fhandler_dev_dsp ():
fhandler_base ()
{
+ debug_printf ("0x%08x", (int)this);
+ audio_in_ = NULL;
+ 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)
{
- // currently we only support writing
- if ((flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_WRONLY)
+ open_count++;
+ if (open_count > 1)
{
- set_errno (EACCES);
+ set_errno (EBUSY);
return 0;
}
-
set_flags ((flags & ~O_TEXT) | O_BINARY);
-
- if (!s_audio)
- s_audio = new (audio_buf) Audio;
-
- // Work out initial sample format & frequency
- // dev/dsp defaults
- audioformat_ = AFMT_S8;
+ // Work out initial sample format & frequency, /dev/dsp defaults
+ audioformat_ = AFMT_U8;
audiofreq_ = 8000;
audiobits_ = 8;
audiochannels_ = 1;
-
- int res;
- if (!s_audio->open (audiofreq_, audiobits_, audiochannels_))
- res = 0;
- else
+ switch (flags & O_ACCMODE)
{
+ case O_WRONLY:
+ audio_out_ = new Audio_out;
+ if (!audio_out_->query (audiofreq_, audiobits_, audiochannels_))
+ {
+ delete audio_out_;
+ audio_out_ = NULL;
+ }
+ 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;
+ }
+ break;
+ default:
+ set_errno (EINVAL);
+ return 0;
+ } // switch (flags & O_ACCMODE)
+ int rc;
+ if (audio_in_ || audio_out_)
+ { /* All tried query () succeeded */
+ rc = 1;
set_open_status ();
- res = 1;
+ set_need_fork_fixup ();
+ set_close_on_exec_flag (1);
}
-
- debug_printf ("returns %d", res);
- return res;
+ 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;
}
+#define RETURN_ERROR_WHEN_BUSY(audio)\
+ if ((audio)->denyAccess ()) \
+ {\
+ set_errno (EBUSY);\
+ return -1;\
+ }
+
int
fhandler_dev_dsp::write (const void *ptr, size_t len)
{
- if (s_audio->numbytesoutput () == 0)
+ int len_s = len;
+ debug_printf ("ptr=%08x len=%d", ptr, len);
+ if (!audio_out_)
{
- // check for wave file & setup frequencys properly if possible.
- setupwav ((const char *) ptr, len);
-
+ set_errno (EACCES); // device was opened for read?
+ 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 ((const char *) ptr, len_s,
+ audiofreq_, audiobits_, audiochannels_))
+ { // update our format conversion
+ audioformat_ = ((audiobits_ == 8) ? AFMT_U8 : AFMT_S16_LE);
+ audio_out_->setformat (audioformat_);
+ }
// Open audio device properly with callbacks.
- s_audio->close ();
- if (!s_audio->open (audiofreq_, audiobits_, audiochannels_, true))
- return 0;
+ if (!audio_out_->start (audiofreq_, audiobits_, audiochannels_))
+ {
+ set_errno (EIO);
+ return -1;
+ }
}
- s_audio->write (ptr, len);
+ audio_out_->write ((char *)ptr, len_s);
return len;
}
void __stdcall
fhandler_dev_dsp::read (void *ptr, size_t& len)
{
+ debug_printf ("ptr=%08x len=%d", ptr, len);
+ if (!audio_in_)
+ {
+ len = (size_t)-1;
+ set_errno (EACCES); // device was opened for write?
+ return;
+ }
+ if (audio_in_->denyAccess ())
+ {
+ len = (size_t)-1;
+ set_errno (EBUSY);
+ 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;
}
@@ -496,13 +1166,27 @@ fhandler_dev_dsp::lseek (_off64_t offset, int whence)
int
fhandler_dev_dsp::close (void)
{
- s_audio->close ();
+ debug_printf ("audio_in=%08x audio_out=%08x",
+ (int)audio_in_, (int)audio_out_);
+ if (audio_in_)
+ {
+ delete audio_in_;
+ audio_in_ = NULL;
+ }
+ if (audio_out_)
+ {
+ delete audio_out_;
+ audio_out_ = NULL;
+ }
+ if (open_count > 0)
+ open_count--;
return 0;
}
int
fhandler_dev_dsp::dup (fhandler_base * child)
{
+ debug_printf ("");
fhandler_dev_dsp *fhc = (fhandler_dev_dsp *) child;
fhc->set_flags (get_flags ());
@@ -517,100 +1201,182 @@ 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_);
switch (cmd)
{
-#define CASE(a) case a : debug_printf("/dev/dsp: ioctl %s", #a);
+#define CASE(a) case a : debug_printf ("/dev/dsp: ioctl %s", #a);
CASE (SNDCTL_DSP_RESET)
- audioformat_ = AFMT_S8;
- audiofreq_ = 8000;
- audiobits_ = 8;
- audiochannels_ = 1;
+ if (audio_out_)
+ {
+ RETURN_ERROR_WHEN_BUSY (audio_out_);
+ audioformat_ = AFMT_U8;
+ audiofreq_ = 8000;
+ audiobits_ = 8;
+ audiochannels_ = 1;
+ audio_out_->stop ();
+ audio_out_->setformat (audioformat_);
+ }
+ if (audio_in_)
+ {
+ RETURN_ERROR_WHEN_BUSY (audio_in_);
+ audioformat_ = AFMT_U8;
+ audiofreq_ = 8000;
+ audiobits_ = 8;
+ audiochannels_ = 1;
+ audio_in_->stop ();
+ audio_in_->setformat (audioformat_);
+ }
return 0;
+ break;
CASE (SNDCTL_DSP_GETBLKSIZE)
- *intptr = Audio::BLOCK_SIZE;
+ if (audio_out_)
+ {
+ *intptr = audio_out_->blockSize (audiofreq_,
+ audiobits_,
+ audiochannels_);
+ }
+ else
+ { // I am very sure that audio_in_ is valid
+ *intptr = audio_in_->blockSize (audiofreq_,
+ audiobits_,
+ audiochannels_);
+ }
return 0;
+ break;
CASE (SNDCTL_DSP_SETFMT)
{
- int nBits = 0;
- if (*intptr == AFMT_S16_LE)
- nBits = 16;
- else if (*intptr == AFMT_U8)
- nBits = 8;
- else if (*intptr == AFMT_S8)
- nBits = 8;
- if (nBits)
+ int nBits;
+ switch (*intptr)
+ {
+ case AFMT_QUERY:
+ *intptr = audioformat_;
+ return 0;
+ break;
+ case AFMT_U16_BE:
+ case AFMT_U16_LE:
+ case AFMT_S16_BE:
+ case AFMT_S16_LE:
+ nBits = 16;
+ break;
+ case AFMT_U8:
+ case AFMT_S8:
+ nBits = 8;
+ break;
+ default:
+ nBits = 0;
+ }
+ if (nBits && audio_out_)
{
- s_audio->setformat (*intptr);
- s_audio->close ();
- if (s_audio->open (audiofreq_, nBits, audiochannels_) == true)
+ RETURN_ERROR_WHEN_BUSY (audio_out_);
+ audio_out_->stop ();
+ audio_out_->setformat (*intptr);
+ if (audio_out_->query (audiofreq_, nBits, audiochannels_))
{
audiobits_ = nBits;
- return 0;
+ audioformat_ = *intptr;
}
else
{
- s_audio->open (audiofreq_, audiobits_, audiochannels_);
+ *intptr = audiobits_;
return -1;
}
}
+ if (nBits && audio_in_)
+ {
+ RETURN_ERROR_WHEN_BUSY (audio_in_);
+ audio_in_->stop ();
+ audio_in_->setformat (*intptr);
+ if (audio_in_->query (audiofreq_, nBits, audiochannels_))
+ {
+ audiobits_ = nBits;
+ audioformat_ = *intptr;
+ }
+ else
+ {
+ *intptr = audiobits_;
+ return -1;
+ }
+ }
+ return 0;
}
break;
CASE (SNDCTL_DSP_SPEED)
- s_audio->close ();
- if (s_audio->open (*intptr, audiobits_, audiochannels_) == true)
- {
- audiofreq_ = *intptr;
- return 0;
+ {
+ if (audio_out_)
+ {
+ RETURN_ERROR_WHEN_BUSY (audio_out_);
+ audio_out_->stop ();
+ if (audio_out_->query (*intptr, audiobits_, audiochannels_))
+ audiofreq_ = *intptr;
+ else
+ {
+ *intptr = audiofreq_;
+ return -1;
+ }
}
- else
+ if (audio_in_)
{
- s_audio->open (audiofreq_, audiobits_, audiochannels_);
- return -1;
+ RETURN_ERROR_WHEN_BUSY (audio_in_);
+ audio_in_->stop ();
+ if (audio_in_->query (*intptr, audiobits_, audiochannels_))
+ audiofreq_ = *intptr;
+ else
+ {
+ *intptr = audiofreq_;
+ return -1;
+ }
}
- break;
+ return 0;
+ }
+ break;
CASE (SNDCTL_DSP_STEREO)
{
int nChannels = *intptr + 1;
- s_audio->close ();
- if (s_audio->open (audiofreq_, audiobits_, nChannels) == true)
- {
- audiochannels_ = nChannels;
- return 0;
+ 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;
+ }
}
- else
+ if (audio_in_)
{
- s_audio->open (audiofreq_, audiobits_, audiochannels_);
- return -1;
+ 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;
}
break;
CASE (SNDCTL_DSP_GETOSPACE)
{
audio_buf_info *p = (audio_buf_info *) ptr;
-
- int nBlocks = s_audio->blocks ();
- int leftblocks = ((Audio::MAX_BLOCKS - nBlocks) - 1);
- if (leftblocks < 0)
- leftblocks = 0;
- if (leftblocks > 1)
- leftblocks = 1;
- int left = leftblocks * Audio::BLOCK_SIZE;
-
- p->fragments = leftblocks;
- p->fragstotal = Audio::MAX_BLOCKS;
- p->fragsize = Audio::BLOCK_SIZE;
- p->bytes = left;
-
- debug_printf ("ptr %p nblocks %d leftblocks %d left bytes %d ",
- ptr, nBlocks, leftblocks, left);
-
+ if (audio_out_)
+ {
+ 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);
+ }
return 0;
}
break;
@@ -625,29 +1391,62 @@ fhandler_dev_dsp::ioctl (unsigned int cmd, void *ptr)
CASE (SNDCTL_DSP_GETFMTS)
{
- *intptr = AFMT_S16_LE | AFMT_U8 | AFMT_S8; // more?
+ *intptr = AFMT_S16_LE | AFMT_U8; // only native formats returned here
+ 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 ();
+ }
return 0;
}
break;
default:
- debug_printf ("/dev/dsp: ioctl not handled yet! FIXME:");
+ debug_printf ("/dev/dsp: ioctl 0x%08x not handled yet! FIXME:", cmd);
break;
#undef CASE
};
+ set_errno (EINVAL);
return -1;
}
void
fhandler_dev_dsp::dump ()
{
- paranoid_printf ("here, fhandler_dev_dsp");
+ paranoid_printf ("here");
+}
+
+void
+fhandler_dev_dsp::fixup_after_fork (HANDLE parent)
+{ // called from new child process
+ debug_printf ("audio_in=%08x audio_out=%08x",
+ (int)audio_in_, (int)audio_out_);
+ if (audio_in_ )
+ audio_in_ ->fork_fixup (parent);
+ if (audio_out_)
+ audio_out_->fork_fixup (parent);
}
void
fhandler_dev_dsp::fixup_after_exec ()
{
- /* FIXME: Is there a better way to do this? */
- s_audio = new (audio_buf) Audio;
+ debug_printf ("audio_in=%08x audio_out=%08x",
+ (int)audio_in_, (int)audio_out_);
}
+
+