OSDN Git Service

* devices.cc: New file.
[pf3gnuchains/sourceware.git] / winsup / cygwin / fhandler_dsp.cc
1 /* fhandler_dev_dsp: code to emulate OSS sound model /dev/dsp
2
3    Copyright 2001, 2002, 2003 Red Hat, Inc
4
5    Written by Andy Younger (andy@snoogie.demon.co.uk)
6
7 This file is part of Cygwin.
8
9 This software is a copyrighted work licensed under the terms of the
10 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
11 details. */
12
13 #include "winsup.h"
14 #include <stdio.h>
15 #include <windows.h>
16 #include <sys/soundcard.h>
17 #include <mmsystem.h>
18 #include "cygerrno.h"
19 #include "security.h"
20 #include "path.h"
21 #include "fhandler.h"
22
23 //------------------------------------------------------------------------
24 // Simple encapsulation of the win32 audio device.
25 //
26 static void CALLBACK wave_callback (HWAVE hWave, UINT msg, DWORD instance,
27                                     DWORD param1, DWORD param2);
28 class Audio
29 {
30 public:
31   enum
32   {
33     MAX_BLOCKS = 12,
34     BLOCK_SIZE = 16384,
35     TOT_BLOCK_SIZE = BLOCK_SIZE + sizeof (WAVEHDR)
36    };
37
38     Audio ();
39    ~Audio ();
40
41   bool open (int rate, int bits, int channels, bool bCallback = false);
42   void close ();
43   int getvolume ();
44   void setvolume (int newVolume);
45   bool write (const void *pSampleData, int nBytes);
46   int blocks ();
47   void callback_sampledone (void *pData);
48   void setformat (int format) {formattype_ = format;}
49   int numbytesoutput ();
50
51   void *operator new (size_t, void *p) {return p;}
52
53 private:
54   char *initialisebuffer ();
55   void waitforcallback ();
56   bool flush ();
57
58   HWAVEOUT dev_;
59   volatile int nBlocksInQue_;
60   int nBytesWritten_;
61   char *buffer_;
62   int bufferIndex_;
63   CRITICAL_SECTION lock_;
64   char *freeblocks_[MAX_BLOCKS];
65   int formattype_;
66
67   char bigwavebuffer_[MAX_BLOCKS * TOT_BLOCK_SIZE];
68 };
69
70 static char audio_buf[sizeof (class Audio)];
71
72 Audio::Audio ()
73 {
74   InitializeCriticalSection (&lock_);
75   memset (bigwavebuffer_, 0, sizeof (bigwavebuffer_));
76   for (int i = 0; i < MAX_BLOCKS; i++)
77     freeblocks_[i] =  &bigwavebuffer_[i * TOT_BLOCK_SIZE];
78 }
79
80 Audio::~Audio ()
81 {
82   if (dev_)
83     close ();
84   DeleteCriticalSection (&lock_);
85 }
86
87 bool
88 Audio::open (int rate, int bits, int channels, bool bCallback)
89 {
90   WAVEFORMATEX format;
91   int nDevices = waveOutGetNumDevs ();
92
93   nBytesWritten_ = 0L;
94   bufferIndex_ = 0;
95   buffer_ = 0L;
96   debug_printf ("number devices %d", nDevices);
97   if (nDevices <= 0)
98     return false;
99
100   debug_printf ("trying to map device freq %d, bits %d, "
101                 "channels %d, callback %d", rate, bits, channels,
102                 bCallback);
103
104   int bytesperSample = bits / 8;
105
106   memset (&format, 0, sizeof (format));
107   format.wFormatTag = WAVE_FORMAT_PCM;
108   format.wBitsPerSample = bits;
109   format.nChannels = channels;
110   format.nSamplesPerSec = rate;
111   format.nAvgBytesPerSec = format.nSamplesPerSec * format.nChannels *
112     bytesperSample;
113   format.nBlockAlign = format.nChannels * bytesperSample;
114
115   nBlocksInQue_ = 0;
116   HRESULT res = waveOutOpen (&dev_, WAVE_MAPPER, &format, (DWORD) wave_callback,
117                              (DWORD) this, bCallback ? CALLBACK_FUNCTION : 0);
118   if (res == S_OK)
119     {
120       debug_printf ("Sucessfully opened!");
121       return true;
122     }
123   else
124     {
125       debug_printf ("failed to open");
126       return false;
127     }
128 }
129
130 void
131 Audio::close ()
132 {
133   if (dev_)
134     {
135       flush ();                 // force out last block whatever size..
136
137       while (blocks ())         // block till finished..
138         waitforcallback ();
139
140       waveOutReset (dev_);
141       waveOutClose (dev_);
142       dev_ = 0L;
143     }
144   nBytesWritten_ = 0L;
145 }
146
147 int
148 Audio::numbytesoutput ()
149 {
150   return nBytesWritten_;
151 }
152
153 int
154 Audio::getvolume ()
155 {
156   DWORD volume;
157
158   waveOutGetVolume (dev_, &volume);
159   return ((volume >> 16) + (volume & 0xffff)) >> 1;
160 }
161
162 void
163 Audio::setvolume (int newVolume)
164 {
165   waveOutSetVolume (dev_, (newVolume << 16) | newVolume);
166 }
167
168 char *
169 Audio::initialisebuffer ()
170 {
171   EnterCriticalSection (&lock_);
172   WAVEHDR *pHeader = 0L;
173   for (int i = 0; i < MAX_BLOCKS; i++)
174     {
175       char *pData = freeblocks_[i];
176       if (pData)
177         {
178           pHeader = (WAVEHDR *) pData;
179           if (pHeader->dwFlags & WHDR_DONE)
180             {
181               waveOutUnprepareHeader (dev_, pHeader, sizeof (WAVEHDR));
182             }
183           freeblocks_[i] = 0L;
184           break;
185         }
186     }
187   LeaveCriticalSection (&lock_);
188
189   if (pHeader)
190     {
191       memset (pHeader, 0, sizeof (WAVEHDR));
192       pHeader->dwBufferLength = BLOCK_SIZE;
193       pHeader->lpData = (LPSTR) (&pHeader[1]);
194       return (char *) pHeader->lpData;
195     }
196   return 0L;
197 }
198
199 bool
200 Audio::write (const void *pSampleData, int nBytes)
201 {
202   // split up big blocks into smaller BLOCK_SIZE chunks
203   while (nBytes > BLOCK_SIZE)
204     {
205       write (pSampleData, BLOCK_SIZE);
206       nBytes -= BLOCK_SIZE;
207       pSampleData = (void *) ((char *) pSampleData + BLOCK_SIZE);
208     }
209
210   // Block till next sound is flushed
211   if (blocks () == MAX_BLOCKS)
212     waitforcallback ();
213
214   // Allocate new wave buffer if necessary
215   if (buffer_ == 0L)
216     {
217       buffer_ = initialisebuffer ();
218       if (buffer_ == 0L)
219         return false;
220     }
221
222
223   // Handle gathering blocks into larger buffer
224   int sizeleft = BLOCK_SIZE - bufferIndex_;
225   if (nBytes < sizeleft)
226     {
227       memcpy (&buffer_[bufferIndex_], pSampleData, nBytes);
228       bufferIndex_ += nBytes;
229       nBytesWritten_ += nBytes;
230       return true;
231     }
232
233   // flushing when we reach our limit of BLOCK_SIZE
234   memcpy (&buffer_[bufferIndex_], pSampleData, sizeleft);
235   bufferIndex_ += sizeleft;
236   nBytesWritten_ += sizeleft;
237   flush ();
238
239   // change pointer to rest of sample, and size accordingly
240   pSampleData = (void *) ((char *) pSampleData + sizeleft);
241   nBytes -= sizeleft;
242
243   // if we still have some sample left over write it out
244   if (nBytes)
245     return write (pSampleData, nBytes);
246
247   return true;
248 }
249
250 // return number of blocks back.
251 int
252 Audio::blocks ()
253 {
254   EnterCriticalSection (&lock_);
255   int ret = nBlocksInQue_;
256   LeaveCriticalSection (&lock_);
257   return ret;
258 }
259
260 // This is called on an interupt so use locking.. Note nBlocksInQue_ is
261 // modified by it so we should wrap all references to it in locks.
262 void
263 Audio::callback_sampledone (void *pData)
264 {
265   EnterCriticalSection (&lock_);
266
267   nBlocksInQue_--;
268   for (int i = 0; i < MAX_BLOCKS; i++)
269     if (!freeblocks_[i])
270       {
271         freeblocks_[i] = (char *) pData;
272         break;
273       }
274
275   LeaveCriticalSection (&lock_);
276 }
277
278 void
279 Audio::waitforcallback ()
280 {
281   int n = blocks ();
282   if (!n)
283     return;
284   do
285     {
286       Sleep (250);
287     }
288   while (n == blocks ());
289 }
290
291 bool
292 Audio::flush ()
293 {
294   if (!buffer_)
295     return false;
296
297   // Send internal buffer out to the soundcard
298   WAVEHDR *pHeader = ((WAVEHDR *) buffer_) - 1;
299   pHeader->dwBufferLength = bufferIndex_;
300
301   // Quick bit of sample buffer conversion
302   if (formattype_ == AFMT_S8)
303     {
304       unsigned char *p = ((unsigned char *) buffer_);
305       for (int i = 0; i < bufferIndex_; i++)
306         {
307           p[i] -= 0x7f;
308         }
309     }
310
311   if (waveOutPrepareHeader (dev_, pHeader, sizeof (WAVEHDR)) == S_OK &&
312       waveOutWrite (dev_, pHeader, sizeof (WAVEHDR)) == S_OK)
313     {
314       EnterCriticalSection (&lock_);
315       nBlocksInQue_++;
316       LeaveCriticalSection (&lock_);
317       bufferIndex_ = 0;
318       buffer_ = 0L;
319       return true;
320     }
321   else
322     {
323       EnterCriticalSection (&lock_);
324       for (int i = 0; i < MAX_BLOCKS; i++)
325         if (!freeblocks_[i])
326           {
327             freeblocks_[i] = (char *) pHeader;
328             break;
329           }
330       LeaveCriticalSection (&lock_);
331     }
332   return false;
333 }
334
335 //------------------------------------------------------------------------
336 // Call back routine
337 static void CALLBACK
338 wave_callback (HWAVE hWave, UINT msg, DWORD instance, DWORD param1,
339                DWORD param2)
340 {
341   if (msg == WOM_DONE)
342     {
343       Audio *ptr = (Audio *) instance;
344       ptr->callback_sampledone ((void *) param1);
345     }
346 }
347
348 //------------------------------------------------------------------------
349 // /dev/dsp handler
350 static Audio *s_audio;          // static instance of the Audio handler
351
352 //------------------------------------------------------------------------
353 // wav file detection..
354 #pragma pack(1)
355 struct wavchunk
356 {
357   char id[4];
358   unsigned int len;
359 };
360 struct wavformat
361 {
362   unsigned short wFormatTag;
363   unsigned short wChannels;
364   unsigned int dwSamplesPerSec;
365   unsigned int dwAvgBytesPerSec;
366   unsigned short wBlockAlign;
367   unsigned short wBitsPerSample;
368 };
369 #pragma pack()
370
371 bool
372 fhandler_dev_dsp::setupwav (const char *pData, int nBytes)
373 {
374   int len;
375   const char *end = pData + nBytes;
376
377   if (!(pData[0] == 'R' && pData[1] == 'I' &&
378         pData[2] == 'F' && pData[3] == 'F'))
379     return false;
380   if (!(pData[8] == 'W' && pData[9] == 'A' &&
381         pData[10] == 'V' && pData[11] == 'E'))
382     return false;
383
384   len = *(int *) &pData[4];
385   pData += 12;
386   while (len && pData < end)
387     {
388       wavchunk * pChunk = (wavchunk *) pData;
389       int blklen = pChunk-> len;
390       if (pChunk->id[0] == 'f' && pChunk->id[1] == 'm' &&
391           pChunk->id[2] == 't' && pChunk->id[3] == ' ')
392         {
393           wavformat *format = (wavformat *) (pChunk + 1);
394           if ((char *) (format + 1) > end)
395             return false;
396
397           // Open up audio device with correct frequency for wav file
398           //
399           // FIXME: should through away all the header & not output
400           // it to the soundcard.
401           s_audio->close ();
402           if (s_audio->open (format->dwSamplesPerSec, format->wBitsPerSample,
403                              format->wChannels) == false)
404             {
405               s_audio->open (audiofreq_, audiobits_, audiochannels_);
406             }
407           else
408             {
409               audiofreq_ = format->dwSamplesPerSec;
410               audiobits_ = format->wBitsPerSample;
411               audiochannels_ = format->wChannels;
412             }
413           return true;
414         }
415
416       pData += blklen + sizeof (wavchunk);
417     }
418   return false;
419 }
420
421 //------------------------------------------------------------------------
422 fhandler_dev_dsp::fhandler_dev_dsp ():
423   fhandler_base ()
424 {
425 }
426
427 fhandler_dev_dsp::~fhandler_dev_dsp ()
428 {
429 }
430
431 int
432 fhandler_dev_dsp::open (int flags, mode_t mode)
433 {
434   // currently we only support writing
435   if ((flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_WRONLY)
436     {
437       set_errno (EACCES);
438       return 0;
439     }
440
441   set_flags ((flags & ~O_TEXT) | O_BINARY);
442
443   if (!s_audio)
444     s_audio = new (audio_buf) Audio;
445
446   // Work out initial sample format & frequency
447       // dev/dsp defaults
448   audioformat_ = AFMT_S8;
449   audiofreq_ = 8000;
450   audiobits_ = 8;
451   audiochannels_ = 1;
452
453   int res;
454   if (!s_audio->open (audiofreq_, audiobits_, audiochannels_))
455     res = 0;
456   else
457     {
458       set_open_status ();
459       res = 1;
460     }
461
462   debug_printf ("returns %d", res);
463   return res;
464 }
465
466 int
467 fhandler_dev_dsp::write (const void *ptr, size_t len)
468 {
469   if (s_audio->numbytesoutput () == 0)
470     {
471       // check for wave file & setup frequencys properly if possible.
472       setupwav ((const char *) ptr, len);
473
474       // Open audio device properly with callbacks.
475       s_audio->close ();
476       if (!s_audio->open (audiofreq_, audiobits_, audiochannels_, true))
477         return 0;
478     }
479
480   s_audio->write (ptr, len);
481   return len;
482 }
483
484 void __stdcall
485 fhandler_dev_dsp::read (void *ptr, size_t& len)
486 {
487   return;
488 }
489
490 _off64_t
491 fhandler_dev_dsp::lseek (_off64_t offset, int whence)
492 {
493   return 0;
494 }
495
496 int
497 fhandler_dev_dsp::close (void)
498 {
499   s_audio->close ();
500   return 0;
501 }
502
503 int
504 fhandler_dev_dsp::dup (fhandler_base * child)
505 {
506   fhandler_dev_dsp *fhc = (fhandler_dev_dsp *) child;
507
508   fhc->set_flags (get_flags ());
509   fhc->audiochannels_ = audiochannels_;
510   fhc->audiobits_ = audiobits_;
511   fhc->audiofreq_ = audiofreq_;
512   fhc->audioformat_ = audioformat_;
513   return 0;
514 }
515
516 int
517 fhandler_dev_dsp::ioctl (unsigned int cmd, void *ptr)
518 {
519   int *intptr = (int *) ptr;
520   switch (cmd)
521     {
522 #define CASE(a) case a : debug_printf("/dev/dsp: ioctl %s", #a);
523
524       CASE (SNDCTL_DSP_RESET)
525         audioformat_ = AFMT_S8;
526         audiofreq_ = 8000;
527         audiobits_ = 8;
528         audiochannels_ = 1;
529         return 0;
530
531       CASE (SNDCTL_DSP_GETBLKSIZE)
532         *intptr = Audio::BLOCK_SIZE;
533         return 0;
534
535       CASE (SNDCTL_DSP_SETFMT)
536       {
537         int nBits = 0;
538         if (*intptr == AFMT_S16_LE)
539           nBits = 16;
540         else if (*intptr == AFMT_U8)
541           nBits = 8;
542         else if (*intptr == AFMT_S8)
543           nBits = 8;
544         if (nBits)
545           {
546             s_audio->setformat (*intptr);
547             s_audio->close ();
548             if (s_audio->open (audiofreq_, nBits, audiochannels_) == true)
549               {
550                 audiobits_ = nBits;
551                 return 0;
552               }
553             else
554               {
555                 s_audio->open (audiofreq_, audiobits_, audiochannels_);
556                 return -1;
557               }
558           }
559       }
560       break;
561
562       CASE (SNDCTL_DSP_SPEED)
563         s_audio->close ();
564         if (s_audio->open (*intptr, audiobits_, audiochannels_) == true)
565           {
566             audiofreq_ = *intptr;
567             return 0;
568           }
569         else
570           {
571             s_audio->open (audiofreq_, audiobits_, audiochannels_);
572             return -1;
573           }
574         break;
575
576       CASE (SNDCTL_DSP_STEREO)
577       {
578         int nChannels = *intptr + 1;
579
580         s_audio->close ();
581         if (s_audio->open (audiofreq_, audiobits_, nChannels) == true)
582           {
583             audiochannels_ = nChannels;
584             return 0;
585           }
586         else
587           {
588             s_audio->open (audiofreq_, audiobits_, audiochannels_);
589             return -1;
590           }
591       }
592       break;
593
594       CASE (SNDCTL_DSP_GETOSPACE)
595       {
596         audio_buf_info *p = (audio_buf_info *) ptr;
597
598         int nBlocks = s_audio->blocks ();
599         int leftblocks = ((Audio::MAX_BLOCKS - nBlocks) - 1);
600         if (leftblocks < 0)
601           leftblocks = 0;
602         if (leftblocks > 1)
603           leftblocks = 1;
604         int left = leftblocks * Audio::BLOCK_SIZE;
605
606         p->fragments = leftblocks;
607         p->fragstotal = Audio::MAX_BLOCKS;
608         p->fragsize = Audio::BLOCK_SIZE;
609         p->bytes = left;
610
611         debug_printf ("ptr %p nblocks %d leftblocks %d left bytes %d ",
612                       ptr, nBlocks, leftblocks, left);
613
614         return 0;
615       }
616       break;
617
618       CASE (SNDCTL_DSP_SETFRAGMENT)
619       {
620         // Fake!! esound & mikmod require this on non PowerPC platforms.
621         //
622         return 0;
623       }
624       break;
625
626       CASE (SNDCTL_DSP_GETFMTS)
627       {
628         *intptr = AFMT_S16_LE | AFMT_U8 | AFMT_S8; // more?
629         return 0;
630       }
631       break;
632
633     default:
634       debug_printf ("/dev/dsp: ioctl not handled yet! FIXME:");
635       break;
636
637 #undef CASE
638     };
639   return -1;
640 }
641
642 void
643 fhandler_dev_dsp::dump ()
644 {
645   paranoid_printf ("here, fhandler_dev_dsp");
646 }
647
648 void
649 fhandler_dev_dsp::fixup_after_exec (HANDLE)
650 {
651   /* FIXME:  Is there a better way to do this? */
652   s_audio = new (audio_buf) Audio;
653 }