12465
|
1 /*
|
|
2 ao_alsa9/1.x - ALSA-0.9.x-1.x output plugin for MPlayer
|
|
3
|
|
4 (C) Alex Beregszaszi
|
|
5
|
|
6 modified for real alsa-0.9.0-support by Zsolt Barat <joy@streamminister.de>
|
|
7 additional AC3 passthrough support by Andy Lo A Foe <andy@alsaplayer.org>
|
|
8 08/22/2002 iec958-init rewritten and merged with common init, zsolt
|
|
9 04/13/2004 merged with ao_alsa1.x, fixes provided by Jindrich Makovicka
|
|
10 04/25/2004 printfs converted to mp_msg, Zsolt.
|
|
11
|
|
12 Any bugreports regarding to this driver are welcome.
|
|
13 */
|
|
14
|
|
15 #include <errno.h>
|
|
16 #include <sys/time.h>
|
|
17 #include <stdlib.h>
|
|
18 #include <math.h>
|
|
19 #include <string.h>
|
|
20 #include <sys/poll.h>
|
|
21
|
|
22 #include "../config.h"
|
|
23 #include "../mixer.h"
|
|
24 #include "../mp_msg.h"
|
|
25
|
|
26 #define ALSA_PCM_NEW_HW_PARAMS_API
|
|
27 #define ALSA_PCM_NEW_SW_PARAMS_API
|
|
28
|
|
29 #if HAVE_SYS_ASOUNDLIB_H
|
|
30 #include <sys/asoundlib.h>
|
|
31 #elif HAVE_ALSA_ASOUNDLIB_H
|
|
32 #include <alsa/asoundlib.h>
|
|
33 #else
|
|
34 #error "asoundlib.h is not in sys/ or alsa/ - please bugreport"
|
|
35 #endif
|
|
36
|
|
37
|
|
38 #include "audio_out.h"
|
|
39 #include "audio_out_internal.h"
|
|
40 #include "afmt.h"
|
|
41
|
|
42 static ao_info_t info =
|
|
43 {
|
|
44 "ALSA-0.9.x-1.x audio output",
|
|
45 "alsa",
|
|
46 "Alex Beregszaszi, Zsolt Barat <joy@streamminister.de>",
|
|
47 "under developement"
|
|
48 };
|
|
49
|
|
50 LIBAO_EXTERN(alsa)
|
|
51
|
|
52 static snd_pcm_t *alsa_handler;
|
|
53 static snd_pcm_format_t alsa_format;
|
|
54 static snd_pcm_hw_params_t *alsa_hwparams;
|
|
55 static snd_pcm_sw_params_t *alsa_swparams;
|
|
56
|
|
57 /* possible 4096, original 8192
|
|
58 * was only needed for calculating chunksize? */
|
|
59 static int alsa_fragsize = 4096;
|
|
60 /* 16 sets buffersize to 16 * chunksize is as default 1024
|
|
61 * which seems to be good avarge for most situations
|
|
62 * so buffersize is 16384 frames by default */
|
|
63 static int alsa_fragcount = 16;
|
|
64 static snd_pcm_uframes_t chunk_size = 1024;//is alsa_fragsize / 4
|
|
65
|
|
66 #define MIN_CHUNK_SIZE 1024
|
|
67
|
|
68 static size_t bits_per_sample, bytes_per_sample, bits_per_frame;
|
|
69 static size_t chunk_bytes;
|
|
70
|
|
71 int ao_mmap = 0;
|
|
72 int ao_noblock = 0;
|
|
73 int first = 1;
|
|
74
|
|
75 static int open_mode;
|
|
76 static int set_block_mode;
|
|
77 static int alsa_can_pause = 0;
|
|
78
|
12747
|
79 #define ALSA_DEVICE_SIZE 256
|
12465
|
80
|
|
81 #undef BUFFERTIME
|
|
82 #undef SET_CHUNKSIZE
|
|
83 #undef USE_POLL
|
|
84
|
|
85 /* to set/get/query special features/parameters */
|
|
86 static int control(int cmd, void *arg)
|
|
87 {
|
|
88 switch(cmd) {
|
|
89 case AOCONTROL_QUERY_FORMAT:
|
|
90 return CONTROL_TRUE;
|
|
91 #ifndef WORDS_BIGENDIAN
|
|
92 case AOCONTROL_GET_VOLUME:
|
|
93 case AOCONTROL_SET_VOLUME:
|
|
94 {
|
|
95 ao_control_vol_t *vol = (ao_control_vol_t *)arg;
|
|
96
|
|
97 int err;
|
|
98 snd_mixer_t *handle;
|
|
99 snd_mixer_elem_t *elem;
|
|
100 snd_mixer_selem_id_t *sid;
|
|
101
|
12747
|
102 static char *mix_name = "PCM";
|
|
103 static char *card = "default";
|
12465
|
104
|
|
105 long pmin, pmax;
|
|
106 long get_vol, set_vol;
|
|
107 float calc_vol, diff, f_multi;
|
|
108
|
12747
|
109 if(mixer_channel) mix_name = mixer_channel;
|
|
110 if(mixer_device) card = mixer_device;
|
12465
|
111
|
|
112 if(ao_data.format == AFMT_AC3)
|
|
113 return CONTROL_TRUE;
|
|
114
|
|
115 //allocate simple id
|
|
116 snd_mixer_selem_id_alloca(&sid);
|
|
117
|
|
118 //sets simple-mixer index and name
|
|
119 snd_mixer_selem_id_set_index(sid, 0);
|
|
120 snd_mixer_selem_id_set_name(sid, mix_name);
|
|
121
|
|
122 if ((err = snd_mixer_open(&handle, 0)) < 0) {
|
|
123 mp_msg(MSGT_AO,MSGL_ERR,"alsa-control: mixer open error: %s\n", snd_strerror(err));
|
|
124 return CONTROL_ERROR;
|
|
125 }
|
|
126
|
|
127 if ((err = snd_mixer_attach(handle, card)) < 0) {
|
|
128 mp_msg(MSGT_AO,MSGL_ERR,"alsa-control: mixer attach %s error: %s\n",
|
|
129 card, snd_strerror(err));
|
|
130 snd_mixer_close(handle);
|
|
131 return CONTROL_ERROR;
|
|
132 }
|
|
133
|
|
134 if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
|
|
135 mp_msg(MSGT_AO,MSGL_ERR,"alsa-control: mixer register error: %s\n", snd_strerror(err));
|
|
136 snd_mixer_close(handle);
|
|
137 return CONTROL_ERROR;
|
|
138 }
|
|
139 err = snd_mixer_load(handle);
|
|
140 if (err < 0) {
|
|
141 mp_msg(MSGT_AO,MSGL_ERR,"alsa-control: mixer load error: %s\n", snd_strerror(err));
|
|
142 snd_mixer_close(handle);
|
|
143 return CONTROL_ERROR;
|
|
144 }
|
|
145
|
|
146 elem = snd_mixer_find_selem(handle, sid);
|
|
147 if (!elem) {
|
|
148 mp_msg(MSGT_AO,MSGL_ERR,"alsa-control: unable to find simple control '%s',%i\n",
|
|
149 snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
|
|
150 snd_mixer_close(handle);
|
|
151 return CONTROL_ERROR;
|
|
152 }
|
|
153
|
|
154 snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax);
|
|
155 f_multi = (100 / (float)pmax);
|
|
156
|
|
157 if (cmd == AOCONTROL_SET_VOLUME) {
|
|
158
|
|
159 diff = (vol->left+vol->right) / 2;
|
|
160 set_vol = rint(diff / f_multi);
|
|
161
|
|
162 if (set_vol < 0)
|
|
163 set_vol = 0;
|
|
164 else if (set_vol > pmax)
|
|
165 set_vol = pmax;
|
|
166
|
|
167 //setting channels
|
|
168 if ((err = snd_mixer_selem_set_playback_volume(elem, 0, set_vol)) < 0) {
|
|
169 mp_msg(MSGT_AO,MSGL_ERR,"alsa-control: error setting left channel, %s\n",
|
|
170 snd_strerror(err));
|
|
171 return CONTROL_ERROR;
|
|
172 }
|
|
173 if ((err = snd_mixer_selem_set_playback_volume(elem, 1, set_vol)) < 0) {
|
|
174 mp_msg(MSGT_AO,MSGL_ERR,"alsa-control: error setting right channel, %s\n",
|
|
175 snd_strerror(err));
|
|
176 return CONTROL_ERROR;
|
|
177 }
|
|
178
|
|
179 mp_msg(MSGT_AO,MSGL_DBG2,"diff=%f, set_vol=%li, pmax=%li, mult=%f\n",
|
|
180 diff, set_vol, pmax, f_multi);
|
|
181 }
|
|
182 else {
|
|
183 snd_mixer_selem_get_playback_volume(elem, 0, &get_vol);
|
|
184 calc_vol = get_vol;
|
|
185 calc_vol = rintf(calc_vol * f_multi);
|
|
186
|
|
187 vol->left = vol->right = (int)calc_vol;
|
|
188
|
|
189 mp_msg(MSGT_AO,MSGL_DBG2,"get_vol = %li, calc=%f\n",get_vol, calc_vol);
|
|
190 }
|
|
191 snd_mixer_close(handle);
|
|
192 return CONTROL_OK;
|
|
193 }
|
|
194 #endif
|
|
195
|
|
196 } //end switch
|
|
197 return(CONTROL_UNKNOWN);
|
|
198 }
|
|
199
|
|
200
|
|
201 /*
|
|
202 open & setup audio device
|
|
203 return: 1=success 0=fail
|
|
204 */
|
|
205 static int init(int rate_hz, int channels, int format, int flags)
|
|
206 {
|
|
207 int err;
|
|
208 int cards = -1;
|
|
209 int period_val;
|
|
210 snd_pcm_info_t *alsa_info;
|
|
211 char *str_block_mode;
|
|
212 int device_set = 0;
|
|
213 int dir = 0;
|
|
214 snd_pcm_uframes_t bufsize;
|
12747
|
215 char alsa_device[ALSA_DEVICE_SIZE + 1];
|
|
216 // make sure alsa_device is null-terminated even when using strncpy etc.
|
|
217 memset(alsa_device, 0, ALSA_DEVICE_SIZE + 1);
|
12465
|
218
|
|
219 mp_msg(MSGT_AO,MSGL_V,"alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz,
|
|
220 channels, audio_out_format_name(format));
|
|
221 alsa_handler = NULL;
|
|
222 mp_msg(MSGT_AO,MSGL_V,"alsa-init: compiled for ALSA-%s\n", SND_LIB_VERSION_STR);
|
|
223
|
|
224 if ((err = snd_card_next(&cards)) < 0 || cards < 0)
|
|
225 {
|
|
226 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: no soundcards found: %s\n", snd_strerror(err));
|
|
227 return(0);
|
|
228 }
|
|
229
|
|
230 ao_data.samplerate = rate_hz;
|
|
231 ao_data.bps = channels * rate_hz;
|
|
232 ao_data.format = format;
|
|
233 ao_data.channels = channels;
|
|
234 ao_data.outburst = OUTBURST;
|
|
235
|
|
236 switch (format)
|
|
237 {
|
|
238 case AFMT_S8:
|
|
239 alsa_format = SND_PCM_FORMAT_S8;
|
|
240 break;
|
|
241 case AFMT_U8:
|
|
242 alsa_format = SND_PCM_FORMAT_U8;
|
|
243 break;
|
|
244 case AFMT_U16_LE:
|
|
245 alsa_format = SND_PCM_FORMAT_U16_LE;
|
|
246 break;
|
|
247 case AFMT_U16_BE:
|
|
248 alsa_format = SND_PCM_FORMAT_U16_BE;
|
|
249 break;
|
|
250 #ifndef WORDS_BIGENDIAN
|
|
251 case AFMT_AC3:
|
|
252 #endif
|
|
253 case AFMT_S16_LE:
|
|
254 alsa_format = SND_PCM_FORMAT_S16_LE;
|
|
255 break;
|
|
256 #ifdef WORDS_BIGENDIAN
|
|
257 case AFMT_AC3:
|
|
258 #endif
|
|
259 case AFMT_S16_BE:
|
|
260 alsa_format = SND_PCM_FORMAT_S16_BE;
|
|
261 break;
|
|
262 case AFMT_S32_LE:
|
|
263 alsa_format = SND_PCM_FORMAT_S32_LE;
|
|
264 break;
|
|
265 case AFMT_S32_BE:
|
|
266 alsa_format = SND_PCM_FORMAT_S32_BE;
|
|
267 break;
|
12570
|
268 case AFMT_FLOAT:
|
|
269 alsa_format = SND_PCM_FORMAT_FLOAT_LE;
|
|
270 break;
|
12465
|
271
|
|
272 default:
|
|
273 alsa_format = SND_PCM_FORMAT_MPEG; //? default should be -1
|
|
274 break;
|
|
275 }
|
|
276
|
|
277 //setting bw according to the input-format. resolution seems to be always s16_le or
|
|
278 //u16_le so 32bit is probably obsolet.
|
|
279 switch(alsa_format)
|
|
280 {
|
12570
|
281 case SND_PCM_FORMAT_S8:
|
|
282 case SND_PCM_FORMAT_U8:
|
|
283 ao_data.bps *= 1;
|
|
284 break;
|
12465
|
285 case SND_PCM_FORMAT_S16_LE:
|
|
286 case SND_PCM_FORMAT_U16_LE:
|
|
287 ao_data.bps *= 2;
|
|
288 break;
|
|
289 case SND_PCM_FORMAT_S32_LE:
|
|
290 case SND_PCM_FORMAT_S32_BE:
|
12570
|
291 case SND_PCM_FORMAT_FLOAT_LE:
|
12465
|
292 ao_data.bps *= 4;
|
|
293 break;
|
|
294 case -1:
|
|
295 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: invalid format (%s) requested - output disabled\n",
|
|
296 audio_out_format_name(format));
|
|
297 return(0);
|
|
298 break;
|
|
299 default:
|
|
300 ao_data.bps *= 2;
|
|
301 mp_msg(MSGT_AO,MSGL_WARN,"alsa-init: couldn't convert to right format. setting bps to: %d", ao_data.bps);
|
|
302 }
|
|
303
|
|
304 if (ao_subdevice) {
|
|
305 //start parsing ao_subdevice, ugly and not thread safe!
|
|
306 //maybe there's a better way?
|
|
307 int i2 = 1;
|
|
308 int i3 = 0;
|
|
309 char *sub_str;
|
|
310
|
|
311 char *token_str[3];
|
|
312 char* test_str = strdup(ao_subdevice);
|
|
313
|
|
314
|
|
315 if ((strcspn(ao_subdevice, ":")) > 0) {
|
|
316
|
|
317 sub_str = strtok(test_str, ":");
|
|
318 *(token_str) = sub_str;
|
|
319
|
|
320 while (((sub_str = strtok(NULL, ":")) != NULL) && (i2 <= 3)) {
|
|
321 *(token_str+i2) = sub_str;
|
|
322 i2 += 1;
|
|
323 }
|
|
324
|
|
325 for (i3=0; i3 <= i2-1; i3++) {
|
|
326 if (strcmp(*(token_str + i3), "mmap") == 0) {
|
|
327 ao_mmap = 1;
|
|
328 }
|
|
329 else if (strcmp(*(token_str+i3), "noblock") == 0) {
|
|
330 ao_noblock = 1;
|
|
331 }
|
|
332 else if (strcmp(*(token_str+i3), "hw") == 0) {
|
|
333 if ((i3 < i2-1) && (strcmp(*(token_str+i3+1), "noblock") != 0) && (strcmp(*(token_str+i3+1), "mmap") != 0)) {
|
|
334 char *tmp;
|
|
335
|
|
336 snprintf(alsa_device, ALSA_DEVICE_SIZE, "hw:%s", *(token_str+(i3+1)));
|
|
337 if ((tmp = strrchr(alsa_device, '.')) && isdigit(*(tmp+1)))
|
|
338 *tmp = ',';
|
|
339 device_set = 1;
|
|
340 }
|
|
341 else {
|
12747
|
342 strncpy (alsa_device, token_str[i3], ALSA_DEVICE_SIZE);
|
12465
|
343 device_set = 1;
|
|
344 }
|
|
345 }
|
|
346 else if (device_set == 0 && (!ao_mmap || !ao_noblock)) {
|
12747
|
347 strncpy (alsa_device, token_str[i3], ALSA_DEVICE_SIZE);
|
12465
|
348 device_set = 1;
|
|
349 }
|
|
350 }
|
|
351 }
|
|
352 } else { //end parsing ao_subdevice
|
|
353 /* in any case for multichannel playback we should select
|
|
354 * appropriate device
|
|
355 */
|
|
356 switch (channels) {
|
|
357 case 1:
|
|
358 case 2:
|
|
359 mp_msg(MSGT_AO,MSGL_V,"alsa-init: setup for 1/2 channel(s)\n");
|
|
360 break;
|
|
361 case 4:
|
12747
|
362 if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
|
|
363 // hack - use the converter plugin
|
|
364 strncpy(alsa_device, "plug:surround40", ALSA_DEVICE_SIZE);
|
|
365 else
|
|
366 strncpy(alsa_device, "surround40", ALSA_DEVICE_SIZE);
|
|
367 device_set = 1;
|
12465
|
368 mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround40\n");
|
|
369 break;
|
|
370 case 6:
|
12747
|
371 if (alsa_format == SND_PCM_FORMAT_FLOAT_LE)
|
|
372 strncpy(alsa_device, "plug:surround51", ALSA_DEVICE_SIZE);
|
|
373 else
|
|
374 strncpy(alsa_device, "surround51", ALSA_DEVICE_SIZE);
|
|
375 device_set = 1;
|
12465
|
376 mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround51\n");
|
|
377 break;
|
|
378 default:
|
|
379 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: %d channels are not supported\n",channels);
|
|
380 }
|
|
381 }
|
|
382
|
|
383 /* switch for spdif
|
|
384 * sets opening sequence for SPDIF
|
|
385 * sets also the playback and other switches 'on the fly'
|
|
386 * while opening the abstract alias for the spdif subdevice
|
|
387 * 'iec958'
|
|
388 */
|
|
389 if (format == AFMT_AC3) {
|
|
390 unsigned char s[4];
|
|
391
|
|
392 switch (channels) {
|
|
393 case 1:
|
|
394 case 2:
|
|
395
|
|
396 s[0] = IEC958_AES0_NONAUDIO |
|
|
397 IEC958_AES0_CON_EMPHASIS_NONE;
|
|
398 s[1] = IEC958_AES1_CON_ORIGINAL |
|
|
399 IEC958_AES1_CON_PCM_CODER;
|
|
400 s[2] = 0;
|
|
401 s[3] = IEC958_AES3_CON_FS_48000;
|
|
402
|
12747
|
403 snprintf(alsa_device, ALSA_DEVICE_SIZE,
|
|
404 "iec958:AES0=0x%x,AES1=0x%x,AES2=0x%x,AES3=0x%x",
|
|
405 s[0], s[1], s[2], s[3]);
|
12465
|
406
|
12747
|
407 mp_msg(MSGT_AO,MSGL_V,"alsa-spdif-init: playing AC3, %i channels\n", channels);
|
|
408 device_set = 1;
|
12465
|
409 break;
|
|
410 case 4:
|
12747
|
411 strncpy(alsa_device, "surround40", ALSA_DEVICE_SIZE);
|
|
412 device_set = 1;
|
12465
|
413 break;
|
|
414
|
|
415 case 6:
|
12747
|
416 strncpy(alsa_device, "surround51", ALSA_DEVICE_SIZE);
|
|
417 device_set = 1;
|
12465
|
418 break;
|
|
419
|
|
420 default:
|
|
421 mp_msg(MSGT_AO,MSGL_ERR,"alsa-spdif-init: %d channels are not supported\n", channels);
|
|
422 }
|
|
423 }
|
|
424
|
12747
|
425 if (!device_set)
|
12465
|
426 {
|
|
427 int tmp_device, tmp_subdevice, err;
|
|
428
|
12747
|
429 snd_pcm_info_alloca(&alsa_info);
|
12465
|
430
|
|
431 if ((tmp_device = snd_pcm_info_get_device(alsa_info)) < 0)
|
|
432 {
|
|
433 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: can't get device\n");
|
|
434 }
|
|
435
|
|
436 if ((tmp_subdevice = snd_pcm_info_get_subdevice(alsa_info)) < 0)
|
|
437 {
|
|
438 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: can't get subdevice\n");
|
|
439 }
|
|
440
|
|
441 mp_msg(MSGT_AO,MSGL_INFO,"alsa-init: got device=%i, subdevice=%i\n",
|
|
442 tmp_device, tmp_subdevice);
|
|
443
|
|
444 //we are setting here device to default cause it could be configured by the user
|
|
445 //if its not set by the user, it defaults to hw:0,0
|
|
446 if ((err = snprintf(alsa_device, ALSA_DEVICE_SIZE, "default")) <= 0)
|
|
447 {
|
|
448 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: can't write device-id\n");
|
|
449 }
|
|
450
|
|
451 mp_msg(MSGT_AO,MSGL_INFO,"alsa-init: %d soundcard%s found, using: %s\n", cards+1,(cards >= 0) ? "" : "s", alsa_device);
|
|
452 } else if (strcmp(alsa_device, "help") == 0) {
|
|
453 printf("alsa-help: available options are:\n");
|
|
454 printf(" mmap: sets mmap-mode\n");
|
|
455 printf(" noblock: sets noblock-mode\n");
|
|
456 printf(" device-name: sets device name (change comma to point)\n");
|
|
457 printf(" example -ao alsa9:mmap:noblock:hw:0.3 sets noblock-mode,\n");
|
|
458 printf(" mmap-mode and the device-name as first card fourth device\n");
|
|
459 return(0);
|
|
460 } else {
|
|
461 mp_msg(MSGT_AO,MSGL_INFO,"alsa-init: soundcard set to %s\n", alsa_device);
|
|
462 }
|
|
463
|
|
464 //setting modes for block or nonblock-mode
|
|
465 if (ao_noblock) {
|
|
466 open_mode = SND_PCM_NONBLOCK;
|
|
467 set_block_mode = 1;
|
|
468 str_block_mode = "nonblock-mode";
|
|
469 }
|
|
470 else {
|
|
471 open_mode = 0;
|
|
472 set_block_mode = 0;
|
|
473 str_block_mode = "block-mode";
|
|
474 }
|
|
475
|
|
476 //sets buff/chunksize if its set manually
|
|
477 if (ao_data.buffersize) {
|
|
478 switch (ao_data.buffersize)
|
|
479 {
|
|
480 case 1:
|
|
481 alsa_fragcount = 16;
|
|
482 chunk_size = 512;
|
|
483 mp_msg(MSGT_AO,MSGL_V,"alsa-init: buffersize set manually to 8192\n");
|
|
484 mp_msg(MSGT_AO,MSGL_V,"alsa-init: chunksize set manually to 512\n");
|
|
485 break;
|
|
486 case 2:
|
|
487 alsa_fragcount = 8;
|
|
488 chunk_size = 1024;
|
|
489 mp_msg(MSGT_AO,MSGL_V,"alsa-init: buffersize set manually to 8192\n");
|
|
490 mp_msg(MSGT_AO,MSGL_V,"alsa-init: chunksize set manually to 1024\n");
|
|
491 break;
|
|
492 case 3:
|
|
493 alsa_fragcount = 32;
|
|
494 chunk_size = 512;
|
|
495 mp_msg(MSGT_AO,MSGL_V,"alsa-init: buffersize set manually to 16384\n");
|
|
496 mp_msg(MSGT_AO,MSGL_V,"alsa-init: chunksize set manually to 512\n");
|
|
497 break;
|
|
498 case 4:
|
|
499 alsa_fragcount = 16;
|
|
500 chunk_size = 1024;
|
|
501 mp_msg(MSGT_AO,MSGL_V,"alsa-init: buffersize set manually to 16384\n");
|
|
502 mp_msg(MSGT_AO,MSGL_V,"alsa-init: chunksize set manually to 1024\n");
|
|
503 break;
|
|
504 default:
|
|
505 alsa_fragcount = 16;
|
|
506 if (ao_mmap)
|
|
507 chunk_size = 512;
|
|
508 else
|
|
509 chunk_size = 1024;
|
|
510 break;
|
|
511 }
|
|
512 }
|
|
513
|
|
514 if (!alsa_handler) {
|
|
515 //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
|
|
516 if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, open_mode)) < 0)
|
|
517 {
|
|
518 if (err != -EBUSY && ao_noblock) {
|
|
519 mp_msg(MSGT_AO,MSGL_INFO,"alsa-init: open in nonblock-mode failed, trying to open in block-mode\n");
|
|
520 if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
|
|
521 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: playback open error: %s\n", snd_strerror(err));
|
|
522 return(0);
|
|
523 } else {
|
|
524 set_block_mode = 0;
|
|
525 str_block_mode = "block-mode";
|
|
526 }
|
|
527 } else {
|
|
528 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: playback open error: %s\n", snd_strerror(err));
|
|
529 return(0);
|
|
530 }
|
|
531 }
|
|
532
|
|
533 if ((err = snd_pcm_nonblock(alsa_handler, set_block_mode)) < 0) {
|
|
534 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: error set block-mode %s\n", snd_strerror(err));
|
|
535 } else {
|
|
536 mp_msg(MSGT_AO,MSGL_V,"alsa-init: pcm opend in %s\n", str_block_mode);
|
|
537 }
|
|
538
|
|
539 snd_pcm_hw_params_alloca(&alsa_hwparams);
|
|
540 snd_pcm_sw_params_alloca(&alsa_swparams);
|
|
541
|
|
542 // setting hw-parameters
|
|
543 if ((err = snd_pcm_hw_params_any(alsa_handler, alsa_hwparams)) < 0)
|
|
544 {
|
|
545 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to get initial parameters: %s\n",
|
|
546 snd_strerror(err));
|
|
547 return(0);
|
|
548 }
|
|
549
|
|
550 if (ao_mmap) {
|
|
551 snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof());
|
|
552 snd_pcm_access_mask_none(mask);
|
|
553 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
|
|
554 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
|
|
555 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
|
|
556 err = snd_pcm_hw_params_set_access_mask(alsa_handler, alsa_hwparams, mask);
|
|
557 mp_msg(MSGT_AO,MSGL_INFO,"alsa-init: mmap set\n");
|
|
558 } else {
|
|
559 err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,
|
|
560 SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
561 }
|
|
562 if (err < 0) {
|
|
563 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set access type: %s\n",
|
|
564 snd_strerror(err));
|
|
565 return (0);
|
|
566 }
|
|
567
|
|
568 /* workaround for nonsupported formats
|
|
569 sets default format to S16_LE if the given formats aren't supported */
|
|
570 if ((err = snd_pcm_hw_params_test_format(alsa_handler, alsa_hwparams,
|
|
571 alsa_format)) < 0)
|
|
572 {
|
|
573 mp_msg(MSGT_AO,MSGL_INFO,
|
|
574 "alsa-init: format %s are not supported by hardware, trying default\n",
|
|
575 audio_out_format_name(format));
|
|
576 alsa_format = SND_PCM_FORMAT_S16_LE;
|
|
577 ao_data.format = AFMT_S16_LE;
|
|
578 ao_data.bps = channels * rate_hz * 2;
|
|
579 }
|
|
580
|
|
581 bytes_per_sample = ao_data.bps / ao_data.samplerate; //it should be here
|
|
582
|
|
583
|
|
584 if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams,
|
|
585 alsa_format)) < 0)
|
|
586 {
|
|
587 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set format: %s\n",
|
|
588 snd_strerror(err));
|
|
589 }
|
|
590
|
|
591 if ((err = snd_pcm_hw_params_set_channels(alsa_handler, alsa_hwparams,
|
|
592 ao_data.channels)) < 0)
|
|
593 {
|
|
594 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set channels: %s\n",
|
|
595 snd_strerror(err));
|
|
596 }
|
|
597
|
|
598 if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams,
|
|
599 &ao_data.samplerate, &dir)) < 0)
|
|
600 {
|
|
601 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set samplerate-2: %s\n",
|
|
602 snd_strerror(err));
|
|
603 return(0);
|
|
604 }
|
|
605
|
|
606 #ifdef BUFFERTIME
|
|
607 {
|
|
608 int alsa_buffer_time = 500000; /* original 60 */
|
|
609 int alsa_period_time;
|
|
610 alsa_period_time = alsa_buffer_time/4;
|
|
611 if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams,
|
|
612 &alsa_buffer_time, &dir)) < 0)
|
|
613 {
|
|
614 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set buffer time near: %s\n",
|
|
615 snd_strerror(err));
|
|
616 return(0);
|
|
617 } else
|
|
618 alsa_buffer_time = err;
|
|
619
|
|
620 if ((err = snd_pcm_hw_params_set_period_time_near(alsa_handler, alsa_hwparams,
|
|
621 &alsa_period_time, &dir)) < 0)
|
|
622 /* original: alsa_buffer_time/ao_data.bps */
|
|
623 {
|
|
624 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set period time: %s\n",
|
|
625 snd_strerror(err));
|
|
626 }
|
|
627 mp_msg(MSGT_AO,MSGL_INFO,"alsa-init: buffer_time: %d, period_time :%d\n",
|
|
628 alsa_buffer_time, err);
|
|
629 }
|
|
630 #endif//end SET_BUFFERTIME
|
|
631
|
|
632 #ifdef SET_CHUNKSIZE
|
|
633 {
|
|
634 //set chunksize
|
|
635 dir=0;
|
|
636 if ((err = snd_pcm_hw_params_set_period_size_near(alsa_handler, alsa_hwparams,
|
|
637 &chunk_size, &dir)) < 0)
|
|
638 {
|
|
639 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set periodsize(%d): %s\n",
|
|
640 chunk_size, snd_strerror(err));
|
|
641 }
|
|
642 else {
|
|
643 mp_msg(MSGT_AO,MSGL_V,"alsa-init: chunksize set to %i\n", chunk_size);
|
|
644 }
|
|
645 if ((err = snd_pcm_hw_params_set_periods_near(alsa_handler, alsa_hwparams,
|
|
646 &alsa_fragcount, &dir)) < 0) {
|
|
647 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set periods: %s\n",
|
|
648 snd_strerror(err));
|
|
649 }
|
|
650 else {
|
|
651 mp_msg(MSGT_AO,MSGL_V,"alsa-init: fragcount=%i\n", alsa_fragcount);
|
|
652 }
|
|
653 }
|
|
654 #endif//end SET_CHUNKSIZE
|
|
655
|
|
656 /* finally install hardware parameters */
|
|
657 if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0)
|
|
658 {
|
|
659 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set hw-parameters: %s\n",
|
|
660 snd_strerror(err));
|
|
661 }
|
|
662 // end setting hw-params
|
|
663
|
|
664
|
|
665 // gets buffersize for control
|
|
666 if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize)) < 0)
|
|
667 {
|
|
668 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to get buffersize: %s\n", snd_strerror(err));
|
|
669 }
|
|
670 else {
|
|
671 ao_data.buffersize = bufsize * bytes_per_sample;
|
|
672 mp_msg(MSGT_AO,MSGL_V,"alsa-init: got buffersize=%i\n", ao_data.buffersize);
|
|
673 }
|
|
674
|
|
675 // setting sw-params (only avail-min) if noblocking mode was choosed
|
|
676 if (ao_noblock)
|
|
677 {
|
|
678
|
|
679 if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0)
|
|
680 {
|
|
681 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to get parameters: %s\n",
|
|
682 snd_strerror(err));
|
|
683
|
|
684 }
|
|
685
|
|
686 //set min available frames to consider pcm ready (4)
|
|
687 //increased for nonblock-mode should be set dynamically later
|
|
688 if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, 4)) < 0)
|
|
689 {
|
|
690 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to set avail_min %s\n",
|
|
691 snd_strerror(err));
|
|
692 }
|
|
693
|
|
694 if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0)
|
|
695 {
|
|
696 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: unable to install sw-params\n");
|
|
697 }
|
|
698
|
|
699 bits_per_sample = snd_pcm_format_physical_width(alsa_format);
|
|
700 bits_per_frame = bits_per_sample * channels;
|
|
701 chunk_bytes = chunk_size * bits_per_frame / 8;
|
|
702
|
|
703 mp_msg(MSGT_AO,MSGL_V,"alsa-init: bits per sample (bps)=%i, bits per frame (bpf)=%i, chunk_bytes=%i\n",bits_per_sample,bits_per_frame,chunk_bytes);}
|
|
704 //end swparams
|
|
705
|
|
706 if ((err = snd_pcm_prepare(alsa_handler)) < 0)
|
|
707 {
|
|
708 mp_msg(MSGT_AO,MSGL_ERR,"alsa-init: pcm prepare error: %s\n", snd_strerror(err));
|
|
709 }
|
|
710
|
|
711 mp_msg(MSGT_AO,MSGL_INFO,"alsa: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n",
|
|
712 ao_data.samplerate, ao_data.channels, bytes_per_sample, ao_data.buffersize,
|
|
713 snd_pcm_format_description(alsa_format));
|
|
714
|
|
715 } // end switch alsa_handler (spdif)
|
|
716 alsa_can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams);
|
|
717 return(1);
|
|
718 } // end init
|
|
719
|
|
720
|
|
721 /* close audio device */
|
|
722 static void uninit(int immed)
|
|
723 {
|
|
724
|
|
725 if (alsa_handler) {
|
|
726 int err;
|
|
727
|
|
728 if (!ao_noblock) {
|
|
729 if ((err = snd_pcm_drop(alsa_handler)) < 0)
|
|
730 {
|
|
731 mp_msg(MSGT_AO,MSGL_ERR,"alsa-uninit: pcm drop error: %s\n", snd_strerror(err));
|
|
732 return;
|
|
733 }
|
|
734 }
|
|
735
|
|
736 if ((err = snd_pcm_close(alsa_handler)) < 0)
|
|
737 {
|
|
738 mp_msg(MSGT_AO,MSGL_ERR,"alsa-uninit: pcm close error: %s\n", snd_strerror(err));
|
|
739 return;
|
|
740 }
|
|
741 else {
|
|
742 alsa_handler = NULL;
|
|
743 mp_msg(MSGT_AO,MSGL_INFO,"alsa-uninit: pcm closed\n");
|
|
744 }
|
|
745 }
|
|
746 else {
|
|
747 mp_msg(MSGT_AO,MSGL_ERR,"alsa-uninit: no handler defined!\n");
|
|
748 }
|
|
749 }
|
|
750
|
|
751 static void audio_pause()
|
|
752 {
|
|
753 int err;
|
|
754
|
|
755 if (alsa_can_pause) {
|
|
756 if ((err = snd_pcm_pause(alsa_handler, 1)) < 0)
|
|
757 {
|
|
758 mp_msg(MSGT_AO,MSGL_ERR,"alsa-pause: pcm pause error: %s\n", snd_strerror(err));
|
|
759 return;
|
|
760 }
|
|
761 mp_msg(MSGT_AO,MSGL_V,"alsa-pause: pause supported by hardware\n");
|
|
762 } else {
|
|
763 if ((err = snd_pcm_drop(alsa_handler)) < 0)
|
|
764 {
|
|
765 mp_msg(MSGT_AO,MSGL_ERR,"alsa-pause: pcm drop error: %s\n", snd_strerror(err));
|
|
766 return;
|
|
767 }
|
|
768 }
|
|
769 }
|
|
770
|
|
771 static void audio_resume()
|
|
772 {
|
|
773 int err;
|
|
774
|
|
775 if (alsa_can_pause) {
|
|
776 if ((err = snd_pcm_pause(alsa_handler, 0)) < 0)
|
|
777 {
|
|
778 mp_msg(MSGT_AO,MSGL_ERR,"alsa-resume: pcm resume error: %s\n", snd_strerror(err));
|
|
779 return;
|
|
780 }
|
|
781 mp_msg(MSGT_AO,MSGL_V,"alsa-resume: resume supported by hardware\n");
|
|
782 } else {
|
|
783 if ((err = snd_pcm_prepare(alsa_handler)) < 0)
|
|
784 {
|
|
785 mp_msg(MSGT_AO,MSGL_ERR,"alsa-resume: pcm prepare error: %s\n", snd_strerror(err));
|
|
786 return;
|
|
787 }
|
|
788 }
|
|
789 }
|
|
790
|
|
791 /* stop playing and empty buffers (for seeking/pause) */
|
|
792 static void reset()
|
|
793 {
|
|
794 int err;
|
|
795
|
|
796 if ((err = snd_pcm_drop(alsa_handler)) < 0)
|
|
797 {
|
|
798 mp_msg(MSGT_AO,MSGL_ERR,"alsa-reset: pcm drop error: %s\n", snd_strerror(err));
|
|
799 return;
|
|
800 }
|
|
801 if ((err = snd_pcm_prepare(alsa_handler)) < 0)
|
|
802 {
|
|
803 mp_msg(MSGT_AO,MSGL_ERR,"alsa-reset: pcm prepare error: %s\n", snd_strerror(err));
|
|
804 return;
|
|
805 }
|
|
806 return;
|
|
807 }
|
|
808
|
|
809 #ifdef USE_POLL
|
|
810 static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
|
|
811 {
|
|
812 unsigned short revents;
|
|
813
|
|
814 while (1) {
|
|
815 poll(ufds, count, -1);
|
|
816 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
|
|
817 if (revents & POLLERR)
|
|
818 return -EIO;
|
|
819 if (revents & POLLOUT)
|
|
820 return 0;
|
|
821 }
|
|
822 }
|
|
823 #endif
|
|
824
|
|
825 #ifndef timersub
|
|
826 #define timersub(a, b, result) \
|
|
827 do { \
|
|
828 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
|
829 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
|
830 if ((result)->tv_usec < 0) { \
|
|
831 --(result)->tv_sec; \
|
|
832 (result)->tv_usec += 1000000; \
|
|
833 } \
|
|
834 } while (0)
|
|
835 #endif
|
|
836
|
|
837 /* I/O error handler */
|
|
838 static int xrun(u_char *str_mode)
|
|
839 {
|
|
840 int err;
|
|
841 snd_pcm_status_t *status;
|
|
842
|
|
843 snd_pcm_status_alloca(&status);
|
|
844
|
|
845 if ((err = snd_pcm_status(alsa_handler, status))<0) {
|
|
846 mp_msg(MSGT_AO,MSGL_ERR,"status error: %s", snd_strerror(err));
|
|
847 return(0);
|
|
848 }
|
|
849
|
|
850 if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
|
|
851 struct timeval now, diff, tstamp;
|
|
852 gettimeofday(&now, 0);
|
|
853 snd_pcm_status_get_trigger_tstamp(status, &tstamp);
|
|
854 timersub(&now, &tstamp, &diff);
|
|
855 mp_msg(MSGT_AO,MSGL_INFO,"alsa-%s: xrun of at least %.3f msecs. resetting stream\n",
|
|
856 str_mode,
|
|
857 diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
|
|
858 }
|
|
859
|
|
860 if ((err = snd_pcm_prepare(alsa_handler))<0) {
|
|
861 mp_msg(MSGT_AO,MSGL_ERR,"xrun: prepare error: %s", snd_strerror(err));
|
|
862 return(0);
|
|
863 }
|
|
864
|
|
865 return(1); /* ok, data should be accepted again */
|
|
866 }
|
|
867
|
|
868 static int play_normal(void* data, int len);
|
|
869 static int play_mmap(void* data, int len);
|
|
870
|
|
871 static int play(void* data, int len, int flags)
|
|
872 {
|
|
873 int result;
|
|
874 if (ao_mmap)
|
|
875 result = play_mmap(data, len);
|
|
876 else
|
|
877 result = play_normal(data, len);
|
|
878
|
|
879 return result;
|
|
880 }
|
|
881
|
|
882 /*
|
|
883 plays 'len' bytes of 'data'
|
|
884 returns: number of bytes played
|
|
885 modified last at 29.06.02 by jp
|
|
886 thanxs for marius <marius@rospot.com> for giving us the light ;)
|
|
887 */
|
|
888
|
|
889 static int play_normal(void* data, int len)
|
|
890 {
|
|
891
|
|
892 //bytes_per_sample is always 4 for 2 chn S16_LE
|
|
893 int num_frames = len / bytes_per_sample;
|
|
894 char *output_samples = (char *)data;
|
|
895 snd_pcm_sframes_t res = 0;
|
|
896
|
|
897 //mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: frames=%i, len=%i\n",num_frames,len);
|
|
898
|
|
899 if (!alsa_handler) {
|
|
900 mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: device configuration error");
|
|
901 return 0;
|
|
902 }
|
|
903
|
|
904 while (num_frames > 0) {
|
|
905
|
|
906 res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames);
|
|
907
|
|
908 if (res == -EAGAIN) {
|
|
909 snd_pcm_wait(alsa_handler, 1000);
|
|
910 }
|
|
911 else if (res == -EPIPE) { /* underrun */
|
|
912 if (xrun("play") <= 0) {
|
|
913 mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: xrun reset error");
|
|
914 return(0);
|
|
915 }
|
|
916 }
|
|
917 else if (res == -ESTRPIPE) { /* suspend */
|
|
918 mp_msg(MSGT_AO,MSGL_INFO,"alsa-play: pcm in suspend mode. trying to resume\n");
|
|
919 while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN)
|
|
920 sleep(1);
|
|
921 }
|
|
922 else if (res < 0) {
|
|
923 mp_msg(MSGT_AO,MSGL_INFO,"alsa-play: unknown status, trying to reset soundcard\n");
|
|
924 if ((res = snd_pcm_prepare(alsa_handler)) < 0) {
|
|
925 mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: snd prepare error");
|
|
926 return(0);
|
|
927 break;
|
|
928 }
|
|
929 }
|
|
930
|
|
931 if (res > 0) {
|
|
932
|
|
933 /* output_samples += ao_data.channels * res; */
|
|
934 output_samples += res * bytes_per_sample;
|
|
935
|
|
936 num_frames -= res;
|
|
937 }
|
|
938
|
|
939 } //end while
|
|
940
|
|
941 if (res < 0) {
|
|
942 mp_msg(MSGT_AO,MSGL_ERR,"alsa-play: write error %s", snd_strerror(res));
|
|
943 return 0;
|
|
944 }
|
|
945 return res < 0 ? (int)res : len - len % bytes_per_sample;
|
|
946 }
|
|
947
|
|
948 /* mmap-mode mainly based on descriptions by Joshua Haberman <joshua@haberman.com>
|
|
949 * 'An overview of the ALSA API' http://people.debian.org/~joshua/x66.html
|
|
950 * and some help by Paul Davis <pbd@op.net> */
|
|
951
|
|
952 static int play_mmap(void* data, int len)
|
|
953 {
|
|
954 snd_pcm_sframes_t commitres, frames_available;
|
|
955 snd_pcm_uframes_t frames_transmit, size, offset;
|
|
956 const snd_pcm_channel_area_t *area;
|
|
957 void *outbuffer;
|
|
958 int err, result;
|
|
959
|
|
960 #ifdef USE_POLL //seems not really be needed
|
|
961 struct pollfd *ufds;
|
|
962 int count;
|
|
963
|
|
964 count = snd_pcm_poll_descriptors_count (alsa_handler);
|
|
965 ufds = malloc(sizeof(struct pollfd) * count);
|
|
966 snd_pcm_poll_descriptors(alsa_handler, ufds, count);
|
|
967
|
|
968 //first wait_for_poll
|
|
969 if (err = (wait_for_poll(alsa_handler, ufds, count) < 0)) {
|
|
970 if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN ||
|
|
971 snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) {
|
|
972 xrun("play");
|
|
973 }
|
|
974 }
|
|
975 #endif
|
|
976
|
|
977 outbuffer = alloca(ao_data.buffersize);
|
|
978
|
|
979 //don't trust get_space() ;)
|
|
980 frames_available = snd_pcm_avail_update(alsa_handler) * bytes_per_sample;
|
|
981 if (frames_available < 0)
|
|
982 xrun("play");
|
|
983
|
|
984 if (frames_available < 4) {
|
|
985 if (first) {
|
|
986 first = 0;
|
|
987 snd_pcm_start(alsa_handler);
|
|
988 }
|
|
989 else { //FIXME should break and return 0?
|
|
990 snd_pcm_wait(alsa_handler, -1);
|
|
991 first = 1;
|
|
992 }
|
|
993 }
|
|
994
|
|
995 /* len is simply the available bufferspace got by get_space()
|
|
996 * but real avail_buffer in frames is ab/bytes_per_sample */
|
|
997 size = len / bytes_per_sample;
|
|
998
|
|
999 //mp_msg(MSGT_AO,MSGL_V,"len: %i size %i, f_avail %i, bps %i ...\n", len, size, frames_available, bytes_per_sample);
|
|
1000
|
|
1001 frames_transmit = size;
|
|
1002
|
|
1003 /* prepare areas and set sw-pointers
|
|
1004 * frames_transmit returns the real available buffer-size
|
|
1005 * sometimes != frames_available cause of ringbuffer 'emulation' */
|
|
1006 snd_pcm_mmap_begin(alsa_handler, &area, &offset, &frames_transmit);
|
|
1007
|
|
1008 /* this is specific to interleaved streams (or non-interleaved
|
|
1009 * streams with only one channel) */
|
|
1010 outbuffer = ((char *) area->addr + (area->first + area->step * offset) / 8); //8
|
|
1011
|
|
1012 //write data
|
|
1013 memcpy(outbuffer, data, (frames_transmit * bytes_per_sample));
|
|
1014
|
|
1015 commitres = snd_pcm_mmap_commit(alsa_handler, offset, frames_transmit);
|
|
1016
|
|
1017 if (commitres < 0 || commitres != frames_transmit) {
|
|
1018 if (snd_pcm_state(alsa_handler) == SND_PCM_STATE_XRUN ||
|
|
1019 snd_pcm_state(alsa_handler) == SND_PCM_STATE_SUSPENDED) {
|
|
1020 xrun("play");
|
|
1021 }
|
|
1022 }
|
|
1023
|
|
1024 //mp_msg(MSGT_AO,MSGL_V,"mmap ft: %i, cres: %i\n", frames_transmit, commitres);
|
|
1025
|
|
1026 /* err = snd_pcm_area_copy(&area, offset, &data, offset, len, alsa_format); */
|
|
1027 /* if (err < 0) { */
|
|
1028 /* mp_msg(MSGT_AO,MSGL_ERR,"area-copy-error\n"); */
|
|
1029 /* return 0; */
|
|
1030 /* } */
|
|
1031
|
|
1032
|
|
1033 //calculate written frames!
|
|
1034 result = commitres * bytes_per_sample;
|
|
1035
|
|
1036
|
|
1037 /* if (verbose) { */
|
|
1038 /* if (len == result) */
|
|
1039 /* mp_msg(MSGT_AO,MSGL_V,"result: %i, frames written: %i ...\n", result, frames_transmit); */
|
|
1040 /* else */
|
|
1041 /* mp_msg(MSGT_AO,MSGL_V,"result: %i, frames written: %i, result != len ...\n", result, frames_transmit); */
|
|
1042 /* } */
|
|
1043
|
|
1044 //mplayer doesn't like -result
|
|
1045 if (result < 0)
|
|
1046 result = 0;
|
|
1047
|
|
1048 #ifdef USE_POLL
|
|
1049 free(ufds);
|
|
1050 #endif
|
|
1051
|
|
1052 return result;
|
|
1053 }
|
|
1054
|
|
1055 /* how many byes are free in the buffer */
|
|
1056 static int get_space()
|
|
1057 {
|
|
1058 snd_pcm_status_t *status;
|
|
1059 int ret;
|
|
1060 char *str_status;
|
|
1061
|
|
1062 //snd_pcm_sframes_t avail_frames = 0;
|
|
1063
|
12747
|
1064 snd_pcm_status_alloca(&status);
|
12465
|
1065
|
|
1066 if ((ret = snd_pcm_status(alsa_handler, status)) < 0)
|
|
1067 {
|
|
1068 mp_msg(MSGT_AO,MSGL_ERR,"alsa-space: cannot get pcm status: %s\n", snd_strerror(ret));
|
|
1069 return(0);
|
|
1070 }
|
|
1071
|
|
1072 switch(snd_pcm_status_get_state(status))
|
|
1073 {
|
|
1074 case SND_PCM_STATE_OPEN:
|
|
1075 str_status = "open";
|
|
1076 case SND_PCM_STATE_PREPARED:
|
|
1077 if (str_status != "open") {
|
|
1078 str_status = "prepared";
|
|
1079 first = 1;
|
|
1080 ret = snd_pcm_status_get_avail(status) * bytes_per_sample;
|
|
1081 if (ret == 0) //ugly workaround for hang in mmap-mode
|
|
1082 ret = 10;
|
|
1083 break;
|
|
1084 }
|
|
1085 case SND_PCM_STATE_RUNNING:
|
|
1086 ret = snd_pcm_status_get_avail(status) * bytes_per_sample;
|
|
1087 //avail_frames = snd_pcm_avail_update(alsa_handler) * bytes_per_sample;
|
|
1088 if (str_status != "open" && str_status != "prepared")
|
|
1089 str_status = "running";
|
|
1090 break;
|
|
1091 case SND_PCM_STATE_PAUSED:
|
|
1092 mp_msg(MSGT_AO,MSGL_V,"alsa-space: paused");
|
|
1093 str_status = "paused";
|
|
1094 ret = 0;
|
|
1095 break;
|
|
1096 case SND_PCM_STATE_XRUN:
|
|
1097 xrun("space");
|
|
1098 str_status = "xrun";
|
|
1099 first = 1;
|
|
1100 ret = 0;
|
|
1101 break;
|
|
1102 default:
|
|
1103 str_status = "undefined";
|
|
1104 ret = snd_pcm_status_get_avail(status) * bytes_per_sample;
|
|
1105 if (ret <= 0) {
|
|
1106 xrun("space");
|
|
1107 }
|
|
1108 }
|
|
1109
|
|
1110 if (str_status != "running")
|
|
1111 mp_msg(MSGT_AO,MSGL_V,"alsa-space: free space = %i, status=%i, %s --\n", ret, status, str_status);
|
|
1112
|
|
1113 if (ret < 0) {
|
|
1114 mp_msg(MSGT_AO,MSGL_ERR,"negative value!!\n");
|
|
1115 ret = 0;
|
|
1116 }
|
|
1117
|
|
1118 // workaround for too small value returned
|
|
1119 if (ret < MIN_CHUNK_SIZE)
|
|
1120 ret = 0;
|
|
1121
|
|
1122 return(ret);
|
|
1123 }
|
|
1124
|
|
1125 /* delay in seconds between first and last sample in buffer */
|
|
1126 static float get_delay()
|
|
1127 {
|
|
1128
|
|
1129 if (alsa_handler) {
|
|
1130
|
|
1131 snd_pcm_status_t *status;
|
|
1132 float ret;
|
|
1133
|
12747
|
1134 snd_pcm_status_alloca(&status);
|
12465
|
1135
|
|
1136 if ((ret = snd_pcm_status(alsa_handler, status)) < 0)
|
|
1137 {
|
|
1138 mp_msg(MSGT_AO,MSGL_ERR,"alsa-delay: cannot get pcm status: %s\n", snd_strerror(ret));
|
|
1139 }
|
|
1140
|
|
1141 switch(snd_pcm_status_get_state(status))
|
|
1142 {
|
|
1143 case SND_PCM_STATE_OPEN:
|
|
1144 case SND_PCM_STATE_PREPARED:
|
|
1145 case SND_PCM_STATE_RUNNING:
|
|
1146 ret = (float)snd_pcm_status_get_delay(status)/(float)ao_data.samplerate;
|
|
1147 break;
|
|
1148 default:
|
|
1149 ret = 0;
|
|
1150 }
|
|
1151
|
|
1152
|
|
1153 if (ret < 0)
|
|
1154 ret = 0;
|
|
1155 return(ret);
|
|
1156
|
|
1157 } else {
|
|
1158 return(0);
|
|
1159 }
|
|
1160 }
|