Browse Source

Allow to pass MIDI bank/program changes to LV2 plugins, closes #136

tags/1.9.5
falkTX 11 years ago
parent
commit
2d1a596f0f
5 changed files with 116 additions and 24 deletions
  1. +12
    -5
      resources/ui/carla_edit.ui
  2. +6
    -0
      source/backend/CarlaBackend.h
  3. +53
    -11
      source/backend/plugin/Lv2Plugin.cpp
  4. +4
    -0
      source/carla_backend.py
  5. +41
    -8
      source/carla_widgets.py

+ 12
- 5
resources/ui/carla_edit.ui View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>469</width>
<height>407</height>
<width>501</width>
<height>431</height>
</rect>
</property>
<property name="windowTitle">
@@ -343,14 +343,14 @@
<item>
<widget class="QCheckBox" name="ch_fixed_buffer">
<property name="text">
<string>Fixed-size buffer</string>
<string>Fixed-Size Buffer</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ch_force_stereo">
<property name="text">
<string>Force stereo (needs restart)</string>
<string>Force Stereo (needs restart)</string>
</property>
</widget>
</item>
@@ -383,7 +383,14 @@
<item>
<widget class="QCheckBox" name="ch_map_program_changes">
<property name="text">
<string>Map Program changes</string>
<string>Map Program Changes</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="ch_send_program_changes">
<property name="text">
<string>Send Bank/Program Changes</string>
</property>
</widget>
</item>


+ 6
- 0
source/backend/CarlaBackend.h View File

@@ -226,6 +226,12 @@ const uint PLUGIN_OPTION_SEND_PITCHBEND = 0x080;
*/
const uint PLUGIN_OPTION_SEND_ALL_SOUND_OFF = 0x100;

/*!
* Send MIDI bank/program changes.
* @note: This option conflicts with PLUGIN_OPTION_MAP_PROGRAM_CHANGES and cannot be used at the same time.
*/
const uint PLUGIN_OPTION_SEND_PROGRAM_CHANGES = 0x200;

/** @} */

