static void forge_message_kv (BITui* ui, LV2_URID uri, int key, float value) { uint8_t obj_buf[1024]; if (ui->disable_signals) return; lv2_atom_forge_set_buffer (&ui->forge, obj_buf, 1024); LV2_Atom* msg = forge_kvcontrolmessage (&ui->forge, &ui->uris, uri, key, value); ui->write (ui->controller, 0, lv2_atom_total_size (msg), ui->uris.atom_eventTransfer, msg); }
/** notify backend that UI is active: * request state and enable data-transmission */ static void ui_enable(LV2UI_Handle handle) { SFSUI* ui = (SFSUI*)handle; uint8_t obj_buf[64]; lv2_atom_forge_set_buffer(&ui->forge, obj_buf, 64); LV2_Atom_Forge_Frame frame; lv2_atom_forge_frame_time(&ui->forge, 0); LV2_Atom* msg = (LV2_Atom*)x_forge_object(&ui->forge, &frame, 1, ui->uris.ui_on); lv2_atom_forge_pop(&ui->forge, &frame); ui->write(ui->controller, 0, lv2_atom_total_size(msg), ui->uris.atom_eventTransfer, msg); }
/** Callback for preset combobox changed */ static void on_changed(GtkWidget *widget, void *handle) { FluidUI *self = (FluidUI *)handle; uint8_t obj_buf[OBJ_BUF_SIZE]; int selected = gtk_combo_box_get_active((GtkComboBox *)widget); lv2_atom_forge_set_buffer(&self->forge, obj_buf, OBJ_BUF_SIZE); LV2_Atom *patch_set = write_patch_set(&self->forge, &self->uris, selected); /* Send preset setting to synth plugin */ self->write(self->controller, INPUT_PORT, lv2_atom_total_size(patch_set), self->uris.atom_event_transfer, patch_set); }
/** Instantiate the user interface */ static LV2UI_Handle instantiate(const LV2UI_Descriptor *descriptor, const char *plugin_uri, const char *bundle_path, LV2UI_Write_Function write_function, LV2UI_Controller controller, LV2UI_Widget *widget, const LV2_Feature *const *features) { /* Create object data */ FluidUI *self = malloc(sizeof(FluidUI)); self->write = write_function; self->controller = controller; for (int i = 0; features[i]; i++) { if (strcmp(features[i]->URI, LV2_URID__map) == 0) self->map = (LV2_URID_Map *)features[i]->data; } map_uris(self->map, &self->uris); lv2_atom_forge_init(&self->forge, self->map); /* Create GUI */ self->box = gtk_hbox_new(FALSE, 4); self->label = gtk_label_new("Preset"); self->combo_box = gtk_combo_box_text_new(); get_presets(self); gtk_box_pack_start(GTK_BOX(self->box), self->label, TRUE, TRUE, 4); gtk_box_pack_start(GTK_BOX(self->box), self->combo_box, FALSE, TRUE, 4); g_signal_connect(self->combo_box, "changed", G_CALLBACK(on_changed), self); *widget = self->box; /* Send patch get to synth plug to get current preset setting */ uint8_t obj_buf[OBJ_BUF_SIZE]; lv2_atom_forge_set_buffer(&self->forge, obj_buf, OBJ_BUF_SIZE); LV2_Atom *patch_get = write_patch_get(&self->forge, &self->uris); self->write(self->controller, INPUT_PORT, lv2_atom_total_size(patch_get), self->uris.atom_event_transfer, patch_get); return self; }
static void on_load_clicked(GtkWidget* widget, void* handle) { FeedbackSuppressorUI* ui = (FeedbackSuppressorUI*)handle; /* Create a dialog to select a sample file. */ GtkWidget* dialog = gtk_file_chooser_dialog_new( "Load Sample", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); GtkFileFilter *filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "Filter Lists"); gtk_file_filter_add_pattern(filter,"*.ofs"); gtk_file_chooser_add_filter((GtkFileChooser *) dialog,filter); /* Run the dialog, and return if it is cancelled. */ if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) { gtk_widget_destroy(dialog); return; } /* Get the file path from the dialog. */ char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); /* Got what we need, destroy the dialog and filter. */ gtk_widget_destroy(dialog); #define OBJ_BUF_SIZE 1024 uint8_t obj_buf[OBJ_BUF_SIZE]; lv2_atom_forge_set_buffer(&ui->forge, obj_buf, OBJ_BUF_SIZE); LV2_Atom* msg = write_set_file(&ui->forge, &ui->uris, filename, strlen(filename)); ui->write(ui->controller, 0, lv2_atom_total_size(msg), ui->uris.atom_eventTransfer, msg); g_free(filename); }
// 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; }
static void run (LV2_Handle instance, uint32_t n_samples) { MidiMap* self = (MidiMap*)instance; if (!self->midiout || !self->midiin) { return; } /* prepare midiout port */ const uint32_t capacity = self->midiout->atom.size; lv2_atom_forge_set_buffer (&self->forge, (uint8_t*)self->midiout, capacity); lv2_atom_forge_sequence_head (&self->forge, &self->frame, 0); /* update internal frame counter (host only sends update on change) */ if (self->transport_rolling) { self->transport_frame += n_samples * self->transport_speed; } else { self->play_cnt = 0; } /* inform the GUI */ if (self->inform_ui) { self->inform_ui = false; inform_ui (self); } uint32_t gen_n_samples = 0; /* Process incoming midi events */ LV2_Atom_Event* ev = lv2_atom_sequence_begin (&(self->midiin)->body); while (!lv2_atom_sequence_is_end (&(self->midiin)->body, (self->midiin)->atom.size, ev)) { if (ev->body.type == self->uris.midi_MidiEvent) { /* generate signals until current time */ generate_until (self, gen_n_samples, ev->time.frames); gen_n_samples = ev->time.frames; /* process midi event */ filter_midimessage (self, ev->time.frames, (uint8_t*)(ev+1), ev->body.size); } else if (ev->body.type == self->uris.atom_Blank || ev->body.type == self->uris.atom_Object) { /* schedule loading config file */ const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body; if (obj->body.otype == self->uris.time_Position) { parse_host_transport (self, obj); } else if (obj->body.otype == self->uris.patch_Set) { self->schedule->schedule_work (self->schedule->handle, lv2_atom_total_size (&ev->body), &ev->body); } } ev = lv2_atom_sequence_next (ev); } /* generate remaining events */ generate_until (self, gen_n_samples, n_samples); /* keep track of position (for generator) */ self->monotonic_cnt += n_samples; if (self->transport_rolling) { self->play_cnt += n_samples; } /* report active rules */ *self->p_rulecount = self->rules ? self->rules->count : 0; }