Mercurial > audlegacy-plugins
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); |