1 /** EMULib Emulation Library *********************************/
2 /**                                                         **/
3 /**                        SndUnix.c                        **/
4 /**                                                         **/
5 /** This file contains Unix-dependent sound implementation  **/
6 /** for the emulation library.                              **/
7 /**                                                         **/
8 /** Copyright (C) Marat Fayzullin 1996-2009                 **/
9 /**     You are not allowed to distribute this software     **/
10 /**     commercially. Please, notify me, if you make any    **/
11 /**     changes to this file.                               **/
12 /*************************************************************/
13 #include "EMULib.h"
14 #include "Sound.h"
15 
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <signal.h>
23 #include <sys/ioctl.h>
24 #include <sys/wait.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <pthread.h>
28 
29 #if defined(ESD_AUDIO)
30 
31 #include "esd.h"
32 
33 #elif defined(SUN_AUDIO)
34 
35 #include <sys/audioio.h>
36 #include <sys/conf.h>
37 #include <stropts.h>
38 
39 static const unsigned char ULAW[256] =
40 {
41     31,   31,   31,   32,   32,   32,   32,   33,
42     33,   33,   33,   34,   34,   34,   34,   35,
43     35,   35,   35,   36,   36,   36,   36,   37,
44     37,   37,   37,   38,   38,   38,   38,   39,
45     39,   39,   39,   40,   40,   40,   40,   41,
46     41,   41,   41,   42,   42,   42,   42,   43,
47     43,   43,   43,   44,   44,   44,   44,   45,
48     45,   45,   45,   46,   46,   46,   46,   47,
49     47,   47,   47,   48,   48,   49,   49,   50,
50     50,   51,   51,   52,   52,   53,   53,   54,
51     54,   55,   55,   56,   56,   57,   57,   58,
52     58,   59,   59,   60,   60,   61,   61,   62,
53     62,   63,   63,   64,   65,   66,   67,   68,
54     69,   70,   71,   72,   73,   74,   75,   76,
55     77,   78,   79,   81,   83,   85,   87,   89,
56     91,   93,   95,   99,  103,  107,  111,  119,
57    255,  247,  239,  235,  231,  227,  223,  221,
58    219,  217,  215,  213,  211,  209,  207,  206,
59    205,  204,  203,  202,  201,  200,  199,  198,
60    219,  217,  215,  213,  211,  209,  207,  206,
61    205,  204,  203,  202,  201,  200,  199,  198,
62    197,  196,  195,  194,  193,  192,  191,  191,
63    190,  190,  189,  189,  188,  188,  187,  187,
64    186,  186,  185,  185,  184,  184,  183,  183,
65    182,  182,  181,  181,  180,  180,  179,  179,
66    178,  178,  177,  177,  176,  176,  175,  175,
67    175,  175,  174,  174,  174,  174,  173,  173,
68    173,  173,  172,  172,  172,  172,  171,  171,
69    171,  171,  170,  170,  170,  170,  169,  169,
70    169,  169,  168,  168,  168,  168,  167,  167,
71    167,  167,  166,  166,  166,  166,  165,  165,
72    165,  165,  164,  164,  164,  164,  163,  163
73 };
74 
75 #else /* !SUN_AUDIO && !ESD_AUDIO */
76 
77 #if defined(__FreeBSD__) || defined(__DragonFly__)
78 #include <sys/soundcard.h>
79 #endif
80 
81 #ifdef __NetBSD__
82 #include <soundcard.h>
83 #endif
84 
85 #ifdef __linux__
86 #include <sys/soundcard.h>
87 #endif
88 
89 #endif /* !SUN_AUDIO && !ESD_AUDIO */
90 
91 #if defined(SUN_AUDIO)
92 #define AUDIO_CONV(A) (ULAW[0xFF&(128+(A))])
93 #elif defined(BPS16)
94 #define AUDIO_CONV(A) (A)
95 #else
96 #define AUDIO_CONV(A) (128+(A))
97 #endif
98 
99 static int SoundFD     = -1; /* Audio device descriptor      */
100 static int SndRate     = 0;  /* Audio sampling rate          */
101 static int SndSize     = 0;  /* SndData[] size               */
102 static sample *SndData = 0;  /* Audio buffers                */
103 static int RPtr        = 0;  /* Read pointer into Bufs       */
104 static int WPtr        = 0;  /* Write pointer into Bufs      */
105 static pthread_t Thr   = 0;  /* Audio thread                 */
106 static volatile int AudioPaused = 0; /* 1: Audio paused      */
107 
108 /** ThrHandler() *********************************************/
109 /** This is the thread function responsible for sending     **/
110 /** buffers to the audio device.                            **/
111 /*************************************************************/
ThrHandler(void * Arg)112 static void *ThrHandler(void *Arg)
113 {
114   int J;
115 
116   /* Spin until audio has been trashed */
117   for(RPtr=WPtr=0;SndRate&&SndData&&(SoundFD>=0);)
118   {
119 #ifdef SUN_AUDIO
120     /* Flush output first, don't care about return status. After this
121     ** write next buffer of audio data. This method produces a horrible
122     ** click on each buffer :( Any ideas, how to fix this?
123     */
124     ioctl(SoundFD,AUDIO_DRAIN);
125     write(SoundFD,SndData+RPtr,SND_BUFSIZE*sizeof(sample));
126 #else
127     /* We'll block here until next DMA buffer becomes free. It happens
128     ** once per SND_BUFSIZE/SndRate seconds.
129     */
130     write(SoundFD,SndData+RPtr,SND_BUFSIZE*sizeof(sample));
131 #endif
132 
133     /* Advance buffer pointer, clearing the buffer */
134     for(J=0;J<SND_BUFSIZE;++J) SndData[RPtr++]=AUDIO_CONV(0);
135     if(RPtr>=SndSize) RPtr=0;
136   }
137 
138   return(0);
139 }
140 
141 /** InitAudio() **********************************************/
142 /** Initialize sound. Returns rate (Hz) on success, else 0. **/
143 /** Rate=0 to skip initialization (will be silent).         **/
144 /*************************************************************/
InitAudio(unsigned int Rate,unsigned int Latency)145 unsigned int InitAudio(unsigned int Rate,unsigned int Latency)
146 {
147   int I,J,K;
148 
149   /* Shut down audio, just to be sure */
150   TrashAudio();
151   SndRate     = 0;
152   SoundFD     = -1;
153   SndSize     = 0;
154   SndData     = 0;
155   RPtr        = 0;
156   WPtr        = 0;
157   Thr         = 0;
158   AudioPaused = 0;
159 
160   /* Have to have at least 8kHz sampling rate and 1ms buffer */
161   if((Rate<8000)||!Latency) return(0);
162 
163   /* Compute number of sound buffers */
164   SndSize=(Rate*Latency/1000+SND_BUFSIZE-1)/SND_BUFSIZE;
165 
166 #if defined(ESD_AUDIO)
167 
168   /* ESD options for playing wave audio */
169   J=ESD_MONO|ESD_STREAM|ESD_PLAY|(sizeof(sample)>1? ESD_BITS16:ESD_BITS8);
170   /* Open ESD socket, fall back to /dev/dsp is no ESD */
171   if((SoundFD=esd_play_stream_fallback(J,Rate,0,0))<0) return(0);
172 
173 #elif defined(SUN_AUDIO)
174 
175   /* Open Sun's audio device */
176   if((SoundFD=open("/dev/audio",O_WRONLY|O_NONBLOCK))<0) return(0);
177 
178   /*
179   ** Sun's specific initialization should be here...
180   ** We assume, that it's set to 8000Hz u-law mono right now.
181   */
182 
183 #else /* !SUN_AUDIO */
184 
185   /* Open /dev/dsp audio device */
186   if((SoundFD=open("/dev/dsp",O_WRONLY))<0) return(0);
187   /* Set sound format */
188   J=sizeof(sample)>1? AFMT_S16_NE:AFMT_U8;
189   I=ioctl(SoundFD,SNDCTL_DSP_SETFMT,&J)<0;
190   /* Set mono sound */
191   J=0;
192   I|=ioctl(SoundFD,SNDCTL_DSP_STEREO,&J)<0;
193   /* Set sampling rate */
194   I|=ioctl(SoundFD,SNDCTL_DSP_SPEED,&Rate)<0;
195 
196   /* Set buffer length and number of buffers */
197   J=K=SND_BITS|(SndSize<<16);
198   I|=ioctl(SoundFD,SNDCTL_DSP_SETFRAGMENT,&J)<0;
199 
200   /* Buffer length as n, not 2^n! */
201   if((J&0xFFFF)<=16) J=(J&0xFFFF0000)|(1<<(J&0xFFFF));
202   K=SND_BUFSIZE|(SndSize<<16);
203 
204   /* Check audio parameters */
205   I|=(J!=K)&&(((J>>16)<SndSize)||((J&0xFFFF)!=SND_BUFSIZE));
206 
207   /* If something went wrong, drop out */
208   if(I) { TrashSound();return(0); }
209 
210 #endif /* !SUN_AUDIO */
211 
212   /* SndSize now means the total buffer size */
213   SndSize*=SND_BUFSIZE;
214 
215   /* Allocate audio buffers */
216   SndData=(sample *)malloc(SndSize*sizeof(sample));
217   if(!SndData) { TrashSound();return(0); }
218 
219   /* Clear audio buffers */
220   for(J=0;J<SndSize;++J) SndData[J]=AUDIO_CONV(0);
221 
222   /* Thread expects valid SndRate!=0 at the start */
223   SndRate=Rate;
224 
225   /* Create audio thread */
226   if(pthread_create(&Thr,0,ThrHandler,0)) { TrashSound();SndRate=0;return(0); }
227 
228   /* Done, return effective audio rate */
229   return(SndRate);
230 }
231 
232 /** TrashAudio() *********************************************/
233 /** Free resources allocated by InitAudio().                **/
234 /*************************************************************/
TrashAudio(void)235 void TrashAudio(void)
236 {
237   /* Sound off, pause off */
238   SndRate     = 0;
239   AudioPaused = 0;
240 
241   /* Wait for audio thread to finish */
242   if(Thr) pthread_join(Thr,0);
243 
244   /* If audio was initialized... */
245   if(SoundFD>=0)
246   {
247 #if defined(ESD_AUDIO)
248     esd_close(SoundFD);
249 #elif defined(SUN_AUDIO)
250     close(SoundFD);
251 #else
252     ioctl(SoundFD,SNDCTL_DSP_RESET);
253     close(SoundFD);
254 #endif
255   }
256 
257   /* If buffers were allocated... */
258   if(SndData) free(SndData);
259 
260   /* Sound trashed */
261   SoundFD = -1;
262   SndData = 0;
263   SndSize = 0;
264   RPtr    = 0;
265   WPtr    = 0;
266   Thr     = 0;
267 }
268 
269 /** PauseAudio() *********************************************/
270 /** Pause/resume audio playback. Returns current playback   **/
271 /** state.                                                  **/
272 /*************************************************************/
PauseAudio(int Switch)273 int PauseAudio(int Switch)
274 {
275   static int Rate,Latency;
276 
277   /* If audio not initialized, return "off" */
278   if(!SndRate&&!AudioPaused) return(0);
279 
280   /* Toggle audio status if requested */
281   if(Switch==2) Switch=AudioPaused? 0:1;
282 
283   /* When switching audio state... */
284   if((Switch>=0)&&(Switch<=1)&&(Switch!=AudioPaused))
285   {
286     if(Switch)
287     {
288       /* Memorize audio parameters and kill audio */
289       Rate    = SndRate;
290       Latency = 1000*SndSize/SndRate;
291       TrashAudio();
292     }
293     else
294     {
295       /* Start audio using memorized parameters */
296       if(!InitAudio(Rate,Latency)) Switch=0;
297     }
298 
299     /* Audio switched */
300     AudioPaused=Switch;
301   }
302 
303   /* Return current status */
304   return(AudioPaused);
305 }
306 
307 /** GetFreeAudio() *******************************************/
308 /** Get the amount of free samples in the audio buffer.     **/
309 /*************************************************************/
GetFreeAudio(void)310 unsigned int GetFreeAudio(void)
311 {
312   return(!SndRate? 0:RPtr>=WPtr? RPtr-WPtr:RPtr-WPtr+SndSize);
313 }
314 
315 /** WriteAudio() *********************************************/
316 /** Write up to a given number of samples to audio buffer.  **/
317 /** Returns the number of samples written.                  **/
318 /*************************************************************/
WriteAudio(sample * Data,unsigned int Length)319 unsigned int WriteAudio(sample *Data,unsigned int Length)
320 {
321   unsigned int J;
322 
323   /* Require audio to be initialized */
324   if(!SndRate) return(0);
325 
326   /* Copy audio samples */
327   for(J=0;(J<Length)&&(RPtr!=WPtr);++J)
328   {
329     SndData[WPtr++]=AUDIO_CONV(Data[J]);
330     if(WPtr>=SndSize) WPtr=0;
331   }
332 
333   /* Return number of samples copied */
334   return(J);
335 }
336 
337