[alsa_out] omg !!! now its becoming academic :)
[jack.git] / tools / alsa_out.c
blobe4688f442cd9e706c7b6fa5bef05aa02de27f3c2
1 /** @file simple_client.c
3 * @brief This simple client demonstrates the basic features of JACK
4 * as they would be used by many applications.
5 */
7 #define _ISOC99_SOURCE 1
8 #define _XOPEN_SOURCE 600
10 #include <stdio.h>
11 #include <errno.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <string.h>
16 #include <alloca.h>
17 #include <math.h>
19 #include <jack/jack.h>
20 #include <jack/jslist.h>
22 #include "alsa/asoundlib.h"
24 #include <samplerate.h>
26 #define SAMPLE_16BIT_SCALING 32767.0f
27 #define SAMPLE_16BIT_MAX 32767
28 #define SAMPLE_16BIT_MIN -32767
29 #define NORMALIZED_FLOAT_MIN -1.0f
30 #define NORMALIZED_FLOAT_MAX 1.0f
31 #define f_round(f) lrintf(f)
33 #define float_16(s, d)\
34 if ((s) <= NORMALIZED_FLOAT_MIN) {\
35 (d) = SAMPLE_16BIT_MIN;\
36 } else if ((s) >= NORMALIZED_FLOAT_MAX) {\
37 (d) = SAMPLE_16BIT_MAX;\
38 } else {\
39 (d) = f_round ((s) * SAMPLE_16BIT_SCALING);\
42 #define OFF_D_SIZE 256
44 typedef signed short ALSASAMPLE;
46 // Here are the lists of the jack ports...
48 JSList *capture_ports = NULL;
49 JSList *capture_srcs = NULL;
50 JSList *playback_ports = NULL;
51 JSList *playback_srcs = NULL;
52 jack_client_t *client;
54 // TODO: make the sample format configurable soon...
55 snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
57 snd_pcm_t *alsa_handle;
59 int jack_sample_rate;
60 int jack_buffer_size;
62 double current_resample_factor = 1.0;
64 double resample_mean = 1.0;
65 double old_offset = 0.0;
66 double offset_differential_array[OFF_D_SIZE];
67 int offset_differential_index = 0;
68 double old_resample_factor = 1.0;
69 double old_old_resample_factor = 1.0;
70 double dd_resample_factor = 0.0;
71 // ------------------------------------------------------ commandline parameters
73 int sample_rate = 0; /* stream rate */
74 int num_channels = 2; /* count of channels */
75 int period_size = 1024;
76 int num_periods = 2;
78 int target_delay = 0; /* the delay which the program should try to approach. */
79 int max_diff = 0; /* the diff value, when a hard readpointer skip should occur */
80 int catch_factor = 1000;
81 int catch_factor2 = 10000;
82 int good_window=0;
83 int verbose = 0;
84 int instrument = 0;
86 // Debug stuff:
88 volatile float output_resampling_factor = 0.0;
89 volatile int output_new_delay = 0;
90 volatile float output_offset = 0.0;
91 volatile float output_diff = 0.0;
93 snd_pcm_uframes_t real_buffer_size;
94 snd_pcm_uframes_t real_period_size;
96 // Alsa stuff... i dont want to touch this bullshit in the next years.... please...
98 static int xrun_recovery(snd_pcm_t *handle, int err) {
99 printf( "xrun !!!.... %d\n", err );
100 if (err == -EPIPE) { /* under-run */
101 err = snd_pcm_prepare(handle);
102 if (err < 0)
103 printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
104 return 0;
105 } else if (err == -EAGAIN) {
106 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
107 usleep(100); /* wait until the suspend flag is released */
108 if (err < 0) {
109 err = snd_pcm_prepare(handle);
110 if (err < 0)
111 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
113 return 0;
115 return err;
118 static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access, int rate, int channels, int period, int nperiods ) {
119 int err, dir=0;
120 unsigned int buffer_time;
121 unsigned int period_time;
122 unsigned int rrate;
124 /* choose all parameters */
125 err = snd_pcm_hw_params_any(handle, params);
126 if (err < 0) {
127 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
128 return err;
130 /* set the interleaved read/write format */
131 err = snd_pcm_hw_params_set_access(handle, params, access);
132 if (err < 0) {
133 printf("Access type not available for playback: %s\n", snd_strerror(err));
134 return err;
136 /* set the sample format */
137 err = snd_pcm_hw_params_set_format(handle, params, format);
138 if (err < 0) {
139 printf("Sample format not available for playback: %s\n", snd_strerror(err));
140 return err;
142 /* set the count of channels */
143 err = snd_pcm_hw_params_set_channels(handle, params, channels);
144 if (err < 0) {
145 printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err));
146 return err;
148 /* set the stream rate */
149 rrate = rate;
150 err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
151 if (err < 0) {
152 printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
153 return err;
155 if (rrate != rate) {
156 printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, rrate);
157 return -EINVAL;
159 /* set the buffer time */
161 buffer_time = 1000000*period*nperiods/rate;
162 err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
163 if (err < 0) {
164 printf("Unable to set buffer time %i for playback: %s\n", 1000000*period*nperiods/rate, snd_strerror(err));
165 return err;
167 err = snd_pcm_hw_params_get_buffer_size( params, &real_buffer_size );
168 if (err < 0) {
169 printf("Unable to get buffer size back: %s\n", snd_strerror(err));
170 return err;
172 if( real_buffer_size != nperiods * period ) {
173 printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) real_buffer_size );
175 /* set the period time */
176 period_time = 1000000*period/rate;
177 err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
178 if (err < 0) {
179 printf("Unable to set period time %i for playback: %s\n", 1000000*period/rate, snd_strerror(err));
180 return err;
182 err = snd_pcm_hw_params_get_period_size(params, &real_period_size, NULL );
183 if (err < 0) {
184 printf("Unable to get period size back: %s\n", snd_strerror(err));
185 return err;
187 if( real_period_size != period ) {
188 printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, (int)real_period_size );
190 /* write the parameters to device */
191 err = snd_pcm_hw_params(handle, params);
192 if (err < 0) {
193 printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
194 return err;
196 return 0;
199 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int period, int nperiods) {
200 int err;
202 /* get the current swparams */
203 err = snd_pcm_sw_params_current(handle, swparams);
204 if (err < 0) {
205 printf("Unable to determine current swparams for capture: %s\n", snd_strerror(err));
206 return err;
208 /* start the transfer when the buffer is full */
209 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period );
210 if (err < 0) {
211 printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err));
212 return err;
214 err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, -1 );
215 if (err < 0) {
216 printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err));
217 return err;
219 /* allow the transfer when at least period_size samples can be processed */
220 err = snd_pcm_sw_params_set_avail_min(handle, swparams, 1 );
221 if (err < 0) {
222 printf("Unable to set avail min for capture: %s\n", snd_strerror(err));
223 return err;
225 /* align all transfers to 1 sample */
226 // err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
227 // if (err < 0) {
228 // printf("Unable to set transfer align for capture: %s\n", snd_strerror(err));
229 // return err;
230 // }
231 /* write the parameters to the playback device */
232 err = snd_pcm_sw_params(handle, swparams);
233 if (err < 0) {
234 printf("Unable to set sw params for capture: %s\n", snd_strerror(err));
235 return err;
237 return 0;
240 // ok... i only need this function to communicate with the alsa bloat api...
242 static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int channels, int period, int nperiods ) {
243 int err;
244 snd_pcm_t *handle;
245 snd_pcm_hw_params_t *hwparams;
246 snd_pcm_sw_params_t *swparams;
248 snd_pcm_hw_params_alloca(&hwparams);
249 snd_pcm_sw_params_alloca(&swparams);
251 if ((err = snd_pcm_open(&(handle), device_name, capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK )) < 0) {
252 printf("Capture open error: %s\n", snd_strerror(err));
253 return NULL;
256 if ((err = set_hwparams(handle, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED, rate, channels, period, nperiods )) < 0) {
257 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
258 return NULL;
260 if ((err = set_swparams(handle, swparams, period, nperiods)) < 0) {
261 printf("Setting of swparams failed: %s\n", snd_strerror(err));
262 return NULL;
265 //snd_pcm_start( handle );
266 //snd_pcm_wait( handle, 200 );
267 int num_null_samples = nperiods * period * channels;
268 ALSASAMPLE *tmp = alloca( num_null_samples * sizeof( ALSASAMPLE ) );
269 memset( tmp, 0, num_null_samples * sizeof( ALSASAMPLE ) );
270 snd_pcm_writei( handle, tmp, num_null_samples );
273 return handle;
276 double hann( double x )
278 return 0.5 * (1.0 - cos( M_PI * x ) );
281 int jumped = 0;
283 * The process callback for this JACK application.
284 * It is called by JACK at the appropriate times.
286 int process (jack_nframes_t nframes, void *arg) {
288 ALSASAMPLE *outbuf;
289 float *floatbuf, *resampbuf;
290 int rlen;
291 int err;
292 snd_pcm_sframes_t delay = target_delay;
295 snd_pcm_delay( alsa_handle, &delay );
297 //delay -= jack_frames_since_cycle_start( client );
298 // Do it the hard way.
299 // this is for compensating xruns etc...
301 if( delay > (target_delay+max_diff) ) {
302 snd_pcm_rewind( alsa_handle, delay - target_delay );
303 output_new_delay = (int) delay;
305 delay = target_delay;
306 //current_resample_factor = (double) sample_rate / (double) jack_sample_rate;
307 current_resample_factor = resample_mean;
308 jumped = 10;
310 if( delay < (target_delay-max_diff) ) {
311 ALSASAMPLE *tmp = alloca( (target_delay-delay) * sizeof( ALSASAMPLE ) * num_channels );
312 memset( tmp, 0, sizeof( ALSASAMPLE ) * num_channels * (target_delay-delay) );
313 snd_pcm_writei( alsa_handle, tmp, target_delay-delay );
315 output_new_delay = (int) delay;
317 delay = target_delay;
318 //current_resample_factor = (double) sample_rate / (double) jack_sample_rate;
319 current_resample_factor = resample_mean;
320 jumped = 10;
322 /* ok... now we should have target_delay +- max_diff on the alsa side.
324 * calculate the number of frames, we want to get.
327 double offset = delay - target_delay;
328 #if 0
329 double request_samples = nframes * current_resample_factor; //== alsa_samples;
332 double frlen = request_samples - offset;
334 // Calculate the added resampling factor, which would move us straight to target delay.
335 double compute_factor = frlen / (double) nframes;
337 // Now calculate the diff_value, which we want to add to current_resample_factor
338 // here are the coefficients of the dll.
339 double diff_value = pow(current_resample_factor - compute_factor, 3) / (double) catch_factor;
340 diff_value += pow(current_resample_factor - compute_factor, 1) / (double) catch_factor2;
341 #endif
343 //smooth_offset_differential = 0.999 * smooth_offset_differential + 0.001 * (offset - old_offset);
344 if( jumped==0 ) //fabs(offset-old_offset) < 10.0 )
345 offset_differential_array[(offset_differential_index++) % OFF_D_SIZE ] = offset-old_offset;
346 else
348 jumped--;
349 offset_differential_array[(offset_differential_index++) % OFF_D_SIZE ] = 0.0;
352 int i;
353 double smooth_offset_differential = 0.0;
354 for( i=0; i<OFF_D_SIZE; i++ )
355 smooth_offset_differential +=
356 offset_differential_array[ (i + offset_differential_index-1) % OFF_D_SIZE] * hann( (double) i / ((double) OFF_D_SIZE - 1.0) );
357 smooth_offset_differential /= (double) OFF_D_SIZE;
359 old_offset = offset;
361 current_resample_factor -= pow(offset/ (double) nframes, 3) / (double) catch_factor;
362 //current_resample_factor -= dd_resample_factor/(double)nframes * 5.0;
363 current_resample_factor -= smooth_offset_differential / (double) nframes / 999.0;
365 // Dampening:
366 // use hysteresis, only do it once offset was more than 150 off,
367 // and now came into 50samples window.
368 // Also only damp when current_resample_factor is more than 0.01% off.
369 if( good_window ) {
370 if( (offset > 75) || (offset < -75) ) {
371 good_window = 0;
373 } else {
374 if( (offset < 20) && (offset > -20) ) {
375 if( 0.0001 < fabs( current_resample_factor - resample_mean ) )
376 //current_resample_factor = ((double) sample_rate / (double) jack_sample_rate);
377 current_resample_factor = resample_mean;
378 good_window = 1;
382 // Output "instrumentatio" gonna change that to real instrumentation in a few.
383 output_resampling_factor = (float) current_resample_factor;
384 output_diff = (float) smooth_offset_differential;
385 output_offset = (float) offset;
387 // Clamp a bit.
388 if( current_resample_factor < 0.25 ) current_resample_factor = 0.25;
389 if( current_resample_factor > 4 ) current_resample_factor = 4;
391 // Now Calculate how many samples we need.
392 rlen = ceil( ((double)nframes) * current_resample_factor )+2;
393 assert( rlen > 10 );
395 // Calculate resample_mean so we can init ourselves to saner values.
396 resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor;
398 * now this should do it...
401 outbuf = alloca( rlen * sizeof( ALSASAMPLE ) * num_channels );
403 floatbuf = alloca( rlen * sizeof( float ) );
404 resampbuf = alloca( nframes * sizeof( float ) );
406 * render jack ports to the outbuf...
409 int chn = 0;
410 JSList *node = playback_ports;
411 JSList *src_node = playback_srcs;
412 SRC_DATA src;
414 while ( node != NULL)
416 int i;
417 jack_port_t *port = (jack_port_t *) node->data;
418 float *buf = jack_port_get_buffer (port, nframes);
420 SRC_STATE *src_state = src_node->data;
422 src.data_in = buf;
423 src.input_frames = nframes;
425 src.data_out = resampbuf;
426 src.output_frames = rlen;
427 src.end_of_input = 0;
429 src.src_ratio = current_resample_factor;
431 src_process( src_state, &src );
433 for (i=0; i < rlen; i++) {
434 float_16( resampbuf[i], outbuf[chn+ i*num_channels] );
437 src_node = jack_slist_next (src_node);
438 node = jack_slist_next (node);
439 chn++;
442 // now write the output...
444 err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen);
445 //err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen);
446 if( err < 0 ) {
447 printf( "err = %d\n", err );
448 if (xrun_recovery(alsa_handle, err) < 0) {
449 printf("Write error: %s\n", snd_strerror(err));
450 exit(EXIT_FAILURE);
454 return 0;
459 * Allocate the necessary jack ports...
462 void alloc_ports( int n_capture, int n_playback ) {
464 int port_flags = JackPortIsOutput;
465 int chn;
466 jack_port_t *port;
467 char buf[32];
469 capture_ports = NULL;
470 for (chn = 0; chn < n_capture; chn++)
472 snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1);
474 port = jack_port_register (client, buf,
475 JACK_DEFAULT_AUDIO_TYPE,
476 port_flags, 0);
478 if (!port)
480 printf( "jacknet_client: cannot register port for %s", buf);
481 break;
484 capture_srcs = jack_slist_append( capture_srcs, src_new( SRC_SINC_FASTEST, 1, NULL ) );
485 capture_ports = jack_slist_append (capture_ports, port);
488 port_flags = JackPortIsInput;
490 playback_ports = NULL;
491 for (chn = 0; chn < n_playback; chn++)
493 snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1);
495 port = jack_port_register (client, buf,
496 JACK_DEFAULT_AUDIO_TYPE,
497 port_flags, 0);
499 if (!port)
501 printf( "jacknet_client: cannot register port for %s", buf);
502 break;
505 playback_srcs = jack_slist_append( playback_srcs, src_new( SRC_SINC_FASTEST, 1, NULL ) );
506 playback_ports = jack_slist_append (playback_ports, port);
511 * This is the shutdown callback for this JACK application.
512 * It is called by JACK if the server ever shuts down or
513 * decides to disconnect the client.
516 void jack_shutdown (void *arg) {
518 exit (1);
522 * be user friendly.
523 * be user friendly.
524 * be user friendly.
527 void printUsage() {
528 fprintf(stderr, "usage: alsa_out [options]\n"
529 "\n"
530 " -j <jack name> - reports a different name to jack\n"
531 " -d <alsa_device> \n"
532 " -c <channels> \n"
533 " -p <period_size> \n"
534 " -n <num_period> \n"
535 " -r <sample_rate> \n"
536 " -m <max_diff> \n"
537 " -t <target_delay> \n"
538 " -f <catch_factor> \n"
539 " -i turns on instrumentation\n"
540 " -v turns on printouts\n"
541 "\n");
546 * the main function....
550 int main (int argc, char *argv[]) {
551 char jack_name[30] = "alsa_out";
552 char alsa_device[30] = "hw:0";
554 extern char *optarg;
555 extern int optind, optopt;
556 int errflg=0;
557 int c;
559 while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:m:t:f:")) != -1) {
560 switch(c) {
561 case 'j':
562 strcpy(jack_name,optarg);
563 break;
564 case 'r':
565 sample_rate = atoi(optarg);
566 break;
567 case 'c':
568 num_channels = atoi(optarg);
569 break;
570 case 'p':
571 period_size = atoi(optarg);
572 break;
573 case 'n':
574 num_periods = atoi(optarg);
575 break;
576 case 'd':
577 strcpy(alsa_device,optarg);
578 break;
579 case 't':
580 target_delay = atoi(optarg);
581 break;
582 case 'm':
583 max_diff = atoi(optarg);
584 break;
585 case 'f':
586 catch_factor = atoi(optarg);
587 break;
588 case 'v':
589 verbose = 1;
590 break;
591 case 'i':
592 instrument = 1;
593 break;
594 case ':':
595 fprintf(stderr,
596 "Option -%c requires an operand\n", optopt);
597 errflg++;
598 break;
599 case '?':
600 fprintf(stderr,
601 "Unrecognized option: -%c\n", optopt);
602 errflg++;
605 if (errflg) {
606 printUsage();
607 exit(2);
610 if ((client = jack_client_new (jack_name)) == 0) {
611 fprintf (stderr, "jack server not running?\n");
612 return 1;
615 /* tell the JACK server to call `process()' whenever
616 there is work to be done.
619 jack_set_process_callback (client, process, 0);
621 /* tell the JACK server to call `jack_shutdown()' if
622 it ever shuts down, either entirely, or if it
623 just decides to stop calling us.
626 jack_on_shutdown (client, jack_shutdown, 0);
629 // alloc input ports, which are blasted out to alsa...
630 alloc_ports( 0, num_channels );
632 // get jack sample_rate
634 jack_sample_rate = jack_get_sample_rate( client );
636 if( !sample_rate )
637 sample_rate = jack_sample_rate;
639 current_resample_factor = (double) sample_rate / (double) jack_sample_rate;
640 resample_mean = current_resample_factor;
642 int i;
643 for( i=0; i<OFF_D_SIZE; i++ )
644 offset_differential_array[i] = 0.0;
646 jack_buffer_size = jack_get_buffer_size( client );
647 // Setup target delay and max_diff for the normal user, who does not play with them...
648 if( !target_delay )
649 target_delay = (num_periods*period_size / 2) - jack_buffer_size;
651 if( !max_diff )
652 max_diff = period_size / 2;
654 if( max_diff > target_delay ) {
655 fprintf( stderr, "target_delay (%d) cant be smaller than max_diff(%d)\n", target_delay, max_diff );
656 exit(20);
658 if( (target_delay+max_diff) > (num_periods*period_size) ) {
659 fprintf( stderr, "target_delay+max_diff (%d) cant be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size );
660 exit(20);
662 // now open the alsa fd...
663 alsa_handle = open_audiofd( alsa_device, 0, sample_rate, num_channels, period_size, num_periods);
664 if( alsa_handle < 0 )
665 exit(20);
668 /* tell the JACK server that we are ready to roll */
670 if (jack_activate (client)) {
671 fprintf (stderr, "cannot activate client");
672 return 1;
675 if( verbose ) {
676 while(1) {
677 usleep(500000);
678 if( output_new_delay ) {
679 printf( "delay = %d\n", output_new_delay );
680 output_new_delay = 0;
682 printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset );
684 } else if( instrument ) {
685 printf( "# n\tresamp\tdiff\toffseti\n");
686 int n=0;
687 while(1) {
688 usleep(1000);
689 printf( "%d\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset );
691 } else {
692 while(1) sleep(10);
695 jack_client_close (client);
696 exit (0);