OSDN Git Service

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