Mercurial > sdl-ios-xcode
comparison src/audio/nto/SDL_nto_audio.c @ 718:cbc0f7fabd1c
Date: Sat, 13 Sep 2003 15:50:43 +0300
From: "Mike Gorchak"
Subject: QNX fixes
improved sound code for the QNX, added workarounds for known bugs, fixed photon detect code. Update .qpg file.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sun, 21 Sep 2003 18:13:48 +0000 |
parents | 8bedd6d61642 |
children | b8d311d90021 |
comparison
equal
deleted
inserted
replaced
717:42ed44b2c8b6 | 718:cbc0f7fabd1c |
---|---|
28 #include <fcntl.h> | 28 #include <fcntl.h> |
29 #include <signal.h> | 29 #include <signal.h> |
30 #include <sys/types.h> | 30 #include <sys/types.h> |
31 #include <sys/time.h> | 31 #include <sys/time.h> |
32 #include <sched.h> | 32 #include <sched.h> |
33 #include <sys/select.h> | |
34 #include <sys/neutrino.h> | |
33 #include <sys/asoundlib.h> | 35 #include <sys/asoundlib.h> |
34 #include <sys/select.h> | |
35 | 36 |
36 #include "SDL_audio.h" | 37 #include "SDL_audio.h" |
37 #include "SDL_error.h" | 38 #include "SDL_error.h" |
38 #include "SDL_audiomem.h" | 39 #include "SDL_audiomem.h" |
39 #include "SDL_audio_c.h" | 40 #include "SDL_audio_c.h" |
40 #include "SDL_timer.h" | 41 #include "SDL_timer.h" |
41 #include "SDL_nto_audio.h" | 42 #include "SDL_nto_audio.h" |
42 | 43 |
43 /* The tag name used by NTO audio */ | 44 /* The tag name used by NTO audio */ |
44 #define DRIVER_NAME "nto" | 45 #define DRIVER_NAME "qsa-nto" |
45 | 46 |
46 /* default channel communication parameters */ | 47 /* default channel communication parameters */ |
47 #define DEFAULT_CPARAMS_RATE 22050 | 48 #define DEFAULT_CPARAMS_RATE 22050 |
48 #define DEFAULT_CPARAMS_VOICES 1 | 49 #define DEFAULT_CPARAMS_VOICES 1 |
50 /* FIXME: need to add in the near future flexible logic with frag_size and frags count */ | |
49 #define DEFAULT_CPARAMS_FRAG_SIZE 4096 | 51 #define DEFAULT_CPARAMS_FRAG_SIZE 4096 |
50 #define DEFAULT_CPARAMS_FRAGS_MIN 1 | 52 #define DEFAULT_CPARAMS_FRAGS_MIN 1 |
51 #define DEFAULT_CPARAMS_FRAGS_MAX 1 | 53 #define DEFAULT_CPARAMS_FRAGS_MAX 1 |
52 | 54 |
53 /* Open the audio device for playback, and don't block if busy */ | 55 /* Open the audio device for playback, and don't block if busy */ |
54 #define OPEN_FLAGS SND_PCM_OPEN_PLAYBACK | 56 #define OPEN_FLAGS SND_PCM_OPEN_PLAYBACK |
55 | 57 |
58 #define QSA_NO_WORKAROUNDS 0x00000000 | |
59 #define QSA_MMAP_WORKAROUND 0x00000001 | |
60 | |
61 struct BuggyCards | |
62 { | |
63 char* cardname; | |
64 unsigned long bugtype; | |
65 }; | |
66 | |
67 #define QSA_WA_CARDS 3 | |
68 | |
69 struct BuggyCards buggycards[QSA_WA_CARDS]= | |
70 { | |
71 {"Sound Blaster Live!", QSA_MMAP_WORKAROUND}, | |
72 {"Vortex 8820", QSA_MMAP_WORKAROUND}, | |
73 {"Vortex 8830", QSA_MMAP_WORKAROUND}, | |
74 }; | |
75 | |
56 /* Audio driver functions */ | 76 /* Audio driver functions */ |
57 static int NTO_OpenAudio(_THIS, SDL_AudioSpec *spec); | 77 static void NTO_ThreadInit(_THIS); |
78 static int NTO_OpenAudio(_THIS, SDL_AudioSpec* spec); | |
58 static void NTO_WaitAudio(_THIS); | 79 static void NTO_WaitAudio(_THIS); |
59 static void NTO_PlayAudio(_THIS); | 80 static void NTO_PlayAudio(_THIS); |
60 static Uint8 *NTO_GetAudioBuf(_THIS); | 81 static Uint8* NTO_GetAudioBuf(_THIS); |
61 static void NTO_CloseAudio(_THIS); | 82 static void NTO_CloseAudio(_THIS); |
62 | 83 |
63 static snd_pcm_channel_status_t cstatus; | 84 /* card names check to apply the workarounds */ |
64 static snd_pcm_channel_params_t cparams; | 85 static int NTO_CheckBuggyCards(_THIS, unsigned long checkfor) |
65 static snd_pcm_channel_setup_t csetup; | 86 { |
87 char scardname[33]; | |
88 int it; | |
89 | |
90 if (snd_card_get_name(cardno, scardname, 32)<0) | |
91 { | |
92 return 0; | |
93 } | |
94 | |
95 for (it=0; it<QSA_WA_CARDS; it++) | |
96 { | |
97 if (strcmp(buggycards[it].cardname, scardname)==0) | |
98 { | |
99 if (buggycards[it].bugtype==checkfor) | |
100 { | |
101 return 1; | |
102 } | |
103 } | |
104 } | |
105 | |
106 return 0; | |
107 } | |
108 | |
109 static void NTO_ThreadInit(_THIS) | |
110 { | |
111 int status; | |
112 struct sched_param param; | |
113 | |
114 /* increasing default 10 priority to 25 to avoid jerky sound */ | |
115 status=SchedGet(0, 0, ¶m); | |
116 param.sched_priority=param.sched_curpriority+15; | |
117 status=SchedSet(0, 0, SCHED_NOCHANGE, ¶m); | |
118 } | |
66 | 119 |
67 /* PCM transfer channel parameters initialize function */ | 120 /* PCM transfer channel parameters initialize function */ |
68 static void init_pcm_cparams(snd_pcm_channel_params_t* cparams) | 121 static void NTO_InitAudioParams(snd_pcm_channel_params_t* cpars) |
69 { | 122 { |
70 memset(cparams,0,sizeof(snd_pcm_channel_params_t)); | 123 memset(cpars, 0, sizeof(snd_pcm_channel_params_t)); |
71 | 124 |
72 cparams->channel = SND_PCM_CHANNEL_PLAYBACK; | 125 cpars->channel = SND_PCM_CHANNEL_PLAYBACK; |
73 cparams->mode = SND_PCM_MODE_BLOCK; | 126 cpars->mode = SND_PCM_MODE_BLOCK; |
74 cparams->start_mode = SND_PCM_START_DATA; | 127 cpars->start_mode = SND_PCM_START_DATA; |
75 cparams->stop_mode = SND_PCM_STOP_STOP; | 128 cpars->stop_mode = SND_PCM_STOP_STOP; |
76 cparams->format.format = SND_PCM_SFMT_S16_LE; | 129 cpars->format.format = SND_PCM_SFMT_S16_LE; |
77 cparams->format.interleave = 1; | 130 cpars->format.interleave = 1; |
78 cparams->format.rate = DEFAULT_CPARAMS_RATE; | 131 cpars->format.rate = DEFAULT_CPARAMS_RATE; |
79 cparams->format.voices = DEFAULT_CPARAMS_VOICES; | 132 cpars->format.voices = DEFAULT_CPARAMS_VOICES; |
80 cparams->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE; | 133 cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE; |
81 cparams->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN; | 134 cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN; |
82 cparams->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; | 135 cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; |
83 } | 136 } |
84 | 137 |
85 static int Audio_Available(void) | 138 static int NTO_AudioAvailable(void) |
86 { | 139 { |
87 /* | 140 /* See if we can open a nonblocking channel. |
88 See if we can open a nonblocking channel. | 141 Return value '1' means we can. |
89 Return value '1' means we can. | 142 Return value '0' means we cannot. */ |
90 Return value '0' means we cannot. | 143 |
91 */ | 144 int available; |
92 | 145 int rval; |
93 int available; | 146 snd_pcm_t* handle; |
94 int rval; | 147 |
95 snd_pcm_t *handle; | 148 available = 0; |
96 | 149 handle = NULL; |
97 available = 0; | 150 |
98 handle = NULL; | 151 rval = snd_pcm_open_preferred(&handle, NULL, NULL, OPEN_FLAGS); |
99 | 152 |
100 rval = snd_pcm_open_preferred(&handle, NULL, NULL, OPEN_FLAGS); | 153 if (rval >= 0) |
101 | 154 { |
102 if (rval >= 0){ | 155 available = 1; |
103 available = 1; | 156 |
104 | 157 if ((rval = snd_pcm_close(handle)) < 0) |
105 if ((rval = snd_pcm_close(handle)) < 0){ | 158 { |
106 SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval)); | 159 SDL_SetError("NTO_AudioAvailable(): snd_pcm_close failed: %s\n", snd_strerror(rval)); |
107 available = 0; | 160 available = 0; |
108 } | 161 } |
162 } | |
163 else | |
164 { | |
165 SDL_SetError("NTO_AudioAvailable(): there are no available audio devices.\n"); | |
166 } | |
167 | |
168 return (available); | |
169 } | |
170 | |
171 static void NTO_DeleteAudioDevice(SDL_AudioDevice *device) | |
172 { | |
173 if ((device)&&(device->hidden)) | |
174 { | |
175 free(device->hidden); | |
176 } | |
177 if (device) | |
178 { | |
179 free(device); | |
180 } | |
181 } | |
182 | |
183 static SDL_AudioDevice* NTO_CreateAudioDevice(int devindex) | |
184 { | |
185 SDL_AudioDevice *this; | |
186 | |
187 /* Initialize all variables that we clean on shutdown */ | |
188 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); | |
189 if (this) | |
190 { | |
191 memset(this, 0, sizeof(SDL_AudioDevice)); | |
192 this->hidden = (struct SDL_PrivateAudioData *)malloc(sizeof(struct SDL_PrivateAudioData)); | |
193 } | |
194 if ((this == NULL) || (this->hidden == NULL)) | |
195 { | |
196 SDL_OutOfMemory(); | |
197 if (this) | |
198 { | |
199 free(this); | |
109 } | 200 } |
110 else{ | 201 return (0); |
111 SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval)); | 202 } |
112 } | 203 memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData)); |
113 | 204 audio_handle = NULL; |
114 #ifdef DEBUG_AUDIO | 205 |
115 fprintf(stderr,"AudioAvailable rtns %d\n", available); | 206 /* Set the function pointers */ |
116 #endif | 207 this->ThreadInit = NTO_ThreadInit; |
117 | 208 this->OpenAudio = NTO_OpenAudio; |
118 return(available); | 209 this->WaitAudio = NTO_WaitAudio; |
119 } | 210 this->PlayAudio = NTO_PlayAudio; |
120 | 211 this->GetAudioBuf = NTO_GetAudioBuf; |
121 static void Audio_DeleteDevice(SDL_AudioDevice *device) | 212 this->CloseAudio = NTO_CloseAudio; |
122 { | 213 |
123 #ifdef DEBUG_AUDIO | 214 this->free = NTO_DeleteAudioDevice; |
124 fprintf(stderr,"Audio_DeleteDevice\n"); | 215 |
125 #endif | 216 return this; |
126 | 217 } |
127 free(device->hidden); | 218 |
128 free(device); | 219 AudioBootStrap QNXNTOAUDIO_bootstrap = |
129 } | 220 { |
130 | 221 DRIVER_NAME, "QNX6 QSA-NTO Audio", |
131 static SDL_AudioDevice *Audio_CreateDevice(int devindex) | 222 NTO_AudioAvailable, |
132 { | 223 NTO_CreateAudioDevice |
133 SDL_AudioDevice *this; | |
134 #ifdef DEBUG_AUDIO | |
135 fprintf(stderr,"Audio_CreateDevice\n"); | |
136 #endif | |
137 /* Initialize all variables that we clean on shutdown */ | |
138 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); | |
139 if ( this ) { | |
140 memset(this, 0, (sizeof *this)); | |
141 this->hidden = (struct SDL_PrivateAudioData *) | |
142 malloc((sizeof *this->hidden)); | |
143 } | |
144 if ( (this == NULL) || (this->hidden == NULL) ) { | |
145 SDL_OutOfMemory(); | |
146 if ( this ) { | |
147 free(this); | |
148 } | |
149 return(0); | |
150 } | |
151 memset(this->hidden, 0, (sizeof *this->hidden)); | |
152 audio_handle = NULL; | |
153 | |
154 /* Set the function pointers */ | |
155 this->OpenAudio = NTO_OpenAudio; | |
156 this->WaitAudio = NTO_WaitAudio; | |
157 this->PlayAudio = NTO_PlayAudio; | |
158 this->GetAudioBuf = NTO_GetAudioBuf; | |
159 this->CloseAudio = NTO_CloseAudio; | |
160 | |
161 this->free = Audio_DeleteDevice; | |
162 | |
163 return this; | |
164 } | |
165 | |
166 AudioBootStrap QNXNTOAUDIO_bootstrap = { | |
167 DRIVER_NAME, "QNX6 NTO PCM audio", | |
168 Audio_Available, Audio_CreateDevice | |
169 }; | 224 }; |
170 | 225 |
171 /* This function waits until it is possible to write a full sound buffer */ | 226 /* This function waits until it is possible to write a full sound buffer */ |
172 static void NTO_WaitAudio(_THIS) | 227 static void NTO_WaitAudio(_THIS) |
173 { | 228 { |
174 fd_set wfds; | 229 fd_set wfds; |
175 | 230 int selectret; |
176 FD_SET( audio_fd, &wfds ); | 231 |
177 switch( select( audio_fd + 1, NULL, &wfds, NULL, NULL ) ) | 232 FD_ZERO(&wfds); |
178 { | 233 FD_SET(audio_fd, &wfds); |
179 case -1: | 234 |
180 case 0: | 235 do { |
181 /* Error */ | 236 selectret=select(audio_fd + 1, NULL, &wfds, NULL, NULL); |
182 SDL_SetError("select() in NTO_WaitAudio failed: %s\n", strerror(errno)); | 237 switch (selectret) |
183 break; | 238 { |
184 default: | 239 case -1: |
185 if(FD_ISSET(audio_fd, &wfds)) | 240 case 0: SDL_SetError("NTO_WaitAudio(): select() failed: %s\n", strerror(errno)); |
186 return; | 241 return; |
187 } | 242 default: if (FD_ISSET(audio_fd, &wfds)) |
243 { | |
244 return; | |
245 } | |
246 break; | |
247 } | |
248 } while(1); | |
188 } | 249 } |
189 | 250 |
190 static void NTO_PlayAudio(_THIS) | 251 static void NTO_PlayAudio(_THIS) |
191 { | 252 { |
192 int written, rval; | 253 int written, rval; |
193 int towrite; | 254 int towrite; |
194 | 255 void* pcmbuffer; |
195 #ifdef DEBUG_AUDIO | 256 |
196 fprintf(stderr, "NTO_PlayAudio\n"); | 257 if (!this->enabled) |
197 #endif | 258 { |
198 | 259 return; |
199 if( !this->enabled){ | 260 } |
200 return; | 261 |
201 } | 262 towrite = this->spec.size; |
202 | 263 pcmbuffer = pcm_buf; |
203 towrite = pcm_len; | 264 |
265 /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ | |
266 do { | |
267 written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite); | |
268 if (written != towrite) | |
269 { | |
270 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) | |
271 { | |
272 /* Let a little CPU time go by and try to write again */ | |
273 SDL_Delay(1); | |
274 /* if we wrote some data */ | |
275 towrite -= written; | |
276 pcmbuffer += written * this->spec.channels; | |
277 continue; | |
278 } | |
279 else | |
280 { | |
281 if ((errno == EINVAL) || (errno == EIO)) | |
282 { | |
283 memset(&cstatus, 0, sizeof(cstatus)); | |
284 cstatus.channel = SND_PCM_CHANNEL_PLAYBACK; | |
285 if ((rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0) | |
286 { | |
287 SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_status failed: %s\n", snd_strerror(rval)); | |
288 return; | |
289 } | |
290 if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) | |
291 { | |
292 if ((rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) | |
293 { | |
294 SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rval)); | |
295 return; | |
296 } | |
297 } | |
298 continue; | |
299 } | |
300 else | |
301 { | |
302 return; | |
303 } | |
304 } | |
305 } | |
306 else | |
307 { | |
308 /* we wrote all remaining data */ | |
309 towrite -= written; | |
310 pcmbuffer += written * this->spec.channels; | |
311 } | |
312 } while ((towrite > 0) && (this->enabled)); | |
313 | |
314 /* If we couldn't write, assume fatal error for now */ | |
315 if (towrite != 0) | |
316 { | |
317 this->enabled = 0; | |
318 } | |
319 | |
320 return; | |
321 } | |
322 | |
323 static Uint8* NTO_GetAudioBuf(_THIS) | |
324 { | |
325 return pcm_buf; | |
326 } | |
327 | |
328 static void NTO_CloseAudio(_THIS) | |
329 { | |
330 int rval; | |
331 | |
332 this->enabled = 0; | |
333 | |
334 if (audio_handle != NULL) | |
335 { | |
336 if ((rval = snd_pcm_plugin_flush(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) | |
337 { | |
338 SDL_SetError("NTO_CloseAudio(): snd_pcm_plugin_flush failed: %s\n", snd_strerror(rval)); | |
339 return; | |
340 } | |
341 if ((rval = snd_pcm_close(audio_handle)) < 0) | |
342 { | |
343 SDL_SetError("NTO_CloseAudio(): snd_pcm_close failed: %s\n",snd_strerror(rval)); | |
344 return; | |
345 } | |
346 audio_handle = NULL; | |
347 } | |
348 } | |
349 | |
350 static int NTO_OpenAudio(_THIS, SDL_AudioSpec* spec) | |
351 { | |
352 int rval; | |
353 int format; | |
354 Uint16 test_format; | |
355 int found; | |
356 | |
357 audio_handle = NULL; | |
358 this->enabled = 0; | |
359 | |
360 if (pcm_buf != NULL) | |
361 { | |
362 SDL_FreeAudioMem(pcm_buf); | |
363 pcm_buf = NULL; | |
364 } | |
365 | |
366 /* initialize channel transfer parameters to default */ | |
367 NTO_InitAudioParams(&cparams); | |
368 | |
369 /* Open the audio device */ | |
370 rval = snd_pcm_open_preferred(&audio_handle, &cardno, &deviceno, OPEN_FLAGS); | |
371 if (rval < 0) | |
372 { | |
373 SDL_SetError("NTO_OpenAudio(): snd_pcm_open failed: %s\n", snd_strerror(rval)); | |
374 return (-1); | |
375 } | |
376 | |
377 if (!NTO_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) | |
378 { | |
379 /* enable count status parameter */ | |
380 if ((rval = snd_pcm_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP)) < 0) | |
381 { | |
382 SDL_SetError("snd_pcm_plugin_set_disable failed: %s\n", snd_strerror(rval)); | |
383 return (-1); | |
384 } | |
385 } | |
386 | |
387 /* Try for a closest match on audio format */ | |
388 format = 0; | |
389 /* can't use format as SND_PCM_SFMT_U8 = 0 in nto */ | |
390 found = 0; | |
391 | |
392 for (test_format=SDL_FirstAudioFormat(spec->format); !found ;) | |
393 { | |
394 /* if match found set format to equivalent ALSA format */ | |
395 switch (test_format) | |
396 { | |
397 case AUDIO_U8: | |
398 format = SND_PCM_SFMT_U8; | |
399 found = 1; | |
400 break; | |
401 case AUDIO_S8: | |
402 format = SND_PCM_SFMT_S8; | |
403 found = 1; | |
404 break; | |
405 case AUDIO_S16LSB: | |
406 format = SND_PCM_SFMT_S16_LE; | |
407 found = 1; | |
408 break; | |
409 case AUDIO_S16MSB: | |
410 format = SND_PCM_SFMT_S16_BE; | |
411 found = 1; | |
412 break; | |
413 case AUDIO_U16LSB: | |
414 format = SND_PCM_SFMT_U16_LE; | |
415 found = 1; | |
416 break; | |
417 case AUDIO_U16MSB: | |
418 format = SND_PCM_SFMT_U16_BE; | |
419 found = 1; | |
420 break; | |
421 default: | |
422 break; | |
423 } | |
424 | |
425 if (!found) | |
426 { | |
427 test_format = SDL_NextAudioFormat(); | |
428 } | |
429 } | |
430 | |
431 /* assumes test_format not 0 on success */ | |
432 if (test_format == 0) | |
433 { | |
434 SDL_SetError("NTO_OpenAudio(): Couldn't find any hardware audio formats"); | |
435 return (-1); | |
436 } | |
437 | |
438 spec->format = test_format; | |
439 | |
440 /* Set the audio format */ | |
441 cparams.format.format = format; | |
442 | |
443 /* Set mono or stereo audio (currently only two channels supported) */ | |
444 cparams.format.voices = spec->channels; | |
204 | 445 |
205 /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ | 446 /* Set rate */ |
206 do { | 447 cparams.format.rate = spec->freq; |
207 written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite); | 448 |
208 #ifdef DEBUG_AUDIO | 449 /* Setup the transfer parameters according to cparams */ |
209 fprintf(stderr, "NTO_PlayAudio: written = %d towrite = %d\n",written,towrite); | 450 rval = snd_pcm_plugin_params(audio_handle, &cparams); |
210 #endif | 451 if (rval < 0) |
211 if (written != towrite){ | 452 { |
212 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)){ | 453 SDL_SetError("NTO_OpenAudio(): snd_pcm_channel_params failed: %s\n", snd_strerror(rval)); |
213 SDL_Delay(1); /* Let a little CPU time go by and try to write again */ | 454 return (-1); |
214 #ifdef DEBUG_AUDIO | 455 } |
215 fprintf(stderr, "errno == EAGAIN written %d\n", written); | 456 |
216 #endif | 457 /* Make sure channel is setup right one last time */ |
217 towrite -= written; //we wrote some data | 458 memset(&csetup, 0x00, sizeof(csetup)); |
218 continue; | 459 csetup.channel = SND_PCM_CHANNEL_PLAYBACK; |
219 } | 460 if (snd_pcm_plugin_setup(audio_handle, &csetup) < 0) |
220 else if((errno == EINVAL) || (errno == EIO)){ | 461 { |
221 if(errno == EIO){ | 462 SDL_SetError("NTO_OpenAudio(): Unable to setup playback channel\n"); |
222 #ifdef DEBUG_AUDIO | 463 return -1; |
223 fprintf(stderr,"snd_pcm_plugin_write failed EIO: %s\n", snd_strerror(written)); | 464 } |
224 #endif | 465 |
225 } | 466 |
226 if(errno == EINVAL){ | 467 /* Calculate the final parameters for this audio specification */ |
227 #ifdef DEBUG_AUDIO | 468 SDL_CalculateAudioSpec(spec); |
228 fprintf(stderr,"snd_pcm_plugin_write failed EINVAL: %s\n", snd_strerror(written)); | 469 |
229 #endif | 470 pcm_len = spec->size; |
230 } | 471 |
231 | 472 if (pcm_len==0) |
232 memset(&cstatus, 0, sizeof(cstatus)); | 473 { |
233 if( (rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0 ){ | 474 pcm_len = csetup.buf.block.frag_size * spec->channels * (snd_pcm_format_width(format)/8); |
234 #ifdef DEBUG_AUDIO | 475 } |
235 fprintf(stderr, "snd_pcm_plugin_status failed %s\n",snd_strerror(rval)); | 476 |
236 #endif | 477 /* Allocate memory to the audio buffer and initialize with silence (Note that |
237 SDL_SetError("snd_pcm_plugin_status failed: %s\n", snd_strerror(rval)); | 478 buffer size must be a multiple of fragment size, so find closest multiple) |
238 return; | 479 */ |
239 } | 480 pcm_buf = (Uint8*)SDL_AllocAudioMem(pcm_len); |
240 | 481 if (pcm_buf == NULL) |
241 if ( (cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY) ){ | 482 { |
242 #ifdef DEBUG_AUDIO | 483 SDL_SetError("NTO_OpenAudio(): pcm buffer allocation failed\n"); |
243 fprintf(stderr, "buffer underrun\n"); | 484 return (-1); |
244 #endif | 485 } |
245 if ( (rval = snd_pcm_plugin_prepare (audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0 ){ | 486 memset(pcm_buf, spec->silence, pcm_len); |
246 #ifdef DEBUG_AUDIO | 487 |
247 fprintf(stderr, "NTO_PlayAudio: prepare failed %s\n",snd_strerror(rval)); | 488 /* get the file descriptor */ |
248 #endif | 489 if ((audio_fd = snd_pcm_file_descriptor(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) |
249 SDL_SetError("snd_pcm_plugin_prepare failed: %s\n",snd_strerror(rval) ); | 490 { |
250 return; | 491 SDL_SetError("NTO_OpenAudio(): snd_pcm_file_descriptor failed with error code: %s\n", snd_strerror(rval)); |
251 } | 492 return (-1); |
252 } | 493 } |
253 continue; | 494 |
254 } | 495 /* Trigger audio playback */ |
255 else{ | 496 rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK); |
256 #ifdef DEBUG_AUDIO | 497 if (rval < 0) |
257 fprintf(stderr, "NTO_PlayAudio: snd_pcm_plugin_write failed unknown errno %d %s\n",errno, snd_strerror(rval)); | 498 { |
258 #endif | 499 SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rval)); |
259 return; | 500 return (-1); |
260 } | 501 } |
261 | 502 |
262 } | 503 this->enabled = 1; |
263 else | 504 |
264 { | 505 /* Get the parent process id (we're the parent of the audio thread) */ |
265 towrite -= written; //we wrote all remaining data | 506 parent = getpid(); |
266 } | 507 |
267 } while ( (towrite > 0) && (this->enabled) ); | 508 /* We're really ready to rock and roll. :-) */ |
268 | 509 return (0); |
269 /* If we couldn't write, assume fatal error for now */ | 510 } |
270 if ( towrite != 0 ) { | |
271 this->enabled = 0; | |
272 } | |
273 return; | |
274 } | |
275 | |
276 static Uint8 *NTO_GetAudioBuf(_THIS) | |
277 { | |
278 #ifdef DEBUG_AUDIO | |
279 fprintf(stderr, "NTO_GetAudioBuf: pcm_buf %X\n",(Uint8 *)pcm_buf); | |
280 #endif | |
281 return(pcm_buf); | |
282 } | |
283 | |
284 static void NTO_CloseAudio(_THIS) | |
285 { | |
286 int rval; | |
287 | |
288 #ifdef DEBUG_AUDIO | |
289 fprintf(stderr, "NTO_CloseAudio\n"); | |
290 #endif | |
291 | |
292 this->enabled = 0; | |
293 | |
294 if ( audio_handle != NULL ) { | |
295 if ((rval = snd_pcm_plugin_flush(audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0){ | |
296 SDL_SetError("snd_pcm_plugin_flush failed: %s\n",snd_strerror(rval)); | |
297 return; | |
298 } | |
299 if ((rval = snd_pcm_close(audio_handle)) < 0){ | |
300 SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval)); | |
301 return; | |
302 } | |
303 audio_handle = NULL; | |
304 } | |
305 } | |
306 | |
307 static int NTO_OpenAudio(_THIS, SDL_AudioSpec *spec) | |
308 { | |
309 int rval; | |
310 int format; | |
311 Uint16 test_format; | |
312 int twidth; | |
313 int found; | |
314 | |
315 #ifdef DEBUG_AUDIO | |
316 fprintf(stderr, "NTO_OpenAudio\n"); | |
317 #endif | |
318 | |
319 audio_handle = NULL; | |
320 this->enabled = 0; | |
321 | |
322 if ( pcm_buf != NULL ) { | |
323 free((Uint8 *)pcm_buf); | |
324 pcm_buf = NULL; | |
325 } | |
326 | |
327 /* initialize channel transfer parameters to default */ | |
328 init_pcm_cparams(&cparams); | |
329 | |
330 /* Open the audio device */ | |
331 rval = snd_pcm_open_preferred(&audio_handle, NULL, NULL, OPEN_FLAGS); | |
332 if ( rval < 0 ) { | |
333 SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval)); | |
334 return(-1); | |
335 } | |
336 | |
337 /* enable count status parameter */ | |
338 if ((rval = snd_pcm_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP))<0){ | |
339 SDL_SetError("snd_pcm_plugin_set_disable failed: %s\n", snd_strerror(rval)); | |
340 return(-1); | |
341 } | |
342 | |
343 /* Try for a closest match on audio format */ | |
344 format = 0; | |
345 found = 0; /* can't use format as SND_PCM_SFMT_U8 = 0 in nto */ | |
346 for ( test_format = SDL_FirstAudioFormat(spec->format); !found ; ) | |
347 { | |
348 #ifdef DEBUG_AUDIO | |
349 fprintf(stderr, "Trying format 0x%4.4x spec->samples %d\n", test_format,spec->samples); | |
350 #endif | |
351 | |
352 /* if match found set format to equivalent ALSA format */ | |
353 switch ( test_format ) { | |
354 case AUDIO_U8: | |
355 format = SND_PCM_SFMT_U8; | |
356 found = 1; | |
357 break; | |
358 case AUDIO_S8: | |
359 format = SND_PCM_SFMT_S8; | |
360 found = 1; | |
361 break; | |
362 case AUDIO_S16LSB: | |
363 format = SND_PCM_SFMT_S16_LE; | |
364 found = 1; | |
365 break; | |
366 case AUDIO_S16MSB: | |
367 format = SND_PCM_SFMT_S16_BE; | |
368 found = 1; | |
369 break; | |
370 case AUDIO_U16LSB: | |
371 format = SND_PCM_SFMT_U16_LE; | |
372 found = 1; | |
373 break; | |
374 case AUDIO_U16MSB: | |
375 format = SND_PCM_SFMT_U16_BE; | |
376 found = 1; | |
377 break; | |
378 default: | |
379 break; | |
380 } | |
381 if ( ! found ) { | |
382 test_format = SDL_NextAudioFormat(); | |
383 } | |
384 } | |
385 | |
386 /* assumes test_format not 0 on success */ | |
387 if ( test_format == 0 ) { | |
388 SDL_SetError("Couldn't find any hardware audio formats"); | |
389 return(-1); | |
390 } | |
391 | |
392 spec->format = test_format; | |
393 | |
394 /* Set the audio format */ | |
395 cparams.format.format = format; | |
396 | |
397 /* Set mono or stereo audio (currently only two channels supported) */ | |
398 cparams.format.voices = spec->channels; | |
399 | |
400 #ifdef DEBUG_AUDIO | |
401 fprintf(stderr,"intializing channels %d\n", cparams.format.voices); | |
402 #endif | |
403 | |
404 /* Set rate */ | |
405 cparams.format.rate = spec->freq ; | |
406 | |
407 /* Setup the transfer parameters according to cparams */ | |
408 rval = snd_pcm_plugin_params(audio_handle, &cparams); | |
409 if (rval < 0) { | |
410 SDL_SetError("snd_pcm_channel_params failed: %s\n", snd_strerror (rval)); | |
411 return(-1); | |
412 } | |
413 | |
414 /* Make sure channel is setup right one last time */ | |
415 memset( &csetup, 0, sizeof( csetup ) ); | |
416 csetup.channel = SND_PCM_CHANNEL_PLAYBACK; | |
417 if ( snd_pcm_plugin_setup( audio_handle, &csetup ) < 0 ) | |
418 { | |
419 SDL_SetError("Unable to setup playback channel\n" ); | |
420 return(-1); | |
421 } | |
422 else | |
423 { | |
424 #ifdef DEBUG_AUDIO | |
425 fprintf(stderr,"requested format: %d\n",cparams.format.format); | |
426 fprintf(stderr,"requested frag size: %d\n",cparams.buf.block.frag_size); | |
427 fprintf(stderr,"requested max frags: %d\n\n",cparams.buf.block.frags_max); | |
428 | |
429 fprintf(stderr,"real format: %d\n", csetup.format.format ); | |
430 fprintf(stderr,"real frag size : %d\n", csetup.buf.block.frag_size ); | |
431 fprintf(stderr,"real max frags : %d\n", csetup.buf.block.frags_max ); | |
432 #endif | |
433 } | |
434 | |
435 /* | |
436 Allocate memory to the audio buffer and initialize with silence (Note that | |
437 buffer size must be a multiple of fragment size, so find closest multiple) | |
438 */ | |
439 | |
440 twidth = snd_pcm_format_width(format); | |
441 if (twidth < 0) { | |
442 printf("snd_pcm_format_width failed\n"); | |
443 twidth = 0; | |
444 } | |
445 | |
446 #ifdef DEBUG_AUDIO | |
447 fprintf(stderr,"format is %d bits wide\n",twidth); | |
448 #endif | |
449 | |
450 pcm_len = spec->size ; | |
451 | |
452 #ifdef DEBUG_AUDIO | |
453 fprintf(stderr,"pcm_len set to %d\n", pcm_len); | |
454 #endif | |
455 | |
456 if (pcm_len == 0){ | |
457 pcm_len = csetup.buf.block.frag_size; | |
458 } | |
459 | |
460 pcm_buf = (Uint8*)malloc(pcm_len); | |
461 if (pcm_buf == NULL) { | |
462 SDL_SetError("pcm_buf malloc failed\n"); | |
463 return(-1); | |
464 } | |
465 memset(pcm_buf,spec->silence,pcm_len); | |
466 | |
467 #ifdef DEBUG_AUDIO | |
468 fprintf(stderr,"pcm_buf malloced and silenced.\n"); | |
469 #endif | |
470 | |
471 /* get the file descriptor */ | |
472 if( (audio_fd = snd_pcm_file_descriptor(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0){ | |
473 fprintf(stderr, "snd_pcm_file_descriptor failed with error code: %d\n", audio_fd); | |
474 } | |
475 | |
476 /* Trigger audio playback */ | |
477 rval = snd_pcm_plugin_prepare( audio_handle, SND_PCM_CHANNEL_PLAYBACK); | |
478 if (rval < 0) { | |
479 SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rval)); | |
480 return(-1); | |
481 } | |
482 | |
483 this->enabled = 1; | |
484 | |
485 /* Get the parent process id (we're the parent of the audio thread) */ | |
486 parent = getpid(); | |
487 | |
488 /* We're ready to rock and roll. :-) */ | |
489 return(0); | |
490 } |