/*----------------------------------------------------------------------------*/ /* lv2_plugin.c */ /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ /* This file is part of Drummer. */ /* */ /* Drummer is free software: you can redistribute it and/or modify it */ /* under the terms of the GNU General Public License as published by */ /* the Free Software Foundation, either version 3 of the License, or */ /* (at your option) any later version. */ /* */ /* Drummer is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with Drummer. If not, see . */ /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ /* Includes */ /*----------------------------------------------------------------------------*/ #define _LV2_PLUGIN_C_ #include static int current_kit_changed = 0; /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ DRT_Status DR_LV2_Kit_Sample_Load( DRT_Kit *Kit_Ptr) { DRT_Status status; NDT_Node *cur_node_ptr; int sample_id = 0; if( Kit_Ptr != NULL) { // fprintf( stderr, "Sample Rate: (%d)\n", DRG_LV2_Base.Base.SampleRate); if( ( status = DR_Kit_Sample_Load( Kit_Ptr)) == DRS_OK) { DRG_LV2_Base.Kit_Ptr = Kit_Ptr; cur_node_ptr = Kit_Ptr->Instrument_DS_Ptr->Index_Tab[NDD_INDEX_PRIMARY].Head; pthread_mutex_lock( &( DRG_LV2_Base.Load_Mutex)); for( sample_id = 0; sample_id < DRD_PORT_NUMBER_MAX; sample_id++) { DRG_LV2_Base.Samples[sample_id].Active = 0; DRG_LV2_Base.Samples[sample_id].Offset = 0; DRG_LV2_Base.Samples[sample_id].Limit = 0; DRG_LV2_Base.Samples[sample_id].Velocity = 0; DRG_LV2_Base.Samples[sample_id].Layer_Ptr = 0; DRG_LV2_Base.Samples[sample_id].Data_Ptr = NULL; DRG_LV2_Base.Samples[sample_id].Data_Offset = 0; DRG_LV2_Base.Samples[sample_id].Sustained = false; if( cur_node_ptr == NULL) { // fprintf( stderr, "Skip sample\n"); DRG_LV2_Base.Samples[sample_id].Instrument_Ptr = NULL; } else { DRG_LV2_Base.Samples[sample_id].Instrument_Ptr = (DRT_Instrument *)cur_node_ptr->Value; cur_node_ptr = cur_node_ptr->Right; // fprintf( stderr, "Add sample: [%s]\n", DRG_LV2_Base.Samples[sample_id].Instrument_Ptr->Name); } } pthread_mutex_unlock( &( DRG_LV2_Base.Load_Mutex)); DRG_LV2_Base.Sample_Number = Kit_Ptr->Instrument_DS_Ptr->Index_Tab[NDD_INDEX_PRIMARY].Node_Number; LG_LOG_INFO_1( "Loaded: (%d) instruments!", DRG_LV2_Base.Sample_Number); // DR_Kit_Dump( Kit_Ptr, 0); } } return( status); } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ DRT_Status DR_LV2_CurKit_Sample_Load() { DRT_Status status; DRT_Kit *kit_ptr; DRT_Kit_Id kit_id; if( DRG_LV2_Base.Kit_Id_New != DRG_LV2_Base.Kit_Id) { DR_Kit_Id_Convert( &DRG_LV2_Base.Bank_Id_LSB_New, &DRG_LV2_Base.Bank_Id_MSB_New, &DRG_LV2_Base.Program_Id_New, DRG_LV2_Base.Kit_Id_New); } DR_Bank_Program_Id_Convert( &kit_id, DRG_LV2_Base.Bank_Id_LSB_New, DRG_LV2_Base.Bank_Id_MSB_New, DRG_LV2_Base.Program_Id_New); if( kit_id == DRG_LV2_Base.Kit_Id) { LG_LOG_INFO_5( "Same kit id: (%d) Bank/Program: (%d/%d/%d) Name: [%s]!", kit_id, DRG_LV2_Base.Bank_Id_LSB_New, DRG_LV2_Base.Bank_Id_MSB_New, DRG_LV2_Base.Program_Id_New, DRG_LV2_Base.Kit_Ptr->Name); status = DRS_OK; } else { if( ( status = DR_Kit_Logical_Id_Find( &kit_ptr, kit_id)) != DRS_OK) { LG_LOG_WARNING_4( "Can't find kit id: (%d) Bank/Program: (%d/%d/%d)!", kit_id, DRG_LV2_Base.Bank_Id_LSB_New, DRG_LV2_Base.Bank_Id_MSB_New, DRG_LV2_Base.Program_Id_New); if( DRG_LV2_Base.Kit_Id == DRD_ID_UNKNOWN) { LG_LOG_ERROR_0( "No Kit available!"); status = DRS_KO; } else { LG_LOG_INFO_5( "Keep kit id: (%d) Bank/Program: (%d/%d/%d) Name: [%s]!", DRG_LV2_Base.Kit_Id, DRG_LV2_Base.Bank_Id_LSB, DRG_LV2_Base.Bank_Id_MSB, DRG_LV2_Base.Program_Id, DRG_LV2_Base.Kit_Ptr->Name); } } else { LG_LOG_INFO_5( "New kit id: (%d) Bank/Program: (%d/%d/%d) Name: [%s]!", kit_id, DRG_LV2_Base.Bank_Id_LSB_New, DRG_LV2_Base.Bank_Id_MSB_New, DRG_LV2_Base.Program_Id_New, kit_ptr->Name); DRG_LV2_Base.Bank_Id_LSB = DRG_LV2_Base.Bank_Id_LSB_New; DRG_LV2_Base.Bank_Id_MSB = DRG_LV2_Base.Bank_Id_MSB_New; DRG_LV2_Base.Program_Id = DRG_LV2_Base.Program_Id_New; DRG_LV2_Base.Kit_Id = kit_id; DRG_LV2_Base.Kit_Id_New = kit_id; DRG_LV2_Base.Kit_Ptr = kit_ptr; status = DR_LV2_Kit_Sample_Load( kit_ptr); DRG_LV2_Base.Kit_Updated_Flag = true; LG_LOG_INFO_0( "Sample loaded!"); } } return( status); } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ static void *DR_LV2_Load_Thread() { DRT_Status status; LG_LOG_INFO_0( "Start Load Thread!"); for(;;) { pthread_mutex_lock( &( DRG_LV2_Base.Load_Mutex)); pthread_cond_wait( &( DRG_LV2_Base.Load_Cond), &( DRG_LV2_Base.Load_Mutex)); LG_LOG_INFO_0( "Load_Thread: New load!"); pthread_mutex_unlock( &(DRG_LV2_Base.Load_Mutex)); if( ( status = DR_LV2_CurKit_Sample_Load()) != DRS_OK) { LG_LOG_ERROR_1( "Can't load kit sample: (%d)", status); } pthread_mutex_lock( &(DRG_LV2_Base.Load_Mutex)); pthread_mutex_unlock( &(DRG_LV2_Base.Load_Mutex)); /* // old_samples = DRG_LV2_Base.Samples; // old_scount = DRG_LV2_Base.Num_Samples; request_orig = request = DRG_LV2_Base.Request_Buf[ DRG_LV2_Base.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( &(DRG_LV2_Base.Load_Mutex)); // DRG_LV2_Base.Num_Samples = 0; // DRG_LV2_Base.Samples = NULL; pthread_mutex_unlock( &(DRG_LV2_Base.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( &(DRG_LV2_Base.Load_Mutex)); // DRG_LV2_Base.Samples = loaded_samples; // DRG_LV2_Base.Num_Samples = loaded_count; pthread_mutex_unlock( &(DRG_LV2_Base.Load_Mutex)); } // if( old_scount > 0) free_samples( old_samples, old_scount); DRG_LV2_Base.Current_Path = request_orig; current_kit_changed = 1; */ } return 0; } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ /* 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( &( DRG_LV2_Base.Forge), &set_frame, 1, DRG_LV2_Base.URIS.midi_info); lv2_atom_forge_property_head( &( DRG_LV2_Base.Forge), DRG_LV2_Base.URIS.midi_event, 0); lv2_atom_forge_write( &( DRG_LV2_Base.Forge), Data, 3); lv2_atom_forge_pop( &( DRG_LV2_Base.Forge), &set_frame); return( msg); } */ /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ DRT_Status DR_UI_Port_Notify( LV2_URID URId, float Value) { LV2_Atom_Forge_Frame set_frame; lv2_atom_forge_frame_time( &( DRG_LV2_Base.Forge), 0); lv2_atom_forge_object( &( DRG_LV2_Base.Forge), &set_frame, 1, DRG_LV2_Base.URIS.UI_Msg); lv2_atom_forge_property_head( &( DRG_LV2_Base.Forge), URId, 0); lv2_atom_forge_float( &( DRG_LV2_Base.Forge), Value); lv2_atom_forge_pop( &( DRG_LV2_Base.Forge), &set_frame); LG_LOG_INFO_2( "Notify: URId: (%d) Value: (%d)!", URId, (int)Value); return( DRS_OK); } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ /* 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; LG_LOG_INFO_3( "Layer: (%d) MGain: (%f) Gain: (%f)", i, mapped_gain, Gain); } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ static inline void DR_Sample_Trigger( 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( &( DRG_LV2_Base.Load_Mutex)); LG_LOG_INFO_2( "Trigger Sample: Id: (%d) Offset: (%d)", Sample_Id, Offset); if( ( Sample_Id >= 0) && ( Sample_Id < DRG_LV2_Base.Sample_Number)) { /* if( Data) { lv2_atom_forge_frame_time( &( DRG_LV2_Base.Forge), 0); DR_LV2_Message_Midi_Info_Build( LV2_Base_Ptr, Data); } */ DRG_LV2_Base.Samples[Sample_Id].Active = 1; DRG_LV2_Base.Samples[Sample_Id].Offset = 0; DRG_LV2_Base.Samples[Sample_Id].Velocity = *( DRG_Base.Velocity_Ignore_Flag_Ptr) ? 1.0f : ( (float)Data[2]) / DRD_VELOCITY_MAX; DRG_LV2_Base.Samples[Sample_Id].Data_Offset = Offset; DRG_LV2_Base.Samples[Sample_Id].Sustained = false; if( DRG_LV2_Base.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( &(DRG_LV2_Base.Samples[Sample_Id]), *( DRG_LV2_Base.Gains[ gain_idx])); DR_Layer_To_Sample( &(DRG_LV2_Base.Samples[Sample_Id]), DRG_LV2_Base.Samples[Sample_Id].Velocity); if( DRG_LV2_Base.Samples[Sample_Id].Limit == 0) LG_LOG_ERROR_2( "Failed to find layer at: (%i) for: (%f)", Sample_Id, *( DRG_LV2_Base.Gains[ gain_idx])); } } pthread_mutex_unlock( &( DRG_LV2_Base.Load_Mutex)); } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ static inline void DR_Sample_Untrigger( int Sample_Id, uint32_t Offset) { pthread_mutex_lock( &( DRG_LV2_Base.Load_Mutex)); if( DRG_LV2_Base.Sustain == true) { LG_LOG_INFO_2( "Sustains Sample: Id: (%d) Offset: (%d)", Sample_Id, Offset); DRG_LV2_Base.Samples[Sample_Id].Sustained = true; } else { LG_LOG_INFO_2( "UnTrigger Sample: Id: (%d) Offset: (%d)", 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; } */ DRG_LV2_Base.Samples[Sample_Id].Active = 0; DRG_LV2_Base.Samples[Sample_Id].Data_Offset = Offset; DRG_LV2_Base.Samples[Sample_Id].Sustained = false; } pthread_mutex_unlock( &( DRG_LV2_Base.Load_Mutex)); } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ static inline void DR_Sample_UnSustain( uint32_t Offset) { int sample_id; for( sample_id =0; sample_id < DRD_PORT_NUMBER_MAX; sample_id++) { if( DRG_LV2_Base.Samples[sample_id].Sustained) { DR_Sample_Untrigger( sample_id, Offset); } } } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ static LV2_Handle DR_LV2_Instantiate( const LV2_Descriptor *LV2_Descriptor_Ptr, double SampleRate, const char *Bundle_Path, const LV2_Feature * const *LV2_Features_Ptr) { LGT_Status lg_status; DRT_Status status; int i; if( ( lg_status = LG_Library_Open( LGD_LOG_WRITER_DEFAULT, true)) != LGS_OK) { fprintf( stderr, "Can't open LibLog library: (%d)\n", status); return( (LV2_Handle)NULL); } LG_LOG_INFO_0( "LV2 instantiate start"); DRG_LV2_Base.Map_Ptr = NULL; DRG_LV2_Base.Logger.log = NULL; DRG_LV2_Base.Kit_Ptr = NULL; DRG_LV2_Base.Kit_Id = DRD_ID_UNKNOWN; DRG_LV2_Base.Kit_Id_New = DRD_ID_UNKNOWN; DRG_LV2_Base.Bank_Id_LSB = -1; DRG_LV2_Base.Bank_Id_LSB = -1; DRG_LV2_Base.Program_Id = -1; DRG_LV2_Base.Bank_Id_LSB_New = 0; DRG_LV2_Base.Bank_Id_MSB_New = 0; DRG_LV2_Base.Program_Id_New = 0; DRG_LV2_Base.Kit_Updated_Flag = false; DRG_LV2_Base.Current_Path = NULL; DRG_LV2_Base.CurReq = -1; // DRG_LV2_Base.Channel_Id = 0; DRG_LV2_Base.Zero_Position = 0; DRG_LV2_Base.Sustain = false; DRG_LV2_Base.Sample_Number = 0; while( *LV2_Features_Ptr != NULL) { LG_LOG_INFO_1( "Feature URI: [%s]", (*LV2_Features_Ptr)->URI); if( !strcmp( ( *LV2_Features_Ptr)->URI, LV2_URID_URI "#map")) { DRG_LV2_Base.Map_Ptr = (LV2_URID_Map *)( ( *LV2_Features_Ptr)->data); } else { if( !strcmp( ( *LV2_Features_Ptr)->URI, LV2_LOG__log)) { DRG_LV2_Base.Logger.log = (LV2_Log_Log *)( ( *LV2_Features_Ptr)->data); } } LV2_Features_Ptr++; } if( ( DRG_LV2_Base.Map_Ptr == NULL) || ( DRG_LV2_Base.Logger.log == NULL)) { if( DRG_LV2_Base.Map_Ptr == NULL) { LG_LOG_WARNING_0( "LV2 host does not support urid#map!"); } if( DRG_LV2_Base.Logger.log == NULL) { LG_LOG_WARNING_0( "LV2 host does not support log#log!"); } } else { // lv2_log_logger_init( &(DRG_LV2_Base.Logger), DRG_LV2_Base.Map_Ptr, DRG_LV2_Base.Logger.log); lv2_log_logger_set_map( &(DRG_LV2_Base.Logger), DRG_LV2_Base.Map_Ptr); // if( ( status = DR_DataStruct_Init( DR_LV2_Log_Write, (DRT_SampleRate)SampleRate, DRD_THREAD_NUMBER_DEFAULT)) != DRS_OK) if( ( status = DR_DataStruct_Init( (DRT_SampleRate)SampleRate, DRD_THREAD_NUMBER_DEFAULT)) != DRS_OK) { LG_LOG_ERROR_1( "Can't init data structures: (%d)!", status); } else { lv2_log_error( &(DRG_LV2_Base.Logger), "LV2Log: Test error log: [%s]!\n", "Krash!"); LG_LOG_ERROR_1( "LibLog: Test error log: [%s]!", "Krash!"); if( pthread_mutex_init( &( DRG_LV2_Base.Load_Mutex), 0)) { LG_LOG_ERROR_0( "Could not initialize load_mutex!"); } else if( pthread_cond_init( &( DRG_LV2_Base.Load_Cond), 0)) { LG_LOG_ERROR_0( "Could not initialize load_cond!"); } else { DR_LV2_Map_URIS( DRG_LV2_Base.Map_Ptr, &( DRG_LV2_Base.URIS)); lv2_atom_forge_init( &(DRG_LV2_Base.Forge), DRG_LV2_Base.Map_Ptr); if( pthread_create( &DRG_LV2_Base.Load_Thread, 0, (void * (*)(void *))DR_LV2_Load_Thread, NULL)) { LG_LOG_ERROR_0( "Could not initialize loading thread!"); } else { if( ( DRG_LV2_Base.Request_Buf = malloc( DRD_REQ_BUF_SIZE * sizeof(char *))) == NULL) { LG_LOG_ERROR_0( "Can't allocate request buffer!"); } else { memset( DRG_LV2_Base.Request_Buf, 0, DRD_REQ_BUF_SIZE * sizeof(char *)); if( ( DRG_LV2_Base.Left = malloc( DRD_PORT_NUMBER_MAX * sizeof(float *))) == NULL) { LG_LOG_ERROR_0( "Can't allocate left buffer!"); } else { if( ( DRG_LV2_Base.Right = malloc( DRD_PORT_NUMBER_MAX * sizeof(float *))) == NULL) { LG_LOG_ERROR_0( "Can't allocate right buffer!"); } else { if( ( DRG_LV2_Base.Gains = malloc( DRD_PORT_NUMBER_MAX * sizeof(float *))) == NULL) { LG_LOG_ERROR_0( "Can't allocate gains buffer!"); } else { if( ( DRG_LV2_Base.Pans = malloc( DRD_PORT_NUMBER_MAX * sizeof(float *))) == NULL) { LG_LOG_ERROR_0( "Can't allocate pans buffer!"); } else { for( i = 0; i < 32; i++) { DRG_LV2_Base.Gains[i] = NULL; DRG_LV2_Base.Pans[i] = NULL; } if( ( status = DR_Kits_Load()) != DRS_OK) { LG_LOG_ERROR_1( "Can't load kits: (%d)!", status); } else { if( ( status = DR_LV2_CurKit_Sample_Load()) != DRS_OK) { LG_LOG_ERROR_1( "Can't load kit sample: (%d)!", status); } else { LG_LOG_INFO_1( "DRG_Base_Ptr: (%lx)!", (char *)( &DRG_Base)); return( (LV2_Handle)&DRG_LV2_Base); } } } free( DRG_LV2_Base.Gains); } free( DRG_LV2_Base.Right); } free( DRG_LV2_Base.Left); } free( DRG_LV2_Base.Request_Buf); } } } DR_DataStruct_DeInit(); } } LG_LOG_INFO_0( "LV2 instantiate end"); 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; // LG_LOG_INFO_1( "LV2 Connect Port (%d)!", 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_CHANNEL_ID: { if( Data_Ptr) DRG_Base.Channel_Id_Ptr = (float *)Data_Ptr; break; } case DRD_LV2_BASE_NOTE: { if( Data_Ptr) DRG_Base.Base_Note_Ptr = (float *)Data_Ptr; break; } case DRD_LV2_VELOCITY_IGNORE_NOTE: { if( Data_Ptr) DRG_Base.Velocity_Ignore_Note_Ptr = (float *)Data_Ptr; break; } case DRD_LV2_NOTE_OFF_IGNORE_NOTE: { if( Data_Ptr) DRG_Base.Note_Off_Ignore_Note_Ptr = (float *)Data_Ptr; break; } case DRD_LV2_VELOCITY_IGNORE_FLAG: { if( Data_Ptr) DRG_Base.Velocity_Ignore_Flag_Ptr = (float *)Data_Ptr; break; } case DRD_LV2_NOTE_OFF_IGNORE_FLAG: { if( Data_Ptr) DRG_Base.Note_Off_Ignore_Flag_Ptr = (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 { LG_LOG_ERROR_1( "LV2 Connect Port: unknown port: (%d)!", Port_Id); } break; } } } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ static void DR_LV2_Activate( LV2_Handle instance) { LG_LOG_INFO_0( "LV2 Activate!"); } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ static void DR_LV2_Run( LV2_Handle Instance_Ptr, uint32_t N_Samples) { DRT_Status status; int i, j, base_note; DRT_LV2_Base *lv2_base_ptr = (DRT_LV2_Base *)Instance_Ptr; // LG_LOG_INFO_0( "LV2 Run!"); base_note = (int)floorf( *( DRG_Base.Base_Note_Ptr)); 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) { LG_LOG_INFO_0( "LV2 Event!"); 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( ( *( DRG_Base.Channel_Id_Ptr) == 0) || ( channel == ( *( DRG_Base.Channel_Id_Ptr) - 1))) { switch( ( *data) >> 4) { case 8: // Note Off { if( !*( DRG_Base.Note_Off_Ignore_Flag_Ptr)) { nn = data[1]; nn -= base_note; DR_Sample_Untrigger( nn, offset); } break; } case 9: // Note On { nn = data[1]; if( nn == *( DRG_Base.Velocity_Ignore_Note_Ptr)) { *( DRG_Base.Velocity_Ignore_Flag_Ptr) = (float)( (int)*( DRG_Base.Velocity_Ignore_Flag_Ptr) ^ true); LG_LOG_INFO_2( "Velocity Ignore: [%f] - [%f]", *( DRG_Base.Velocity_Ignore_Flag_Ptr), *( DRG_Base.Velocity_Ignore_Note_Ptr)); DR_UI_Port_Notify( DRG_LV2_Base.URIS.Velocity_Ignore_Flag_Toggle, *( DRG_Base.Velocity_Ignore_Flag_Ptr)); } else { if( nn == *( DRG_Base.Note_Off_Ignore_Note_Ptr)) { *( DRG_Base.Note_Off_Ignore_Flag_Ptr) = (float)( (int)*( DRG_Base.Note_Off_Ignore_Flag_Ptr) ^ true); LG_LOG_INFO_2( "Note Off Ignore: [%f] - [%f]", *( DRG_Base.Note_Off_Ignore_Flag_Ptr), *( DRG_Base.Note_Off_Ignore_Note_Ptr)); DR_UI_Port_Notify( DRG_LV2_Base.URIS.Note_Off_Ignore_Flag_Toggle, *( DRG_Base.Note_Off_Ignore_Flag_Ptr)); } else { if( nn == 26) { LV2_Atom_Forge_Frame set_frame; LG_LOG_INFO_1( "Kit Update Reply: Kit_Id: (%d)", lv2_base_ptr->Kit_Id); lv2_atom_forge_frame_time( &( DRG_LV2_Base.Forge), 0); lv2_atom_forge_object( &( DRG_LV2_Base.Forge), &set_frame, 1, DRG_LV2_Base.URIS.UI_Msg); lv2_atom_forge_property_head( &( DRG_LV2_Base.Forge), DRG_LV2_Base.URIS.Kit_Update_Reply, 0); lv2_atom_forge_long( &( DRG_LV2_Base.Forge), lv2_base_ptr->Kit_Id); lv2_atom_forge_pop( &( DRG_LV2_Base.Forge), &set_frame); } else { // LG_LOG_INFO_1( "ZZZ Update Kit Name Tab: [%d]", nn); nn -= base_note; DR_Sample_Trigger( nn, data, offset); } } } break; } case 11: // Control Change { controler = data[1]; value = data[2]; switch( controler) { case 0: // Bank MSB Select { LG_LOG_INFO_1( "Bank MSB select: (%d)!", value); lv2_base_ptr->Bank_Id_MSB_New = value; break; } case 32: // Bank LSB Select { LG_LOG_INFO_1( "Bank LSB select: (%d)!", value); lv2_base_ptr->Bank_Id_LSB_New = value; break; } case 64: // Sustain { LG_LOG_INFO_1( "Sustain: (%d)!", value); if( value == 127) { lv2_base_ptr->Sustain = true; } else { lv2_base_ptr->Sustain = false; } if( !( lv2_base_ptr->Sustain)) { DR_Sample_UnSustain( offset); } break; } default: { LG_LOG_WARNING_2( "Unhandled controler: (%d) value: (%d)!", controler, value); break; } } break; } case 12: // Program Change { value = data[1]; LG_LOG_INFO_1( "Program change: (%d)!", value); lv2_base_ptr->Program_Id_New = value; pthread_cond_signal( &( lv2_base_ptr->Load_Cond)); 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) { LG_LOG_INFO_0( "LV2 AO..."); 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 *ui_enable_ptr = NULL; const LV2_Atom *ui_disable_ptr = NULL; const LV2_Atom *kit_update_request_ptr = NULL; const LV2_Atom *kit_update_reply_ptr = NULL; lv2_atom_object_get( obj_ptr, lv2_base_ptr->URIS.UI_Enable, &ui_enable_ptr, lv2_base_ptr->URIS.UI_Disable, &ui_disable_ptr, lv2_base_ptr->URIS.Kit_Update_Request, &kit_update_request_ptr, lv2_base_ptr->URIS.Kit_Update_Reply, &kit_update_reply_ptr, 0); if( ui_enable_ptr) { LV2_Atom_Forge_Frame obj_frame; LG_LOG_INFO_0( "UI Enable!"); lv2_atom_forge_frame_time( &( DRG_LV2_Base.Forge), 0); lv2_atom_forge_object( &( DRG_LV2_Base.Forge), &obj_frame, 1, DRG_LV2_Base.URIS.UI_Msg); lv2_atom_forge_property_head( &( DRG_LV2_Base.Forge), DRG_LV2_Base.URIS.Kit_Update_Reply, 0); lv2_atom_forge_long( &( DRG_LV2_Base.Forge), DRG_LV2_Base.Kit_Id); // Cur kit Id lv2_atom_forge_pop( &( DRG_LV2_Base.Forge), &obj_frame); LG_LOG_INFO_2( "Kit Update Reply: Kit_Id: (%ld) Kit_Name: [%s]", DRG_LV2_Base.Kit_Id, DRG_LV2_Base.Kit_Ptr->Name); /* DRT_Kit_Name *kit_name_tab; long kit_number; if( ( status = DR_Kits_Name_Get( &kit_name_tab, &kit_number)) != DRS_OK) { LG_LOG_ERROR_1( "Can't get kit names: (%d)", status); } else { LV2_Atom_Forge_Frame obj_frame, tup_frame; LV2_Atom_Forge_Ref ref; LG_LOG_INFO_3( "Update Kit Name: Nb: (%d) First: [%s] Size: (%ld)", kit_number, kit_name_tab[0].Name, sizeof( DRT_Kit_Name) * kit_number); /* lv2_atom_forge_frame_time( &( DRG_LV2_Base.Forge), 0); lv2_atom_forge_object( &( DRG_LV2_Base.Forge), &obj_frame, 1, DRG_LV2_Base.URIS.UI_Msg); lv2_atom_forge_property_head( &( DRG_LV2_Base.Forge), DRG_LV2_Base.URIS.Kit_Name_Update, 0); lv2_atom_forge_tuple( &( DRG_LV2_Base.Forge), &tup_frame); lv2_atom_forge_long( &( DRG_LV2_Base.Forge), kit_number); // Max number of kit lv2_atom_forge_long( &( DRG_LV2_Base.Forge), DRG_LV2_Base.Kit_Ptr->Id); // Cur kit Id // ref = lv2_atom_forge_write( &( DRG_LV2_Base.Forge), &( kit_name_tab[i]), sizeof( DRT_Kit_Name) * kit_number); // LG_LOG_INFO_1( "Forge: KNT Ref: (%lx)", ref); lv2_atom_forge_pop( &( DRG_LV2_Base.Forge), &tup_frame); lv2_atom_forge_pop( &( DRG_LV2_Base.Forge), &obj_frame); LG_LOG_INFO_3( "Kit Name Update: Kit_Number: (%ld) Kit_Id: (%ld) Kit_Name: [%s]", kit_number, DRG_LV2_Base.Kit_Ptr->Id, DRG_LV2_Base.Kit_Ptr->Name); } */ } if( ui_disable_ptr) { LG_LOG_INFO_0( "UI Disable!"); } if( kit_update_request_ptr) { lv2_base_ptr->Kit_Id_New = ( (const LV2_Atom_Long *)kit_update_request_ptr)->body; LG_LOG_INFO_1( "Kit Update Request: Kit_Id: (%ld)!", lv2_base_ptr->Kit_Id_New); pthread_cond_signal( &( lv2_base_ptr->Load_Cond)); } if( kit_update_reply_ptr) { LG_LOG_ERROR_0( "Kit Update Reply: This shouldn't be received by me!"); } /* 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_id = 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_id, &channel_id, 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 + base_note; mdata[2] = 0x7f; DR_Sample_Trigger( lv2_base_ptr, si, mdata, offset); } if( ignvel) DRG_Base.Velocity_Ignore_Flag = ((const LV2_Atom_Bool*)ignvel)->body; if( ignno) { fprintf( stderr, "Change Note Off Ignore Flag!\n"); // DRG_Base.Note_Off_Ignore_Flag = ((const LV2_Atom_Bool*)ignno)->body; } if( channel_id) DRG_Base.Channel_Id_Ptr = ((const LV2_Atom_Int*)channel_id)->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) { fprintf( stderr, "Get state!\n"); lv2_atom_forge_frame_time( &(lv2_base_ptr->Forge), 0); // build_state_message( lv2_base_ptr); } */ } } else { LG_LOG_WARNING_2( "Unrecognized event (%d) != (%d)", 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( lv2_base_ptr->Kit_Updated_Flag) { lv2_base_ptr->Kit_Updated_Flag = false; // Send Kit_Update_Reply LV2_Atom_Forge_Frame obj_frame; lv2_atom_forge_frame_time( &( DRG_LV2_Base.Forge), 0); lv2_atom_forge_object( &( DRG_LV2_Base.Forge), &obj_frame, 1, DRG_LV2_Base.URIS.UI_Msg); lv2_atom_forge_property_head( &( DRG_LV2_Base.Forge), DRG_LV2_Base.URIS.Kit_Update_Reply, 0); lv2_atom_forge_long( &( DRG_LV2_Base.Forge), DRG_LV2_Base.Kit_Id); // Cur kit Id lv2_atom_forge_pop( &( DRG_LV2_Base.Forge), &obj_frame); LG_LOG_INFO_2( "Kit Update Reply: Kit_Id: (%ld) Kit_Name: [%s]", DRG_LV2_Base.Kit_Id, DRG_LV2_Base.Kit_Ptr->Name); } /* 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 < DR_MIN( lv2_base_ptr->Sample_Number, 32); 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_DeActivate( LV2_Handle instance) { LG_LOG_INFO_0( "LV2 DeActivate!"); } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ static void DR_LV2_Cleanup( LV2_Handle Instance_Ptr) { DRT_Status status; LGT_Status lg_status; DRT_LV2_Base *lv2_base_ptr = (DRT_LV2_Base *)Instance_Ptr; LG_LOG_INFO_0( "LV2 Cleanup!"); DR_Tasks_Dump(); DR_Kits_Stats_Dump(); if( ( status = DR_DataStruct_DeInit()) != DRS_OK) { LG_LOG_ERROR_1( "Can't deinit data structures: (%d)!", status); } free( lv2_base_ptr->Gains); free( lv2_base_ptr->Right); free( lv2_base_ptr->Left); free( lv2_base_ptr->Request_Buf); if( ( lg_status = LG_Library_Close( true)) != LGS_OK) { fprintf( stderr, "Can't close LibLog library: (%d)\n", lg_status); } } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ 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; LG_LOG_INFO_0( "LV2 Save State!"); 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; LG_LOG_INFO_0( "LV2 Restore State!"); return( lv2_status); } /*----------------------------------------------------------------------------*/ /* LV2_State_Interface */ /*----------------------------------------------------------------------------*/ static const LV2_State_Interface DRG_LV2_State_Interface = { DR_LV2_Save_State, DR_LV2_Restore_State }; /*----------------------------------------------------------------------------*/ /* LV2_Extension_Data */ /*----------------------------------------------------------------------------*/ static const void *DR_LV2_Extension_Data( const char *uri) { if( !strcmp( uri, LV2_STATE__interface)) { return &DRG_LV2_State_Interface; } return NULL; } /*----------------------------------------------------------------------------*/ /* */ /*----------------------------------------------------------------------------*/ LV2_SYMBOL_EXPORT const LV2_Descriptor *lv2_descriptor( uint32_t Index) { switch( Index) { case 0: { return &DRG_LV2_Descriptor; } default: { return NULL; } } }