/* ------------------------------------------------------------------------------------------------------------


+ 53
- 11
source/backend/plugin/Lv2Plugin.cpp View File

@@ -724,6 +724,7 @@ public:

if (hasMidiIn)
{
options |= PLUGIN_OPTION_SEND_PROGRAM_CHANGES;
options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
@@ -3004,26 +3005,67 @@ public:
} // case kEngineControlEventTypeParameter

case kEngineControlEventTypeMidiBank:
if (event.channel == pData->ctrlChannel && (pData->options & PLUGIN_OPTION_MAP_PROGRAM_CHANGES) != 0)
nextBankId = ctrlEvent.param;
if (pData->options & PLUGIN_OPTION_MAP_PROGRAM_CHANGES)
{
if (event.channel == pData->ctrlChannel)
nextBankId = ctrlEvent.param;
}
else if (pData->options & PLUGIN_OPTION_SEND_PROGRAM_CHANGES)
{
uint8_t midiData[3];
midiData[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
midiData[1] = MIDI_CONTROL_BANK_SELECT;
midiData[2] = uint8_t(ctrlEvent.param);

const uint32_t mtime(isSampleAccurate ? startTime : event.time);

if (fEventsIn.ctrl->type & CARLA_EVENT_DATA_ATOM)
lv2_atom_buffer_write(&evInAtomIters[fEventsIn.ctrlIndex], mtime, 0, CARLA_URI_MAP_ID_MIDI_EVENT, 3, midiData);

else if (fEventsIn.ctrl->type & CARLA_EVENT_DATA_EVENT)
lv2_event_write(&evInEventIters[fEventsIn.ctrlIndex], mtime, 0, CARLA_URI_MAP_ID_MIDI_EVENT, 3, midiData);

else if (fEventsIn.ctrl->type & CARLA_EVENT_DATA_MIDI_LL)
lv2midi_put_event(&evInMidiStates[fEventsIn.ctrlIndex], mtime, 3, midiData);
}
break;

case kEngineControlEventTypeMidiProgram:
if (event.channel == pData->ctrlChannel && (pData->options & PLUGIN_OPTION_MAP_PROGRAM_CHANGES) != 0)
if (pData->options & PLUGIN_OPTION_MAP_PROGRAM_CHANGES)
{
const uint32_t nextProgramId = ctrlEvent.param;

for (uint32_t k=0; k < pData->midiprog.count; ++k)
if (event.channel == pData->ctrlChannel)
{
if (pData->midiprog.data[k].bank == nextBankId && pData->midiprog.data[k].program == nextProgramId)
const uint32_t nextProgramId = ctrlEvent.param;

for (uint32_t k=0; k < pData->midiprog.count; ++k)
{
const int32_t index(static_cast<int32_t>(k));
setMidiProgram(index, false, false, false);
pData->postponeRtEvent(kPluginPostRtEventMidiProgramChange, index, 0, 0.0f);
break;
if (pData->midiprog.data[k].bank == nextBankId && pData->midiprog.data[k].program == nextProgramId)
{
const int32_t index(static_cast<int32_t>(k));
setMidiProgram(index, false, false, false);
pData->postponeRtEvent(kPluginPostRtEventMidiProgramChange, index, 0, 0.0f);
break;
}
}
}
}
else if (pData->options & PLUGIN_OPTION_SEND_PROGRAM_CHANGES)
{
uint8_t midiData[2];
midiData[0] = uint8_t(MIDI_STATUS_PROGRAM_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
midiData[1] = uint8_t(ctrlEvent.param);

const uint32_t mtime(isSampleAccurate ? startTime : event.time);

if (fEventsIn.ctrl->type & CARLA_EVENT_DATA_ATOM)
lv2_atom_buffer_write(&evInAtomIters[fEventsIn.ctrlIndex], mtime, 0, CARLA_URI_MAP_ID_MIDI_EVENT, 2, midiData);

else if (fEventsIn.ctrl->type & CARLA_EVENT_DATA_EVENT)
lv2_event_write(&evInEventIters[fEventsIn.ctrlIndex], mtime, 0, CARLA_URI_MAP_ID_MIDI_EVENT, 2, midiData);

else if (fEventsIn.ctrl->type & CARLA_EVENT_DATA_MIDI_LL)
lv2midi_put_event(&evInMidiStates[fEventsIn.ctrlIndex], mtime, 2, midiData);
}
break;

case kEngineControlEventTypeAllSoundOff:


+ 4
- 0
source/carla_backend.py View File

@@ -254,6 +254,10 @@ PLUGIN_OPTION_SEND_PITCHBEND = 0x080
# Send MIDI all-sounds/notes-off events, single note-offs otherwise.
PLUGIN_OPTION_SEND_ALL_SOUND_OFF = 0x100

# Send MIDI bank/program changes.
# @note: This option conflicts with PLUGIN_OPTION_MAP_PROGRAM_CHANGES and cannot be used at the same time.
PLUGIN_OPTION_SEND_PROGRAM_CHANGES = 0x200

# ------------------------------------------------------------------------------------------------------------
# Parameter Hints
# Various parameter hints.


+ 41
- 8
source/carla_widgets.py View File

@@ -554,6 +554,7 @@ class PluginEdit(QDialog):
self.ui.ch_force_stereo.clicked.connect(self.slot_optionChanged)
self.ui.ch_map_program_changes.clicked.connect(self.slot_optionChanged)
self.ui.ch_use_chunks.clicked.connect(self.slot_optionChanged)
self.ui.ch_send_program_changes.clicked.connect(self.slot_optionChanged)
self.ui.ch_send_control_changes.clicked.connect(self.slot_optionChanged)
self.ui.ch_send_channel_pressure.clicked.connect(self.slot_optionChanged)
self.ui.ch_send_note_aftertouch.clicked.connect(self.slot_optionChanged)
@@ -744,14 +745,14 @@ class PluginEdit(QDialog):
self.ui.dial_b_right.setEnabled(pluginHints & PLUGIN_CAN_BALANCE)
self.ui.dial_pan.setEnabled(pluginHints & PLUGIN_CAN_PANNING)

self.ui.ch_use_chunks.setEnabled(self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_USE_CHUNKS)
self.ui.ch_use_chunks.setChecked(self.fPluginInfo['optionsEnabled'] & PLUGIN_OPTION_USE_CHUNKS)
self.ui.ch_fixed_buffer.setEnabled(self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_FIXED_BUFFERS)
self.ui.ch_fixed_buffer.setChecked(self.fPluginInfo['optionsEnabled'] & PLUGIN_OPTION_FIXED_BUFFERS)
self.ui.ch_force_stereo.setEnabled(self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_FORCE_STEREO)
self.ui.ch_force_stereo.setChecked(self.fPluginInfo['optionsEnabled'] & PLUGIN_OPTION_FORCE_STEREO)
self.ui.ch_map_program_changes.setEnabled(self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_MAP_PROGRAM_CHANGES)
self.ui.ch_map_program_changes.setChecked(self.fPluginInfo['optionsEnabled'] & PLUGIN_OPTION_MAP_PROGRAM_CHANGES)
self.ui.ch_use_chunks.setEnabled(self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_USE_CHUNKS)
self.ui.ch_use_chunks.setChecked(self.fPluginInfo['optionsEnabled'] & PLUGIN_OPTION_USE_CHUNKS)
self.ui.ch_send_control_changes.setEnabled(self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_SEND_CONTROL_CHANGES)
self.ui.ch_send_control_changes.setChecked(self.fPluginInfo['optionsEnabled'] & PLUGIN_OPTION_SEND_CONTROL_CHANGES)
self.ui.ch_send_channel_pressure.setEnabled(self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_SEND_CHANNEL_PRESSURE)
@@ -763,6 +764,11 @@ class PluginEdit(QDialog):
self.ui.ch_send_all_sound_off.setEnabled(self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
self.ui.ch_send_all_sound_off.setChecked(self.fPluginInfo['optionsEnabled'] & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)

canSendPrograms = bool((self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_SEND_PROGRAM_CHANGES) != 0 and
(self.fPluginInfo['optionsEnabled'] & PLUGIN_OPTION_MAP_PROGRAM_CHANGES) == 0)
self.ui.ch_send_program_changes.setEnabled(canSendPrograms)
self.ui.ch_send_program_changes.setChecked(self.fPluginInfo['optionsEnabled'] & PLUGIN_OPTION_SEND_PROGRAM_CHANGES)

self.ui.sw_programs.setCurrentIndex(0 if self.fPluginInfo['type'] in (PLUGIN_VST, PLUGIN_GIG, PLUGIN_SFZ) else 1)

# Show/hide keyboard
@@ -1042,14 +1048,16 @@ class PluginEdit(QDialog):
self.ui.cb_midi_programs.blockSignals(False)

def setOption(self, option, yesNo):
if option == PLUGIN_OPTION_FIXED_BUFFERS:
if option == PLUGIN_OPTION_USE_CHUNKS:
widget = self.ui.ch_use_chunks
elif option == PLUGIN_OPTION_FIXED_BUFFERS:
widget = self.ui.ch_fixed_buffer
elif option == PLUGIN_OPTION_FORCE_STEREO:
widget = self.ui.ch_force_stereo
elif option == PLUGIN_OPTION_MAP_PROGRAM_CHANGES:
widget = self.ui.ch_map_program_changes
elif option == PLUGIN_OPTION_USE_CHUNKS:
widget = self.ui.ch_use_chunks
elif option == PLUGIN_OPTION_SEND_PROGRAM_CHANGES:
widget = self.ui.ch_send_program_changes
elif option == PLUGIN_OPTION_SEND_CONTROL_CHANGES:
widget = self.ui.ch_send_control_changes
elif option == PLUGIN_OPTION_SEND_CHANNEL_PRESSURE:
@@ -1219,14 +1227,16 @@ class PluginEdit(QDialog):
def slot_optionChanged(self, clicked):
sender = self.sender()

if sender == self.ui.ch_fixed_buffer:
if sender == self.ui.ch_use_chunks:
option = PLUGIN_OPTION_USE_CHUNKS
elif sender == self.ui.ch_fixed_buffer:
option = PLUGIN_OPTION_FIXED_BUFFERS
elif sender == self.ui.ch_force_stereo:
option = PLUGIN_OPTION_FORCE_STEREO
elif sender == self.ui.ch_map_program_changes:
option = PLUGIN_OPTION_MAP_PROGRAM_CHANGES
elif sender == self.ui.ch_use_chunks:
option = PLUGIN_OPTION_USE_CHUNKS
elif sender == self.ui.ch_send_program_changes:
option = PLUGIN_OPTION_SEND_PROGRAM_CHANGES
elif sender == self.ui.ch_send_control_changes:
option = PLUGIN_OPTION_SEND_CONTROL_CHANGES
elif sender == self.ui.ch_send_channel_pressure:
@@ -1240,8 +1250,31 @@ class PluginEdit(QDialog):
else:
return

#--------------------------------------------------------------
# handle map-program-changes and send-program-changes conflict

if option == PLUGIN_OPTION_MAP_PROGRAM_CHANGES and clicked:
self.ui.ch_send_program_changes.setEnabled(False)

# disable send-program-changes if needed
if self.ui.ch_send_program_changes.isChecked():
self.host.set_option(self.fPluginId, PLUGIN_OPTION_SEND_PROGRAM_CHANGES, False)

#--------------------------------------------------------------
# set option

self.host.set_option(self.fPluginId, option, clicked)

#--------------------------------------------------------------
# handle map-program-changes and send-program-changes conflict

if option == PLUGIN_OPTION_MAP_PROGRAM_CHANGES and not clicked:
self.ui.ch_send_program_changes.setEnabled(self.fPluginInfo['optionsAvailable'] & PLUGIN_OPTION_SEND_PROGRAM_CHANGES)

# restore send-program-changes if needed
if self.ui.ch_send_program_changes.isChecked():
self.host.set_option(self.fPluginId, PLUGIN_OPTION_SEND_PROGRAM_CHANGES, True)

#------------------------------------------------------------------

@pyqtSlot(int)


Loading…
Cancel
Save