Arnaud G. GIBERT
84e23826c3
- Add preliminary LV2 sample loading support, - Add LV2 multy layer sample play, - Add LV2 Bank select & Program change midi message support, - Fix minor bugs... - No GFX
1188 lines
33 KiB
C
1188 lines
33 KiB
C
/*----------------------------------------------------------------------------*/
|
|
/* Includes */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
#define _LV2_PLUGIN_C_
|
|
|
|
|
|
|
|
#include <drummer.h>
|
|
|
|
|
|
|
|
|
|
|
|
static int current_kit_changed = 0;
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static void *DR_LV2_Load_Thread( DRT_LV2_Base *LV2_Base_Ptr)
|
|
{
|
|
|
|
// drmr_sample *loaded_samples,*old_samples;
|
|
DRT_Sample *loaded_samples = NULL, *old_samples = NULL;
|
|
int loaded_count, old_scount;
|
|
char *request, *request_orig;
|
|
|
|
|
|
fprintf( stderr, "Start Load Thread!\n");
|
|
|
|
return(0);
|
|
|
|
for(;;)
|
|
{
|
|
pthread_mutex_lock( &( LV2_Base_Ptr->Load_Mutex));
|
|
pthread_cond_wait( &( LV2_Base_Ptr->Load_Cond), &( LV2_Base_Ptr->Load_Mutex));
|
|
|
|
fprintf( stderr, "Load_Thread: New load!\n");
|
|
|
|
pthread_mutex_unlock( &(LV2_Base_Ptr->Load_Mutex));
|
|
|
|
// old_samples = LV2_Base_Ptr->Samples;
|
|
// old_scount = LV2_Base_Ptr->Num_Samples;
|
|
|
|
request_orig = request = LV2_Base_Ptr->Request_Buf[ LV2_Base_Ptr->CurReq];
|
|
|
|
if( !strncmp( request, "file://", 7))
|
|
{
|
|
request += 7;
|
|
}
|
|
|
|
// loaded_samples = load_hydrogen_kit(request,drmr->rate,&loaded_count);
|
|
|
|
if( !loaded_samples)
|
|
{
|
|
fprintf(stderr,"Failed to load kit at: %s\n",request);
|
|
|
|
pthread_mutex_lock( &(LV2_Base_Ptr->Load_Mutex));
|
|
|
|
// LV2_Base_Ptr->Num_Samples = 0;
|
|
// LV2_Base_Ptr->Samples = NULL;
|
|
|
|
pthread_mutex_unlock( &(LV2_Base_Ptr->Load_Mutex));
|
|
}
|
|
else
|
|
{
|
|
// just lock for the critical moment when we swap in the new kit
|
|
printf( "loaded kit at: %s\n", request);
|
|
|
|
pthread_mutex_lock( &(LV2_Base_Ptr->Load_Mutex));
|
|
|
|
// LV2_Base_Ptr->Samples = loaded_samples;
|
|
// LV2_Base_Ptr->Num_Samples = loaded_count;
|
|
|
|
pthread_mutex_unlock( &(LV2_Base_Ptr->Load_Mutex));
|
|
}
|
|
|
|
// if( old_scount > 0) free_samples( old_samples, old_scount);
|
|
|
|
LV2_Base_Ptr->Current_Path = request_orig;
|
|
current_kit_changed = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
/*
|
|
static inline LV2_Atom *build_update_message(DrMr *drmr)
|
|
{
|
|
LV2_Atom_Forge_Frame set_frame;
|
|
LV2_Atom *msg = (LV2_Atom *)lv2_atom_forge_object( &drmr->forge, &set_frame, 1, drmr->uris.ui_msg);
|
|
|
|
|
|
if (drmr->current_path)
|
|
{
|
|
lv2_atom_forge_property_head( &drmr->forge, drmr->uris.kit_path, 0);
|
|
lv2_atom_forge_string( &drmr->forge, drmr->current_path, strlen(drmr->current_path));
|
|
}
|
|
|
|
lv2_atom_forge_pop( &drmr->forge,&set_frame);
|
|
|
|
return msg;
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
/*
|
|
static inline LV2_Atom *build_state_message(DrMr *drmr)
|
|
{
|
|
LV2_Atom_Forge_Frame set_frame;
|
|
LV2_Atom *msg = (LV2_Atom *)lv2_atom_forge_object( &drmr->forge, &set_frame, 1, drmr->uris.get_state);
|
|
|
|
|
|
if( drmr->current_path)
|
|
{
|
|
lv2_atom_forge_property_head( &drmr->forge, drmr->uris.kit_path, 0);
|
|
lv2_atom_forge_string( &drmr->forge, drmr->current_path, strlen( drmr->current_path));
|
|
}
|
|
|
|
lv2_atom_forge_property_head(&drmr->forge, drmr->uris.velocity_toggle,0);
|
|
lv2_atom_forge_bool(&drmr->forge, drmr->ignore_velocity?true:false);
|
|
lv2_atom_forge_property_head(&drmr->forge, drmr->uris.note_off_toggle,0);
|
|
lv2_atom_forge_bool(&drmr->forge, drmr->ignore_note_off?true:false);
|
|
lv2_atom_forge_property_head(&drmr->forge, drmr->uris.channel_nb,0);
|
|
lv2_atom_forge_int(&drmr->forge, drmr->channel_nb);
|
|
lv2_atom_forge_property_head(&drmr->forge, drmr->uris.zero_position,0);
|
|
lv2_atom_forge_int(&drmr->forge, drmr->zero_position);
|
|
lv2_atom_forge_pop(&drmr->forge,&set_frame);
|
|
|
|
return msg;
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static inline LV2_Atom *DR_LV2_Message_Midi_Info_Build( DRT_LV2_Base *LV2_Base_Ptr, uint8_t *Data)
|
|
{
|
|
LV2_Atom_Forge_Frame set_frame;
|
|
LV2_Atom *msg;
|
|
|
|
|
|
msg = (LV2_Atom *)lv2_atom_forge_object( &( LV2_Base_Ptr->Forge), &set_frame, 1, LV2_Base_Ptr->URIS.midi_info);
|
|
lv2_atom_forge_property_head( &( LV2_Base_Ptr->Forge), LV2_Base_Ptr->URIS.midi_event, 0);
|
|
lv2_atom_forge_write( &( LV2_Base_Ptr->Forge), Data, 3);
|
|
lv2_atom_forge_pop( &( LV2_Base_Ptr->Forge), &set_frame);
|
|
|
|
return( msg);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
DRT_Status DR_LV2_Kit_Sample_Load( DRT_LV2_Base *LV2_Base_Ptr, DRT_Kit *Kit_Ptr)
|
|
{
|
|
DRT_Status status;
|
|
NDT_Node *cur_node_ptr;
|
|
int sample_id = 0;
|
|
|
|
|
|
if( Kit_Ptr != NULL)
|
|
{
|
|
fprintf( stderr, "Sample Rate: (%lf)\n", LV2_Base_Ptr->Base.SampleRate);
|
|
|
|
if( ( status = DR_Kit_Sample_Load( Kit_Ptr, LV2_Base_Ptr->Base.SampleRate)) == DRS_OK)
|
|
{
|
|
LV2_Base_Ptr->Kit_Cur_Ptr = Kit_Ptr;
|
|
|
|
cur_node_ptr = Kit_Ptr->Instrument_DS_Ptr->Index_Tab[NDD_INDEX_PRIMARY].Head;
|
|
|
|
for( sample_id = 0; sample_id < DRD_PORT_NUMBER_MAX; sample_id++)
|
|
{
|
|
LV2_Base_Ptr->Samples[sample_id].Active = 0;
|
|
LV2_Base_Ptr->Samples[sample_id].Offset = 0;
|
|
LV2_Base_Ptr->Samples[sample_id].Limit = 0;
|
|
LV2_Base_Ptr->Samples[sample_id].Velocity = 0;
|
|
LV2_Base_Ptr->Samples[sample_id].Layer_Ptr = 0;
|
|
LV2_Base_Ptr->Samples[sample_id].Data_Ptr = NULL;
|
|
LV2_Base_Ptr->Samples[sample_id].Data_Offset = 0;
|
|
|
|
if( cur_node_ptr == NULL)
|
|
{
|
|
fprintf( stderr, "Skip sample\n");
|
|
LV2_Base_Ptr->Samples[sample_id].Instrument_Ptr = NULL;
|
|
}
|
|
else
|
|
{
|
|
LV2_Base_Ptr->Samples[sample_id].Instrument_Ptr = (DRT_Instrument *)cur_node_ptr->Value;
|
|
cur_node_ptr = cur_node_ptr->Right;
|
|
|
|
fprintf( stderr, "Add sample: [%s]\n", LV2_Base_Ptr->Samples[sample_id].Instrument_Ptr->Name);
|
|
}
|
|
}
|
|
|
|
LV2_Base_Ptr->Sample_Number = Kit_Ptr->Instrument_DS_Ptr->Index_Tab[NDD_INDEX_PRIMARY].Node_Number;
|
|
fprintf( stderr, "Loaded: (%d) samples!\n", LV2_Base_Ptr->Sample_Number);
|
|
|
|
DR_Kit_Dump( Kit_Ptr, 0);
|
|
}
|
|
}
|
|
|
|
return( status);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
DRT_Status DR_LV2_CurKit_Sample_Load( DRT_LV2_Base *LV2_Base_Ptr)
|
|
{
|
|
DRT_Status status;
|
|
DRT_Kit *Kit_Ptr;
|
|
DRT_Kit_Id kit_id;
|
|
|
|
|
|
kit_id = ( LV2_Base_Ptr->Bank_Id_LSB_New * 128 + LV2_Base_Ptr->Bank_Id_MSB_New) * 128 + LV2_Base_Ptr->Program_Id_New;
|
|
|
|
if( kit_id == LV2_Base_Ptr->Kit_Id)
|
|
{
|
|
fprintf( stderr, "Same Kit id: (%d) Bank/Program: (%d/%d/%d)!\n", kit_id, LV2_Base_Ptr->Bank_Id_LSB_New, LV2_Base_Ptr->Bank_Id_MSB_New, LV2_Base_Ptr->Program_Id_New);
|
|
status = DRS_OK;
|
|
}
|
|
else
|
|
{
|
|
if( ( status = DR_Kit_Id_Find( &Kit_Ptr, LV2_Base_Ptr->Base.Kit_DS_Ptr, kit_id)) == DRS_OK)
|
|
{
|
|
fprintf( stderr, "New Kit id: (%d) Bank/Program: (%d/%d/%d)!\n", kit_id, LV2_Base_Ptr->Bank_Id_LSB_New, LV2_Base_Ptr->Bank_Id_MSB_New, LV2_Base_Ptr->Program_Id_New);
|
|
|
|
LV2_Base_Ptr->Bank_Id_LSB = LV2_Base_Ptr->Bank_Id_LSB_New;
|
|
LV2_Base_Ptr->Bank_Id_MSB = LV2_Base_Ptr->Bank_Id_MSB_New;
|
|
LV2_Base_Ptr->Program_Id = LV2_Base_Ptr->Program_Id_New;
|
|
LV2_Base_Ptr->Kit_Id = kit_id;
|
|
LV2_Base_Ptr->Kit_Cur_Ptr = Kit_Ptr;
|
|
|
|
status = DR_LV2_Kit_Sample_Load( LV2_Base_Ptr, Kit_Ptr);
|
|
}
|
|
}
|
|
|
|
return( status);
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
/*
|
|
static inline void layer_to_sample( drmr_sample *sample, float gain)
|
|
{
|
|
int i;
|
|
float mapped_gain = ( 1 - ( gain / GAIN_MIN));
|
|
|
|
|
|
if( mapped_gain > 1.0f) mapped_gain = 1.0f;
|
|
|
|
for( i = 0; i < sample->layer_count; i++)
|
|
{
|
|
if( sample->layers[i].min <= mapped_gain &&
|
|
(sample->layers[i].max > mapped_gain ||
|
|
(sample->layers[i].max == 1 && mapped_gain == 1)))
|
|
{
|
|
sample->limit = sample->layers[i].limit;
|
|
sample->info = sample->layers[i].info;
|
|
sample->data = sample->layers[i].data;
|
|
return;
|
|
}
|
|
}
|
|
|
|
fprintf(stderr,"Couldn't find layer for gain %f in sample\n\n",gain);
|
|
// to avoid not playing something, and to deal with kits like the
|
|
// k-27_trash_kit, let's just use the first layer
|
|
|
|
sample->limit = sample->layers[0].limit;
|
|
sample->info = sample->layers[0].info;
|
|
sample->data = sample->layers[0].data;
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static inline void DR_Layer_To_Sample( DRT_Sample *Sample_Ptr, float Gain)
|
|
{
|
|
NDT_Node *first_node_ptr, *cur_node_ptr;
|
|
DRT_Layer *cur_layer_ptr;
|
|
int i = 0;
|
|
DRT_Boolean found = DRD_FALSE;
|
|
// float mapped_gain = ( 1 - ( Gain / DRD_GAIN_MIN));
|
|
float mapped_gain = Gain;
|
|
|
|
|
|
if( mapped_gain > 1.0f) mapped_gain = 1.0f;
|
|
|
|
first_node_ptr = Sample_Ptr->Instrument_Ptr->Layer_DS_Ptr->Index_Tab[NDD_INDEX_PRIMARY].Head;
|
|
cur_node_ptr = first_node_ptr;
|
|
|
|
while( ( cur_node_ptr != NULL) && ( found == DRD_FALSE))
|
|
{
|
|
cur_layer_ptr = (DRT_Layer *)cur_node_ptr->Value;
|
|
|
|
if( ( cur_layer_ptr->Min <= mapped_gain) &&
|
|
( ( cur_layer_ptr->Max > mapped_gain) || ( ( cur_layer_ptr->Max == 1) && ( mapped_gain == 1))))
|
|
{
|
|
found = DRD_TRUE;
|
|
}
|
|
else
|
|
{
|
|
cur_node_ptr = cur_node_ptr->Right;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if( cur_node_ptr == NULL)
|
|
{
|
|
cur_node_ptr = first_node_ptr;
|
|
cur_layer_ptr = (DRT_Layer *)cur_node_ptr->Value;
|
|
i=0;
|
|
}
|
|
|
|
Sample_Ptr->Layer_Ptr = cur_layer_ptr;
|
|
Sample_Ptr->Data_Ptr = cur_layer_ptr->Sample_Ptr;
|
|
Sample_Ptr->Limit = cur_layer_ptr->Sample_Size;
|
|
Sample_Ptr->SF_Info_Ptr = cur_layer_ptr->SF_Info_Ptr;
|
|
|
|
fprintf( stderr, "Layer: (%d) MGain: (%f) Gain: (%f)\n", i, mapped_gain, Gain);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static inline void DR_Sample_Trigger( DRT_LV2_Base *LV2_Base_Ptr, int Sample_Id, uint8_t *const Data, uint32_t Offset)
|
|
{
|
|
// need to mutex this to avoid getting the samples array
|
|
// changed after the check that the midi-note is valid
|
|
pthread_mutex_lock( &( LV2_Base_Ptr->Load_Mutex));
|
|
|
|
fprintf( stderr, "Trigger Sample: Id: (%d) Offset: (%d)\n", Sample_Id, Offset);
|
|
|
|
|
|
if( ( Sample_Id >= 0) && ( Sample_Id < LV2_Base_Ptr->Sample_Number))
|
|
{
|
|
if( Data)
|
|
{
|
|
lv2_atom_forge_frame_time( &( LV2_Base_Ptr->Forge), 0);
|
|
DR_LV2_Message_Midi_Info_Build( LV2_Base_Ptr, Data);
|
|
}
|
|
|
|
LV2_Base_Ptr->Samples[Sample_Id].Active = 1;
|
|
LV2_Base_Ptr->Samples[Sample_Id].Offset = 0;
|
|
LV2_Base_Ptr->Samples[Sample_Id].Velocity = LV2_Base_Ptr->Ignore_Velocity ? 1.0f : ( (float)Data[2]) / DRD_VELOCITY_MAX;
|
|
LV2_Base_Ptr->Samples[Sample_Id].Data_Offset = Offset;
|
|
|
|
if( LV2_Base_Ptr->Samples[Sample_Id].Instrument_Ptr->Layer_DS_Ptr->Index_Tab[NDD_INDEX_PRIMARY].Node_Number > 0)
|
|
{
|
|
// drmr currently has 32 hard-coded gains so just use the last gain
|
|
// to prevent a segfault
|
|
int gain_idx = Sample_Id < 32 ? Sample_Id : 31;
|
|
|
|
// DR_Layer_To_Sample( &(LV2_Base_Ptr->Samples[Sample_Id]), *( LV2_Base_Ptr->Gains[ gain_idx]));
|
|
DR_Layer_To_Sample( &(LV2_Base_Ptr->Samples[Sample_Id]), LV2_Base_Ptr->Samples[Sample_Id].Velocity);
|
|
|
|
if( LV2_Base_Ptr->Samples[Sample_Id].Limit == 0)
|
|
fprintf(stderr,"Failed to find layer at: %i for %f\n", Sample_Id, *( LV2_Base_Ptr->Gains[ gain_idx]));
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock( &( LV2_Base_Ptr->Load_Mutex));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static inline void DR_Sample_Untrigger( DRT_LV2_Base *LV2_Base_Ptr, int Sample_Id, uint32_t Offset)
|
|
{
|
|
pthread_mutex_lock( &( LV2_Base_Ptr->Load_Mutex));
|
|
|
|
fprintf( stderr, "UnTrigger Sample: Id: (%d) Offset: (%d)\n", Sample_Id, Offset);
|
|
|
|
/*
|
|
if (nn >= 0 && nn < drmr->num_samples) {
|
|
if (drmr->samples[nn].layer_count > 0) {
|
|
layer_to_sample(drmr->samples+nn,*(drmr->gains[nn]));
|
|
if (drmr->samples[nn].limit == 0)
|
|
fprintf(stderr,"Failed to find layer at: %i for %f\n",nn,*drmr->gains[nn]);
|
|
}
|
|
drmr->samples[nn].active = 0;
|
|
drmr->samples[nn].dataoffset = offset;
|
|
}
|
|
*/
|
|
LV2_Base_Ptr->Samples[Sample_Id].Active = 0;
|
|
LV2_Base_Ptr->Samples[Sample_Id].Data_Offset = Offset;
|
|
|
|
pthread_mutex_unlock( &( LV2_Base_Ptr->Load_Mutex));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static LV2_Handle DR_LV2_Instantiate( const LV2_Descriptor *LV2_Descriptor_Ptr,
|
|
double Rate,
|
|
const char *Bundle_Path,
|
|
const LV2_Feature* const *LV2_Features_Ptr)
|
|
{
|
|
DRT_Status status;
|
|
int i;
|
|
DRT_LV2_Base *lv2_base_ptr;
|
|
|
|
|
|
fprintf( stderr, "LV2 Instantiate\n");
|
|
|
|
if( ( lv2_base_ptr = malloc( sizeof( DRT_LV2_Base))) == NULL)
|
|
{
|
|
fprintf( stderr, "Can't allocate LV2 Base!\n");
|
|
return( (LV2_Handle)NULL);
|
|
}
|
|
|
|
|
|
lv2_base_ptr->Kit_Cur_Ptr = NULL;
|
|
lv2_base_ptr->Bank_Id_LSB = -1;
|
|
lv2_base_ptr->Bank_Id_LSB = -1;
|
|
lv2_base_ptr->Program_Id = -1;
|
|
lv2_base_ptr->Bank_Id_LSB_New = 0;
|
|
lv2_base_ptr->Bank_Id_MSB_New = 0;
|
|
lv2_base_ptr->Program_Id_New = 0;
|
|
lv2_base_ptr->Kit_Id = -1;
|
|
lv2_base_ptr->Current_Path = NULL;
|
|
lv2_base_ptr->CurReq = -1;
|
|
|
|
lv2_base_ptr->Ignore_Velocity = false;
|
|
lv2_base_ptr->Ignore_Note_Off = true;
|
|
// lv2_base_ptr->Ignore_Note_Off = false;
|
|
lv2_base_ptr->Channel_Nb = 0;
|
|
lv2_base_ptr->Zero_Position = 0;
|
|
|
|
lv2_base_ptr->Sample_Number = 0;
|
|
|
|
while( *LV2_Features_Ptr != NULL)
|
|
{
|
|
if( !strcmp( (*LV2_Features_Ptr)->URI, LV2_URID_URI "#map"))
|
|
{
|
|
lv2_base_ptr->Map_Ptr = (LV2_URID_Map *)((*LV2_Features_Ptr)->data);
|
|
}
|
|
|
|
LV2_Features_Ptr++;
|
|
}
|
|
|
|
if( lv2_base_ptr->Map_Ptr == NULL)
|
|
{
|
|
fprintf( stderr, "LV2 host does not support urid#map!\n");
|
|
}
|
|
else
|
|
{
|
|
if( pthread_mutex_init( &( lv2_base_ptr->Load_Mutex), 0))
|
|
{
|
|
fprintf( stderr, "Could not initialize load_mutex!\n");
|
|
}
|
|
else if( pthread_cond_init( &( lv2_base_ptr->Load_Cond), 0))
|
|
{
|
|
fprintf( stderr, "Could not initialize load_cond!\n");
|
|
}
|
|
else
|
|
{
|
|
DR_Map_Drummer_URIS( lv2_base_ptr->Map_Ptr, &(lv2_base_ptr->URIS));
|
|
|
|
lv2_atom_forge_init( &(lv2_base_ptr->Forge), lv2_base_ptr->Map_Ptr);
|
|
|
|
if( pthread_create( &lv2_base_ptr->Load_Thread, 0, (void * (*)(void *))DR_LV2_Load_Thread, lv2_base_ptr))
|
|
{
|
|
fprintf( stderr, "Could not initialize loading thread!\n");
|
|
}
|
|
else
|
|
{
|
|
if( ( lv2_base_ptr->Request_Buf = malloc( DRD_REQ_BUF_SIZE * sizeof(char *))) == NULL)
|
|
{
|
|
fprintf( stderr, "Cant'allocate Request buffer!\n");
|
|
}
|
|
else
|
|
{
|
|
memset( lv2_base_ptr->Request_Buf, 0, DRD_REQ_BUF_SIZE * sizeof(char *));
|
|
|
|
if( ( lv2_base_ptr->Left = malloc( DRD_PORT_NUMBER_MAX * sizeof(float *))) == NULL)
|
|
{
|
|
fprintf( stderr, "Cant'allocate Left buffer!\n");
|
|
free( lv2_base_ptr);
|
|
|
|
return( NULL);
|
|
}
|
|
else
|
|
{
|
|
if( ( lv2_base_ptr->Right = malloc( DRD_PORT_NUMBER_MAX * sizeof(float *))) == NULL)
|
|
{
|
|
fprintf( stderr, "Cant'allocate Right buffer!\n");
|
|
}
|
|
else
|
|
{
|
|
if( ( lv2_base_ptr->Gains = malloc( DRD_PORT_NUMBER_MAX * sizeof(float *))) == NULL)
|
|
{
|
|
fprintf( stderr, "Cant'allocate Gains buffer!\n");
|
|
}
|
|
else
|
|
{
|
|
if( ( lv2_base_ptr->Pans = malloc( DRD_PORT_NUMBER_MAX * sizeof(float *))) == NULL)
|
|
{
|
|
fprintf( stderr, "Cant'allocate Pans buffer!\n");
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < 32; i++)
|
|
{
|
|
lv2_base_ptr->Gains[i] = NULL;
|
|
lv2_base_ptr->Pans[i] = NULL;
|
|
}
|
|
|
|
if( ( status = DR_DataStruct_Init( &( lv2_base_ptr->Base), Rate)) != DRS_OK)
|
|
{
|
|
fprintf( stderr, "Can't init data structures (%d) !", status);
|
|
}
|
|
else
|
|
{
|
|
if( ( status = DR_Kits_Load( & ( lv2_base_ptr->Base))) != DRS_OK)
|
|
{
|
|
fprintf( stderr, "Can't load kits (%d) !", status);
|
|
}
|
|
else
|
|
{
|
|
if( ( status = DR_LV2_CurKit_Sample_Load( lv2_base_ptr)) != DRS_OK)
|
|
{
|
|
fprintf( stderr, "Can't load kit sample (%d) !", status);
|
|
}
|
|
else
|
|
{
|
|
return( (LV2_Handle)lv2_base_ptr);
|
|
}
|
|
}
|
|
|
|
DR_DataStruct_DeInit( &( lv2_base_ptr->Base));
|
|
}
|
|
}
|
|
|
|
free( lv2_base_ptr->Gains);
|
|
}
|
|
|
|
free( lv2_base_ptr->Right);
|
|
|
|
}
|
|
|
|
free( lv2_base_ptr->Left);
|
|
}
|
|
|
|
free( lv2_base_ptr->Request_Buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free( lv2_base_ptr);
|
|
|
|
return( (LV2_Handle)NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static void DR_LV2_Connect_Port( LV2_Handle Instance_Ptr, uint32_t Port_Id, void *Data_Ptr)
|
|
{
|
|
DRT_LV2_Base *lv2_base_ptr = (DRT_LV2_Base *)Instance_Ptr;
|
|
DRT_Port_Index port_index = (DRT_Port_Index)Port_Id;
|
|
|
|
|
|
// fprintf( stderr, "LV2 Connect Port (%d)!\n", Port_Id);
|
|
|
|
switch( port_index)
|
|
{
|
|
case DRD_LV2_CONTROL:
|
|
{
|
|
lv2_base_ptr->Control_Port = (LV2_Atom_Sequence *)Data_Ptr;
|
|
break;
|
|
}
|
|
|
|
case DRD_LV2_CORE_EVENT:
|
|
{
|
|
lv2_base_ptr->Core_Event_Port = (LV2_Atom_Sequence *)Data_Ptr;
|
|
break;
|
|
}
|
|
|
|
case DRD_LV2_BASENOTE:
|
|
{
|
|
if( Data_Ptr) lv2_base_ptr->BaseNote = (float *)Data_Ptr;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
if( port_index == DRD_LV2_MASTER_LEFT)
|
|
{
|
|
lv2_base_ptr->Master_Left = (float *)Data_Ptr;
|
|
}
|
|
else if( port_index == DRD_LV2_MASTER_RIGHT)
|
|
{
|
|
lv2_base_ptr->Master_Right = (float *)Data_Ptr;
|
|
}
|
|
else if( port_index >= DRD_LV2_LEFT_00 && port_index <= DRD_LV2_RIGHT_31)
|
|
{
|
|
int outoff = (port_index - DRD_LV2_LEFT_00) / 2;
|
|
|
|
if( ( port_index - DRD_LV2_LEFT_00) % 2)
|
|
{
|
|
lv2_base_ptr->Right[outoff] = (float *)Data_Ptr;
|
|
}
|
|
else
|
|
{
|
|
lv2_base_ptr->Left[outoff] = (float *)Data_Ptr;
|
|
}
|
|
}
|
|
else if( port_index >= DRD_LV2_GAIN_00 && port_index <= DRD_LV2_GAIN_31)
|
|
{
|
|
int goff = port_index - DRD_LV2_GAIN_00;
|
|
lv2_base_ptr->Gains[goff] = (float *)Data_Ptr;
|
|
}
|
|
else if( port_index >= DRD_LV2_PAN_00 && port_index <= DRD_LV2_PAN_31)
|
|
{
|
|
int poff = port_index - DRD_LV2_PAN_00;
|
|
lv2_base_ptr->Pans[poff] = (float *)Data_Ptr;
|
|
}
|
|
else
|
|
{
|
|
fprintf( stderr, "LV2 Connect Port: unknown port: (%d)!\n", Port_Id);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static void DR_LV2_Run( LV2_Handle Instance_Ptr, uint32_t N_Samples)
|
|
{
|
|
DRT_Status status;
|
|
int i, j, basenote;
|
|
DRT_LV2_Base *lv2_base_ptr = (DRT_LV2_Base *)Instance_Ptr;
|
|
|
|
|
|
// fprintf( stderr, "LV2 Run!\n");
|
|
|
|
basenote = (int)floorf( *( lv2_base_ptr->BaseNote));
|
|
|
|
const uint32_t event_capacity = lv2_base_ptr->Core_Event_Port->atom.size;
|
|
|
|
lv2_atom_forge_set_buffer( &lv2_base_ptr->Forge, (uint8_t *)lv2_base_ptr->Core_Event_Port, event_capacity);
|
|
|
|
LV2_Atom_Forge_Frame seq_frame;
|
|
lv2_atom_forge_sequence_head( &(lv2_base_ptr->Forge), &seq_frame, 0);
|
|
|
|
LV2_ATOM_SEQUENCE_FOREACH( lv2_base_ptr->Control_Port, ev_ptr)
|
|
{
|
|
fprintf( stderr, "LV2 Event!\n");
|
|
|
|
if( ev_ptr->body.type == lv2_base_ptr->URIS.midi_event)
|
|
{
|
|
uint8_t nn;
|
|
uint8_t *const data = (uint8_t *const)(ev_ptr + 1);
|
|
uint32_t offset = ( ev_ptr->time.frames > 0 && ev_ptr->time.frames < N_Samples) ? ev_ptr->time.frames : 0;
|
|
uint8_t channel = *data & 15;
|
|
uint8_t controler;
|
|
uint8_t value;
|
|
|
|
|
|
fprintf( stderr, " Midi Event!\n");
|
|
|
|
if( ( lv2_base_ptr->Channel_Nb == 0) || ( channel == ( lv2_base_ptr->Channel_Nb - 1)))
|
|
{
|
|
switch( ( *data) >> 4)
|
|
{
|
|
case 8: // Note Off
|
|
{
|
|
if( !lv2_base_ptr->Ignore_Note_Off)
|
|
{
|
|
nn = data[1];
|
|
nn -= basenote;
|
|
|
|
DR_Sample_Untrigger( lv2_base_ptr, nn, offset);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 9: // Note On
|
|
{
|
|
nn = data[1];
|
|
nn -= basenote;
|
|
|
|
DR_Sample_Trigger( lv2_base_ptr, nn, data, offset);
|
|
|
|
break;
|
|
}
|
|
|
|
case 11: // Control Change
|
|
{
|
|
controler = data[1];
|
|
value = data[2];
|
|
|
|
switch( controler)
|
|
{
|
|
case 0: // Bank MSB Select
|
|
{
|
|
fprintf( stderr, "Bank MSB select: (%d)!\n", value);
|
|
|
|
lv2_base_ptr->Bank_Id_MSB_New = value;
|
|
break;
|
|
}
|
|
|
|
case 32: // Bank LSB Select
|
|
{
|
|
fprintf( stderr, "Bank LSB select: (%d)!\n", value);
|
|
|
|
lv2_base_ptr->Bank_Id_LSB_New = value;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
fprintf( stderr, "Unhandled controler: (%d) value: (%d)!\n", controler, value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 12: // Program Change
|
|
{
|
|
value = data[1];
|
|
|
|
fprintf( stderr, "Program change: (%d)!\n", value);
|
|
|
|
lv2_base_ptr->Program_Id_New = value;
|
|
|
|
if( ( status = DR_LV2_CurKit_Sample_Load( lv2_base_ptr)) != DRS_OK)
|
|
{
|
|
fprintf( stderr, "Can't load kit sample (%d) !", status);
|
|
}
|
|
|
|
/*
|
|
int reqpos = ( lv2_base_ptr->CurReq + 1) % DRD_REQ_BUF_SIZE;
|
|
char *tmp = NULL;
|
|
|
|
|
|
if( reqpos >= 0 && lv2_base_ptr->Request_Buf[reqpos])
|
|
tmp = lv2_base_ptr->Request_Buf[reqpos];
|
|
lv2_base_ptr->Request_Buf[reqpos] = strdup( LV2_ATOM_BODY( lv2_base_ptr->Kit_Id));
|
|
lv2_base_ptr->CurReq = reqpos;
|
|
|
|
if( tmp) free(tmp);
|
|
|
|
fprintf( stderr, "Path!\n");
|
|
*/
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
fprintf( stderr, "Unhandeled status: (%i) Data 1: (%i) Data 2: (%d)\n", ( *data) >> 4, data[1], data[2]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if( ev_ptr->body.type == lv2_base_ptr->URIS.atom_object)
|
|
{
|
|
fprintf( stderr, "LV2 AO...\n");
|
|
|
|
const LV2_Atom_Object *obj_ptr = (LV2_Atom_Object *)&(ev_ptr->body);
|
|
|
|
if( obj_ptr->body.otype == lv2_base_ptr->URIS.ui_msg)
|
|
{
|
|
const LV2_Atom *path = NULL;
|
|
const LV2_Atom *trigger = NULL;
|
|
const LV2_Atom *ignvel = NULL;
|
|
const LV2_Atom *ignno = NULL;
|
|
const LV2_Atom *channel_nb = NULL;
|
|
const LV2_Atom *zerop = NULL;
|
|
const LV2_Atom *sample_add = NULL;
|
|
const LV2_Atom *sample_remove = NULL;
|
|
|
|
|
|
lv2_atom_object_get( obj_ptr,
|
|
lv2_base_ptr->URIS.kit_path, &path,
|
|
lv2_base_ptr->URIS.sample_trigger, &trigger,
|
|
lv2_base_ptr->URIS.velocity_toggle, &ignvel,
|
|
lv2_base_ptr->URIS.note_off_toggle, &ignno,
|
|
lv2_base_ptr->URIS.channel_nb, &channel_nb,
|
|
lv2_base_ptr->URIS.zero_position, &zerop,
|
|
lv2_base_ptr->URIS.sample_add, &sample_add,
|
|
lv2_base_ptr->URIS.sample_remove, &sample_remove,
|
|
0);
|
|
|
|
if( path)
|
|
{
|
|
int reqpos = ( lv2_base_ptr->CurReq + 1) % DRD_REQ_BUF_SIZE;
|
|
char *tmp = NULL;
|
|
|
|
|
|
if( reqpos >= 0 && lv2_base_ptr->Request_Buf[reqpos])
|
|
tmp = lv2_base_ptr->Request_Buf[reqpos];
|
|
lv2_base_ptr->Request_Buf[reqpos] = strdup( LV2_ATOM_BODY( path));
|
|
lv2_base_ptr->CurReq = reqpos;
|
|
|
|
if( tmp) free(tmp);
|
|
|
|
fprintf( stderr, "Path!\n");
|
|
}
|
|
|
|
if( trigger)
|
|
{
|
|
int32_t si = ( ( const LV2_Atom_Int *)trigger)->body;
|
|
uint8_t mdata[3];
|
|
uint32_t offset = ( ev_ptr->time.frames > 0 && ev_ptr->time.frames < N_Samples) ? ev_ptr->time.frames : 0;
|
|
|
|
fprintf(stderr, "Trigger event!\n");
|
|
|
|
mdata[0] = 0x90; // note on
|
|
mdata[1] = si + basenote;
|
|
mdata[2] = 0x7f;
|
|
|
|
DR_Sample_Trigger( lv2_base_ptr, si, mdata, offset);
|
|
}
|
|
|
|
if( ignvel)
|
|
lv2_base_ptr->Ignore_Velocity = ((const LV2_Atom_Bool*)ignvel)->body;
|
|
|
|
if( ignno)
|
|
lv2_base_ptr->Ignore_Note_Off = ((const LV2_Atom_Bool*)ignno)->body;
|
|
|
|
if( channel_nb)
|
|
lv2_base_ptr->Channel_Nb = ((const LV2_Atom_Int*)channel_nb)->body;
|
|
|
|
if( zerop)
|
|
lv2_base_ptr->Zero_Position = ((const LV2_Atom_Int*)zerop)->body;
|
|
|
|
if( sample_add)
|
|
{
|
|
fprintf(stderr, "Sample Add event!\n");
|
|
|
|
lv2_atom_forge_frame_time( &lv2_base_ptr->Forge, 0);
|
|
// build_update_message( lv2_base_ptr);
|
|
}
|
|
|
|
if( sample_remove)
|
|
{
|
|
fprintf(stderr, "Sample Remove event (%d)\n", ( ( const LV2_Atom_Int *)sample_remove)->body);
|
|
|
|
lv2_atom_forge_frame_time( &(lv2_base_ptr->Forge), 0);
|
|
// build_update_message( lv2_base_ptr);
|
|
}
|
|
|
|
fprintf( stderr, "LV2 AO End!\n");
|
|
}
|
|
else if( obj_ptr->body.otype == lv2_base_ptr->URIS.get_state)
|
|
{
|
|
lv2_atom_forge_frame_time( &(lv2_base_ptr->Forge), 0);
|
|
// build_state_message( lv2_base_ptr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf( stderr, "unrecognized event (%d) != (%d)\n", ev_ptr->body.type, lv2_base_ptr->URIS.atom_object);
|
|
}
|
|
}
|
|
/*
|
|
if( ( lv2_base_ptr->curReq >= 0) &&
|
|
lv2_base_ptr->request_buf[lv2_base_ptr->curReq] &&
|
|
( !lv2_base_ptr->Current_Path || strcmp( lv2_base_ptr->Current_Path, lv2_base_ptr->Request_Buf[lv2_base_ptr->Cur_Req])))
|
|
{
|
|
pthread_cond_signal( &( lv2_base_ptr->Load_Cond));
|
|
}
|
|
*/
|
|
/*
|
|
if( current_kit_changed)
|
|
{
|
|
current_kit_changed = 0;
|
|
lv2_atom_forge_frame_time( &drmr->forge, 0);
|
|
build_update_message( drmr);
|
|
|
|
fprintf(stderr, "Kit Changes!\n");
|
|
}
|
|
*/
|
|
lv2_atom_forge_pop( &( lv2_base_ptr->Forge), &seq_frame);
|
|
|
|
pthread_mutex_lock( &( lv2_base_ptr->Load_Mutex));
|
|
|
|
for( j = 0; j < N_Samples; j++)
|
|
{
|
|
lv2_base_ptr->Master_Left[j] = 0.0f;
|
|
lv2_base_ptr->Master_Right[j] = 0.0f;
|
|
}
|
|
|
|
for( i = 0; i < lv2_base_ptr->Sample_Number; i++)
|
|
{
|
|
int pos,lim;
|
|
DRT_Sample *cur_sample = lv2_base_ptr->Samples + i;
|
|
|
|
|
|
if( ( cur_sample->Active || cur_sample->Data_Offset) && ( cur_sample->Limit > 0))
|
|
{
|
|
float coef_right, coef_left;
|
|
|
|
|
|
// fprintf( stderr, ".");
|
|
|
|
if( i < 32)
|
|
{
|
|
float gain = DRD_DB_CO( *( lv2_base_ptr->Gains[i]));
|
|
float pan_right = ( ( *( lv2_base_ptr->Pans[i])) + 1 ) / 2.0f;
|
|
float pan_left = 1 - pan_right;
|
|
|
|
|
|
coef_right = ( pan_right * ( DRD_DB3SCALE * pan_right + DRD_DB3SCALEPO)) * gain * cur_sample->Velocity;
|
|
coef_left = ( pan_left * ( DRD_DB3SCALE * pan_left + DRD_DB3SCALEPO)) * gain * cur_sample->Velocity;
|
|
}
|
|
else
|
|
{
|
|
coef_right = coef_left = 1.0f;
|
|
}
|
|
|
|
int data_start, data_end;
|
|
|
|
|
|
if( cur_sample->Active)
|
|
{
|
|
data_start = cur_sample->Data_Offset;
|
|
data_end = N_Samples;
|
|
}
|
|
else
|
|
{
|
|
data_start = 0;
|
|
data_end = cur_sample->Data_Offset;
|
|
}
|
|
cur_sample->Data_Offset = 0;
|
|
|
|
for( j = 0; j < N_Samples; j++)
|
|
{
|
|
lv2_base_ptr->Left[i][j] = 0.0f;
|
|
lv2_base_ptr->Right[i][j] = 0.0f;
|
|
}
|
|
|
|
if( cur_sample->SF_Info_Ptr->channels == 1)
|
|
{ // play mono sample
|
|
lim = ( N_Samples < ( cur_sample->Limit - cur_sample->Offset) ? N_Samples : ( cur_sample->Limit - cur_sample->Offset));
|
|
|
|
for( pos = data_start; ( pos < lim) && ( pos < data_end); pos++)
|
|
{
|
|
lv2_base_ptr->Master_Left[pos] += cur_sample->Data_Ptr[ cur_sample->Offset] * coef_left;
|
|
lv2_base_ptr->Left[i][pos] += cur_sample->Data_Ptr[ cur_sample->Offset] * coef_left;
|
|
|
|
lv2_base_ptr->Master_Right[pos] += cur_sample->Data_Ptr[ cur_sample->Offset] * coef_right;
|
|
lv2_base_ptr->Right[i][pos] += cur_sample->Data_Ptr[ cur_sample->Offset] * coef_right;
|
|
|
|
cur_sample->Offset++;
|
|
}
|
|
}
|
|
else
|
|
{ // play stereo sample
|
|
lim = ( cur_sample->Limit - cur_sample->Offset) / cur_sample->SF_Info_Ptr->channels;
|
|
|
|
if( lim > N_Samples) lim = N_Samples;
|
|
|
|
for( pos = data_start; ( pos < lim) && ( pos < data_end); pos++)
|
|
{
|
|
lv2_base_ptr->Master_Left[pos] += cur_sample->Data_Ptr[ cur_sample->Offset] * coef_left;
|
|
lv2_base_ptr->Left[i][pos] += cur_sample->Data_Ptr[ cur_sample->Offset++] * coef_left;
|
|
|
|
lv2_base_ptr->Master_Right[pos] += cur_sample->Data_Ptr[ cur_sample->Offset] * coef_right;
|
|
lv2_base_ptr->Right[i][pos] += cur_sample->Data_Ptr[ cur_sample->Offset++] * coef_right;
|
|
}
|
|
}
|
|
|
|
if( cur_sample->Offset >= cur_sample->Limit) cur_sample->Active = 0;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock( &( lv2_base_ptr->Load_Mutex));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static void DR_LV2_Cleanup( LV2_Handle Instance_Ptr)
|
|
{
|
|
DRT_Status status;
|
|
DRT_LV2_Base *lv2_base_ptr = (DRT_LV2_Base *)Instance_Ptr;
|
|
|
|
|
|
fprintf( stderr, "LV2 Cleanup!\n");
|
|
|
|
if( ( status = DR_DataStruct_DeInit( &( lv2_base_ptr->Base))) != DRS_OK)
|
|
{
|
|
printf( "Can't deinit data structures (%d)!\n", status);
|
|
}
|
|
|
|
free( lv2_base_ptr->Gains);
|
|
free( lv2_base_ptr->Right);
|
|
free( lv2_base_ptr->Left);
|
|
free( lv2_base_ptr->Request_Buf);
|
|
free( lv2_base_ptr);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static LV2_State_Status DR_LV2_Save_State( LV2_Handle instance,
|
|
LV2_State_Store_Function store,
|
|
void *handle,
|
|
uint32_t flags,
|
|
const LV2_Feature *const *features)
|
|
{
|
|
LV2_State_Status lv2_status = LV2_STATE_SUCCESS;
|
|
|
|
|
|
fprintf( stderr, "LV2 Save State!\n");
|
|
return( lv2_status);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static LV2_State_Status DR_LV2_Restore_State( LV2_Handle instance,
|
|
LV2_State_Retrieve_Function retrieve,
|
|
void *handle,
|
|
uint32_t flags,
|
|
const LV2_Feature *const *features)
|
|
{
|
|
LV2_State_Status lv2_status = LV2_STATE_SUCCESS;
|
|
|
|
|
|
fprintf( stderr, "LV2 Restore State!\n");
|
|
return( lv2_status);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* LV2_State_Interface */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static const LV2_State_Interface DRG_LV2_State_Interface =
|
|
{
|
|
DR_LV2_Save_State,
|
|
DR_LV2_Restore_State
|
|
};
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static const void *DR_LV2_Extension_Data( const char *uri)
|
|
{
|
|
|
|
|
|
if( !strcmp( uri, LV2_STATE__interface))
|
|
{
|
|
return &DRG_LV2_State_Interface;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* LV2_Descritor */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
static const LV2_Descriptor DRG_LV2_Descriptor =
|
|
{
|
|
DRD_DRUMMER_URI,
|
|
DR_LV2_Instantiate,
|
|
DR_LV2_Connect_Port,
|
|
NULL, // activate
|
|
DR_LV2_Run,
|
|
NULL, // deactivate
|
|
DR_LV2_Cleanup,
|
|
DR_LV2_Extension_Data
|
|
};
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
LV2_SYMBOL_EXPORT const LV2_Descriptor *lv2_descriptor( uint32_t index)
|
|
{
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
{
|
|
return &DRG_LV2_Descriptor;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|