/** non-realtime function to read config, * called from state-restore or worker-thread */ static void parse_config_file (MidiMap* self, const char* fn) { assert (self->state == NULL); FILE *f; if (!fn) { lv2_log_error (&self->logger, "MidiMap.lv2: invalid config file handle\n"); } if (!(f = fopen (fn, "r"))) { lv2_log_error (&self->logger, "MidiMap.lv2: cannot open config file '%s'\n", fn); return; } lv2_log_note (&self->logger, "MidiMap.lv2: parsing config file '%s'\n", fn); self->state = calloc (1, sizeof (RuleSet)); char line[MAX_CFG_LINE_LEN]; unsigned int lineno = 0; unsigned int cfg_version = 0; while (fgets (line, MAX_CFG_LINE_LEN - 1, f) != NULL ) { ++lineno; if (strlen (line) == MAX_CFG_LINE_LEN - 1) { lv2_log_error (&self->logger, "MidiMap.lv2: Too long config line %d\n", lineno); continue; } // strip trailing whitespace while (strlen (line) > 0 && (line[strlen (line) - 1] == '\n' || line[strlen (line) - 1] == '\r' || line[strlen (line) - 1] == ' ' || line[strlen (line) - 1] == '\t')) { line[strlen (line) - 1] = '\0'; } // ignore comments and empty lines if (strlen (line) == 0 || line[0] == '#') { continue; } parse_config_line (self, line, &cfg_version, lineno); } fclose (f); if (cfg_version > 0) { /* remember config file - for state */ free (self->cfg_file_path); self->cfg_file_path = strdup (fn); #ifndef NDEBUG char* dump = serialize_ruleset (self->state); printf ("----\n%s\n----\n", dump); free (dump); #endif } else { lv2_log_error (&self->logger, "MidiMap.lv2: error parsing config file\n"); free (self->state); self->state = NULL; } }
/** ==== Instantiate Method ==== */ static LV2_Handle instantiate(const LV2_Descriptor* descriptor, double rate, const char* bundle_path, const LV2_Feature* const* features) { (void)descriptor; // Unused variable (void)bundle_path; // Unused variable // Allocate and initialise instance structure. EgScope* self = (EgScope*)calloc(1, sizeof(EgScope)); if (!self) { return NULL; } // Get host features const char* missing = lv2_features_query( features, LV2_LOG__log, &self->logger.log, false, LV2_URID__map, &self->map, true, NULL); lv2_log_logger_set_map(&self->logger, self->map); if (missing) { lv2_log_error(&self->logger, "Missing feature <%s>\n", missing); free(self); return NULL; } // Decide which variant to use depending on the plugin URI if (!strcmp(descriptor->URI, SCO_URI "#Stereo")) { self->n_channels = 2; } else if (!strcmp(descriptor->URI, SCO_URI "#Mono")) { self->n_channels = 1; } else { free(self); return NULL; } // Initialise local variables self->ui_active = false; self->send_settings_to_ui = false; self->rate = rate; // Set default UI settings self->ui_spp = 50; self->ui_amp = 1.0; // Map URIs and initialise forge/logger map_sco_uris(self->map, &self->uris); lv2_atom_forge_init(&self->forge, self->map); return (LV2_Handle)self; }
static void parse_config_line (MidiMap* self, const char* line, unsigned int* cfg_version, unsigned int lineno) { if (0 == strncmp (line, "midimap v", 9) && strlen (line) > 9) { *cfg_version = atoi (&line[9]); return; } switch (*cfg_version) { case 1: if (!parse_line_v1 (self->state, line)) { lv2_log_error (&self->logger, "MidiMap.lv2: Parser error on line %d\n", lineno); } break; default: lv2_log_error (&self->logger, "MidiMap.lv2: invalid version '%d' on config line %d\n", *cfg_version, lineno); break; } }
static LV2_Handle instantiate (const LV2_Descriptor* descriptor, double rate, const char* bundle_path, const LV2_Feature* const* features) { MidiMap* self = (MidiMap*)calloc (1, sizeof (MidiMap)); int i; for (i=0; features[i]; ++i) { if (!strcmp (features[i]->URI, LV2_URID__map)) { self->map = (LV2_URID_Map*)features[i]->data; } else if (!strcmp (features[i]->URI, LV2_WORKER__schedule)) { self->schedule = (LV2_Worker_Schedule*)features[i]->data; } else if (!strcmp (features[i]->URI, LV2_LOG__log)) { self->log = (LV2_Log_Log*)features[i]->data; } } lv2_log_logger_init (&self->logger, self->map, self->log); if (!self->map) { lv2_log_error (&self->logger, "MidiMap.lv2 error: Host does not support urid:map\n"); free (self); return NULL; } if (!self->schedule) { lv2_log_error (&self->logger, "MidiMap.lv2 error: Host does not support worker:schedule\n"); free (self); return NULL; } self->sample_rate = rate; lv2_atom_forge_init (&self->forge, self->map); map_mem_uris (self->map, &self->uris); return (LV2_Handle)self; }
/** ==== 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); }