/***************************************************************************** * Process: callback for JACK *****************************************************************************/ int Process( jack_nframes_t i_frames, void *p_arg ) { unsigned int i, j, i_nb_samples = 0; audio_output_t *p_aout = (audio_output_t*) p_arg; struct aout_sys_t *p_sys = p_aout->sys; jack_sample_t *p_src = NULL; jack_nframes_t dframes = p_sys->latency - jack_frames_since_cycle_start( p_sys->p_jack_client ); jack_time_t dtime = dframes * 1000 * 1000 / jack_get_sample_rate( p_sys->p_jack_client ); mtime_t play_date = mdate() + (mtime_t) ( dtime ); /* Get the next audio data buffer */ aout_buffer_t *p_buffer = aout_PacketNext( p_aout, play_date ); if( p_buffer != NULL ) { p_src = (jack_sample_t *)p_buffer->p_buffer; i_nb_samples = p_buffer->i_nb_samples; } /* Get the JACK buffers to write to */ for( i = 0; i < p_sys->i_channels; i++ ) { p_sys->p_jack_buffers[i] = jack_port_get_buffer( p_sys->p_jack_ports[i], i_frames ); } /* Copy in the audio data */ for( j = 0; j < i_nb_samples; j++ ) { for( i = 0; i < p_sys->i_channels; i++ ) { jack_sample_t *p_dst = p_sys->p_jack_buffers[i]; p_dst[j] = *p_src; p_src++; } } /* Fill any remaining buffer with silence */ if( i_nb_samples < i_frames ) { for( i = 0; i < p_sys->i_channels; i++ ) { memset( p_sys->p_jack_buffers[i] + i_nb_samples, 0, sizeof( jack_sample_t ) * (i_frames - i_nb_samples) ); } } if( p_buffer ) { aout_BufferFree( p_buffer ); } return 0; }
static int process_callback(jack_nframes_t nframes, void *arg) { /* Warning: this function runs in realtime. One mustn't allocate memory here * or do any other thing that could block. */ int i, j; JackData *self = arg; float * buffer; jack_nframes_t latency, cycle_delay; AVPacket pkt; float *pkt_data; double cycle_time; if (!self->client) return 0; /* The approximate delay since the hardware interrupt as a number of frames */ cycle_delay = jack_frames_since_cycle_start(self->client); /* Retrieve filtered cycle time */ cycle_time = ff_timefilter_update(self->timefilter, av_gettime() / 1000000.0 - (double) cycle_delay / self->sample_rate, self->buffer_size); /* Check if an empty packet is available, and if there's enough space to send it back once filled */ if ((av_fifo_size(self->new_pkts) < sizeof(pkt)) || (av_fifo_space(self->filled_pkts) < sizeof(pkt))) { self->pkt_xrun = 1; return 0; } /* Retrieve empty (but allocated) packet */ av_fifo_generic_read(self->new_pkts, &pkt, sizeof(pkt), NULL); pkt_data = (float *) pkt.data; latency = 0; /* Copy and interleave audio data from the JACK buffer into the packet */ for (i = 0; i < self->nports; i++) { latency += jack_port_get_total_latency(self->client, self->ports[i]); buffer = jack_port_get_buffer(self->ports[i], self->buffer_size); for (j = 0; j < self->buffer_size; j++) pkt_data[j * self->nports + i] = buffer[j]; } /* Timestamp the packet with the cycle start time minus the average latency */ pkt.pts = (cycle_time - (double) latency / (self->nports * self->sample_rate)) * 1000000.0; /* Send the now filled packet back, and increase packet counter */ av_fifo_generic_write(self->filled_pkts, &pkt, sizeof(pkt), NULL); sem_post(&self->packet_count); return 0; }
/** * The process callback for this JACK application. * It is called by JACK at the appropriate times. */ int process (jack_nframes_t nframes, void *arg) { int rlen; int err; snd_pcm_sframes_t delay = target_delay; int i; delay = (num_periods*period_size)-snd_pcm_avail( alsa_handle ) ; delay -= jack_frames_since_cycle_start( client ); // Do it the hard way. // this is for compensating xruns etc... if( delay > (target_delay+max_diff) ) { snd_pcm_rewind( alsa_handle, delay - target_delay ); output_new_delay = (int) delay; delay = target_delay; // Set the resample_rate... we need to adjust the offset integral, to do this. // first look at the PI controller, this code is just a special case, which should never execute once // everything is swung in. offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; // Also clear the array. we are beginning a new control cycle. for( i=0; i<smooth_size; i++ ) offset_array[i] = 0.0; } if( delay < (target_delay-max_diff) ) { output_new_delay = (int) delay; while ((target_delay-delay) > 0) { snd_pcm_uframes_t to_write = ((target_delay-delay) > 512) ? 512 : (target_delay-delay); snd_pcm_writei( alsa_handle, tmpbuf, to_write ); delay += to_write; } delay = target_delay; // Set the resample_rate... we need to adjust the offset integral, to do this. offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; // Also clear the array. we are beginning a new control cycle. for( i=0; i<smooth_size; i++ ) offset_array[i] = 0.0; } /* ok... now we should have target_delay +- max_diff on the alsa side. * * calculate the number of frames, we want to get. */ double offset = delay - target_delay; // Save offset. offset_array[(offset_differential_index++)% smooth_size ] = offset; // Build the mean of the windowed offset array // basically fir lowpassing. double smooth_offset = 0.0; for( i=0; i<smooth_size; i++ ) smooth_offset += offset_array[ (i + offset_differential_index-1) % smooth_size] * window_array[i]; smooth_offset /= (double) smooth_size; // this is the integral of the smoothed_offset offset_integral += smooth_offset; // Clamp offset. // the smooth offset still contains unwanted noise // which would go straigth onto the resample coeff. // it only used in the P component and the I component is used for the fine tuning anyways. if( fabs( smooth_offset ) < pclamp ) smooth_offset = 0.0; // ok. now this is the PI controller. // u(t) = K * ( e(t) + 1/T \int e(t') dt' ) // K = 1/catch_factor and T = catch_factor2 double current_resample_factor = static_resample_factor - smooth_offset / (double) catch_factor - offset_integral / (double) catch_factor / (double)catch_factor2; // now quantize this value around resample_mean, so that the noise which is in the integral component doesnt hurt. current_resample_factor = floor( (current_resample_factor - resample_mean) * controlquant + 0.5 ) / controlquant + resample_mean; // Output "instrumentatio" gonna change that to real instrumentation in a few. output_resampling_factor = (float) current_resample_factor; output_diff = (float) smooth_offset; output_integral = (float) offset_integral; output_offset = (float) offset; // Clamp a bit. if( current_resample_factor < resample_lower_limit ) current_resample_factor = resample_lower_limit; if( current_resample_factor > resample_upper_limit ) current_resample_factor = resample_upper_limit; // Now Calculate how many samples we need. rlen = ceil( ((double)nframes) * current_resample_factor )+2; assert( rlen > 2 ); // Calculate resample_mean so we can init ourselves to saner values. resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor; /* * now this should do it... */ outbuf = alloca( rlen * formats[format].sample_size * num_channels ); resampbuf = alloca( rlen * sizeof( float ) ); /* * render jack ports to the outbuf... */ int chn = 0; JSList *node = playback_ports; JSList *src_node = playback_srcs; SRC_DATA src; while ( node != NULL) { jack_port_t *port = (jack_port_t *) node->data; float *buf = jack_port_get_buffer (port, nframes); SRC_STATE *src_state = src_node->data; src.data_in = buf; src.input_frames = nframes; src.data_out = resampbuf; src.output_frames = rlen; src.end_of_input = 0; src.src_ratio = current_resample_factor; src_process( src_state, &src ); formats[format].jack_to_soundcard( outbuf + format[formats].sample_size * chn, resampbuf, src.output_frames_gen, num_channels*format[formats].sample_size, NULL); src_node = jack_slist_next (src_node); node = jack_slist_next (node); chn++; } // now write the output... again: err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen); //err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen); if( err < 0 ) { printf( "err = %d\n", err ); if (xrun_recovery(alsa_handle, err) < 0) { printf("Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } goto again; } return 0; }
// rt static int _process(jack_nframes_t nsamples, void *data) { prog_t *handle = data; bin_t *bin = &handle->bin; sp_app_t *app = bin->app; #if defined(JACK_HAS_CYCLE_TIMES) clock_gettime(CLOCK_REALTIME, &handle->ntp); handle->ntp.tv_sec += JAN_1970; // convert NTP to OSC time jack_nframes_t offset = jack_frames_since_cycle_start(handle->client); float T; jack_get_cycle_times(handle->client, &handle->cycle.cur_frames, &handle->cycle.cur_usecs, &handle->cycle.nxt_usecs, &T); (void)T; handle->cycle.ref_frames = handle->cycle.cur_frames + offset; // calculate apparent period double diff = 1e-6 * (handle->cycle.nxt_usecs - handle->cycle.cur_usecs); // calculate apparent samples per period handle->cycle.dT = nsamples / diff; handle->cycle.dTm1 = 1.0 / handle->cycle.dT; #endif // get transport position jack_position_t pos; jack_transport_state_t rolling = jack_transport_query(handle->client, &pos) == JackTransportRolling; int trans_changed = (rolling != handle->trans.rolling) || (pos.frame != handle->trans.frame) || (pos.beats_per_bar != handle->trans.beats_per_bar) || (pos.beat_type != handle->trans.beat_type) || (pos.ticks_per_beat != handle->trans.ticks_per_beat) || (pos.beats_per_minute != handle->trans.beats_per_minute); const size_t sample_buf_size = sizeof(float) * nsamples; const sp_app_system_source_t *sources = sp_app_get_system_sources(app); const sp_app_system_sink_t *sinks = sp_app_get_system_sinks(app); if(sp_app_bypassed(app)) // aka loading state { //fprintf(stderr, "app is bypassed\n"); // clear output buffers for(const sp_app_system_sink_t *sink=sinks; sink->type != SYSTEM_PORT_NONE; sink++) { switch(sink->type) { case SYSTEM_PORT_NONE: case SYSTEM_PORT_CONTROL: case SYSTEM_PORT_COM: break; case SYSTEM_PORT_AUDIO: case SYSTEM_PORT_CV: { void *out_buf = jack_port_get_buffer(sink->sys_port, nsamples); memset(out_buf, 0x0, sample_buf_size); break; } case SYSTEM_PORT_MIDI: case SYSTEM_PORT_OSC: { void *out_buf = jack_port_get_buffer(sink->sys_port, nsamples); jack_midi_clear_buffer(out_buf); break; } } } bin_process_pre(bin, nsamples, true); bin_process_post(bin); return 0; } //TODO use __builtin_assume_aligned // fill input buffers for(const sp_app_system_source_t *source=sources; source->type != SYSTEM_PORT_NONE; source++) { switch(source->type) { case SYSTEM_PORT_NONE: case SYSTEM_PORT_CONTROL: break; case SYSTEM_PORT_AUDIO: case SYSTEM_PORT_CV: { const void *in_buf = jack_port_get_buffer(source->sys_port, nsamples); memcpy(source->buf, in_buf, sample_buf_size); break; } case SYSTEM_PORT_MIDI: { void *in_buf = jack_port_get_buffer(source->sys_port, nsamples); void *seq_in = source->buf; LV2_Atom_Forge *forge = &handle->forge; LV2_Atom_Forge_Frame frame; lv2_atom_forge_set_buffer(forge, seq_in, SEQ_SIZE); LV2_Atom_Forge_Ref ref = lv2_atom_forge_sequence_head(forge, &frame, 0); if(ref && trans_changed) ref = _trans_event(handle, forge, rolling, &pos); int n = jack_midi_get_event_count(in_buf); for(int i=0; i<n; i++) { jack_midi_event_t mev; jack_midi_event_get(&mev, in_buf, i); //add jack midi event to in_buf if(ref) ref = lv2_atom_forge_frame_time(forge, mev.time); if(ref) ref = lv2_atom_forge_atom(forge, mev.size, handle->midi_MidiEvent); if(ref) ref = lv2_atom_forge_raw(forge, mev.buffer, mev.size); if(ref) lv2_atom_forge_pad(forge, mev.size); } if(ref) lv2_atom_forge_pop(forge, &frame); else lv2_atom_sequence_clear(seq_in); break; } case SYSTEM_PORT_OSC: { void *in_buf = jack_port_get_buffer(source->sys_port, nsamples); void *seq_in = source->buf; LV2_Atom_Forge *forge = &handle->forge; LV2_Atom_Forge_Frame frame; lv2_atom_forge_set_buffer(forge, seq_in, SEQ_SIZE); LV2_Atom_Forge_Ref ref = lv2_atom_forge_sequence_head(forge, &frame, 0); if(ref && trans_changed) ref = _trans_event(handle, forge, rolling, &pos); int n = jack_midi_get_event_count(in_buf); for(int i=0; i<n; i++) { jack_midi_event_t mev; jack_midi_event_get(&mev, (void *)in_buf, i); //add jack osc event to in_buf if(osc_check_packet(mev.buffer, mev.size)) { if(ref) ref = lv2_atom_forge_frame_time(forge, mev.time); handle->ref = ref; osc_dispatch_method(mev.buffer, mev.size, methods, _bundle_in, _bundle_out, handle); ref = handle->ref; } } if(ref) lv2_atom_forge_pop(forge, &frame); else lv2_atom_sequence_clear(seq_in); break; } case SYSTEM_PORT_COM: { void *seq_in = source->buf; LV2_Atom_Forge *forge = &handle->forge; LV2_Atom_Forge_Frame frame; lv2_atom_forge_set_buffer(forge, seq_in, SEQ_SIZE); LV2_Atom_Forge_Ref ref = lv2_atom_forge_sequence_head(forge, &frame, 0); const LV2_Atom_Object *obj; size_t size; while((obj = varchunk_read_request(bin->app_from_com, &size))) { if(ref) ref = lv2_atom_forge_frame_time(forge, 0); if(ref) ref = lv2_atom_forge_raw(forge, obj, size); if(ref) lv2_atom_forge_pad(forge, size); varchunk_read_advance(bin->app_from_com); } if(ref) lv2_atom_forge_pop(forge, &frame); else lv2_atom_sequence_clear(seq_in); break; } } } // update transport state handle->trans.rolling = rolling; handle->trans.frame = rolling ? handle->trans.frame + nsamples : pos.frame; handle->trans.beats_per_bar = pos.beats_per_bar; handle->trans.beat_type = pos.beat_type; handle->trans.ticks_per_beat = pos.ticks_per_beat; handle->trans.beats_per_minute = pos.beats_per_minute; bin_process_pre(bin, nsamples, false); // fill output buffers for(const sp_app_system_sink_t *sink=sinks; sink->type != SYSTEM_PORT_NONE; sink++) { switch(sink->type) { case SYSTEM_PORT_NONE: case SYSTEM_PORT_CONTROL: break; case SYSTEM_PORT_AUDIO: case SYSTEM_PORT_CV: { void *out_buf = jack_port_get_buffer(sink->sys_port, nsamples); memcpy(out_buf, sink->buf, sample_buf_size); break; } case SYSTEM_PORT_MIDI: { void *out_buf = jack_port_get_buffer(sink->sys_port, nsamples); const LV2_Atom_Sequence *seq_out = sink->buf; // fill midi output buffer jack_midi_clear_buffer(out_buf); if(seq_out) { LV2_ATOM_SEQUENCE_FOREACH(seq_out, ev) { const LV2_Atom *atom = &ev->body; if(atom->type != handle->midi_MidiEvent) continue; // ignore non-MIDI events jack_midi_event_write(out_buf, ev->time.frames, LV2_ATOM_BODY_CONST(atom), atom->size); } } break; } case SYSTEM_PORT_OSC: { void *out_buf = jack_port_get_buffer(sink->sys_port, nsamples); const LV2_Atom_Sequence *seq_out = sink->buf; // fill midi output buffer jack_midi_clear_buffer(out_buf); if(seq_out) { LV2_ATOM_SEQUENCE_FOREACH(seq_out, ev) { const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body; handle->osc_ptr = handle->osc_buf; handle->osc_end = handle->osc_buf + OSC_SIZE; osc_atom_event_unroll(&handle->oforge, obj, _bundle_push_cb, _bundle_pop_cb, _message_cb, handle); size_t size = handle->osc_ptr ? handle->osc_ptr - handle->osc_buf : 0; if(size) { jack_midi_event_write(out_buf, ev->time.frames, handle->osc_buf, size); } } } break; } case SYSTEM_PORT_COM: { const LV2_Atom_Sequence *seq_out = sink->buf; LV2_ATOM_SEQUENCE_FOREACH(seq_out, ev) { const LV2_Atom *atom = (const LV2_Atom *)&ev->body; // try do process events directly bin->advance_ui = sp_app_from_ui(bin->app, atom); if(!bin->advance_ui) // queue event in ringbuffer instead { //fprintf(stderr, "plugin ui direct is blocked\n"); void *ptr; size_t size = lv2_atom_total_size(atom); if((ptr = varchunk_write_request(bin->app_from_app, size))) { memcpy(ptr, atom, size); varchunk_write_advance(bin->app_from_app, size); } else { //fprintf(stderr, "app_from_ui ringbuffer full\n"); //FIXME } } } break; } } } bin_process_post(bin); return 0; }
void SC_JackDriver::Run() { jack_client_t* client = mClient; World* world = mWorld; #ifdef SC_JACK_USE_DLL mDLL.Update(secondsSinceEpoch(getTime())); #if SC_JACK_DEBUG_DLL static int tick = 0; if (++tick >= 10) { tick = 0; scprintf("DLL: t %.6f p %.9f sr %.6f e %.9f avg(e) %.9f inc %.9f\n", mDLL.PeriodTime(), mDLL.Period(), mDLL.SampleRate(), mDLL.Error(), mDLL.AvgError(), mOSCincrement * kOSCtoSecs); } #endif #else HostTime hostTime = getTime(); double hostSecs = secondsSinceEpoch(hostTime); double sampleTime = (double)(jack_frame_time(client) + jack_frames_since_cycle_start(client)); if (mStartHostSecs == 0) { mStartHostSecs = hostSecs; mStartSampleTime = sampleTime; } else { double instSampleRate = (sampleTime - mPrevSampleTime) / (hostSecs - mPrevHostSecs); double smoothSampleRate = mSmoothSampleRate; smoothSampleRate = smoothSampleRate + 0.002 * (instSampleRate - smoothSampleRate); if (fabs(smoothSampleRate - mSampleRate) > 10.) { smoothSampleRate = mSampleRate; } mOSCincrement = (int64)(mOSCincrementNumerator / smoothSampleRate); mSmoothSampleRate = smoothSampleRate; #if 0 double avgSampleRate = (sampleTime - mStartSampleTime)/(hostSecs - mStartHostSecs); double jitter = (smoothSampleRate * (hostSecs - mPrevHostSecs)) - (sampleTime - mPrevSampleTime); double drift = (smoothSampleRate - mSampleRate) * (hostSecs - mStartHostSecs); #endif } mPrevHostSecs = hostSecs; mPrevSampleTime = sampleTime; #endif try { mFromEngine.Free(); mToEngine.Perform(); mOscPacketsToEngine.Perform(); int numInputs = mInputList->mSize; int numOutputs = mOutputList->mSize; jack_port_t **inPorts = mInputList->mPorts; jack_port_t **outPorts = mOutputList->mPorts; sc_jack_sample_t **inBuffers = mInputList->mBuffers; sc_jack_sample_t **outBuffers = mOutputList->mBuffers; int numSamples = NumSamplesPerCallback(); int bufFrames = mWorld->mBufLength; int numBufs = numSamples / bufFrames; float *inBuses = mWorld->mAudioBus + mWorld->mNumOutputs * bufFrames; float *outBuses = mWorld->mAudioBus; int32 *inTouched = mWorld->mAudioBusTouched + mWorld->mNumOutputs; int32 *outTouched = mWorld->mAudioBusTouched; int minInputs = sc_min(numInputs, (int)mWorld->mNumInputs); int minOutputs = sc_min(numOutputs, (int)mWorld->mNumOutputs); int bufFramePos = 0; // cache I/O buffers for (int i = 0; i < minInputs; ++i) { inBuffers[i] = (sc_jack_sample_t*)jack_port_get_buffer(inPorts[i], numSamples); } for (int i = 0; i < minOutputs; ++i) { outBuffers[i] = (sc_jack_sample_t*)jack_port_get_buffer(outPorts[i], numSamples); } // main loop #ifdef SC_JACK_USE_DLL int64 oscTime = mOSCbuftime = (int64)((mDLL.PeriodTime() - mMaxOutputLatency) * kSecondsToOSCunits + .5); // int64 oscInc = mOSCincrement = (int64)(mOSCincrementNumerator / mDLL.SampleRate()); int64 oscInc = mOSCincrement = (int64)((mDLL.Period() / numBufs) * kSecondsToOSCunits + .5); mSmoothSampleRate = mDLL.SampleRate(); double oscToSamples = mOSCtoSamples = mSmoothSampleRate * kOSCtoSecs /* 1/pow(2,32) */; #else int64 oscTime = mOSCbuftime = OSCTime(hostTime) - (int64)(mMaxOutputLatency * kSecondsToOSCunits + .5); int64 oscInc = mOSCincrement; double oscToSamples = mOSCtoSamples; #endif for (int i = 0; i < numBufs; ++i, mWorld->mBufCounter++, bufFramePos += bufFrames) { int32 bufCounter = mWorld->mBufCounter; int32 *tch; // copy+touch inputs tch = inTouched; for (int k = 0; k < minInputs; ++k) { sc_jack_sample_t *src = inBuffers[k] + bufFramePos; float *dst = inBuses + k * bufFrames; for (int n = 0; n < bufFrames; ++n) { *dst++ = *src++; } *tch++ = bufCounter; } // run engine int64 schedTime; int64 nextTime = oscTime + oscInc; while ((schedTime = mScheduler.NextTime()) <= nextTime) { float diffTime = (float)(schedTime - oscTime) * oscToSamples + 0.5; float diffTimeFloor = floor(diffTime); world->mSampleOffset = (int)diffTimeFloor; world->mSubsampleOffset = diffTime - diffTimeFloor; if (world->mSampleOffset < 0) world->mSampleOffset = 0; else if (world->mSampleOffset >= world->mBufLength) world->mSampleOffset = world->mBufLength-1; SC_ScheduledEvent event = mScheduler.Remove(); event.Perform(); } world->mSampleOffset = 0; world->mSubsampleOffset = 0.f; World_Run(world); // copy touched outputs tch = outTouched; for (int k = 0; k < minOutputs; ++k) { sc_jack_sample_t *dst = outBuffers[k] + bufFramePos; if (*tch++ == bufCounter) { float *src = outBuses + k * bufFrames; for (int n = 0; n < bufFrames; ++n) { *dst++ = *src++; } } else { for (int n = 0; n < bufFrames; ++n) { *dst++ = 0.0f; } } } // advance OSC time mOSCbuftime = oscTime = nextTime; } } catch (std::exception& exc) { scprintf("%s: exception in real time: %s\n", kJackDriverIdent, exc.what()); } catch (...) { scprintf("%s: unknown exception in real time\n", kJackDriverIdent); } double cpuUsage = (double)jack_cpu_load(mClient); mAvgCPU = mAvgCPU + 0.1 * (cpuUsage - mAvgCPU); if (cpuUsage > mPeakCPU || --mPeakCounter <= 0) { mPeakCPU = cpuUsage; mPeakCounter = mMaxPeakCounter; } mAudioSync.Signal(); }
//================================================================ int process(jack_nframes_t nframes, void *arg) { //fprintf(stderr,"."); //return 0; //if shutting down fill buffers with 0 and return if(shutdown_in_progress==1) { int i; for(i=0; i < output_port_count; i++) { sample_t *o1; o1=(sample_t*)jack_port_get_buffer(ioPortArray[i], nframes); //memset(o1, 0, bytes_per_sample*nframes); //always 4 bytes, 32 bit float memset(o1, 0, 4*nframes); } return 0; } if(process_enabled==1) { //if no data for this cycle(all channels) //is available(!), fill buffers with 0 or re-use old buffers and return if(jack_ringbuffer_read_space(rb) < port_count * bytes_per_sample*nframes) { int i; for(i=0; i < output_port_count; i++) { if(shutdown_in_progress==1 || process_enabled!=1) { return 0; } if(zero_on_underflow==1) { sample_t *o1; o1=(sample_t*)jack_port_get_buffer(ioPortArray[i], nframes); //memset(o1, 0, bytes_per_sample*nframes); //always 4 bytes, 32 bit float memset(o1, 0, 4*nframes); } print_info(); } multi_channel_drop_counter++; if(rebuffer_on_underflow==1) { pre_buffer_counter=0; process_enabled=0; } //reset avg calculation time_interval_avg=0; msg_received_counter=0; fscs_avg_counter=0; return 0; }//end not enough data available in ringbuffer process_cycle_counter++; if(process_cycle_counter>receive_max-1 && test_mode==1) { last_test_cycle=1; } //init to 0. increment before use fscs_avg_counter++; frames_since_cycle_start_sum+=frames_since_cycle_start; frames_since_cycle_start_avg=frames_since_cycle_start_sum/fscs_avg_counter; //check and reset after use if(fscs_avg_calc_interval>=fscs_avg_counter) { fscs_avg_counter=0; frames_since_cycle_start_sum=0; } //if sender sends more channels than we have output channels, ignore them int i; for(i=0; i<port_count; i++) { if(shutdown_in_progress==1 || process_enabled!=1) { return 0; } sample_t *o1; o1=(sample_t*)jack_port_get_buffer(ioPortArray[i], nframes); int16_t *o1_16; if(bytes_per_sample==2) { o1_16=malloc(bytes_per_sample*nframes); } //32 bit float if(bytes_per_sample==4) { jack_ringbuffer_read(rb, (char*)o1, bytes_per_sample*nframes); } //16 bit pcm else { jack_ringbuffer_read(rb, (char*)o1_16, bytes_per_sample*nframes); int x; for(x=0;x<nframes;x++) { o1[x]=(float)MIN_(MAX_((float)o1_16[x]/32760,-1.0f),1.0f); } free(o1_16); } /* fprintf(stderr,"\rreceiving from %s:%s", sender_host,sender_port ); */ print_info(); }//end for i < port_count //requested via /buffer, for test purposes (make buffer "tight") if(requested_drop_count>0) { uint64_t drop_bytes_count=requested_drop_count *port_count*period_size*bytes_per_sample; jack_ringbuffer_read_advance(rb,drop_bytes_count); requested_drop_count=0; multi_channel_drop_counter=0; } }//end if process_enabled==1 else //if process_enabled==0 { int i; for(i=0; i<port_count; i++) { if(shutdown_in_progress==1 || process_enabled!=1) { return 0; } sample_t *o1; o1=(sample_t*)jack_port_get_buffer(ioPortArray[i], nframes); //this is per channel, not per cycle. *port_count if(relaxed_display_counter>=update_display_every_nth_cycle*port_count || last_test_cycle==1 ) { //only for init if((int)message_number<=0 && starting_transmission==0) { if(shutup==0 && quiet==0) { fprintf(stderr,"\rwaiting for audio input data..."); } io_simple("/wait_for_input"); } else { if(shutup==0 && quiet==0) { fprintf(stderr,"\r# %" PRId64 " buffering... mc periods to go: %" PRId64 "%s", message_number, pre_buffer_size-pre_buffer_counter, "\033[0J" ); } if(io_()) { lo_message msgio=lo_message_new(); lo_message_add_int64(msgio,message_number); lo_message_add_int64(msgio,pre_buffer_size-pre_buffer_counter); lo_send_message(loio, "/buffering", msgio); lo_message_free(msgio); } } fflush(stderr); relaxed_display_counter=0; } relaxed_display_counter++; //set output buffer silent //memset(o1, 0, port_count*bytes_per_sample*nframes); //always 4 bytes, 32 bit float memset(o1, 0, port_count*4*nframes); }//end for i < port_count }//end process_enabled==0 //tasks independent of process_enabled 0/1 //if sender sends less channels than we have output channels, wee need to fill them with 0 if(input_port_count < output_port_count) { int i; for(i=0;i < (output_port_count-input_port_count);i++) { //sample_t *o1; //o1=(sample_t*) /////? jack_port_get_buffer(ioPortArray[input_port_count+i], nframes); } } if(last_test_cycle==1) { if(shutup==0) { fprintf(stderr,"\ntest finished after %" PRId64 " process cycles\n",process_cycle_counter); fprintf(stderr,"(waiting and buffering cycles not included)\n"); } io_simple_long("/test_finished",process_cycle_counter); shutdown_in_progress=1; } //simulate long cycle process duration //usleep(1000); frames_since_cycle_start=jack_frames_since_cycle_start(client); return 0; } //end process()
static int JackCallback( jack_nframes_t frames, void *userData ) { PaJackStream *stream = (PaJackStream*)userData; PaStreamCallbackTimeInfo timeInfo; int callbackResult; int chn; int framesProcessed; /* TODO: make this a lot more accurate */ PaTime now = GetStreamTime(stream); timeInfo.currentTime = now; timeInfo.outputBufferDacTime = now; timeInfo.inputBufferAdcTime = now; if( stream->t0 == -1 ) { if( stream->num_outgoing_connections == 0 ) { /* TODO: how to handle stream time for capture-only operation? */ } else { /* the beginning time needs to be initialized */ stream->t0 = jack_frame_time( stream->jack_client ) - jack_frames_since_cycle_start( stream->jack_client) + jack_port_get_total_latency( stream->jack_client, stream->local_output_ports[0] ); } } PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* @todo pass underflow/overflow flags when necessary */ ); for( chn = 0; chn < stream->num_incoming_connections; chn++ ) { jack_default_audio_sample_t *channel_buf; channel_buf = (jack_default_audio_sample_t*) jack_port_get_buffer( stream->local_input_ports[chn], frames ); PaUtil_SetNonInterleavedInputChannel( &stream->bufferProcessor, chn, channel_buf ); } for( chn = 0; chn < stream->num_outgoing_connections; chn++ ) { jack_default_audio_sample_t *channel_buf; channel_buf = (jack_default_audio_sample_t*) jack_port_get_buffer( stream->local_output_ports[chn], frames ); PaUtil_SetNonInterleavedOutputChannel( &stream->bufferProcessor, chn, channel_buf ); } if( stream->num_incoming_connections > 0 ) PaUtil_SetInputFrameCount( &stream->bufferProcessor, frames ); if( stream->num_outgoing_connections > 0 ) PaUtil_SetOutputFrameCount( &stream->bufferProcessor, frames ); callbackResult = paContinue; framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); stream->total_frames_sent += frames; if( callbackResult == paContinue ) { /* nothing special */ } else if( callbackResult == paAbort ) { /* finish playback immediately */ /* TODO: memset 0 the outgoing samples to "cancel" them */ stream->is_active = FALSE; /* return nonzero so we get deactivated (and the callback won't * get called again) */ return 1; } else { /* User callback has asked us to stop with paComplete or other non-zero value. */ stream->is_active = FALSE; /* return nonzero so we get deactivated (and the callback won't * get called again) */ return 1; } return 0; }
jack_nframes_t luajack_frames_since_cycle_start(luajack_t *client) { cud_t *cud = get_cud(client); if(!cud) return 0; return jack_frames_since_cycle_start(cud->client); }