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, &param);
116 param.sched_priority=param.sched_curpriority+15;
117 status=SchedSet(0, 0, SCHED_NOCHANGE, &param);
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 }