// TOOD: support LV2 void KeyItem::accept (lvtk::AtomObject key, fish::SamplerURIs& uris) { LV2_Atom* volume = nullptr, *note = nullptr; LV2_Atom* trigger = nullptr, *muteGroup = nullptr; lv2_atom_object_get (key, (uint32) uris.param_index, ¬e, (uint32) uris.param_volume, &volume, (uint32) uris.prop_muteGroup, &muteGroup, (uint32) uris.prop_mode, &trigger, nullptr); if (note) { const int n = lvtk::Atom(note).as_int(); if (volume) { set ("volume", lvtk::Atom(volume).as_float()); std::clog << "v: " << this->volume() << std::endl; } if (trigger) { set ("trigger-mode", lvtk::Atom(trigger).as_int()); std::clog << "t: " << this->triggerMode() << std::endl; } if (muteGroup) { set ("mute-group", lvtk::Atom(muteGroup).as_int()); std::clog << "mg: " << this->muteGroup() << std::endl; } } }
static void update_bpm(ADelay* self, const LV2_Atom_Object* obj) { const DelayURIs* uris = &self->uris; // Received new transport bpm/beatunit LV2_Atom *beatunit = NULL, *bpm = NULL; lv2_atom_object_get(obj, uris->time_beatUnit, &beatunit, uris->time_beatsPerMinute, &bpm, NULL); // Tempo changed, update BPM if (bpm && bpm->type == uris->atom_Float) { self->bpm = ((LV2_Atom_Float*)bpm)->body; } // Time signature changed, update beatunit if (beatunit && beatunit->type == uris->atom_Int) { int b = ((LV2_Atom_Int*)beatunit)->body; self->beatunit = (float)b; } if (beatunit && beatunit->type == uris->atom_Double) { double b = ((LV2_Atom_Double*)beatunit)->body; self->beatunit = (float)b; } if (beatunit && beatunit->type == uris->atom_Float) { self->beatunit = ((LV2_Atom_Float*)beatunit)->body; } if (beatunit && beatunit->type == uris->atom_Long) { long int b = ((LV2_Atom_Long*)beatunit)->body; self->beatunit = (float)b; } self->bpmvalid = 1; }
static void iowork(B3S* b3s, const LV2_Atom_Object* obj, int cmd) { const LV2_Atom* name = NULL; lv2_atom_object_get(obj, b3s->uris.sb3_cckey, &name, 0); if (name) { struct worknfo w; w.cmd = cmd; w.status = -1;; strncpy(w.msg, (char *)LV2_ATOM_BODY(name), 1024); b3s->schedule->schedule_work(b3s->schedule->handle, sizeof(struct worknfo), &w); } }
void work (const URIs& uris, const lvtk::Atom& atom) override { const lvtk::AtomObject object (atom.as_object()); const lvtk::Atom note; lv2_atom_object_get (object, uris.slugs_note, ¬e, 0); if (note) { SamplerSound* s = new SamplerSound (note.as_int(), static_cast<int> (object.id())); const ObjectRef ref (jobs->getWorkForge(), uris.ksp1_Key, s); if (ref.get<SamplerSound>() != nullptr) { respond (ref.total_size(), ref.cobj()); } } }
/** * Get the file path from a message like: * a patch:Set ; * patch:property midimap:cfgfile ; * patch:value </home/me/foo.cfg> . */ static const LV2_Atom* parse_patch_msg (const MidiMapURIs* uris, const LV2_Atom_Object* obj) { const LV2_Atom* property = NULL; const LV2_Atom* file_path = NULL; if (obj->body.otype != uris->patch_Set) { return NULL; } lv2_atom_object_get (obj, uris->patch_property, &property, 0); if (!property || property->type != uris->atom_URID) { return NULL; } else if (((LV2_Atom_URID*)property)->body != uris->mem_cfgfile) { return NULL; } lv2_atom_object_get(obj, uris->patch_value, &file_path, 0); if (!file_path || file_path->type != uris->atom_Path) { return NULL; } return file_path; }
/** Handle event from synth plug-in */ static void port_event(LV2UI_Handle handle, uint32_t port_index, uint32_t bufer_size, uint32_t format, const void *buffer) { FluidUI *self = (FluidUI *)handle; if (format != self->uris.atom_event_transfer) { fprintf(stderr, "Unknown format\n"); return; } LV2_Atom *atom = (LV2_Atom *)buffer; if (atom->type != self->uris.atom_blank) { fprintf(stderr, "Unknown message type\n"); return; } LV2_Atom_Object *obj = (LV2_Atom_Object *)atom; if (obj->body.otype != self->uris.patch_response) { fprintf(stderr, "Unknown object type\n"); return; } LV2_Atom_Object *body = NULL; lv2_atom_object_get(obj, self->uris.patch_body, &body, 0); LV2_Atom_Int *preset = NULL; lv2_atom_object_get(body, self->uris.fluid_preset, &preset, 0); /* Update combo box selection */ if (preset) gtk_combo_box_set_active((GtkComboBox *)self->combo_box, preset->body); }
/******************************************************************************* * parse LV2 time extension data */ static void parse_host_transport (MidiMap* self, const LV2_Atom_Object* obj) { LV2_Atom* beat = NULL; LV2_Atom* bpm = NULL; LV2_Atom* speed = NULL; LV2_Atom* frame = NULL; lv2_atom_object_get(obj, self->uris.time_barBeat, &beat, self->uris.time_beatsPerMinute, &bpm, self->uris.time_speed, &speed, self->uris.time_frame, &frame, NULL); if (bpm && bpm->type == self->uris.atom_Float) { // Tempo changed, update BPM self->bpm = ((LV2_Atom_Float*)bpm)->body; self->bpm_avail = true; } else { self->bpm_avail = false; } if (speed && speed->type == self->uris.atom_Float) { self->transport_speed = ((LV2_Atom_Float*)speed)->body; self->transport_rolling = (self->transport_speed != 0); } if (frame && speed->type == self->uris.atom_Long) { self->transport_frame = ((LV2_Atom_Long*)speed)->body; } if (beat && beat->type == self->uris.atom_Float) { self->bbt = ((LV2_Atom_Float*)beat)->body; self->bbt_avail = true; } else { self->bbt_avail = false; } if (!self->transport_rolling) { self->play_cnt = 0; } }
static void advanced_config_set(B3S* b3s, const LV2_Atom_Object* obj) { const LV2_Atom* cfgline = NULL; lv2_atom_object_get(obj, b3s->uris.sb3_cckey, &cfgline, 0); if (cfgline) { struct worknfo w; char* msg = (char *)LV2_ATOM_BODY(cfgline); if(!strcmp("special.reset=1", msg)) { w.cmd = CMD_RESET; } else if(!strcmp("special.reconfigure=1", msg)) { w.cmd = CMD_PURGE; } else { w.cmd = CMD_SETCFG; } w.status = -1; strncpy(w.msg, msg, 1024); b3s->schedule->schedule_work(b3s->schedule->handle, sizeof(struct worknfo), &w); } }
static void parse_time_position(LV2dr14* self, const LV2_Atom_Object* obj) { const EBULV2URIs* uris = &self->uris; // Received new transport position/speed LV2_Atom *speed = NULL; lv2_atom_object_get(obj, uris->time_speed, &speed, NULL); if (speed && speed->type == uris->atom_Float) { float ts = ((LV2_Atom_Float*)speed)->body; if (ts != 0 && !self->tranport_rolling) { if (self->follow_host_transport) { reset_peaks(self); } } self->tranport_rolling = (ts != 0); } }
/** Update the current position based on a host message. This is called by run() when a time:Position is received. */ static void update_position(Metro* self, const LV2_Atom_Object* obj) { const MetroURIs* uris = &self->uris; // Received new transport position/speed LV2_Atom *beat = NULL, *bpm = NULL, *speed = NULL; lv2_atom_object_get(obj, uris->time_barBeat, &beat, uris->time_beatsPerMinute, &bpm, uris->time_speed, &speed, NULL); if (bpm && bpm->type == uris->atom_Float) { // Tempo changed, update BPM self->bpm = ((LV2_Atom_Float*)bpm)->body; } if (speed && speed->type == uris->atom_Float) { // Speed changed, e.g. 0 (stop) to 1 (play) self->speed = ((LV2_Atom_Float*)speed)->body; } if (beat && beat->type == uris->atom_Float) { // Received a beat position, synchronise // This hard sync may cause clicks, a real plugin would be more graceful const float frames_per_beat = 60.0f / self->bpm * self->rate; const float bar_beats = ((LV2_Atom_Float*)beat)->body; const float beat_beats = bar_beats - floorf(bar_beats); self->elapsed_len = beat_beats * frames_per_beat; if (self->elapsed_len < self->attack_len) { self->state = STATE_ATTACK; } else if (self->elapsed_len < self->attack_len + self->decay_len) { self->state = STATE_DECAY; } else { self->state = STATE_OFF; } } }
static void run(LV2_Handle instance, uint32_t n_samples) { B3S* b3s = (B3S*)instance; float* audio[2]; audio[0] = b3s->outL; audio[1] = b3s->outR; /* prepare outgoing MIDI */ const uint32_t capacity = b3s->midiout->atom.size; static bool warning_printed = false; if (!warning_printed && capacity < 4096) { warning_printed = true; fprintf(stderr, "B3LV2: LV message buffer is only %d bytes. Expect problems.\n", capacity); fprintf(stderr, "B3LV2: if your LV2 host allows one to configure a buffersize use at least 4kBytes.\n"); } lv2_atom_forge_set_buffer(&b3s->forge, (uint8_t*)b3s->midiout, capacity); lv2_atom_forge_sequence_head(&b3s->forge, &b3s->frame, 0); uint32_t written = 0; if (b3s->queue_panic) { b3s->queue_panic = 0; midi_panic(b3s->inst); } /* Process incoming events from GUI and handle MIDI events */ if (b3s->midiin) { LV2_Atom_Event* ev = lv2_atom_sequence_begin(&(b3s->midiin)->body); while(!lv2_atom_sequence_is_end(&(b3s->midiin)->body, (b3s->midiin)->atom.size, ev)) { if (ev->body.type == b3s->uris.midi_MidiEvent) { /* process midi messages from player */ if (written + BUFFER_SIZE_SAMPLES < ev->time.frames && ev->time.frames < n_samples) { /* first syntheize sound up until the message timestamp */ written = synthSound(b3s, written, ev->time.frames, audio); } /* send midi message to synth, CC's will trigger hook -> update GUI */ parse_raw_midi_data(b3s->inst, (uint8_t*)(ev+1), ev->body.size); } else if (ev->body.type == b3s->uris.atom_Blank || ev->body.type == b3s->uris.atom_Object) { /* process messages from GUI */ const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body; if (obj->body.otype == b3s->uris.sb3_uiinit) { b3s->update_gui_now = 1; } else if (obj->body.otype == b3s->uris.sb3_uimccquery) { midi_loopCCAssignment(b3s->inst->midicfg, 7, mcc_cb, b3s); } else if (obj->body.otype == b3s->uris.sb3_uimccset) { const LV2_Atom* cmd = NULL; const LV2_Atom* flags = NULL; lv2_atom_object_get(obj, b3s->uris.sb3_cckey, &flags, b3s->uris.sb3_ccval, &cmd, 0); if (cmd && flags) { midi_uiassign_cc(b3s->inst->midicfg, (const char*)LV2_ATOM_BODY(cmd), ((LV2_Atom_Int*)flags)->body); } } else if (obj->body.otype == b3s->uris.sb3_midipgm) { const LV2_Atom* key = NULL; lv2_atom_object_get(obj, b3s->uris.sb3_cckey, &key, 0); if (key) { installProgram(b3s->inst, ((LV2_Atom_Int*)key)->body); } } else if (obj->body.otype == b3s->uris.sb3_midisavepgm) { const LV2_Atom* pgm = NULL; const LV2_Atom* name = NULL; lv2_atom_object_get(obj, b3s->uris.sb3_cckey, &pgm, b3s->uris.sb3_ccval, &name, 0); if (pgm && name) { saveProgramm(b3s->inst, (int) ((LV2_Atom_Int*)pgm)->body, (char*) LV2_ATOM_BODY(name), 0); b3s->update_pgm_now = 1; } } else if (obj->body.otype == b3s->uris.sb3_loadpgm) { iowork(b3s, obj, CMD_LOADPGM); } else if (obj->body.otype == b3s->uris.sb3_loadcfg) { iowork(b3s, obj, CMD_LOADCFG); } else if (obj->body.otype == b3s->uris.sb3_savepgm) { iowork(b3s, obj, CMD_SAVEPGM); } else if (obj->body.otype == b3s->uris.sb3_savecfg) { iowork(b3s, obj, CMD_SAVECFG); } else if (obj->body.otype == b3s->uris.sb3_cfgstr) { if (!b3s->inst_offline) { advanced_config_set(b3s, obj); } } else if (obj->body.otype == b3s->uris.sb3_control) { b3s->suspend_ui_msg = 1; const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body; char *k; int v; if (!get_cc_key_value(&b3s->uris, obj, &k, &v)) { #ifdef DEBUGPRINT fprintf(stderr, "B3LV2: callMIDIControlFunction(..,\"%s\", %d);\n", k, v); #endif callMIDIControlFunction(b3s->inst->midicfg, k, v); } b3s->suspend_ui_msg = 0; } } ev = lv2_atom_sequence_next(ev); } } /* synthesize [remaining] sound */ synthSound(b3s, written, n_samples, audio); /* send active keys to GUI - IFF changed */ bool keychanged = false; for (int i = 0 ; i < MAX_KEYS/32; ++i) { if (b3s->active_keys[i] != b3s->inst->synth->_activeKeys[i]) { keychanged = true; } b3s->active_keys[i] = b3s->inst->synth->_activeKeys[i]; } if (keychanged) { LV2_Atom_Forge_Frame frame; lv2_atom_forge_frame_time(&b3s->forge, 0); x_forge_object(&b3s->forge, &frame, 1, b3s->uris.sb3_activekeys); lv2_atom_forge_property_head(&b3s->forge, b3s->uris.sb3_keyarrary, 0); lv2_atom_forge_vector(&b3s->forge, sizeof(unsigned int), b3s->uris.atom_Int, MAX_KEYS/32, b3s->active_keys); lv2_atom_forge_pop(&b3s->forge, &frame); } /* check for new instances */ postrun(b3s); if (b3s->update_gui_now) { b3s->update_gui_now = 0; b3s->update_pgm_now = 1; b3s->suspend_ui_msg = 1; rc_loop_state(b3s->inst->state, rc_cb, b3s); b3s->suspend_ui_msg = 0; forge_kvconfigmessage(&b3s->forge, &b3s->uris, b3s->uris.sb3_cfgkv, "lv2.info", b3s->lv2nfo); forge_kvcontrolmessage(&b3s->forge, &b3s->uris, "special.init", (int32_t) b3s->thirtysec); } else if (b3s->update_pgm_now) { b3s->update_pgm_now = 0; loopProgammes(b3s->inst->progs, 1, pgm_cb, b3s); } }
static void port_event (LV2UI_Handle handle, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer) { BITui* ui = (BITui*)handle; const EBULV2URIs* uris = &ui->uris; if (format != uris->atom_eventTransfer) { return; } LV2_Atom* atom = (LV2_Atom*)buffer; if (atom->type != uris->atom_Blank && atom->type != uris->atom_Object) { fprintf (stderr, "UI: Unknown message type.\n"); return; } LV2_Atom_Object* obj = (LV2_Atom_Object*)atom; if (obj->body.otype == uris->mtr_control) { int k; float v; get_cc_key_value (&ui->uris, obj, &k, &v); if (k == CTL_SAMPLERATE) { if (v > 0) { ui->rate = v; } queue_draw (ui->m0); } } else if (obj->body.otype == uris->bim_stats) { LV2_Atom *bcnt = NULL; LV2_Atom *bmin = NULL; LV2_Atom *bmax = NULL; LV2_Atom *bnan = NULL; LV2_Atom *binf = NULL; LV2_Atom *bden = NULL; LV2_Atom *bpos = NULL; LV2_Atom *bnul = NULL; LV2_Atom *bdat = NULL; if (9 == lv2_atom_object_get (obj, uris->ebu_integr_time, &bcnt, uris->bim_zero, &bnul, uris->bim_pos, &bpos, uris->bim_max, &bmax, uris->bim_min, &bmin, uris->bim_nan, &bnan, uris->bim_inf, &binf, uris->bim_den, &bden, uris->bim_data, &bdat, NULL) && bcnt && bnul && bpos && bmin && bmax && bnan && binf && bden && bdat && bcnt->type == uris->atom_Long && bpos->type == uris->atom_Int && bnul->type == uris->atom_Int && bmin->type == uris->atom_Double && bmax->type == uris->atom_Double && bnan->type == uris->atom_Int && binf->type == uris->atom_Int && bden->type == uris->atom_Int && bdat->type == uris->atom_Vector ) { CB_INT(bnan, update_oops, 0); CB_INT(binf, update_oops, 1); CB_INT(bden, update_oops, 2); PARSE_A_INT(bpos, ui->f_pos); PARSE_A_INT(bnul, ui->f_zero); CB_DBL(bmin, update_minmax, 0); CB_DBL(bmax, update_minmax, 1); LV2_Atom_Vector* data = (LV2_Atom_Vector*)LV2_ATOM_BODY(bdat); if (data->atom.type == uris->atom_Int) { const size_t n_elem = (bdat->size - sizeof (LV2_Atom_Vector_Body)) / data->atom.size; assert (n_elem == BIM_LAST); const int32_t *d = (int32_t*) LV2_ATOM_BODY(&data->atom); memcpy (ui->flt, d, sizeof (int32_t) * n_elem); } update_time (ui, (uint64_t)(((LV2_Atom_Long*)bcnt)->body)); btn_start_sens (ui); // maybe set 2^31 limit. queue_draw (ui->m0); } } else if (obj->body.otype == uris->bim_information) { LV2_Atom *ii = NULL; LV2_Atom *av = NULL; lv2_atom_object_get (obj, uris->ebu_integrating, &ii, uris->bim_averaging, &av, NULL); ui->disable_signals = true; if (ii && ii->type == uris->atom_Bool) { bool ix = ((LV2_Atom_Bool*)ii)->body; robtk_cbtn_set_active (ui->btn_freeze, !ix); } if (av && av->type == uris->atom_Bool) { bool ix = ((LV2_Atom_Bool*)av)->body; robtk_cbtn_set_active (ui->btn_avg, ix); } ui->disable_signals = false; } else { fprintf (stderr, "UI: Unknown control message.\n"); } }
static void run(LV2_Handle instance, uint32_t n_samples) { uint32_t i,c; BalanceControl* self = (BalanceControl*)instance; const float balance = *self->balance; const float trim = db_to_gain(*self->trim); float gain_left = 1.0; float gain_right = 1.0; const int ascnt = self->samplerate / UPDATE_FREQ; const uint32_t capacity = self->notify->atom.size; lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->notify, capacity); lv2_atom_forge_sequence_head(&self->forge, &self->frame, 0); /* reset after state restore */ if (self->queue_stateswitch) { self->queue_stateswitch = 0; self->peak_integrate_pref = self->state[0] * self->samplerate; self->meter_falloff = self->state[1] / UPDATE_FREQ; self->peak_hold = self->state[2] * UPDATE_FREQ; self->peak_integrate_pref = MAX(0, self->peak_integrate_pref); self->peak_integrate_pref = MIN(self->peak_integrate_pref, self->peak_integrate_max); self->meter_falloff = MAX(0, self->meter_falloff); self->meter_falloff = MIN(self->meter_falloff, 1000); self->peak_hold = MAX(0, self->peak_hold); self->peak_hold = MIN(self->peak_hold, 60 * UPDATE_FREQ); reset_uicom(self); send_cfg_to_ui(self); } /* Process incoming events from GUI */ if (self->control) { LV2_Atom_Event* ev = lv2_atom_sequence_begin(&(self->control)->body); while(!lv2_atom_sequence_is_end(&(self->control)->body, (self->control)->atom.size, ev)) { if (ev->body.type == self->uris.atom_Blank || ev->body.type == self->uris.atom_Object) { const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body; if (obj->body.otype == self->uris.blc_meters_on) { if (self->uicom_active == 0) { reset_uicom(self); send_cfg_to_ui(self); self->uicom_active = 1; } } if (obj->body.otype == self->uris.blc_meters_off) { self->uicom_active = 0; } if (obj->body.otype == self->uris.blc_meters_cfg) { const LV2_Atom* key = NULL; const LV2_Atom* value = NULL; lv2_atom_object_get(obj, self->uris.blc_cckey, &key, self->uris.blc_ccval, &value, 0); if (value && key) { update_meter_cfg(self, ((LV2_Atom_Int*)key)->body, ((LV2_Atom_Float*)value)->body); } } } ev = lv2_atom_sequence_next(ev); } } /* pre-calculate parameters */ if (balance < 0) { gain_right = 1.0 + RAIL(balance, -1.0, 0.0); } else if (balance > 0) { gain_left = 1.0 - RAIL(balance, 0.0, 1.0); } switch ((int) *self->unitygain) { case 1: { /* maintain amplitude sum */ const double gaindiff = (gain_left - gain_right); gain_left = 1.0 + gaindiff; gain_right = 1.0 - gaindiff; } break; case 2: { /* equal power*/ if (balance < 0) { gain_right = MAX(.5, gain_right); gain_left = db_to_gain(-gain_to_db(gain_right)); } else { gain_left = MAX(.5, gain_left); gain_right = db_to_gain(-gain_to_db(gain_left)); } } case 0: /* 'tradidional' balance */ break; } if (*(self->phase[C_LEFT])) gain_left *=-1; if (*(self->phase[C_RIGHT])) gain_right *=-1; /* keep track of input levels -- only if GUI is visiable */ if (self->uicom_active) { for (c=0; c < CHANNELS; ++c) { for (i=0; i < n_samples; ++i) { /* input peak meter */ const float ps = fabsf(self->input[c][i]); if (ps > self->p_peak_in[c]) self->p_peak_in[c] = ps; if (self->peak_integrate_pref < 1) { const float psm = ps * ps; if (psm > self->p_peak_inM[c]) self->p_peak_inM[c] = psm; continue; } /* integrated level, peak */ const int pip = (self->peak_integrate_pos + i ) % self->peak_integrate_pref; const double p_sig = SQUARE(self->input[c][i]); self->p_peak_inP[c] += p_sig - self->p_peak_inPi[c][pip]; self->p_peak_inPi[c][pip] = p_sig; /* peak of integrated signal */ const float psm = self->p_peak_inP[c] / (double) self->peak_integrate_pref; if (psm > self->p_peak_inM[c]) self->p_peak_inM[c] = psm; } } } /* process audio -- delayline + balance & gain */ process_channel(self, gain_left * trim, C_LEFT, n_samples); process_channel(self, gain_right * trim, C_RIGHT, n_samples); /* swap/assign channels */ uint32_t pos = 0; if (self->c_monomode != (int) *self->monomode) { /* smooth change */ const uint32_t fade_len = (n_samples >= FADE_LEN) ? FADE_LEN : n_samples; for (; pos < fade_len; pos++) { const float gain = (float)pos / (float)fade_len; float x1[CHANNELS], x2[CHANNELS]; channel_map_change(self, self->c_monomode, pos, x1); channel_map_change(self, (int) *self->monomode, pos, x2); self->output[C_LEFT][pos] = x1[C_LEFT] * (1.0 - gain) + x2[C_LEFT] * gain; self->output[C_RIGHT][pos] = x1[C_RIGHT] * (1.0 - gain) + x2[C_RIGHT] * gain; } } channel_map(self, (int) *self->monomode, pos, n_samples); self->c_monomode = (int) *self->monomode; /* audio processing done */ if (!self->uicom_active) { return; } /* output peak meter */ for (c=0; c < CHANNELS; ++c) { for (i=0; i < n_samples; ++i) { /* peak */ const float ps = fabsf(self->output[c][i]); if (ps > self->p_peak_out[c]) self->p_peak_out[c] = ps; if (self->peak_integrate_pref < 1) { const float psm = ps * ps; if (psm > self->p_peak_outM[c]) self->p_peak_outM[c] = psm; continue; } /* integrated level, peak */ const int pip = (self->peak_integrate_pos + i ) % self->peak_integrate_pref; const double p_sig = SQUARE(self->output[c][i]); self->p_peak_outP[c] += p_sig - self->p_peak_outPi[c][pip]; self->p_peak_outPi[c][pip] = p_sig; /* peak of integrated signal */ const float psm = self->p_peak_outP[c] / (double) self->peak_integrate_pref; if (psm > self->p_peak_outM[c]) self->p_peak_outM[c] = psm; } } if (self->peak_integrate_pref > 0) { self->peak_integrate_pos = (self->peak_integrate_pos + n_samples ) % self->peak_integrate_pref; } /* simple output phase correlation */ for (i=0; i < n_samples; ++i) { const double p_pos = SQUARE(self->output[C_LEFT][i] + self->output[C_RIGHT][i]); const double p_neg = SQUARE(self->output[C_LEFT][i] - self->output[C_RIGHT][i]); /* integrate over 500ms */ self->p_phase_outP += p_pos - self->p_phase_outPi[self->phase_integrate_pos]; self->p_phase_outN += p_neg - self->p_phase_outNi[self->phase_integrate_pos]; self->p_phase_outPi[self->phase_integrate_pos] = p_pos; self->p_phase_outNi[self->phase_integrate_pos] = p_neg; self->phase_integrate_pos = (self->phase_integrate_pos + 1) % self->phase_integrate_max; } /* abs peak hold */ #define PKM(A,CHN,ID) \ { \ const float peak = VALTODB(self->p_peak_##A[CHN]); \ if (peak > self->p_max_##A[CHN]) { \ self->p_max_##A[CHN] = peak; \ self->p_tme_##A[CHN] = 0; \ forge_kvcontrolmessage(&self->forge, &self->uris, ID, self->p_max_##A[CHN]); \ } else if (self->peak_hold <= 0) { \ (self->p_tme_##A[CHN])=0; /* infinite hold */ \ } else if (self->p_tme_##A[CHN] <= self->peak_hold) { \ (self->p_tme_##A[CHN])++; \ } else if (self->meter_falloff == 0) { \ self->p_max_##A[CHN] = peak; \ forge_kvcontrolmessage(&self->forge, &self->uris, ID, self->p_max_##A[CHN]); \ } else { \ self->p_max_##A[CHN] -= self->meter_falloff; \ self->p_max_##A[CHN] = MAX(peak, self->p_max_##A[CHN]); \ forge_kvcontrolmessage(&self->forge, &self->uris, ID, self->p_max_##A[CHN]); \ } \ } /* RMS meter */ #define PKF(A,CHN,ID) \ { \ float dbp = VALTODB(sqrt(2.0 * self->p_peak_##A##M[CHN])); \ if (dbp > self->p_vpeak_##A[CHN]) { \ self->p_vpeak_##A[CHN] = dbp; \ } else if (self->meter_falloff == 0) { \ self->p_vpeak_##A[CHN] = dbp; \ } else { \ self->p_vpeak_##A[CHN] -= self->meter_falloff; \ self->p_vpeak_##A[CHN] = MAX(dbp, self->p_vpeak_##A[CHN]); \ } \ forge_kvcontrolmessage(&self->forge, &self->uris, ID, (self->p_vpeak_##A [CHN])); \ } /* report peaks to UI */ self->p_peakcnt += n_samples; if (self->p_peakcnt > ascnt) { PKF(in, C_LEFT, METER_IN_LEFT) PKF(in, C_RIGHT, METER_IN_RIGHT); PKF(out, C_LEFT, METER_OUT_LEFT); PKF(out, C_RIGHT, METER_OUT_RIGHT); PKM(in, C_LEFT, PEAK_IN_LEFT); PKM(in, C_RIGHT, PEAK_IN_RIGHT); PKM(out, C_LEFT, PEAK_OUT_LEFT); PKM(out, C_RIGHT, PEAK_OUT_RIGHT); #define RMSF(A) sqrt( ( (A) / (double)self->phase_integrate_max ) + 1.0e-12 ) double phase = 0.0; const double phasdiv = self->p_phase_outP + self->p_phase_outN; if (phasdiv >= 1.0e-6) { phase = (RMSF(self->p_phase_outP) - RMSF(self->p_phase_outN)) / RMSF(phasdiv); } else if (self->p_phase_outP > .001 && self->p_phase_outN > .001) { phase = 1.0; } forge_kvcontrolmessage(&self->forge, &self->uris, PHASE_OUT, phase); self->p_peakcnt -= ascnt; for (c=0; c < CHANNELS; ++c) { self->p_peak_in[c] = -INFINITY; self->p_peak_out[c] = -INFINITY; self->p_peak_inM[c] = -INFINITY; self->p_peak_outM[c] = -INFINITY; } } /* report values to UI - if changed*/ float bal = gain_to_db(fabsf(gain_left)); if (bal != self->p_bal[C_LEFT]) { forge_kvcontrolmessage(&self->forge, &self->uris, GAIN_LEFT, bal); } self->p_bal[C_LEFT] = bal; bal = gain_to_db(fabsf(gain_right)); if (bal != self->p_bal[C_RIGHT]) { forge_kvcontrolmessage(&self->forge, &self->uris, GAIN_RIGHT, bal); } self->p_bal[C_RIGHT] = bal; if (self->p_dly[C_LEFT] != self->c_dly[C_LEFT]) { forge_kvcontrolmessage(&self->forge, &self->uris, DELAY_LEFT, (float) self->c_dly[C_LEFT] / self->samplerate); } self->p_dly[C_LEFT] = self->c_dly[C_LEFT]; if (self->p_dly[C_RIGHT] != self->c_dly[C_RIGHT]) { forge_kvcontrolmessage(&self->forge, &self->uris, DELAY_RIGHT, (float) self->c_dly[C_RIGHT] / self->samplerate); } self->p_dly[C_RIGHT] = self->c_dly[C_RIGHT]; }
/** ==== Run Method ==== */ static void run(LV2_Handle handle, uint32_t n_samples) { EgScope* self = (EgScope*)handle; /* Ensure notify port buffer is large enough to hold all audio-samples and configuration settings. A minimum size was requested in the .ttl file, but check here just to be sure. TODO: Explain these magic numbers. */ const size_t size = (sizeof(float) * n_samples + 64) * self->n_channels; const uint32_t space = self->notify->atom.size; if (space < size + 128) { /* Insufficient space, report error and do nothing. Note that a real-time production plugin mustn't call log functions in run(), but this can be useful for debugging and example purposes. */ lv2_log_error(&self->logger, "Buffer size is insufficient\n"); return; } // Prepare forge buffer and initialize atom-sequence lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->notify, space); lv2_atom_forge_sequence_head(&self->forge, &self->frame, 0); /* Send settings to UI The plugin can continue to run while the UI is closed and re-opened. The state and settings of the UI are kept here and transmitted to the UI every time it asks for them or if the user initializes a 'load preset'. */ if (self->send_settings_to_ui && self->ui_active) { self->send_settings_to_ui = false; // Forge container object of type 'ui_state' LV2_Atom_Forge_Frame frame; lv2_atom_forge_frame_time(&self->forge, 0); lv2_atom_forge_object(&self->forge, &frame, 0, self->uris.ui_State); // Add UI state as properties lv2_atom_forge_key(&self->forge, self->uris.ui_spp); lv2_atom_forge_int(&self->forge, self->ui_spp); lv2_atom_forge_key(&self->forge, self->uris.ui_amp); lv2_atom_forge_float(&self->forge, self->ui_amp); lv2_atom_forge_key(&self->forge, self->uris.param_sampleRate); lv2_atom_forge_float(&self->forge, self->rate); lv2_atom_forge_pop(&self->forge, &frame); } // Process incoming events from GUI if (self->control) { const LV2_Atom_Event* ev = lv2_atom_sequence_begin( &(self->control)->body); // For each incoming message... while (!lv2_atom_sequence_is_end( &self->control->body, self->control->atom.size, ev)) { // If the event is an atom:Blank object if (ev->body.type == self->uris.atom_Blank) { const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body; if (obj->body.otype == self->uris.ui_On) { // If the object is a ui-on, the UI was activated self->ui_active = true; self->send_settings_to_ui = true; } else if (obj->body.otype == self->uris.ui_Off) { // If the object is a ui-off, the UI was closed self->ui_active = false; } else if (obj->body.otype == self->uris.ui_State) { // If the object is a ui-state, it's the current UI settings const LV2_Atom* spp = NULL; const LV2_Atom* amp = NULL; lv2_atom_object_get(obj, self->uris.ui_spp, &spp, self->uris.ui_amp, &, 0); if (spp) { self->ui_spp = ((const LV2_Atom_Int*)spp)->body; } if (amp) { self->ui_amp = ((const LV2_Atom_Float*)amp)->body; } } } ev = lv2_atom_sequence_next(ev); } } // Process audio data for (uint32_t c = 0; c < self->n_channels; ++c) { if (self->ui_active) { // If UI is active, send raw audio data to UI tx_rawaudio(&self->forge, &self->uris, c, n_samples, self->input[c]); } // If not processing audio in-place, forward audio if (self->input[c] != self->output[c]) { memcpy(self->output[c], self->input[c], sizeof(float) * n_samples); } } // Close off sequence lv2_atom_forge_pop(&self->forge, &self->frame); }
static void port_event(LV2UI_Handle handle, uint32_t port_index, uint32_t buffer_size, uint32_t format, const void* buffer) { SFSUI* ui = (SFSUI*)handle; LV2_Atom* atom = (LV2_Atom*)buffer; if (format == ui->uris.atom_eventTransfer && (atom->type == ui->uris.atom_Blank|| atom->type == ui->uris.atom_Object)) { /* cast the buffer to Atom Object */ LV2_Atom_Object* obj = (LV2_Atom_Object*)atom; LV2_Atom *a0 = NULL; LV2_Atom *a1 = NULL; if (obj->body.otype == ui->uris.rawstereo && 2 == lv2_atom_object_get(obj, ui->uris.audioleft, &a0, ui->uris.audioright, &a1, NULL) && a0 && a1 && a0->type == ui->uris.atom_Vector && a1->type == ui->uris.atom_Vector ) { LV2_Atom_Vector* left = (LV2_Atom_Vector*)LV2_ATOM_BODY(a0); LV2_Atom_Vector* right = (LV2_Atom_Vector*)LV2_ATOM_BODY(a1); if (left->atom.type == ui->uris.atom_Float && right->atom.type == ui->uris.atom_Float) { const size_t n_elem = (a0->size - sizeof(LV2_Atom_Vector_Body)) / left->atom.size; const float *l = (float*) LV2_ATOM_BODY(&left->atom); const float *r = (float*) LV2_ATOM_BODY(&right->atom); process_audio(ui, n_elem, l, r); } } else if ( /* handle 'state/settings' data object */ obj->body.otype == ui->uris.ui_state /* retrieve properties from object and * check that there the [here] three required properties are set.. */ && 1 == lv2_atom_object_get(obj, ui->uris.samplerate, &a0, NULL) /* ..and non-null.. */ && a0 /* ..and match the expected type */ && a0->type == ui->uris.atom_Float ) { ui->rate = ((LV2_Atom_Float*)a0)->body; reinitialize_fft(ui, ui->fft_bins); } } else if (format != 0) return; if (port_index == SS_FFT) { float val = *(float *)buffer; uint32_t fft_bins = floorf(val / 2.0); if (ui->fft_bins != fft_bins) { reinitialize_fft(ui, fft_bins); robtk_select_set_value(ui->sel_fft, ui->fft_bins); } } else if (port_index == SS_BAND) { float val = *(float *)buffer; ui->disable_signals = true; robtk_cbtn_set_active(ui->btn_oct, val != 0); ui->disable_signals = false; } else if (port_index == SS_SCREEN) { ui->disable_signals = true; robtk_dial_set_value(ui->screen, *(float *)buffer); ui->disable_signals = false; } }
static void run(LV2_Handle handle, uint32_t n_samples) { SiSco* self = (SiSco*)handle; const uint32_t size = (sizeof(float) * n_samples + 80) * self->n_channels; const uint32_t capacity = self->notify->atom.size; bool capacity_ok = true; /* check if atom-port buffer is large enough to hold * all audio-samples and configuration settings */ if (capacity < size + 216 + self->n_channels * 16) { capacity_ok = false; if (!self->printed_capacity_warning) { fprintf(stderr, "SiSco.lv2 error: LV2 comm-buffersize is insufficient %d/%d bytes.\n", capacity, size + 216 + self->n_channels * 16); self->printed_capacity_warning = true; } } /* prepare forge buffer and initialize atom-sequence */ lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->notify, capacity); lv2_atom_forge_sequence_head(&self->forge, &self->frame, 0); /* Send settings to UI */ if (self->send_settings_to_ui && self->ui_active) { self->send_settings_to_ui = false; /* forge container object of type 'ui_state' */ LV2_Atom_Forge_Frame frame; lv2_atom_forge_frame_time(&self->forge, 0); x_forge_object(&self->forge, &frame, 1, self->uris.ui_state); /* forge attributes for 'ui_state' */ lv2_atom_forge_property_head(&self->forge, self->uris.samplerate, 0); lv2_atom_forge_float(&self->forge, capacity_ok ? self->rate : 0); lv2_atom_forge_property_head(&self->forge, self->uris.ui_state_grid, 0); lv2_atom_forge_int(&self->forge, self->ui_grid); lv2_atom_forge_property_head(&self->forge, self->uris.ui_state_trig, 0); lv2_atom_forge_vector(&self->forge, sizeof(float), self->uris.atom_Float, sizeof(struct triggerstate) / sizeof(float), &self->triggerstate); lv2_atom_forge_property_head(&self->forge, self->uris.ui_state_curs, 0); lv2_atom_forge_vector(&self->forge, sizeof(int32_t), self->uris.atom_Int, sizeof(struct cursorstate) / sizeof(int32_t), &self->cursorstate); lv2_atom_forge_property_head(&self->forge, self->uris.ui_state_chn, 0); lv2_atom_forge_vector(&self->forge, sizeof(float), self->uris.atom_Float, self->n_channels * sizeof(struct channelstate) / sizeof(float), self->channelstate); lv2_atom_forge_property_head(&self->forge, self->uris.ui_state_misc, 0); lv2_atom_forge_int(&self->forge, self->ui_misc); /* close-off frame */ lv2_atom_forge_pop(&self->forge, &frame); } /* Process incoming events from GUI */ if (self->control) { LV2_Atom_Event* ev = lv2_atom_sequence_begin(&(self->control)->body); /* for each message from UI... */ while(!lv2_atom_sequence_is_end(&(self->control)->body, (self->control)->atom.size, ev)) { /* .. only look at atom-events.. */ if (ev->body.type == self->uris.atom_Blank || ev->body.type == self->uris.atom_Object) { const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body; /* interpret atom-objects: */ if (obj->body.otype == self->uris.ui_on) { /* UI was activated */ self->ui_active = true; self->send_settings_to_ui = true; } else if (obj->body.otype == self->uris.ui_off) { /* UI was closed */ self->ui_active = false; } else if (obj->body.otype == self->uris.ui_state) { /* UI sends current settings */ const LV2_Atom* grid = NULL; const LV2_Atom* trig = NULL; const LV2_Atom* curs = NULL; const LV2_Atom* misc = NULL; const LV2_Atom* chn = NULL; lv2_atom_object_get(obj, self->uris.ui_state_grid, &grid, self->uris.ui_state_trig, &trig, self->uris.ui_state_curs, &curs, self->uris.ui_state_misc, &misc, self->uris.ui_state_chn, &chn, 0); if (grid && grid->type == self->uris.atom_Int) { self->ui_grid = ((LV2_Atom_Int*)grid)->body; } if (misc && misc->type == self->uris.atom_Int) { self->ui_misc = ((LV2_Atom_Int*)misc)->body; } if (trig && trig->type == self->uris.atom_Vector) { LV2_Atom_Vector *vof = (LV2_Atom_Vector*)LV2_ATOM_BODY(trig); if (vof->atom.type == self->uris.atom_Float) { struct triggerstate *ts = (struct triggerstate *) LV2_ATOM_BODY(&vof->atom); memcpy(&self->triggerstate, ts, sizeof(struct triggerstate)); } } if (curs && curs->type == self->uris.atom_Vector) { LV2_Atom_Vector *vof = (LV2_Atom_Vector*)LV2_ATOM_BODY(curs); if (vof->atom.type == self->uris.atom_Int) { struct cursorstate *cs = (struct cursorstate *) LV2_ATOM_BODY(&vof->atom); memcpy(&self->cursorstate, cs, sizeof(struct cursorstate)); } } if (chn && chn->type == self->uris.atom_Vector) { LV2_Atom_Vector *vof = (LV2_Atom_Vector*)LV2_ATOM_BODY(chn); if (vof->atom.type == self->uris.atom_Float) { struct channelstate *cs = (struct channelstate *) LV2_ATOM_BODY(&vof->atom); memcpy(self->channelstate, cs, self->n_channels * sizeof(struct channelstate)); } } } } ev = lv2_atom_sequence_next(ev); } } /* process audio data */ for (uint32_t c = 0; c < self->n_channels; ++c) { if (self->ui_active && capacity_ok) { /* if UI is active, send raw audio data to UI */ tx_rawaudio(&self->forge, &self->uris, c, n_samples, self->input[c]); } /* if not processing in-place, forward audio */ if (self->input[c] != self->output[c]) { memcpy(self->output[c], self->input[c], sizeof(float) * n_samples); } } /* close off atom-sequence */ lv2_atom_forge_pop(&self->forge, &self->frame); }
void lv2_run(const uint32_t sampleCount) { // cache midi input and time position first #if DISTRHO_PLUGIN_WANT_MIDI_INPUT uint32_t midiEventCount = 0; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) { if (event == nullptr) break; # if DISTRHO_PLUGIN_WANT_MIDI_INPUT if (event->body.type == fURIDs.midiEvent) { if (midiEventCount >= kMaxMidiEvents) continue; const uint8_t* const data((const uint8_t*)(event + 1)); MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); midiEvent.frame = event->time.frames; midiEvent.size = event->body.size; if (midiEvent.size > MidiEvent::kDataSize) { midiEvent.dataExt = data; std::memset(midiEvent.data, 0, MidiEvent::kDataSize); } else { midiEvent.dataExt = nullptr; std::memcpy(midiEvent.data, data, midiEvent.size); } continue; } # endif # if DISTRHO_PLUGIN_WANT_TIMEPOS if (event->body.type == fURIDs.atomBlank || event->body.type == fURIDs.atomObject) { const LV2_Atom_Object* const obj((const LV2_Atom_Object*)&event->body); if (obj->body.otype != fURIDs.timePosition) continue; LV2_Atom* bar = nullptr; LV2_Atom* barBeat = nullptr; LV2_Atom* beatUnit = nullptr; LV2_Atom* beatsPerBar = nullptr; LV2_Atom* beatsPerMinute = nullptr; LV2_Atom* frame = nullptr; LV2_Atom* speed = nullptr; LV2_Atom* ticksPerBeat = nullptr; lv2_atom_object_get(obj, fURIDs.timeBar, &bar, fURIDs.timeBarBeat, &barBeat, fURIDs.timeBeatUnit, &beatUnit, fURIDs.timeBeatsPerBar, &beatsPerBar, fURIDs.timeBeatsPerMinute, &beatsPerMinute, fURIDs.timeFrame, &frame, fURIDs.timeSpeed, &speed, fURIDs.timeTicksPerBeat, &ticksPerBeat, nullptr); // need to handle this first as other values depend on it if (ticksPerBeat != nullptr) { /**/ if (ticksPerBeat->type == fURIDs.atomDouble) fLastPositionData.ticksPerBeat = ((LV2_Atom_Double*)ticksPerBeat)->body; else if (ticksPerBeat->type == fURIDs.atomFloat) fLastPositionData.ticksPerBeat = ((LV2_Atom_Float*)ticksPerBeat)->body; else if (ticksPerBeat->type == fURIDs.atomInt) fLastPositionData.ticksPerBeat = ((LV2_Atom_Int*)ticksPerBeat)->body; else if (ticksPerBeat->type == fURIDs.atomLong) fLastPositionData.ticksPerBeat = ((LV2_Atom_Long*)ticksPerBeat)->body; else d_stderr("Unknown lv2 ticksPerBeat value type"); if (fLastPositionData.ticksPerBeat > 0) fTimePosition.bbt.ticksPerBeat = fLastPositionData.ticksPerBeat; } // same if (speed != nullptr) { /**/ if (speed->type == fURIDs.atomDouble) fLastPositionData.speed = ((LV2_Atom_Double*)speed)->body; else if (speed->type == fURIDs.atomFloat) fLastPositionData.speed = ((LV2_Atom_Float*)speed)->body; else if (speed->type == fURIDs.atomInt) fLastPositionData.speed = ((LV2_Atom_Int*)speed)->body; else if (speed->type == fURIDs.atomLong) fLastPositionData.speed = ((LV2_Atom_Long*)speed)->body; else d_stderr("Unknown lv2 speed value type"); fTimePosition.playing = d_isNotZero(fLastPositionData.speed); } if (bar != nullptr) { /**/ if (bar->type == fURIDs.atomDouble) fLastPositionData.bar = ((LV2_Atom_Double*)bar)->body; else if (bar->type == fURIDs.atomFloat) fLastPositionData.bar = ((LV2_Atom_Float*)bar)->body; else if (bar->type == fURIDs.atomInt) fLastPositionData.bar = ((LV2_Atom_Int*)bar)->body; else if (bar->type == fURIDs.atomLong) fLastPositionData.bar = ((LV2_Atom_Long*)bar)->body; else d_stderr("Unknown lv2 bar value type"); if (fLastPositionData.bar >= 0) fTimePosition.bbt.bar = fLastPositionData.bar + 1; } if (barBeat != nullptr) { /**/ if (barBeat->type == fURIDs.atomDouble) fLastPositionData.barBeat = ((LV2_Atom_Double*)barBeat)->body; else if (barBeat->type == fURIDs.atomFloat) fLastPositionData.barBeat = ((LV2_Atom_Float*)barBeat)->body; else if (barBeat->type == fURIDs.atomInt) fLastPositionData.barBeat = ((LV2_Atom_Int*)barBeat)->body; else if (barBeat->type == fURIDs.atomLong) fLastPositionData.barBeat = ((LV2_Atom_Long*)barBeat)->body; else d_stderr("Unknown lv2 barBeat value type"); if (fLastPositionData.barBeat >= 0.0f) { const double rest = std::fmod(fLastPositionData.barBeat, 1.0); fTimePosition.bbt.beat = fLastPositionData.barBeat-rest+1.0; fTimePosition.bbt.tick = rest*fTimePosition.bbt.ticksPerBeat+0.5; } } if (beatUnit != nullptr) { /**/ if (beatUnit->type == fURIDs.atomDouble) fLastPositionData.beatUnit = ((LV2_Atom_Double*)beatUnit)->body; else if (beatUnit->type == fURIDs.atomFloat) fLastPositionData.beatUnit = ((LV2_Atom_Float*)beatUnit)->body; else if (beatUnit->type == fURIDs.atomInt) fLastPositionData.beatUnit = ((LV2_Atom_Int*)beatUnit)->body; else if (beatUnit->type == fURIDs.atomLong) fLastPositionData.beatUnit = ((LV2_Atom_Long*)beatUnit)->body; else d_stderr("Unknown lv2 beatUnit value type"); if (fLastPositionData.beatUnit > 0) fTimePosition.bbt.beatType = fLastPositionData.beatUnit; } if (beatsPerBar != nullptr) { /**/ if (beatsPerBar->type == fURIDs.atomDouble) fLastPositionData.beatsPerBar = ((LV2_Atom_Double*)beatsPerBar)->body; else if (beatsPerBar->type == fURIDs.atomFloat) fLastPositionData.beatsPerBar = ((LV2_Atom_Float*)beatsPerBar)->body; else if (beatsPerBar->type == fURIDs.atomInt) fLastPositionData.beatsPerBar = ((LV2_Atom_Int*)beatsPerBar)->body; else if (beatsPerBar->type == fURIDs.atomLong) fLastPositionData.beatsPerBar = ((LV2_Atom_Long*)beatsPerBar)->body; else d_stderr("Unknown lv2 beatsPerBar value type"); if (fLastPositionData.beatsPerBar > 0.0f) fTimePosition.bbt.beatsPerBar = fLastPositionData.beatsPerBar; } if (beatsPerMinute != nullptr) { /**/ if (beatsPerMinute->type == fURIDs.atomDouble) fLastPositionData.beatsPerMinute = ((LV2_Atom_Double*)beatsPerMinute)->body; else if (beatsPerMinute->type == fURIDs.atomFloat) fLastPositionData.beatsPerMinute = ((LV2_Atom_Float*)beatsPerMinute)->body; else if (beatsPerMinute->type == fURIDs.atomInt) fLastPositionData.beatsPerMinute = ((LV2_Atom_Int*)beatsPerMinute)->body; else if (beatsPerMinute->type == fURIDs.atomLong) fLastPositionData.beatsPerMinute = ((LV2_Atom_Long*)beatsPerMinute)->body; else d_stderr("Unknown lv2 beatsPerMinute value type"); if (fLastPositionData.beatsPerMinute > 0.0f) { fTimePosition.bbt.beatsPerMinute = fLastPositionData.beatsPerMinute; if (d_isNotZero(fLastPositionData.speed)) fTimePosition.bbt.beatsPerMinute *= std::abs(fLastPositionData.speed); } } if (frame != nullptr) { /**/ if (frame->type == fURIDs.atomDouble) fLastPositionData.frame = ((LV2_Atom_Double*)frame)->body; else if (frame->type == fURIDs.atomFloat) fLastPositionData.frame = ((LV2_Atom_Float*)frame)->body; else if (frame->type == fURIDs.atomInt) fLastPositionData.frame = ((LV2_Atom_Int*)frame)->body; else if (frame->type == fURIDs.atomLong) fLastPositionData.frame = ((LV2_Atom_Long*)frame)->body; else d_stderr("Unknown lv2 frame value type"); if (fLastPositionData.frame >= 0) fTimePosition.frame = fLastPositionData.frame; } fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat* fTimePosition.bbt.beatsPerBar* (fTimePosition.bbt.bar-1); fTimePosition.bbt.valid = (fLastPositionData.beatsPerMinute > 0.0 && fLastPositionData.beatUnit > 0 && fLastPositionData.beatsPerBar > 0.0f); fPlugin.setTimePosition(fTimePosition); continue; } # endif } #endif // check for messages from UI #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) { if (event == nullptr) break; if (event->body.type == fURIDs.distrhoState && fWorker != nullptr) { const void* const data((const void*)(event + 1)); // check if this is our special message if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0) { for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) fNeededUiSends[i] = true; } else // no, send to DSP as usual { fWorker->schedule_work(fWorker->handle, event->body.size, data); } } } #endif // Check for updated parameters float curValue; for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) { if (fPortControls[i] == nullptr) continue; curValue = *fPortControls[i]; if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i)) { fLastControlValues[i] = curValue; fPlugin.setParameterValue(i, curValue); } } // Run plugin if (sampleCount != 0) { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, fMidiEvents, midiEventCount); #else fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); #endif #if DISTRHO_PLUGIN_WANT_TIMEPOS // update timePos for next callback if (d_isNotZero(fLastPositionData.speed)) { if (fLastPositionData.speed > 0.0) { // playing forwards fLastPositionData.frame += sampleCount; } else { // playing backwards fLastPositionData.frame -= sampleCount; if (fLastPositionData.frame < 0) fLastPositionData.frame = 0; } fTimePosition.frame = fLastPositionData.frame; if (fTimePosition.bbt.valid) { const double beatsPerMinute = fLastPositionData.beatsPerMinute * fLastPositionData.speed; const double framesPerBeat = 60.0 * fSampleRate / beatsPerMinute; const double addedBarBeats = double(sampleCount) / framesPerBeat; if (fLastPositionData.barBeat >= 0.0f) { fLastPositionData.barBeat = std::fmod(fLastPositionData.barBeat+addedBarBeats, fLastPositionData.beatsPerBar); const double rest = std::fmod(fLastPositionData.barBeat, 1.0); fTimePosition.bbt.beat = fLastPositionData.barBeat-rest+1.0; fTimePosition.bbt.tick = rest*fTimePosition.bbt.ticksPerBeat+0.5; if (fLastPositionData.bar >= 0) { fLastPositionData.bar += std::floor((fLastPositionData.barBeat+addedBarBeats)/ fLastPositionData.beatsPerBar); if (fLastPositionData.bar < 0) fLastPositionData.bar = 0; fTimePosition.bbt.bar = fLastPositionData.bar + 1; fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat* fTimePosition.bbt.beatsPerBar* (fTimePosition.bbt.bar-1); } } fTimePosition.bbt.beatsPerMinute = std::abs(beatsPerMinute); } } #endif } updateParameterOutputs(); #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI const uint32_t capacity = fPortEventsOut->atom.size; bool needsInit = true; uint32_t size, offset = 0; LV2_Atom_Event* aev; // TODO - MIDI Output for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) { if (! fNeededUiSends[i]) continue; const String& key = fPlugin.getStateKey(i); for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) { const String& curKey = cit->first; if (curKey != key) continue; const String& value = cit->second; // set msg size (key + value + separator + 2x null terminator) const size_t msgSize(key.length()+value.length()+3); if (sizeof(LV2_Atom_Event) + msgSize > capacity - offset) break; if (needsInit) { fPortEventsOut->atom.size = 0; fPortEventsOut->atom.type = fURIDs.atomSequence; fPortEventsOut->body.unit = 0; fPortEventsOut->body.pad = 0; needsInit = false; } // reserve msg space char msgBuf[msgSize]; std::memset(msgBuf, 0, msgSize); // write key and value in atom bufer std::memcpy(msgBuf, key.buffer(), key.length()); std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()); // put data aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fPortEventsOut) + offset); aev->time.frames = 0; aev->body.type = fURIDs.distrhoState; aev->body.size = msgSize; std::memcpy(LV2_ATOM_BODY(&aev->body), msgBuf, msgSize-1); size = lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize); offset += size; fPortEventsOut->atom.size += size; fNeededUiSends[i] = false; break; } } #endif }