diff --git a/device_FIRE-NFX.py b/device_FIRE-NFX.py index c66cf38..31170ac 100644 --- a/device_FIRE-NFX.py +++ b/device_FIRE-NFX.py @@ -43,14 +43,15 @@ # print('task started', id) from harmonicScales import * -from fireNFX_DefaultSettings import * +from fireNFX_DefaultSettings import * # from fireNFX_Classes import * from fireNFX_Defs import * from fireNFX_PadDefs import * from fireNFX_Utils import * from fireNFX_Display import * from fireNFX_PluginDefs import * - +from fireNFX_Helpers import * +from fireNFX_Macros import _MacroList #region globals dimDim = Settings.DIM_DIM @@ -108,7 +109,7 @@ _menuBackText = '' _ProgressPadLenIdx = 1 -_knownPlugins = {} +LOADED_PLUGINS = {} _DirtyChannelFlags = 0 @@ -140,19 +141,8 @@ _DebugPrn = True _DebugMin = lvlD - -# MACROS DEFINED HERE -macCloseAll = TnfxMacro("Close All", getShade(cCyan, shDim) ) -macTogChanRack = TnfxMacro("Chan Rack", cCyan) -macTogPlaylist = TnfxMacro("Playlist", cCyan) -macTogMixer = TnfxMacro("Mixer", cCyan) -#macPluginInfo = TnfxMacro("Show Plugin", getShade(cYellow, shNorm)) -macUndo = TnfxMacro("Undo", getShade(cYellow, shNorm) ) -macCopy = TnfxMacro("Copy", getShade(cBlue, shLight)) -macCut = TnfxMacro("Cut", getShade(cMagenta, shNorm) ) -macPaste = TnfxMacro("Paste", getShade(cGreen, shLight)) -_MacroList = [macCloseAll, macTogChanRack, macTogPlaylist, macTogMixer, macUndo, macCopy, macCut, macPaste] -#colMacros = [ cGreen, cCyan, cBlue, cPurple, cRed, cOrange, cYellow, cWhite ] +from fireNFX_Classes import _rd3d2PotParams, _rd3d2PotParamOffsets +from fireNFX_Macros import * # list of notes that are mapped to pads _NoteMap = list() @@ -193,18 +183,19 @@ def OnInit(): InitDisplay() line = '----------------------' - DisplayText(Font6x8, JustifyCenter, 0, "-={ FIRE-NFX }=-", True) + DisplayText(Font6x8, JustifyCenter, 0, Settings.STARTUP_TEXT_TOP, True) DisplayText(Font6x16, JustifyCenter, 1, '+', True) - DisplayText(Font10x16, JustifyCenter, 2, "Version 2.0", True) + DisplayText(Font10x16, JustifyCenter, 2, Settings.STARTUP_TEXT_BOT, True) #fun "animation" for i in range(16): text = line[0:i] DisplayText(Font6x16, JustifyCenter, 1, text, True) - time.sleep(.05) + time.sleep(.066) # Init some data RefreshAll() _ScrollTo = False + ui.setHintMsg(Settings.STARTUP_FL_HINT) _shuttingDown = False def OnDeInit(): @@ -225,8 +216,6 @@ def OnDeInit(): SendCC(IDKnobModeLEDArray, 16) for ctrlID in getNonPadLightCtrls(): SendCC(ctrlID, 0) - - def ClearAllPads(): # clear the Pads @@ -236,10 +225,24 @@ def ClearAllPads(): def OnDoFullRefresh(): RefreshAll() +_lastHints = [] +MAX_HINTS = 20 +MONITOR_HINTS = False def OnIdle(): + global _lastHints + if(_shuttingDown): return + if(MONITOR_HINTS): # needs a condition + hintMsg = ui.getHintMsg() + if( len(_lastHints) == 0 ): + _lastHints.append('') + if(hintMsg != _lastHints[-1]): + _lastHints.append(hintMsg) + if(len(_lastHints) > MAX_HINTS): + _lastHints.pop(0) + # no event I know of to hook into song length change so I'll check here if(_PadMode.Mode == MODE_PERFORM) and (_isAltMode): CheckAndHandleSongLenChanged() @@ -350,14 +353,14 @@ def OnRefresh(flags): #print('OnRefresh', flags) if(flags == HW_CustomEvent_ShiftAlt): # called by HandleShiftAlt - RefreshShiftAltButtons() - # if(_ShiftHeld): - # RefreshShiftedStates() - if(_DoubleTap) and (_ShiftHeld): - ShowScriptDebug() - + if(_ShiftHeld): + RefreshShiftedStates() + if(_DoubleTap): + macShowScriptWindow.Execute() + #ShowScriptDebug() else: RefreshPadModeButtons() + RefreshShiftAltButtons() RefreshTransport() return # no more processing needed. @@ -438,11 +441,11 @@ def FLHasFocus(): res = _tempMsg.startswith("File -") or _tempMsg.startswith("Menu - File") if (ui.isInPopupMenu()): ui.closeActivePopupMenu() - print('hasfocus:', res) + # print('hasfocus:', res) return res def isKnownPlugin(): - return getCurrChanPluginID() in _knownPlugins.keys() + return getCurrChanPluginID() in LOADED_PLUGINS.keys() _prevCtrlID = 0 _proctime = 0 @@ -457,17 +460,17 @@ def OnMidiIn(event): ctrlID = event.data1 # the low level hardware id of a button, knob, pad, etc + #print('OnMidiIn: event', event.data1, event.data2, event.velocity, event.note, event.isIncrement, event.inEv, event.outEv) + if(event.data2 > 0): prevtime = _proctime _proctime = time.monotonic_ns() // 1000000 elapsed = _proctime-prevtime if (_prevCtrlID == ctrlID): - #print(ctrlID, _prevCtrlID, 'proc', _proctime, prevtime, 'elapsed', elapsed) _DoubleTap = (elapsed < 220) else: _prevCtrlID = ctrlID _DoubleTap = False - #print('dbltap', _DoubleTap) # handle shift/alt if(ctrlID in [IDAlt, IDShift]): @@ -475,24 +478,26 @@ def OnMidiIn(event): event.handled = True return - if(ctrlID in KnobCtrls) and (_KnobMode in [KM_USER1, KM_USER2]): + if(ctrlID in KnobCtrls): # and (_KnobMode in [KM_USER1, KM_USER2]): if (event.status in [MIDI_NOTEON, MIDI_NOTEOFF]): # to prevent the mere touching of the knob generating a midi note event. event.handled = True - - # check if we have predefined user knob settings, if NOT shortcut out + + # check if we have predefined user knob settings, if NOT shortcut out # to be processed by OnMidiMsg() to use processMIDICC per the docs pName, plugin = getCurrChanPlugin() - if(plugin == None): # invalid plugin - return - hasParams = False - if(_KnobMode == KM_USER1): - hasParams = len( [a for a in plugin.User1Knobs if a.Offset > -1]) > 0 - elif(_KnobMode == KM_USER2): - hasParams = len( [a for a in plugin.User2Knobs if a.Offset > -1]) > 0 + if(_KnobMode in [KM_USER1, KM_USER2]): + if(plugin == None): # invalid plugin + return + hasParams = False + if(_KnobMode == KM_USER1): + hasParams = len( [a for a in plugin.User1Knobs if a.Offset > -1]) > 0 + elif(_KnobMode == KM_USER2): + hasParams = len( [a for a in plugin.User2Knobs if a.Offset > -1]) > 0 + if(not hasParams): + return + HandleKnob(event, ctrlID, None, True) - if(not hasParams): - return # handle a pad if( IDPadFirst <= ctrlID <= IDPadLast): @@ -668,6 +673,10 @@ def HandleChannelStrip(padNum): #, isChannelStripB): else: #not SHIFTed if(newChanIdx == prevChanIdx): # if it's already on the channel, toggle the windows + if(_DoubleTap): + ui.showWindow(widPianoRoll) + macZoom.Execute(Settings.DBL_TAP_ZOOM) + else: ShowPianoRoll(-1, True) else: #'new' channel, close the previous windows first SelectAndShowChannel(newChanIdx) @@ -692,18 +701,21 @@ def SelectAndShowChannel(newChanIdx, keepPRopen = True): channels.selectOneChannel(newChanIdx) _CurrentChannel = newChanIdx - if( oldChanIdx != newChanIdx): + if( oldChanIdx != newChanIdx): # if the channel has changed... _ShowChannelEditor = False _ShowCSForm = False - #close previous windows + # ...close previous windows... channels.showEditor(oldChanIdx, 0) channels.showCSForm(oldChanIdx, 0) + if(ui.getVisible(widPianoRoll)): # PR Check + if(ui.getFocused(widPianoRoll)): + ShowPianoRoll(1, True, False, newChanIdx) + else: + ShowPianoRoll(0) UpdateWindowStates() - if(ui.getVisible(widPianoRoll)): #closes previous instance - ShowPianoRoll(1, True, False, newChanIdx) - + # change to and show the rect for the linked mixer track if(not _ShiftHeld) and (not _AltHeld): mixerTrk = channels.getTargetFxTrack(newChanIdx) mixer.setTrackNumber(mixerTrk, curfxScrollToMakeVisible) @@ -807,7 +819,6 @@ def HandlePads(event, padNum): if(padNum in apads): # was in pdPatternStripA if(_AltHeld): patt, pMap = getPatternNumFromPad(padNum) - #print('v', patt, pMap) if(pMap != None): CopyPattern(pMap.FLIndex) else: @@ -917,48 +928,53 @@ def HandleMacros(macIdx): if(_PadMode.NavSet.MacroNav == False): return + macro = _MacroList[macIdx] - if(macIdx == 4): - if(Settings.UNDO_STYLE == 0): - general.undoUp() - else: - general.undo() - elif(macIdx == 1): - ShowChannelRack(-1) - if(Settings.TOGGLE_CR_AND_BROWSER): - if(_ShowChanRack == 0): - ui.showWindow(widBrowser) - RefreshBrowserDisplay() - elif(macIdx == 2): - ShowPlaylist(-1) - elif(macIdx == 3): - ShowMixer(-1) - elif(macIdx == 0): - DisplayTimedText('Reset Windows') - transport.globalTransport(FPT_F12, 1) # close all... - - # enable the following lines to have it re-open windows - if(Settings.REOPEN_WINDOWS_AFTER_CLOSE_ALL): - ShowBrowser(1) - ShowMixer(1) - ShowChannelRack(1) - ShowPlaylist(1) - #else: - # ShowMixer(0) - # ShowChannelRack(0) - # ShowPlaylist(0) - - elif(macIdx == 5): - DisplayTimedText('Copy') - ui.copy() - elif(macIdx == 6): - DisplayTimedText('Cut') - ui.cut() - elif(macIdx == 7): - DisplayTimedText('Paste') - ui.paste() + if(macro == None): + return + + DisplayTimedText(macro.Name) + if(macro.Execute != None): + macro.Execute() else: - return False + # if macro.Name == 'Undo': #if(macIdx == 4): + # if(Settings.UNDO_STYLE == 0): + # general.undoUp() + # else: + # general.undo() + # elif(macro.Name == "Copy"): #macIdx == 5): + # DisplayTimedText('Copy') + # ui.copy() + # elif(macro.Name == "Cut"): # "macIdx == 6): + # DisplayTimedText('Cut') + # ui.cut() + # elif(macro.Name == "Paste"): #"macIdx == 7): + # DisplayTimedText('Paste') + # ui.paste() + if( macro.Name == "Chan Rack"): #macIdx == 1): + ShowChannelRack(-1) + if(Settings.TOGGLE_CR_AND_BROWSER): + if(_ShowChanRack == 0): + ui.showWindow(widBrowser) + RefreshBrowserDisplay() + elif(macro.Name == "Playlist"): # "macIdx == 2): + if(_DoubleTap): + ui.showWindow(widPlaylist) + macZoom.Execute(Settings.DBL_TAP_ZOOM) + else: + ShowPlaylist(-1) + elif(macro.Name == "Mixer"): #"macIdx == 3): + ShowMixer(-1) + elif(macro.Name == "Close All"): #macIdx == 0): + # DisplayTimedText('Reset Windows') + transport.globalTransport(FPT_F12, 1) # close all... + if(Settings.REOPEN_WINDOWS_AFTER_CLOSE_ALL): + ShowBrowser(1) + ShowMixer(1) + ShowChannelRack(1) + ShowPlaylist(1) + else: + return False return True @@ -1157,7 +1173,6 @@ def HandlePattUpDn(ctrlID): # SetTop() else: newPattern = patterns.patternNumber() + moveby - #print('newpat', newPattern, patterns.patternCount()) if( 0 <= newPattern <= patterns.patternCount()): #if it's a valid spot then move it patterns.jumpToPattern(newPattern) else: @@ -1183,22 +1198,22 @@ def HandlePattUpDn(ctrlID): def HandleGridLR(ctrlID): global _ScrollTo - if(_AltHeld): - if(ctrlID == IDBankL): - DisplayTimedText('hZoom Out') - ui.horZoom(2) - SetTop() - else: - DisplayTimedText('hZoom In') - ui.horZoom(-2) - SetTop() - else: - if(ctrlID == IDBankL): - NavSetList(-1) - elif(ctrlID == IDBankR): - NavSetList(1) - _ScrollTo = True - RefreshModes() + # if(_AltHeld): + # if(ctrlID == IDBankL): + # DisplayTimedText('hZoom Out') + # ui.horZoom(2) + # SetTop() + # else: + # DisplayTimedText('hZoom In') + # ui.horZoom(-2) + # SetTop() + # else: + if(ctrlID == IDBankL): + NavSetList(-1) + elif(ctrlID == IDBankR): + NavSetList(1) + _ScrollTo = True + RefreshModes() return True def HandleKnobMode(): @@ -1206,7 +1221,11 @@ def HandleKnobMode(): RefreshDisplay() return True -def HandleKnob(event, ctrlID, useparam = None): +_lastKnobCtrlID = -1 +def HandleKnob(event, ctrlID, useparam = None, displayUpdateOny = False): + global _lastKnobCtrlID + # print('HandleKnob: event', event.data1, event.data2, event.velocity, event.note, event.isIncrement, event.inEv, event.outEv) + # print('ctrl', ctrlID, _lastKnobCtrlID, useparam) if(event.isIncrement != 1): event.inEv = event.data2 if event.inEv >= 0x40: @@ -1214,14 +1233,18 @@ def HandleKnob(event, ctrlID, useparam = None): else: event.outEv = event.inEv event.isIncrement = 1 - value = event.outEv - + if(displayUpdateOny): + value = 0 chanNum = getCurrChanIdx() # channels.channelNumber() recEventID = channels.getRecEventId(chanNum) + plugin = getPlugin(chanNum) + if(plugin.isNative): + recEventID = 0 - if(ctrlID == IDSelect) and (useparam != None): # tweaking via Select Knob + #print('plugin', plugin.Name, plugin.isNative, value) + if(ctrlID == IDSelect) and (useparam != None): # tweaking via Select Knob if(not _FLChannelFX) and (isGenPlug()): # for plugins/generators recEventID += REC_Chan_Plugin_First @@ -1241,10 +1264,11 @@ def HandleKnob(event, ctrlID, useparam = None): knobres = shiftres elif(_AltHeld): knobres = altres - return HandleKnobReal(recEventID + useparam.Offset, event.outEv, useparam.Caption + ': ', useparam.Bipolar, 0, knobres) + return HandleKnobReal(recEventID + useparam.Offset, value, useparam.Caption + ': ', useparam.Bipolar, 0, knobres) if _KnobMode == KM_CHANNEL : + #print('channel', event.data1, event.data2, event.velocity, event.isIncrement, event.inEv, event.outEv) if chanNum > -1: # -1 is none selected # check if a pad is being held for the FPC params pMapPressed = next((x for x in _PadMap if x.Pressed == 1), None) @@ -1285,6 +1309,7 @@ def HandleKnob(event, ctrlID, useparam = None): else: return True elif _KnobMode == KM_MIXER : + #print('mixer') mixerNum = mixer.trackNumber() mixerName = mixer.getTrackName(mixerNum) recEventID = mixer.getTrackPluginId(mixerNum, 0) @@ -1294,10 +1319,18 @@ def HandleKnob(event, ctrlID, useparam = None): elif ctrlID == IDKnob2: return HandleKnobReal(recEventID + REC_Mixer_Pan, value, 'Mx Pan: '+ mixerName, True) elif ctrlID == IDKnob3: - return HandleKnobReal(recEventID + REC_Mixer_EQ_Gain, value, 'Mx EQLo: '+ mixerName, True) + if(_ShiftHeld): + return HandleKnobReal(recEventID + REC_Mixer_EQ_Freq, value, 'Lo Freq: '+ mixerName, True) + else: + return HandleKnobReal(recEventID + REC_Mixer_EQ_Gain, value, 'Lo Gain: '+ mixerName, True) elif ctrlID == IDKnob4: - return HandleKnobReal(recEventID + REC_Mixer_EQ_Gain + 2, value, 'Mix EQHi: '+ mixerName, True) + #return HandleKnobReal(recEventID + REC_Mixer_EQ_Gain + 2, value, 'Mix EQHi: '+ mixerName, True) + if(_ShiftHeld): + return HandleKnobReal(recEventID + REC_Mixer_EQ_Freq + 1, value, ' Mid Freq: '+ mixerName, True) + else: + return HandleKnobReal(recEventID + REC_Mixer_EQ_Gain + 1, value, 'Mid Gain: '+ mixerName, True) elif(isKnownPlugin()): + #print('isKnown') knobParam = None recEventID = channels.getRecEventId(getCurrChanIdx()) + REC_Chan_Plugin_First pluginName, plugin = getCurrChanPlugin() @@ -1305,6 +1338,9 @@ def HandleKnob(event, ctrlID, useparam = None): return True knobOffs = ctrlID - IDKnob1 + value = event.outEv + if(displayUpdateOny): + value = 0 if(_KnobMode == KM_USER1): knobParam = plugin.User1Knobs[knobOffs] knobParam.Caption = plugin.User1Knobs[knobOffs].Caption @@ -1312,7 +1348,7 @@ def HandleKnob(event, ctrlID, useparam = None): knobParam = plugin.User2Knobs[knobOffs] knobParam.Caption = plugin.User2Knobs[knobOffs].Caption if( knobParam.Offset > -1 ): # valid offset? - return HandleKnobReal(recEventID + knobParam.Offset, event.outEv, knobParam.Caption + ': ', knobParam.Bipolar) + return HandleKnobReal(recEventID + knobParam.Offset, value, knobParam.Caption + ': ', knobParam.Bipolar) return True else: #user modes.. if (event.status in [MIDI_NOTEON, MIDI_NOTEOFF]): @@ -1325,7 +1361,8 @@ def HandleKnobReal(recEventIDIndex, value, Name, Bipolar, stepsAfterZero = 0, kn knobres = 1/stepsAfterZero currVal = device.getLinkedValue(recEventIDIndex) #general.processRECEvent(recEventIDIndex, value, REC_MIDIController) #doesnt use knobres - mixer.automateEvent(recEventIDIndex, value, REC_MIDIController, 0, 1, knobres) + if(value != 0): + mixer.automateEvent(recEventIDIndex, value, REC_MIDIController, 0, 1, knobres) currVal = device.getLinkedValue(recEventIDIndex) valstr = device.getLinkedValueString(recEventIDIndex) DisplayBar2(Name, currVal, valstr, Bipolar) @@ -1451,6 +1488,10 @@ def HandlePadMode(event): def HandleTransport(event): global _turnOffMetronomeOnNextPlay + + # if(_ShiftHeld): + # HandleShifted(event) + if(event.data1 == IDPatternSong): if(_ShiftHeld): pass @@ -1478,7 +1519,6 @@ def HandleTransport(event): transport.record() RefreshTransport() - return True @@ -1526,11 +1566,15 @@ def HandleSelectWheel(event, ctrlID): if(ctrlID == IDSelect): caption = '' if(event.data2 == jogNext): - #ui.down() - caption = ui.navigateBrowserMenu(1, _ShiftHeld) + if(FLVersionAtLeast('20.99.0')): + ui.down() + else: + caption = ui.navigateBrowserMenu(1, _ShiftHeld) elif(event.data2 == jogPrev): - #ui.up() - caption = ui.navigateBrowserMenu(0, _ShiftHeld) + if(FLVersionAtLeast('20.99.0')): + ui.up() + else: + caption = ui.navigateBrowserMenu(0, _ShiftHeld) RefreshBrowserDisplay(caption) @@ -1561,37 +1605,30 @@ def HandleSelectWheel(event, ctrlID): numIdx = -1 name = '' window = '' - - if(event.data2 == jogNext): - if(ui.getFocused(widMixer)): - if (not _ShiftHeld): + #print('seleckwheel', _ShiftHeld, _AltHeld) + if(not _ShiftHeld) and (not _AltHeld): + if(event.data2 == jogNext): + if(ui.getFocused(widMixer)): ui.right() else: ui.down() - else: - if (not _ShiftHeld): - ui.down() - else: - ui.right() - - elif(event.data2 == jogPrev): - if(ui.getFocused(widMixer)): - if (not _ShiftHeld): + elif(event.data2 == jogPrev): + if(ui.getFocused(widMixer)): ui.left() else: ui.up() - else: - if(not _ShiftHeld): - ui.up() - else: - ui.left() - - time.sleep(0.02) # if no delay, it reads the previous info + time.sleep(0.02) # if no delay, it reads the previous info if(ui.getFocused(widMixer)): window = 'Mixer' numIdx = mixer.trackNumber() name = mixer.getTrackName(numIdx) + if(_ShiftHeld): + if(event.data2 == jogNext): + ui.right() + if(event.data2 == jogPrev): + ui.left() + elif(ui.getFocused(widChannelRack)): window = 'Channel Rack' numIdx = getCurrChanIdx() @@ -1601,6 +1638,30 @@ def HandleSelectWheel(event, ctrlID): elif(ui.getFocused(widPianoRoll)): window = 'Piano Roll' + if(window in ['Piano Roll', 'Playlist', 'ChannelRack']): + if(_ShiftHeld): + if(event.data2 == jogNext): + ui.right() + if(event.data2 == jogPrev): + ui.left() + + if(window in ['Playlist']): # 'Piano Roll' crashes ATM + if(_AltHeld): + if(_ShiftHeld): + window += 'vZoom' + if(event.data2 == jogNext): + ui.verZoom(2) + if(event.data2 == jogPrev): + ui.verZoom(-2) + else: + window += 'hZoom' + if(event.data2 == jogNext): + ui.horZoom(2) + if(event.data2 == jogPrev): + ui.horZoom(-2) + SetTop() + + if(numIdx > -1): DisplayTimedText2(window, "{}-{}".format(numIdx, name), '') else: @@ -1632,7 +1693,7 @@ def HandleSelectWheel(event, ctrlID): _chosenItem = _menuItemSelected if(len(_menuHistory) == MAXLEVELS) and (plugin.TweakableParam != None): - return HandleKnob(event, IDSelect, plugin.TweakableParam) + return HandleKnob(event, IDSelect, plugin.TweakableParam, True) else: ShowMenuItems() return True @@ -1787,18 +1848,16 @@ def HandleChord(chan, chordNum, noteOn, noteVelocity, play7th, playInverted): def HandleUDLR(padIndex): if(padIndex == pdTab): ui.selectWindow(0) - elif(padIndex == pdShiftTab): - print('focus:', FLHasFocus()) + elif(padIndex == pdMenu): + #print('focus:', FLHasFocus()) NavigateFLMenu('', _AltHeld) elif(padIndex == pdUp): if(ui.isInPopupMenu()) and (ui.getFocused(widBrowser)) and (_ShiftHeld): - print('x') NavigateFLMenu(',UUUUE') else: ui.up() elif(padIndex == pdDown): if(ui.isInPopupMenu()) and (ui.getFocused(widBrowser)) and (_ShiftHeld): - print('y') NavigateFLMenu(',UUUE') else: ui.down() @@ -1850,6 +1909,10 @@ def RefreshModes(): def RefreshPadModeButtons(): + if(_ShiftHeld): + RefreshShiftedStates() + return + SendCC(IDStepSeq, DualColorOff) SendCC(IDNote, DualColorOff) SendCC(IDDrum, DualColorOff) @@ -1867,6 +1930,10 @@ def RefreshPadModeButtons(): SendCC(IDPerform, DualColorFull2) def RefreshShiftAltButtons(): + if(_ShiftHeld): + RefreshShiftedStates() + return + if(_AltHeld): SendCC(IDAlt, SingleColorFull) elif(_isAltMode): @@ -1888,6 +1955,10 @@ def RefreshShiftAltButtons(): RefreshTransport() def RefreshTransport(): + if(_ShiftHeld): + RefreshShiftedStates() + return + if(transport.getLoopMode() == SM_Pat): SendCC(IDPatternSong, IDColPattMode) else: @@ -1911,6 +1982,8 @@ def RefreshShiftedStates(): if(_ShiftHeld): SendCC(IDShift, DualColorFull1) + if(_PadMode.Mode == MODE_PATTERNS): + RefreshChannelStrip() else: SendCC(IDShift, ColOff) @@ -1937,11 +2010,13 @@ def RefreshShiftedStates(): if(ui.isLoopRecEnabled()): SendCC(IDLoop, ColOn) + def RefreshPadsFromPadMap(): for pad in range(0,64): SetPadColor(pad, _PadMap[pad].Color, dimDefault) + def RefreshMacros(): prn(lvlA, 'RefreshMacros') if isNoMacros(): @@ -1962,6 +2037,7 @@ def RefreshMarkers(): def RefreshNavPads(): global _PadMode + global _ChannelMap # mode specific showPresetNav = _PadMode.NavSet.PresetNav showNoteRepeat = _PadMode.NavSet.NoteRepeat @@ -1972,8 +2048,8 @@ def RefreshNavPads(): showOctaveNav = _PadMode.NavSet.OctaveNav showLayoutNav = _PadMode.NavSet.LayoutNav - RefreshGridLR() + currChan = getCurrChanIdx() if(isNoNav()): return @@ -2001,8 +2077,8 @@ def RefreshNavPads(): SetPadColor(pad, color, dimDefault) if(showChanWinNav): - SetPadColor(pdShowChanEditor, _ChannelMap[getCurrChanIdx()].Color, dimBright) - SetPadColor(pdShowChanPianoRoll, cWhite, dimDefault) + SetPadColor(pdShowChanEditor, _ChannelMap[currChan].PadAColor, _ChannelMap[currChan].DimA) + SetPadColor(pdShowChanPianoRoll, _ChannelMap[currChan].PadBColor, _ChannelMap[currChan].DimB) if(showNoteRepeat): if(_NoteRepeat): @@ -2312,7 +2388,7 @@ def RefreshChannelStrip(scrollToChannel = False): global _CurrentChannel global _PatternMap global _ChannelPage - global _PadMap + #global _PadMap #only run when in paatern mode if(_PadMode.Mode != MODE_PATTERNS): @@ -2350,47 +2426,46 @@ def RefreshChannelStrip(scrollToChannel = False): if(chanIdx < len(channelMap)): channel = channelMap[chanIdx] - dimA = dimDim + dimA = dimDefault dimB = dimDim + bColor = cOff - if(currChan == channel.FLIndex): - if(ui.getFocused(widPlugin) or ui.getFocused(widPluginGenerator)): - dimA = dimFull - elif(ui.getVisible(widPlugin) or ui.getVisible(widPluginGenerator)): - dimA = dimBright - if(ui.getFocused(widPianoRoll)): - dimB = dimFull - elif(ui.getVisible(widPianoRoll)): - dimB = dimDim - SetPadColor(padAIdx, channel.Color, dimA) - SetPadColor(padBIdx, cWhite, dimB) - else: - SetPadColor(padAIdx, channel.Color, dimA) - SetPadColor(padBIdx, cDimWhite, dimB) - if(channel.FLIndex >= 0): + bColor = cDimWhite + if(currChan == channel.FLIndex): # the channel is selected + if(ui.getFocused(widPlugin) or ui.getFocused(widPluginGenerator)): + dimA = dimFull + if(ui.getVisible(widPianoRoll)): + bColor = channel.PadAColor + if(ui.getFocused(widPianoRoll)): + bColor = channel.PadAColor + dimB = dimFull + elif(currMixerNum == channels.getTargetFxTrack(channel.FLIndex)): + bColor = cDimWhite + dimB = dimDefault + else: # not selected and not sharing an mixer channel... + bColor = cOff + + SetPadColor(padAIdx, channel.PadAColor, dimA) + SetPadColor(padBIdx, bColor, dimB) + + _ChannelMap[channel.FLIndex].DimA = dimA + _ChannelMap[channel.FLIndex].PadBColor = bColor + _ChannelMap[channel.FLIndex].Dimb = dimB + + if(_PadMode.NavSet.ChanNav): + RefreshNavPads() + if(_ShiftHeld): # Shifted will display Mute states col = cNotMuted if(_KnobMode == KM_MIXER): if (channel.Mixer.FLIndex > -1): if(mixer.isTrackMuted(channel.Mixer.FLIndex)): col = cMuted - else: + elif(_KnobMode == KM_CHANNEL): if(channels.isChannelMuted(channel.FLIndex)): col = cMuted - SetPadColor(padBIdx, col, dimBright) #cWhite, dimBright - elif(currMixerNum == channels.getTargetFxTrack(channel.FLIndex)): - #not Shifted - if(currChan == channel.FLIndex): - SetPadColor(padBIdx, cWhite, dimDefault) - else: - SetPadColor(padBIdx, cDimWhite, dimDefault) - else: #not shifted and not an fx track match - SetPadColor(padBIdx, cOff, dimDefault) - else: - SetPadColor(padBIdx, cOff, dimDefault) - #SelectAndShowChannel(currChan) RefreshNavPads() @@ -2515,7 +2590,7 @@ def RefreshDisplay(): prn(lvlD, ' |-------------------------------------') def RefreshUDLR(): for pad in pdUDLR: - if(pad == pdShiftTab): + if(pad == pdMenu): SetPadColor(pad, cBlue, dimDefault) elif(pad == pdTab): SetPadColor(pad, cCyan, dimBright) @@ -2854,7 +2929,7 @@ def UpdateChannelMap(): for chan in range(_ChannelCount): chanMap = TnfxChannel(chan, channels.getChannelName(chan)) - chanMap.Color = FLColorToPadColor( channels.getChannelColor(chan) ) + chanMap.PadAColor = FLColorToPadColor( channels.getChannelColor(chan) ) chanMap.ChannelType = channels.getChannelType(chan) chanMap.GlobalIndex = channels.getChannelIndex(chan) chanMap.Selected = channels.isChannelSelected(chan) @@ -3041,14 +3116,6 @@ def SetPadMode(): RefreshPadModeButtons() # lights the button RefreshAll() -def getCurrChanIdx(): - return channels.selectedChannel() - globalIdx = channels.channelNumber() - res = -1 - for cMap in _ChannelMap: - if(cMap.GlobalIndex == globalIdx): - res = cMap.FLIndex - return res def getCurrChanPluginID(): name, plugin = getCurrChanPlugin() @@ -3081,7 +3148,6 @@ def SetKnobMode(mode=-1): _KnobMode = mode if(-1 < _KnobMode > 3): #if(_KnobMode > 3): _KnobMode = KM_CHANNEL - #print('km', _KnobMode) RefreshKnobMode() def PatternPageNav(moveby): @@ -3408,6 +3474,7 @@ def ShowChannelRack(showVal, bUpdateDisplay = False): _resetAutoHide = False _prevNavSet = -1 + def ShowBrowser(showVal, bUpdateDisplay = False): global _ShowBrowser global _resetAutoHide @@ -3600,7 +3667,24 @@ def UpdateWindowStates(): SetKnobMode(KM_MIXER) if(ui.getFocused(widChannelRack)): SetKnobMode(KM_CHANNEL) - + + dimA = dimDefault + dimB = dimDefault + bColor = cOff + currChan = getCurrChanIdx() + if(currChan >= 0): + bColor = cDimWhite + if(ui.getFocused(widPlugin) or ui.getFocused(widPluginGenerator)): + dimA = dimFull + if(ui.getVisible(widPianoRoll)): + bColor = _ChannelMap[currChan].PadAColor + dimB = dimDefault + if(ui.getFocused(widPianoRoll)): + bColor = _ChannelMap[currChan].PadAColor + dimB = dimFull + _ChannelMap[currChan].DimA = dimA + _ChannelMap[currChan].PadBColor = bColor + _ChannelMap[currChan].DimB = dimB if isNoMacros(): return @@ -3656,6 +3740,11 @@ def RefreshWindowStates(): else: SetPadColor(pdMacros[1], getShade(_MacroList[1].PadColor, shDark), dimDefault) + if(_ShowPianoRoll): + shd = shDark + if(ui.getFocused(widPianoRoll)): + RefreshChannelStrip() + if(_ShowPlaylist): shd = shDark if(ui.getFocused(widPlaylist)): @@ -3758,16 +3847,7 @@ def InititalizePadModes(): _PadMode = modePattern -def SetChannelFXParam(offset, value): - chanNum = getCurrChanIdx() - recEventID = channels.getRecEventId(chanNum) - return general.processRECEvent(recEventID + offset, value, REC_UpdateValue) -def GetChannelFXParam(offset): - chanNum = getCurrChanIdx() - recEventID = channels.getRecEventId(chanNum) - value = 0 - return general.processRECEvent(recEventID + offset, value, REC_GetValue) def getChannelType(chan = -1): @@ -3793,13 +3873,16 @@ def getPlugin(pluginName): NOTE: passing an empty string will load the current channel's plugin ''' - global _knownPlugins + global LOADED_PLUGINS if(pluginName == "") and (isSampler()): return plSampler + if(pluginName == "GLOBAL CTRL") and (isSampler()): + return plGlobal + if(pluginName == FLEFFECTS): - plFLChanFX.ChannelType = getChannelType() + plFLChanFX.FLChannelType = getChannelType() return plFLChanFX if(not plugins.isValid(channels.selectedChannel())): @@ -3808,15 +3891,25 @@ def getPlugin(pluginName): basePluginName, userPluginName = getCurrChanPluginNames() pl = TnfxChannelPlugin(basePluginName, userPluginName) # in case we need a new instance - if(pl.getID() in _knownPlugins.keys()): - return _knownPlugins[pl.getID()] - - if(basePluginName in CUSTOM_PLUGINS.keys()): - pl = CUSTOM_PLUGINS[basePluginName].copy() #clonePluginParams(CUSTOM_PLUGINS[basePluginName], pl) + if(pl.getID() in LOADED_PLUGINS.keys()): + return LOADED_PLUGINS[pl.getID()] + + UseExternalKnobPresets = False + if(basePluginName in KNOWN_PLUGINS.keys()): + pl = KNOWN_PLUGINS[basePluginName].copy() # clonePluginParams(CUSTOM_PLUGINS[basePluginName], pl) else: pl = getPluginInfo(-1) - _knownPlugins[pl.getID()] = pl + rd3d2Present = ('rd3d2 Ext' in pl.ParameterGroups.keys()) + UseExternalKnobPresets = (len(pl.getCurrentKnobParamOffsets()) == 0) and rd3d2Present + + if(UseExternalKnobPresets): + pl.assignKnobs(_rd3d2PotParamOffsets) + plist = _rd3d2PotParams.get(pl.PluginName) + pl.assignKnobsFromParamList(plist) + print('rd3d2 params loaded for {}'.format(pl.Name)) + + LOADED_PLUGINS[pl.getID()] = pl return pl def getChannelRecEventID(): @@ -3863,7 +3956,7 @@ def OpenMainMenu(menuName = 'Patterns'): for i in range(10): ui.left() - time.sleep(Settings.MENU_DELAY) + time.sleep(Settings.MENU_DELAY * 2) msg = _tempMsg match1 = 'Menu - {}'.format(menuName) match2 = '{} -'.format(menuName) @@ -3875,6 +3968,9 @@ def OpenMainMenu(menuName = 'Patterns'): def ClonePattern(): NavigateFLMainMenu('Patterns', 'Clone') +def ClonePattern2(): + NavMainMenu('Patterns', ['Clone']) + def ViewCurrentProject(): if NavMainMenu('View', ['Remote']): ProcessKeys(',ELLLLR') @@ -3882,14 +3978,10 @@ def ViewCurrentProject(): def ViewCurrentProjec1t(): NavMainMenu('View', ['Plugin database']) - - - def MenuNavTo(menuItemStartsWith, verticalNav = True, hasMenuItems = False): visitedMenuItems = [] matched = False msg = '' - res = False while (not matched): msg = _tempMsg # getting a copy of this value in case it changes matched = msg.startswith(menuItemStartsWith) or " - {}".format(menuItemStartsWith) in msg @@ -3898,25 +3990,27 @@ def MenuNavTo(menuItemStartsWith, verticalNav = True, hasMenuItems = False): ui.down() else: ui.right - #time.sleep(Settings.MENU_DELAY) + time.sleep(Settings.MENU_DELAY) + + matched = _lastHints[-1].startswith(menuItemStartsWith) + + if(hasMenuItems): + ui.right() else: - if(hasMenuItems): - ui.right() - else: - ui.enter() + ui.enter() + if (msg not in visitedMenuItems): visitedMenuItems.append(msg) else: - res = True break - return True + + return matched def NavMainMenu(mainMenu = 'File', subMenuNav = ['New']): if OpenMainMenu(mainMenu): lastItem = subMenuNav[-1] - print(lastItem) for menuItem in subMenuNav: - print('looking for >', menuItem) + print('looking for ', "[{}]".format(menuItem), "lastHint", _lastHints[-1]) if not MenuNavTo(menuItem, True, (menuItem != lastItem)): return False return True @@ -3939,12 +4033,12 @@ def NavigateFLMainMenu(menu1 = 'Patterns', menu2 = 'Clone', menu3 = ''): msg = '' while (not matched): msg = _tempMsg # getting a copy of this value in case it changes - #print('tm looking for ', "[{}]".format(match)) + print('tm looking for ', "[{}]".format(match), "msg", msg) matched = msg.startswith(match) if(not matched): #print('up', msg) ui.down() - time.sleep(Settings.MENU_DELAY) + time.sleep(Settings.MENU_DELAY//2) else: ui.enter() if (msg not in visited): @@ -3955,7 +4049,25 @@ def NavigateFLMainMenu(menu1 = 'Patterns', menu2 = 'Clone', menu3 = ''): return matched, msg, _tempMsg, (msg in visited) +def getEventData(ctrlID): + s = '' + s2 = '' + if (general.getVersion() > 9): + BaseID = EncodeRemoteControlID(device.getPortNumber(), 0, 0) + eventId = device.findEventID(BaseID + ctrlID, 0) + if eventId != 2147483647: + s = device.getLinkedParamName(eventId) + s2 = device.getLinkedValueString(eventId) + DisplayTextAll(s, s2, '') + +# def CloseAllWindows(): +# transport.globalTransport(FPT_F12, 1) # close all... +# if(Settings.REOPEN_WINDOWS_AFTER_CLOSE_ALL): +# ShowBrowser(1) +# ShowMixer(1) +# ShowChannelRack(1) +# ShowPlaylist(1) + - menuMoves = MainMenu.get(firstMenu) - NavigateFLMenu(firstMenu) + \ No newline at end of file diff --git a/fireNFX_Anim.py b/fireNFX_Anim.py new file mode 100644 index 0000000..e69de29 diff --git a/fireNFX_Classes.py b/fireNFX_Classes.py index 5e7b047..9a67bf2 100644 --- a/fireNFX_Classes.py +++ b/fireNFX_Classes.py @@ -32,11 +32,16 @@ def clonePluginParams(srcPlugin, destPlugin): destPlugin.assignParameterToUserKnob(KM_USER2, knob, newParam2 ) return destPlugin - +cpGlobal = 0 +cpChannel = 1 +cpChannelPlugin = 2 +cpMixer = 3 +cpMixerPlugin = 4 class TnfxChannelPlugin: def __init__(self, name, username = ""): self.Name = name self.PluginName = name + self.UserName = username self.ParameterGroups = {} # { groupName: [TnfxParameters] } self.Parameters = [] #self.GroupName = '' @@ -45,7 +50,9 @@ def __init__(self, name, username = ""): self.User2Knobs = [] self.isNative = False self.AlwaysRescan = True - self.ChannelType = -1 + self.FLChannelType = -1 + self.PresetGroups = {} + self.Type = cpChannel for i in range(4): # pre-allocate these to have 4 each p = TnfxParameter(-1,'',i,'',False) # offset = -1 to identify it's unassigned self.User1Knobs.append(p) @@ -80,13 +87,35 @@ def getGroupNames(self): def addParamToGroup(self, groupName, nfxParameter): nfxParameter.GroupName = groupName self.Parameters.append(nfxParameter) # add to root level Param list - if(groupName in self.ParameterGroups.keys()): # add to group self.ParameterGroups[groupName].append(nfxParameter) else: self.ParameterGroups[groupName] = [nfxParameter] + def assignKnobsFromParamGroup(self, groupName): + offslist = [] + for param in self.ParameterGroups[groupName]: + offslist.append(param.Offset) + if(len(offslist) > 0): + self.assignKnobs(offslist) + return True + return False + + def getCurrentKnobParamOffsets(self): + u1 = [] + u2 = [] + res = [] + for i in range(4): # pre-allocate these to have 4 each + if(self.User1Knobs[i].Offset > -1): + u1.append(self.User1Knobs[i].Offset) + if(self.User2Knobs[i].Offset > -1): + u2.append(self.User2Knobs[i].Offset) + res.extend(u1) + res.extend(u2) + return res + def assignParameterToUserKnob(self, knobMode, knobIdx, nfxParameter): + #print('ass', knobMode, knobIdx, nfxParameter.Offset ) if(4 < knobIdx < 0): return if(knobMode == KM_USER1): @@ -94,10 +123,38 @@ def assignParameterToUserKnob(self, knobMode, knobIdx, nfxParameter): elif(knobMode == KM_USER2): self.User2Knobs[knobIdx] = nfxParameter - + def assignOffsetToUserKnob(self, usermode, knob, paramOffs): + self.assignParameterToUserKnob(usermode, knob, self.getParamFromOffset(paramOffs) ) + + def assignKnobsFromParamList(self, paramList): + offsetList = [] + for param in paramList: + offsetList.append(param.Offset) + #print('appended offset', param.Offset) + self.assignKnobs(offsetList) + + def assignKnobs(self, offsetList, PresetGroup = ''): + #print('offsets', offsetList) + res = 0 + for idx, offs in enumerate(offsetList): + if idx > 7: + return idx + km = KM_USER1 + ko = idx + if idx > 3: + km = KM_USER2 + ko = idx - 4 + if(offs < 0) or (self.getParamFromOffset(offs) == None): + self.assignParameterToUserKnob(km, ko, None) + else: + self.assignOffsetToUserKnob(km, ko, offs) + res = idx + 1 + if(PresetGroup != ''): + self.PresetGroups[PresetGroup] = res + return res class TnfxParameter: - def __init__(self, offset, caption, value, valuestr, bipolar, stepsAfterZero = 0): + def __init__(self, offset, caption, value=0, valuestr='', bipolar= False, stepsAfterZero = 0): self.Offset = offset self.Caption = caption self.Value = value @@ -106,15 +163,12 @@ def __init__(self, offset, caption, value, valuestr, bipolar, stepsAfterZero = 0 self.StepsAfterZero = stepsAfterZero self.GroupName = '' def __str__(self): - #0, 'Chord Type', 0, 'Movable', False + # 0, 'Chord Type', 0, 'Movable', False return "{}, '{}', {}, '{}'".format(self.Offset, self.Caption, self.Value, self.ValueStr) def getFullName(self): return self.GroupName + "-" + self.Caption def updateCaption(self, caption): self.Caption = caption - - - class TnfxPadMode: def __init__(self, name, mode, btnId = IDStepSeq, isAlt = False): @@ -127,7 +181,6 @@ def __init__(self, name, mode, btnId = IDStepSeq, isAlt = False): self.AllowedNavSets = [nsDefault] self.AllowedNavSetIdx = 0 self.LayoutIdx = 0 - class TnfxProgressStep: def __init__(self, padIdx, color, songpos, abspos, barnum, selected = False): @@ -163,7 +216,10 @@ def __init__(self, flIdx, name): self.Mixer = TnfxMixer(-1, "") self.LoopSize = 0 self.Muted = 0 - self.Color = 0 + self.PadAColor = 0 + self.DimA = 3 + self.PadBColor = 0 + self.DimB = 3 self.ChannelType = -1 self.GlobalIndex = -1 self.ShowChannelEditor = -1 @@ -289,10 +345,11 @@ def __init__(self, padIndex, flIndex, color, tag): self.NoteInfo = TnfxNoteInfo() class TnfxMacro: - def __init__(self, name, color): + def __init__(self, name, color, command = None): self.Name = name self.PadIndex = -1 self.PadColor = color + self.Execute = command class TnfxColorMap: def __init__(self, padIndex, color, dimFactor): @@ -328,3 +385,30 @@ def addSubItem(self, item): break if(not exists): self.SubItems.append(item) + +_rd3d2PotParams = {} # 'PluginName':[param1, .., paramX] +_rd3d2PotParamOffsets = {} +try: + # from native_pot_parameters import PluginParameter, native_plugin_parameters as npp + # until rd3d2 approves my submitted code I will use the local file. + from rd3d2_pot_params import PluginParameter, native_plugin_parameters as npp + if(len(npp) < 1): # no error, but no list either + print('rd3d2 Pot Parameters found, but the dictionary did not load.') + else: + print('rd3d2 Pot Parameters found. {} plugins available in the dictionary.'.format(len(npp))) + for plugin in npp.keys(): + _rd3d2PotParams[plugin] = [] + # for param in npp[plugin]: + # if param != None: + # bipolar = param.deadzone_centre != None + # name = param.name + # if name == '': + # name = '?' + # nfxParam = TnfxParameter(param.index, name, 0, '', bipolar) + # _rd3d2PotParams[plugin].append(nfxParam) + # _rd3d2PotParamOffsets[plugin].append(param.index) + print('rd3d2 Pot Parameters conversion. {} plugins converted.'.format(len(_rd3d2PotParams))) +except ImportError: + print('rd3d2 Pot Parameters NOT found.')# Failed to import - assume they don't have custom settings + + diff --git a/fireNFX_CustomPlugins.py b/fireNFX_CustomPlugins.py new file mode 100644 index 0000000..becdf07 --- /dev/null +++ b/fireNFX_CustomPlugins.py @@ -0,0 +1,2 @@ +# custom imports go after this line. : +from pluginSoundFontPlayer import pluginSoundFontPlayer diff --git a/fireNFX_DefaultSettings.py b/fireNFX_DefaultSettings.py index 7c739ac..8c1c7aa 100644 --- a/fireNFX_DefaultSettings.py +++ b/fireNFX_DefaultSettings.py @@ -121,6 +121,13 @@ def __init__(self) -> None: # Set this to False to prevent them from auto mapping. self.AUTO_MAP_KNOWN_PARAMS_TO_USER_KNOBS = True + self.STARTUP_TEXT_TOP = "-={ FIRE-NFX-V2 }=-" + self.STARTUP_TEXT_BOT = "Version 2.0" + self.STARTUP_FL_HINT = 'FIRE-NFX-V2 loaded.' + + self.DBL_TAP_DELAY_MS = 220 + self.DBL_TAP_ZOOM = 4 + # DO NOT EDIT BELOW: Settings = TnfxDefaultSettings() diff --git a/fireNFX_Defs.py b/fireNFX_Defs.py index deee6e1..2718c71 100644 --- a/fireNFX_Defs.py +++ b/fireNFX_Defs.py @@ -239,4 +239,3 @@ def getNonPadLightCtrls(): FLEFFECTS = 'CHAN FX' NOSUPPTEXT = "UNSUPPORTED" - diff --git a/fireNFX_Display.py b/fireNFX_Display.py index 2b0c379..c088025 100644 --- a/fireNFX_Display.py +++ b/fireNFX_Display.py @@ -50,7 +50,7 @@ def DisplayText(Font, Justification, PageTop, Text, CheckIfSame, DisplayTime = 0 screen.displayText(Font, Justification, PageTop, Text, CheckIfSame, DisplayTime) screen.update() except Exception as e: - print('Display Text exception: ' + str(e)) + print('Display Text Exception: ' + str(e)) return def DisplayBar3(p1, p2, Text, Value, Bipolar): diff --git a/fireNFX_Helpers.py b/fireNFX_Helpers.py new file mode 100644 index 0000000..c1db941 --- /dev/null +++ b/fireNFX_Helpers.py @@ -0,0 +1,38 @@ +import general +from midi import * +import channels +import mixer +from older.OBS_midi import REC_SetChanged + +def getCurrChanIdx(): # backwards compatibility + return channels.selectedChannel() + +def SetNativeParam(offset, value): + return general.processRECEvent(offset, value, REC_SetAll) # REC_SetAll forces the refresh... + +def GetNativeParam(offset): + return general.processRECEvent(offset, 0, REC_GetValue) + +def SetChannelFXParam(offset, value, chanNum = -1): + if(chanNum == -1): + chanNum = channels.selectedChannel() + recEventID = channels.getRecEventId(chanNum) + return general.processRECEvent(recEventID + offset, value, REC_SetAll) # REC_SetAll forces the refresh... + +def GetChannelFXParam(offset, chanNum = -1): + if(chanNum == -1): + chanNum = channels.selectedChannel() + recEventID = channels.getRecEventId(chanNum) + return general.processRECEvent(recEventID + offset, 0, REC_GetValue) + +def SetMixerParam(offset, value, trkNum = -1): + if(trkNum == -1): + trkNum = mixer.trackNumber() + recEventID = channels.getRecEventId(trkNum) + return general.processRECEvent(recEventID + offset, value, REC_SetAll) # REC_SetAll forces the refresh... + +def GetMixerParam(offset, trkNum = -1): + if(trkNum == -1): + trkNum = getCurrChanIdx() + recEventID = channels.getRecEventId(trkNum) + return general.processRECEvent(recEventID + offset, 0, REC_GetValue) diff --git a/fireNFX_Macros.py b/fireNFX_Macros.py new file mode 100644 index 0000000..f73a100 --- /dev/null +++ b/fireNFX_Macros.py @@ -0,0 +1,67 @@ +from midi import * +import transport +import ui +import general +from fireNFX_Classes import TnfxMacro +from fireNFX_Colors import * +from fireNFX_DefaultSettings import Settings +from fireNFX_Utils import getShade, shDark, shDim, shLight, shNorm, NavigateFLMenu + +# code for macros +def Undo(): + if(Settings.UNDO_STYLE == 0): + general.undoUp() + else: + general.undo() + +def ZoomSelection(zoomVal = Settings.DBL_TAP_ZOOM): + zStr = 'DDDDDDDD'[0:zoomVal] + print("[{}]".format(zStr)) + NavigateFLMenu(',DRDDDDDR,DD,{}E'.format(zStr) ) + +def Articulate(): + NavigateFLMenu(',R,DR,DDDE') + +def QuickQuantize(): + NavigateFLMenu(',R,DR,DDDDE') + +def ShowScriptOutputWindow(): + ui.showWindow(widChannelRack) # make CR the active window so it pulls up the main menu + NavigateFLMenu(',LLLLDDDDDDDDDDE') # series of keys to pass + +def CloseAll(): + transport.globalTransport(FPT_F12, 1) # close all... + if(Settings.REOPEN_WINDOWS_AFTER_CLOSE_ALL): + ui.showWindow(widBrowser) + ui.showWindow(widChannelRack) + ui.showWindow(widPlaylist) + ui.showWindow(widMixer) + +# BUILT-IN MACROS DEFINED HERE +# +macCloseAll = TnfxMacro("Close All", getShade(cCyan, shDim), CloseAll) # special +macTogChanRack = TnfxMacro("Chan Rack", cCyan) +macTogPlaylist = TnfxMacro("Playlist", cCyan) +macTogMixer = TnfxMacro("Mixer", cCyan) +# +macUndo = TnfxMacro("Undo", getShade(cYellow, shNorm), Undo ) +macCopy = TnfxMacro("Copy", getShade(cBlue, shLight), ui.copy) +macCut = TnfxMacro("Cut", getShade(cMagenta, shNorm), ui.cut ) +macPaste = TnfxMacro("Paste", getShade(cGreen, shLight), ui.paste) +# +macShowScriptWindow = TnfxMacro("Script Window", cWhite, ShowScriptOutputWindow) +macZoom = TnfxMacro("Zoom", cWhite, ZoomSelection) +macZoom = TnfxMacro("Zoom", cWhite, ZoomSelection) +macZoom = TnfxMacro("Zoom", cWhite, ZoomSelection) + + +# master macro list +_MacroList = [macCloseAll, macTogChanRack, macTogPlaylist, macTogMixer, + macUndo, macCopy, macCut, macPaste ] + + + + + + + diff --git a/fireNFX_PadDefs.py b/fireNFX_PadDefs.py index d449530..e78993f 100644 --- a/fireNFX_PadDefs.py +++ b/fireNFX_PadDefs.py @@ -119,7 +119,7 @@ def getDrumPads(isAlt, noNav, layoutIdx): pdMacros = [ 12, 13, 14, 15, 28, 29, 30, 31] # thx to "a candle" for the tab idea -pdShiftTab = 44 +pdMenu = 44 pdEsc = 45 pdUp = 46 pdEnter = 47 @@ -127,7 +127,7 @@ def getDrumPads(isAlt, noNav, layoutIdx): pdLeft = 61 pdDown = 62 pdRight = 63 -pdUDLR = [pdShiftTab, pdEsc, pdUp, pdEnter, pdTab, pdLeft, pdDown, pdRight] +pdUDLR = [pdMenu, pdEsc, pdUp, pdEnter, pdTab, pdLeft, pdDown, pdRight] # for modes that need channel specific window control - ie Note mode, FPC, etc pdShowChanEditor = 44 diff --git a/fireNFX_PluginDefs.py b/fireNFX_PluginDefs.py index 356d304..be8fd14 100644 --- a/fireNFX_PluginDefs.py +++ b/fireNFX_PluginDefs.py @@ -1,5 +1,6 @@ -from fireNFX_Classes import TnfxChannelPlugin, TnfxParameter +from fireNFX_Classes import TnfxChannelPlugin, TnfxParameter, cpGlobal from fireNFX_Defs import FLEFFECTS + from pluginFLEX import plFLEX from pluginFLKeys import plFLKeys from pluginSTRUMGS2 import plStrumGS2 @@ -8,8 +9,19 @@ from pluginLoungeLizardEP4 import plLoungeLizardEP4 from midi import * +USER_PLUGINS = {} +KNOWN_PLUGINS = {plFLEX.Name:plFLEX, plFLKeys.Name:plFLKeys, plStrumGS2.Name:plStrumGS2, plSlicex.Name: plSlicex, + plFruityDance.Name:plFruityDance, plLoungeLizardEP4.Name: plLoungeLizardEP4} + +try: + from fireNFX_CustomPlugins import * + KNOWN_PLUGINS.update(USER_PLUGINS) +except: + pass + + + # for the FPC modes -#plFPC = TnfxChannelPlugin('FPC') ppFPC_Volume = TnfxParameter( 0, 'PAD Volume', 0, '', False) ppFPC_Pan = TnfxParameter( 16, 'PAD Pan', 0, '', True) ppFPC_Mute = TnfxParameter( 32, 'PAD Mute', 0, '', True) @@ -103,13 +115,34 @@ # plSampler.addParamToGroup('ENV', TnfxParameter(REC_Chan_Env_Hole+1, '15-Hole', 0, '', False)) # plSampler.addParamToGroup('ENV', TnfxParameter(REC_Chan_Env_PLast, '16-PLast', 0, '', False)) -#plFLChanFX +# global +plGlobal = TnfxChannelPlugin('Global') +plGlobal.isNative = True +plGlobalType = cpGlobal +plGlobal.addParamToGroup('FL Main', TnfxParameter(REC_MainVol, 'Volume', 0, '', False)) +plGlobal.addParamToGroup('FL Main', TnfxParameter(REC_MainShuffle, 'Shuffle', 0, '', False)) +plGlobal.addParamToGroup('FL Main', TnfxParameter(REC_MainPitch, 'Pitch', 0, '', False)) +plGlobal.addParamToGroup('FL Main', TnfxParameter(REC_Tempo, 'Tempo', 0, '', False)) + +# mixer +plMixer = TnfxChannelPlugin('Mixer') +plMixer.isNative = True +plMixer.addParamToGroup('Main', TnfxParameter(REC_Mixer_Vol, 'Volume')) +plMixer.addParamToGroup('Main', TnfxParameter(REC_Mixer_Pan, 'Pan')) +plMixer.addParamToGroup('Main', TnfxParameter(REC_Mixer_SS, 'Stereo Sep')) +plMixer.addParamToGroup('EQ', TnfxParameter(REC_Mixer_EQ_Gain, 'Lo Gain')) +plMixer.addParamToGroup('EQ', TnfxParameter(REC_Mixer_EQ_Freq, 'Lo Freq')) +plMixer.addParamToGroup('EQ', TnfxParameter(REC_Mixer_EQ_Q, 'Lo Q')) +plMixer.addParamToGroup('EQ', TnfxParameter(REC_Mixer_EQ_Gain+1, 'Mid Gain')) +plMixer.addParamToGroup('EQ', TnfxParameter(REC_Mixer_EQ_Freq+1, 'Mid Freq')) +plMixer.addParamToGroup('EQ', TnfxParameter(REC_Mixer_EQ_Q+1, 'Mid Q')) +plMixer.addParamToGroup('EQ', TnfxParameter(REC_Mixer_EQ_Gain+2, 'Hi Gain')) +plMixer.addParamToGroup('EQ', TnfxParameter(REC_Mixer_EQ_Freq+2, 'Hi Freq')) +plMixer.addParamToGroup('EQ', TnfxParameter(REC_Mixer_EQ_Q+2, 'Hi Q')) + -CUSTOM_PLUGINS = {plFLEX.Name:plFLEX, plFLKeys.Name:plFLKeys, plStrumGS2.Name:plStrumGS2, plSlicex.Name: plSlicex, - plFruityDance.Name:plFruityDance, plLoungeLizardEP4.Name: plLoungeLizardEP4} -#from pluginSoundFontPlayer import plSoundFontPlayer diff --git a/fireNFX_Settings.py b/fireNFX_Settings.py index acaa830..61cac80 100644 --- a/fireNFX_Settings.py +++ b/fireNFX_Settings.py @@ -17,6 +17,7 @@ def __init__(self) -> None: # # keep indentation as-is to function properly # + # OK TO EDIT OR ADD AFTER THIS LINE: self.PAD_PRESSED_COLOR = cRed self.SHOW_PLAYBACK_NOTES = True @@ -24,7 +25,7 @@ def __init__(self) -> None: self.MARKER_PREFIX_TEXT = "{}-Marker" # the {} will be replaced with the bar number self.PATTERN_NAME = "{}-Pattern" # the {} will be replaced with the pattern number self.AUTO_SWITCH_KNOBMODE = True - self.AUTO_MAP_KNOWN_PARAMS_TO_USER_KNOBS = True + self.AUTO_MAP_KNOWN_PARAMS_TO_USER_KNOBS = False diff --git a/fireNFX_Utils.py b/fireNFX_Utils.py index cc854c8..f5d0688 100644 --- a/fireNFX_Utils.py +++ b/fireNFX_Utils.py @@ -1,8 +1,9 @@ +import sys import math import transport import time import device -from fireNFX_Classes import TnfxColorMap, TnfxParameter, TnfxChannelPlugin +from fireNFX_Classes import TnfxColorMap, TnfxParameter, TnfxChannelPlugin, _rd3d2PotParams import utils import plugins import mixer @@ -228,8 +229,7 @@ def getPluginParam(chanIdx, paramIdx, prn = False): value = plugins.getParamValue(paramIdx, chanIdx, -1) valuestr = plugins.getParamValueString(paramIdx, chanIdx, -1) bipolar = False - name = plugins.getPluginName(chanIdx, -1, 1) - varName = "pl" + name.replace(' ', '') + name, uname, varName = getPluginNames(chanIdx) if(caption != '') and prn: print(varName + ".addParamToGroup('ALL', TnfxParameter(" + str(paramIdx) +", '" + caption +"', 0, '" + valuestr + "', " + str(bipolar) + ") )") # print('# Param', paramIdx, caption ) @@ -263,69 +263,106 @@ def getBeatLenInMS(div): #print('tempo', tempo, 'div', div, 'beatlen', beatlen, 'output', timeval, 'Barlen', barlen) return int(timeval) -def getPluginInfo(chanIdx, prn = False, inclBlanks = False): +def RemoveBadChars(badChars, textStr): + res = textStr + for badChar in badChars: + res = res.replace(badChar, '') + return res + +def getAlphaNum(textStr): + res = textStr + for idx, char in enumerate(textStr): + if(not char.isalnum()): + res = res.replace(char, '') + return res + + +def getPluginNames(chanIdx = -1): + badChars = ' -!,.[]+=' if chanIdx == -1: chanIdx = channels.selectedChannel() - - name = plugins.getPluginName(chanIdx, -1, 0) + name = plugins.getPluginName(chanIdx, -1, 0) uname = plugins.getPluginName(chanIdx, -1, 1) + vname = getAlphaNum("plugin{}".format(name)) + return name, uname, vname + +def getPluginInfo(chanIdx, prn = False, inclBlanks = False, isChannel = True): + if chanIdx == -1: + chanIdx = channels.selectedChannel() + + + name, uname, vname = getPluginNames(chanIdx) res = TnfxChannelPlugin(name, uname) res.Parameters.clear() pCnt = plugins.getParamCount(chanIdx, -1) - varName = "pl" + name.replace(' ', '').replace('-', '') - fileName = "plugin" + name.replace(' ', '').replace('-', '') + '.py' + knobsSamples = [] + varName = vname + fileName = vname + '.py' if(prn): - print('# Save this file as: ', fileName) - print('# -----------------------------------------------------------------') + print('# -----[ COPY AFTER THIS LINE ]--------------------------------------------------------') + print('# Save this file as: "{}{}"'.format(sys.path[1],fileName)) + print('# ') print('# PluginName: ', res.Name) - print('# ParamCount: ', pCnt) - print('# -----------------------------------------------------------------') + print('# Created by: ', '') + print('# ') print('from fireNFX_Classes import TnfxParameter, TnfxChannelPlugin') - print('from fireNFX_Defs import KM_USER1, KM_USER2') print('from fireNFX_PluginDefs import CUSTOM_PLUGINS') - print(varName + " = TnfxChannelPlugin('" + name.replace(' ', '') + "')") - knobsSamples = [] + print(varName + " = TnfxChannelPlugin('" + name + "')") + print("if({}.Name not in CUSTOM_PLUGINS.keys()):".format(varName)) + print(" CUSTOM_PLUGINS[{}.Name] = {}".format(varName, varName)) + print(' ') + for paramIdx in range(0, pCnt): - #if(plugins.getParamName(paramIdx, chanIdx, -1) != '') or (inclBlanks): param = getPluginParam(chanIdx, paramIdx, prn) if(param.Caption != "") or (inclBlanks): - if(len(knobsSamples) < 8): - knobsSamples.append(param) + if(param.Caption == ""): + param.Caption == "{}.Offset".format(paramIdx) + if('MIDI CC' in param.Caption): param.Caption = param.Caption.replace('MIDI CC', '').replace('#', '').lstrip() res.addParamToGroup("MIDI CCs", param) else: res.addParamToGroup("ALL", param) + + if(res.Name in _rd3d2PotParams.keys()): + res.addParamToGroup("rd32d3 Ext", param) + knobsSamples.append(param) + elif(len(knobsSamples) < 8): + knobsSamples.append(param) + res.Parameters.append(param) + if(prn): - print('# -----------------------------------------------------------------') - print('# Non Blank Params Count: ' + str(len(res.Parameters))) + print('# [PARAMETER OFFSETS] ') + print('# Notice, the code lines above contains the text "TnfxParameter(" followed by a number') + print('# That number represents the parameter offset for the parameter described on that line') + print('# You can use the parameter offset number to program your own USER Knob mappings below') print("# ") sampleCount = len(knobsSamples) if(sampleCount > 0 ): - if(sampleCount < 4): - print("# Sample mapping of first " + str(sampleCount) + " params to USER1 Knobs") - else: - print("# Sample mapping of first " + str(sampleCount) + " params to the USER1 and USER2 Knobs") - print("# ") + paramlist = [] for idx, sample in enumerate(knobsSamples): - km = 'KM_USER1' - offs = idx - if(idx > 3): - km = 'KM_USER2' - offs = idx - 4 - #paramCode = "TnfxParameter({}, False)".format(str(sample)) - paramCode = "{}.getParamFromOffset({})".format(varName, sample.Offset) - print("# {}.assignParameterToUserKnob({}, {}, {} ) # {}".format(varName, km, offs, paramCode, sample.Caption)) - print("# ") - print("if({}.Name not in CUSTOM_PLUGINS.keys()):".format(varName)) - print(" CUSTOM_PLUGINS[{}.Name] = {}".format(varName, varName)) - print("# ") - print("# add the following line (without the #) to the end of fireNFX_PluginDefs.py ") - print("# from {} import {}".format(fileName, varName) ) - - print('# ') - print('# -----------------------------------------------------------------') + if idx < 8: + paramlist.append(sample.Offset) + else: + break + print('# [HOW TO SET CUSTOM KNOB MAPPINGS]') + print('# The assignKnobs() function takes a list of up to 8 parameter offsets.') + print('# The list must be in brackets like this [ 21, 12, 3, 7]. Max 8 offsets in list.') + print('# it assigns them in order from :') + print('# USER1, KNOBS 1-4 as the first 4 params') + print('# USER2, KNOBS 1-4 as the second 4 params') + print('') + print('# [ENABLING THE CUSTOM MAPPING]') + print("# Comment/Uncomment the next line to disable/enable the knob mappings. ") + print("#{}.assignKnobs({}) ".format(varName, str(paramlist))) + print(" ") + print("# [LAST STEP. DO NOT FORGET. NEEDED TO INCLUDE YOUR MAPPINGS] ") + print("# Add the following line (without the #) to the end of fireNFX_PluginDefs.py") + print("#from {} import {}".format(fileName, varName) ) + print(' ') + print('# -----[ COPY UP TO THIS LINE, BUT DO NOT INCLUDE ]---------------') + return "" return res def ShowPluginInfo(chanIdx): @@ -613,40 +650,25 @@ def TestThreadMove(): PRToolsMenu = {'Tools', 'LL'} PRTools = {} +def menuPause(seconds = Settings.MENU_DELAY): + time.sleep(seconds) + def ProcessKeys(cmdStr): + commands = {'U':ui.up, 'D':ui.down, 'L':ui.left, 'R':ui.right, + 'E':ui.enter, 'S': ui.escape, 'N': ui.next, ',':menuPause } for cmd in cmdStr: - #print(cmd) - if(cmd == 'U'): - ui.up() - elif(cmd == 'D'): - ui.down() - elif(cmd == 'L'): - ui.left() - elif(cmd == 'R'): - ui.right() - elif(cmd == 'E'): - ui.enter() - elif(cmd == 'S'): - ui.escape() - elif(cmd == 'N'): - ui.next() - elif(cmd == ','): - time.sleep(Settings.MENU_DELAY) - - + commands.get(cmd.upper(), menuPause)() def NavigateFLMenu(cmdString = '', altmenu = False): # this code was inspired by HDSQ's implementation: # https://github.com/MiguelGuthridge/Universal-Controller-Script/blob/main/src/plugs/windows/piano_roll.py # - if (ui.isInPopupMenu()): ui.closeActivePopupMenu() # open the File menu if(altmenu): transport.globalTransport(91, 1) else: - transport.globalTransport(90, 1) if(ui.getFocused(widPianoRoll) == 1): # auto move to the tools when the PR is active. ProcessKeys('LL') @@ -661,8 +683,6 @@ def ShowProject(): ui.showWindow(widChannelRack) # make CR the active window so it pulls up the main menu NavigateFLMenu(',LLL,LUUUUUELL') # series of keys to pass - - def ViewArrangeIntoWorkSpace(): ui.showWindow(widChannelRack) # make CR the active window so it pulls up the main menu NavigateFLMenu(',LLLLDDDDDDDDDDDDDDDDDDRDE') # series of keys to pass @@ -691,7 +711,8 @@ def showPLRect(startBar, endBar, firstPLTrackIdx, numTracks): startBar = startBar - 1 playlist.liveDisplayZone(startBar, firstPLTrackIdx, endBar, firstPLTrackIdx+numTracks) - +def test(): + return """ Helper code for dealing with version checking. @@ -719,3 +740,4 @@ def FLVersionAtLeast(version: str) -> bool: """ return getVersionTuple(getVersionStr()) >= getVersionTuple(version) + \ No newline at end of file diff --git a/pluginBooBass.py b/pluginBooBass.py new file mode 100644 index 0000000..8d46766 --- /dev/null +++ b/pluginBooBass.py @@ -0,0 +1,35 @@ +# Save this file as: "D:\ImageLineData\FL Studio\Settings\Hardware\nfxTest\pluginBooBass.py" +# +# PluginName: BooBass +# Created by: +# +from fireNFX_Classes import TnfxParameter, TnfxChannelPlugin +from fireNFX_PluginDefs import KNOWN_PLUGINS +pluginBooBass = TnfxChannelPlugin('BooBass') +if(pluginBooBass.Name not in KNOWN_PLUGINS.keys()): + KNOWN_PLUGINS[pluginBooBass.Name] = pluginBooBass + +pluginBooBass.addParamToGroup('ALL', TnfxParameter(0, 'Bass', 0, '50%', False) ) +pluginBooBass.addParamToGroup('ALL', TnfxParameter(1, 'Mid', 0, '50%', False) ) +pluginBooBass.addParamToGroup('ALL', TnfxParameter(2, 'Treble', 0, '50%', False) ) + +# [PARAMETER OFFSETS] +# Notice, the code lines above contains the text "TnfxParameter(" followed by a number +# That number represents the parameter offset for the parameter described on that line +# You can use the parameter offset number to program your own USER Knob mappings below +# +# [HOW TO SET CUSTOM KNOB MAPPINGS] +# The assignKnobs() function takes a list of up to 8 parameter offsets. +# The list must be in brackets like this [ 21, 12, 3, 7]. Max 8 offsets in list. +# it assigns them in order from : +# USER1, KNOBS 1-4 as the first 4 params +# USER2, KNOBS 1-4 as the second 4 params +# +# [ENABLING THE CUSTOM MAPPING] +# Comment/Uncomment the next line to disable/enable the knob mappings. +#pluginBooBass.assignKnobs([0, 1, 2]) +# +# [LAST STEP. DO NOT FORGET. NEEDED TO INCLUDE YOUR MAPPINGS] +# Add the following line (without the #) to the end of fireNFX_PluginDefs.py +#from pluginBooBass.py import pluginBooBass + \ No newline at end of file diff --git a/pluginFLEX.py b/pluginFLEX.py index c5c0e0c..978bc7e 100644 --- a/pluginFLEX.py +++ b/pluginFLEX.py @@ -67,9 +67,12 @@ plFLEX.addParamToGroup('GENERAL', TnfxParameter(36, 'Output volume', 0, '57%', False) ) plFLEX.addParamToGroup('GENERAL', TnfxParameter(39, 'Pitch', 0, '+0 cent', True) ) -# set up the predetermined USER1 and USER2 knob parameters to link to the 8 macros -for knobIdx in range(4): - plFLEX.assignParameterToUserKnob(KM_USER1, knobIdx, TnfxParameter(10 + knobIdx, '?', 0, '', False) ) - plFLEX.assignParameterToUserKnob(KM_USER2, knobIdx, TnfxParameter(14 + knobIdx, '?', 0, '', False) ) +# set up the predetermined USER1 and USER2 knob parameters to link to the 8 macros, last group is the active one at startup +plFLEX.assignKnobs([ 0, 2, 3, 4, 5, 7, 8, 9], 'Envelopes') +plFLEX.assignKnobs([10,11,12,13,14,15,16,17], 'Macros') + +# for knobIdx in range(4): +# plFLEX.assignParameterToUserKnob(KM_USER1, knobIdx, TnfxParameter(10 + knobIdx, '?', 0, '', False) ) +# plFLEX.assignParameterToUserKnob(KM_USER2, knobIdx, TnfxParameter(14 + knobIdx, '?', 0, '', False) ) #----------------------------------------------------------------- diff --git a/pluginFPC.py b/pluginFPC.py new file mode 100644 index 0000000..ba5a2a2 --- /dev/null +++ b/pluginFPC.py @@ -0,0 +1,543 @@ +# Save this file as: pluginFPC.py +# ----------------------------------------------------------------- +# PluginName: FPC +# ParamCount: 768 +# ----------------------------------------------------------------- +from fireNFX_Classes import TnfxParameter, TnfxChannelPlugin +from fireNFX_Defs import KM_USER1, KM_USER2 +from fireNFX_PluginDefs import KNOWN_PLUGINS +plFPC = TnfxChannelPlugin('FPC') +plFPC.addParamToGroup('PAD-1', TnfxParameter( 0, 'volume', 0, '79%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter( 1, 'volume', 0, '85%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter( 2, 'volume', 0, '86%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 3, 'Pad 4 volume', 0, '86%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 4, 'Pad 5 volume', 0, '82%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 5, 'Pad 6 volume', 0, '79%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 6, 'Pad 7 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 7, 'Pad 8 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 8, 'Pad 9 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 9, 'Pad 10 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 10, 'Pad 11 volume', 0, '75%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 11, 'Pad 12 volume', 0, '69%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 12, 'Pad 13 volume', 0, '80%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 13, 'Pad 14 volume', 0, '87%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 14, 'Pad 15 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 15, 'Pad 16 volume', 0, '89%', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter( 16, 'pan', 0, '0%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter( 17, 'pan', 0, '0%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter( 18, 'pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 19, 'Pad 4 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 20, 'Pad 5 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 21, 'Pad 6 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 22, 'Pad 7 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 23, 'Pad 8 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 24, 'Pad 9 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 25, 'Pad 10 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 26, 'Pad 11 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 27, 'Pad 12 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 28, 'Pad 13 pan', 0, '-26%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 29, 'Pad 14 pan', 0, '39%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 30, 'Pad 15 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 31, 'Pad 16 pan', 0, '0%', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter( 32, 'mute', 0, 'off', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter( 33, 'mute', 0, 'off', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter( 34, 'mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 35, 'Pad 4 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 36, 'Pad 5 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 37, 'Pad 6 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 38, 'Pad 7 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 39, 'Pad 8 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 40, 'Pad 9 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 41, 'Pad 10 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 42, 'Pad 11 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 43, 'Pad 12 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 44, 'Pad 13 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 45, 'Pad 14 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 46, 'Pad 15 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 47, 'Pad 16 mute', 0, 'off', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter( 48, 'solo', 0, 'off', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter( 49, 'solo', 0, 'off', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter( 50, 'solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 51, 'Pad 4 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 52, 'Pad 5 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 53, 'Pad 6 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 54, 'Pad 7 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 55, 'Pad 8 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 56, 'Pad 9 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 57, 'Pad 10 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 58, 'Pad 11 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 59, 'Pad 12 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 60, 'Pad 13 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 61, 'Pad 14 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 62, 'Pad 15 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 63, 'Pad 16 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 64, 'Pad 17 volume', 0, '86%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 65, 'Pad 18 volume', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 66, 'Pad 19 volume', 0, '84%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 67, 'Pad 20 volume', 0, '83%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 68, 'Pad 21 volume', 0, '81%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 69, 'Pad 22 volume', 0, '80%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 70, 'Pad 23 volume', 0, '83%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 71, 'Pad 24 volume', 0, '79%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 72, 'Pad 25 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 73, 'Pad 26 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 74, 'Pad 27 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 75, 'Pad 28 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 76, 'Pad 29 volume', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 77, 'Pad 30 volume', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 78, 'Pad 31 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 79, 'Pad 32 volume', 0, '78%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 80, 'Pad 17 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 81, 'Pad 18 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 82, 'Pad 19 pan', 0, '-30%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 83, 'Pad 20 pan', 0, '33%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 84, 'Pad 21 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 85, 'Pad 22 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 86, 'Pad 23 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 87, 'Pad 24 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 88, 'Pad 25 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 89, 'Pad 26 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 90, 'Pad 27 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 91, 'Pad 28 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 92, 'Pad 29 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 93, 'Pad 30 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 94, 'Pad 31 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 95, 'Pad 32 pan', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 96, 'Pad 17 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 97, 'Pad 18 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 98, 'Pad 19 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter( 99, 'Pad 20 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(100, 'Pad 21 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(101, 'Pad 22 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(102, 'Pad 23 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(103, 'Pad 24 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(104, 'Pad 25 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(105, 'Pad 26 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(106, 'Pad 27 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(107, 'Pad 28 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(108, 'Pad 29 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(109, 'Pad 30 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(110, 'Pad 31 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(111, 'Pad 32 mute', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(112, 'Pad 17 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(113, 'Pad 18 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(114, 'Pad 19 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(115, 'Pad 20 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(116, 'Pad 21 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(117, 'Pad 22 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(118, 'Pad 23 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(119, 'Pad 24 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(120, 'Pad 25 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(121, 'Pad 26 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(122, 'Pad 27 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(123, 'Pad 28 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(124, 'Pad 29 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(125, 'Pad 30 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(126, 'Pad 31 solo', 0, 'off', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(127, 'Pad 32 solo', 0, 'off', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter(256, 'tune', 0, '37 cents', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter(257, 'tune', 0, '37 cents', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter(258, 'tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(259, 'Pad 4 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(260, 'Pad 5 tune', 0, '37 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(261, 'Pad 6 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(262, 'Pad 7 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(263, 'Pad 8 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(264, 'Pad 9 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(265, 'Pad 10 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(266, 'Pad 11 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(267, 'Pad 12 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(268, 'Pad 13 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(269, 'Pad 14 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(270, 'Pad 15 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(271, 'Pad 16 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(272, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(273, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(274, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(275, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(276, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(277, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(278, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(279, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(280, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(281, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(282, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(283, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(284, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(285, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(286, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(287, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(288, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(289, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(290, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(291, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(292, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(293, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(294, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(295, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(296, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(297, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(298, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(299, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(300, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(301, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(302, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(303, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(304, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(305, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(306, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(307, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(308, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(309, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(310, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(311, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(312, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(313, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(314, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(315, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(316, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(317, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(318, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(319, 'Pad 16 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(320, 'Pad 17 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(321, 'Pad 18 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(322, 'Pad 19 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(323, 'Pad 20 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(324, 'Pad 21 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(325, 'Pad 22 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(326, 'Pad 23 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(327, 'Pad 24 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(328, 'Pad 25 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(329, 'Pad 26 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(330, 'Pad 27 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(331, 'Pad 28 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(332, 'Pad 29 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(333, 'Pad 30 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(334, 'Pad 31 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(335, 'Pad 32 tune', 0, '0 cents', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(336, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(337, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(338, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(339, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(340, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(341, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(342, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(343, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(344, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(345, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(346, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(347, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(348, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(349, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(350, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(351, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(352, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(353, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(354, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(355, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(356, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(357, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(358, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(359, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(360, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(361, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(362, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(363, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(364, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(365, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(366, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(367, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(368, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(369, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(370, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(371, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(372, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(373, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(374, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(375, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(376, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(377, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(378, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(379, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(380, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(381, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(382, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(383, 'Pad 32 tune', 0, '', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter(512, 'volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter(513, 'volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter(514, 'volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter(515, 'volume release', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter(516, 'pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter(517, 'pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter(518, 'pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('PAD-1', TnfxParameter(519, 'pan release', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter(520, 'volume attack', 0, '9%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter(521, 'volume decay', 0, '59%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter(522, 'volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter(523, 'volume release', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter(524, 'pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter(525, 'pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter(526, 'pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('PAD-2', TnfxParameter(527, 'pan release', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter(528, 'volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter(529, 'volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter(530, 'volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter(531, 'volume release', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter(532, 'pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter(533, 'pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter(534, 'pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('PAD-3', TnfxParameter(535, 'pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(536, 'Pad 4 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(537, 'Pad 4 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(538, 'Pad 4 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(539, 'Pad 4 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(540, 'Pad 4 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(541, 'Pad 4 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(542, 'Pad 4 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(543, 'Pad 4 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(544, 'Pad 5 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(545, 'Pad 5 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(546, 'Pad 5 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(547, 'Pad 5 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(548, 'Pad 5 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(549, 'Pad 5 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(550, 'Pad 5 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(551, 'Pad 5 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(552, 'Pad 6 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(553, 'Pad 6 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(554, 'Pad 6 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(555, 'Pad 6 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(556, 'Pad 6 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(557, 'Pad 6 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(558, 'Pad 6 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(559, 'Pad 6 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(560, 'Pad 7 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(561, 'Pad 7 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(562, 'Pad 7 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(563, 'Pad 7 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(564, 'Pad 7 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(565, 'Pad 7 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(566, 'Pad 7 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(567, 'Pad 7 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(568, 'Pad 8 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(569, 'Pad 8 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(570, 'Pad 8 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(571, 'Pad 8 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(572, 'Pad 8 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(573, 'Pad 8 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(574, 'Pad 8 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(575, 'Pad 8 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(576, 'Pad 9 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(577, 'Pad 9 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(578, 'Pad 9 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(579, 'Pad 9 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(580, 'Pad 9 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(581, 'Pad 9 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(582, 'Pad 9 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(583, 'Pad 9 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(584, 'Pad 10 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(585, 'Pad 10 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(586, 'Pad 10 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(587, 'Pad 10 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(588, 'Pad 10 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(589, 'Pad 10 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(590, 'Pad 10 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(591, 'Pad 10 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(592, 'Pad 11 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(593, 'Pad 11 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(594, 'Pad 11 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(595, 'Pad 11 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(596, 'Pad 11 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(597, 'Pad 11 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(598, 'Pad 11 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(599, 'Pad 11 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(600, 'Pad 12 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(601, 'Pad 12 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(602, 'Pad 12 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(603, 'Pad 12 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(604, 'Pad 12 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(605, 'Pad 12 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(606, 'Pad 12 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(607, 'Pad 12 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(608, 'Pad 13 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(609, 'Pad 13 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(610, 'Pad 13 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(611, 'Pad 13 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(612, 'Pad 13 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(613, 'Pad 13 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(614, 'Pad 13 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(615, 'Pad 13 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(616, 'Pad 14 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(617, 'Pad 14 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(618, 'Pad 14 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(619, 'Pad 14 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(620, 'Pad 14 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(621, 'Pad 14 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(622, 'Pad 14 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(623, 'Pad 14 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(624, 'Pad 15 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(625, 'Pad 15 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(626, 'Pad 15 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(627, 'Pad 15 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(628, 'Pad 15 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(629, 'Pad 15 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(630, 'Pad 15 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(631, 'Pad 15 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(632, 'Pad 16 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(633, 'Pad 16 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(634, 'Pad 16 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(635, 'Pad 16 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(636, 'Pad 16 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(637, 'Pad 16 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(638, 'Pad 16 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(639, 'Pad 16 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(640, 'Pad 17 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(641, 'Pad 17 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(642, 'Pad 17 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(643, 'Pad 17 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(644, 'Pad 17 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(645, 'Pad 17 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(646, 'Pad 17 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(647, 'Pad 17 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(648, 'Pad 18 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(649, 'Pad 18 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(650, 'Pad 18 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(651, 'Pad 18 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(652, 'Pad 18 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(653, 'Pad 18 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(654, 'Pad 18 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(655, 'Pad 18 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(656, 'Pad 19 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(657, 'Pad 19 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(658, 'Pad 19 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(659, 'Pad 19 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(660, 'Pad 19 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(661, 'Pad 19 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(662, 'Pad 19 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(663, 'Pad 19 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(664, 'Pad 20 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(665, 'Pad 20 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(666, 'Pad 20 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(667, 'Pad 20 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(668, 'Pad 20 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(669, 'Pad 20 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(670, 'Pad 20 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(671, 'Pad 20 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(672, 'Pad 21 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(673, 'Pad 21 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(674, 'Pad 21 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(675, 'Pad 21 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(676, 'Pad 21 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(677, 'Pad 21 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(678, 'Pad 21 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(679, 'Pad 21 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(680, 'Pad 22 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(681, 'Pad 22 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(682, 'Pad 22 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(683, 'Pad 22 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(684, 'Pad 22 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(685, 'Pad 22 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(686, 'Pad 22 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(687, 'Pad 22 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(688, 'Pad 23 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(689, 'Pad 23 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(690, 'Pad 23 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(691, 'Pad 23 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(692, 'Pad 23 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(693, 'Pad 23 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(694, 'Pad 23 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(695, 'Pad 23 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(696, 'Pad 24 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(697, 'Pad 24 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(698, 'Pad 24 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(699, 'Pad 24 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(700, 'Pad 24 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(701, 'Pad 24 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(702, 'Pad 24 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(703, 'Pad 24 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(704, 'Pad 25 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(705, 'Pad 25 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(706, 'Pad 25 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(707, 'Pad 25 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(708, 'Pad 25 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(709, 'Pad 25 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(710, 'Pad 25 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(711, 'Pad 25 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(712, 'Pad 26 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(713, 'Pad 26 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(714, 'Pad 26 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(715, 'Pad 26 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(716, 'Pad 26 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(717, 'Pad 26 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(718, 'Pad 26 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(719, 'Pad 26 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(720, 'Pad 27 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(721, 'Pad 27 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(722, 'Pad 27 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(723, 'Pad 27 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(724, 'Pad 27 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(725, 'Pad 27 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(726, 'Pad 27 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(727, 'Pad 27 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(728, 'Pad 28 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(729, 'Pad 28 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(730, 'Pad 28 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(731, 'Pad 28 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(732, 'Pad 28 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(733, 'Pad 28 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(734, 'Pad 28 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(735, 'Pad 28 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(736, 'Pad 29 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(737, 'Pad 29 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(738, 'Pad 29 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(739, 'Pad 29 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(740, 'Pad 29 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(741, 'Pad 29 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(742, 'Pad 29 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(743, 'Pad 29 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(744, 'Pad 30 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(745, 'Pad 30 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(746, 'Pad 30 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(747, 'Pad 30 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(748, 'Pad 30 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(749, 'Pad 30 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(750, 'Pad 30 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(751, 'Pad 30 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(752, 'Pad 31 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(753, 'Pad 31 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(754, 'Pad 31 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(755, 'Pad 31 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(756, 'Pad 31 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(757, 'Pad 31 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(758, 'Pad 31 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(759, 'Pad 31 pan release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(760, 'Pad 32 volume attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(761, 'Pad 32 volume decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(762, 'Pad 32 volume sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(763, 'Pad 32 volume release', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(764, 'Pad 32 pan attack', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(765, 'Pad 32 pan decay', 0, '100%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(766, 'Pad 32 pan sustain', 0, '0%', False) ) +plFPC.addParamToGroup('ALL', TnfxParameter(767, 'Pad 32 pan release', 0, '100%', False) ) +# ----------------------------------------------------------------- +# Non Blank Params Count: 1024 +# +# Sample mapping of first 8 params to the USER1 and USER2 Knobs +# +# plFPC.assignParameterToUserKnob(KM_USER1, 0, plFPC.PAD-1ParamFromOffset(0) ) # volume +# plFPC.assignParameterToUserKnob(KM_USER1, 1, plFPC.PAD-2ParamFromOffset(1) ) # volume +# plFPC.assignParameterToUserKnob(KM_USER1, 2, plFPC.PAD-3ParamFromOffset(2) ) # volume +# plFPC.assignParameterToUserKnob(KM_USER1, 3, plFPC.getParamFromOffset(3) ) # Pad 4 volume +# plFPC.assignParameterToUserKnob(KM_USER2, 0, plFPC.getParamFromOffset(4) ) # Pad 5 volume +# plFPC.assignParameterToUserKnob(KM_USER2, 1, plFPC.getParamFromOffset(5) ) # Pad 6 volume +# plFPC.assignParameterToUserKnob(KM_USER2, 2, plFPC.getParamFromOffset(6) ) # Pad 7 volume +# plFPC.assignParameterToUserKnob(KM_USER2, 3, plFPC.getParamFromOffset(7) ) # Pad 8 volume +# +if(plFPC.Name not in KNOWN_PLUGINS.keys()): + KNOWN_PLUGINS[plFPC.Name] = plFPC +# +# add the following line (without the #) to the end of fireNFX_PluginDefs.py +# from pluginFPC.py import plFPC +# +# ----------------------------------------------------------------- + diff --git a/pluginSTRUMGS2.py b/pluginSTRUMGS2.py index df8caf6..6233e79 100644 --- a/pluginSTRUMGS2.py +++ b/pluginSTRUMGS2.py @@ -451,7 +451,8 @@ plStrumGS2.addParamToGroup('MIDI CCs', TnfxParameter(4239, 'MIDI CCs Channel 16 Aftertouch', 0, '', False) ) # set up the predetermined USER1 and/or USER2 knob parameters to link to the 8 macros -plStrumGS2.assignParameterToUserKnob(KM_USER1, 0, TnfxParameter(0, 'Play Mode', 0, 'Guitar', False, 2) ) +#plStrumGS2.assignParameterToUserKnob(KM_USER1, 0, TnfxParameter(0, 'Play Mode', 0, 'Guitar', False, 2) ) +plStrumGS2.assignParameterToUserKnob(KM_USER1, 0, plStrumGS2.getParamFromOffset(0) ) plStrumGS2.assignParameterToUserKnob(KM_USER1, 1, TnfxParameter(4, 'Auto Strum', 0, 'Off', False, 1) ) plStrumGS2.assignParameterToUserKnob(KM_USER1, 2, TnfxParameter(7, 'Chord Type', 0, 'Movable', False) ) plStrumGS2.assignParameterToUserKnob(KM_USER1, 3, TnfxParameter(8, 'Voicing Pos', 0, 'Fret 1', False) )