diff --git a/src/common/plug_indexes.py b/src/common/plug_indexes.py index bac9b66d..5d34f06e 100644 --- a/src/common/plug_indexes.py +++ b/src/common/plug_indexes.py @@ -11,11 +11,13 @@ """ from abc import abstractmethod from typing import Literal +import channels import plugins from common import consts from common.consts import PARAM_CC_START +from common.types.color import Color class FlIndex: @@ -151,6 +153,52 @@ def slotIndex(self) -> Literal[-1]: """ return -1 + def fpcGetPadSemitone(self, pad_index: int) -> int: + """ + Returns the note number for the drum pad at the given index. + + For use with the FPC plugin. + """ + return plugins.getPadInfo( + self.index, + self.slotIndex, + 1, # Note number + pad_index, + True, + ) + + def fpcGetPadColor(self, pad_index: int) -> Color: + """ + Returns the color of the drum pad at the given index. + + For use with the FPC plugin. + """ + return Color.fromInteger(plugins.getPadInfo( + self.index, + self.slotIndex, + 2, # Color + pad_index, + True, + )) + + def getNoteName(self, note_number: int) -> str: + """ + Returns the name of the note at the given index + """ + return plugins.getName( + self.index, + self.slotIndex, + 2, # Note name + note_number, + True, + ) + + def triggerNote(self, note_number: int, value: float) -> None: + """ + Trigger a note on the given channel + """ + channels.midiNoteOn(self.index, note_number, int(value * 127)) + class EffectIndex(PluginIndex): def __init__(self, index: int, slotIndex: int) -> None: diff --git a/src/plugs/plugin.py b/src/plugs/plugin.py index 787716df..604957ea 100644 --- a/src/plugs/plugin.py +++ b/src/plugs/plugin.py @@ -14,7 +14,7 @@ from typing import final from common import log, verbosity from common.util.abstract_method_error import AbstractMethodError -from common.plug_indexes import UnsafeIndex, WindowIndex +from common.plug_indexes import WindowIndex, FlIndex from control_surfaces import ControlEvent from devices import DeviceShadow from plugs.mapping_strategies import IMappingStrategy @@ -96,7 +96,7 @@ def create(cls, shadow: DeviceShadow) -> 'Plugin': """ raise AbstractMethodError(cls) - def processEvent(self, mapping: ControlEvent, index: UnsafeIndex) -> bool: + def processEvent(self, mapping: ControlEvent, index: FlIndex) -> bool: """ Process the event @@ -116,7 +116,7 @@ def processEvent(self, mapping: ControlEvent, index: UnsafeIndex) -> bool: return self._shadow.processEvent(mapping, index) @final - def doTick(self, index: UnsafeIndex) -> None: + def doTick(self, index: FlIndex) -> None: """ Tick the plugin, to allow parameters to update if required. @@ -131,7 +131,7 @@ def doTick(self, index: UnsafeIndex) -> None: # Then tick the device shadow self._shadow.tick(index) - def tick(self, index: UnsafeIndex) -> None: + def tick(self, index: FlIndex) -> None: """ Tick the plugin, to allow parameters to update if required. diff --git a/src/plugs/standard/fl/fpc.py b/src/plugs/standard/fl/fpc.py index bc1723a1..b00c6a27 100644 --- a/src/plugs/standard/fl/fpc.py +++ b/src/plugs/standard/fl/fpc.py @@ -7,12 +7,10 @@ This code is licensed under the GPL v3 license. Refer to the LICENSE file for more details. """ -import plugins -import channels from typing import Any from common.types import Color from common.extension_manager import ExtensionManager -from common.plug_indexes import GeneratorIndex, UnsafeIndex +from common.plug_indexes import GeneratorIndex, FlIndex from common.util.grid_mapper import GridCell from control_surfaces import Note from control_surfaces import ControlShadowEvent, ControlShadow @@ -38,21 +36,15 @@ def calculate_overall_index(pad_idx: GridCell) -> int: def colorPad( control: ControlShadow, - ch_idx: UnsafeIndex, + ch_idx: FlIndex, pad_idx: GridCell, ) -> Color: """ Determine the color to use for a particular drum pad """ - if not isinstance(ch_idx, tuple): + if not isinstance(ch_idx, GeneratorIndex): return Color() - chan = ch_idx[0] - return Color.fromInteger(plugins.getPadInfo( - chan, - -1, - 2, - calculate_overall_index(pad_idx), - )) + return ch_idx.fpcGetPadColor(calculate_overall_index(pad_idx)) @event_filters.toGeneratorIndex(False) @@ -62,11 +54,9 @@ def triggerPad( pad_idx: GridCell, ) -> bool: overall_index = calculate_overall_index(pad_idx) - note = plugins.getPadInfo(*ch_idx, -1, 1, overall_index) - # Work-around for horrible bug where wrong note numbers are given - if note > 127: - note = note >> 16 - channels.midiNoteOn(*ch_idx, note, int(control.value*127)) + + note = ch_idx.fpcGetPadSemitone(overall_index) + ch_idx.triggerNote(note, control.value) return True @@ -98,21 +88,18 @@ def getPlugIds(cls) -> tuple[str, ...]: @tick_filters.toGeneratorIndex() def tick(self, index: GeneratorIndex): + # Set properties for each keyboard note notes = set() for idx in range(32): # Get the note number - note = plugins.getPadInfo(*index, -1, 1, idx) - # Get the color - color = plugins.getPadInfo(*index, -1, 2, idx) - # get the annotation - annotation = plugins.getName(*index, -1, 2, note) + note = index.fpcGetPadSemitone(idx) # Set values - self._notes[note].color = Color.fromInteger(color) - self._notes[note].annotation = annotation + self._notes[note].color = index.fpcGetPadColor(idx) + self._notes[note].annotation = index.getNoteName(note) notes.add(note) - # Set colors and annotations for the others + # Set colors and annotations for the others to be blank for i in range(128): if i not in notes: self._notes[i].color = Color() @@ -125,12 +112,7 @@ def noteEvent( index: GeneratorIndex, *args: Any ) -> bool: - channels.midiNoteOn( - *index, - control.getControl().coordinate[1], - int(control.value * 127), - control.channel - ) + index.triggerNote(control.getControl().coordinate[1], control.value) return True diff --git a/src/plugs/standard/fl/parametric_eq.py b/src/plugs/standard/fl/parametric_eq.py index f8a0e215..8468770e 100644 --- a/src/plugs/standard/fl/parametric_eq.py +++ b/src/plugs/standard/fl/parametric_eq.py @@ -7,8 +7,8 @@ This code is licensed under the GPL v3 license. Refer to the LICENSE file for more details. """ -import plugins from typing import Any +from common.param import Param from common.types import Color from common.extension_manager import ExtensionManager from common.plug_indexes import GeneratorIndex @@ -18,11 +18,14 @@ from plugs import StandardPlugin from plugs import event_filters, tick_filters +# Number of parameters in the parametric EQ NUM_PARAMS = 7 +# Start indexes for each param set LEVEL = 0 FREQUENCY = 7 BANDWIDTH = 14 +# Colors of the bands in the eq COLORS = [ Color.fromInteger(0xCD5689), # Purple Color.fromInteger(0xA254C1), # Pink @@ -36,7 +39,7 @@ class ParametricEq(StandardPlugin): """ - Used to interact with the Fruit parametric EQ 2 plugin + Used to interact with the Fruity Parametric EQ 2 plugin """ def __init__(self, shadow: DeviceShadow) -> None: @@ -64,27 +67,28 @@ def create(cls, shadow: DeviceShadow) -> 'StandardPlugin': @tick_filters.toEffectIndex() def tick(self, index: GeneratorIndex): - if len(self._levels): - for color, (f, i) in enumerate( + # List of (color_index, (control, param_index)) + # CHECK: Does this work if some properties weren't bound? + properties = list( + enumerate( zip(self._levels, range(LEVEL, LEVEL+NUM_PARAMS)) - ): - f.annotation = plugins.getParamName(i, *index) - f.color = COLORS[color] - f.value = plugins.getParamValue(i, *index) - if len(self._frequencies): - for color, (f, i) in enumerate( + ) + ) + list( + enumerate( zip(self._frequencies, range(FREQUENCY, FREQUENCY+NUM_PARAMS)) - ): - f.annotation = plugins.getParamName(i, *index) - f.color = COLORS[color] - f.value = plugins.getParamValue(i, *index) - if len(self._frequencies): - for color, (f, i) in enumerate( + ) + ) + list( + enumerate( zip(self._bandwidths, range(BANDWIDTH, BANDWIDTH+NUM_PARAMS)) - ): - f.annotation = plugins.getParamName(i, *index) - f.color = COLORS[color] - f.value = plugins.getParamValue(i, *index) + ) + ) + + # Set each property + for color_index, (control, param_index) in properties: + param = Param(param_index)(index) + control.annotation = param.name + control.color = COLORS[color_index] + control.value = param.value @classmethod def getPlugIds(cls) -> tuple[str, ...]: @@ -97,8 +101,8 @@ def levels( index: GeneratorIndex, *args: Any ) -> bool: - plugins.setParamValue(control.value, LEVEL + - control.getShadow().coordinate[1], *index) + param = Param(LEVEL + control.getShadow().coordinate[1]) + param(index).value = control.value return True @event_filters.toEffectIndex() @@ -108,8 +112,8 @@ def frequencies( index: GeneratorIndex, *args: Any ) -> bool: - plugins.setParamValue(control.value, FREQUENCY + - control.getShadow().coordinate[1], *index) + param = Param(FREQUENCY + control.getShadow().coordinate[1]) + param(index).value = control.value return True @event_filters.toEffectIndex() @@ -119,8 +123,8 @@ def bandwidths( index: GeneratorIndex, *args: Any ) -> bool: - plugins.setParamValue(control.value, BANDWIDTH + - control.getShadow().coordinate[1], *index) + param = Param(BANDWIDTH + control.getShadow().coordinate[1]) + param(index).value = control.value return True diff --git a/src/plugs/standard/fl/slicers.py b/src/plugs/standard/fl/slicers.py index 87c9c253..b4367332 100644 --- a/src/plugs/standard/fl/slicers.py +++ b/src/plugs/standard/fl/slicers.py @@ -9,8 +9,6 @@ This code is licensed under the GPL v3 license. Refer to the LICENSE file for more details. """ -import plugins -import channels from common.types import Color from common.extension_manager import ExtensionManager from common.plug_indexes import GeneratorIndex @@ -56,11 +54,11 @@ def trigger( """ Trigger a note at the required index """ + # TODO: why do we need to check <= 0? if 0 <= pad_idx.overall_index < len(self.__indexes): - channels.midiNoteOn( - channels.getChannelIndex(*ch_idx), + ch_idx.triggerNote( self.__indexes[pad_idx.overall_index], - int(control.value * 127), + control.value, ) return True else: @@ -92,7 +90,7 @@ def tick(self, index: GeneratorIndex) -> None: """ self.__indexes = [] for i in range(128): - if plugins.getName(*index, -1, 2, i) != "": + if index.getNoteName(i) != "": self.__indexes.append(i) return super().tick(index) diff --git a/src/plugs/standard/klevgrand/daw_cassette.py b/src/plugs/standard/klevgrand/daw_cassette.py index 82cc980d..af3215cd 100644 --- a/src/plugs/standard/klevgrand/daw_cassette.py +++ b/src/plugs/standard/klevgrand/daw_cassette.py @@ -9,7 +9,7 @@ This code is licensed under the GPL v3 license. Refer to the LICENSE file for more details. """ -import plugins +from common.param import Param from common.types import Color from common.extension_manager import ExtensionManager from common.plug_indexes import EffectIndex @@ -27,7 +27,7 @@ VALS = [0.0, 0.1, 0.2] MAX_VALS = [0.05, 0.15] -PARAMS = [7, 6] +PARAMS = [Param(7), Param(6)] class DawCassette(StandardPlugin): @@ -66,11 +66,12 @@ def triggerDrumPads( pad: GridCell, ) -> bool: try: - param = PARAMS[pad.overall_index // 3] + param = PARAMS[pad.overall_index // 3](plug_index) val = VALS[pad.overall_index % 3] except IndexError: return False - plugins.setParamValue(val, param, *plug_index) + + param.value = val return True def colorDrumPads( @@ -92,7 +93,7 @@ def colorDrumPads( @tEffectIndex() def tick(self, index: EffectIndex) -> None: for active_index, param in enumerate(PARAMS): - val = plugins.getParamValue(param, *index) + val = param(index).value for i, upper in enumerate(MAX_VALS): if val < upper: self.__active_pads[active_index] = i diff --git a/src/plugs/standard/spitfire/spitfire_generic.py b/src/plugs/standard/spitfire/spitfire_generic.py index c58b4266..e442622d 100644 --- a/src/plugs/standard/spitfire/spitfire_generic.py +++ b/src/plugs/standard/spitfire/spitfire_generic.py @@ -10,7 +10,7 @@ from typing import Any import channels -import plugins +from common.param import Param from common.types import Color from common.extension_manager import ExtensionManager from common.plug_indexes import GeneratorIndex @@ -22,6 +22,13 @@ from plugs import event_filters, tick_filters from plugs.mapping_strategies import GridStrategy, IMappingStrategy +# Params + +EXPRESSION = Param(0) +DYNAMICS = Param(1) +FADER_PARAMS = [EXPRESSION, DYNAMICS] + + # Generate list of supported plugins # HELP WANTED: I don't own all of these libraries, so the naming may be @@ -82,7 +89,7 @@ def trigger( pad_idx: GridCell, ) -> bool: channels.midiNoteOn( - ch_idx[0], + ch_idx.index, pad_idx.overall_index, int(control.value * 127) ) @@ -135,8 +142,8 @@ def faders( index: GeneratorIndex, *args: Any ) -> bool: - plugins.setParamValue( - control.value, control.getShadow().coordinate[1], *index) + FADER_PARAMS[control.getShadow().coordinate[1]](index).value \ + = control.value return True