/** @file simple_client.c * * @brief This simple client demonstrates the basic features of JACK * as they would be used by many applications. */ #include #include #include #include #include #include #include #define ALSA_PCM_OLD_HW_PARAMS_API #define ALSA_PCM_OLD_SW_PARAMS_API #include "alsa/asoundlib.h" #include typedef signed short OUTPUTSAMPLE; #define SAMPLE_RATE 48000 #define PRIVATE static #define SAMPLE jack_default_audio_sample_t jack_port_t *input_port; jack_port_t *output_port1, *output_port2; jack_client_t *client; snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ int rate = SAMPLE_RATE; /* stream rate */ int channels = 2; /* count of channels */ int buffer_time = 1000000*256 / SAMPLE_RATE; /* ring buffer length in us */ int period_time = 1000000*128 / SAMPLE_RATE; /* period time in us */ int target_delay = 150; /* the delay which the program should try to approach. */ int max_diff = 32; /* the diff value, when a hard readpointer skip should occur */ snd_pcm_sframes_t buffer_size; snd_pcm_sframes_t period_size; snd_output_t *output = NULL; snd_pcm_t *alsa_handle; SRC_STATE *src_state; static int xrun_recovery(snd_pcm_t *handle, int err) { //printf( "xrun !!!....\n" ); if (err == -EPIPE) { /* under-run */ err = snd_pcm_prepare(handle); if (err < 0) printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); return 0; } else if (err == -ESTRPIPE) { while ((err = snd_pcm_resume(handle)) == -EAGAIN) sleep(1); /* wait until the suspend flag is released */ if (err < 0) { err = snd_pcm_prepare(handle); if (err < 0) printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); } return 0; } return err; } PRIVATE void audio_input_fragment(snd_pcm_t *handle, SAMPLE *left, SAMPLE *right, int length) { OUTPUTSAMPLE *outbuf; float *floatbuf, *resampbuf; int rlen; int i, err; snd_pcm_sframes_t delay; SRC_DATA src_data; if (length <= 0) return; snd_pcm_delay( handle, &delay ); // Do it the hard way. // this is for compensating xruns etc... if ( delay > (target_delay + max_diff) ) { OUTPUTSAMPLE *tmp = alloca( (delay - target_delay) * sizeof( OUTPUTSAMPLE ) * 2 ); snd_pcm_readi( handle, tmp, delay - target_delay ); printf( "delay = %d\n", (int) delay ); delay = target_delay; } if ( delay < (target_delay - max_diff) ) { snd_pcm_rewind( handle, target_delay - delay ); printf( "delay = %d\n", (int) delay ); delay = target_delay; } // ok... now to the resampling code... // rlen = length - target_delay + delay; //(target_delay/10) + (delay/10); outbuf = alloca( rlen * sizeof( OUTPUTSAMPLE ) * 2 ); floatbuf = alloca( rlen * sizeof( float ) * 2 ); resampbuf = alloca( length * sizeof( float ) * 2 ); again: err = snd_pcm_readi(handle, outbuf, rlen); if ( err < 0 ) { //printf( "err = %d\n", err ); if (xrun_recovery(handle, err) < 0) { //printf("Write error: %s\n", snd_strerror(err)); //exit(EXIT_FAILURE); } goto again; } if ( err != rlen ) { printf( "read = %d\n", rlen ); } for (i = 0; i < (rlen*2); i++) { floatbuf[i] = (float)outbuf[i] / (float)32767; } src_data.data_in = floatbuf; src_data.data_out = resampbuf; src_data.input_frames = rlen; src_data.output_frames = length; src_data.src_ratio = (double)length / (double)rlen; src_data.end_of_input = 0; src_set_ratio( src_state, src_data.src_ratio ); src_process( src_state, &src_data ); // for( i=0; i < length*2; i++ ) // resampbuf[i] = floatbuf[i]; for (i = 0; i < length; i++) { left[i] = resampbuf[i*2]; right[i] = resampbuf[(i*2)+1]; } //printf( "len=%d, err=%d state=%d\n", length, err, snd_pcm_state(handle) ); } static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access) { int err, dir; /* choose all parameters */ err = snd_pcm_hw_params_any(handle, params); if (err < 0) { printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); return err; } /* set the interleaved read/write format */ err = snd_pcm_hw_params_set_access(handle, params, access); if (err < 0) { printf("Access type not available for playback: %s\n", snd_strerror(err)); return err; } /* set the sample format */ err = snd_pcm_hw_params_set_format(handle, params, format); if (err < 0) { printf("Sample format not available for playback: %s\n", snd_strerror(err)); return err; } /* set the count of channels */ err = snd_pcm_hw_params_set_channels(handle, params, channels); if (err < 0) { printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err)); return err; } /* set the stream rate */ err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0); if (err < 0) { printf("Rate %iHz not available for capture: %s\n", rate, snd_strerror(err)); return err; } if (err != rate) { printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err); return -EINVAL; } /* set the buffer time */ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir); if (err < 0) { printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)); return err; } buffer_size = snd_pcm_hw_params_get_buffer_size(params); /* set the period time */ err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir); if (err < 0) { printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err)); return err; } period_size = snd_pcm_hw_params_get_period_size(params, &dir); /* write the parameters to device */ err = snd_pcm_hw_params(handle, params); if (err < 0) { printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); return err; } printf( "bs=%d, ps=%d\n", (int)buffer_size, (int)period_size ); return 0; } static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams) { int err; /* get the current swparams */ err = snd_pcm_sw_params_current(handle, swparams); if (err < 0) { printf("Unable to determine current swparams for capture: %s\n", snd_strerror(err)); return err; } /* start the transfer when the buffer is full */ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size ); if (err < 0) { printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); return err; } err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, -1 ); if (err < 0) { printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); return err; } /* allow the transfer when at least period_size samples can be processed */ err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size ); if (err < 0) { printf("Unable to set avail min for capture: %s\n", snd_strerror(err)); return err; } /* align all transfers to 1 sample */ err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); if (err < 0) { printf("Unable to set transfer align for capture: %s\n", snd_strerror(err)); return err; } /* write the parameters to the playback device */ err = snd_pcm_sw_params(handle, swparams); if (err < 0) { printf("Unable to set sw params for capture: %s\n", snd_strerror(err)); return err; } return 0; } PRIVATE snd_pcm_t *open_audiofd( void ) { int err; snd_pcm_t *handle; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); if ((err = snd_pcm_open(&(handle), "hw:0", SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK )) < 0) { printf("Capture open error: %s\n", snd_strerror(err)); return NULL; } if ((err = set_hwparams(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED )) < 0) { printf("Setting of hwparams failed: %s\n", snd_strerror(err)); return NULL; } if ((err = set_swparams(handle, swparams)) < 0) { printf("Setting of swparams failed: %s\n", snd_strerror(err)); return NULL; } snd_pcm_start( handle ); snd_pcm_wait( handle, 200 ); return handle; } /** * The process callback for this JACK application. * It is called by JACK at the appropriate times. */ int process (jack_nframes_t nframes, void *arg) { jack_default_audio_sample_t *out1 = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port1, nframes); jack_default_audio_sample_t *out2 = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port2, nframes); //jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_port_get_buffer (input_port, nframes); //memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes); audio_input_fragment( alsa_handle, out1, out2, nframes ); //sendto( sockfd, "x", 1, 0, &destaddr, sizeof( destaddr ) ); return 0; } /** * This is the shutdown callback for this JACK application. * It is called by JACK if the server ever shuts down or * decides to disconnect the client. */ void jack_shutdown (void *arg) { exit (1); } int main (int argc, char *argv[]) { //const char **ports; // if (argc < 2) { // fprintf (stderr, "usage: udpsync_source desthost\n"); // return 1; // } // sockfd = socket( PF_INET, SOCK_DGRAM, 0 ); // init_sockaddr_in( &destaddr, argv[1], 3000 ); /* try to become a client of the JACK server */ src_state = src_new(SRC_SINC_FASTEST, 2, NULL); src_set_ratio( src_state, 1.0 ); alsa_handle = open_audiofd(); if ((client = jack_client_new ("alsa_unsynced_pcm")) == 0) { fprintf (stderr, "jack server not running?\n"); return 1; } /* tell the JACK server to call `process()' whenever there is work to be done. */ jack_set_process_callback(client, process, 0); /* tell the JACK server to call `jack_shutdown()' if it ever shuts down, either entirely, or if it just decides to stop calling us. */ jack_on_shutdown(client, jack_shutdown, 0); /* display the current sample rate. */ printf("engine sample rate: %" PRIu32 "\n", jack_get_sample_rate (client)); /* create two ports */ // input_port = jack_port_register (client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); output_port1 = jack_port_register(client, "output1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); output_port2 = jack_port_register(client, "output2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); /* tell the JACK server that we are ready to roll */ if (jack_activate(client)) { fprintf (stderr, "cannot activate client"); return 1; } while (1) sleep(1); jack_client_close(client); exit (0); }