OSDN Git Service

* fhandler_dsp.cc: Reformat to GNU standards.
[pf3gnuchains/sourceware.git] / winsup / cygwin / fhandler_dsp.cc
1 /* fhandler_dev_dsp: code to emulate OSS sound model /dev/dsp
2  
3    Copyright 2001 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 "cygerrno.h"
17 #include "fhandler.h"
18 #include <windows.h>
19 #include <sys/soundcard.h>
20 #include <sys/fcntl.h>
21 #include <mmsystem.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 { MAX_BLOCKS = 12, BLOCK_SIZE = 16384 };
32
33     Audio ();
34    ~Audio ();
35
36   bool open (int rate, int bits, int channels, bool bCallback = false);
37   void close ();
38   int getvolume ();
39   void setvolume (int newVolume);
40   bool write (const void *pSampleData, int nBytes);
41   int blocks ();
42   void callback_sampledone (void *pData);
43   void setformat (int format) {formattype_ = format;}
44   int numbytesoutput ();
45
46 private:
47   char *initialisebuffer ();
48   void waitforcallback ();
49   bool flush ();
50
51   HWAVEOUT dev_;
52   volatile int nBlocksInQue_;
53   int nBytesWritten_;
54   char *buffer_;
55   int bufferIndex_;
56   CRITICAL_SECTION lock_;
57   char *freeblocks_[MAX_BLOCKS];
58   int formattype_;
59
60   char bigwavebuffer_[MAX_BLOCKS * BLOCK_SIZE];
61 };
62
63 Audio::Audio ()
64 {
65   int size = BLOCK_SIZE + sizeof (WAVEHDR);
66
67   InitializeCriticalSection (&lock_);
68   memset (freeblocks_, 0, sizeof (freeblocks_));
69   for (int i = 0; i < MAX_BLOCKS; i++)
70     {
71       char *pBuffer = &bigwavebuffer_[i * size];
72       memset (pBuffer, 0, size);
73       freeblocks_[i] = pBuffer;
74     }
75 }
76
77 Audio::~Audio ()
78 {
79   if (dev_)
80     close ();
81   DeleteCriticalSection (&lock_);
82 }
83
84 bool
85 Audio::open (int rate, int bits, int channels, bool bCallback = false)
86 {
87   WAVEFORMATEX format;
88   int nDevices = waveOutGetNumDevs ();
89
90   nBytesWritten_ = 0L;
91   bufferIndex_ = 0;
92   buffer_ = 0L;
93   debug_printf ("number devices %d\n", nDevices);
94   if (nDevices <= 0)
95     return false;
96
97   debug_printf ("trying to map device freq %d, bits %d, "
98                 "channels %d, callback %d\n", rate, bits, channels,
99                 bCallback);
100
101   int bytesperSample = bits / 8;
102
103   memset (&format, 0, sizeof (format));
104   format.wFormatTag = WAVE_FORMAT_PCM;
105   format.wBitsPerSample = bits;
106   format.nChannels = channels;
107   format.nSamplesPerSec = rate;
108   format.nAvgBytesPerSec = format.nSamplesPerSec * format.nChannels *
109     bytesperSample;
110   format.nBlockAlign = format.nChannels * bytesperSample;
111
112   nBlocksInQue_ = 0;
113   HRESULT res = waveOutOpen (&dev_, WAVE_MAPPER, &format, (DWORD) wave_callback,
114                              (DWORD) this, bCallback ? CALLBACK_FUNCTION : 0);
115   if (res == S_OK)
116     {
117       debug_printf ("Sucessfully opened!");
118       return true;
119     }
120   else
121     {
122       debug_printf ("failed to open");
123       return false;
124     }
125 }
126
127 void
128 Audio::close ()
129 {
130   if (dev_)
131     {
132       flush ();                 // force out last block whatever size..
133
134       while (blocks ())         // block till finished..
135         waitforcallback ();
136
137       waveOutReset (dev_);
138       waveOutClose (dev_);
139       dev_ = 0L;
140     }
141   nBytesWritten_ = 0L;
142 }
143
144 int
145 Audio::numbytesoutput ()
146 {
147   return nBytesWritten_;
148 }
149
150 int
151 Audio::getvolume ()
152 {
153   DWORD volume;
154
155   waveOutGetVolume (dev_, &volume);
156   return ((volume >> 16) + (volume & 0xffff)) >> 1;
157 }
158
159 void
160 Audio::setvolume (int newVolume)
161 {
162   waveOutSetVolume (dev_, (newVolume << 16) | newVolume);
163 }
164
165 char *
166 Audio::initialisebuffer ()
167 {
168   EnterCriticalSection (&lock_);
169   WAVEHDR *pHeader = 0L;
170   for (int i = 0; i < MAX_BLOCKS; i++)
171     {
172       char *pData = freeblocks_[i];
173       if (pData)
174         {
175           pHeader = (WAVEHDR *) pData;
176           if (pHeader->dwFlags & WHDR_DONE)
177             {
178               waveOutUnprepareHeader (dev_, pHeader, sizeof (WAVEHDR));
179             }
180           freeblocks_[i] = 0L;
181           break;
182         }
183     }
184   LeaveCriticalSection (&lock_);
185
186   if (pHeader)
187     {
188       memset (pHeader, 0, sizeof (WAVEHDR));
189       pHeader->dwBufferLength = BLOCK_SIZE;
190       pHeader->lpData = (LPSTR) (&pHeader[1]);
191       return (char *) pHeader->lpData;
192     }
193   return 0L;
194 }
195
196 bool
197 Audio::write (const void *pSampleData, int nBytes)
198 {
199   // split up big blocks into smaller BLOCK_SIZE chunks
200   while (nBytes > BLOCK_SIZE)
201     {
202       write (pSampleData, BLOCK_SIZE);
203       nBytes -= BLOCK_SIZE;
204       pSampleData = (void *) ((char *) pSampleData + BLOCK_SIZE);
205     }
206
207   // Block till next sound is flushed
208   if (blocks () == MAX_BLOCKS)
209     waitforcallback ();
210
211   // Allocate new wave buffer if necessary
212   if (buffer_ == 0L)
213     {
214       buffer_ = initialisebuffer ();
215       if (buffer_ == 0L)
216         return false;
217     }
218
219
220   // Handle gathering blocks into larger buffer
221   int sizeleft = BLOCK_SIZE - bufferIndex_;
222   if (nBytes < sizeleft)
223     {
224       memcpy (&buffer_[bufferIndex_], pSampleData, nBytes);
225       bufferIndex_ += nBytes;
226       nBytesWritten_ += nBytes;
227       return true;
228     }
229
230   // flushing when we reach our limit of BLOCK_SIZE
231   memcpy (&buffer_[bufferIndex_], pSampleData, sizeleft);
232   bufferIndex_ += sizeleft;
233   nBytesWritten_ += sizeleft;
234   flush ();
235
236   // change pointer to rest of sample, and size accordingly
237   pSampleData = (void *) ((char *) pSampleData + sizeleft);
238   nBytes -= sizeleft;
239
240   // if we still have some sample left over write it out
241   if (nBytes)
242     return write (pSampleData, nBytes);
243
244   return true;
245 }
246
247 // return number of blocks back.
248 int
249 Audio::blocks ()
250 {
251   EnterCriticalSection (&lock_);
252   int ret = nBlocksInQue_;
253   LeaveCriticalSection (&lock_);
254   return ret;
255 }
256
257 // This is called on an interupt so use locking.. Note nBlocksInQue_ is
258 // modified by it so we should wrap all references to it in locks.
259 void
260 Audio::callback_sampledone (void *pData)
261 {
262   EnterCriticalSection (&lock_);
263
264   nBlocksInQue_--;
265   for (int i = 0; i < MAX_BLOCKS; i++)
266     if (!freeblocks_[i])
267       {
268         freeblocks_[i] = (char *) pData;
269         break;
270       }
271
272   LeaveCriticalSection (&lock_);
273 }
274
275 void
276 Audio::waitforcallback ()
277 {
278   int n = blocks ();
279   if (!n)
280     return;
281   do
282     {
283       Sleep (250);
284     }
285   while (n == blocks ());
286 }
287
288 bool
289 Audio::flush ()
290 {
291   if (!buffer_)
292     return false;
293
294   // Send internal buffer out to the soundcard
295   WAVEHDR *pHeader = ((WAVEHDR *) buffer_) - 1;
296   pHeader->dwBufferLength = bufferIndex_;
297
298   // Quick bit of sample buffer conversion
299   if (formattype_ == AFMT_S8)
300     {
301       unsigned char *p = ((unsigned char *) buffer_);
302       for (int i = 0; i < bufferIndex_; i++)
303         {
304           p[i] -= 0x7f;
305         }
306     }
307
308   if (waveOutPrepareHeader (dev_, pHeader, sizeof (WAVEHDR)) == S_OK &&
309       waveOutWrite (dev_, pHeader, sizeof (WAVEHDR)) == S_OK)
310     {
311       EnterCriticalSection (&lock_);
312       nBlocksInQue_++;
313       LeaveCriticalSection (&lock_);
314       bufferIndex_ = 0;
315       buffer_ = 0L;
316       return true;
317     }
318   else
319     {
320       EnterCriticalSection (&lock_);
321       for (int i = 0; i < MAX_BLOCKS; i++)
322         if (!freeblocks_[i])
323           {
324             freeblocks_[i] = (char *) pHeader;
325             break;
326           }
327       LeaveCriticalSection (&lock_);
328     }
329   return false;
330 }
331
332 //------------------------------------------------------------------------
333 // Call back routine
334 static void CALLBACK
335 wave_callback (HWAVE hWave, UINT msg, DWORD instance, DWORD param1,
336                DWORD param2)
337 {
338   if (msg == WOM_DONE)
339     {
340       Audio *ptr = (Audio *) instance;
341       ptr->callback_sampledone ((void *) param1);
342     }
343 }
344
345 //------------------------------------------------------------------------
346 // /dev/dsp handler
347 static Audio *s_audio;          // static instance of the Audio handler
348
349 //------------------------------------------------------------------------
350 // wav file detection..
351 #pragma pack(1)
352 struct wavchunk
353 {
354   char id[4];
355   unsigned int len;
356 };
357 struct wavformat
358 {
359   unsigned short wFormatTag;
360   unsigned short wChannels;
361   unsigned int dwSamplesPerSec;
362   unsigned int dwAvgBytesPerSec;
363   unsigned short wBlockAlign;
364   unsigned short wBitsPerSample;
365 };
366 #pragma pack()
367
368 bool
369 fhandler_dev_dsp::setupwav (const char *pData, int nBytes)
370 {
371   int len;
372   const char *end = pData + nBytes;
373
374   if (!(pData[0] == 'R' && pData[1] == 'I' &&
375         pData[2] == 'F' && pData[3] == 'F'))
376     return false;
377   if (!(pData[8] == 'W' && pData[9] == 'A' &&
378         pData[10] == 'V' && pData[11] == 'E'))
379     return false;
380
381   len = *(int *) &pData[4];
382   pData += 12;
383   while (len && pData < end)
384     {
385       wavchunk * pChunk = (wavchunk *) pData;
386       int blklen = pChunk-> len;
387       if (pChunk->id[0] == 'f' && pChunk->id[1] == 'm' &&
388           pChunk->id[2] == 't' && pChunk->id[3] == ' ')
389         {
390           wavformat *format = (wavformat *) (pChunk + 1);
391           if ((char *) (format + 1) > end)
392             return false;
393
394           // Open up audio device with correct frequency for wav file
395           //
396           // FIXME: should through away all the header & not output
397           // it to the soundcard.
398           s_audio->close ();
399           if (s_audio->open (format->dwSamplesPerSec, format->wBitsPerSample,
400                              format->wChannels) == false)
401             {
402               s_audio->open (audiofreq_, audiobits_, audiochannels_);
403             }
404           else
405             {
406               audiofreq_ = format->dwSamplesPerSec;
407               audiobits_ = format->wBitsPerSample;
408               audiochannels_ = format->wChannels;
409             }
410           return true;
411         }
412
413       pData += blklen + sizeof (wavchunk);
414     }
415   return false;
416 }
417
418 //------------------------------------------------------------------------
419 fhandler_dev_dsp::fhandler_dev_dsp (const char *name):
420   fhandler_base (FH_OSS_DSP, name)
421 {
422   set_cb (sizeof *this);
423 }
424
425 fhandler_dev_dsp::~fhandler_dev_dsp ()
426 {
427 }
428
429 int
430 fhandler_dev_dsp::open (const char *path, int flags, mode_t mode = 0)
431 {
432   // currently we only support writing
433   if ((flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_WRONLY)
434     return 0;
435
436   set_flags (flags);
437
438   if (!s_audio)
439     s_audio = new Audio;
440
441   // Work out initial sample format & frequency
442   if (strcmp (path, "/dev/dsp") == 0L)
443     {
444       // dev/dsp defaults
445       audioformat_ = AFMT_S8;
446       audiofreq_ = 8000;
447       audiobits_ = 8;
448       audiochannels_ = 1;
449     }
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
480 fhandler_dev_dsp::read (void *ptr, size_t len)
481 {
482   return len;
483 }
484
485 off_t
486 fhandler_dev_dsp::lseek (off_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 1;
525
526       CASE (SNDCTL_DSP_GETBLKSIZE)
527         *intptr = Audio::BLOCK_SIZE;
528         break;
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 1;
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 1;
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 1;
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 1;
610       }
611       break;
612
613       CASE (SNDCTL_DSP_SETFRAGMENT)
614       {
615         // Fake!! esound & mikmod require this on non PowerPC platforms.
616         //
617         return 1;
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 }