/***************************************************************************** * * PROCESS_BUFFER() * * callback for jack to read audio data from ringbuffer * jack uses a realtime priority thread for this * *****************************************************************************/ int process_buffer(jack_nframes_t nframes, void *arg) { jack_default_audio_sample_t *in1; jack_default_audio_sample_t *in2; jack_default_audio_sample_t *out1; jack_default_audio_sample_t *out2; int portion1; int portion2; int j, osc, lfo; /* Get the input and output buffers for audio data */ in1 = jack_port_get_buffer (input_port1, nframes); in2 = jack_port_get_buffer (input_port2, nframes); out1 = jack_port_get_buffer (output_port1, nframes); out2 = jack_port_get_buffer (output_port2, nframes); /* check for jack not running or global shutdown so we don't get hung on mutex */ if (!jack_running || shutdown) { return 0; } /* copy circular buffers and update index */ /* WARNING: needs to handle circular buffer properly */ if ((buffer_read_index + nframes) > buffer_size) { portion1 = buffer_size - buffer_read_index; portion2 = nframes - portion1; memcpy (&(input_buffer1[buffer_read_index]), in1, sizeof (jack_default_audio_sample_t) * portion1); memcpy (&(input_buffer2[buffer_read_index]), in2, sizeof (jack_default_audio_sample_t) * portion1); memcpy (input_buffer1, &(in1[portion1]), sizeof (jack_default_audio_sample_t) * portion2); memcpy (input_buffer2, &(in2[portion1]), sizeof (jack_default_audio_sample_t) * portion2); memcpy (out1, &(output_buffer1[buffer_read_index]), sizeof (jack_default_audio_sample_t) * portion1); memcpy (out2, &(output_buffer2[buffer_read_index]), sizeof (jack_default_audio_sample_t) * portion1); memcpy (&(out1[portion1]), output_buffer1, sizeof (jack_default_audio_sample_t) * portion2); memcpy (&(out2[portion1]), output_buffer2, sizeof (jack_default_audio_sample_t) * portion2); } else { memcpy (&(input_buffer1[buffer_read_index]), in1, sizeof (jack_default_audio_sample_t) * nframes); memcpy (&(input_buffer2[buffer_read_index]), in2, sizeof (jack_default_audio_sample_t) * nframes); memcpy (out1, &(output_buffer1[buffer_read_index]), sizeof (jack_default_audio_sample_t) * nframes); memcpy (out2, &(output_buffer2[buffer_read_index]), sizeof (jack_default_audio_sample_t) * nframes); } buffer_read_index += nframes; buffer_read_index %= buffer_size; /* Update buffer full and buffer needed counters */ buffer_full -= nframes; if(setting_jack_transport_mode) { /* jack transport sync */ jack_state = jack_transport_query (client, &jack_pos); /* reinit sync vars if transport is just started */ if((jack_prev_state == JackTransportStopped) && (jack_state == JackTransportStarting)) { #ifdef EXTRA_DEBUG if (debug) { fprintf (stderr, "Starting sync.\n"); } #endif need_resync = 1; } if(jack_state == JackTransportRolling) { if(need_resync) { prev_frame = jack_pos.frame - nframes; frames_per_beat = sample_rate / global.bps; frames_per_tick = sample_rate / (jack_pos.ticks_per_beat * global.bps); current_frame = 0; need_resync = 0; } /* Handle BPM change */ if(jack_pos.beats_per_minute && (global.bps != jack_pos.beats_per_minute / 60.0)) { param[0].cc_val[program_number] = jack_pos.beats_per_minute - 64; param[0].callback (main_window, (gpointer)(&(param[0]))); } /* frame-based sync */ current_frame = (jack_pos.frame - prev_frame); prev_frame = jack_pos.frame; if(current_frame != nframes) { /* whoooaaaa, we're traveling through time! */ phase_correction = current_frame - nframes; } /* do the actual sync */ if(phase_correction && (setting_jack_transport_mode == JACK_TRANSPORT_TNP)) { #ifdef EXTRA_DEBUG if (debug) { fprintf (stderr, "Out of sync. Phase correction: %d\n", phase_correction); } #endif part.delay_write_index += phase_correction; while(part.delay_write_index < 0.0) part.delay_write_index += part.delay_bufsize; while(part.delay_write_index >= part.delay_bufsize) part.delay_write_index -= part.delay_bufsize; part.chorus_lfo_index_a += phase_correction * part.chorus_lfo_adjust; while(part.chorus_lfo_index_a < 0.0) part.chorus_lfo_index_a += F_WAVEFORM_SIZE; while(part.chorus_lfo_index_a >= F_WAVEFORM_SIZE) part.chorus_lfo_index_a -= F_WAVEFORM_SIZE; part.chorus_lfo_index_b = part.chorus_lfo_index_a + (F_WAVEFORM_SIZE * 0.25); while(part.chorus_lfo_index_b < 0.0) part.chorus_lfo_index_b += F_WAVEFORM_SIZE; while(part.chorus_lfo_index_b >= F_WAVEFORM_SIZE) part.chorus_lfo_index_b -= F_WAVEFORM_SIZE; part.chorus_lfo_index_c = part.chorus_lfo_index_a + (F_WAVEFORM_SIZE * 0.5); while(part.chorus_lfo_index_c < 0.0) part.chorus_lfo_index_c += F_WAVEFORM_SIZE; while(part.chorus_lfo_index_c >= F_WAVEFORM_SIZE) part.chorus_lfo_index_c -= F_WAVEFORM_SIZE; part.chorus_lfo_index_d = part.chorus_lfo_index_a + (F_WAVEFORM_SIZE * 0.75); while(part.chorus_lfo_index_d < 0.0) part.chorus_lfo_index_d += F_WAVEFORM_SIZE; while(part.chorus_lfo_index_d >= F_WAVEFORM_SIZE) part.chorus_lfo_index_d -= F_WAVEFORM_SIZE; for (osc = 0; osc < NUM_OSCS; osc++) { for (j = 0; j < setting_polyphony; j++) { if (patch->osc_freq_base[osc] >= FREQ_BASE_TEMPO) { switch (patch->freq_mod_type[osc]) { case MOD_TYPE_LFO: tmp_1 = part.lfo_out[patch->freq_lfo[osc]]; break; case MOD_TYPE_OSC: tmp_1 = ( voice[j].osc_out1[part.osc_freq_mod[osc]] + voice[j].osc_out2[part.osc_freq_mod[osc]] ) * 0.5; break; case MOD_TYPE_VELOCITY: tmp_1 = voice[j].velocity_coef_linear; break; default: tmp_1 = 0.0; break; } voice[j].index[osc] += phase_correction * halfsteps_to_freq_mult ( ( tmp_1 * patch->freq_lfo_amount[osc] ) + part.osc_pitch_bend[osc] + patch->osc_transpose[osc] ) * voice[j].osc_freq[osc] * wave_period; while (voice[j].index[osc] < 0.0) voice[j].index[osc] += F_WAVEFORM_SIZE; while (voice[j].index[osc] >= F_WAVEFORM_SIZE) voice[j].index[osc] -= F_WAVEFORM_SIZE; } } } for (lfo = 0; lfo < NUM_LFOS; lfo++) { if (patch->lfo_freq_base[lfo] >= FREQ_BASE_TEMPO) { part.lfo_index[lfo] += phase_correction * part.lfo_freq[lfo] * halfsteps_to_freq_mult (patch->lfo_transpose[lfo] + part.lfo_pitch_bend[lfo]) * wave_period; while (part.lfo_index[lfo] < 0.0) part.lfo_index[lfo] += F_WAVEFORM_SIZE; while (part.lfo_index[lfo] >= F_WAVEFORM_SIZE) part.lfo_index[lfo] -= F_WAVEFORM_SIZE; } } phase_correction = 0; } } else if ((jack_state == JackTransportStopped) && (jack_prev_state == JackTransportRolling)) { /* send NOTE_OFFs on transport stop */ engine_notes_off(); } jack_prev_state = jack_state; } /* if(setting_jack_transport) */ /* Signal the engine that there's space again */ if (pthread_mutex_trylock (&buffer_mutex) == 0) { pthread_cond_broadcast (&buffer_has_space); pthread_mutex_unlock (&buffer_mutex); } return 0; }
void ModeHandler::process(const jack_nframes_t nframes) { void* inPortBuf = jack_port_get_buffer(m_inputPort, nframes); void* outPortBuf = jack_port_get_buffer(m_outputPort, nframes); jack_midi_clear_buffer(outPortBuf); const jack_transport_state_t state = jack_transport_query(m_client, nullptr); const jack_nframes_t eventCount = jack_midi_get_event_count(inPortBuf); for (size_t i = 0; i < eventCount; ++i) { jack_midi_event_t inEvent; jack_midi_event_get(&inEvent, inPortBuf, i); const jack_midi_data_t cmd = inEvent.buffer[0] & 0xf0; switch (cmd) { case MIDI_NOTEON: case MIDI_NOTEOFF: { const jack_midi_data_t note = inEvent.buffer[1]; const jack_midi_data_t velocity = inEvent.buffer[2]; int noteToUse = note; // we only map on a note on // on a note off we use the previously mapped note // and only is transport is rolling if (cmd == MIDI_NOTEON && velocity > 0) { if (state == JackTransportRolling) { const int newNote = transpose(m_offset, m_quirkOffset, note); m_mappedNotes[note] = newNote; noteToUse = newNote; } } else { // NOTEOFF if (m_mappedNotes[note] != NOT_MAPPED) { noteToUse = m_mappedNotes[note]; m_mappedNotes[note] = NOT_MAPPED; } } if (noteToUse != SKIP) { jack_midi_data_t data[3]; data[0] = inEvent.buffer[0]; data[1] = noteToUse;; data[2] = inEvent.buffer[2]; jack_midi_event_write(outPortBuf, inEvent.time, data, 3); } break; } default: { // just forward everything else jack_midi_event_write(outPortBuf, inEvent.time, inEvent.buffer, inEvent.size); } } } }
static void do_jack(jack_client_t* client, char *button, int bk) { black_keys = bk; // Jack Transport if (button[0] && (button[0] != button_prev_val[0])) //select :: Change octave { if (oct == 0) { oct = 1; //C6 if (debug) printf("STAT: Changed base octave to C6\n"); } else if (oct == 1) { oct = 2; //C7 if (debug) printf("STAT: Changed base octave to C7\n"); } else if (oct == 2) { oct = -2; //C3 if (debug) printf("STAT: Changed base octave to C3\n"); } else if (oct == -2) { oct = -1; //C4 if (debug) printf("STAT: Changed base octave to C4\n"); } else { oct = 0; //C5 if (debug) printf("STAT: Changed base octave to C5\n"); } } else if (button[3] && (button[3] != button_prev_val[3])) //start :: Start/Stop Transport { state = jack_transport_query(client, NULL); if (state) { jack_transport_stop(client); if (debug) printf("STAT: Transport stopped\n"); } else { jack_transport_start(client); if (debug) printf("STAT: Transport started\n"); } } if (button[16] && (button[16] != button_prev_val[16])) //PS :: Panic button { if (debug) printf("STAT: Panic!\n"); jack_transport_stop(client); jack_transport_locate(client, 0); jack_transport_stop(client); int h; for (h=8; h<20; h++) { axis[h] = 0; axis_prev_velocity[h] = 0; } } else if (button[1]) //L3 :: Move transport backwards { jack_transport_query(client, &position); int prevJackFrame = position.frame - 100000; if (prevJackFrame < 0) prevJackFrame = 0; jack_transport_locate(client, prevJackFrame); if (button[1] != button_prev_val[1]) if (debug) printf("STAT: Transport backwards...\n"); } else if (button[1] != button_prev_val[1]) { if (debug) printf("STAT: Transport normal\n"); } else if (button[2]) //R3 :: Move transport forwards { jack_transport_query(client, &position); int nextJackFrame = position.frame + 100000; jack_transport_locate(client, nextJackFrame); if (button[2] != button_prev_val[2]) if (debug) printf("STAT: Transport forwards...\n"); } else if (button[2] != button_prev_val[2]) { if (debug) printf("STAT: Transport normal\n"); } button_prev_val[0] = button[0]; button_prev_val[1] = button[1]; button_prev_val[2] = button[2]; button_prev_val[3] = button[3]; button_prev_val[16] = button[16]; }
int drumkv1_jack::process ( jack_nframes_t nframes ) { if (!m_activated) return 0; const uint16_t nchannels = drumkv1::channels(); float *ins[nchannels], *outs[nchannels]; for (uint16_t k = 0; k < nchannels; ++k) { ins[k] = static_cast<float *> ( ::jack_port_get_buffer(m_audio_ins[k], nframes)); outs[k] = static_cast<float *> ( ::jack_port_get_buffer(m_audio_outs[k], nframes)); } jack_position_t pos; jack_transport_query(m_client, &pos); if (pos.valid & JackPositionBBT) { const float host_bpm = float(pos.beats_per_minute); if (paramValue(drumkv1::LFO1_BPMSYNC) > 0.0f) { #ifdef CONFIG_LFO_BPMRATEX_0 const float rate_bpm = lfo_rate_bpm(host_bpm); const float rate = paramValue(drumkv1::LFO1_RATE); if (::fabsf(rate_bpm - rate) > 0.01f) setParamValue(drumkv1::LFO1_RATE, rate_bpm); #else const float bpm = paramValue(drumkv1::LFO1_BPM); if (::fabsf(host_bpm - bpm) > 0.01f) setParamValue(drumkv1::LFO1_BPM, host_bpm); #endif } if (paramValue(drumkv1::DEL1_BPMSYNC) > 0.0f) { const float bpm = paramValue(drumkv1::DEL1_BPM); if (bpm > 0.0f && ::fabsf(host_bpm - bpm) > 0.01f) setParamValue(drumkv1::DEL1_BPM, host_bpm); } } uint32_t ndelta = 0; #ifdef CONFIG_JACK_MIDI void *midi_in = ::jack_port_get_buffer(m_midi_in, nframes); if (midi_in) { const uint32_t nevents = ::jack_midi_get_event_count(midi_in); for (uint32_t n = 0; n < nevents; ++n) { jack_midi_event_t event; ::jack_midi_event_get(&event, midi_in, n); if (event.time > ndelta) { const uint32_t nread = event.time - ndelta; if (nread > 0) { drumkv1::process(ins, outs, nread); for (uint16_t k = 0; k < nchannels; ++k) { ins[k] += nread; outs[k] += nread; } } } ndelta = event.time; drumkv1::process_midi(event.buffer, event.size); } } #endif #ifdef CONFIG_ALSA_MIDI const jack_nframes_t buffer_size = ::jack_get_buffer_size(m_client); const jack_nframes_t frame_time = ::jack_last_frame_time(m_client); uint8_t event_buffer[1024]; jack_midi_event_t event; while (::jack_ringbuffer_peek(m_alsa_buffer, (char *) &event, sizeof(event)) == sizeof(event)) { if (event.time > frame_time) break; jack_nframes_t event_time = frame_time - event.time; if (event_time > buffer_size) event_time = 0; else event_time = buffer_size - event_time; if (event_time > ndelta) { const uint32_t nread = event_time - ndelta; if (nread > 0) { drumkv1::process(ins, outs, nread); for (uint16_t k = 0; k < nchannels; ++k) { ins[k] += nread; outs[k] += nread; } } ndelta = event_time; } ::jack_ringbuffer_read_advance(m_alsa_buffer, sizeof(event)); ::jack_ringbuffer_read(m_alsa_buffer, (char *) event_buffer, event.size); drumkv1::process_midi(event_buffer, event.size); } #endif // CONFIG_ALSA_MIDI if (nframes > ndelta) drumkv1::process(ins, outs, nframes - ndelta); return 0; }
int samplv1_jack::process ( jack_nframes_t nframes ) { if (!m_activated) return 0; const uint16_t nchannels = samplv1::channels(); float *ins[nchannels], *outs[nchannels]; for (uint16_t k = 0; k < nchannels; ++k) { ins[k] = static_cast<float *> ( ::jack_port_get_buffer(m_audio_ins[k], nframes)); outs[k] = static_cast<float *> ( ::jack_port_get_buffer(m_audio_outs[k], nframes)); } jack_position_t pos; jack_transport_query(m_client, &pos); if (pos.valid & JackPositionBBT) { const float host_bpm = float(pos.beats_per_minute); if (::fabsf(host_bpm - samplv1::tempo()) > 0.001f) samplv1::setTempo(host_bpm); } uint32_t ndelta = 0; #ifdef CONFIG_JACK_MIDI void *midi_in = ::jack_port_get_buffer(m_midi_in, nframes); if (midi_in) { const uint32_t nevents = ::jack_midi_get_event_count(midi_in); for (uint32_t n = 0; n < nevents; ++n) { jack_midi_event_t event; ::jack_midi_event_get(&event, midi_in, n); if (event.time > ndelta) { const uint32_t nread = event.time - ndelta; if (nread > 0) { samplv1::process(ins, outs, nread); for (uint16_t k = 0; k < nchannels; ++k) { ins[k] += nread; outs[k] += nread; } } } ndelta = event.time; samplv1::process_midi(event.buffer, event.size); } } #endif #ifdef CONFIG_ALSA_MIDI const jack_nframes_t buffer_size = ::jack_get_buffer_size(m_client); const jack_nframes_t frame_time = ::jack_last_frame_time(m_client); uint8_t event_buffer[1024]; jack_midi_event_t event; while (::jack_ringbuffer_peek(m_alsa_buffer, (char *) &event, sizeof(event)) == sizeof(event)) { if (event.time > frame_time) break; jack_nframes_t event_time = frame_time - event.time; if (event_time > buffer_size) event_time = 0; else event_time = buffer_size - event_time; if (event_time > ndelta) { const uint32_t nread = event_time - ndelta; if (nread > 0) { samplv1::process(ins, outs, nread); for (uint16_t k = 0; k < nchannels; ++k) { ins[k] += nread; outs[k] += nread; } } ndelta = event_time; } ::jack_ringbuffer_read_advance(m_alsa_buffer, sizeof(event)); ::jack_ringbuffer_read(m_alsa_buffer, (char *) event_buffer, event.size); samplv1::process_midi(event_buffer, event.size); } #endif // CONFIG_ALSA_MIDI if (nframes > ndelta) samplv1::process(ins, outs, nframes - ndelta); return 0; }
void JackAudioDriver::updateTransportInfo() { // The following four lines do only cover the special case of // audioEngine_updateNoteQueue() returning -1 in // audioEngine_process() and triggering an addition relocation // of the transport position to the beginning of the song // using locateInNCycles() to possibly keep synchronization // with Ardour. if ( locate_countdown == 1 ) locate( locate_frame ); if ( locate_countdown > 0 ) locate_countdown--; if ( Preferences::get_instance()->m_bJackTransportMode != Preferences::USE_JACK_TRANSPORT ){ return; } // jack_transport_query() (jack/transport.h) queries the // current transport state and position. If called from the // process thread, the second argument, which is a pointer to // a structure for returning current transport, corresponds to // the first frame of the current cycle and the state returned // is valid for the entire cycle. #m_JackTransportPos->valid // will show which fields contain valid data. If // #m_JackTransportPos is NULL, do not return position // information. m_JackTransportState = jack_transport_query( m_pClient, &m_JackTransportPos ); // WARNINGLOG( QString( "[Jack-Query] state: %1, frame: %2, position bit: %3, bpm: %4" ) // .arg( m_JackTransportState ) // .arg( m_JackTransportPos.frame ) // .arg( m_JackTransportPos.valid ) // .arg( m_JackTransportPos.beats_per_minute ) ); // Update the TransportInfo in m_transport based on the state // returned by jack_transport_query. switch ( m_JackTransportState ) { case JackTransportStopped: // Transport is halted m_transport.m_status = TransportInfo::STOPPED; //INFOLOG( "[updateTransportInfo] STOPPED - frames: " + to_string(m_transportPos.frame) ); break; case JackTransportRolling: // Transport is playing // If the valid member matches the JackPositionBBT // bits, there is a JACK timebase master present // supplying bar, beat, and tick information along // with the plain transport position in frames. We // will use it for re-positioning. But only at the end // of the next cycle, because this timebase master has // to properly initialize its state during this cycle // too. if ( m_transport.m_status != TransportInfo::ROLLING && ( m_JackTransportPos.valid & JackPositionBBT ) ) { must_relocate = 2; //WARNINGLOG( "Jack transport starting: Resyncing in 2 x Buffersize!!" ); } m_transport.m_status = TransportInfo::ROLLING; //INFOLOG( "[updateTransportInfo] ROLLING - frames: " + to_string(m_transportPos.frame) ); break; case JackTransportStarting: // Waiting for sync ready. If // there are slow-sync clients, // this can take more than one // cycle. m_transport.m_status = TransportInfo::STOPPED; //INFOLOG( "[updateTransportInfo] STARTING (stopped) - frames: " + to_string(m_transportPos.frame) ); break; default: ERRORLOG( "Unknown jack transport state" ); } // Updating TickSize and BPM Hydrogen * H = Hydrogen::get_instance(); // JACK may have re-located us anywhere. Therefore, we have to // check for a bpm change every cycle. We will do so by // updating both the global speed of the song, as well as the // fallback speed (H->getNewBpmJTM) with the local tempo at // the current position on the timeline. H->setTimelineBpm(); // Check whether another JACK master is present (the second if // clause won't evaluate to true if Hydrogen itself is the // timebase master) and whether it changed the transport // state. Note that only the JACK timebase master and not // arbitrary clients can do this. If so, the speed is updated // an a relocation according to the bar, beat, and tick // information will be triggered right away. if ( m_JackTransportPos.valid & JackPositionBBT ) { float bpm = ( float )m_JackTransportPos.beats_per_minute; if ( m_transport.m_nBPM != bpm ) { if ( Preferences::get_instance()->m_bJackMasterMode == Preferences::NO_JACK_TIME_MASTER ){ // WARNINGLOG( QString( "Tempo change from jack-transport: %1" ).arg( bpm ) ); m_transport.m_nBPM = bpm; must_relocate = 1; } } } // This clause detects a re-location, which was either // triggered by an user-interaction (e.g. clicking the forward // button or clicking somewhere on the timeline) or by a // different JACK client. if ( m_transport.m_nFrames + bbt_frame_offset != m_JackTransportPos.frame ) { // Whether there is a JACK timebase master registered // but the audio engine was neither (re)started nor a // change in the speed was found beforehand. if ( ( m_JackTransportPos.valid & JackPositionBBT ) && must_relocate == 0 ) { // WARNINGLOG( "Frame offset mismatch; triggering resync in 2 cycles" ); must_relocate = 2; // re-locate at the end of // the next cycle. } else { if ( Preferences::get_instance()->m_bJackMasterMode == Preferences::NO_JACK_TIME_MASTER ) { // Just consider the transport // position as dirty, reassign the // one obtained in the query, and // reset the offset. m_transport.m_nFrames = m_JackTransportPos.frame; bbt_frame_offset = 0; // If not registered as JACK time // master, the following line is // needed to be able to relocate by // clicking the song ruler (wired // corner case, but still...) if ( m_transport.m_status == TransportInfo::ROLLING ) // Setting // H->m_nPatternStartTick = -1 // if in pattern mode. H->triggerRelocateDuringPlay(); } else { // Fallback to the transport position // of the last cycle. m_transport.m_nFrames = H->getHumantimeFrames(); } } } // Only used if Hydrogen is in JACK timebase master // mode. Updated every cycle. if ( H->getHumantimeFrames() != m_JackTransportPos.frame ) { H->setHumantimeFrames(m_JackTransportPos.frame); // WARNINGLOG( QString( "fix Humantime: %1" ).arg( m_JackTransportPos.frame ) ); } // Trigger the re-location based on the information provided // by another JACK timebase master. if ( must_relocate == 1 ) { WARNINGLOG( QString( "Resyncing! bbt_frame_offset: %1" ).arg( bbt_frame_offset ) ); relocateBBT(); if ( m_transport.m_status == TransportInfo::ROLLING ) { // Setting H->m_nPatternStartTick = -1 if in // pattern mode H->triggerRelocateDuringPlay(); } } if ( must_relocate > 0 ) must_relocate--; }
/** * The process callback for this JACK application is called in a * special realtime thread once for each audio cycle. * * This client follows a simple rule: when the JACK transport is * running, copy the input port to the output. When it stops, exit. */ int my_jack_process (jack_nframes_t nframes, void * arg) { extern FILE * err_log; static long play_cur = 0; static jack_default_audio_sample_t * in = NULL; static jack_nframes_t in_len = 0; static double * left = NULL; static double * right = NULL; static int hop_res0 = 0; static int cur = 0; jack_transport_state_t ts; jack_default_audio_sample_t * out = NULL; struct pv_jack * pv_jack = (struct pv_jack *) arg; if (in == NULL) { in = (jack_default_audio_sample_t *)malloc (sizeof (jack_default_audio_sample_t) * nframes); in_len = nframes; } else if (in_len < nframes) { in = (jack_default_audio_sample_t *)realloc (in, sizeof (jack_default_audio_sample_t) * nframes); in_len = nframes; } if (left == NULL) { left = (double *)malloc (sizeof (double) * pv_jack->pv->hop_res); right = (double *)malloc (sizeof (double) * pv_jack->pv->hop_res); CHECK_MALLOC (left, "my_jack_process"); CHECK_MALLOC (right, "my_jack_process"); hop_res0 = pv_jack->pv->hop_res; cur = hop_res0; /* no data left */ } else if (hop_res0 < pv_jack->pv->hop_res) { left = (double *)realloc (left, sizeof (double) * pv_jack->pv->hop_res); right = (double *)realloc (right, sizeof (double) * pv_jack->pv->hop_res); CHECK_MALLOC (left, "my_jack_process"); CHECK_MALLOC (right, "my_jack_process"); hop_res0 = pv_jack->pv->hop_res; cur = hop_res0; /* no data left */ } /* check */ fprintf (err_log, "cur = %d\n", cur); ts = jack_transport_query (pv_jack->client, NULL); if (ts == JackTransportRolling) { int i, j; if (pv_jack->state == Init) { pv_jack->state = Run; } out = (jack_default_audio_sample_t *) jack_port_get_buffer (pv_jack->out, nframes); /* the process for nframes */ /* paste the data left in left[] and right[] from cur to hop_res0 */ for (i = 0, j = cur; (j < hop_res0) && (i < (int) nframes); i ++, j ++) { in[i] = (jack_default_audio_sample_t)(0.5 * (left[j] + right[j])); } cur = j; cur = cur % hop_res0; /* if the data is not enough, */ while (i < (int) nframes) { int j; /* process further data (next hop_res frames) */ if (cur != 0) { fprintf (stderr, "cur update failed...\n"); fprintf (err_log, "cur update failed...\n"); fprintf (err_log, "cur = %d\n", cur); fclose (err_log); exit (1); } jack_pv_complex_play_step (pv_jack->pv, play_cur, left, right); play_cur += pv_jack->pv->hop_ana; /* then, paste the data in left[] and right[] from 0 to hop_res0 */ for (j = 0; (j < hop_res0) && (i < (int) nframes); j ++, i ++) { in[i] = (jack_default_audio_sample_t)(0.5 * (left[j] + right[j])); } cur = j; cur = cur % hop_res0; } /* now, the data in in[] is enough to pass jack server */ memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes); } else if (ts == JackTransportStopped) { if (pv_jack->state == Run) { pv_jack->state = Exit; } } return 0; }
static int process_cb(jack_nframes_t nframes, void *arg) { struct cbox_jack_io_impl *jii = arg; struct cbox_io *io = jii->ioi.pio; struct cbox_io_callbacks *cb = io->cb; io->io_env.buffer_size = nframes; for (int i = 0; i < io->io_env.input_count; i++) io->input_buffers[i] = jack_port_get_buffer(jii->inputs[i], nframes); for (int i = 0; i < io->io_env.output_count; i++) { io->output_buffers[i] = jack_port_get_buffer(jii->outputs[i], nframes); if (!io->output_buffers[i]) continue; for (int j = 0; j < nframes; j ++) io->output_buffers[i][j] = 0.f; } for (GSList *p = io->midi_inputs; p; p = p->next) { struct cbox_jack_midi_input *input = p->data; if (input->hdr.output_set || input->hdr.enable_appsink) { copy_midi_data_to_buffer(input->port, io->io_env.buffer_size, &input->hdr.buffer); if (input->hdr.enable_appsink) cbox_midi_appsink_supply(&input->hdr.appsink, &input->hdr.buffer); } else cbox_midi_buffer_clear(&input->hdr.buffer); } if (cb->on_transport_sync) { jack_transport_state_t state = jack_transport_query(jii->client, NULL); if (state != jii->last_transport_state) { jack_position_t pos; jack_transport_query(jii->client, &pos); if (jii->debug_transport) g_message("JACK transport: incoming state change, state = %s, last state = %s, pos = %d\n", transport_state_names[state], transport_state_names[(int)jii->last_transport_state], (int)pos.frame); if (state == JackTransportStopped) { if (cb->on_transport_sync(cb->user_data, ts_stopping, pos.frame)) jii->last_transport_state = state; } else if (state == JackTransportRolling && jii->last_transport_state == JackTransportStarting) { if (cb->on_transport_sync(cb->user_data, ts_rolling, pos.frame)) jii->last_transport_state = state; } else jii->last_transport_state = state; } } cb->process(cb->user_data, io, nframes); for (int i = 0; i < io->io_env.input_count; i++) io->input_buffers[i] = NULL; for (int i = 0; i < io->io_env.output_count; i++) io->output_buffers[i] = NULL; for (GSList *p = io->midi_outputs; p; p = g_slist_next(p)) { struct cbox_jack_midi_output *midiout = p->data; void *pbuf = jack_port_get_buffer(midiout->port, nframes); jack_midi_clear_buffer(pbuf); cbox_midi_merger_render(&midiout->hdr.merger); if (midiout->hdr.buffer.count) { uint8_t tmp_data[4]; for (int i = 0; i < midiout->hdr.buffer.count; i++) { const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&midiout->hdr.buffer, i); const uint8_t *pdata = cbox_midi_event_get_data(event); if ((pdata[0] & 0xF0) == 0x90 && !pdata[2] && event->size == 3) { tmp_data[0] = pdata[0] & ~0x10; tmp_data[1] = pdata[1]; tmp_data[2] = pdata[2]; pdata = tmp_data; } if (jack_midi_event_write(pbuf, event->time, pdata, event->size)) { g_warning("MIDI buffer overflow on JACK output port '%s'", midiout->hdr.name); break; } } } } return 0; }
float AUD_JackDevice::getPlaybackPosition() { jack_position_t position; jack_transport_query(m_client, &position); return position.frame / (float) m_specs.rate; }
void jack_assistant::position (bool songmode, midipulse tick) { #ifdef SEQ64_JACK_SUPPORT long current_tick = 0; if (songmode) /* master in song mode */ { if (! is_null_midipulse(tick)) current_tick = tick * 10; } int ticks_per_beat = m_ppqn * 10; int beats_per_minute = parent().get_beats_per_minute(); uint64_t tick_rate = (uint64_t(m_jack_frame_rate) * current_tick * 60.0); long tpb_bpm = ticks_per_beat * beats_per_minute * 4.0 / m_beat_width; uint64_t jack_frame = tick_rate / tpb_bpm; jack_transport_locate(m_jack_client, jack_frame); #ifdef SEQ64_STAZED_JACK_SUPPORT #if 0 /* * The call to jack_BBT_position() is not necessary to change JACK * position! Must set these here since they are set in timebase. */ jack_position_t pos; double jack_tick = current_tick * m_bw / 4.0; pos.ticks_per_beat = m_ppqn * 10; pos.beats_per_minute = m_master_bus.get_bpm(); jack_BBT_position(pos, jack_tick); /* * Calculate JACK frame to put into pos.frame; it is what matters for * position change. Very similar to the uncommented code above. */ uint64_t tick_rate = ((uint64_t)pos.frame_rate * current_tick * 60.0); long tpb_bpm = pos.ticks_per_beat * pos.beats_per_minute * 4.0 / pos.beat_type; pos.frame = tick_rate / tpb_bpm; /* * ticks * 10 = jack ticks; * jack ticks / ticks per beat = num beats; * num beats / beats per minute = num minutes * num minutes * 60 = num seconds * num secords * frame_rate = frame */ jack_transport_reposition(m_jack_client, &pos); #endif // 0 if (parent().is_running()) parent().set_reposition(false); #endif // SEQ64_STAZED_JACK_SUPPORT Tutorial code from jack_assistant::position(): #ifdef SAMPLE_AUDIO_CODE // disabled, shown only for reference & learning jack_transport_state_t ts = jack_transport_query(jack->client(), NULL); if (ts == JackTransportRolling) { jack_default_audio_sample_t * in; jack_default_audio_sample_t * out; if (client_state == Init) client_state = Run; in = jack_port_get_buffer(input_port, nframes); out = jack_port_get_buffer(output_port, nframes); memcpy(out, in, sizeof (jack_default_audio_sample_t) * nframes); } else if (ts == JackTransportStopped) { if (client_state == Run) client_state = Exit; } #endif }
AUD_JackDevice::AUD_JackDevice(std::string name, AUD_DeviceSpecs specs, int buffersize) { if(specs.channels == AUD_CHANNELS_INVALID) specs.channels = AUD_CHANNELS_STEREO; // jack uses floats m_specs = specs; m_specs.format = AUD_FORMAT_FLOAT32; jack_options_t options = JackNullOption; jack_status_t status; // open client m_client = jack_client_open(name.c_str(), options, &status); if(m_client == NULL) AUD_THROW(AUD_ERROR_JACK, clientopen_error); // set callbacks jack_set_process_callback(m_client, AUD_JackDevice::jack_mix, this); jack_on_shutdown(m_client, AUD_JackDevice::jack_shutdown, this); jack_set_sync_callback(m_client, AUD_JackDevice::jack_sync, this); // register our output channels which are called ports in jack m_ports = new jack_port_t*[m_specs.channels]; try { char portname[64]; for(int i = 0; i < m_specs.channels; i++) { sprintf(portname, "out %d", i+1); m_ports[i] = jack_port_register(m_client, portname, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if(m_ports[i] == NULL) AUD_THROW(AUD_ERROR_JACK, port_error); } } catch(AUD_Exception&) { jack_client_close(m_client); delete[] m_ports; throw; } m_specs.rate = (AUD_SampleRate)jack_get_sample_rate(m_client); buffersize *= sizeof(sample_t); m_ringbuffers = new jack_ringbuffer_t*[specs.channels]; for(unsigned int i = 0; i < specs.channels; i++) m_ringbuffers[i] = jack_ringbuffer_create(buffersize); buffersize *= specs.channels; m_deinterleavebuf.resize(buffersize); m_buffer.resize(buffersize); create(); m_valid = true; m_sync = 0; m_syncFunc = NULL; m_nextState = m_state = jack_transport_query(m_client, NULL); pthread_mutex_init(&m_mixingLock, NULL); pthread_cond_init(&m_mixingCondition, NULL); // activate the client if(jack_activate(m_client)) { jack_client_close(m_client); delete[] m_ports; for(unsigned int i = 0; i < specs.channels; i++) jack_ringbuffer_free(m_ringbuffers[i]); delete[] m_ringbuffers; pthread_mutex_destroy(&m_mixingLock); pthread_cond_destroy(&m_mixingCondition); destroy(); AUD_THROW(AUD_ERROR_JACK, activate_error); } const char** ports = jack_get_ports(m_client, NULL, NULL, JackPortIsPhysical | JackPortIsInput); if(ports != NULL) { for(int i = 0; i < m_specs.channels && ports[i]; i++) jack_connect(m_client, jack_port_name(m_ports[i]), ports[i]); free(ports); } pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&m_mixingThread, &attr, runMixingThread, this); pthread_attr_destroy(&attr); }
static void process_midi_output(jack_nframes_t nframes) { int i, t, bytes_remaining, track_number; unsigned char *buffer, tmp_status; void *port_buffers[MAX_NUMBER_OF_TRACKS]; jack_nframes_t last_frame_time; jack_transport_state_t transport_state; static jack_transport_state_t previous_transport_state = JackTransportStopped; static int previous_playback_started = -1; static int previous_playback_paused = 0; for (i = 0; i <= smf->number_of_tracks; i++) { port_buffers[i] = jack_port_get_buffer(output_ports[i], nframes); if (port_buffers[i] == NULL) { warn_from_jack_thread_context("jack_port_get_buffer failed, cannot send anything."); return; } #ifdef JACK_MIDI_NEEDS_NFRAMES jack_midi_clear_buffer(port_buffers[i], nframes); #else jack_midi_clear_buffer(port_buffers[i]); #endif if (just_one_output) break; } if (ctrl_c_pressed) { send_all_sound_off(port_buffers, nframes); /* The idea here is to exit at the second time process_midi_output gets called. Otherwise, All Sound Off won't be delivered. */ ctrl_c_pressed++; if (ctrl_c_pressed >= 3) exit(0); return; } // g_debug("PROCESS CALLBACK!!!"); if (playback_paused) { if (!previous_playback_paused) { send_all_sound_off(port_buffers, nframes); } previous_playback_paused = playback_paused; return; } previous_playback_paused = playback_paused; if (use_transport) { // if (!ready_to_roll) return; transport_state = jack_transport_query(jack_client, NULL); if (transport_state == JackTransportStopped) { if (previous_transport_state == JackTransportRolling) { send_all_sound_off(port_buffers, nframes); } playback_started = -1; } if (transport_state == JackTransportStarting) { playback_started = -1; } if (transport_state == JackTransportRolling) { if (previous_transport_state != JackTransportRolling) { jack_position_t position; jack_transport_query(jack_client, &position); song_position = position.frame; playback_started = jack_last_frame_time(jack_client); } } previous_transport_state = transport_state; } else { if (playback_started == -1) { if (previous_playback_started >= 0) { send_all_sound_off(port_buffers, nframes); send_sond_end(); } } previous_playback_started = playback_started; } /* End of song already? */ if (playback_started < 0) return; last_frame_time = jack_last_frame_time(jack_client); /* We may push at most one byte per 0.32ms to stay below 31.25 Kbaud limit. */ bytes_remaining = nframes_to_ms(nframes) * rate_limit; double loop_offset = loop_song(smf); for (;;) { smf_event_t *event = smf_peek_next_event(smf); int end_of_song = 0; int is_meta_event = 0; if (event == NULL) { end_of_song = 1; } else if (smf_event_is_metadata(event)) { is_meta_event = 1; char *decoded = smf_event_decode(event); if (decoded) { if (!be_quiet) g_debug("Metadata: %s", decoded); end_of_song = process_meta_event(decoded); free(decoded); } } if (end_of_song) { if (!be_quiet) g_debug("End of song."); playback_started = -1; if (!use_transport) ctrl_c_pressed = 1; break; } if (is_meta_event) { smf_event_t *ret = smf_get_next_event(smf); assert(ret != 0); continue; } bytes_remaining -= event->midi_buffer_length; if (rate_limit > 0.0 && bytes_remaining <= 0) { warn_from_jack_thread_context("Rate limiting in effect."); break; } // t = seconds_to_nframes(event->time_seconds) + playback_started - song_position + nframes - last_frame_time; t = seconds_to_nframes(event->time_seconds + loop_offset) + playback_started - song_position - last_frame_time; /* If computed time is too much into the future, we'll need to send it later. */ if (t >= (int)nframes) break; /* If computed time is < 0, we missed a cycle because of xrun. */ if (t < 0) t = 0; assert(event->track->track_number >= 0 && event->track->track_number <= MAX_NUMBER_OF_TRACKS); /* We will send this event; remove it from the queue. */ smf_event_t *ret = smf_get_next_event(smf); assert(ret != 0); /* First, send it via midi_out. */ track_number = 0; #ifdef JACK_MIDI_NEEDS_NFRAMES buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length, nframes); #else buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length); #endif if (buffer == NULL) { warn_from_jack_thread_context("jack_midi_event_reserve failed, NOTE LOST."); break; } memcpy(buffer, event->midi_buffer, event->midi_buffer_length); /* Ignore per-track outputs? */ if (just_one_output) continue; /* Send it via proper output port. */ track_number = event->track->track_number; #ifdef JACK_MIDI_NEEDS_NFRAMES buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length, nframes); #else buffer = jack_midi_event_reserve(port_buffers[track_number], t, event->midi_buffer_length); #endif if (buffer == NULL) { warn_from_jack_thread_context("jack_midi_event_reserve failed, NOTE LOST."); break; } /* Before sending, reset channel to 0. XXX: Not very pretty. */ assert(event->midi_buffer_length >= 1); tmp_status = event->midi_buffer[0]; if (event->midi_buffer[0] >= 0x80 && event->midi_buffer[0] <= 0xEF) event->midi_buffer[0] &= 0xF0; memcpy(buffer, event->midi_buffer, event->midi_buffer_length); event->midi_buffer[0] = tmp_status; } }
static int ui_update(ui_t *ui) { int ret = 0; lff_msg_t lff_msg; jack_transport_state_t ts; while (lff_msg_read(ui->read, &lff_msg)) { switch (lff_msg.type) { case LFF_MSG_ADD: ui_add_note(ui, lff_msg.note, lff_msg.pattern); break; case LFF_MSG_REMOVE: ui_remove_note(ui, lff_msg.note, lff_msg.pattern); break; case LFF_MSG_CHANGE: ui->display->pattern = lff_msg.pattern; /* ui_change_pattern(ui, lff_msg.pattern); */ break; case LFF_MSG_VEL: break; case LFF_MSG_LEN: break; case LFF_MSG_REC: ui->display->rec = lff_msg.value; break; case LFF_MSG_SWING: ui->swing = lff_msg.value; break; case LFF_MSG_BPM: ui->bpm = lff_msg.value; break; case LFF_MSG_TRANSPORT: break; case LFF_MSG_MUTE: ui->display->mute[lff_msg.value] = !ui->display->mute[lff_msg.value]; break; case LFF_MSG_ACC: ui->display->acc = lff_msg.value; break; case LFF_MSG_REQUEST: ui_send_request(ui); break; case LFF_MSG_PSEQ: if (lff_msg.pattern < 0) { ui->pseq.len = lff_msg.value; } else { ui->pseq.patterns[lff_msg.value] = lff_msg.pattern; if (lff_msg.value >= ui->pseq.len) { ui->pseq.len++; } } break; case LFF_MSG_QUIT: return -1; default: break; } ret = 1; } ts = jack_transport_query(ui->client, NULL); if (ui->ts != ts) { ui->ts = ts; ret = 1; } while (lff_msg_read(ui->read_pos, &lff_msg)) { if (ui->display->step != lff_msg.value) { ui->display->step = lff_msg.value; ret = 1; } } return ret; }
// 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 jackProcess(const jack_nframes_t nframes) { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 const float* audioIns[DISTRHO_PLUGIN_NUM_INPUTS]; for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) audioIns[i] = (const float*)jack_port_get_buffer(fPortAudioIns[i], nframes); #else static const float** audioIns = nullptr; #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 float* audioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) audioOuts[i] = (float*)jack_port_get_buffer(fPortAudioOuts[i], nframes); #else static float** audioOuts = nullptr; #endif #if DISTRHO_PLUGIN_WANT_TIMEPOS jack_position_t pos; fTimePosition.playing = (jack_transport_query(fClient, &pos) == JackTransportRolling); if (pos.unique_1 == pos.unique_2) { fTimePosition.frame = pos.frame; if (pos.valid & JackTransportBBT) { fTimePosition.bbt.valid = true; fTimePosition.bbt.bar = pos.bar; fTimePosition.bbt.beat = pos.beat; fTimePosition.bbt.tick = pos.tick; fTimePosition.bbt.barStartTick = pos.bar_start_tick; fTimePosition.bbt.beatsPerBar = pos.beats_per_bar; fTimePosition.bbt.beatType = pos.beat_type; fTimePosition.bbt.ticksPerBeat = pos.ticks_per_beat; fTimePosition.bbt.beatsPerMinute = pos.beats_per_minute; } else fTimePosition.bbt.valid = false; } else { fTimePosition.bbt.valid = false; fTimePosition.frame = 0; } fPlugin.setTimePosition(fTimePosition); #endif #if DISTRHO_PLUGIN_IS_SYNTH void* const midiBuf = jack_port_get_buffer(fPortMidiIn, nframes); if (const uint32_t eventCount = jack_midi_get_event_count(midiBuf)) { uint32_t midiEventCount = 0; MidiEvent midiEvents[eventCount]; jack_midi_event_t jevent; for (uint32_t i=0; i < eventCount; ++i) { if (jack_midi_event_get(&jevent, midiBuf, i) != 0) break; MidiEvent& midiEvent(midiEvents[midiEventCount++]); midiEvent.frame = jevent.time; midiEvent.size = jevent.size; if (midiEvent.size > MidiEvent::kDataSize) midiEvent.dataExt = jevent.buffer; else std::memcpy(midiEvent.data, jevent.buffer, midiEvent.size); } fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount); } else { fPlugin.run(audioIns, audioOuts, nframes, nullptr, 0); } #else fPlugin.run(audioIns, audioOuts, nframes); #endif }
static int jack_process (jack_nframes_t frames, void * data) { mlt_filter filter = (mlt_filter) data; mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); int channels = mlt_properties_get_int( properties, "channels" ); int frame_size = mlt_properties_get_int( properties, "_samples" ) * sizeof(float); int sync = mlt_properties_get_int( properties, "_sync" ); int err = 0; int i; static int total_size = 0; jack_ringbuffer_t **output_buffers = mlt_properties_get_data( properties, "output_buffers", NULL ); if ( output_buffers == NULL ) return 0; jack_ringbuffer_t **input_buffers = mlt_properties_get_data( properties, "input_buffers", NULL ); jack_port_t **jack_output_ports = mlt_properties_get_data( properties, "jack_output_ports", NULL ); jack_port_t **jack_input_ports = mlt_properties_get_data( properties, "jack_input_ports", NULL ); float **jack_output_buffers = mlt_properties_get_data( properties, "jack_output_buffers", NULL ); float **jack_input_buffers = mlt_properties_get_data( properties, "jack_input_buffers", NULL ); pthread_mutex_t *output_lock = mlt_properties_get_data( properties, "output_lock", NULL ); pthread_cond_t *output_ready = mlt_properties_get_data( properties, "output_ready", NULL ); for ( i = 0; i < channels; i++ ) { size_t jack_size = ( frames * sizeof(float) ); size_t ring_size; // Send audio through out port jack_output_buffers[i] = jack_port_get_buffer( jack_output_ports[i], frames ); if ( ! jack_output_buffers[i] ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "no buffer for output port %d\n", i ); err = 1; break; } ring_size = jack_ringbuffer_read_space( output_buffers[i] ); jack_ringbuffer_read( output_buffers[i], ( char * )jack_output_buffers[i], ring_size < jack_size ? ring_size : jack_size ); if ( ring_size < jack_size ) memset( &jack_output_buffers[i][ring_size], 0, jack_size - ring_size ); // Return audio through in port jack_input_buffers[i] = jack_port_get_buffer( jack_input_ports[i], frames ); if ( ! jack_input_buffers[i] ) { mlt_log_error( MLT_FILTER_SERVICE(filter), "no buffer for input port %d\n", i ); err = 1; break; } // Do not start returning audio until we have sent first mlt frame if ( sync && i == 0 && frame_size > 0 ) total_size += ring_size; mlt_log_debug( MLT_FILTER_SERVICE(filter), "sync %d frame_size %d ring_size %zu jack_size %zu\n", sync, frame_size, ring_size, jack_size ); if ( ! sync || ( frame_size > 0 && total_size >= frame_size ) ) { ring_size = jack_ringbuffer_write_space( input_buffers[i] ); jack_ringbuffer_write( input_buffers[i], ( char * )jack_input_buffers[i], ring_size < jack_size ? ring_size : jack_size ); if ( sync ) { // Tell mlt that audio is available pthread_mutex_lock( output_lock); pthread_cond_signal( output_ready ); pthread_mutex_unlock( output_lock); // Clear sync phase mlt_properties_set_int( properties, "_sync", 0 ); } } } // Often jackd does not send the stopped event through the JackSyncCallback jack_client_t *jack_client = mlt_properties_get_data( properties, "jack_client", NULL ); jack_position_t jack_pos; jack_transport_state_t state = jack_transport_query( jack_client, &jack_pos ); int transport_state = mlt_properties_get_int( properties, "_transport_state" ); if ( state != transport_state ) { mlt_properties_set_int( properties, "_transport_state", state ); if ( state == JackTransportStopped ) jack_sync( state, &jack_pos, filter ); } return err; }
jack_transport_state_t Client::transport_query ( jack_position_t *pos ) { return jack_transport_query( _client, pos ); }
static gboolean cbox_jackio_get_sync_completed(struct cbox_io_impl *impl) { struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl; return jack_transport_query(jii->client, NULL) != JackTransportStarting; }
/** * 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_nframes_t net_period; int rx_bufsize, tx_bufsize; jack_default_audio_sample_t *buf; jack_port_t *port; JSList *node; int chn; int size, i; const char *porttype; int input_fd; jack_position_t local_trans_pos; uint32_t *packet_buf_tx, *packet_bufX; uint32_t *rx_packet_ptr; jack_time_t packet_recv_timestamp; if( bitdepth == 1000 ) net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8)&(~1) ; else net_period = (float) nframes / (float) factor; rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header); tx_bufsize = get_sample_size (bitdepth) * playback_channels * net_period + sizeof (jacknet_packet_header); /* Allocate a buffer where both In and Out Buffer will fit */ packet_buf_tx = alloca (tx_bufsize); jacknet_packet_header *pkthdr_tx = (jacknet_packet_header *) packet_buf_tx; /* * for latency==0 we need to send out the packet before we wait on the reply. * but this introduces a cycle of latency, when netsource is connected to itself. * so we send out before read only in zero latency mode. * */ if( latency == 0 ) { /* reset packet_bufX... */ packet_bufX = packet_buf_tx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); /* ---------- Send ---------- */ render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes, packet_bufX, net_period, dont_htonl_floats); /* fill in packet hdr */ pkthdr_tx->transport_state = jack_transport_query (client, &local_trans_pos); pkthdr_tx->transport_frame = local_trans_pos.frame; pkthdr_tx->framecnt = framecnt; pkthdr_tx->latency = latency; pkthdr_tx->reply_port = reply_port; pkthdr_tx->sample_rate = jack_get_sample_rate (client); pkthdr_tx->period_size = nframes; /* playback for us is capture on the other side */ pkthdr_tx->capture_channels_audio = playback_channels_audio; pkthdr_tx->playback_channels_audio = capture_channels_audio; pkthdr_tx->capture_channels_midi = playback_channels_midi; pkthdr_tx->playback_channels_midi = capture_channels_midi; pkthdr_tx->mtu = mtu; if( freewheeling!= 0 ) pkthdr_tx->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; else pkthdr_tx->sync_state = (jack_nframes_t)deadline_goodness; //printf("goodness=%d\n", deadline_goodness ); packet_header_hton (pkthdr_tx); if (cont_miss < 3*latency+5) { int r; for( r=0; r<redundancy; r++ ) netjack_sendto (outsockfd, (char *) packet_buf_tx, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu); } else if (cont_miss > 50+5*latency) { state_connected = 0; packet_cache_reset_master_address( packcache ); //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); cont_miss = 0; } } /* * ok... now the RECEIVE code. * */ if( reply_port ) input_fd = insockfd; else input_fd = outsockfd; // for latency == 0 we can poll. if( (latency == 0) || (freewheeling!=0) ) { jack_time_t deadline = jack_get_time() + 1000000 * jack_get_buffer_size(client)/jack_get_sample_rate(client); // Now loop until we get the right packet. while(1) { jack_nframes_t got_frame; if ( ! netjack_poll_deadline( input_fd, deadline ) ) break; packet_cache_drain_socket(packcache, input_fd); if (packet_cache_get_next_available_framecnt( packcache, framecnt - latency, &got_frame )) if( got_frame == (framecnt - latency) ) break; } } else { // normally: // only drain socket. packet_cache_drain_socket(packcache, input_fd); } size = packet_cache_retreive_packet_pointer( packcache, framecnt - latency, (char**)&rx_packet_ptr, rx_bufsize, &packet_recv_timestamp ); /* First alternative : we received what we expected. Render the data * to the JACK ports so it can be played. */ if (size == rx_bufsize) { uint32_t *packet_buf_rx = rx_packet_ptr; jacknet_packet_header *pkthdr_rx = (jacknet_packet_header *) packet_buf_rx; packet_bufX = packet_buf_rx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); // calculate how much time there would have been, if this packet was sent at the deadline. int recv_time_offset = (int) (jack_get_time() - packet_recv_timestamp); packet_header_ntoh (pkthdr_rx); deadline_goodness = recv_time_offset - (int)pkthdr_rx->latency; //printf( "deadline goodness = %d ---> off: %d\n", deadline_goodness, recv_time_offset ); if (cont_miss) { //printf("Frame %d \tRecovered from dropouts\n", framecnt); cont_miss = 0; } render_payload_to_jack_ports (bitdepth, packet_bufX, net_period, capture_ports, capture_srcs, nframes, dont_htonl_floats); state_currentframe = framecnt; state_recv_packet_queue_time = recv_time_offset; state_connected = 1; sync_state = pkthdr_rx->sync_state; packet_cache_release_packet( packcache, framecnt - latency ); } /* Second alternative : we've received something that's not * as big as expected or we missed a packet. We render silence * to the ouput ports */ else { jack_nframes_t latency_estimate; if( packet_cache_find_latency( packcache, framecnt, &latency_estimate ) ) //if( (state_latency == 0) || (latency_estimate < state_latency) ) state_latency = latency_estimate; // Set the counters up. state_currentframe = framecnt; //state_latency = framecnt - pkthdr->framecnt; state_netxruns += 1; //printf ("Frame %d \tPacket missed or incomplete (expected: %d bytes, got: %d bytes)\n", framecnt, rx_bufsize, size); //printf ("Frame %d \tPacket missed or incomplete\n", framecnt); cont_miss += 1; chn = 0; node = capture_ports; while (node != NULL) { port = (jack_port_t *) node->data; buf = jack_port_get_buffer (port, nframes); porttype = jack_port_type (port); if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size ()) == 0) for (i = 0; i < nframes; i++) buf[i] = 0.0; else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size ()) == 0) jack_midi_clear_buffer (buf); node = jack_slist_next (node); chn++; } } if( latency != 0 ) { /* reset packet_bufX... */ packet_bufX = packet_buf_tx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); /* ---------- Send ---------- */ render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes, packet_bufX, net_period, dont_htonl_floats); /* fill in packet hdr */ pkthdr_tx->transport_state = jack_transport_query (client, &local_trans_pos); pkthdr_tx->transport_frame = local_trans_pos.frame; pkthdr_tx->framecnt = framecnt; pkthdr_tx->latency = latency; pkthdr_tx->reply_port = reply_port; pkthdr_tx->sample_rate = jack_get_sample_rate (client); pkthdr_tx->period_size = nframes; /* playback for us is capture on the other side */ pkthdr_tx->capture_channels_audio = playback_channels_audio; pkthdr_tx->playback_channels_audio = capture_channels_audio; pkthdr_tx->capture_channels_midi = playback_channels_midi; pkthdr_tx->playback_channels_midi = capture_channels_midi; pkthdr_tx->mtu = mtu; if( freewheeling!= 0 ) pkthdr_tx->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; else pkthdr_tx->sync_state = (jack_nframes_t)deadline_goodness; //printf("goodness=%d\n", deadline_goodness ); packet_header_hton (pkthdr_tx); if (cont_miss < 3*latency+5) { int r; for( r=0; r<redundancy; r++ ) netjack_sendto (outsockfd, (char *) packet_buf_tx, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu); } else if (cont_miss > 50+5*latency) { state_connected = 0; packet_cache_reset_master_address( packcache ); //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); cont_miss = 0; } } framecnt++; return 0; }
static int jack_process_cb (jack_nframes_t nframes, void *arg) { GstJackAudioConnection *conn = (GstJackAudioConnection *) arg; GList *walk; int res = 0; jack_transport_state_t ts = jack_transport_query (conn->client, NULL); if (ts != conn->cur_ts) { conn->cur_ts = ts; switch (ts) { case JackTransportStopped: GST_DEBUG ("transport state is 'stopped'"); conn->transport_state = GST_STATE_PAUSED; break; case JackTransportStarting: GST_DEBUG ("transport state is 'starting'"); conn->transport_state = GST_STATE_READY; break; case JackTransportRolling: GST_DEBUG ("transport state is 'rolling'"); conn->transport_state = GST_STATE_PLAYING; break; default: break; } GST_DEBUG ("num of clients: src=%d, sink=%d", g_list_length (conn->src_clients), g_list_length (conn->sink_clients)); } g_mutex_lock (&conn->lock); /* call sources first, then sinks. Sources will either push data into the * ringbuffer of the sinks, which will then pull the data out of it, or * sinks will pull the data from the sources. */ for (walk = conn->src_clients; walk; walk = g_list_next (walk)) { GstJackAudioClient *client = (GstJackAudioClient *) walk->data; /* only call active clients */ if ((client->active || client->deactivate) && client->process) { res = client->process (nframes, client->user_data); if (client->deactivate) { client->deactivate = FALSE; g_cond_signal (&conn->flush_cond); } } } for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) { GstJackAudioClient *client = (GstJackAudioClient *) walk->data; /* only call active clients */ if ((client->active || client->deactivate) && client->process) { res = client->process (nframes, client->user_data); if (client->deactivate) { client->deactivate = FALSE; g_cond_signal (&conn->flush_cond); } } } /* handle transport state requisition, do sinks first, stop after the first * element that handled it */ if (conn->transport_state != GST_STATE_VOID_PENDING) { for (walk = conn->sink_clients; walk; walk = g_list_next (walk)) { if (jack_handle_transport_change ((GstJackAudioClient *) walk->data, conn->transport_state)) { conn->transport_state = GST_STATE_VOID_PENDING; break; } } } if (conn->transport_state != GST_STATE_VOID_PENDING) { for (walk = conn->src_clients; walk; walk = g_list_next (walk)) { if (jack_handle_transport_change ((GstJackAudioClient *) walk->data, conn->transport_state)) { conn->transport_state = GST_STATE_VOID_PENDING; break; } } } g_mutex_unlock (&conn->lock); return res; }
void audioStreamer_JACK::timebase_cb(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos ) { static double jack_tick; static jack_nframes_t last_frame; static jack_nframes_t current_frame; jack_position_t cur_pos; static jack_transport_state_t state_current; static jack_transport_state_t state_last; static int locating = 0; #if 0 if( state != JackTransportRolling ) { jack_transport_locate( client, 0 ); jack_transport_start( client ); } #endif if( !njc ) return; int posi, len; njc->GetPosition(&posi,&len); float bpm = njc->GetActualBPM(); //posi = njc->GetSessionPosition() * m_srate / 1000; //len = (int)(njc->GetBPI() * m_srate * 60 / bpm); // sync jack_transport_frame to njframe //current_frame = jack_get_current_transport_frame( client ); jack_transport_query(client, &cur_pos); current_frame = cur_pos.frame; // FIXME: This will not work right, if there are slow-sync clients.... int diff = abs(current_frame % len) - (posi % len); if( diff > nframes ) { #if 1 jack_transport_locate( client, (current_frame / len) * len + (posi%len) + 2*nframes ); //printf( "no: current= %d diff = %d\n", (current_frame % len) - (posi % len), diff ); #endif } // taken from seq24-0.7.0 perform.cpp state_current = state; //printf( "jack_timebase_callback() [%d] [%d] [%d]", state, new_pos, current_frame); pos->valid = JackPositionBBT; pos->beats_per_bar = 4; pos->beat_type = 4; pos->ticks_per_beat = c_ppqn * 10; pos->beats_per_minute = bpm; /* Compute BBT info from frame number. This is relatively * simple here, but would become complex if we supported tempo * or time signature changes at specific locations in the * transport timeline. */ // if we are in a new position if ( state_last == JackTransportStarting && state_current == JackTransportRolling ){ //printf ( "Starting [%d] [%d]\n", last_frame, current_frame ); jack_tick = 0.0; last_frame = current_frame; } if ( current_frame > last_frame ){ double jack_delta_tick = (current_frame - last_frame) * pos->ticks_per_beat * pos->beats_per_minute / (pos->frame_rate * 60.0); jack_tick += jack_delta_tick; last_frame = current_frame; } long ptick = 0, pbeat = 0, pbar = 0; pbar = (long) ((long) jack_tick / (pos->ticks_per_beat * pos->beats_per_bar )); pbeat = (long) ((long) jack_tick % (long) (pos->ticks_per_beat * pos->beats_per_bar )); pbeat = pbeat / (long) pos->ticks_per_beat; ptick = (long) jack_tick % (long) pos->ticks_per_beat; pos->bar = pbar + 1; pos->beat = pbeat + 1; pos->tick = ptick;; pos->bar_start_tick = pos->bar * pos->beats_per_bar * pos->ticks_per_beat; //printf( " bbb [%2d:%2d:%4d]\n", pos->bar, pos->beat, pos->tick ); state_last = state_current; }
void JackOutput::updateTransportInfo() { if ( locate_countdown == 1 ) locate( locate_frame ); if ( locate_countdown > 0 ) locate_countdown--; if ( Preferences::get_instance()->m_bJackTransportMode != Preferences::USE_JACK_TRANSPORT ) return; m_JackTransportState = jack_transport_query( client, &m_JackTransportPos ); // update m_transport with jack-transport data switch ( m_JackTransportState ) { case JackTransportStopped: m_transport.m_status = TransportInfo::STOPPED; //infoLog( "[updateTransportInfo] STOPPED - frames: " + to_string(m_transportPos.frame) ); break; case JackTransportRolling: if ( m_transport.m_status != TransportInfo::ROLLING && ( m_JackTransportPos.valid & JackPositionBBT ) ) { must_relocate = 2; //WARNINGLOG( "Jack transport starting: Resyncing in 2 x Buffersize!!" ); } m_transport.m_status = TransportInfo::ROLLING; //infoLog( "[updateTransportInfo] ROLLING - frames: " + to_string(m_transportPos.frame) ); break; case JackTransportStarting: m_transport.m_status = TransportInfo::STOPPED; //infoLog( "[updateTransportInfo] STARTING (stopped) - frames: " + to_string(m_transportPos.frame) ); break; default: ERRORLOG( "Unknown jack transport state" ); } // FIXME // TickSize and BPM Hydrogen * H = Hydrogen::get_instance(); H->setTimelineBpm(); // dlr: fix #168, jack may have re-located us anywhere, check for bpm change every cycle if ( m_JackTransportPos.valid & JackPositionBBT ) { float bpm = ( float )m_JackTransportPos.beats_per_minute; if ( m_transport.m_nBPM != bpm ) { if ( Preferences::get_instance()->m_bJackMasterMode == Preferences::NO_JACK_TIME_MASTER ){ // WARNINGLOG( QString( "Tempo change from jack-transport: %1" ).arg( bpm ) ); m_transport.m_nBPM = bpm; must_relocate = 1; // The tempo change has happened somewhere during the previous cycle; relocate right away. // This commenting out is rude perhaps, but I cant't figure out what this bit is doing. // In any case, setting must_relocate = 1 here causes too many relocates. Jakob Lund /* } else { if ( m_transport.m_status == TransportInfo::STOPPED ) { oldpo = H->getPatternPos(); must_relocate = 1; //changer =1; }*/ } // Hydrogen::get_instance()->setBPM( m_JackTransportPos.beats_per_minute ); // unnecessary, as Song->m_BPM gets updated in audioEngine_process_transport (after calling this function) } } if ( m_transport.m_nFrames + bbt_frame_offset != m_JackTransportPos.frame ) { if ( ( m_JackTransportPos.valid & JackPositionBBT ) && must_relocate == 0 ) { WARNINGLOG( "Frame offset mismatch; triggering resync in 2 cycles" ); must_relocate = 2; } else { if ( Preferences::get_instance()->m_bJackMasterMode == Preferences::NO_JACK_TIME_MASTER ) { // If There's no timebase_master, and audioEngine_process_checkBPMChanged handled a tempo change during last cycle, the offset doesn't match, but hopefully it was calculated correctly: //this perform Jakobs mod in pattern mode, but both m_transport.m_nFrames works with the same result in pattern Mode // in songmode the first case dont work. //so we can remove this "if query" and only use this old mod: m_transport.m_nFrames = H->getHumantimeFrames(); //because to get the songmode we have to add this "H2Core::Hydrogen *m_pEngine" to the header file //if we remove this we also can remove *m_pEngine from header #if 0 // dlr: fix #169, why do we have a different behaviour for SONG_MODE? if ( m_pEngine->getSong()->get_mode() == Song::PATTERN_MODE ){ m_transport.m_nFrames = m_JackTransportPos.frame/* - bbt_frame_offset*/; ///see comment in svn changeset 753 } else { m_transport.m_nFrames = H->getHumantimeFrames(); } #else m_transport.m_nFrames = m_JackTransportPos.frame; bbt_frame_offset = 0; // dlr: stop re-syncing in every cycle when STOPPED #endif // In jack 'slave' mode, if there's no master, the following line is needed to be able to relocate by clicking the song ruler (wierd corner case, but still...) if ( m_transport.m_status == TransportInfo::ROLLING ) H->triggerRelocateDuringPlay(); } else { ///this is experimantal... but it works for the moment... fix me fix :-) wolke // ... will this actually happen? keeping it for now ( jakob lund ) m_transport.m_nFrames = H->getHumantimeFrames(); } } } // humantime fix if ( H->getHumantimeFrames() != m_JackTransportPos.frame ) { H->setHumantimeFrames(m_JackTransportPos.frame); //WARNINGLOG("fix Humantime " + to_string (m_JackTransportPos.frame)); } if ( must_relocate == 1 ) { //WARNINGLOG( "Resyncing!" ); relocateBBT(); if ( m_transport.m_status == TransportInfo::ROLLING ) { H->triggerRelocateDuringPlay(); } } if ( must_relocate > 0 ) must_relocate--; }
jack_transport_state_t luajack_transport_query(luajack_t *client, jack_position_t *pos) { cud_t *cud = get_cud(client); if(!cud) return (jack_transport_state_t)0; return jack_transport_query(cud->client, pos); }