comparison src/alsa-ng/alsa-core.c @ 3162:e387614b9be9

alsa-ng: Import rewritten ALSA plugin. This is still woefully incomplete, but supports basic playback. This driver uses the "safe" ALSA API subset, including use of blocking I/O. Right now, it is hardcoded to use "default". Do not complain about bugs in this plugin.
author William Pitcock <nenolod@atheme.org>
date Thu, 14 May 2009 21:05:11 -0500
parents
children 26a2c237ef53
comparison
equal deleted inserted replaced
3161:6dd886b5c72b 3162:e387614b9be9
1 /*
2 * Audacious ALSA Plugin (-ng)
3 * Copyright (c) 2009 William Pitcock <nenolod@dereferenced.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #define ALSA_DEBUG
21 #include "alsa-stdinc.h"
22
23 static snd_pcm_t *pcm_handle = NULL;
24 static alsaplug_ringbuf_t pcm_ringbuf;
25 static gboolean pcm_going = FALSE;
26 static GThread *audio_thread = NULL;
27 static gint bps;
28 static GMutex *pcm_mutex;
29 static GCond *pcm_cond;
30
31 static gsize wr_total = 0;
32 static gsize wr_hwframes = 0;
33
34 static gint flush_request;
35
36 static void
37 alsaplug_write_buffer(gpointer data, gint length)
38 {
39 snd_pcm_sframes_t wr_frames;
40
41 while (length > 0)
42 {
43 gint frames = snd_pcm_bytes_to_frames(pcm_handle, length);
44 wr_frames = snd_pcm_writei(pcm_handle, data, frames);
45
46 if (wr_frames > 0)
47 {
48 gint written = snd_pcm_frames_to_bytes(pcm_handle, wr_frames);
49 length -= written;
50 data += written;
51 }
52 else
53 {
54 gint err = snd_pcm_recover(pcm_handle, wr_frames, 1);
55 if (err < 0)
56 _ERROR("(write) snd_pcm_recover: %s", snd_strerror(err));
57
58 return;
59 }
60 }
61 }
62
63 static gpointer
64 alsaplug_loop(gpointer unused)
65 {
66 gchar buf[2048];
67
68 while (pcm_going)
69 {
70 if (flush_request != -1)
71 {
72 snd_pcm_drop(pcm_handle);
73 snd_pcm_prepare(pcm_handle);
74 wr_total = flush_request * (bps / 1000);
75 flush_request = -1;
76 }
77
78 if (alsaplug_ringbuffer_read(&pcm_ringbuf, buf, 2048) == -1)
79 {
80 GTimeVal pcm_abs_time;
81
82 g_get_current_time(&pcm_abs_time);
83 g_time_val_add(&pcm_abs_time, 10000);
84
85 g_mutex_lock(pcm_mutex);
86 g_cond_timed_wait(pcm_cond, pcm_mutex, &pcm_abs_time);
87 g_mutex_unlock(pcm_mutex);
88
89 continue;
90 }
91
92 alsaplug_write_buffer(buf, 2048);
93 }
94
95 snd_pcm_close(pcm_handle);
96 pcm_handle = NULL;
97
98 return NULL;
99 }
100
101 /********************************************************************************
102 * Output Plugin API implementation. *
103 ********************************************************************************/
104
105 static OutputPluginInitStatus
106 alsaplug_init(void)
107 {
108 gint card = -1;
109
110 pcm_mutex = g_mutex_new();
111 pcm_cond = g_cond_new();
112
113 if (snd_card_next(&card) != 0)
114 return OUTPUT_PLUGIN_INIT_NO_DEVICES;
115
116 return OUTPUT_PLUGIN_INIT_FOUND_DEVICES;
117 }
118
119 static gint
120 alsaplug_open_audio(AFormat fmt, gint rate, gint nch)
121 {
122 gint err, bitwidth, ringbuf_size;
123 snd_pcm_format_t afmt;
124 snd_pcm_hw_params_t *hwparams = NULL;
125
126 afmt = alsaplug_format_convert(fmt);
127
128 if ((err = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
129 {
130 _ERROR("snd_pcm_open: %s", snd_strerror(err));
131 pcm_handle = NULL;
132 return -1;
133 }
134
135 snd_pcm_hw_params_alloca(&hwparams);
136 snd_pcm_hw_params_any(pcm_handle, hwparams);
137 snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
138 snd_pcm_hw_params_set_format(pcm_handle, hwparams, afmt);
139 snd_pcm_hw_params_set_channels(pcm_handle, hwparams, nch);
140 snd_pcm_hw_params_set_rate(pcm_handle, hwparams, rate, 0);
141
142 err = snd_pcm_hw_params(pcm_handle, hwparams);
143 if (err < 0)
144 {
145 _ERROR("snd_pcm_hw_params failed: %s", snd_strerror(err));
146 return -1;
147 }
148
149 bitwidth = snd_pcm_format_physical_width(afmt);
150 bps = (rate * bitwidth * nch) >> 3;
151 ringbuf_size = aud_cfg->output_buffer_size * bps / 1000;
152 alsaplug_ringbuffer_init(&pcm_ringbuf, ringbuf_size);
153 pcm_going = TRUE;
154 flush_request = -1;
155
156 audio_thread = g_thread_create(alsaplug_loop, NULL, TRUE, NULL);
157 return 1;
158 }
159
160 static void
161 alsaplug_close_audio(void)
162 {
163 pcm_going = FALSE;
164
165 g_thread_join(audio_thread);
166
167 wr_total = 0;
168 wr_hwframes = 0;
169 bps = 0;
170 }
171
172 static void
173 alsaplug_write_audio(gpointer data, gint length)
174 {
175 wr_total += length;
176 alsaplug_ringbuffer_write(&pcm_ringbuf, data, length);
177 }
178
179 static gint
180 alsaplug_output_time(void)
181 {
182 snd_pcm_sframes_t delay;
183 gsize bytes = wr_total;
184
185 if (pcm_going && pcm_handle != NULL)
186 {
187 if (!snd_pcm_delay(pcm_handle, &delay))
188 {
189 guint d = snd_pcm_frames_to_bytes(pcm_handle, delay);
190 if (bytes < d)
191 bytes = 0;
192 else
193 bytes -= d;
194 }
195
196 return (bytes * 1000) / bps;
197 }
198
199 return 0;
200 }
201
202 static gint
203 alsaplug_written_time(void)
204 {
205 if (pcm_going)
206 return (wr_total * 1000) / bps;
207
208 return 0;
209 }
210
211 static gint
212 alsaplug_buffer_free(void)
213 {
214 return alsaplug_ringbuffer_free(&pcm_ringbuf);
215 }
216
217 static void
218 alsaplug_flush(gint time)
219 {
220 flush_request = time;
221 while (flush_request != -1 && pcm_going)
222 g_usleep(10000);
223 }
224
225 static gint
226 alsaplug_buffer_playing(void)
227 {
228 return pcm_going;
229 }
230
231 /********************************************************************************
232 * Plugin glue. *
233 ********************************************************************************/
234
235 static OutputPlugin alsa_op = {
236 .description = "ALSA Output Plugin (-ng)",
237 .probe_priority = 1,
238 .init = alsaplug_init,
239 .open_audio = alsaplug_open_audio,
240 .close_audio = alsaplug_close_audio,
241 .write_audio = alsaplug_write_audio,
242 .output_time = alsaplug_output_time,
243 .written_time = alsaplug_written_time,
244 .buffer_free = alsaplug_buffer_free,
245 .buffer_playing = alsaplug_buffer_playing,
246 .flush = alsaplug_flush,
247 };
248
249 OutputPlugin *alsa_oplist[] = { &alsa_op, NULL };
250 SIMPLE_OUTPUT_PLUGIN(alsa, alsa_oplist);