/** * process this MIDI input: pack LV2 input buffer from EventBuffer + EventConnection */ void Lv2MidiInput::process(bool rolling, jack_position_t &pos, jack_nframes_t nframes, jack_nframes_t time) { // clear atom sequence lv2_atom_sequence_clear(atomSequence); // send all notes off messages if newly stopped if(localRolling && !rolling) { Lv2MidiEvent lv2Event; lv2Event.event.time.frames = 0; lv2Event.buffer[1] = 0x7b; // CC 123 = all notes off lv2Event.buffer[2] = 0; for(uint8_t channel = 0; channel < 16; channel++) { lv2Event.buffer[0] = MidiEvent::TYPE_CONTROL + channel; lv2_atom_sequence_append_event(atomSequence, CAPACITY, &lv2Event.event); } } localRolling = rolling; // get connection and event count MidiConnection *connection = eventConnector.getConnection(); uint32_t eventCount = 0; if(connection) { connection->getSource()->process(rolling, pos, nframes, time); eventCount = connection->getEventCount(); } // get top events from buffer + connection MidiEvent* bufferEvent = eventBuffer.getNextEvent(rolling, pos, nframes); MidiEvent *connectionEvent = eventCount ? connection->getEvent(0) : 0; uint32_t eventIndex = 1; // loop while events on either buffer or connection while(bufferEvent || connectionEvent) { bool bufferNext = bufferEvent && (!connectionEvent || bufferEvent->getFrameOffset() < connectionEvent->getFrameOffset()); MidiEvent *event = bufferNext ? bufferEvent : connectionEvent; // lv2 event Lv2MidiEvent lv2Event; long frame = event->getFrameOffset(); lv2Event.event.time.frames = frame >= 0 ? frame : 0; // copy event data event->pack(&lv2Event.buffer); // append this event to sequence lv2_atom_sequence_append_event(atomSequence, CAPACITY, &lv2Event.event); // get next event if(bufferNext) { // recycle and get next buffer event ObjectCollector::scriptCollector().recycle(bufferEvent); bufferEvent = eventBuffer.getNextEvent(rolling, pos, nframes); } else { connectionEvent = eventIndex < eventCount ? connection->getEvent(eventIndex++) : 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 Note2midi::run(LV2_Handle instance, uint32_t SampleCount) { Note2midi *plugin = (Note2midi *) instance; unsigned int new_onset_method = (unsigned int)(*plugin->_onset_method); unsigned int new_pitch_method = (unsigned int)(*plugin->_pitch_method); char_t* on_meth; char_t* pi_meth; silence_threshold = *(plugin->_silence_threshold); aubio_onset_set_threshold(plugin->o, *plugin->_onset_threshold); aubio_pitch_set_tolerance(plugin->pitch, *plugin->_pitch_method); if(0 <= new_onset_method && 16 >= new_onset_method && new_onset_method != plugin->current_onset_method){ del_aubio_onset(plugin->o); switch(new_onset_method){ case ONSET_METHOD_ENERGY: on_meth = "energy"; break; case ONSET_METHOD_SPECDIFF: on_meth = "specdiff"; break; case ONSET_METHOD_HFC: on_meth = "hfc"; break; case ONSET_METHOD_COMPLEXDOMAIN: on_meth = "complexdomain"; break; case ONSET_METHOD_COMPLEX: on_meth = "complex"; break; case ONSET_METHOD_PHASE: on_meth = "phase"; break; case ONSET_METHOD_MKL: on_meth = "mkl"; break; case ONSET_METHOD_KL: on_meth = "kl"; break; case ONSET_METHOD_SPECFLUX: on_meth = "specflux"; break; case ONSET_METHOD_CENTROID: on_meth = "centroid"; break; case ONSET_METHOD_SPREAD: on_meth = "spread"; break; case ONSET_METHOD_SKEWNESS: on_meth = "skewness"; break; case ONSET_METHOD_KURTOSIS: on_meth = "kurtosis"; break; case ONSET_METHOD_SLOPE: on_meth = "slope"; break; case ONSET_METHOD_DECREASE: on_meth = "decrease"; break; case ONSET_METHOD_ROLLOFF: on_meth = "rolloff"; break; default: on_meth = "default"; break; } plugin->o = new_aubio_onset (on_meth, PARAM_BUFFER_SIZE, PARAM_HOP_SIZE_ONSET, plugin->samplerate); plugin->current_onset_method = new_onset_method; } if(0 <= new_pitch_method && 6 >= new_pitch_method && new_pitch_method != plugin->current_pitch_method){ del_aubio_pitch(plugin->pitch); switch(new_pitch_method){ case PITCH_METHOD_MCOMB: pi_meth = "mcomb"; break; case PITCH_METHOD_YINFFT: pi_meth = "yinfft"; break; case PITCH_METHOD_YIN: pi_meth = "yin"; break; case PITCH_METHOD_SCHMITT: pi_meth = "schmitt"; break; case PITCH_METHOD_FCOMB: pi_meth = "fcomb"; break; case PITCH_METHOD_SPECACF: pi_meth = "specacf"; break; default: pi_meth = "default"; break; } plugin->pitch = new_aubio_pitch (pi_meth, PARAM_BUFFER_SIZE * PARAM_PITCH_BUFF_TIMES, PARAM_HOP_SIZE_PITCH, plugin->samplerate); // aubio_pitch_set_unit (plugin->pitch, plugin->pitch_unit); plugin->current_pitch_method = new_pitch_method; } out_capacity = plugin->out->atom.size; plugin->out->atom.type = plugin->atom_sequence; lv2_atom_sequence_clear(plugin->out); plugin->ibuf->data = (smpl_t *) plugin->in; counter++; if(counter++ >= PARAM_BUFFER_SIZE) counter = 0; plugin->process_block(); }