/
SDL_nto_audio.c
490 lines (415 loc) · 12.3 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
20
slouken@libsdl.org
21
22
23
24
25
26
27
28
29
30
31
32
33
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sched.h>
#include <sys/asoundlib.h>
34
#include <sys/select.h>
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "SDL_audio.h"
#include "SDL_error.h"
#include "SDL_audiomem.h"
#include "SDL_audio_c.h"
#include "SDL_timer.h"
#include "SDL_nto_audio.h"
/* The tag name used by NTO audio */
#define DRIVER_NAME "nto"
/* default channel communication parameters */
#define DEFAULT_CPARAMS_RATE 22050
#define DEFAULT_CPARAMS_VOICES 1
49
#define DEFAULT_CPARAMS_FRAG_SIZE 4096
50
#define DEFAULT_CPARAMS_FRAGS_MIN 1
51
#define DEFAULT_CPARAMS_FRAGS_MAX 1
52
53
/* Open the audio device for playback, and don't block if busy */
54
#define OPEN_FLAGS SND_PCM_OPEN_PLAYBACK
55
56
57
58
59
60
61
62
63
/* Audio driver functions */
static int NTO_OpenAudio(_THIS, SDL_AudioSpec *spec);
static void NTO_WaitAudio(_THIS);
static void NTO_PlayAudio(_THIS);
static Uint8 *NTO_GetAudioBuf(_THIS);
static void NTO_CloseAudio(_THIS);
static snd_pcm_channel_status_t cstatus;
64
65
static snd_pcm_channel_params_t cparams;
static snd_pcm_channel_setup_t csetup;
66
67
68
69
70
71
72
73
/* PCM transfer channel parameters initialize function */
static void init_pcm_cparams(snd_pcm_channel_params_t* cparams)
{
memset(cparams,0,sizeof(snd_pcm_channel_params_t));
cparams->channel = SND_PCM_CHANNEL_PLAYBACK;
cparams->mode = SND_PCM_MODE_BLOCK;
74
cparams->start_mode = SND_PCM_START_DATA;
75
76
77
78
79
80
81
82
83
84
85
86
cparams->stop_mode = SND_PCM_STOP_STOP;
cparams->format.format = SND_PCM_SFMT_S16_LE;
cparams->format.interleave = 1;
cparams->format.rate = DEFAULT_CPARAMS_RATE;
cparams->format.voices = DEFAULT_CPARAMS_VOICES;
cparams->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
cparams->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
cparams->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
}
static int Audio_Available(void)
{
87
88
89
90
91
92
/*
See if we can open a nonblocking channel.
Return value '1' means we can.
Return value '0' means we cannot.
*/
93
94
95
96
97
98
99
int available;
int rval;
snd_pcm_t *handle;
available = 0;
handle = NULL;
100
rval = snd_pcm_open_preferred(&handle, NULL, NULL, OPEN_FLAGS);
101
102
103
104
105
106
if (rval >= 0){
available = 1;
if ((rval = snd_pcm_close(handle)) < 0){
SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval));
107
available = 0;
108
}
109
}
110
111
else{
SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval));
112
113
}
114
#ifdef DEBUG_AUDIO
115
fprintf(stderr,"AudioAvailable rtns %d\n", available);
116
117
#endif
118
119
120
121
122
return(available);
}
static void Audio_DeleteDevice(SDL_AudioDevice *device)
{
123
#ifdef DEBUG_AUDIO
124
fprintf(stderr,"Audio_DeleteDevice\n");
125
#endif
126
127
128
129
130
131
132
133
free(device->hidden);
free(device);
}
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
{
SDL_AudioDevice *this;
134
#ifdef DEBUG_AUDIO
135
fprintf(stderr,"Audio_CreateDevice\n");
136
#endif
137
138
139
140
141
/* Initialize all variables that we clean on shutdown */
this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
if ( this ) {
memset(this, 0, (sizeof *this));
this->hidden = (struct SDL_PrivateAudioData *)
142
malloc((sizeof *this->hidden));
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
}
if ( (this == NULL) || (this->hidden == NULL) ) {
SDL_OutOfMemory();
if ( this ) {
free(this);
}
return(0);
}
memset(this->hidden, 0, (sizeof *this->hidden));
audio_handle = NULL;
/* Set the function pointers */
this->OpenAudio = NTO_OpenAudio;
this->WaitAudio = NTO_WaitAudio;
this->PlayAudio = NTO_PlayAudio;
this->GetAudioBuf = NTO_GetAudioBuf;
this->CloseAudio = NTO_CloseAudio;
this->free = Audio_DeleteDevice;
return this;
}
166
167
AudioBootStrap QNXNTOAUDIO_bootstrap = {
DRIVER_NAME, "QNX6 NTO PCM audio",
168
169
170
171
172
173
Audio_Available, Audio_CreateDevice
};
/* This function waits until it is possible to write a full sound buffer */
static void NTO_WaitAudio(_THIS)
{
174
fd_set wfds;
175
176
177
FD_SET( audio_fd, &wfds );
switch( select( audio_fd + 1, NULL, &wfds, NULL, NULL ) )
178
{
179
180
181
182
183
184
185
186
187
case -1:
case 0:
/* Error */
SDL_SetError("select() in NTO_WaitAudio failed: %s\n", strerror(errno));
break;
default:
if(FD_ISSET(audio_fd, &wfds))
return;
}
188
189
190
191
}
static void NTO_PlayAudio(_THIS)
{
192
193
int written, rval;
int towrite;
194
195
196
197
#ifdef DEBUG_AUDIO
fprintf(stderr, "NTO_PlayAudio\n");
#endif
198
199
200
201
if( !this->enabled){
return;
}
202
203
204
towrite = pcm_len;
205
206
/* Write the audio data, checking for EAGAIN (buffer full) and underrun */
do {
207
written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite);
208
#ifdef DEBUG_AUDIO
209
fprintf(stderr, "NTO_PlayAudio: written = %d towrite = %d\n",written,towrite);
210
211
212
213
214
#endif
if (written != towrite){
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)){
SDL_Delay(1); /* Let a little CPU time go by and try to write again */
#ifdef DEBUG_AUDIO
215
fprintf(stderr, "errno == EAGAIN written %d\n", written);
216
#endif
217
218
towrite -= written; //we wrote some data
continue;
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
}
else if((errno == EINVAL) || (errno == EIO)){
if(errno == EIO){
#ifdef DEBUG_AUDIO
fprintf(stderr,"snd_pcm_plugin_write failed EIO: %s\n", snd_strerror(written));
#endif
}
if(errno == EINVAL){
#ifdef DEBUG_AUDIO
fprintf(stderr,"snd_pcm_plugin_write failed EINVAL: %s\n", snd_strerror(written));
#endif
}
memset(&cstatus, 0, sizeof(cstatus));
if( (rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0 ){
#ifdef DEBUG_AUDIO
235
fprintf(stderr, "snd_pcm_plugin_status failed %s\n",snd_strerror(rval));
236
237
238
239
#endif
SDL_SetError("snd_pcm_plugin_status failed: %s\n", snd_strerror(rval));
return;
}
240
241
242
if ( (cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY) ){
#ifdef DEBUG_AUDIO
243
fprintf(stderr, "buffer underrun\n");
244
245
246
#endif
if ( (rval = snd_pcm_plugin_prepare (audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0 ){
#ifdef DEBUG_AUDIO
247
fprintf(stderr, "NTO_PlayAudio: prepare failed %s\n",snd_strerror(rval));
248
#endif
249
250
251
252
253
254
SDL_SetError("snd_pcm_plugin_prepare failed: %s\n",snd_strerror(rval) );
return;
}
}
continue;
}
255
256
257
258
else{
#ifdef DEBUG_AUDIO
fprintf(stderr, "NTO_PlayAudio: snd_pcm_plugin_write failed unknown errno %d %s\n",errno, snd_strerror(rval));
#endif
259
260
261
262
263
264
265
266
return;
}
}
else
{
towrite -= written; //we wrote all remaining data
}
267
} while ( (towrite > 0) && (this->enabled) );
268
269
270
271
272
/* If we couldn't write, assume fatal error for now */
if ( towrite != 0 ) {
this->enabled = 0;
}
273
274
275
276
277
return;
}
static Uint8 *NTO_GetAudioBuf(_THIS)
{
278
279
280
#ifdef DEBUG_AUDIO
fprintf(stderr, "NTO_GetAudioBuf: pcm_buf %X\n",(Uint8 *)pcm_buf);
#endif
281
282
283
284
285
286
287
return(pcm_buf);
}
static void NTO_CloseAudio(_THIS)
{
int rval;
288
289
290
291
292
#ifdef DEBUG_AUDIO
fprintf(stderr, "NTO_CloseAudio\n");
#endif
this->enabled = 0;
293
294
if ( audio_handle != NULL ) {
295
296
if ((rval = snd_pcm_plugin_flush(audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0){
SDL_SetError("snd_pcm_plugin_flush failed: %s\n",snd_strerror(rval));
297
298
return;
}
299
if ((rval = snd_pcm_close(audio_handle)) < 0){
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval));
return;
}
audio_handle = NULL;
}
}
static int NTO_OpenAudio(_THIS, SDL_AudioSpec *spec)
{
int rval;
int format;
Uint16 test_format;
int twidth;
int found;
315
316
317
#ifdef DEBUG_AUDIO
fprintf(stderr, "NTO_OpenAudio\n");
#endif
318
319
audio_handle = NULL;
320
this->enabled = 0;
321
322
323
324
325
326
327
328
329
330
if ( pcm_buf != NULL ) {
free((Uint8 *)pcm_buf);
pcm_buf = NULL;
}
/* initialize channel transfer parameters to default */
init_pcm_cparams(&cparams);
/* Open the audio device */
331
rval = snd_pcm_open_preferred(&audio_handle, NULL, NULL, OPEN_FLAGS);
332
333
334
335
336
if ( rval < 0 ) {
SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval));
return(-1);
}
337
338
339
340
341
/* enable count status parameter */
if ((rval = snd_pcm_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP))<0){
SDL_SetError("snd_pcm_plugin_set_disable failed: %s\n", snd_strerror(rval));
return(-1);
}
342
343
344
/* Try for a closest match on audio format */
format = 0;
345
found = 0; /* can't use format as SND_PCM_SFMT_U8 = 0 in nto */
346
347
for ( test_format = SDL_FirstAudioFormat(spec->format); !found ; )
{
348
#ifdef DEBUG_AUDIO
349
fprintf(stderr, "Trying format 0x%4.4x spec->samples %d\n", test_format,spec->samples);
350
351
352
353
#endif
/* if match found set format to equivalent ALSA format */
switch ( test_format ) {
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
case AUDIO_U8:
format = SND_PCM_SFMT_U8;
found = 1;
break;
case AUDIO_S8:
format = SND_PCM_SFMT_S8;
found = 1;
break;
case AUDIO_S16LSB:
format = SND_PCM_SFMT_S16_LE;
found = 1;
break;
case AUDIO_S16MSB:
format = SND_PCM_SFMT_S16_BE;
found = 1;
break;
case AUDIO_U16LSB:
format = SND_PCM_SFMT_U16_LE;
found = 1;
break;
case AUDIO_U16MSB:
format = SND_PCM_SFMT_U16_BE;
found = 1;
break;
default:
break;
}
if ( ! found ) {
test_format = SDL_NextAudioFormat();
}
}
385
386
387
388
389
390
/* assumes test_format not 0 on success */
if ( test_format == 0 ) {
SDL_SetError("Couldn't find any hardware audio formats");
return(-1);
}
391
392
393
394
395
396
397
398
399
spec->format = test_format;
/* Set the audio format */
cparams.format.format = format;
/* Set mono or stereo audio (currently only two channels supported) */
cparams.format.voices = spec->channels;
400
#ifdef DEBUG_AUDIO
401
fprintf(stderr,"intializing channels %d\n", cparams.format.voices);
402
#endif
403
404
405
406
407
408
409
410
411
412
413
/* Set rate */
cparams.format.rate = spec->freq ;
/* Setup the transfer parameters according to cparams */
rval = snd_pcm_plugin_params(audio_handle, &cparams);
if (rval < 0) {
SDL_SetError("snd_pcm_channel_params failed: %s\n", snd_strerror (rval));
return(-1);
}
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/* Make sure channel is setup right one last time */
memset( &csetup, 0, sizeof( csetup ) );
csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
if ( snd_pcm_plugin_setup( audio_handle, &csetup ) < 0 )
{
SDL_SetError("Unable to setup playback channel\n" );
return(-1);
}
else
{
#ifdef DEBUG_AUDIO
fprintf(stderr,"requested format: %d\n",cparams.format.format);
fprintf(stderr,"requested frag size: %d\n",cparams.buf.block.frag_size);
fprintf(stderr,"requested max frags: %d\n\n",cparams.buf.block.frags_max);
fprintf(stderr,"real format: %d\n", csetup.format.format );
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
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
#endif
}
/*
Allocate memory to the audio buffer and initialize with silence (Note that
buffer size must be a multiple of fragment size, so find closest multiple)
*/
twidth = snd_pcm_format_width(format);
if (twidth < 0) {
printf("snd_pcm_format_width failed\n");
twidth = 0;
}
#ifdef DEBUG_AUDIO
fprintf(stderr,"format is %d bits wide\n",twidth);
#endif
pcm_len = spec->size ;
#ifdef DEBUG_AUDIO
fprintf(stderr,"pcm_len set to %d\n", pcm_len);
#endif
if (pcm_len == 0){
pcm_len = csetup.buf.block.frag_size;
}
pcm_buf = (Uint8*)malloc(pcm_len);
if (pcm_buf == NULL) {
SDL_SetError("pcm_buf malloc failed\n");
return(-1);
}
memset(pcm_buf,spec->silence,pcm_len);
#ifdef DEBUG_AUDIO
468
fprintf(stderr,"pcm_buf malloced and silenced.\n");
469
#endif
470
471
472
473
474
/* get the file descriptor */
if( (audio_fd = snd_pcm_file_descriptor(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0){
fprintf(stderr, "snd_pcm_file_descriptor failed with error code: %d\n", audio_fd);
}
475
476
477
478
/* Trigger audio playback */
rval = snd_pcm_plugin_prepare( audio_handle, SND_PCM_CHANNEL_PLAYBACK);
if (rval < 0) {
479
480
SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rval));
return(-1);
481
}
482
483
484
this->enabled = 1;
485
486
487
488
489
490
/* Get the parent process id (we're the parent of the audio thread) */
parent = getpid();
/* We're ready to rock and roll. :-) */
return(0);
}