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