missing commit in generator.h
[galan.git] / plugins / libalsa_out_callback.c
blob6228c6032e645d9fe8bc6e2f07013b936a307477
1 /* gAlan - Graphical Audio Language
2 * Copyright (C) 1999 Tony Garnock-Jones
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdio.h>
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <sys/soundcard.h>
26 #include <fcntl.h>
27 #include <unistd.h>
29 #include <gdk/gdk.h>
30 #include <gtk/gtk.h>
32 #include "global.h"
33 #include "generator.h"
34 #include "comp.h"
35 #include "control.h"
36 #include "gencomp.h"
37 #include "msgbox.h"
38 #include "prefs.h"
40 #define ALSA_PCM_OLD_HW_PARAMS_API
41 #define ALSA_PCM_OLD_SW_PARAMS_API
42 #include "alsa/asoundlib.h"
44 #define SIG_LEFT_CHANNEL 0
45 #define SIG_RIGHT_CHANNEL 1
47 #define DEFAULT_FRAGMENT_EXPONENT 12
49 typedef signed short OUTPUTSAMPLE;
51 typedef struct ALSAData {
52 snd_pcm_t *handle;
53 int count;
54 struct pollfd *ufds;
55 gint input_tag;
56 snd_async_handler_t *async_handler;
57 AClock *clock;
58 } ALSAData;
60 PRIVATE int instance_count = 0;
61 PRIVATE char device[256];
63 snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
64 int rate = SAMPLE_RATE; /* stream rate */
65 int channels = 2; /* count of channels */
66 int buffer_time = 1000000/SAMPLE_RATE*4096; /* ring buffer length in us */
67 int period_time = 1000000*1024/SAMPLE_RATE; /* period time in us */
69 snd_pcm_sframes_t buffer_size;
70 snd_pcm_sframes_t period_size;
71 snd_output_t *output = NULL;
74 * Underrun and suspend recovery
77 static int xrun_recovery(snd_pcm_t *handle, int err)
79 g_print( "xrun !!!....\n" );
80 if (err == -EPIPE) { /* under-run */
81 err = snd_pcm_prepare(handle);
82 if (err < 0)
83 printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
84 return 0;
85 } else if (err == -ESTRPIPE) {
86 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
87 sleep(1); /* wait until the suspend flag is released */
88 if (err < 0) {
89 err = snd_pcm_prepare(handle);
90 if (err < 0)
91 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
93 return 0;
95 return err;
98 PRIVATE void audio_play_fragment(snd_pcm_t *handle, SAMPLE *left, SAMPLE *right, int length) {
99 OUTPUTSAMPLE *outbuf;
100 int buflen = length * sizeof(OUTPUTSAMPLE) * 2;
101 int i,err;
103 if (length <= 0)
104 return;
106 outbuf = malloc(buflen);
107 RETURN_UNLESS(outbuf != NULL);
109 for (i = 0; i < length; i++) {
110 outbuf[i<<1] = (OUTPUTSAMPLE) MIN(MAX(left[i] * 32767, -32768), 32767);
111 outbuf[(i<<1) + 1] = (OUTPUTSAMPLE) MIN(MAX(right[i] * 32767, -32768), 32767);
114 g_print( "writing %d frames\n", length );
116 err = snd_pcm_writei(handle, outbuf, length);
117 if( err < 0 ) {
118 if (xrun_recovery(handle, err) < 0) {
119 printf("Write error: %s\n", snd_strerror(err));
120 exit(EXIT_FAILURE);
124 //g_print( "len=%d, err=%d state=%d\n", length, err, snd_pcm_state(handle) );
125 free(outbuf);
128 static int set_hwparams(snd_pcm_t *handle,
129 snd_pcm_hw_params_t *params,
130 snd_pcm_access_t access)
132 int err, dir;
134 /* choose all parameters */
135 err = snd_pcm_hw_params_any(handle, params);
136 if (err < 0) {
137 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
138 return err;
140 /* set the interleaved read/write format */
141 err = snd_pcm_hw_params_set_access(handle, params, access);
142 if (err < 0) {
143 printf("Access type not available for playback: %s\n", snd_strerror(err));
144 return err;
146 /* set the sample format */
147 err = snd_pcm_hw_params_set_format(handle, params, format);
148 if (err < 0) {
149 printf("Sample format not available for playback: %s\n", snd_strerror(err));
150 return err;
152 /* set the count of channels */
153 err = snd_pcm_hw_params_set_channels(handle, params, channels);
154 if (err < 0) {
155 printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
156 return err;
158 /* set the stream rate */
159 err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
160 if (err < 0) {
161 printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
162 return err;
164 if (err != rate) {
165 printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
166 return -EINVAL;
168 /* set the buffer time */
169 err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir);
170 if (err < 0) {
171 printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
172 return err;
174 buffer_size = snd_pcm_hw_params_get_buffer_size(params);
175 /* set the period time */
176 err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir);
177 if (err < 0) {
178 printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
179 return err;
181 period_size = snd_pcm_hw_params_get_period_size(params, &dir);
182 /* write the parameters to device */
183 err = snd_pcm_hw_params(handle, params);
184 if (err < 0) {
185 printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
186 return err;
188 //g_print( "bs=%d, ps=%d\n", buffer_size, period_size );
189 return 0;
192 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
194 int err;
196 /* get the current swparams */
197 err = snd_pcm_sw_params_current(handle, swparams);
198 if (err < 0) {
199 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
200 return err;
202 /* start the transfer when the buffer is full */
203 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size );
204 if (err < 0) {
205 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
206 return err;
208 /* allow the transfer when at least period_size samples can be processed */
209 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size );
210 if (err < 0) {
211 printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
212 return err;
214 /* align all transfers to 1 sample */
215 err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
216 if (err < 0) {
217 printf("Unable to set transfer align for playback: %s\n", snd_strerror(err));
218 return err;
220 /* write the parameters to the playback device */
221 err = snd_pcm_sw_params(handle, swparams);
222 if (err < 0) {
223 printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
224 return err;
226 return 0;
230 PRIVATE gboolean open_audiofd(ALSAData *d) {
231 int err;
232 snd_pcm_hw_params_t *hwparams;
233 snd_pcm_sw_params_t *swparams;
235 snd_pcm_hw_params_alloca(&hwparams);
236 snd_pcm_sw_params_alloca(&swparams);
238 if ((err = snd_pcm_open(&(d->handle), device, SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC )) < 0) {
239 printf("Playback open error: %s\n", snd_strerror(err));
240 return FALSE;
243 if ((err = set_hwparams(d->handle, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED )) < 0) {
244 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
245 return FALSE;
247 if ((err = set_swparams(d->handle, swparams)) < 0) {
248 printf("Setting of swparams failed: %s\n", snd_strerror(err));
249 return FALSE;
252 return TRUE;
255 PRIVATE void async_callback( snd_async_handler_t *handler ) {
256 ALSAData *data = snd_async_handler_get_callback_private( handler );
258 gint avail = snd_pcm_avail_update( data->handle );
259 g_print( "in callback...\n" );
261 gen_clock_mainloop_have_remaining( avail );
265 PRIVATE void clock_handler(AClock *clock, AClockReason reason) {
266 ALSAData *d = clock->gen->data;
268 switch (reason) {
269 case CLOCK_DISABLE:
270 snd_async_del_handler(d->async_handler);
271 d->async_handler = NULL;
272 break;
274 case CLOCK_ENABLE:
275 if (d->async_handler == NULL)
277 int err;
279 err = snd_async_add_pcm_handler(&d->async_handler, d->handle, async_callback, d);
280 if (err < 0) {
281 printf("Unable to register async handler\n");
282 return;
284 //g_print( "Pre Start State1: %d\n", snd_pcm_state( d->handle ) );
285 //gen_clock_mainloop_have_remaining( period_size );
286 //g_print( "Pre Start State2: %d\n", snd_pcm_state( d->handle ) );
287 //gen_clock_mainloop_have_remaining( period_size );
288 // for (count = 0; count < 2; count++) {
289 // generate_sine(areas, 0, period_size, &data.phase);
290 // err = snd_pcm_writei(handle, samples, period_size);
291 // if (err < 0) {
292 // printf("Initial write error: %s\n", snd_strerror(err));
293 // exit(EXIT_FAILURE);
294 // }
295 // if (err != period_size) {
296 // printf("Initial write error: written %i expected %li\n", err, period_size);
297 // exit(EXIT_FAILURE);
298 // }
299 // }
302 g_print( "Pre Start State3: %d\n", snd_pcm_state( d->handle ) );
303 //err = snd_pcm_start(d->handle);
304 //if (err < 0) {
305 // printf("Start error: %s\n", snd_strerror(err));
306 // return;
309 /* because all other work is done in the signal handler,
310 suspend the process */
312 break;
314 default:
315 g_message("Unreachable code reached (oss_output)... reason = %d", reason);
316 break;
320 PRIVATE void realtime_handler(Generator *g, AEvent *event) {
321 ALSAData *data = g->data;
323 switch (event->kind) {
324 case AE_REALTIME: {
325 SAMPLE *l_buf, *r_buf;
326 int bufbytes = event->d.integer * sizeof(SAMPLE);
328 l_buf = safe_malloc(bufbytes);
329 r_buf = safe_malloc(bufbytes);
331 if (!gen_read_realtime_input(g, SIG_LEFT_CHANNEL, -1, l_buf, event->d.integer))
332 memset(l_buf, 0, bufbytes);
334 if (!gen_read_realtime_input(g, SIG_RIGHT_CHANNEL, -1, r_buf, event->d.integer))
335 memset(r_buf, 0, bufbytes);
337 audio_play_fragment(data->handle, l_buf, r_buf, event->d.integer);
338 free(l_buf);
339 free(r_buf);
341 break;
344 default:
345 g_warning("oss_output module doesn't care for events of kind %d.", event->kind);
346 break;
350 PRIVATE int init_instance(Generator *g) {
351 ALSAData *data;
353 instance_count++;
354 if (instance_count > 1)
355 /* Not allowed more than one of these things. */
356 return 0;
358 data = safe_malloc(sizeof(ALSAData));
362 if (open_audiofd(data) == FALSE) {
363 free(data);
364 popup_msgbox("Error", MSGBOX_OK, 120000, MSGBOX_OK,
365 "Could not open audio device, %s.", device);
366 return 0;
370 data->async_handler = NULL;
371 data->clock = gen_register_clock(g, "ALSA Output Callback Clock", clock_handler);
373 g->data = data;
375 gen_register_realtime_fn(g, realtime_handler);
376 //gen_select_clock(data->clock); /* a not unreasonable assumption? */
378 return 1;
381 PRIVATE void destroy_instance(Generator *g) {
382 ALSAData *data = g->data;
384 gen_deregister_realtime_fn(g, realtime_handler);
386 if (data != NULL) {
387 gen_deregister_clock(data->clock);
388 if (data->input_tag != -1)
389 gdk_input_remove(data->input_tag);
390 //close(data->audiofd);
392 free(data);
395 instance_count--;
398 PRIVATE InputSignalDescriptor input_sigs[] = {
399 { "Left Channel", SIG_FLAG_REALTIME },
400 { "Right Channel", SIG_FLAG_REALTIME },
401 { NULL, }
404 PRIVATE void setup_class(void) {
405 GeneratorClass *k;
406 gboolean prefer;
409 char *value = prefs_get_item("output_plugin");
410 prefer = value ? !strcmp(value, "ALSA Callback") : FALSE;
412 prefs_register_option("output_plugin", "ALSA Callback");
415 char *name = prefs_get_item("output_alsa_callback_device");
416 sprintf(device, "%s", name ? name : "plughw:0,0");
418 prefs_register_option("output_alsa_callback_device", "hw:0,0");
419 prefs_register_option("output_alsa_callback_device", "plughw:0,0");
422 k = gen_new_generatorclass("audio_out_cb", prefer, 0, 0,
423 input_sigs, NULL, NULL,
424 init_instance, destroy_instance,
425 (AGenerator_pickle_t) init_instance, NULL);
427 gencomp_register_generatorclass(k, prefer, "Outputs/ALSA Output (callback) ",
428 PIXMAPDIRIFY("oss_output.xpm"),
429 NULL);
432 PUBLIC void init_plugin(void) {
433 setup_class();