paulstretch_cpp

PaulStretch
Log | Files | Refs | LICENSE

JAaudiooutput.cpp (6915B)


      1 /*
      2   JAaudiooutput.C - Audio output for JACK
      3   Copyright (C) 2002-2009 Nasca Octavian Paul
      4   Author: Robin Gareus <robin@gareus.org>
      5 
      6   This program is free software; you can redistribute it and/or modify
      7   it under the terms of version 2 of the GNU General Public License 
      8   as published by the Free Software Foundation.
      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 (version 2) for more details.
     14 
     15   You should have received a copy of the GNU General Public License (version 2)
     16   along with this program; if not, write to the Free Software Foundation,
     17   Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     18 
     19 */
     20 
     21 #ifdef HAVE_JACK
     22 
     23 #ifndef DEFAULT_RB_SIZE
     24 #define DEFAULT_RB_SIZE 16384
     25 #endif
     26 
     27 #include <stdlib.h>
     28 #include <string.h>
     29 #include <pthread.h>
     30 #include <jack/jack.h>
     31 #include <jack/ringbuffer.h>
     32 #include "JAaudiooutput.h"
     33 
     34 Player *myplayer=NULL;
     35 jack_client_t *j_client = NULL;
     36 jack_port_t **j_output_port = NULL;
     37 jack_default_audio_sample_t **j_out = NULL;
     38 jack_ringbuffer_t *rb = NULL;
     39 
     40 int m_channels = 2;
     41 int thread_run = 0;
     42 
     43 pthread_t player_thread_id;
     44 pthread_mutex_t player_thread_lock = PTHREAD_MUTEX_INITIALIZER;
     45 pthread_cond_t  buffer_ready = PTHREAD_COND_INITIALIZER;
     46 
     47 #ifdef ENABLE_RESAMPLING
     48 #include <samplerate.h>
     49 float m_fResampleRatio = 1.0;
     50 
     51 #ifndef SRC_QUALITY // alternatives: SRC_SINC_MEDIUM_QUALITY, SRC_SINC_BEST_QUALITY, (SRC_ZERO_ORDER_HOLD, SRC_LINEAR)
     52 #define SRC_QUALITY SRC_SINC_FASTEST
     53 #endif
     54 
     55 #endif
     56 
     57 void jack_shutdown_callback(void *arg){
     58     fprintf(stderr, "jack server [unexpectedly] shut down.\n");
     59     j_client=NULL;
     60     JACKclose();
     61 }
     62 
     63 int jack_audio_callback(jack_nframes_t nframes, void *arg){
     64     int c,s;
     65 
     66     for(c=0; c< m_channels; c++) {
     67 	j_out[c] = (jack_default_audio_sample_t*) jack_port_get_buffer(j_output_port[c], nframes);
     68     }
     69 
     70     if(jack_ringbuffer_read_space(rb) < m_channels * nframes * sizeof(jack_default_audio_sample_t)) {
     71 	for(c=0; c< m_channels; c++) {
     72 	    memset(j_out[c], 0, nframes * sizeof(jack_default_audio_sample_t));
     73 	}
     74     } else {
     75 	/* de-interleave */
     76 	for(s=0; s<nframes; s++) {
     77 	    for(c=0; c< m_channels; c++) {
     78 		jack_ringbuffer_read(rb, (char*) &j_out[c][s], sizeof(jack_default_audio_sample_t));
     79 	    }
     80 	}
     81     }
     82 
     83     /* Tell the player thread there is work to do. */
     84     if(pthread_mutex_trylock(&player_thread_lock) == 0) {
     85 	pthread_cond_signal(&buffer_ready);
     86 	pthread_mutex_unlock(&player_thread_lock);
     87     }
     88 
     89     return(0);
     90 };
     91 
     92 void *jack_player_thread(void *){
     93     const int nframes = 4096;
     94     float *tmpbuf = (float*) calloc(nframes * m_channels, sizeof(float));
     95     float *bufptr = tmpbuf;
     96 #ifdef ENABLE_RESAMPLING
     97     SRC_STATE* src_state = src_new(SRC_QUALITY, 2, NULL);
     98     SRC_DATA src_data;
     99     int nframes_r = floorf((float) nframes*m_fResampleRatio); ///< # of frames after resampling
    100     float *smpbuf = (float*) calloc((1+nframes_r) * m_channels, sizeof(float));
    101 
    102     src_data.input_frames  = nframes;
    103     src_data.output_frames = nframes_r;
    104     src_data.end_of_input  = 0;
    105     src_data.src_ratio     = m_fResampleRatio;
    106     src_data.input_frames_used = 0;
    107     src_data.output_frames_gen = 0;
    108     src_data.data_in       = tmpbuf;
    109     src_data.data_out      = smpbuf;
    110 #else
    111     int nframes_r = nframes;
    112 #endif
    113 
    114     size_t rbsize = nframes_r * m_channels * sizeof(float);
    115 
    116     pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    117     pthread_mutex_lock(&player_thread_lock);
    118 
    119     while(thread_run) {
    120 	myplayer->getaudiobuffer(nframes, tmpbuf);
    121 
    122 #ifdef ENABLE_RESAMPLING
    123 	if(m_fResampleRatio != 1.0) {
    124 	    src_process(src_state, &src_data);
    125 	    bufptr = smpbuf;
    126 	    rbsize = src_data.output_frames_gen * m_channels * sizeof(float);
    127 	}
    128 #endif
    129 	jack_ringbuffer_write(rb, (char *) bufptr, rbsize);
    130 
    131 	while(thread_run && jack_ringbuffer_write_space(rb) <= rbsize) {
    132 	    pthread_cond_wait(&buffer_ready, &player_thread_lock);
    133 	}
    134     }
    135 
    136     pthread_mutex_unlock(&player_thread_lock);
    137     free(tmpbuf);
    138 #ifdef ENABLE_RESAMPLING
    139     src_delete(src_state);
    140     free(smpbuf);
    141 #endif
    142 }
    143 
    144 void JACKaudiooutputinit(Player *player_, int samplerate){
    145     int i;
    146     myplayer=player_;
    147 
    148     if(j_client || thread_run || rb) {
    149 	fprintf(stderr, "already connected to jack.\n");
    150 	return;
    151     }
    152 
    153     j_client = jack_client_open("paulstretch", (jack_options_t) 0, NULL);
    154 
    155     if(!j_client) {
    156 	fprintf(stderr, "could not connect to jack.\n");
    157 	return;
    158     }	
    159 
    160     jack_on_shutdown(j_client, jack_shutdown_callback, NULL);
    161     jack_set_process_callback(j_client, jack_audio_callback, NULL);
    162 
    163     j_output_port=(jack_port_t**) calloc(m_channels, sizeof(jack_port_t*));
    164 
    165     for(i=0;i<m_channels;i++) {
    166 	char channelid[16];
    167 	snprintf(channelid, 16, "output_%i", i);
    168 	j_output_port[i] = jack_port_register(j_client, channelid, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
    169 	if(!j_output_port[i]) {
    170 	    fprintf(stderr, "no more jack ports availabe.\n");
    171 	    JACKclose();
    172 	    return;
    173 	}
    174     }
    175 
    176     j_out = (jack_default_audio_sample_t**) calloc(m_channels, sizeof(jack_default_audio_sample_t*));
    177     const size_t rbsize = DEFAULT_RB_SIZE * m_channels * sizeof(jack_default_audio_sample_t);
    178     rb = jack_ringbuffer_create(rbsize);
    179     memset(rb->buf, 0, rbsize);
    180 
    181     jack_nframes_t jsr = jack_get_sample_rate(j_client);
    182     if(jsr != samplerate) {
    183 #ifdef ENABLE_RESAMPLING
    184 	m_fResampleRatio = (float) jsr / (float) samplerate;
    185 #else
    186 	fprintf(stderr, "Note: paulstretch audio samplerate does not match JACK's samplerate.\n");
    187 #endif
    188     }
    189 
    190     thread_run = 1;
    191     pthread_create(&player_thread_id, NULL, jack_player_thread, NULL);
    192     pthread_yield();
    193 
    194     jack_activate(j_client);
    195 
    196     char *jack_autoconnect = getenv("JACK_AUTOCONNECT");
    197     if(!jack_autoconnect) {
    198 	jack_autoconnect = (char*) "system:playback_";
    199     } else if(!strncmp(jack_autoconnect,"DISABLE", 7)) {
    200 	jack_autoconnect = NULL;
    201     }
    202     if(jack_autoconnect) {
    203 	int myc=0;
    204 	const char **found_ports = jack_get_ports(j_client, jack_autoconnect, NULL, JackPortIsInput);
    205 	for(i = 0; found_ports && found_ports[i]; i++) {
    206 	    if(jack_connect(j_client, jack_port_name(j_output_port[myc]), found_ports[i])) {
    207 		fprintf(stderr, "can not connect to jack output\n");
    208 	    }
    209 	    if(myc >= m_channels) break;
    210 	}
    211     }
    212 }
    213 
    214 void JACKclose(){
    215     if(thread_run) {
    216 	thread_run = 0;
    217 	if(pthread_mutex_trylock(&player_thread_lock) == 0) {
    218 	    pthread_cond_signal(&buffer_ready);
    219 	    pthread_mutex_unlock(&player_thread_lock);
    220 	}
    221 	pthread_join(player_thread_id, NULL);
    222     }
    223     if(j_client){
    224 	jack_client_close(j_client);
    225     }
    226     if(j_output_port) {
    227 	free(j_output_port);
    228 	j_output_port=NULL;
    229     }
    230     if(j_out) {
    231 	free(j_out);
    232 	j_out = NULL;
    233     }
    234     if(rb) {
    235 	jack_ringbuffer_free(rb);
    236 	rb=NULL;
    237     }
    238     j_client=NULL;
    239 };
    240 
    241 #endif /* HAVE_JACK */
    242 
    243 /* vim: set ts=8 sw=4: */