psyn/psyn.c

200 lines
4.2 KiB
C

#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include <lv2.h>
#include "lv2_event.h"
#include "lv2_event_helpers.h"
#include "lv2_uri_map.h"
#include "osc.h"
#include "filter.h"
#include "voice.h"
#include "control.h"
#include "engine.h"
#include "psyn.h"
double _sample_rate;
struct psyn_t
{
LV2_Event_Buffer *events;
LV2_Event_Feature *event_ref;
int midi_event_id;
float *out_l;
float *out_r;
float *ctrlMono;
float *ctrlLP;
float *ctrlOscShape[4];
float *ctrlOscLevel[4];
};
static void psyn_init(struct psyn_t *psyn, uint32_t sample_rate)
{
_sample_rate = sample_rate;
engine_init();
}
static LV2_Handle instantiate(
const LV2_Descriptor *descriptor,
double sample_rate,
const char *bundle_path,
const LV2_Feature * const *host_features)
{
struct psyn_t *psyn;
LV2_URI_Map_Feature *map_feature;
int i;
psyn = malloc(sizeof *psyn);
memset(psyn, 0, sizeof *psyn);
psyn_init(psyn, sample_rate);
for (i = 0; host_features[i]; i++) {
if (!strcmp(host_features[i]->URI, "http://lv2plug.in/ns/ext/uri-map")) {
map_feature = host_features[i]->data;
psyn->midi_event_id = map_feature->uri_to_id(map_feature->callback_data, "http://lv2plug.in/ns/ext/event", "http://lv2plug.in/ns/ext/midi#MidiEvent");
} else if (!strcmp(host_features[i]->URI, "http://lv2plug.in/ns/ext/event")) {
psyn->event_ref = host_features[i]->data;
}
}
if (psyn->midi_event_id == 0 || psyn->event_ref == NULL) {
printf("psyn instantiate failed, leaving\n");
return NULL;
}
return (LV2_Handle)psyn;
}
static void connect_port(LV2_Handle lv2instance, uint32_t port, void *data)
{
struct psyn_t *psyn = (struct psyn_t *)lv2instance;
switch (port) {
case 0:
psyn->events = data;
break;
case 1:
psyn->out_l = data;
break;
case 2:
psyn->out_r = data;
break;
case 3:
psyn->ctrlMono = data;
break;
case 4:
psyn->ctrlLP = data;
break;
case 5: case 6: case 7: case 8:
psyn->ctrlOscShape[port - 5] = data;
break;
case 9: case 10: case 11: case 12:
psyn->ctrlOscLevel[port - 9] = data;
break;
}
}
static void cleanup(LV2_Handle lv2instance)
{
struct psyn_t *psyn = (struct psyn_t *)lv2instance;
free(psyn);
}
static void run(LV2_Handle lv2instance, uint32_t sample_count)
{
uint8_t i;
struct psyn_t *psyn = (struct psyn_t *)lv2instance;
uint32_t frame = 0;
LV2_Event *ev = NULL;
LV2_Event_Iterator iterator;
lv2_event_begin(&iterator, psyn->events);
if (lv2_event_is_valid(&iterator)) ev = lv2_event_get(&iterator, NULL);
control_setstep(&_engine.lowpass, *psyn->ctrlLP, sample_count);
_engine.monosynth = (int)*psyn->ctrlMono;
for (i = 0; i < 4; i++) {
_engine.osc_shape[i].value = *psyn->ctrlOscShape[i];
_engine.osc_level[i].value = *psyn->ctrlOscLevel[i];
}
while (frame < sample_count) {
uint32_t to;
if (ev != NULL) {
to = ev->frames;
} else {
to = sample_count;
}
engine_run(&_engine, to - frame, psyn->out_l + frame, psyn->out_r + frame);
frame = to;
if (ev != NULL) {
if (ev->type == 0) {
psyn->event_ref->lv2_event_unref(psyn->event_ref->callback_data, ev);
} else if (ev->type == psyn->midi_event_id) {
uint8_t *data = (uint8_t *)(ev + 1);
switch (data[0] & 0xF0) {
case 0x80:
engine_endvoice(&_engine, data[1], data[2]);
break;
case 0x90:
engine_startvoice(&_engine, data[1], data[2]);
break;
case 0xA0:
engine_aftertouch(&_engine, data[2]);
break;
case 0xC0:
engine_programchange(&_engine, data[1]);
break;
case 0xB0:
engine_controlchange(&_engine, data[1], data[2]);
break;
case 0xD0:
engine_aftertouch(&_engine, data[1]);
break;
case 0xE0:
engine_pitchchange(&_engine, (data[1] | data[2] << 7) - 0x2000);
break;
}
}
lv2_event_increment(&iterator);
if (lv2_event_is_valid(&iterator)) {
ev = lv2_event_get(&iterator, NULL);
} else {
ev = NULL;
}
}
}
}
static LV2_Descriptor g_lv2descriptor =
{
.URI = "http://fuzzle.org/~petern/psyn/1",
.instantiate = &instantiate,
.connect_port = &connect_port,
.run = &run,
.cleanup = &cleanup,
};
const LV2_Descriptor *lv2_descriptor(uint32_t index)
{
if (index == 0) {
return &g_lv2descriptor;
}
return NULL;
}