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
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <sys/soundcard.h>
33 #include "generator.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
{
56 snd_async_handler_t
*async_handler
;
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
);
83 printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err
));
85 } else if (err
== -ESTRPIPE
) {
86 while ((err
= snd_pcm_resume(handle
)) == -EAGAIN
)
87 sleep(1); /* wait until the suspend flag is released */
89 err
= snd_pcm_prepare(handle
);
91 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err
));
98 PRIVATE
void audio_play_fragment(snd_pcm_t
*handle
, SAMPLE
*left
, SAMPLE
*right
, int length
) {
100 int buflen
= length
* sizeof(OUTPUTSAMPLE
) * 2;
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
);
118 if (xrun_recovery(handle
, err
) < 0) {
119 printf("Write error: %s\n", snd_strerror(err
));
124 //g_print( "len=%d, err=%d state=%d\n", length, err, snd_pcm_state(handle) );
128 static int set_hwparams(snd_pcm_t
*handle
,
129 snd_pcm_hw_params_t
*params
,
130 snd_pcm_access_t access
)
134 /* choose all parameters */
135 err
= snd_pcm_hw_params_any(handle
, params
);
137 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err
));
140 /* set the interleaved read/write format */
141 err
= snd_pcm_hw_params_set_access(handle
, params
, access
);
143 printf("Access type not available for playback: %s\n", snd_strerror(err
));
146 /* set the sample format */
147 err
= snd_pcm_hw_params_set_format(handle
, params
, format
);
149 printf("Sample format not available for playback: %s\n", snd_strerror(err
));
152 /* set the count of channels */
153 err
= snd_pcm_hw_params_set_channels(handle
, params
, channels
);
155 printf("Channels count (%i) not available for playbacks: %s\n", channels
, snd_strerror(err
));
158 /* set the stream rate */
159 err
= snd_pcm_hw_params_set_rate_near(handle
, params
, rate
, 0);
161 printf("Rate %iHz not available for playback: %s\n", rate
, snd_strerror(err
));
165 printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate
, err
);
168 /* set the buffer time */
169 err
= snd_pcm_hw_params_set_buffer_time_near(handle
, params
, buffer_time
, &dir
);
171 printf("Unable to set buffer time %i for playback: %s\n", buffer_time
, snd_strerror(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
);
178 printf("Unable to set period time %i for playback: %s\n", period_time
, snd_strerror(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
);
185 printf("Unable to set hw params for playback: %s\n", snd_strerror(err
));
188 //g_print( "bs=%d, ps=%d\n", buffer_size, period_size );
192 static int set_swparams(snd_pcm_t
*handle
, snd_pcm_sw_params_t
*swparams
)
196 /* get the current swparams */
197 err
= snd_pcm_sw_params_current(handle
, swparams
);
199 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err
));
202 /* start the transfer when the buffer is full */
203 err
= snd_pcm_sw_params_set_start_threshold(handle
, swparams
, buffer_size
);
205 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(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
);
211 printf("Unable to set avail min for playback: %s\n", snd_strerror(err
));
214 /* align all transfers to 1 sample */
215 err
= snd_pcm_sw_params_set_xfer_align(handle
, swparams
, 1);
217 printf("Unable to set transfer align for playback: %s\n", snd_strerror(err
));
220 /* write the parameters to the playback device */
221 err
= snd_pcm_sw_params(handle
, swparams
);
223 printf("Unable to set sw params for playback: %s\n", snd_strerror(err
));
230 PRIVATE gboolean
open_audiofd(ALSAData
*d
) {
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
));
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
));
247 if ((err
= set_swparams(d
->handle
, swparams
)) < 0) {
248 printf("Setting of swparams failed: %s\n", snd_strerror(err
));
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
;
270 snd_async_del_handler(d
->async_handler
);
271 d
->async_handler
= NULL
;
275 if (d
->async_handler
== NULL
)
279 err
= snd_async_add_pcm_handler(&d
->async_handler
, d
->handle
, async_callback
, d
);
281 printf("Unable to register async handler\n");
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);
292 // printf("Initial write error: %s\n", snd_strerror(err));
293 // exit(EXIT_FAILURE);
295 // if (err != period_size) {
296 // printf("Initial write error: written %i expected %li\n", err, period_size);
297 // exit(EXIT_FAILURE);
302 g_print( "Pre Start State3: %d\n", snd_pcm_state( d
->handle
) );
303 //err = snd_pcm_start(d->handle);
305 // printf("Start error: %s\n", snd_strerror(err));
309 /* because all other work is done in the signal handler,
310 suspend the process */
315 g_message("Unreachable code reached (oss_output)... reason = %d", reason
);
320 PRIVATE
void realtime_handler(Generator
*g
, AEvent
*event
) {
321 ALSAData
*data
= g
->data
;
323 switch (event
->kind
) {
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
);
345 g_warning("oss_output module doesn't care for events of kind %d.", event
->kind
);
350 PRIVATE
int init_instance(Generator
*g
) {
354 if (instance_count
> 1)
355 /* Not allowed more than one of these things. */
358 data
= safe_malloc(sizeof(ALSAData
));
362 if (open_audiofd(data
) == FALSE
) {
364 popup_msgbox("Error", MSGBOX_OK
, 120000, MSGBOX_OK
,
365 "Could not open audio device, %s.", device
);
370 data
->async_handler
= NULL
;
371 data
->clock
= gen_register_clock(g
, "ALSA Output Callback Clock", clock_handler
);
375 gen_register_realtime_fn(g
, realtime_handler
);
376 //gen_select_clock(data->clock); /* a not unreasonable assumption? */
381 PRIVATE
void destroy_instance(Generator
*g
) {
382 ALSAData
*data
= g
->data
;
384 gen_deregister_realtime_fn(g
, realtime_handler
);
387 gen_deregister_clock(data
->clock
);
388 if (data
->input_tag
!= -1)
389 gdk_input_remove(data
->input_tag
);
390 //close(data->audiofd);
398 PRIVATE InputSignalDescriptor input_sigs
[] = {
399 { "Left Channel", SIG_FLAG_REALTIME
},
400 { "Right Channel", SIG_FLAG_REALTIME
},
404 PRIVATE
void setup_class(void) {
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"),
432 PUBLIC
void init_plugin(void) {