// Copyright 2014-2016 David Robillard // SPDX-License-Identifier: ISC #include "./uris.h" #include "lv2/atom/atom.h" #include "lv2/atom/util.h" #include "lv2/core/lv2.h" #include "lv2/core/lv2_util.h" #include "lv2/log/log.h" #include "lv2/log/logger.h" #include "lv2/midi/midi.h" #include "lv2/urid/urid.h" #include #include #include #include enum { FIFTHS_IN = 0, FIFTHS_OUT = 1 }; typedef struct { // Features LV2_URID_Map* map; LV2_Log_Logger logger; // Ports const LV2_Atom_Sequence* in_port; LV2_Atom_Sequence* out_port; // URIs FifthsURIs uris; } Fifths; static void connect_port(LV2_Handle instance, uint32_t port, void* data) { Fifths* self = (Fifths*)instance; switch (port) { case FIFTHS_IN: self->in_port = (const LV2_Atom_Sequence*)data; break; case FIFTHS_OUT: self->out_port = (LV2_Atom_Sequence*)data; break; default: break; } } static LV2_Handle instantiate(const LV2_Descriptor* descriptor, double rate, const char* path, const LV2_Feature* const* features) { // Allocate and initialise instance structure. Fifths* self = (Fifths*)calloc(1, sizeof(Fifths)); if (!self) { return NULL; } // Scan host features for URID map // clang-format off const char* missing = lv2_features_query( features, LV2_LOG__log, &self->logger.log, false, LV2_URID__map, &self->map, true, NULL); // clang-format on 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; } map_fifths_uris(self->map, &self->uris); return (LV2_Handle)self; } static void cleanup(LV2_Handle instance) { free(instance); } static void run(LV2_Handle instance, uint32_t sample_count) { Fifths* self = (Fifths*)instance; FifthsURIs* uris = &self->uris; // Struct for a 3 byte MIDI event, used for writing notes typedef struct { LV2_Atom_Event event; uint8_t msg[3]; } MIDINoteEvent; // Initially self->out_port contains a Chunk with size set to capacity // Get the capacity const uint32_t out_capacity = self->out_port->atom.size; // Write an empty Sequence header to the output lv2_atom_sequence_clear(self->out_port); self->out_port->atom.type = self->in_port->atom.type; // Read incoming events LV2_ATOM_SEQUENCE_FOREACH (self->in_port, ev) { if (ev->body.type == uris->midi_Event) { const uint8_t* const msg = (const uint8_t*)(ev + 1); switch (lv2_midi_message_type(msg)) { case LV2_MIDI_MSG_NOTE_ON: case LV2_MIDI_MSG_NOTE_OFF: // Forward note to output lv2_atom_sequence_append_event(self->out_port, out_capacity, ev); if (msg[1] <= 127 - 7) { // Make a note one 5th (7 semitones) higher than input MIDINoteEvent fifth; // Could simply do fifth.event = *ev here instead... fifth.event.time.frames = ev->time.frames; // Same time fifth.event.body.type = ev->body.type; // Same type fifth.event.body.size = ev->body.size; // Same size fifth.msg[0] = msg[0]; // Same status fifth.msg[1] = msg[1] + 7; // Pitch up 7 semitones fifth.msg[2] = msg[2]; // Same velocity // Write 5th event lv2_atom_sequence_append_event( self->out_port, out_capacity, &fifth.event); } break; default: // Forward all other MIDI events directly lv2_atom_sequence_append_event(self->out_port, out_capacity, ev); break; } } } } static const void* extension_data(const char* uri) { return NULL; } static const LV2_Descriptor descriptor = {EG_FIFTHS_URI, instantiate, connect_port, NULL, // activate, run, NULL, // deactivate, cleanup, extension_data}; LV2_SYMBOL_EXPORT const LV2_Descriptor* lv2_descriptor(uint32_t index) { return index == 0 ? &descriptor : NULL; }