2012-02-07 12:52:59 +01:00
|
|
|
/* drmr.c
|
|
|
|
* LV2 DrMr plugin
|
|
|
|
* Copyright 2012 Nick Lanham <nick@afternight.org>
|
|
|
|
*
|
|
|
|
* Public License v3. source code is available at
|
|
|
|
* <http://github.com/nicklan/drmr>
|
|
|
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
2012-02-06 22:54:18 +01:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2012-02-07 12:00:07 +01:00
|
|
|
#include <math.h>
|
2012-02-06 22:54:18 +01:00
|
|
|
|
|
|
|
#include "drmr.h"
|
2012-02-07 10:59:24 +01:00
|
|
|
#include "drmr_hydrogen.h"
|
2012-02-06 22:54:18 +01:00
|
|
|
|
2012-02-07 12:00:07 +01:00
|
|
|
static void* load_thread(void* arg) {
|
|
|
|
DrMr* drmr = (DrMr*)arg;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&drmr->load_mutex);
|
|
|
|
for(;;) {
|
|
|
|
pthread_cond_wait(&drmr->load_cond,
|
|
|
|
&drmr->load_mutex);
|
2012-02-07 21:08:33 +01:00
|
|
|
int request = (int)floorf(*(drmr->kitReq));
|
|
|
|
if (request >= drmr->kits->num_kits) {
|
2012-02-07 12:22:26 +01:00
|
|
|
int os = drmr->num_samples;
|
|
|
|
drmr->num_samples = 0;
|
|
|
|
if (os > 0) free_samples(drmr->samples,os);
|
2012-02-07 21:19:44 +01:00
|
|
|
drmr->samples = NULL;
|
2012-02-07 12:22:26 +01:00
|
|
|
} else
|
2012-02-07 21:08:33 +01:00
|
|
|
load_hydrogen_kit(drmr,drmr->kits->kits[request].path);
|
|
|
|
drmr->curKit = request;
|
2012-02-07 12:00:07 +01:00
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&drmr->load_mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-06 22:54:18 +01:00
|
|
|
static LV2_Handle
|
|
|
|
instantiate(const LV2_Descriptor* descriptor,
|
|
|
|
double rate,
|
|
|
|
const char* bundle_path,
|
|
|
|
const LV2_Feature* const* features) {
|
|
|
|
int i;
|
|
|
|
DrMr* drmr = malloc(sizeof(DrMr));
|
|
|
|
drmr->map = NULL;
|
|
|
|
drmr->num_samples = 0;
|
|
|
|
|
2012-02-07 12:00:07 +01:00
|
|
|
if (pthread_mutex_init(&drmr->load_mutex, 0)) {
|
|
|
|
fprintf(stderr, "Could not initialize load_mutex.\n");
|
|
|
|
free(drmr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (pthread_cond_init(&drmr->load_cond, 0)) {
|
|
|
|
fprintf(stderr, "Could not initialize load_cond.\n");
|
|
|
|
free(drmr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (pthread_create(&drmr->load_thread, 0, load_thread, drmr)) {
|
|
|
|
fprintf(stderr, "Could not initialize loading thread.\n");
|
|
|
|
free(drmr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-06 22:54:18 +01:00
|
|
|
// Map midi uri
|
|
|
|
while(*features) {
|
|
|
|
if (!strcmp((*features)->URI, LV2_URI_MAP_URI)) {
|
|
|
|
drmr->map = (LV2_URI_Map_Feature *)((*features)->data);
|
|
|
|
drmr->uris.midi_event = drmr->map->uri_to_id
|
|
|
|
(drmr->map->callback_data,
|
|
|
|
"http://lv2plug.in/ns/ext/event",
|
|
|
|
"http://lv2plug.in/ns/ext/midi#MidiEvent");
|
|
|
|
}
|
|
|
|
features++;
|
|
|
|
}
|
|
|
|
if (!drmr->map) {
|
2012-02-07 12:54:42 +01:00
|
|
|
fprintf(stderr, "LV2 host does not support uri-map.\n");
|
2012-02-06 22:54:18 +01:00
|
|
|
free(drmr);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-02-07 10:59:24 +01:00
|
|
|
|
|
|
|
drmr->kits = scan_kits();
|
|
|
|
if (!drmr->kits) {
|
|
|
|
fprintf(stderr, "No drum kits found\n");
|
|
|
|
free(drmr);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-02-07 12:22:26 +01:00
|
|
|
drmr->samples = NULL; // prevent attempted freeing in load
|
2012-02-07 10:59:24 +01:00
|
|
|
load_hydrogen_kit(drmr,drmr->kits->kits->path);
|
2012-02-07 12:00:07 +01:00
|
|
|
drmr->curKit = 0;
|
2012-02-06 22:54:18 +01:00
|
|
|
|
2012-02-12 21:40:12 +01:00
|
|
|
drmr->gains = malloc(32*sizeof(float*));
|
|
|
|
drmr->pans = malloc(32*sizeof(float*));
|
2012-02-13 11:24:26 +01:00
|
|
|
for(i = 0;i<32;i++) {
|
|
|
|
drmr->gains[i] = NULL;
|
|
|
|
drmr->pans[i] = NULL;
|
|
|
|
}
|
2012-02-07 09:31:51 +01:00
|
|
|
|
2012-02-06 22:54:18 +01:00
|
|
|
return (LV2_Handle)drmr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
connect_port(LV2_Handle instance,
|
|
|
|
uint32_t port,
|
|
|
|
void* data) {
|
|
|
|
DrMr* drmr = (DrMr*)instance;
|
2012-02-12 21:40:12 +01:00
|
|
|
DrMrPortIndex port_index = (DrMrPortIndex)port;
|
|
|
|
switch (port_index) {
|
2012-02-06 22:54:18 +01:00
|
|
|
case DRMR_MIDI:
|
|
|
|
drmr->midi_port = (LV2_Event_Buffer*)data;
|
|
|
|
break;
|
|
|
|
case DRMR_LEFT:
|
|
|
|
drmr->left = (float*)data;
|
|
|
|
break;
|
|
|
|
case DRMR_RIGHT:
|
|
|
|
drmr->right = (float*)data;
|
|
|
|
break;
|
2012-02-07 12:00:07 +01:00
|
|
|
case DRMR_KITNUM:
|
|
|
|
if(data) drmr->kitReq = (float*)data;
|
|
|
|
break;
|
2012-02-06 22:54:18 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-02-12 21:40:12 +01:00
|
|
|
if (port_index >= DRMR_GAIN_ONE && port_index <= DRMR_GAIN_THIRTYTWO) {
|
|
|
|
int goff = port_index - DRMR_GAIN_ONE;
|
|
|
|
drmr->gains[goff] = (float*)data;
|
|
|
|
}
|
2012-02-13 11:24:26 +01:00
|
|
|
|
|
|
|
if (port_index >= DRMR_PAN_ONE && port_index <= DRMR_PAN_THIRTYTWO) {
|
|
|
|
int poff = port_index - DRMR_PAN_ONE;
|
|
|
|
drmr->pans[poff] = (float*)data;
|
|
|
|
}
|
2012-02-12 21:40:12 +01:00
|
|
|
}
|
2012-02-06 22:54:18 +01:00
|
|
|
|
2012-02-12 21:40:12 +01:00
|
|
|
#define DB3SCALE -0.8317830986718104f
|
2012-02-13 11:24:26 +01:00
|
|
|
#define DB3SCALEPO 1.8317830986718104f
|
2012-02-12 13:33:38 +01:00
|
|
|
// taken from lv2 example amp plugin
|
2012-02-13 11:24:26 +01:00
|
|
|
#define DB_CO(g) ((g) > GAIN_MIN ? powf(10.0f, (g) * 0.05f) : 0.0f)
|
2012-02-06 22:54:18 +01:00
|
|
|
|
|
|
|
static void run(LV2_Handle instance, uint32_t n_samples) {
|
2012-02-07 12:00:07 +01:00
|
|
|
int i,kitInt;
|
2012-02-06 22:54:18 +01:00
|
|
|
DrMr* drmr = (DrMr*)instance;
|
|
|
|
|
2012-02-07 12:00:07 +01:00
|
|
|
kitInt = (int)floorf(*(drmr->kitReq));
|
2012-02-07 21:08:33 +01:00
|
|
|
if (kitInt != drmr->curKit) // requested a new kit
|
2012-02-07 12:00:07 +01:00
|
|
|
pthread_cond_signal(&drmr->load_cond);
|
|
|
|
|
2012-02-06 22:54:18 +01:00
|
|
|
LV2_Event_Iterator eit;
|
2012-02-12 13:33:38 +01:00
|
|
|
if (drmr->midi_port && lv2_event_begin(&eit,drmr->midi_port)) { // if we have any events
|
2012-02-06 22:54:18 +01:00
|
|
|
LV2_Event *cur_ev;
|
|
|
|
uint8_t* data;
|
|
|
|
while (lv2_event_is_valid(&eit)) {
|
|
|
|
cur_ev = lv2_event_get(&eit,&data);
|
|
|
|
if (cur_ev->type == drmr->uris.midi_event) {
|
|
|
|
int channel = *data & 15;
|
|
|
|
switch ((*data) >> 4) {
|
|
|
|
case 8: // ignore note-offs for now, should probably be a setting
|
|
|
|
//if (drmr->cur_samp) drmr->cur_samp->active = 0;
|
|
|
|
break;
|
|
|
|
case 9: {
|
|
|
|
uint8_t nn = data[1];
|
|
|
|
nn-=60; // middle c is our root note (setting?)
|
2012-02-07 21:08:33 +01:00
|
|
|
// need to mutex this to avoid getting the samples array
|
|
|
|
// changed after the check that the midi-note is valid
|
|
|
|
pthread_mutex_lock(&drmr->load_mutex);
|
2012-02-06 22:54:18 +01:00
|
|
|
if (nn >= 0 && nn < drmr->num_samples) {
|
|
|
|
drmr->samples[nn].active = 1;
|
|
|
|
drmr->samples[nn].offset = 0;
|
|
|
|
}
|
2012-02-07 21:08:33 +01:00
|
|
|
pthread_mutex_unlock(&drmr->load_mutex);
|
2012-02-06 22:54:18 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
printf("Unhandeled status: %i\n",(*data)>>4);
|
|
|
|
}
|
2012-02-07 09:31:51 +01:00
|
|
|
} else printf("unrecognized event\n");
|
2012-02-06 22:54:18 +01:00
|
|
|
lv2_event_increment(&eit);
|
2012-02-07 09:31:51 +01:00
|
|
|
}
|
2012-02-06 22:54:18 +01:00
|
|
|
}
|
|
|
|
|
2012-02-07 21:25:43 +01:00
|
|
|
for(i = 0;i<n_samples;i++) {
|
|
|
|
drmr->left[i] = 0.0f;
|
|
|
|
drmr->right[i] = 0.0f;
|
|
|
|
}
|
|
|
|
|
2012-02-07 21:08:33 +01:00
|
|
|
pthread_mutex_lock(&drmr->load_mutex);
|
2012-02-06 22:54:18 +01:00
|
|
|
for (i = 0;i < drmr->num_samples;i++) {
|
|
|
|
int pos,lim;
|
|
|
|
drmr_sample* cs = drmr->samples+i;
|
|
|
|
if (cs->active) {
|
2012-02-13 11:24:26 +01:00
|
|
|
float coef_right, coef_left;
|
|
|
|
if (i < 32) {
|
|
|
|
float gain = DB_CO(*(drmr->gains[i]));
|
|
|
|
float pan_right = ((*drmr->pans[i])+1)/2.0f;
|
|
|
|
float pan_left = 1-pan_right;
|
|
|
|
coef_right = (pan_right * (DB3SCALE * pan_right + DB3SCALEPO))*gain;
|
|
|
|
coef_left = (pan_left * (DB3SCALE * pan_left + DB3SCALEPO))*gain;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
coef_right = coef_left = 1.0f;
|
|
|
|
}
|
|
|
|
|
2012-02-06 22:54:18 +01:00
|
|
|
if (cs->info.channels == 1) { // play mono sample
|
|
|
|
lim = (n_samples < (cs->limit - cs->offset)?n_samples:(cs->limit-cs->offset));
|
2012-02-07 21:25:43 +01:00
|
|
|
for(pos = 0;pos < lim;pos++) {
|
2012-02-13 11:24:26 +01:00
|
|
|
drmr->left[pos] += cs->data[cs->offset]*coef_left;
|
|
|
|
drmr->right[pos] += cs->data[cs->offset]*coef_right;
|
2012-02-07 21:25:43 +01:00
|
|
|
cs->offset++;
|
2012-02-06 22:54:18 +01:00
|
|
|
}
|
|
|
|
} else { // play stereo sample
|
|
|
|
lim = (cs->limit-cs->offset)/cs->info.channels;
|
|
|
|
if (lim > n_samples) lim = n_samples;
|
2012-02-07 21:25:43 +01:00
|
|
|
for (pos=0;pos<lim;pos++) {
|
2012-02-13 11:24:26 +01:00
|
|
|
drmr->left[pos] += cs->data[cs->offset++]*coef_left;
|
|
|
|
drmr->right[pos] += cs->data[cs->offset++]*coef_right;
|
2012-02-06 22:54:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cs->offset >= cs->limit) cs->active = 0;
|
|
|
|
}
|
|
|
|
}
|
2012-02-07 21:08:33 +01:00
|
|
|
pthread_mutex_unlock(&drmr->load_mutex);
|
2012-02-06 22:54:18 +01:00
|
|
|
}
|
|
|
|
|
2012-02-12 21:40:12 +01:00
|
|
|
static void activate (LV2_Handle instance) {}
|
2012-02-06 22:54:18 +01:00
|
|
|
static void deactivate(LV2_Handle instance) {}
|
|
|
|
|
|
|
|
static void cleanup(LV2_Handle instance) {
|
2012-02-07 21:30:32 +01:00
|
|
|
DrMr* drmr = (DrMr*)instance;
|
|
|
|
pthread_cancel(drmr->load_thread);
|
|
|
|
pthread_join(drmr->load_thread, 0);
|
|
|
|
if (drmr->num_samples > 0)
|
|
|
|
free_samples(drmr->samples,drmr->num_samples);
|
|
|
|
free(drmr->gains);
|
2012-02-06 22:54:18 +01:00
|
|
|
free(instance);
|
|
|
|
}
|
|
|
|
|
2012-02-12 13:33:38 +01:00
|
|
|
static const void* extension_data(const char* uri) {
|
2012-02-06 22:54:18 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const LV2_Descriptor descriptor = {
|
|
|
|
DRMR_URI,
|
|
|
|
instantiate,
|
|
|
|
connect_port,
|
|
|
|
activate,
|
|
|
|
run,
|
|
|
|
deactivate,
|
|
|
|
cleanup,
|
|
|
|
extension_data
|
|
|
|
};
|
|
|
|
|
|
|
|
LV2_SYMBOL_EXPORT
|
|
|
|
const LV2_Descriptor*
|
|
|
|
lv2_descriptor(uint32_t index)
|
|
|
|
{
|
|
|
|
switch (index) {
|
|
|
|
case 0:
|
|
|
|
return &descriptor;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|