996
|
1 /*
|
|
2 ao_alsa5 - ALSA 5.x output plugin for MPlayer
|
|
3
|
|
4 (C) Alex Beregszaszi <alex@naxine.org>
|
|
5
|
|
6 Thanks to Arpi for helping me ;)
|
|
7 */
|
|
8
|
|
9 #include <errno.h>
|
|
10 #include <sys/soundcard.h> /* AFMT_* */
|
|
11 #include <sys/asoundlib.h>
|
|
12
|
|
13 #include "../config.h"
|
|
14
|
|
15 #include "audio_out.h"
|
|
16 #include "audio_out_internal.h"
|
|
17
|
|
18 extern int verbose;
|
|
19
|
|
20 static ao_info_t info =
|
|
21 {
|
|
22 "ALSA-0.5.x audio output",
|
|
23 "alsa5",
|
|
24 "Alex Beregszaszi <alex@naxine.org>",
|
|
25 ""
|
|
26 };
|
|
27
|
|
28 LIBAO_EXTERN(alsa5)
|
|
29
|
|
30 /* global variables:
|
|
31 ao_samplerate
|
|
32 ao_channels
|
|
33 ao_format
|
|
34 ao_bps
|
|
35 ao_outburst
|
|
36 ao_buffersize
|
|
37 */
|
|
38
|
|
39 static snd_pcm_t *alsa_handler;
|
|
40 static snd_pcm_format_t alsa_format;
|
|
41 static int alsa_rate = SND_PCM_RATE_CONTINUOUS;
|
|
42
|
|
43 /* to set/get/query special features/parameters */
|
|
44 static int control(int cmd, int arg)
|
|
45 {
|
|
46 return(CONTROL_UNKNOWN);
|
|
47 }
|
|
48
|
|
49 /*
|
|
50 open & setup audio device
|
|
51 return: 1=success 0=fail
|
|
52 */
|
|
53 static int init(int rate_hz, int channels, int format, int flags)
|
|
54 {
|
|
55 int err;
|
|
56 int cards = -1;
|
|
57 snd_pcm_channel_params_t params;
|
|
58 snd_pcm_channel_setup_t setup;
|
|
59 snd_pcm_info_t info;
|
|
60 snd_pcm_channel_info_t chninfo;
|
|
61
|
|
62 printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz,
|
|
63 channels, audio_out_format_name(format));
|
|
64
|
|
65 alsa_handler = NULL;
|
|
66
|
|
67 if (verbose)
|
|
68 printf("alsa-init: compiled for ALSA-%s (%d)\n", SND_LIB_VERSION_STR,
|
|
69 SND_LIB_VERSION);
|
|
70
|
|
71 if ((cards = snd_cards()) < 0)
|
|
72 {
|
|
73 printf("alsa-init: no soundcards found\n");
|
|
74 return(0);
|
|
75 }
|
|
76
|
|
77 ao_format = format;
|
|
78 ao_channels = channels - 1;
|
|
79 ao_samplerate = rate_hz;
|
|
80 ao_bps = ao_samplerate*(ao_channels+1);
|
|
81 ao_outburst = OUTBURST;
|
|
82 ao_buffersize = 16384;
|
|
83
|
|
84 memset(&alsa_format, 0, sizeof(alsa_format));
|
|
85 switch (format)
|
|
86 {
|
|
87 case AFMT_S8:
|
|
88 alsa_format.format = SND_PCM_SFMT_S8;
|
|
89 break;
|
|
90 case AFMT_U8:
|
|
91 alsa_format.format = SND_PCM_SFMT_U8;
|
|
92 break;
|
|
93 case AFMT_U16_LE:
|
|
94 alsa_format.format = SND_PCM_SFMT_U16_LE;
|
|
95 break;
|
|
96 case AFMT_U16_BE:
|
|
97 alsa_format.format = SND_PCM_SFMT_U16_BE;
|
|
98 break;
|
|
99 case AFMT_S16_LE:
|
|
100 alsa_format.format = SND_PCM_SFMT_S16_LE;
|
|
101 break;
|
|
102 case AFMT_S16_BE:
|
|
103 alsa_format.format = SND_PCM_SFMT_S16_BE;
|
|
104 break;
|
|
105 default:
|
|
106 alsa_format.format = SND_PCM_SFMT_MPEG;
|
|
107 break;
|
|
108 }
|
|
109
|
|
110 switch(alsa_format.format)
|
|
111 {
|
|
112 case SND_PCM_SFMT_S16_LE:
|
|
113 case SND_PCM_SFMT_U16_LE:
|
|
114 ao_bps *= 2;
|
|
115 break;
|
|
116 case -1:
|
|
117 printf("alsa-init: invalid format (%s) requested - output disabled\n",
|
|
118 audio_out_format_name(format));
|
|
119 return(0);
|
|
120 }
|
|
121
|
|
122 switch(rate_hz)
|
|
123 {
|
|
124 case 8000:
|
|
125 alsa_rate = SND_PCM_RATE_8000;
|
|
126 break;
|
|
127 case 11025:
|
|
128 alsa_rate = SND_PCM_RATE_11025;
|
|
129 break;
|
|
130 case 16000:
|
|
131 alsa_rate = SND_PCM_RATE_16000;
|
|
132 break;
|
|
133 case 22050:
|
|
134 alsa_rate = SND_PCM_RATE_22050;
|
|
135 break;
|
|
136 case 32000:
|
|
137 alsa_rate = SND_PCM_RATE_32000;
|
|
138 break;
|
|
139 case 44100:
|
|
140 alsa_rate = SND_PCM_RATE_44100;
|
|
141 break;
|
|
142 case 48000:
|
|
143 alsa_rate = SND_PCM_RATE_48000;
|
|
144 break;
|
|
145 case 88200:
|
|
146 alsa_rate = SND_PCM_RATE_88200;
|
|
147 break;
|
|
148 case 96000:
|
|
149 alsa_rate = SND_PCM_RATE_96000;
|
|
150 break;
|
|
151 case 176400:
|
|
152 alsa_rate = SND_PCM_RATE_176400;
|
|
153 break;
|
|
154 case 192000:
|
|
155 alsa_rate = SND_PCM_RATE_192000;
|
|
156 break;
|
|
157 default:
|
|
158 alsa_rate = SND_PCM_RATE_CONTINUOUS;
|
|
159 break;
|
|
160 }
|
|
161
|
|
162 alsa_format.rate = ao_samplerate;
|
|
163 alsa_format.voices = ao_channels*2;
|
|
164 alsa_format.interleave = 1;
|
|
165
|
|
166 if ((err = snd_pcm_open(&alsa_handler, 0, 0, SND_PCM_OPEN_PLAYBACK)) < 0)
|
|
167 {
|
|
168 printf("alsa-init: playback open error: %s\n", snd_strerror(err));
|
|
169 return(0);
|
|
170 }
|
|
171
|
|
172 if ((err = snd_pcm_info(alsa_handler, &info)) < 0)
|
|
173 {
|
|
174 printf("alsa-init: pcm info error: %s\n", snd_strerror(err));
|
|
175 return(0);
|
|
176 }
|
|
177
|
|
178 printf("alsa-init: %d soundcard%s found, using: %s\n", cards,
|
|
179 (cards == 1) ? "" : "s", info.name);
|
|
180
|
|
181 if (info.flags & SND_PCM_INFO_PLAYBACK)
|
|
182 {
|
|
183 bzero(&chninfo, sizeof(chninfo));
|
|
184 chninfo.channel = SND_PCM_CHANNEL_PLAYBACK;
|
|
185 if ((err = snd_pcm_channel_info(alsa_handler, &chninfo)) < 0)
|
|
186 {
|
|
187 printf("alsa-init: pcm channel info error: %s\n", snd_strerror(err));
|
|
188 return(0);
|
|
189 }
|
|
190 if (chninfo.buffer_size)
|
|
191 ao_buffersize = chninfo.buffer_size;
|
|
192 if (verbose)
|
|
193 printf("alsa-init: setting preferred buffer size from driver: %d bytes\n",
|
|
194 ao_buffersize);
|
|
195 }
|
|
196
|
|
197 memset(¶ms, 0, sizeof(params));
|
|
198 params.channel = SND_PCM_CHANNEL_PLAYBACK;
|
|
199 params.mode = SND_PCM_MODE_STREAM;
|
|
200 params.format = alsa_format;
|
|
201 params.start_mode = SND_PCM_START_DATA;
|
|
202 params.stop_mode = SND_PCM_STOP_ROLLOVER;
|
|
203 params.buf.stream.queue_size = ao_buffersize;
|
|
204 params.buf.stream.fill = SND_PCM_FILL_NONE;
|
|
205
|
|
206 if ((err = snd_pcm_channel_params(alsa_handler, ¶ms)) < 0)
|
|
207 {
|
|
208 printf("alsa-init: error setting parameters: %s\n", snd_strerror(err));
|
|
209 return(0);
|
|
210 }
|
|
211
|
|
212 memset(&setup, 0, sizeof(setup));
|
|
213 setup.channel = SND_PCM_CHANNEL_PLAYBACK;
|
|
214 setup.mode = SND_PCM_MODE_STREAM;
|
|
215 setup.format = alsa_format;
|
|
216 setup.buf.stream.queue_size = ao_buffersize;
|
|
217 setup.msbits_per_sample = ao_bps;
|
|
218
|
|
219 if ((err = snd_pcm_channel_setup(alsa_handler, &setup)) < 0)
|
|
220 {
|
|
221 printf("alsa-init: error setting up channel: %s\n", snd_strerror(err));
|
|
222 return(0);
|
|
223 }
|
|
224
|
|
225 if ((err = snd_pcm_channel_prepare(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
|
|
226 {
|
|
227 printf("alsa-init: channel prepare error: %s\n", snd_strerror(err));
|
|
228 return(0);
|
|
229 }
|
|
230
|
|
231 printf("AUDIO: %d Hz/%d channels/%d bps/%d bytes buffer/%s\n",
|
|
232 ao_samplerate, ao_channels+1, ao_bps, ao_buffersize,
|
|
233 snd_pcm_get_format_name(alsa_format.format));
|
|
234 return(1);
|
|
235 }
|
|
236
|
|
237 /* close audio device */
|
|
238 static void uninit()
|
|
239 {
|
|
240 reset();
|
|
241 snd_pcm_close(alsa_handler);
|
|
242 }
|
|
243
|
|
244 /* stop playing and empty buffers (for seeking/pause) */
|
|
245 static void reset()
|
|
246 {
|
|
247 int err;
|
|
248
|
|
249 if ((err = snd_pcm_playback_drain(alsa_handler)) < 0)
|
|
250 {
|
|
251 printf("alsa-reset: playback drain error: %s\n", snd_strerror(err));
|
|
252 return;
|
|
253 }
|
|
254
|
|
255 if ((err = snd_pcm_channel_flush(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
|
|
256 {
|
|
257 printf("alsa-reset: playback flush error: %s\n", snd_strerror(err));
|
|
258 return;
|
|
259 }
|
|
260
|
|
261 if ((err = snd_pcm_channel_prepare(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
|
|
262 {
|
|
263 printf("alsa-reset: channel prepare error: %s\n", snd_strerror(err));
|
|
264 return;
|
|
265 }
|
|
266 }
|
|
267
|
|
268 /*
|
|
269 plays 'len' bytes of 'data'
|
|
270 returns: number of bytes played
|
|
271 */
|
|
272 static int play(void* data, int len, int flags)
|
|
273 {
|
|
274 if ((len = snd_pcm_write(alsa_handler, data, len)) != len)
|
|
275 {
|
|
276 if (len == -EPIPE) /* underrun? */
|
|
277 {
|
|
278 printf("alsa-play: alsa underrun, resetting stream\n");
|
|
279 if ((len = snd_pcm_channel_prepare(alsa_handler, SND_PCM_CHANNEL_PLAYBACK)) < 0)
|
|
280 {
|
|
281 printf("alsa-play: playback prepare error: %s\n", snd_strerror(len));
|
|
282 return(0);
|
|
283 }
|
|
284 if ((len = snd_pcm_write(alsa_handler, data, len)) != len)
|
|
285 {
|
|
286 printf("alsa-play: write error after reset: %s - giving up\n",
|
|
287 snd_strerror(len));
|
|
288 return(0);
|
|
289 }
|
|
290 return(len); /* 2nd write was ok */
|
|
291 }
|
|
292 printf("alsa-play: output error: %s\n", snd_strerror(len));
|
|
293 }
|
|
294 return(len);
|
|
295 }
|
|
296
|
|
297 /* how many byes are free in the buffer */
|
|
298 static int get_space()
|
|
299 {
|
|
300 snd_pcm_channel_status_t ch_stat;
|
|
301
|
|
302 ch_stat.channel = SND_PCM_CHANNEL_PLAYBACK;
|
|
303
|
|
304 if (snd_pcm_channel_status(alsa_handler, &ch_stat) < 0)
|
|
305 return(0);
|
|
306 else
|
|
307 return(ch_stat.free);
|
|
308 }
|
|
309
|
|
310 /* how many unplayed bytes are in the buffer */
|
|
311 static int get_delay()
|
|
312 {
|
|
313 snd_pcm_channel_status_t ch_stat;
|
|
314
|
|
315 ch_stat.channel = SND_PCM_CHANNEL_PLAYBACK;
|
|
316
|
|
317 if (snd_pcm_channel_status(alsa_handler, &ch_stat) < 0)
|
|
318 return(0);
|
|
319 else
|
|
320 return(ch_stat.count);
|
|
321 }
|