diff --git a/src/carla-backend/Makefile b/src/carla-backend/Makefile index b2580b8..416632c 100644 --- a/src/carla-backend/Makefile +++ b/src/carla-backend/Makefile @@ -13,7 +13,7 @@ CARLA_CXX_FLAGS += -DCARLA_BACKEND_NO_NAMESPACE -DCARLA_ENGINE_JACK $(CXXFLAGS) CARLA_CXX_FLAGS += -DVESTIGE_HEADER -I../carla-includes/vestige # Comment this line to not use vestige header CARLA_CXX_FLAGS += -DNDEBUG -DQT_NO_DEBUG -DQT_NO_DEBUG_STREAM -DQT_NO_DEBUG_OUTPUT -O2 -ffast-math -fomit-frame-pointer -fvisibility=hidden -mtune=generic -msse # CARLA_CXX_FLAGS += -DDEBUG -O0 -g -CARLA_LINK_FLAGS = -shared -fPIC -ldl `pkg-config --libs jack fluidsynth liblo QtCore QtGui` $(LDFLAGS) +CARLA_LINK_FLAGS = -shared -fPIC -ldl `pkg-config --libs jack fluidsynth linuxsampler liblo QtCore QtGui` $(LDFLAGS) OBJS = carla_backend.o carla_bridge.o carla_engine_jack.o carla_osc.o carla_shared.o carla_threads.o ladspa.o dssi.o lv2.o vst.o fluidsynth.o linuxsampler.o lv2-rtmempool/rtmempool.o diff --git a/src/carla-backend/carla_backend.cpp b/src/carla-backend/carla_backend.cpp index 59a310c..d76f726 100644 --- a/src/carla-backend/carla_backend.cpp +++ b/src/carla-backend/carla_backend.cpp @@ -1257,7 +1257,7 @@ int main(int argc, char* argv[]) if (engine_init("carla_demo")) { set_callback_function(main_callback); - short id = add_plugin_dssi("/usr/lib/dssi/calf.so", "Reverb", "/usr/lib/dssi/calf/calf_gtk"); + short id = add_plugin_sfz("/home/falktx/Personal/Muzyks/Kits/SFZ/AcousticGuitarFREE/AcousticGuitarFREE Samples/AcousticGuitar.sfz", "xaxaxa"); if (id >= 0) { diff --git a/src/carla-backend/dssi.cpp b/src/carla-backend/dssi.cpp index 7f63087..9ad18b6 100644 --- a/src/carla-backend/dssi.cpp +++ b/src/carla-backend/dssi.cpp @@ -928,7 +928,8 @@ public: snd_seq_event_t* midi_event = &midi_events[midi_event_count]; memset(midi_event, 0, sizeof(snd_seq_event_t)); - midi_event->type = extMidiNotes[i].onoff ? SND_SEQ_EVENT_NOTEON : SND_SEQ_EVENT_NOTEOFF; + midi_event->type = extMidiNotes[i].onoff ? SND_SEQ_EVENT_NOTEON : SND_SEQ_EVENT_NOTEOFF; + midi_event->time.tick = nframesOffset; // FIXME - other types may also need time-check here midi_event->data.note.channel = cin_channel; midi_event->data.note.note = extMidiNotes[i].note; midi_event->data.note.velocity = extMidiNotes[i].velo; diff --git a/src/carla-backend/fluidsynth.cpp b/src/carla-backend/fluidsynth.cpp index ac71b17..582cf4c 100644 --- a/src/carla-backend/fluidsynth.cpp +++ b/src/carla-backend/fluidsynth.cpp @@ -82,9 +82,9 @@ public: { switch (param_id) { - case Sf2ChorusType: + case FluidSynthChorusType: return 2; - case Sf2Interpolation: + case FluidSynthInterpolation: return 4; default: return 0; @@ -103,7 +103,7 @@ public: { switch (param_id) { - case Sf2ChorusType: + case FluidSynthChorusType: switch (scalepoint_id) { case 0: @@ -113,7 +113,7 @@ public: default: return FLUID_CHORUS_DEFAULT_TYPE; } - case Sf2Interpolation: + case FluidSynthInterpolation: switch (scalepoint_id) { case 0: @@ -156,46 +156,46 @@ public: { switch (param_id) { - case Sf2ReverbOnOff: + case FluidSynthReverbOnOff: strncpy(buf_str, "Reverb On/Off", STR_MAX); break; - case Sf2ReverbRoomSize: + case FluidSynthReverbRoomSize: strncpy(buf_str, "Reverb Room Size", STR_MAX); break; - case Sf2ReverbDamp: + case FluidSynthReverbDamp: strncpy(buf_str, "Reverb Damp", STR_MAX); break; - case Sf2ReverbLevel: + case FluidSynthReverbLevel: strncpy(buf_str, "Reverb Level", STR_MAX); break; - case Sf2ReverbWidth: + case FluidSynthReverbWidth: strncpy(buf_str, "Reverb Width", STR_MAX); break; - case Sf2ChorusOnOff: + case FluidSynthChorusOnOff: strncpy(buf_str, "Chorus On/Off", STR_MAX); break; - case Sf2ChorusNr: + case FluidSynthChorusNr: strncpy(buf_str, "Chorus Voice Count", STR_MAX); break; - case Sf2ChorusLevel: + case FluidSynthChorusLevel: strncpy(buf_str, "Chorus Level", STR_MAX); break; - case Sf2ChorusSpeedHz: + case FluidSynthChorusSpeedHz: strncpy(buf_str, "Chorus Speed", STR_MAX); break; - case Sf2ChorusDepthMs: + case FluidSynthChorusDepthMs: strncpy(buf_str, "Chorus Depth", STR_MAX); break; - case Sf2ChorusType: + case FluidSynthChorusType: strncpy(buf_str, "Chorus Type", STR_MAX); break; - case Sf2Polyphony: + case FluidSynthPolyphony: strncpy(buf_str, "Polyphony", STR_MAX); break; - case Sf2Interpolation: + case FluidSynthInterpolation: strncpy(buf_str, "Interpolation", STR_MAX); break; - case Sf2VoiceCount: + case FluidSynthVoiceCount: strncpy(buf_str, "Voice Count", STR_MAX); break; default: @@ -208,10 +208,10 @@ public: { switch (param_id) { - case Sf2ChorusSpeedHz: + case FluidSynthChorusSpeedHz: strncpy(buf_str, "Hz", STR_MAX); break; - case Sf2ChorusDepthMs: + case FluidSynthChorusDepthMs: strncpy(buf_str, "ms", STR_MAX); break; default: @@ -224,7 +224,7 @@ public: { switch (param_id) { - case Sf2ChorusType: + case FluidSynthChorusType: switch (scalepoint_id) { case 0: @@ -234,7 +234,7 @@ public: strncpy(buf_str, "Triangle wave", STR_MAX); return; } - case Sf2Interpolation: + case FluidSynthInterpolation: switch (scalepoint_id) { case 0: @@ -263,19 +263,19 @@ public: switch(param_id) { - case Sf2ReverbOnOff: + case FluidSynthReverbOnOff: value = value > 0.5 ? 1 : 0; fluid_synth_set_reverb_on(f_synth, value); break; - case Sf2ReverbRoomSize: - case Sf2ReverbDamp: - case Sf2ReverbLevel: - case Sf2ReverbWidth: - fluid_synth_set_reverb(f_synth, param_buffers[Sf2ReverbRoomSize], param_buffers[Sf2ReverbDamp], param_buffers[Sf2ReverbWidth], param_buffers[Sf2ReverbLevel]); + case FluidSynthReverbRoomSize: + case FluidSynthReverbDamp: + case FluidSynthReverbLevel: + case FluidSynthReverbWidth: + fluid_synth_set_reverb(f_synth, param_buffers[FluidSynthReverbRoomSize], param_buffers[FluidSynthReverbDamp], param_buffers[FluidSynthReverbWidth], param_buffers[FluidSynthReverbLevel]); break; - case Sf2ChorusOnOff: + case FluidSynthChorusOnOff: { const CarlaPluginScopedDisabler m(this, ! CarlaEngine::isOffline()); value = value > 0.5 ? 1 : 0; @@ -283,25 +283,25 @@ public: break; } - case Sf2ChorusNr: - case Sf2ChorusLevel: - case Sf2ChorusSpeedHz: - case Sf2ChorusDepthMs: - case Sf2ChorusType: + case FluidSynthChorusNr: + case FluidSynthChorusLevel: + case FluidSynthChorusSpeedHz: + case FluidSynthChorusDepthMs: + case FluidSynthChorusType: { const CarlaPluginScopedDisabler m(this, ! CarlaEngine::isOffline()); - fluid_synth_set_chorus(f_synth, rint(param_buffers[Sf2ChorusNr]), param_buffers[Sf2ChorusLevel], param_buffers[Sf2ChorusSpeedHz], param_buffers[Sf2ChorusDepthMs], rint(param_buffers[Sf2ChorusType])); + fluid_synth_set_chorus(f_synth, rint(param_buffers[FluidSynthChorusNr]), param_buffers[FluidSynthChorusLevel], param_buffers[FluidSynthChorusSpeedHz], param_buffers[FluidSynthChorusDepthMs], rint(param_buffers[FluidSynthChorusType])); break; } - case Sf2Polyphony: + case FluidSynthPolyphony: { const CarlaPluginScopedDisabler m(this, ! CarlaEngine::isOffline()); fluid_synth_set_polyphony(f_synth, rint(value)); break; } - case Sf2Interpolation: + case FluidSynthInterpolation: { const CarlaPluginScopedDisabler m(this, ! CarlaEngine::isOffline()); for (int i=0; i < 16; i++) @@ -360,7 +360,7 @@ public: uint32_t aouts, params, j; aouts = 2; - params = Sf2ParametersMax; + params = FluidSynthParametersMax; aout.ports = new CarlaEngineAudioPort*[aouts]; aout.rindexes = new uint32_t[aouts]; @@ -443,7 +443,7 @@ public: param.port_cout = (CarlaEngineControlPort*)x_client->addPort(port_name, CarlaEnginePortTypeControl, false); // ---------------------- - j = Sf2ReverbOnOff; + j = FluidSynthReverbOnOff; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -459,7 +459,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ReverbRoomSize; + j = FluidSynthReverbRoomSize; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -475,7 +475,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ReverbDamp; + j = FluidSynthReverbDamp; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -491,7 +491,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ReverbLevel; + j = FluidSynthReverbLevel; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -507,7 +507,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ReverbWidth; + j = FluidSynthReverbWidth; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -523,7 +523,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ChorusOnOff; + j = FluidSynthChorusOnOff; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -539,7 +539,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ChorusNr; + j = FluidSynthChorusNr; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -555,7 +555,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ChorusLevel; + j = FluidSynthChorusLevel; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -571,7 +571,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ChorusSpeedHz; + j = FluidSynthChorusSpeedHz; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -587,7 +587,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ChorusDepthMs; + j = FluidSynthChorusDepthMs; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -603,7 +603,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2ChorusType; + j = FluidSynthChorusType; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -619,7 +619,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2Polyphony; + j = FluidSynthPolyphony; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -635,7 +635,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2Interpolation; + j = FluidSynthInterpolation; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_INPUT; @@ -651,7 +651,7 @@ public: param_buffers[j] = param.ranges[j].def; // ---------------------- - j = Sf2VoiceCount; + j = FluidSynthVoiceCount; param.data[j].index = j; param.data[j].rindex = j; param.data[j].type = PARAMETER_OUTPUT; @@ -743,27 +743,24 @@ public: callback_action(CALLBACK_RELOAD_PROGRAMS, m_id, 0, 0, 0.0); #endif - if (init) + if (init && midiprog.count > 0) { - if (midiprog.count > 0) - { - fluid_synth_program_reset(f_synth); + fluid_synth_program_reset(f_synth); - for (i=0; i < 16 && i != 9; i++) - { + for (i=0; i < 16 && i != 9; i++) + { #ifdef FLUIDSYNTH_VERSION_NEW_API - fluid_synth_set_channel_type(f_synth, i, CHANNEL_TYPE_MELODIC); + fluid_synth_set_channel_type(f_synth, i, CHANNEL_TYPE_MELODIC); #endif - fluid_synth_program_select(f_synth, i, f_id, midiprog.data[0].bank, midiprog.data[0].program); - } + fluid_synth_program_select(f_synth, i, f_id, midiprog.data[0].bank, midiprog.data[0].program); + } #ifdef FLUIDSYNTH_VERSION_NEW_API - fluid_synth_set_channel_type(f_synth, 9, CHANNEL_TYPE_DRUM); + fluid_synth_set_channel_type(f_synth, 9, CHANNEL_TYPE_DRUM); #endif - fluid_synth_program_select(f_synth, 9, f_id, 128, 0); + fluid_synth_program_select(f_synth, 9, f_id, 128, 0); - set_midi_program(0, false, false, false, true); - } + set_midi_program(0, false, false, false, true); } } @@ -773,7 +770,7 @@ public: void process(float** ains_buffer, float** aouts_buffer, uint32_t nframes, uint32_t nframesOffset) { uint32_t i, k; - unsigned int midi_event_count = 0; + uint32_t midi_event_count = 0; double aouts_peak_tmp[2] = { 0.0 }; @@ -1154,7 +1151,7 @@ public: if (nframesOffset == 0 || ! m_active_before) param.port_cout->initBuffer(cout_buffer); - k = Sf2VoiceCount; + k = FluidSynthVoiceCount; param_buffers[k] = rint(fluid_synth_get_active_voice_count(f_synth)); fix_parameter_value(param_buffers[k], param.ranges[k]); @@ -1199,29 +1196,29 @@ public: } private: - enum Sf2InputParameters { - Sf2ReverbOnOff = 0, - Sf2ReverbRoomSize = 1, - Sf2ReverbDamp = 2, - Sf2ReverbLevel = 3, - Sf2ReverbWidth = 4, - Sf2ChorusOnOff = 5, - Sf2ChorusNr = 6, - Sf2ChorusLevel = 7, - Sf2ChorusSpeedHz = 8, - Sf2ChorusDepthMs = 9, - Sf2ChorusType = 10, - Sf2Polyphony = 11, - Sf2Interpolation = 12, - Sf2VoiceCount = 13, - Sf2ParametersMax = 14 + enum FluidSynthInputParameters { + FluidSynthReverbOnOff = 0, + FluidSynthReverbRoomSize = 1, + FluidSynthReverbDamp = 2, + FluidSynthReverbLevel = 3, + FluidSynthReverbWidth = 4, + FluidSynthChorusOnOff = 5, + FluidSynthChorusNr = 6, + FluidSynthChorusLevel = 7, + FluidSynthChorusSpeedHz = 8, + FluidSynthChorusDepthMs = 9, + FluidSynthChorusType = 10, + FluidSynthPolyphony = 11, + FluidSynthInterpolation = 12, + FluidSynthVoiceCount = 13, + FluidSynthParametersMax = 14 }; fluid_settings_t* f_settings; fluid_synth_t* f_synth; int f_id; - double param_buffers[Sf2ParametersMax]; + double param_buffers[FluidSynthParametersMax]; const char* m_label; }; diff --git a/src/carla-backend/linuxsampler.cpp b/src/carla-backend/linuxsampler.cpp index 9e639fd..6709216 100644 --- a/src/carla-backend/linuxsampler.cpp +++ b/src/carla-backend/linuxsampler.cpp @@ -21,12 +21,124 @@ #include "carla_plugin.h" +#include +#include "linuxsampler/EngineFactory.h" + +#include + CARLA_BACKEND_START_NAMESPACE #if 0 } /* adjust editor indent */ #endif +#define LINUXSAMPLER_VOLUME_MAX 3.16227766f // +10 dB +#define LINUXSAMPLER_VOLUME_MIN 0.0f // -inf dB + +class AudioOutputDevicePlugin : public LinuxSampler::AudioOutputDevice +{ +public: + AudioOutputDevicePlugin(CarlaPlugin* plugin) : + AudioOutputDevice(std::map()), + m_plugin(plugin) + { + } + + // ------------------------------------------------------------------- + // LinuxSampler virtual methods + + void Play() + { + } + + bool IsPlaying() + { + return m_plugin && m_plugin->enabled(); + } + + void Stop() + { + } + + uint MaxSamplesPerCycle() + { + return get_buffer_size(); + } + + uint SampleRate() + { + return get_sample_rate(); + } + + String Driver() + { + return "AudioOutputDevicePlugin"; + } + + LinuxSampler::AudioChannel* CreateChannel(uint channelNr) + { + return new LinuxSampler::AudioChannel(channelNr, nullptr, 0); + } + + // ------------------------------------------------------------------- + + int Render(uint samples) + { + return RenderAudio(samples); + } + +private: + CarlaPlugin* m_plugin; +}; + +class MidiInputDevicePlugin : public LinuxSampler::MidiInputDevice +{ +public: + MidiInputDevicePlugin(LinuxSampler::Sampler* sampler) : LinuxSampler::MidiInputDevice(std::map(), sampler) + { + } + + // ------------------------------------------------------------------- + // MIDI Port implementation for this plugin MIDI input driver + + class MidiInputPortPlugin : public LinuxSampler::MidiInputPort + { + protected: + MidiInputPortPlugin(MidiInputDevicePlugin* device, int portNumber) : LinuxSampler::MidiInputPort(device, portNumber) + { + } + friend class MidiInputDevicePlugin; + }; + + // ------------------------------------------------------------------- + // LinuxSampler virtual methods + + void Listen() + { + } + + void StopListen() + { + } + + String Driver() + { + return "MidiInputDevicePlugin"; + } + + LinuxSampler::MidiInputPort* CreateMidiPort() + { + return new MidiInputPortPlugin(this, Ports.size()); + } + + // ------------------------------------------------------------------- + + void DeleteMidiPort(LinuxSampler::MidiInputPort* port) + { + delete (MidiInputPortPlugin*)port; + } +}; + class LinuxSamplerPlugin : public CarlaPlugin { public: @@ -35,15 +147,42 @@ public: qDebug("LinuxSamplerPlugin::LinuxSamplerPlugin()"); m_type = isGIG ? PLUGIN_GIG : PLUGIN_SFZ; + sampler = new LinuxSampler::Sampler; + + audioOutputDevice = new AudioOutputDevicePlugin(this); + midiInputDevice = new MidiInputDevicePlugin(sampler); + midiInputPort = midiInputDevice->CreateMidiPort(); + + m_isGIG = isGIG; + m_name = nullptr; m_label = nullptr; + m_maker = nullptr; } ~LinuxSamplerPlugin() { qDebug("LinuxSamplerPlugin::~LinuxSamplerPlugin()"); + if (sampler_channel) + { + midiInputPort->Disconnect(sampler_channel->GetEngineChannel()); + sampler->RemoveSamplerChannel(sampler_channel); + } + + midiInputDevice->DeleteMidiPort(midiInputPort); + + delete audioOutputDevice; + delete midiInputDevice; + delete sampler; + + if (m_name) + free((void*)m_name); + if (m_label) free((void*)m_label); + + if (m_maker) + free((void*)m_maker); } // ------------------------------------------------------------------- @@ -54,24 +193,466 @@ public: return PLUGIN_CATEGORY_SYNTH; } + // ------------------------------------------------------------------- + // Information (per-plugin data) + + void get_label(char* buf_str) + { + strncpy(buf_str, m_label, STR_MAX); + } + + void get_maker(char* buf_str) + { + strncpy(buf_str, m_maker, STR_MAX); + } + + void get_copyright(char* buf_str) + { + strncpy(buf_str, m_maker, STR_MAX); + } + + void get_real_name(char* buf_str) + { + strncpy(buf_str, m_name, STR_MAX); + } + + // ------------------------------------------------------------------- + // Plugin state + + void reload() + { + qDebug("LinuxSamplerPlugin::reload() - start"); + + // Safely disable plugin for reload + const CarlaPluginScopedDisabler m(this); + + if (x_client->isActive()) + x_client->deactivate(); + + // Remove client ports + remove_client_ports(); + + // Delete old data + delete_buffers(); + + uint32_t aouts; + aouts = 2; + + aout.ports = new CarlaEngineAudioPort*[aouts]; + aout.rindexes = new uint32_t[aouts]; + + const int port_name_size = CarlaEngine::maxPortNameSize() - 1; + char port_name[port_name_size]; + + // --------------------------------------- + // Audio Outputs + +#ifndef BUILD_BRIDGE + if (carla_options.global_jack_client) + { + strcpy(port_name, m_name); + strcat(port_name, ":out-left"); + } + else +#endif + strcpy(port_name, "out-left"); + + aout.ports[0] = (CarlaEngineAudioPort*)x_client->addPort(port_name, CarlaEnginePortTypeAudio, false); + aout.rindexes[0] = 0; + +#ifndef BUILD_BRIDGE + if (carla_options.global_jack_client) + { + strcpy(port_name, m_name); + strcat(port_name, ":out-right"); + } + else +#endif + strcpy(port_name, "out-right"); + + aout.ports[1] = (CarlaEngineAudioPort*)x_client->addPort(port_name, CarlaEnginePortTypeAudio, false); + aout.rindexes[1] = 1; + + // --------------------------------------- + // MIDI Input + +#ifndef BUILD_BRIDGE + if (carla_options.global_jack_client) + { + strcpy(port_name, m_name); + strcat(port_name, ":midi-in"); + } + else +#endif + strcpy(port_name, "midi-in"); + + midi.port_min = (CarlaEngineMidiPort*)x_client->addPort(port_name, CarlaEnginePortTypeMIDI, true); + + // --------------------------------------- + + aout.count = aouts; + + // plugin checks + m_hints &= ~(PLUGIN_IS_SYNTH | PLUGIN_USES_CHUNKS | PLUGIN_CAN_DRYWET | PLUGIN_CAN_VOLUME | PLUGIN_CAN_BALANCE); + + m_hints |= PLUGIN_IS_SYNTH; + m_hints |= PLUGIN_CAN_VOLUME; + m_hints |= PLUGIN_CAN_BALANCE; + + reload_programs(true); + + x_client->activate(); + + qDebug("LinuxSamplerPlugin::reload() - end"); + } + + void reload_programs(bool init) + { + qDebug("LinuxSamplerPlugin::reload_programs(%s)", bool2str(init)); + + // Delete old programs + if (midiprog.count > 0) + { + for (uint32_t i=0; i < midiprog.count; i++) + free((void*)midiprog.data[i].name); + + delete[] midiprog.data; + } + + midiprog.count = 0; + midiprog.data = nullptr; + + // Query new programs + uint32_t i = 0; + midiprog.count += instrumentIds.size(); + + if (midiprog.count > 0) + midiprog.data = new midi_program_t [midiprog.count]; + + // Update data + for (i=0; i < midiprog.count; i++) + { + LinuxSampler::InstrumentManager::instrument_info_t info = instrument->GetInstrumentInfo(instrumentIds[i]); + + midiprog.data[i].bank = 0; + midiprog.data[i].program = i; + midiprog.data[i].name = strdup(info.InstrumentName.c_str()); + } + +#ifndef BUILD_BRIDGE + // Update OSC Names + osc_global_send_set_midi_program_count(m_id, midiprog.count); + + for (i=0; i < midiprog.count; i++) + osc_global_send_set_midi_program_data(m_id, i, midiprog.data[i].bank, midiprog.data[i].program, midiprog.data[i].name); + + callback_action(CALLBACK_RELOAD_PROGRAMS, m_id, 0, 0, 0.0); +#endif + + if (init && midiprog.count > 0) + { + set_midi_program(0, false, false, false, true); + } + } + + // ------------------------------------------------------------------- + // Plugin processing + + void process(float** ains_buffer, float** aouts_buffer, uint32_t nframes, uint32_t nframesOffset = 0) + { + uint32_t i, k; + uint32_t midi_event_count = 0; + + double aouts_peak_tmp[2] = { 0.0 }; + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // MIDI Input (External) + + if (cin_channel >= 0 && cin_channel < 16 && m_active && m_active_before) + { + carla_midi_lock(); + + for (i=0; i < MAX_MIDI_EVENTS && midi_event_count < MAX_MIDI_EVENTS; i++) + { + if (extMidiNotes[i].valid) + { + if (extMidiNotes[i].onoff) + midiInputPort->DispatchNoteOn(extMidiNotes[i].note, extMidiNotes[i].velo, cin_channel, nframesOffset); + else + midiInputPort->DispatchNoteOff(extMidiNotes[i].note, extMidiNotes[i].velo, cin_channel, nframesOffset); + + extMidiNotes[i].valid = false; + midi_event_count += 1; + } + else + break; + } + + carla_midi_unlock(); + + } // End of MIDI Input (External) + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // MIDI Input (System) + + if (m_active && m_active_before) + { + void* min_buffer = midi.port_min->getBuffer(); + + const CarlaEngineMidiEvent* min_event; + uint32_t time, n_min_events = midi.port_min->getEventCount(min_buffer); + + for (i=0; i < n_min_events && midi_event_count < MAX_MIDI_EVENTS; i++) + { + min_event = midi.port_min->getEvent(min_buffer, i); + + if (! min_event) + continue; + + time = min_event->time - nframesOffset; + + if (time >= nframes) + continue; + + uint8_t status = min_event->data[0]; + uint8_t channel = status & 0x0F; + + // Fix bad note-off + if (MIDI_IS_STATUS_NOTE_ON(status) && min_event->data[2] == 0) + status -= 0x10; + + if (MIDI_IS_STATUS_NOTE_OFF(status)) + { + uint8_t note = min_event->data[1]; + + midiInputPort->DispatchNoteOff(note, 0, channel, time); + + if (channel == cin_channel) + postpone_event(PluginPostEventNoteOff, note, 0.0); + } + else if (MIDI_IS_STATUS_NOTE_ON(status)) + { + uint8_t note = min_event->data[1]; + uint8_t velo = min_event->data[2]; + + midiInputPort->DispatchNoteOn(note, velo, channel, time); + + if (channel == cin_channel) + postpone_event(PluginPostEventNoteOn, note, velo); + } + else if (MIDI_IS_STATUS_AFTERTOUCH(status)) + { + uint8_t pressure = min_event->data[1]; + + midiInputPort->DispatchControlChange(MIDI_STATUS_AFTERTOUCH, pressure, channel, time); + } + else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status)) + { + uint8_t lsb = min_event->data[1]; + uint8_t msb = min_event->data[2]; + + midiInputPort->DispatchPitchbend(((msb << 7) | lsb) - 8192, channel, time); + } + else + continue; + + midi_event_count += 1; + } + } // End of MIDI Input (System) + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // Plugin processing + + if (m_active) + { + if (! m_active_before) + { + if (cin_channel >= 0 && cin_channel < 16) + { + midiInputPort->DispatchControlChange(MIDI_CONTROL_ALL_SOUND_OFF, 0, cin_channel); + midiInputPort->DispatchControlChange(MIDI_CONTROL_ALL_NOTES_OFF, 0, cin_channel); + } + } + + audioOutputDevice->Channel(0)->SetBuffer(aouts_buffer[0]); + audioOutputDevice->Channel(1)->SetBuffer(aouts_buffer[1]); + // QUESTION: Need to clear it before? + audioOutputDevice->Render(nframes); + } + + // -------------------------------------------------------------------------------------------------------- + // Post-processing (dry/wet, volume and balance) + + if (m_active) + { + bool do_volume = x_vol != 1.0; + bool do_balance = (x_bal_left != -1.0 || x_bal_right != 1.0); + + double bal_rangeL, bal_rangeR; + float old_bal_left[do_balance ? nframes : 0]; + + for (i=0; i < aout.count; i++) + { + // Volume + if (do_volume) + { + for (k=0; k aouts_peak_tmp[i]) + aouts_peak_tmp[i] = abs_d(aouts_buffer[i][k]); + } + } + } + else + { + // disable any output sound if not active + for (i=0; i < aout.count; i++) + memset(aouts_buffer[i], 0.0f, sizeof(float)*nframes); + + aouts_peak_tmp[0] = 0.0; + aouts_peak_tmp[1] = 0.0; + + } // End of Post-processing + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // Peak Values + + aouts_peak[(m_id*2)+0] = aouts_peak_tmp[0]; + aouts_peak[(m_id*2)+1] = aouts_peak_tmp[1]; + + m_active_before = m_active; + } + // ------------------------------------------------------------------- bool init(const char* filename, const char* label) { - m_filename = strdup(filename); - m_label = strdup(label); - m_name = get_unique_name(label); - x_client = new CarlaEngineClient(this); + QFileInfo file(filename); - if (x_client->isOk()) - return true; + if (file.exists() && file.isFile() && file.isReadable()) + { + const char* stype = m_isGIG ? "gig" : "sfz"; + + try { + engine = LinuxSampler::EngineFactory::Create(stype); + } + catch (LinuxSampler::Exception& e) + { + set_last_error(e.what()); + return false; + } + + try { + instrument = engine->GetInstrumentManager(); + } + catch (LinuxSampler::Exception& e) + { + set_last_error(e.what()); + return false; + } + + try { + instrumentIds = instrument->GetInstrumentFileContent(filename); + } + catch (LinuxSampler::Exception& e) + { + set_last_error(e.what()); + return false; + } + + if (instrumentIds.size() > 0) + { + LinuxSampler::InstrumentManager::instrument_info_t info = instrument->GetInstrumentInfo(instrumentIds[0]); + + m_name = strdup(info.InstrumentName.c_str()); + m_label = strdup(info.Product.c_str()); + m_maker = strdup(info.Artists.c_str()); + m_filename = strdup(filename); + + sampler_channel = sampler->AddSamplerChannel(); + sampler_channel->SetEngineType(stype); + sampler_channel->SetAudioOutputDevice(audioOutputDevice); + //sampler_channel->SetMidiInputDevice(midiInputDevice); + //sampler_channel->SetMidiInputChannel(LinuxSampler::midi_chan_1); + midiInputPort->Connect(sampler_channel->GetEngineChannel(), LinuxSampler::midi_chan_all); + + engine_channel = sampler_channel->GetEngineChannel(); + engine_channel->Connect(audioOutputDevice); + engine_channel->PrepareLoadInstrument(filename, 0); // todo - find instrument from label + engine_channel->LoadInstrument(); + engine_channel->Volume(LINUXSAMPLER_VOLUME_MAX); + + x_client = new CarlaEngineClient(this); + + if (x_client->isOk()) + return true; + else + set_last_error("Failed to register plugin client"); + } + else + set_last_error("Failed to find any instruments"); + } + else + set_last_error("Requested file is not valid or does not exist"); - set_last_error("Failed to register plugin client"); return false; } private: + LinuxSampler::Sampler* sampler; + LinuxSampler::SamplerChannel* sampler_channel; + LinuxSampler::Engine* engine; + LinuxSampler::EngineChannel* engine_channel; + LinuxSampler::InstrumentManager* instrument; + std::vector instrumentIds; + + AudioOutputDevicePlugin* audioOutputDevice; + MidiInputDevicePlugin* midiInputDevice; + LinuxSampler::MidiInputPort* midiInputPort; + + bool m_isGIG; + const char* m_name; const char* m_label; + const char* m_maker; }; short add_plugin_linuxsampler(const char* filename, const char* label, bool isGIG) diff --git a/src/carla-backend/qtcreator/carla-backend.pro b/src/carla-backend/qtcreator/carla-backend.pro index 9993839..4460ad4 100644 --- a/src/carla-backend/qtcreator/carla-backend.pro +++ b/src/carla-backend/qtcreator/carla-backend.pro @@ -3,7 +3,7 @@ QT = core gui CONFIG = debug link_pkgconfig qt warn_on -PKGCONFIG = jack liblo fluidsynth +PKGCONFIG = jack liblo fluidsynth linuxsampler TARGET = carla_backend TEMPLATE = app diff --git a/src/carla-discovery/carla-discovery.cpp b/src/carla-discovery/carla-discovery.cpp index 0ad4ebe..3f2c904 100644 --- a/src/carla-discovery/carla-discovery.cpp +++ b/src/carla-discovery/carla-discovery.cpp @@ -1150,25 +1150,10 @@ void do_linuxsampler_check(const char* filename, const char* stype) QFileInfo file(filename); - if (! file.exists()) - { - DISCOVERY_OUT("error", "Requested file does not exist"); - return; - } - - if (! file.isFile()) - { - DISCOVERY_OUT("error", "Requested filename is not a file"); - return; - } - - if (! file.isReadable()) - { - DISCOVERY_OUT("error", "Requested file is not readable"); - return; - } - - const ScopedEngine engine(filename, stype); + if (file.exists() && file.isFile() && file.isReadable()) + const ScopedEngine engine(filename, stype); + else + DISCOVERY_OUT("error", "Requested file is not valid or does not exist"); #else (void)filename;