From 58e85085cc6709a945eebc0a57b4eaabcb106966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20Andr=C3=A9?= Date: Fri, 15 Jan 2021 14:33:06 +0100 Subject: [PATCH] chore: add dev script to Live symlinked to source chore: add .env generation refactor: more Py2/3 compatibility refactor: core module refactor: actions dicts --- .gitignore | 3 + .vscode/.settings.json | 13 +- CONTRIBUTING.md | 19 + docs/live_instant_mapping_info.md | 414 +++++++------- src/clyphx/__init__.py | 11 +- src/clyphx/actions/__init__.py | 10 +- src/clyphx/actions/arsenal.py | 19 +- src/clyphx/actions/clip.py | 116 ++-- src/clyphx/actions/clip_env_capture.py | 5 +- src/clyphx/actions/consts.py | 179 +++++++ src/clyphx/actions/control_surface.py | 158 +++--- src/clyphx/actions/device.py | 16 +- src/clyphx/actions/global_.py | 21 +- src/clyphx/actions/mxt_live.py | 7 +- src/clyphx/actions/push.py | 75 +-- src/clyphx/actions/pxt_live.py | 46 +- src/clyphx/actions/snap.py | 57 +- src/clyphx/actions/snap9.py | 95 ++-- src/clyphx/actions/track.py | 17 +- src/clyphx/clyphx.py | 238 ++++----- src/clyphx/consts.py | 505 +++++++----------- src/clyphx/core/__init__.py | 9 + src/clyphx/core/compat.py | 20 + src/clyphx/core/models.py | 47 ++ src/clyphx/{core.py => core/parse.py} | 139 ++--- src/clyphx/core/utils.py | 51 ++ src/clyphx/core/xcomponent.py | 38 ++ src/clyphx/cs_linker.py | 12 +- src/clyphx/extra_prefs.py | 9 +- src/clyphx/instant_doc.py | 154 ++++++ src/clyphx/instant_mapping_make_doc.py | 133 ----- src/clyphx/m4l_browser.py | 59 +- src/clyphx/macrobat/macrobat.py | 17 +- src/clyphx/macrobat/midi_rack.py | 5 +- .../macrobat/parameter_rack_template.py | 53 +- src/clyphx/macrobat/parameter_racks.py | 163 +++--- src/clyphx/macrobat/push_rack.py | 5 +- src/clyphx/macrobat/rnr_rack.py | 7 +- src/clyphx/macrobat/sidechain_rack.py | 5 +- src/clyphx/macrobat/user_config.py | 49 +- src/clyphx/push_apc_combiner.py | 8 +- src/clyphx/push_mocks.py | 3 +- src/clyphx/user_actions.py | 11 +- src/clyphx/utils.py | 37 -- src/clyphx/xtriggers.py | 76 +-- stubs/clyphx/actions/clip.pyi | 12 + tools/vscode.py | 26 +- tools/win.ps1 | Bin 3526 -> 4208 bytes 48 files changed, 1674 insertions(+), 1498 deletions(-) create mode 100644 src/clyphx/actions/consts.py create mode 100644 src/clyphx/core/__init__.py create mode 100644 src/clyphx/core/compat.py create mode 100644 src/clyphx/core/models.py rename src/clyphx/{core.py => core/parse.py} (60%) create mode 100644 src/clyphx/core/utils.py create mode 100644 src/clyphx/core/xcomponent.py create mode 100644 src/clyphx/instant_doc.py delete mode 100644 src/clyphx/instant_mapping_make_doc.py delete mode 100644 src/clyphx/utils.py create mode 100644 stubs/clyphx/actions/clip.pyi diff --git a/.gitignore b/.gitignore index 475b61a..5bf60bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ __pycache__/ +vendor/ +tests/ .bin/ +.env *.msi .vscode/settings.json diff --git a/.vscode/.settings.json b/.vscode/.settings.json index 63cde4c..ae2269f 100644 --- a/.vscode/.settings.json +++ b/.vscode/.settings.json @@ -1,17 +1,19 @@ { "files.associations": { - "UserSettings.txt": "properties", + "*.txt": "properties", "*.pyi": "python" }, "files.encoding": "utf8", "files.eol": "\n", "files.trimFinalNewlines": true, + "files.trimTrailingWhitespace": true, "editor.insertSpaces": true, "editor.unusualLineTerminators": "auto", "python.disableInstallationCheck": true, "search.exclude": { "**/.bin": true, - "**/.git": true + "**/.git": true, + "**/vendor": true }, "[python]": { "editor.rulers": [ @@ -19,8 +21,7 @@ 99 ], "editor.tabSize": 4, - "files.insertFinalNewline": true, - "files.trimTrailingWhitespace": true + "files.insertFinalNewline": true }, "[properties]": { "editor.rulers": [ @@ -31,8 +32,8 @@ "files.encoding": "utf16le", "files.eol": "\r\n" }, - "[html]": { - "editor.tabSize": 2 + "[markdown]": { + "files.trimTrailingWhitespace": false }, "[git-commit]": { "editor.rulers": [ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0bc20b4..fee2c58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,6 @@ # Contributing to ClyphX + ## Installing development environment ### Windows @@ -9,4 +10,22 @@ PS> git clone https://github.com/nuno-andre/clyphx.git PS> cd clyphx PS> . .\tools\win.ps1; install-runtime PS> python3 .\tools\vscode.py +PS> install-dev-script ``` + + +## Vendorized libs + +| package | version | description | +| ------- | ------- | ----------- | +| [`python-future`](https://github.com/PythonCharmers/python-future) | 0.18.2 | Compability layer between Python 2.7 and Python 3 | + + +## VSCode tasks + +Shift+Ctrl+P → _Tasks: Run tasks_ + +| task | description | +| ---- | ----------- | +| `(re)start Live` | Restart Live, or start it if closed. +| `open Log.txt` | Open `Log.txt` with the default application for txt files. diff --git a/docs/live_instant_mapping_info.md b/docs/live_instant_mapping_info.md index 871efcf..929466d 100644 --- a/docs/live_instant_mapping_info.md +++ b/docs/live_instant_mapping_info.md @@ -2,7 +2,7 @@ Live Instant Mapping Info for Ableton Live v9.7.0 ================================================== The following document covers the parameter banks accessible via Ableton Live's _Instant Mapping_ feature for each built in device. This info also applies to controlling device parameters via **ClyphX**'s _Device Actions_. - + > **NOTE:** The order of parameter banks is sometimes changed by Ableton. If you find the information in this document to be incorrect, you can recreate it with **ClyphX** by triggering an action named `MAKE_DEV_DOC`. That will create a new version of this file in your user/home directory. * * * @@ -62,7 +62,7 @@ Device Index * * * -### [](#Vocoder)Amp +### Amp **Best Of Banks** P1: Amp Type @@ -73,7 +73,7 @@ P5: Presence P6: Gain P7: Volume P8: Dry/Wet - + **B1: Global** P1: Amp Type P2: Bass @@ -83,11 +83,11 @@ P5: Presence P6: Gain P7: Volume P8: Dry/Wet - + **B2: Dual Mono** P1: Dual Mono - - + + [Back to Device Index](#device-index) * * * @@ -103,7 +103,7 @@ P5: OSC2 Shape P6: OSC2 Octave P7: OSC2 Detune P8: Volume - + **B1: Oscillators** P1: OSC1 Level P2: OSC1 Octave @@ -113,7 +113,7 @@ P5: OSC2 Level P6: OSC2 Octave P7: OSC2 Semi P8: OSC2 Shape - + **B2: Filters** P1: OSC1 Balance P2: F1 Freq @@ -123,7 +123,7 @@ P5: OSC2 Balance P6: F2 Freq P7: F2 Resonance P8: F2 Type - + **B3: Filter Envelopes** P1: FEG1 Attack P2: FEG1 Decay @@ -133,7 +133,7 @@ P5: FEG2 Attack P6: FEG2 Decay P7: FEG2 Sustain P8: FEG2 Rel - + **B4: Filter Modulation** P1: F1 On/Off P2: F1 Freq < LFO @@ -143,7 +143,7 @@ P5: F2 On/Off P6: F2 Freq < LFO P7: F2 Freq < Env P8: F2 Res < LFO - + **B5: Volume Envelopes** P1: AEG1 Attack P2: AEG1 Decay @@ -153,7 +153,7 @@ P5: AEG2 Attack P6: AEG2 Decay P7: AEG2 Sustain P8: AEG2 Rel - + **B6: Mix** P1: AMP1 Level P2: AMP1 Pan @@ -163,7 +163,7 @@ P5: AMP2 Level P6: AMP2 Pan P7: LFO2 Shape P8: LFO2 Speed - + **B7: Output** P1: Volume P2: Noise On/Off @@ -173,8 +173,8 @@ P5: Unison On/Off P6: Unison Detune P7: Vib On/Off P8: Vib Amount - - + + [Back to Device Index](#device-index) * * * @@ -190,7 +190,7 @@ P5: Gate P6: Tranpose Key P7: Velocity Decay P8: Velocity Target - + **B1: Style** P1: Style P2: Groove @@ -200,7 +200,7 @@ P5: Retrigger Mode P6: Ret. Interval P7: Repeats P8: Gate - + **B2: Pitch/Velocity** P1: Tranpose Mode P2: Tranpose Key @@ -210,8 +210,8 @@ P5: Velocity Decay P6: Velocity Target P7: Velocity On P8: Vel. Retrigger - - + + [Back to Device Index](#device-index) * * * @@ -227,8 +227,8 @@ P5: Macro 5 P6: Macro 6 P7: Macro 7 P8: Macro 8 - - + + [Back to Device Index](#device-index) * * * @@ -244,7 +244,7 @@ P5: LFO Amount P6: LFO Waveform P7: LFO Frequency P8: LFO Phase - + **B1: Filter** P1: Frequency P2: Resonance @@ -254,7 +254,7 @@ P5: Env. Modulation P6: LFO Amount P7: LFO Frequency P8: LFO Phase - + **B2: Filter Extra** P1: Filter Type P2: LFO Quantize On @@ -264,13 +264,13 @@ P5: LFO Spin P6: LFO Sync P7: LFO Sync Rate P8: LFO Offset - + **B3: Side Chain** P6: Ext. In On P7: Ext. In Mix P8: Ext. In Gain - - + + [Back to Device Index](#device-index) * * * @@ -286,8 +286,8 @@ P5: Sync Rate P6: Offset P7: Width (Random) P8: Amount - - + + [Back to Device Index](#device-index) * * * @@ -303,7 +303,7 @@ P5: Pitch P6: Pitch Decay P7: Variation P8: Chance - + **B1: Repeat Rate** P1: Interval P2: Offset @@ -313,7 +313,7 @@ P5: Filter Freq P6: Filter Width P7: Volume P8: Decay - + **B2: Gate/Pitch** P1: Chance P2: Gate @@ -323,8 +323,8 @@ P5: Filter Freq P6: Filter Width P7: Volume P8: Decay - - + + [Back to Device Index](#device-index) * * * @@ -337,8 +337,8 @@ P2: Microphone Position P3: Microphone Type P4: Dual Mono P8: Dry/Wet - - + + [Back to Device Index](#device-index) * * * @@ -354,7 +354,7 @@ P5: Shift5 P6: Velocity5 P7: Shift6 P8: Velocity6 - + **B1: Shift** P1: Shift1 P2: Shift2 @@ -362,7 +362,7 @@ P3: Shift3 P4: Shift4 P5: Shift5 P6: Shift6 - + **B2: Shift %** P1: Velocity1 P2: Velocity2 @@ -370,8 +370,8 @@ P3: Velocity3 P4: Velocity4 P5: Velocity5 P6: Velocity6 - - + + [Back to Device Index](#device-index) * * * @@ -387,8 +387,8 @@ P5: Delay 2 Time P6: Delay 2 Mode P7: Feedback P8: Dry/Wet - - + + [Back to Device Index](#device-index) * * * @@ -404,14 +404,14 @@ P5: Res 1 Inharmonics P6: Res 1 Decay P7: Res 1 Tune P8: Volume - + **B1: Mallet** P1: Mallet On/Off P2: Mallet Volume P3: Mallet Noise Amount P4: Mallet Stiffness P5: Mallet Noise Color - + **B2: Noise** P1: Noise Volume P2: Noise Filter Type @@ -421,7 +421,7 @@ P5: Noise Attack P6: Noise Decay P7: Noise Sustain P8: Noise Release - + **B3: Resonator 1, Set A** P1: Res 1 Decay P2: Res 1 Material @@ -431,7 +431,7 @@ P5: Res 1 Tune P6: Res 1 Fine Tune P7: Res 1 Pitch Env. P8: Res 1 Pitch Env. Time - + **B4: Resonator 1, Set B** P1: Res 1 Listening L P2: Res 1 Listening R @@ -441,7 +441,7 @@ P5: Res 1 Inharmonics P6: Res 1 Radius P7: Res 1 Opening P8: Res 1 Ratio - + **B5: Resonator 2, Set A** P1: Res 2 Decay P2: Res 2 Material @@ -451,7 +451,7 @@ P5: Res 2 Tune P6: Res 2 Fine Tune P7: Res 2 Pitch Env. P8: Res 2 Pitch Env. Time - + **B6: Resonator 2, Set B** P1: Res 2 Listening L P2: Res 2 Listening R @@ -461,8 +461,8 @@ P5: Res 2 Inharmonics P6: Res 2 Radius P7: Res 2 Opening P8: Res 2 Ratio - - + + [Back to Device Index](#device-index) * * * @@ -478,7 +478,7 @@ P5: Model P6: Knee P7: Dry/Wet P8: Output Gain - + **B1: Compression** P1: Threshold P2: Ratio @@ -488,7 +488,7 @@ P5: Auto Release On/Off P6: Env Mode P7: Knee P8: Model - + **B2: Output** P1: Threshold P2: Expansion Ratio @@ -498,7 +498,7 @@ P5: Ext. In Gain P6: Makeup P7: Dry/Wet P8: Output Gain - + **B3: Side Chain** P1: EQ On P2: EQ Mode @@ -508,8 +508,8 @@ P5: EQ Gain P6: Ext. In On P7: Ext. In Mix P8: Ext. In Gain - - + + [Back to Device Index](#device-index) * * * @@ -525,7 +525,7 @@ P5: Decay P6: Ratio P7: Tune P8: Dry Wet - + **B1: Amount** P1: Decay P2: Material @@ -535,7 +535,7 @@ P5: Bleed P6: Resonance Type P7: Gain P8: Dry Wet - + **B2: Body** P1: Listening L P2: Listening R @@ -545,7 +545,7 @@ P5: Inharmonics P6: Radius P7: Opening P8: Ratio - + **B3: Tune** P1: Resonance Type P2: Tune @@ -555,8 +555,8 @@ P5: Spread P6: Resonator Quality P7: Note Off P8: Off Decay - - + + [Back to Device Index](#device-index) * * * @@ -572,8 +572,8 @@ P5: Macro 5 P6: Macro 6 P7: Macro 7 P8: Macro 8 - - + + [Back to Device Index](#device-index) * * * @@ -589,8 +589,8 @@ P5: Attack P6: Release P7: Output P8: Dry/Wet - - + + [Back to Device Index](#device-index) * * * @@ -606,7 +606,7 @@ P5: 3 Frequency A P6: 3 Gain A P7: 4 Frequency A P8: 4 Gain A - + **B1: Band On/Off** P1: 1 Filter On A P2: 2 Filter On A @@ -616,7 +616,7 @@ P5: 5 Filter On A P6: 6 Filter On A P7: 7 Filter On A P8: 8 Filter On A - + **B2: Frequency** P1: 1 Frequency A P2: 2 Frequency A @@ -626,7 +626,7 @@ P5: 5 Frequency A P6: 6 Frequency A P7: 7 Frequency A P8: 8 Frequency A - + **B3: Gain** P1: 1 Gain A P2: 2 Gain A @@ -636,7 +636,7 @@ P5: 5 Gain A P6: 6 Gain A P7: 7 Gain A P8: 8 Gain A - + **B4: Resonance** P1: 1 Resonance A P2: 2 Resonance A @@ -646,7 +646,7 @@ P5: 5 Resonance A P6: 6 Resonance A P7: 7 Resonance A P8: 8 Resonance A - + **B5: Filter Type** P1: 1 Filter Type A P2: 2 Filter Type A @@ -656,12 +656,12 @@ P5: 5 Filter Type A P6: 6 Filter Type A P7: 7 Filter Type A P8: 8 Filter Type A - + **B6: Output** P1: Adaptive Q P7: Scale P8: Output Gain - + **B7: EQs 3-5** P1: 3 Gain A P2: 3 Frequency A @@ -671,8 +671,8 @@ P5: 4 Frequency A P6: 4 Resonance A P7: 5 Gain A P8: 5 Frequency A - - + + [Back to Device Index](#device-index) * * * @@ -688,8 +688,8 @@ P5: LowOn P6: MidOn P7: HighOn P8: FreqHi - - + + [Back to Device Index](#device-index) * * * @@ -705,7 +705,7 @@ P5: F Tone Vol P6: F Release P7: P Symmetry P8: Volume - + **B1: Mallet and Tine** P1: M Stiffness P2: M Force @@ -715,7 +715,7 @@ P5: Noise Amount P6: F Tine Color P7: F Tine Decay P8: F Tine Vol - + **B2: Tone and Damper** P1: F Tone Decay P2: F Tone Vol @@ -723,14 +723,14 @@ P3: F Release P4: Damp Tone P5: Damp Balance P6: Damp Amount - + **B3: Pickup** P1: P Symmetry P2: P Distance P3: P Amp In P4: P Amp Out P5: Pickup Model - + **B4: Modulation** P1: M Stiff < Vel P2: M Stiff < Key @@ -739,7 +739,7 @@ P4: M Force < Key P5: Noise < Key P6: F Tine < Key P7: P Amp < Key - + **B5: Global** P1: Volume P2: Voices @@ -747,8 +747,8 @@ P3: Semitone P4: Detune P5: KB Stretch P6: PB Range - - + + [Back to Device Index](#device-index) * * * @@ -760,8 +760,8 @@ P1: Frequency P2: Width P3: Mode P4: Amount - - + + [Back to Device Index](#device-index) * * * @@ -777,7 +777,7 @@ P5: 1 Volume P6: 3 Volume P7: 2 Volume P8: Dry - + **B1: Input L Filter** P1: 1 Filter Freq P2: 1 Filter Width @@ -787,7 +787,7 @@ P5: 1 Feedback P6: 1 Pan P7: 1 Volume P8: Dry - + **B2: Input L+R Filter** P1: 2 Filter Freq P2: 2 Filter Width @@ -797,7 +797,7 @@ P5: 2 Feedback P6: 2 Pan P7: 2 Volume P8: Dry - + **B3: Input R Filter** P1: 3 Filter Freq P2: 3 Filter Width @@ -807,8 +807,8 @@ P5: 3 Feedback P6: 3 Pan P7: 3 Volume P8: Dry - - + + [Back to Device Index](#device-index) * * * @@ -824,7 +824,7 @@ P5: LFO Amount P6: Env. Modulation P7: Feedback P8: Dry/Wet - + **B1: Frequency Controls** P1: Hi Pass P2: Dry/Wet @@ -833,7 +833,7 @@ P4: Feedback P5: Env. Modulation P6: Env. Attack P7: Env. Release - + **B2: LFO / S&H** P1: LFO Amount P2: Frequency @@ -843,8 +843,8 @@ P5: LFO Offset P6: Sync Rate P7: LFO Width (Random) P8: LFO Waveform - - + + [Back to Device Index](#device-index) * * * @@ -860,8 +860,8 @@ P5: Drive On/Off P6: Drive P7: Wide P8: Dry/Wet - - + + [Back to Device Index](#device-index) * * * @@ -877,7 +877,7 @@ P5: Attack P6: Hold P7: Release P8: Floor - + **B1: Gate** P1: Threshold P2: Return @@ -887,7 +887,7 @@ P5: Attack P6: Hold P7: Release P8: Floor - + **B2: Side Chain** P1: EQ On P2: EQ Mode @@ -897,8 +897,8 @@ P5: EQ Gain P6: Ext. In On P7: Ext. In Mix P8: Ext. In Gain - - + + [Back to Device Index](#device-index) * * * @@ -914,7 +914,7 @@ P5: Peak Clip In P6: Range P7: Makeup P8: Dry/Wet - + **B1: Compression** P1: Threshold P2: Ratio @@ -924,7 +924,7 @@ P5: Peak Clip In P6: Range P7: Dry/Wet P8: Makeup - + **B2: Side Chain** P1: EQ On P2: EQ Mode @@ -934,8 +934,8 @@ P5: EQ Gain P6: Ext. In On P7: Ext. In Mix P8: Ext. In Gain - - + + [Back to Device Index](#device-index) * * * @@ -951,8 +951,8 @@ P5: Random P6: Spray P7: Feedback P8: DryWet - - + + [Back to Device Index](#device-index) * * * @@ -968,7 +968,7 @@ P5: 3 Transpose P6: 4 Transpose P7: 5 Transpose P8: 6 Transpose - + **B1: Pad 1** P1: 1 Start P2: 1 Transpose @@ -978,7 +978,7 @@ P5: 1 Filter Freq P6: 1 Filter Res P7: 1 Pan P8: 1 Volume - + **B2: Pad 2** P1: 2 Start P2: 2 Transpose @@ -988,7 +988,7 @@ P5: 2 Filter Freq P6: 2 Filter Res P7: 2 Pan P8: 2 Volume - + **B3: Pad 3** P1: 3 Start P2: 3 Transpose @@ -998,7 +998,7 @@ P5: 3 Filter Freq P6: 3 Filter Res P7: 3 Pan P8: 3 Volume - + **B4: Pad 4** P1: 4 Start P2: 4 Transpose @@ -1008,7 +1008,7 @@ P5: 4 Filter Freq P6: 4 Filter Res P7: 4 Pan P8: 4 Volume - + **B5: Pad 5** P1: 5 Start P2: 5 Transpose @@ -1018,7 +1018,7 @@ P5: 5 Filter Freq P6: 5 Filter Res P7: 5 Pan P8: 5 Volume - + **B6: Pad 6** P1: 6 Start P2: 6 Transpose @@ -1028,7 +1028,7 @@ P5: 6 Filter Freq P6: 6 Filter Res P7: 6 Pan P8: 6 Volume - + **B7: Pad 7** P1: 7 Start P2: 7 Transpose @@ -1038,7 +1038,7 @@ P5: 7 Filter Freq P6: 7 Filter Res P7: 7 Pan P8: 7 Volume - + **B8: Pad 8** P1: 8 Start P2: 8 Transpose @@ -1048,8 +1048,8 @@ P5: 8 Filter Freq P6: 8 Filter Res P7: 8 Pan P8: 8 Volume - - + + [Back to Device Index](#device-index) * * * @@ -1065,8 +1065,8 @@ P5: Macro 5 P6: Macro 6 P7: Macro 7 P8: Macro 8 - - + + [Back to Device Index](#device-index) * * * @@ -1082,8 +1082,8 @@ P5: Monitor P6: Song Control P7: Tempo Control P8: Feedback - - + + [Back to Device Index](#device-index) * * * @@ -1099,8 +1099,8 @@ P5: Macro 5 P6: Macro 6 P7: Macro 7 P8: Macro 8 - - + + [Back to Device Index](#device-index) * * * @@ -1116,7 +1116,7 @@ P5: Above Threshold (High) P6: Above Ratio (High) P7: Master Output P8: Amount - + **B1: Global** P1: Master Output P2: Amount @@ -1126,7 +1126,7 @@ P5: Peak/RMS Mode P6: Band Activator (High) P7: Band Activator (Mid) P8: Band Activator (Low) - + **B2: Low Band** P1: Input Gain (Low) P2: Below Threshold (Low) @@ -1136,7 +1136,7 @@ P5: Above Ratio (Low) P6: Attack Time (Low) P7: Release Time (Low) P8: Output Gain (Low) - + **B3: Mid Band** P1: Input Gain (Mid) P2: Below Threshold (Mid) @@ -1146,7 +1146,7 @@ P5: Above Ratio (Mid) P6: Attack Time (Mid) P7: Release Time (Mid) P8: Output Gain (Mid) - + **B4: High Band** P1: Input Gain (High) P2: Below Threshold (High) @@ -1156,17 +1156,17 @@ P5: Above Ratio (High) P6: Attack Time (High) P7: Release Time (High) P8: Output Gain (High) - + **B5: Split Frequencies** P1: Low-Mid Crossover P2: Mid-High Crossover - + **B6: Side Chain** P6: Ext. In On P7: Ext. In Mix P8: Ext. In Gain - - + + [Back to Device Index](#device-index) * * * @@ -1181,8 +1181,8 @@ P4: Gate P5: On/Off-Balance P6: Decay Time P7: Decay Key Scale - - + + [Back to Device Index](#device-index) * * * @@ -1198,7 +1198,7 @@ P5: B Coarse P6: B Fine P7: Osc-B Level P8: Volume - + **B1: Oscillator A** P1: Ae Attack P2: Ae Decay @@ -1208,7 +1208,7 @@ P5: A Coarse P6: A Fine P7: Osc-A Lev < Vel P8: Osc-A Level - + **B2: Oscillator B** P1: Be Attack P2: Be Decay @@ -1218,7 +1218,7 @@ P5: B Coarse P6: B Fine P7: Osc-B Lev < Vel P8: Osc-B Level - + **B3: Oscillator C** P1: Ce Attack P2: Ce Decay @@ -1228,7 +1228,7 @@ P5: C Coarse P6: C Fine P7: Osc-C Lev < Vel P8: Osc-C Level - + **B4: Oscillator D** P1: De Attack P2: De Decay @@ -1238,7 +1238,7 @@ P5: D Coarse P6: D Fine P7: Osc-D Lev < Vel P8: Osc-D Level - + **B5: LFO** P1: Le Attack P2: Le Decay @@ -1248,7 +1248,7 @@ P5: LFO Rate P6: LFO Amt P7: LFO Type P8: LFO R < K - + **B6: Filter** P1: Fe Attack P2: Fe Decay @@ -1258,7 +1258,7 @@ P5: Filter Freq P6: Filter Res P7: Fe R < Vel P8: Fe Amount - + **B7: Pitch Modulation** P1: Pe Attack P2: Pe Decay @@ -1268,7 +1268,7 @@ P5: Pe Init P6: Glide Time P7: Pe Amount P8: Spread - + **B8: Routing** P1: Time < Key P2: Panorama @@ -1278,8 +1278,8 @@ P5: Algorithm P6: Time P7: Tone P8: Volume - - + + [Back to Device Index](#device-index) * * * @@ -1293,8 +1293,8 @@ P3: Drive P4: Tone P5: Preserve Dynamics P8: Dry/Wet - - + + [Back to Device Index](#device-index) * * * @@ -1310,7 +1310,7 @@ P5: Color P6: LFO Amount P7: LFO Frequency P8: Dry/Wet - + **B1: Frequency Controls** P1: Poles P2: Color @@ -1320,7 +1320,7 @@ P5: Env. Modulation P6: Env. Attack P7: Env. Release P8: Feedback - + **B2: LFO / S&H** P1: LFO Amount P2: LFO Frequency @@ -1330,8 +1330,8 @@ P5: LFO Offset P6: LFO Sync Rate P7: LFO Spin P8: LFO Waveform - - + + [Back to Device Index](#device-index) * * * @@ -1347,8 +1347,8 @@ P5: Beat Swing P6: Delay Mode P7: Feedback P8: Dry/Wet - - + + [Back to Device Index](#device-index) * * * @@ -1359,8 +1359,8 @@ P8: Dry/Wet P1: Pitch P2: Range P3: Lowest - - + + [Back to Device Index](#device-index) * * * @@ -1372,8 +1372,8 @@ P1: Chance P2: Choices P3: Scale P4: Sign - - + + [Back to Device Index](#device-index) * * * @@ -1386,8 +1386,8 @@ P2: Sample Mode P3: Sample Hard P4: Sample Soft P5: Bit On - - + + [Back to Device Index](#device-index) * * * @@ -1403,7 +1403,7 @@ P5: IV Pitch P6: V Pitch P7: Global Gain P8: Dry/Wet - + **B1: General / Mode I** P1: Frequency P2: Width @@ -1413,7 +1413,7 @@ P5: Decay P6: I Note P7: Color P8: I Gain - + **B2: Modes II-IV** P1: II Gain P2: III Gain @@ -1423,8 +1423,8 @@ P5: II Pitch P6: III Pitch P7: IV Pitch P8: V Pitch - - + + [Back to Device Index](#device-index) * * * @@ -1440,7 +1440,7 @@ P5: ER Level P6: Diffuse Level P7: Stereo Image P8: Dry/Wet - + **B1: Reflections** P1: In Filter Freq P2: In Filter Width @@ -1450,7 +1450,7 @@ P5: ER Spin Rate P6: ER Spin Amount P7: ER Shape P8: DecayTime - + **B2: Diffusion Network** P1: HiShelf Freq P2: LowShelf Freq @@ -1460,7 +1460,7 @@ P5: HiShelf Gain P6: LowShelf Gain P7: Chorus Amount P8: Scale - + **B3: Global** P1: DecayTime P2: Freeze On @@ -1470,8 +1470,8 @@ P5: ER Level P6: Diffuse Level P7: Dry/Wet P8: Quality - - + + [Back to Device Index](#device-index) * * * @@ -1487,7 +1487,7 @@ P5: Ve Attack P6: Ve Release P7: Transpose P8: Volume - + **B1: Volume** P1: Volume P2: Ve Attack @@ -1497,7 +1497,7 @@ P5: Ve Release P6: Vol < Vel P7: Ve R < Vel P8: Time - + **B2: Filter** P1: Filter Type P2: Filter Morph @@ -1507,7 +1507,7 @@ P5: Filt < Vel P6: Filt < Key P7: Fe < Env P8: Shaper Amt - + **B3: Filter Envelope** P1: Fe Attack P2: Fe Decay @@ -1517,7 +1517,7 @@ P5: Fe End P6: Fe Mode P7: Fe Loop P8: Fe Retrig - + **B4: LFO 1** P1: L 1 Wave P2: L 1 Sync @@ -1527,7 +1527,7 @@ P5: Vol < LFO P6: Filt < LFO P7: Pan < LFO P8: Pitch < LFO - + **B5: LFO 2** P1: L 2 Wave P2: L 2 Sync @@ -1537,7 +1537,7 @@ P5: L 2 R < Key P6: L 2 St Mode P7: L 2 Spin P8: L 2 Phase - + **B6: LFO 3** P1: L 3 Wave P2: L 3 Sync @@ -1547,7 +1547,7 @@ P5: L 3 R < Key P6: L 3 St Mode P7: L 3 Spin P8: L 3 Phase - + **B7: Oscillator** P1: O Mode P2: O Volume @@ -1557,7 +1557,7 @@ P5: Oe Attack P6: Oe Decay P7: Oe Sustain P8: Oe Release - + **B8: Pitch** P1: Transpose P2: Spread @@ -1567,8 +1567,8 @@ P5: Pe Peak P6: Pe Decay P7: Pe Sustain P8: Pe Release - - + + [Back to Device Index](#device-index) * * * @@ -1584,7 +1584,7 @@ P5: Width P6: Depth P7: Output P8: Dry/Wet - + **B1: General Controls** P1: Drive P2: Base @@ -1594,7 +1594,7 @@ P5: Depth P6: Output P7: Dry/Wet P8: Type - + **B2: Waveshaper Controls** P1: WS Drive P2: WS Lin @@ -1603,8 +1603,8 @@ P4: WS Damp P5: WS Depth P6: WS Period P7: Dry/Wet - - + + [Back to Device Index](#device-index) * * * @@ -1616,8 +1616,8 @@ P1: Base P2: Transpose P3: Range P4: Lowest - - + + [Back to Device Index](#device-index) * * * @@ -1633,8 +1633,8 @@ P5: R Beat Swing P6: R Time Delay P7: Feedback P8: Dry/Wet - - + + [Back to Device Index](#device-index) * * * @@ -1650,7 +1650,7 @@ P5: Ve Attack P6: Ve Release P7: Transpose P8: Volume - + **B1: Amplitude** P1: Ve Attack P2: Ve Decay @@ -1660,7 +1660,7 @@ P5: S Start P6: S Loop Length P7: S Length P8: S Loop Fade - + **B2: Filter** P1: Fe Attack P2: Fe Decay @@ -1670,7 +1670,7 @@ P5: Filter Freq P6: Filter Res P7: Filt < Vel P8: Fe < Env - + **B3: LFO** P1: L Attack P2: L Rate @@ -1680,7 +1680,7 @@ P5: Vol < LFO P6: Filt < LFO P7: Pitch < LFO P8: Pan < LFO - + **B4: Pitch Modifiers** P1: Pe Attack P2: Pe Decay @@ -1690,8 +1690,8 @@ P5: Glide Time P6: Spread P7: Pan P8: Volume - - + + [Back to Device Index](#device-index) * * * @@ -1707,7 +1707,7 @@ P5: E Pos P6: String Decay P7: Str Damping P8: Volume - + **B1: Excitator and String** P1: Excitator Type P2: String Decay @@ -1717,7 +1717,7 @@ P5: Exc ForceMassProt P6: Exc FricStiff P7: Exc Velocity P8: E Pos - + **B2: Damper** P1: Damper On P2: Damper Mass @@ -1727,7 +1727,7 @@ P5: Damp Pos P6: D Damping P7: D Pos < Vel P8: D Pos Abs - + **B3: Termination and Pickup** P1: Term On/Off P2: Term Mass @@ -1737,7 +1737,7 @@ P5: Pickup On/Off P6: Pickup Pos P7: T Mass < Vel P8: T Mass < Key - + **B4: Body** P1: Body On/Off P2: Body Type @@ -1747,7 +1747,7 @@ P5: Body Low-Cut P6: Body High-Cut P7: Body Mix P8: Volume - + **B5: Vibrato** P1: Vibrato On/Off P2: Vib Delay @@ -1757,7 +1757,7 @@ P5: Vib Amount P6: Vib < ModWh P7: Vib Error P8: Volume - + **B6: Filter** P1: Filter On/Off P2: Filter Type @@ -1767,7 +1767,7 @@ P5: Freq < Env P6: Freq < LFO P7: Reso < Env P8: Reso < LFO - + **B7: Envelope and LFO** P1: FEG On/Off P2: FEG Attack @@ -1777,7 +1777,7 @@ P5: FEG Release P6: LFO On/Off P7: LFO Shape P8: LFO Speed - + **B8: Global** P1: Unison On/Off P2: Uni Detune @@ -1787,8 +1787,8 @@ P5: Voices P6: Octave P7: Semitone P8: Volume - - + + [Back to Device Index](#device-index) * * * @@ -1804,8 +1804,8 @@ P5: Signal Source P6: Panorama P7: Mute P8: Gain - - + + [Back to Device Index](#device-index) * * * @@ -1821,8 +1821,8 @@ P5: Out Hi P6: Out Low P7: Range P8: Lowest - - + + [Back to Device Index](#device-index) * * * @@ -1838,8 +1838,8 @@ P5: Pinch Freq. P6: Pinch Width P7: Pinch Drive P8: Crackle Volume - - + + [Back to Device Index](#device-index) * * * @@ -1855,7 +1855,7 @@ P5: Gate Threshold P6: Filter Bandwidth P7: Envelope Depth P8: Dry/Wet - + **B1: Global** P1: Formant Shift P2: Attack Time @@ -1865,7 +1865,7 @@ P5: Output Level P6: Gate Threshold P7: Envelope Depth P8: Dry/Wet - + **B2: Filters/Voicing** P1: Filter Bandwidth P2: Upper Filter Band @@ -1875,7 +1875,7 @@ P5: Unvoiced Level P6: Unvoiced Sensitivity P7: Unvoiced Speed P8: Enhance - + **B3: Carrier** P1: Noise Rate P2: Noise Crackle @@ -1884,8 +1884,8 @@ P4: Lower Pitch Detection P5: Oscillator Pitch P6: Oscillator Waveform P7: Ext. In Gain - - + + [Back to Device Index](#device-index) * * * \ No newline at end of file diff --git a/src/clyphx/__init__.py b/src/clyphx/__init__.py index 8b73c57..06838ec 100644 --- a/src/clyphx/__init__.py +++ b/src/clyphx/__init__.py @@ -14,7 +14,16 @@ # You should have received a copy of the GNU Lesser General Public License # along with ClyphX. If not, see . -from __future__ import absolute_import, print_function, unicode_literals +# from __future__ import absolute_import, unicode_literals +import sys +import os + +base = os.path.dirname(os.path.realpath(__file__)) +vendor = os.path.join(base, 'vendor', 'future') +sys.path.insert(0, vendor) + +from future import standard_library +standard_library.install_aliases() import Live from .clyphx import ClyphX diff --git a/src/clyphx/actions/__init__.py b/src/clyphx/actions/__init__.py index f68e89f..0758d14 100644 --- a/src/clyphx/actions/__init__.py +++ b/src/clyphx/actions/__init__.py @@ -22,4 +22,12 @@ from .global_ import XGlobalActions from .dr import XDrActions from .snap9 import XSnapActions -from .control_surface import XControlSurfaceActions +from .control_surface import XCsActions +from .consts import ( + GLOBAL_ACTIONS, + TRACK_ACTIONS, + CLIP_ACTIONS, + DEVICE_ACTIONS, + LOOPER_ACTIONS, + DR_ACTIONS, +) diff --git a/src/clyphx/actions/arsenal.py b/src/clyphx/actions/arsenal.py index 66658f2..aba55d0 100644 --- a/src/clyphx/actions/arsenal.py +++ b/src/clyphx/actions/arsenal.py @@ -15,10 +15,11 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super, dict import logging import Live -from _Framework.ControlSurfaceComponent import ControlSurfaceComponent +from ..core import ControlSurfaceComponent try: from _NKFW2.Utils import parse_int @@ -79,23 +80,23 @@ class XArsenalActions(ControlSurfaceComponent): '''Actions related to Arsenal control surface scripts.''' def __init__(self, parent): - super(XArsenalActions, self).__init__() + super().__init__() self._parent = parent - self._scripts = {} + self._scripts = dict() def disconnect(self): - super(XArsenalActions, self).disconnect() + super().disconnect() self._parent = None self._scripts = None def set_script(self, script): ''' Adds the given script to the dict of scripts to work with. ''' - self._scripts[script.script_name.upper()] = { - 'top': script, - 'scl': get_component(script, 'Scale_Settings_Control'), - 'targets': get_component(script, 'Targets_Component'), - } + self._scripts[script.script_name.upper()] = dict( + top = script, + scl = get_component(script, 'Scale_Settings_Control'), + targets = get_component(script, 'Targets_Component'), + ) def dispatch_action(self, track, xclip, ident, script_name, action): '''Dispatches the action to the appropriate handler.''' diff --git a/src/clyphx/actions/clip.py b/src/clyphx/actions/clip.py index 4c44b9a..310800e 100644 --- a/src/clyphx/actions/clip.py +++ b/src/clyphx/actions/clip.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super, dict, range # from builtins import range @@ -23,11 +24,10 @@ from ..core import XComponent from .clip_env_capture import ClyphXClipEnvCapture from ..consts import KEYWORDS -from ..consts import (CLIP_GRID_STATES, R_QNTZ_STATES, WARP_MODES, +from ..consts import (CLIP_GRID_STATES, R_QNTZ_STATES, + WARP_MODES, ENV_TYPES, NOTE_NAMES, OCTAVE_NAMES) -ENV_TYPES = ('IRAMP', 'DRAMP', 'IPYR', 'DPYR', 'SQR', 'SAW') - class XClipActions(XComponent): '''Clip-related actions. @@ -35,7 +35,7 @@ class XClipActions(XComponent): __module__ = __name__ def __init__(self, parent): - super(XClipActions, self).__init__(parent) + super().__init__(parent) self._env_capture = ClyphXClipEnvCapture() def set_clip_name(self, clip, track, xclip, ident, args): @@ -339,7 +339,7 @@ def _get_envelope_parameter(self, track, args): elif 'PAN' in args: param = track.mixer_device.panning elif 'SEND' in args: - param = self._parent._track_actions.get_send_parameter( + param = self._parent.track_actions.get_send_parameter( track, args.replace('SEND', '').strip()) elif 'DEV' in args: arg_array = args.split() @@ -351,10 +351,10 @@ def _get_envelope_parameter(self, track, args): param_array = dev_array[1].strip().split() param = None if len(param_array) > 1: - param = self._parent._device_actions.get_banked_parameter( + param = self._parent.device_actions.get_banked_parameter( dev_array[0], param_array[0], param_array[1]) else: - param = self._parent._device_actions.get_bob_parameter( + param = self._parent.device_actions.get_bob_parameter( dev_array[0], param_array[0]) return param @@ -824,11 +824,11 @@ def get_clip_stats(self, clip): end = clip.loop_end clip.looping = 1 loop_length = clip.loop_end - clip.loop_start - return { - 'clip_length': length, - 'real_end': end, - 'loop_length': loop_length, - } + return dict( + clip_length = length, + real_end = end, + loop_length = loop_length, + ) def get_notes_to_operate_on(self, clip, args = None): '''Get notes within loop braces to operate on.''' @@ -853,11 +853,11 @@ def get_notes_to_operate_on(self, clip, args = None): notes_to_edit.append(n) else: other_notes.append(n) - return { - 'notes_to_edit': notes_to_edit, - 'other_notes': other_notes, - 'args': new_args, - } + return dict( + notes_to_edit = notes_to_edit, + other_notes = other_notes, + args = new_args, + ) def get_pos_range(self, clip, string): '''Get note position or range to operate on.''' @@ -867,84 +867,82 @@ def get_pos_range(self, clip, string): start = float(user_range[0].replace('@', '')) except: start = None - if start is not None and start >= 0.0: - pos_range = (start, start) - if len(user_range) > 1: - try: - pos_range = (start, float(user_range[1])) - except: - pass + else: + if start >= 0.0: + pos_range = (start, start) + if len(user_range) > 1: + try: + pos_range = (start, float(user_range[1])) + except: + pass return pos_range def get_note_range(self, string): '''Get note lane or range to operate on.''' - note_range = (0,128) + note_range = (0, 128) string = string.replace('NOTES', '') if len(string) > 1: - int_range = self.get_note_range_from_string(string) - if int_range: - note_range = int_range - else: - start_note_name = self.get_note_name_from_string(string) - start_note_num = self.string_to_note(start_note_name) - note_range = (start_note_num, start_note_num + 1) - string = string.replace(start_note_name, '').strip() - if len(string) > 1 and string.startswith('-'): - string = string[1:] - end_note_name = self.get_note_name_from_string(string) - end_note_num = self.string_to_note(end_note_name) - if end_note_num > start_note_num: - note_range = (start_note_num, end_note_num + 1) + try: + note_range = self.get_note_range_from_string(string) + except: + try: + start_note_name = self.get_note_name_from_string(string) + start_note_num = self.string_to_note(start_note_name) + note_range = (start_note_num, start_note_num + 1) + string = string.replace(start_note_name, '').strip() + if len(string) > 1 and string.startswith('-'): + string = string[1:] + end_note_name = self.get_note_name_from_string(string) + end_note_num = self.string_to_note(end_note_name) + if end_note_num > start_note_num: + note_range = (start_note_num, end_note_num + 1) + except ValueError: + pass return note_range def get_note_range_from_string(self, string): - '''Attempt to get note range (specified in ints) from string and - return it or None if not specified or invalid. + '''Returns a note range (specified in ints) from string. ''' - result = None int_split = string.split('-') + start = int(int_split[0]) try: - start = int(int_split[0]) + end = int(int_split[1]) + 1 + except IndexError: end = start + 1 - if len(int_split) > 1: - end = int(int_split[1]) + 1 - if 0 <= start and end < 129 and start < end: - result = (start, end) - else: - result = None - except: - result = None - return result + if 0 <= start and end <= 128 and start < end: + return (start, end) + raise ValueError("'{}' is not a valid range note.") def get_note_name_from_string(self, string): '''Get the first note name specified in the given string.''' - result = None if len(string) >= 2: result = string[0:2].strip() if (result.endswith('#') or result.endswith('-')) and len(string) >= 3: result = string[0:3].strip() if result.endswith('-') and len(string) >= 4: result = string[0:4].strip() - return result + return result + raise ValueError("'{}' does not contain a note".format(string)) def string_to_note(self, string): '''Get note value from string.''' - converted_note = None base_note = None - octave = None + for s in string: if s in NOTE_NAMES: base_note = NOTE_NAMES.index(s) if base_note is not None and s == '#': base_note += 1 + if base_note is not None: for o in OCTAVE_NAMES: if o in string: base_note = base_note + (OCTAVE_NAMES.index(o) * 12) break - if 0 <= base_note < 128: - converted_note = base_note - return converted_note + if 0 <= base_note < 128: + return base_note + + raise ValueError("'{}' is not a valid note".format(string)) def write_all_notes(self, clip, edited_notes, other_notes): '''Writes new notes to clip.''' diff --git a/src/clyphx/actions/clip_env_capture.py b/src/clyphx/actions/clip_env_capture.py index 1315b1a..28fcfcf 100644 --- a/src/clyphx/actions/clip_env_capture.py +++ b/src/clyphx/actions/clip_env_capture.py @@ -15,8 +15,9 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super, range -from _Framework.ControlSurfaceComponent import ControlSurfaceComponent +from ..core import ControlSurfaceComponent class ClyphXClipEnvCapture(ControlSurfaceComponent): @@ -24,7 +25,7 @@ class ClyphXClipEnvCapture(ControlSurfaceComponent): def disconnect(self): self._parent = None - super(ClyphXClipEnvCapture, self).disconnect() + super().disconnect() def update(self): pass diff --git a/src/clyphx/actions/consts.py b/src/clyphx/actions/consts.py new file mode 100644 index 0000000..1b36678 --- /dev/null +++ b/src/clyphx/actions/consts.py @@ -0,0 +1,179 @@ +from __future__ import absolute_import +from builtins import dict + +from .global_ import XGlobalActions +from .track import XTrackActions +from .clip import XClipActions +from .device import XDeviceActions +from .dr import XDrActions + +# NOTE: Action names and their corresponding values can't contain a '/' or '-' +# within the first four chars like this 'EX/ONE', but 'EXMP/ONE' is okay. + +GLOBAL_ACTIONS = dict( + ASN = XGlobalActions.do_variable_assignment, + ADDAUDIO = XGlobalActions.create_audio_track, + ADDMIDI = XGlobalActions.create_midi_track, + INSAUDIO = XGlobalActions.insert_and_configure_audio_track, + INSMIDI = XGlobalActions.insert_and_configure_midi_track, + ADDRETURN = XGlobalActions.create_return_track, + ADDSCENE = XGlobalActions.create_scene, + DELSCENE = XGlobalActions.delete_scene, + DUPESCENE = XGlobalActions.duplicate_scene, + LOADDEV = XGlobalActions.load_device, + LOADM4L = XGlobalActions.load_m4l, + SWAP = XGlobalActions.swap_device_preset, + SREC = XGlobalActions.set_session_record, + SRECFIX = XGlobalActions.trigger_session_record, + SATM = XGlobalActions.set_session_automation_record, + B2A = XGlobalActions.set_back_to_arrange, + RPT = XGlobalActions.set_note_repeat, + SWING = XGlobalActions.adjust_swing, + BPM = XGlobalActions.adjust_tempo, + DEVFIRST = XGlobalActions.move_to_first_device, + DEVLAST = XGlobalActions.move_to_last_device, + DEVLEFT = XGlobalActions.move_to_prev_device, + DEVRIGHT = XGlobalActions.move_to_next_device, + FOCBRWSR = XGlobalActions.focus_browser, + FOCDETAIL = XGlobalActions.focus_detail, + FOCMAIN = XGlobalActions.focus_main, + GQ = XGlobalActions.adjust_global_quantize, + GRV = XGlobalActions.adjust_groove, + HZOOM = XGlobalActions.adjust_horizontal_zoom, + VZOOM = XGlobalActions.adjust_vertical_zoom, + UP = XGlobalActions.move_up, + DOWN = XGlobalActions.move_down, + LEFT = XGlobalActions.move_left, + RIGHT = XGlobalActions.move_right, + LOOP = XGlobalActions.do_loop_action, + LOC = XGlobalActions.do_locator_action, + LOCLOOP = XGlobalActions.do_locator_loop_action, + METRO = XGlobalActions.set_metronome, + MIDI = XGlobalActions.send_midi_message, + OVER = XGlobalActions.set_overdub, + PIN = XGlobalActions.set_punch_in, + POUT = XGlobalActions.set_punch_out, + REC = XGlobalActions.set_record, + REDO = XGlobalActions.set_redo, + UNDO = XGlobalActions.set_undo, + RESTART = XGlobalActions.restart_transport, + RQ = XGlobalActions.adjust_record_quantize, + RTRIG = XGlobalActions.retrigger_recording_clips, + SIG = XGlobalActions.adjust_time_signature, + SCENE = XGlobalActions.set_scene, + SHOWCLIP = XGlobalActions.show_clip_view, + SHOWDEV = XGlobalActions.show_track_view, + SHOWDETAIL = XGlobalActions.show_detail_view, + TGLBRWSR = XGlobalActions.toggle_browser, + TGLDETAIL = XGlobalActions.toggle_detail_view, + TGLMAIN = XGlobalActions.toggle_main_view, + STOPALL = XGlobalActions.set_stop_all, + SETCONT = XGlobalActions.set_continue_playback, + SETLOC = XGlobalActions.set_locator, + SETSTOP = XGlobalActions.set_stop_transport, + SETFOLD = XGlobalActions.set_fold_all, + SETJUMP = XGlobalActions.set_jump_all, + TAPBPM = XGlobalActions.set_tap_tempo, + UNARM = XGlobalActions.set_unarm_all, + UNMUTE = XGlobalActions.set_unmute_all, + UNSOLO = XGlobalActions.set_unsolo_all, + MAKE_DEV_DOC = XGlobalActions.make_instant_mapping_docs, +) + +TRACK_ACTIONS = dict( + ARM = XTrackActions.set_arm, + MUTE = XTrackActions.set_mute, + SOLO = XTrackActions.set_solo, + MON = XTrackActions.set_monitor, + XFADE = XTrackActions.set_xfade, + SEL = XTrackActions.set_selection, + ADDCLIP = XTrackActions.create_clip, + DEL = XTrackActions.delete_track, + DELDEV = XTrackActions.delete_device, + DUPE = XTrackActions.duplicate_track, + FOLD = XTrackActions.set_fold, + PLAY = XTrackActions.set_play, + PLAYL = XTrackActions.set_play_w_legato, + PLAYQ = XTrackActions.set_play_w_force_qntz, + PLAYLQ = XTrackActions.set_play_w_force_qntz_and_legato, + STOP = XTrackActions.set_stop, + JUMP = XTrackActions.set_jump, + VOL = XTrackActions.adjust_volume, + PAN = XTrackActions.adjust_pan, + SEND = XTrackActions.adjust_sends, + CUE = XTrackActions.adjust_preview_volume, + XFADER = XTrackActions.adjust_crossfader, + IN = XTrackActions.adjust_input_routing, + INSUB = XTrackActions.adjust_input_sub_routing, + OUT = XTrackActions.adjust_output_routing, + OUTSUB = XTrackActions.adjust_output_sub_routing, + NAME = XTrackActions.set_name, + RENAMEALL = XTrackActions.rename_all_clips, +) + +CLIP_ACTIONS = dict( + CENT = XClipActions.adjust_detune, + SEMI = XClipActions.adjust_transpose, + GAIN = XClipActions.adjust_gain, + CUE = XClipActions.adjust_cue_point, + END = XClipActions.adjust_end, + START = XClipActions.adjust_start, + GRID = XClipActions.adjust_grid_quantization, + TGRID = XClipActions.set_triplet_grid, + ENVINS = XClipActions.insert_envelope, + ENVCLR = XClipActions.clear_envelope, + ENVCAP = XClipActions.capture_to_envelope, + ENVSHOW = XClipActions.show_envelope, + ENVHIDE = XClipActions.hide_envelopes, + QNTZ = XClipActions.quantize, + EXTEND = XClipActions.duplicate_clip_content, + DEL = XClipActions.delete_clip, + DUPE = XClipActions.duplicate_clip, + CHOP = XClipActions.chop_clip, + SPLIT = XClipActions.split_clip, + WARPMODE = XClipActions.adjust_warp_mode, + LOOP = XClipActions.do_clip_loop_action, + SIG = XClipActions.adjust_time_signature, + WARP = XClipActions.set_warp, + NAME = XClipActions.set_clip_name, +) + +DEVICE_ACTIONS = dict( + CSEL = XDeviceActions.adjust_selected_chain, + CS = XDeviceActions.adjust_chain_selector, + RESET = XDeviceActions.reset_params, + RND = XDeviceActions.randomize_params, + SEL = XDeviceActions.select_device, + SET = XDeviceActions.set_all_params, + P1 = XDeviceActions.adjust_best_of_bank_param, + P2 = XDeviceActions.adjust_best_of_bank_param, + P3 = XDeviceActions.adjust_best_of_bank_param, + P4 = XDeviceActions.adjust_best_of_bank_param, + P5 = XDeviceActions.adjust_best_of_bank_param, + P6 = XDeviceActions.adjust_best_of_bank_param, + P7 = XDeviceActions.adjust_best_of_bank_param, + P8 = XDeviceActions.adjust_best_of_bank_param, + B1 = XDeviceActions.adjust_banked_param, + B2 = XDeviceActions.adjust_banked_param, + B3 = XDeviceActions.adjust_banked_param, + B4 = XDeviceActions.adjust_banked_param, + B5 = XDeviceActions.adjust_banked_param, + B6 = XDeviceActions.adjust_banked_param, + B7 = XDeviceActions.adjust_banked_param, + B8 = XDeviceActions.adjust_banked_param, +) + +LOOPER_ACTIONS = dict( + LOOPER = XDeviceActions.set_looper_on_off, + REV = XDeviceActions.set_looper_rev, + OVER = XDeviceActions.set_looper_state, + PLAY = XDeviceActions.set_looper_state, + REC = XDeviceActions.set_looper_state, + STOP = XDeviceActions.set_looper_state, +) + +DR_ACTIONS = dict( + SCROLL = XDrActions.scroll_selector, + UNMUTE = XDrActions.unmute_all, + UNSOLO = XDrActions.unsolo_all, +) diff --git a/src/clyphx/actions/control_surface.py b/src/clyphx/actions/control_surface.py index edb21fd..0256fc9 100644 --- a/src/clyphx/actions/control_surface.py +++ b/src/clyphx/actions/control_surface.py @@ -15,19 +15,17 @@ # along with ClyphX. If not, see . from __future__ import with_statement, absolute_import, unicode_literals -from raven.utils.six import iteritems +from builtins import super, dict, range -# from builtins import range from functools import partial import Live from ableton.v2.control_surface import ControlSurface as CS -from _Framework.ControlSurfaceComponent import ControlSurfaceComponent from _Framework.ControlSurface import ControlSurface -from _Framework.SessionComponent import SessionComponent from _Framework.MixerComponent import MixerComponent from _Framework.DeviceComponent import DeviceComponent -from ..core import XComponent + +from ..core import XComponent, SessionComponent from ..consts import REPEAT_STATES from .push import ClyphXPushActions from .pxt_live import XPxtActions @@ -36,49 +34,50 @@ # TODO: update, enable... ?? -class XControlSurfaceActions(XComponent): +class XCsActions(XComponent): '''Actions related to control surfaces. ''' __module__ = __name__ def __init__(self, parent): - super(XControlSurfaceActions, self).__init__(parent) + super().__init__(parent) self._push_actions = ClyphXPushActions(parent) self._pxt_actions = XPxtActions(parent) self._mxt_actions = XMxtActions(parent) self._arsenal_actions = XArsenalActions(parent) - self._scripts = {} + self._scripts = dict() def disconnect(self): - self._scripts = {} + self._scripts = dict() self._arsenal_actions = None self._push_actions = None self._pxt_actions = None self._mxt_actions = None - super(XControlSurfaceActions, self).disconnect() + super().disconnect() def connect_script_instances(self, instantiated_scripts): '''Build dict of connected scripts and their components, doesn't work with non-Framework scripts, but does work with User Remote Scripts. ''' + # TODO: arg substituted instantiated_scripts = self._parent._control_surfaces() - self._scripts = {} + self._scripts = dict() for i in range(len(instantiated_scripts)): script = instantiated_scripts[i] - self._scripts[i] = { - 'script': script, - 'name': None, - 'repeat': False, - 'mixer': None, - 'device': None, - 'last_ring_pos': None, - 'session': None, - 'track_link': False, - 'scene_link': False, - 'centered_link': False, - 'color': False, - } + self._scripts[i] = dict( + script = script, + name = None, + repeat = False, + mixer = None, + device = None, + last_ring_pos = None, + session = None, + track_link = False, + scene_link = False, + centered_link = False, + color = False, + ) script_name = script.__class__.__name__ if isinstance(script, (ControlSurface, CS)): if script_name == 'GenericScript': @@ -98,27 +97,27 @@ def connect_script_instances(self, instantiated_scripts): if isinstance(c, SessionComponent): self._scripts[i]['session'] = c if script_name.startswith('APC'): - self._scripts[i]['color'] = { - 'GREEN': (1, 2), - 'RED': (3, 4), - 'AMBER': (5, 6), - } - self._scripts[i]['metro'] = { - 'controls': c._stop_track_clip_buttons, - 'component': None, - 'override': None, - } + self._scripts[i]['color'] = dict( + GREEN = (1, 2), + RED = (3, 4), + AMBER = (5, 6), + ) + self._scripts[i]['metro'] = dict( + controls = c._stop_track_clip_buttons, + component = None, + override = None, + ) elif script_name == 'Launchpad': - self._scripts[i]['color'] = { - 'GREEN': (52, 56), - 'RED': (7, 11), - 'AMBER': (55, 59), - } - self._scripts[i]['metro'] = { - 'controls': script._selector._side_buttons, - 'component': None, - 'override': script._selector, - } + self._scripts[i]['color'] = dict( + GREEN = (52, 56), + RED = (7, 11), + AMBER = (55, 59), + ) + self._scripts[i]['metro'] = dict( + controls = script._selector._side_buttons, + component = None, + override = script._selector, + ) if isinstance(c, MixerComponent): self._scripts[i]['mixer'] = c if isinstance(c, DeviceComponent): @@ -208,9 +207,11 @@ def _get_script_to_operate_on(self, script_info): try: script_spec = None if 'SURFACE' in script_info: - script_spec = script_info.strip('SURFACE') + script_spec = script_info.replace('SURFACE', '').strip() elif 'CS' in script_info: - script_spec = script_info.strip('CS') + script_spec = script_info.replace('CS', '').strip() + else: + return if len(script_spec) == 1: script = int(script_spec) - 1 @@ -218,7 +219,7 @@ def _get_script_to_operate_on(self, script_info): script = None else: script_spec = script_spec.strip('"').strip() - for k, v in iteritems(self._scripts): + for k, v in self._scripts.items(): if v['name'] == script_spec: script = k except: @@ -228,17 +229,18 @@ def _get_script_to_operate_on(self, script_info): def handle_note_repeat(self, script, script_index, args): '''Set note repeat for the given surface.''' args = args.replace('RPT', '').strip() - if args in REPEAT_STATES: - if args == 'OFF': - script._c_instance.note_repeat.enabled = False - self._scripts[script_index]['repeat'] = False - else: + if args == 'OFF': + script._c_instance.note_repeat.enabled = False + self._scripts[script_index]['repeat'] = False + else: + try: script._c_instance.note_repeat.repeat_rate = REPEAT_STATES[args] + except KeyError: + self._scripts[script_index]['repeat'] = not self._scripts[script_index]['repeat'] + script._c_instance.note_repeat.enabled = self._scripts[script_index]['repeat'] + else: script._c_instance.note_repeat.enabled = True self._scripts[script_index]['repeat'] = True - else: - self._scripts[script_index]['repeat'] = not self._scripts[script_index]['repeat'] - script._c_instance.note_repeat.enabled = self._scripts[script_index]['repeat'] def handle_track_action(self, script_key, mixer, xclip, ident, args): '''Get control surface track(s) to operate on and call main @@ -252,24 +254,22 @@ def handle_track_action(self, script_key, mixer, xclip, ident, args): new_args = '' if len(actions) > 1: new_args = ' '.join(actions[1:]) - if 'ALL' in track_range: - track_start = 0 - track_end = len(mixer._channel_strips) - elif '-' in track_range: - track_range = track_range.split('-') - try: - track_start = int(track_range[0]) - 1 - track_end = int(track_range[1]) - except: - track_start = None - track_end = None - else: - try: + + try: + if 'ALL' in track_range: + track_start = 0 + track_end = len(mixer._channel_strips) + elif '-' in track_range: + start, end = track_range.split('-') + track_start = int(start) - 1 + track_end = int(end) + else: track_start = int(track_range) - 1 track_end = track_start + 1 - except: - track_start = None - track_end = None + except: + track_start = None + track_end = None + if track_start is not None and track_end is not None: if (0 <= track_start and track_end < len(mixer._channel_strips) + 1 and @@ -300,6 +300,7 @@ def handle_track_bank(self, script_key, xclip, ident, mixer, session, args): t_offset, s_offset = mixer._track_offset, session._scene_offset if session else None tracks = mixer.tracks_to_use() new_offset = None + if args == 'FIRST': new_offset = 0 elif args == 'LAST': @@ -311,6 +312,7 @@ def handle_track_bank(self, script_key, xclip, ident, mixer, session, args): new_offset = offset + t_offset except: new_offset = None + if new_offset >= 0: if session: session.set_offsets(new_offset, s_offset) @@ -425,7 +427,7 @@ def on_selected_track_changed(self): trk = self.song().view.selected_track if trk in self.song().tracks: trk_id = list(self.song().visible_tracks).index(trk) - for k, v in iteritems(self._scripts): + for k, v in self._scripts.items(): if v['track_link']: new_trk_id = trk_id try: @@ -437,7 +439,7 @@ def on_selected_track_changed(self): width = session.width() t_offset, s_offset = session._track_offset, session._scene_offset if self._scripts[k]['centered_link']: - mid_point = (width / 2) + mid_point = width / 2 if new_trk_id < mid_point: if t_offset <= new_trk_id: return @@ -456,7 +458,7 @@ def on_selected_scene_changed(self): selected scene with centering if specified. ''' scn_id = list(self.song().scenes).index(self.song().view.selected_scene) - for k, v in iteritems(self._scripts): + for k, v in self._scripts.items(): if v['scene_link']: new_scn_id = scn_id try: @@ -469,7 +471,7 @@ def on_selected_scene_changed(self): t_offset, s_offset = session._track_offset, session._scene_offset if self._scripts[k]['centered_link']: - mid_point = (height / 2) + mid_point = height / 2 if new_scn_id < mid_point: if s_offset <= new_scn_id: return @@ -490,7 +492,7 @@ class VisualMetro(XComponent): __module__ = __name__ def __init__(self, parent, controls, override): - super(VisualMetro, self).__init__(parent) + super().__init__(parent) self._controls = controls self._override = override self._last_beat = -1 @@ -504,7 +506,7 @@ def disconnect(self): self.song().remove_current_song_time_listener(self.on_time_changed) self.song().remove_is_playing_listener(self.on_time_changed) self._override = None - super(VisualMetro, self).disconnect() + super().disconnect() def on_time_changed(self): '''Show visual metronome via control LEDs upon beat changes @@ -520,7 +522,7 @@ def on_time_changed(self): if self._last_beat < len(self._controls): self._controls[self._last_beat].turn_on() else: - self._controls[len(self._controls)-1].turn_on() + self._controls[len(self._controls) - 1].turn_on() else: self.clear() diff --git a/src/clyphx/actions/device.py b/src/clyphx/actions/device.py index f081187..839afd1 100644 --- a/src/clyphx/actions/device.py +++ b/src/clyphx/actions/device.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super, dict, range import Live from _Generic.Devices import * @@ -22,19 +23,18 @@ from ..core import XComponent from ..consts import KEYWORDS, LOOPER_STATES - class XDeviceActions(XComponent): '''Device and Looper actions. ''' __module__ = __name__ def __init__(self, parent): - super(XDeviceActions, self).__init__(parent) - self._looper_data = {} + super().__init__(parent) + self._looper_data = dict() def disconnect(self): - self._looper_data = {} - super(XDeviceActions, self).disconnect() + self._looper_data = dict() + super().disconnect() def set_all_params(self, device, track, xclip, ident, args): '''Set the value of all macros in a rack in one go. So don't need to @@ -51,10 +51,10 @@ def set_all_params(self, device, track, xclip, ident, args): param_values[i].strip()) else: if isinstance(xclip, Live.Clip.Clip): - assign_string = xclip.name + ' ' + assign_string = '{} '.format(xclip.name) for param in device.parameters: if 'Macro' in param.original_name: - assign_string += str(int(param.value)) + ' ' + assign_string += '{} '.format(int(param.value)) xclip.name = assign_string def adjust_selected_chain(self, device, track, xclip, ident, args): @@ -243,7 +243,7 @@ def get_banked_parameter(self, device, bank_string, param_string): def get_looper(self, track): '''Get first looper device on track and its params.''' - self._looper_data = {} + self._looper_data = dict() for d in track.devices: if d.class_name == 'Looper': self._looper_data['Looper'] = d diff --git a/src/clyphx/actions/global_.py b/src/clyphx/actions/global_.py index 9eeade0..c0c77b8 100644 --- a/src/clyphx/actions/global_.py +++ b/src/clyphx/actions/global_.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super, dict, range from functools import partial from itertools import chain @@ -30,7 +31,7 @@ class XGlobalActions(XComponent): __module__ = __name__ def __init__(self, parent): - super(XComponent, self).__init__(parent) + super().__init__(parent) self._last_gqntz = 4 self._last_rqntz = 5 self._repeat_enabled = False @@ -53,7 +54,7 @@ def disconnect(self): self.song().remove_is_playing_listener(self.on_time_changed) self._tempo_ramp_settings = [] self._scenes_to_monitor = None - super(XComponent, self).disconnect() + super().disconnect() def on_scene_triggered(self, index): self._last_scene_index = index @@ -61,13 +62,15 @@ def on_scene_triggered(self, index): def on_scene_list_changed(self): self.setup_scene_listeners() - def make_instant_mapping_docs(self, *a): - from ..instant_mapping_make_doc import InstantMappingMakeDoc + def make_instant_mapping_docs(self, track, xclip, ident, args): + from ..instant_doc import InstantMappingMakeDoc InstantMappingMakeDoc() + if isinstance(xclip, Live.Clip.Clip): + xclip.name = str(xclip.name).upper().replace('MAKE_DEV_DOC', 'Doc saved') def send_midi_message(self, track, xclip, ident, args): - '''Send formatted note/cc/pc message or raw midi message.''' - status_values = {'NOTE': 144, 'CC': 176, 'PC': 192} + '''Send formatted NOTE/CC/PC message or raw MIDI message.''' + status_values = dict(NOTE=144, CC=176, PC=192) message = [] if args: byte_array = args.split() @@ -87,7 +90,7 @@ def send_midi_message(self, track, xclip, ident, args): if message: try: self._parent._send_midi(tuple(message)) - #---send matching note off for note messages + # send matching note off for note messages if byte_array[0] == 'NOTE': message[-1] = 0 self._parent.schedule_message( @@ -782,7 +785,7 @@ def set_scene(self, track, xclip, ident, args): args = args.strip() scene_to_launch = self.get_scene_to_operate_on(xclip, args) if args: - #--Don't allow randomization unless more than 1 scene + # don't allow randomization unless more than 1 scene if 'RND' in args and len(self.song().scenes) > 1: num_scenes = len(self.song().scenes) rnd_range = [0, num_scenes] @@ -803,7 +806,7 @@ def set_scene(self, track, xclip, ident, args): if scene_to_launch == self._last_scene_index: while scene_to_launch == self._last_scene_index: scene_to_launch = Live.Application.get_random_int(0, rnd_range[1] - rnd_range[0]) + rnd_range[0] - #--Don't allow adjustment unless more than 1 scene + # don't allow adjustment unless more than 1 scene elif args.startswith(('<', '>')) and len(self.song().scenes) > 1: factor = self._parent.get_adjustment_factor(args) if factor < len(self.song().scenes): diff --git a/src/clyphx/actions/mxt_live.py b/src/clyphx/actions/mxt_live.py index 9de74a7..a56ef7f 100644 --- a/src/clyphx/actions/mxt_live.py +++ b/src/clyphx/actions/mxt_live.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super, range import Live import _Framework.Task @@ -31,7 +32,7 @@ class XMxtActions(XComponent): __module__ = __name__ def __init__(self, parent): - super(XMxtActions, self).__init__(parent) + super().__init__(parent) self._script = None self._seq_comp = None self._encoders = None @@ -42,7 +43,7 @@ def disconnect(self): self._seq_comp = None self._encoders = None self._message_display_line = None - super(XMxtActions, self).disconnect() + super().disconnect() def set_script(self, mxt_script): '''Set the MXT script to connect to and get necessary components. @@ -77,7 +78,7 @@ def _handle_seq_action(self, args, xclip, ident): note = comp._note_lane_component._note start = comp._position_component._start_position end = comp._position_component._end_position - self._parent._clip_actions.do_clip_note_action( + self._parent.clip_actions.do_clip_note_action( clip, None, None, '', 'NOTES @{}-{} {}'.format(note, start, end, args) ) diff --git a/src/clyphx/actions/push.py b/src/clyphx/actions/push.py index f523dda..c2afc62 100644 --- a/src/clyphx/actions/push.py +++ b/src/clyphx/actions/push.py @@ -14,40 +14,40 @@ # You should have received a copy of the GNU Lesser General Public License # along with ClyphX. If not, see . -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import, unicode_literals, with_statement +from builtins import super, dict, range -from __future__ import with_statement import Live -from _Framework.ControlSurfaceComponent import ControlSurfaceComponent +from ..core import ControlSurfaceComponent from ..consts import KEYWORDS, NOTE_NAMES UNWRITABLE_INDEXES = (17, 35, 53) -MATRIX_MODES = { - 'SESSION': 'session', - 'NOTE': 'note', -} +MATRIX_MODES = dict( + SESSION = 'session', + NOTE = 'note', +) -TRACK_MODES = { - 'STOP': 'stop', - 'SOLO': 'solo', - 'MUTE': 'mute', -} +TRACK_MODES = dict( + STOP = 'stop', + SOLO = 'solo', + MUTE = 'mute', +) -MAIN_MODES = { - 'VOLUME': 'volumes', - 'PAN': 'pan_sends', - 'TRACK': 'track', - 'CLIP': 'clip', - 'DEVICE': 'device', -} +MAIN_MODES = dict( + VOLUME = 'volumes', + PAN = 'pan_sends', + TRACK = 'track', + CLIP = 'clip', + DEVICE = 'device', +) -P2_MAIN_MODES = { - 'DEVICE': 'device', - 'MIX': 'mix', - 'CLIP': 'clip', -} +P2_MAIN_MODES = dict( + DEVICE = 'device', + MIX = 'mix', + CLIP = 'clip', +) # TODO: on_enabled_change / update @@ -56,7 +56,7 @@ class ClyphXPushActions(ControlSurfaceComponent): ''' def __init__(self, parent): - super(ClyphXPushActions, self).__init__() + super().__init__() self._parent = parent self._script = None self._ins_component = None @@ -70,7 +70,7 @@ def disconnect(self): self._note_editor = None self._scales_component = None self._parent = None - super(ClyphXPushActions, self).disconnect() + super().disconnect() def set_script(self, push_script, is_push2=False): '''Set the Push script to connect to and get necessary @@ -200,27 +200,28 @@ def _handle_scale_action(self, args, xclip, ident): self._capture_scale_settings(xclip, ident) def _handle_in_key(self, arg_array): - if len(arg_array) == 2 and arg_array[1] in KEYWORDS: + try: self._ins_component._note_layout.is_in_key = KEYWORDS[arg_array[1]] - else: + except (IndexError, KeyError): self._ins_component._note_layout.is_in_key =\ not self._ins_component._note_layout.is_in_key def _handle_fixed(self, arg_array): - if len(arg_array) == 2 and arg_array[1] in KEYWORDS: + try: self._ins_component._note_layout.is_fixed = KEYWORDS[arg_array[1]] - else: + except (IndexError, KeyError): self._ins_component._note_layout.is_fixed =\ not self._ins_component._note_layout.is_fixed def _handle_root_note(self, arg_array): - if arg_array[1] in NOTE_NAMES: + try: self._ins_component._note_layout.root_note = NOTE_NAMES.index(arg_array[1]) - elif arg_array[1] in ('<', '>'): - new_root = (self._parent.get_adjustment_factor(arg_array[1]) - + self._ins_component._note_layout.root_note) - if 0 <= new_root < 12: - self._ins_component._note_layout.root_note = new_root + except KeyError: + if arg_array[1] in ('<', '>'): + new_root = (self._parent.get_adjustment_factor(arg_array[1]) + + self._ins_component._note_layout.root_note) + if 0 <= new_root < 12: + self._ins_component._note_layout.root_note = new_root def _handle_octave(self, arg_array): if arg_array[1] == '<': @@ -304,6 +305,6 @@ def _handle_sequence_action(self, args): clip = c if c and c.is_midi_clip else None note = self._script._drum_component.selected_note if clip and note is not None: - self._parent._clip_actions.do_clip_note_action( + self._parent.clip_actions.do_clip_note_action( clip, None, None, '', 'NOTES{} {}'.format(note, args) ) diff --git a/src/clyphx/actions/pxt_live.py b/src/clyphx/actions/pxt_live.py index 928994a..724ba04 100644 --- a/src/clyphx/actions/pxt_live.py +++ b/src/clyphx/actions/pxt_live.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super, range, map import Live import _Framework.Task @@ -39,7 +40,7 @@ class XPxtActions(XComponent): __module__ = __name__ def __init__(self, parent): - super(XPxtActions, self).__init__(parent) + super().__init__(parent) self._script = None self._mono_seq_mode = None self._poly_seq_mode = None @@ -52,7 +53,7 @@ def disconnect(self): self._poly_seq_mode = None self._encoders = None self._message_display_line = None - super(XPxtActions, self).disconnect() + super().disconnect() def set_script(self, pxt_script): '''Set the PXT script to connect to and get necessary @@ -96,7 +97,7 @@ def _handle_mono_seq_action(self, args, xclip, ident): note = comp._note_lane_components[comp._selected_lane_index]._note start = comp._position_component._start_position end = comp._position_component._end_position - self._parent._clip_actions.do_clip_note_action( + self._parent.clip_actions.do_clip_note_action( clip, None, None, '', 'NOTES{} @{}-{} {}'.format(note, start, end, args) ) @@ -131,7 +132,7 @@ def _handle_poly_seq_action(self, args, xclip, ident): if notes: start = comp._position_component._start_position end = comp._position_component._end_position - self._parent._clip_actions.do_clip_note_action( + self._parent.clip_actions.do_clip_note_action( clip, None, None, '', 'NOTES{} @{}-{} {}'.format(notes, start, end, args) ) @@ -160,27 +161,27 @@ def _capture_seq_settings(self, xclip, ident, comp, is_mono): def _recall_seq_settings(self, args, comp): '''Recall the settings for the given seq comp.''' - arg_array = args.replace('CAP', '').strip().split() + arg_array = list(map(int, args.replace('CAP', '').strip().split())) if len(arg_array) >= 7: # res settings - res_comp = comp._resolution_component - if res_comp._resolution_buttons: - res_btn = res_comp._resolution_buttons[int(arg_array[0])] - res_comp._on_resolution_button_value(127, res_btn) + res = comp._resolution_component + if res._resolution_buttons: + res_btn = res._resolution_buttons[arg_array[0]] + res._on_resolution_button_value(127, res_btn) # velo settings - velo_comp = comp._velocity_component - velo_comp._fixed_velocity = int(arg_array[1]) - velo_comp._velocity_type = int(arg_array[2]) - velo_comp.update() + vel = comp._velocity_component + vel._fixed_velocity = arg_array[1] + vel._velocity_type = arg_array[2] + vel.update() # scale settings - scl_comp = comp._scales_component - scl_index = int(arg_array[3]) - scl_comp._scale_index = scl_index - scl_comp._root_note = int(arg_array[4]) - scl_comp._octave_offset = int(arg_array[5]) - scl_comp._offset_within_octave = int(arg_array[6]) - scl_comp._scale = EDITABLE_SCALE if scl_index == -1 else SCALE_TYPES[scl_index] - scl_comp._set_current_notes() + scl = comp._scales_component + i = arg_array[3] + scl._scale = EDITABLE_SCALE if i == -1 else SCALE_TYPES[i] + scl._scale_index = i + scl._root_note = arg_array[4] + scl._octave_offset = arg_array[5] + scl._offset_within_octave = arg_array[6] + scl._set_current_notes() def _handle_encoder_action(self, args): '''Reset or randomize the values of the parameters the encoders @@ -193,6 +194,7 @@ def _handle_encoder_action(self, args): p = enc.mapped_parameter() if p and p.is_enabled and not p.is_quantized: if randomize: + # TODO: int? p.value = (((p.max - p.min) / 127) * Live.Application.get_random_int(0, 128)) + p.min else: p.value = p.default_value @@ -214,7 +216,7 @@ def _display_message(self, args, xclip): note_at_og_case[i:note_len]) note_len += 1 new_len = len(note_at_og_case) - num_segments = (new_len / FULL_SEGMENT) + 1 + num_segments = (new_len // FULL_SEGMENT) + 1 for i in range(num_segments): offset = FULL_SEGMENT_OFFSETS[i] self._message_display_line.write_momentary( diff --git a/src/clyphx/actions/snap.py b/src/clyphx/actions/snap.py index 568b350..45fc481 100644 --- a/src/clyphx/actions/snap.py +++ b/src/clyphx/actions/snap.py @@ -15,13 +15,13 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals -from raven.utils.six import iteritems +from builtins import super, range from functools import partial from itertools import chain import math import Live -from _Framework.ControlSurfaceComponent import ControlSurfaceComponent +from ..core.compat import iteritems from ..core import XComponent @@ -31,10 +31,10 @@ class XSnapActions(XComponent): __module__ = __name__ def __init__(self, parent): - super(XSnapActions, self).__init__(parent) - self._current_tracks = {} - self._parameters_to_smooth = {} - self._rack_parameters_to_smooth = {} + super().__init__(parent) + self.current_tracks = dict() + self._parameters_to_smooth = dict() + self._rack_parameters_to_smooth = dict() self._smoothing_active = False self._synced_smoothing_active = False self._rack_smoothing_active = False @@ -58,24 +58,24 @@ def disconnect(self): self.remove_track_listeners() self.song().remove_current_song_time_listener(self.on_time_changed) self.song().remove_is_playing_listener(self.on_time_changed) - self._current_tracks = {} - self._parameters_to_smooth = {} - self._rack_parameters_to_smooth = {} + self.current_tracks = dict() + self._parameters_to_smooth = dict() + self._rack_parameters_to_smooth = dict() self._control_rack = None self._snap_id = None - super(XSnapActions, self).disconnect() + super().disconnect() def store_track_snapshot(self, track_list, xclip, ident, action, args): '''Store snapshot of track params.''' param_count = 0 if not isinstance(xclip, Live.Clip.Clip): return () - snap_data = {} + snap_data = dict() if track_list: for track in track_list: track_name = self._parent.get_name(track.name) if not track_name.startswith('CLYPHX SNAP') and track.name not in snap_data: - track_data = [[], [], None, {}] + track_data = [[], [], None, dict()] if not args or 'MIX' in args: if not 'MIXS' in args: mix_vals = [track.mixer_device.volume.value, @@ -97,7 +97,7 @@ def store_track_snapshot(self, track_list, xclip, ident, action, args): if (not args or 'DEV' in args) and track.devices: dev_range = self.get_snap_device_range(args, track) if dev_range: - track_devices = {} + track_devices = dict() for dev_index in range(dev_range[0], dev_range[1]): if dev_index < (len(track.devices)): current_device = track.devices[dev_index] @@ -161,8 +161,8 @@ def recall_track_snapshot(self, name, xclip): self._smoothing_active = False self._rack_smoothing_active = False self._synced_smoothing_active = False - self._parameters_to_smooth = {} - self._rack_parameters_to_smooth = {} + self._parameters_to_smooth = dict() + self._rack_parameters_to_smooth = dict() self._is_control_track = track_name.startswith('CLYPHX SNAP') is_synced = False if self._is_control_track: @@ -189,8 +189,8 @@ def recall_track_snapshot(self, name, xclip): if 0 <= new_speed < 501: self._smoothing_speed = new_speed for track, param_data in iteritems(snap_data): - if track in self._current_tracks: - track = self._current_tracks[track] + if track in self.current_tracks: + track = self.current_tracks[track] if param_data[0]: if track.mixer_device.volume.is_enabled and param_data[0][0] != -1: self.get_parameter_data_to_smooth(track.mixer_device.volume, param_data[0][0]) @@ -215,11 +215,14 @@ def recall_track_snapshot(self, name, xclip): for device in track.devices: if device.name in param_data[3]: self.recall_device_snap(device, param_data[3][device.name][0]) - if self._include_nested_devices and self._parent._can_have_nested_devices and device.can_have_chains and param_data[3][device.name][1]: + if (self._include_nested_devices and + self._parent._can_have_nested_devices and + device.can_have_chains and + param_data[3][device.name][1]): self.recall_nested_device_snap(device, param_data[3][device.name][1]) del param_data[3][device.name] if self._is_control_track and self._parameters_to_smooth: - if not self._control_rack or (self._control_rack and not self._control_rack.parameters[0].value == 1.0): + if not self._control_rack or (self._control_rack and self._control_rack.parameters[0].value != 1.0): self._smoothing_active = not is_synced self._synced_smoothing_active = is_synced else: @@ -287,10 +290,10 @@ def control_rack_macro_changed(self): '''Get param values to set based on macro value and build dict. ''' if self._rack_smoothing_active and self._parameters_to_smooth and self._control_rack.parameters[0].value == 1.0: - self._rack_parameters_to_smooth = {} + self._rack_parameters_to_smooth = dict() macro_value = self._control_rack.parameters[1].value - new_dict = {} - for p, v in iteritems(self._parameters_to_smooth): + new_dict = dict() + for p, v in self._parameters_to_smooth.items(): param_value = v[2] + (macro_value * v[0]) if p.is_quantized: if macro_value < 63 and p.value != v[2]: @@ -308,7 +311,7 @@ def on_timer(self): if self._smoothing_active and self._parameters_to_smooth: self.apply_timed_smoothing() if self._rack_smoothing_active and self._rack_parameters_to_smooth: - for p, v in iteritems(self._rack_parameters_to_smooth): + for p, v in self._rack_parameters_to_smooth.items(): p.value = v del self._rack_parameters_to_smooth[p] @@ -323,7 +326,7 @@ def on_time_changed(self): def apply_timed_smoothing(self, arg=None): '''Apply smoothing for either timer or sync.''' self._smoothing_count += 1 - for p, v in iteritems(self._parameters_to_smooth): + for p, v in self._parameters_to_smooth.items(): param_value = v[2] + (self._smoothing_count * v[0]) if p.is_quantized: p.value = v[1] @@ -392,7 +395,7 @@ def get_snap_device_range(self, args, track): def setup_tracks(self): '''Store dictionary of tracks by name.''' - self._current_tracks = {} + self.current_tracks = dict() self.remove_track_listeners() for track in chain(self.song().tracks, self.song().return_tracks, @@ -400,8 +403,8 @@ def setup_tracks(self): if not track.name_has_listener(self.setup_tracks): track.add_name_listener(self.setup_tracks) name = self._parent.get_name(track.name) - if track.name not in self._current_tracks and not name.startswith('CLYPHX SNAP'): - self._current_tracks[track.name] = track + if track.name not in self.current_tracks and not name.startswith('CLYPHX SNAP'): + self.current_tracks[track.name] = track def remove_control_rack(self): '''Remove control rack listeners.''' diff --git a/src/clyphx/actions/snap9.py b/src/clyphx/actions/snap9.py index 150887a..7003168 100644 --- a/src/clyphx/actions/snap9.py +++ b/src/clyphx/actions/snap9.py @@ -15,14 +15,14 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals -from raven.utils.six import iteritems +from builtins import super, dict, range from functools import partial from itertools import chain import Live import math import pickle -# from _Framework.ControlSurfaceComponent import ControlSurfaceComponent +from ..core.compat import iteritems from ..core import XComponent @@ -55,10 +55,10 @@ class XSnapActions(XComponent): __module__ = __name__ def __init__(self, parent): - super(XSnapActions, self).__init__(parent) - self._current_tracks = {} - self._parameters_to_smooth = {} - self._rack_parameters_to_smooth = {} + super().__init__(parent) + self.current_tracks = dict() + self._parameters_to_smooth = dict() + self._rack_parameters_to_smooth = dict() self._smoothing_active = False self._synced_smoothing_active = False self._rack_smoothing_active = False @@ -82,24 +82,25 @@ def disconnect(self): self._remove_track_listeners() self.song().remove_current_song_time_listener(self._on_time_changed) self.song().remove_is_playing_listener(self._on_time_changed) - self._current_tracks = {} - self._parameters_to_smooth = {} - self._rack_parameters_to_smooth = {} + self.current_tracks = dict() + self._parameters_to_smooth = dict() + self._rack_parameters_to_smooth = dict() self._control_rack = None self._snap_id = None - super(XSnapActions, self).disconnect() + super().disconnect() def store_track_snapshot(self, track_list, xclip, ident, action, args, force=False): '''Stores snapshot of track params.''' param_count = 0 + # FIXME if not force and not isinstance(xclip, Live.Clip.Clip()): return () - snap_data = {} + snap_data = dict() if track_list: for track in track_list: track_name = self._parent.get_name(track.name) if not track_name.startswith('CLYPHX SNAP') and track.name not in snap_data: - self._current_track_data = [[], [], None, {}] + self._current_track_data = [[], [], None, dict()] if not args or 'MIX' in args: param_count += self._store_mix_settings(track, args) if 'PLAY' in args and track in self.song().tracks: @@ -142,16 +143,19 @@ def _store_device_settings(self, track, args): parameters that were stored. ''' param_count = 0 - dev_range = self._get_snap_device_range(args, track) - if dev_range: - track_devices = {} - for dev_index in range(dev_range[0], dev_range[1]): + try: + start, end = self._get_snap_device_range(args, track) + except ValueError: + pass + else: + track_devices = dict() + for dev_index in range(start, end): if dev_index < len(track.devices): current_device = track.devices[dev_index] if current_device.name not in track_devices: - track_devices[current_device.name] = { - 'params': [p.value for p in current_device.parameters], - } + track_devices[current_device.name] = dict( + params=[p.value for p in current_device.parameters], + ) param_count += len(current_device.parameters) if (self._include_nested_devices and self._parent._can_have_nested_devices and @@ -162,6 +166,7 @@ def _store_device_settings(self, track, args): ) if track_devices: self._current_track_data[DEVICE_SETTINGS_POS] = track_devices + return param_count def _get_nested_devices(self, rack, nested_devs, parameter_count): @@ -169,13 +174,13 @@ def _get_nested_devices(self, rack, nested_devs, parameter_count): parameters. ''' if rack.chains: - nested_devs['chains'] = {} + nested_devs['chains'] = dict() for ci, c in enumerate(rack.chains): - nested_devs['chains'][ci] = {'devices' : {}} + nested_devs['chains'][ci] = dict(devices=dict()) for di, d in enumerate(c.devices): - nested_devs['chains'][ci]['devices'][di] = { - 'params' : [p.value for p in d.parameters] - } + nested_devs['chains'][ci]['devices'][di] = dict( + params=[p.value for p in d.parameters] + ) parameter_count += len(d.parameters) if not rack.class_name.startswith('Midi'): mix_settings = [c.mixer_device.volume.value, @@ -197,13 +202,14 @@ def _get_nested_devices(self, rack, nested_devs, parameter_count): def recall_track_snapshot(self, name, xclip, disable_smooth=False): '''Recalls snapshot of track params.''' self._snap_id = xclip.name[xclip.name.index('['):xclip.name.index(']')+1].strip().upper() + #FIXME snap_data = pickle.loads(str(xclip.name)[len(self._snap_id) + 4:]) - self._parameters_to_smooth = {} - self._rack_parameters_to_smooth = {} + self._parameters_to_smooth = dict() + self._rack_parameters_to_smooth = dict() is_synced = False if disable_smooth else self._init_smoothing(xclip) for track, param_data in iteritems(snap_data): - if track in self._current_tracks: - track = self._current_tracks[track] + if track in self.current_tracks: + track = self.current_tracks[track] self._recall_mix_settings(track, param_data) if (param_data[PLAY_SETTINGS_POS] is not None and not track.is_foldable and @@ -218,7 +224,7 @@ def recall_track_snapshot(self, name, xclip, disable_smooth=False): if self._is_control_track and self._parameters_to_smooth: if (not self._control_rack or (self._control_rack and - not self._control_rack.parameters[0].value == 1.0)): + self._control_rack.parameters[0].value != 1.0)): self._smoothing_active = not is_synced self._synced_smoothing_active = is_synced else: @@ -390,10 +396,10 @@ def _control_rack_macro_changed(self): if (self._rack_smoothing_active and self._parameters_to_smooth and self._control_rack.parameters[0].value == 1.0): - self._rack_parameters_to_smooth = {} + self._rack_parameters_to_smooth = dict() macro_value = self._control_rack.parameters[1].value - new_dict = {} - for p, v in iteritems(self._parameters_to_smooth): + new_dict = dict() + for p, v in self._parameters_to_smooth.items(): param_value = v[2] + (macro_value * v[0]) if p.is_quantized: if macro_value < 63 and p.value != v[2]: @@ -411,7 +417,7 @@ def _on_timer(self): if self._smoothing_active and self._parameters_to_smooth: self._apply_timed_smoothing() if self._rack_smoothing_active and self._rack_parameters_to_smooth: - for p, v in iteritems(self._rack_parameters_to_smooth): + for p, v in self._rack_parameters_to_smooth.items(): p.value = v del self._rack_parameters_to_smooth[p] @@ -428,7 +434,7 @@ def _on_time_changed(self): def _apply_timed_smoothing(self, arg=None): '''Applies smoothing for either timer or sync.''' self._smoothing_count += 1 - for p, v in iteritems(self._parameters_to_smooth): + for p, v in self._parameters_to_smooth.items(): param_value = v[2] + (self._smoothing_count * v[0]) if p.is_quantized: p.value = v[1] @@ -466,16 +472,15 @@ def _get_snap_device_range(self, args, track): '''Returns range of devices to snapshot.''' dev_args = args.replace('MIX', '').replace('PLAY', '').replace('DEV', '').replace('IO', '') start = 0 - end = start + 1 + end = 1 + if dev_args: if 'ALL' in dev_args: - start = 0 end = len(track.devices) elif '-' in dev_args: try: - name_split = dev_args.split('-') - start = int(name_split[0].strip()) - 1 - end = int(name_split[1].strip()) + start, end = map(int, dev_args.split('-')) + start -= 1 except: pass else: @@ -484,13 +489,15 @@ def _get_snap_device_range(self, args, track): end = start + 1 except: pass - if start > len(track.devices) or start < 0 or end > len(track.devices) or end < start: - return () + + if end > len(track.devices) or start < 0 or end < start: + raise ValueError("Range of devices not found in '{}'".format(dev_args)) + return (start, end) def setup_tracks(self): '''Stores dictionary of tracks by name.''' - self._current_tracks = {} + self.current_tracks = dict() self._remove_track_listeners() for track in chain(self.song().tracks, self.song().return_tracks, @@ -498,8 +505,8 @@ def setup_tracks(self): if not track.name_has_listener(self.setup_tracks): track.add_name_listener(self.setup_tracks) name = self._parent.get_name(track.name) - if track.name not in self._current_tracks and not name.startswith('CLYPHX SNAP'): - self._current_tracks[track.name] = track + if track.name not in self.current_tracks and not name.startswith('CLYPHX SNAP'): + self.current_tracks[track.name] = track def _refresh_xclip_name(self, xclip_data): '''Refreshes xclip's previous name in cases where a snap is diff --git a/src/clyphx/actions/track.py b/src/clyphx/actions/track.py index 1afd77c..22eef97 100644 --- a/src/clyphx/actions/track.py +++ b/src/clyphx/actions/track.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import range import Live from ..consts import KEYWORDS @@ -92,8 +93,7 @@ def rename_all_clips(self, track, xclip, ident, args): ''' if track in self.song().tracks and not track.is_foldable: name = args.strip() if args.strip() else track.name - for i in range(len(track.clip_slots)): - slot = track.clip_slots[i] + for i, slot in enumerate(track.clip_slots): if slot.has_clip: slot.clip.name = '{} {}'.format(name, i + 1) @@ -146,7 +146,7 @@ def set_selection(self, track, xclip, ident, args): if track in self.song().tracks: if args: try: - self.song().view.selected_scene = list(self.song().scenes)[int(args)-1] + self.song().view.selected_scene = list(self.song().scenes)[int(args) - 1] except: pass else: @@ -209,7 +209,9 @@ def _handle_force_qntz_play(self, track, xclip, args, w_legato): qntz_to_use = GQ_STATES[qntz_spec] except KeyError: return - slot_to_play = self._get_slot_index_to_play(track, xclip, args.replace(qntz_spec, '').strip()) + slot_to_play = self._get_slot_index_to_play( + track, xclip, args.replace(qntz_spec, '').strip() + ) if slot_to_play != -1: track.clip_slots[slot_to_play].fire(force_legato=w_legato, launch_quantization=qntz_to_use) @@ -228,7 +230,8 @@ def _get_slot_index_to_play(self, track, xclip, args, allow_empty_slots=False): slot_to_play = play_slot if play_slot >= 0 else select_slot elif args == 'SEL': slot_to_play = select_slot - #--Don't allow randomization unless more than 1 scene + # TODO: repeated, check refactoring + # don't allow randomization unless more than 1 scene elif 'RND' in args and len(self.song().scenes) > 1: num_scenes = len(self.song().scenes) rnd_range = [0, num_scenes] @@ -249,13 +252,13 @@ def _get_slot_index_to_play(self, track, xclip, args, allow_empty_slots=False): if slot_to_play == play_slot: while slot_to_play == play_slot: slot_to_play = Live.Application.get_random_int(0, rnd_range[1] - rnd_range[0]) + rnd_range[0] - #--Don't allow adjustment unless more than 1 scene + # don't allow adjustment unless more than 1 scene elif args.startswith(('<', '>')) and len(self.song().scenes) > 1: if track.is_foldable: return -1 factor = self._parent.get_adjustment_factor(args) if factor < len(self.song().scenes): - #---Only launch slots that contain clips + # only launch slots that contain clips if abs(factor) == 1: for _ in range(len(self.song().scenes)): play_slot += factor diff --git a/src/clyphx/clyphx.py b/src/clyphx/clyphx.py index 10556b7..52c26aa 100644 --- a/src/clyphx/clyphx.py +++ b/src/clyphx/clyphx.py @@ -15,23 +15,26 @@ # along with ClyphX. If not, see . from __future__ import with_statement, absolute_import, unicode_literals +from builtins import super, dict, range, map import logging from functools import partial from itertools import chain import Live from _Framework.ControlSurface import ControlSurface -from _Framework import Task -from raven.utils.six import iteritems from .core import UserSettings from .macrobat import Macrobat from .extra_prefs import ExtraPrefs from .cs_linker import CsLinker from .actions import ( - XTrackActions, XSnapActions, XGlobalActions, - XDeviceActions, XDrActions, XClipActions, - XControlSurfaceActions, + XGlobalActions, GLOBAL_ACTIONS, + XTrackActions, TRACK_ACTIONS, + XClipActions, CLIP_ACTIONS, + XDeviceActions, DEVICE_ACTIONS, LOOPER_ACTIONS, + XDrActions, DR_ACTIONS, + XSnapActions, + XCsActions, ) from .xtriggers import ( XTrackComponent, XControlComponent, XCueComponent @@ -40,17 +43,12 @@ from .m4l_browser import XM4LBrowserInterface from .action_list import ActionList from .push_apc_combiner import PushApcCombiner -from .consts import LIVE_VERSION -from .consts import (CLIP_ACTIONS, DEVICE_ACTIONS, DR_ACTIONS, - GLOBAL_ACTIONS, LOOPER_ACTIONS, TRACK_ACTIONS) +from .consts import LIVE_VERSION, SCRIPT_NAME, SCRIPT_INFO from .push_mocks import MockHandshakeTask, MockHandshake -# from utils import get_python_info +from .core.utils import show_python_info, get_base_path log = logging.getLogger(__name__) -log.setLevel(logging.INFO) - -FOLDER = '/ClyphX/' -SCRIPT_NAME = 'ClyphX v2.7.0' +log.setLevel(logging.DEBUG) class ClyphX(ControlSurface): @@ -59,58 +57,59 @@ class ClyphX(ControlSurface): __module__ = __name__ def __init__(self, c_instance): - super(ClyphX, self).__init__(c_instance) + super().__init__(c_instance) self._user_settings_logged = False self._is_debugging = False self._push_emulation = False self._PushApcCombiner = None self._process_xclips_if_track_muted = True self._user_settings = UserSettings() + # show_python_info() with self.component_guard(): - self._macrobat = Macrobat(self) + self.macrobat = Macrobat(self) self._extra_prefs = ExtraPrefs(self, self._user_settings.prefs) - self._cs_linker = CsLinker() - self._track_actions = XTrackActions(self) - self._snap_actions = XSnapActions(self) - self._global_actions = XGlobalActions(self) - self._device_actions = XDeviceActions(self) - self._dr_actions = XDrActions(self) - self._clip_actions = XClipActions(self) - self._control_surface_actions = XControlSurfaceActions(self) - self._user_actions = XUserActions(self) - self._control_component = XControlComponent(self) + self.cs_linker = CsLinker() + self.track_actions = XTrackActions(self) + self.snap_actions = XSnapActions(self) + self.global_actions = XGlobalActions(self) + self.device_actions = XDeviceActions(self) + self.dr_actions = XDrActions(self) + self.clip_actions = XClipActions(self) + self.cs_actions = XCsActions(self) + self.user_actions = XUserActions(self) + self.control_component = XControlComponent(self) XM4LBrowserInterface(self) XCueComponent(self) self._startup_actions_complete = False - self._user_variables = {} - self._play_seq_clips = {} - self._loop_seq_clips = {} - self._current_tracks = [] + self._user_variables = dict() + self._play_seq_clips = dict() + self._loop_seq_clips = dict() + self.current_tracks = [] self._can_have_nested_devices = True self.setup_tracks() - msg = 'ClyphX LOG ------- %s ------- Live Version: %s ------- END LOG' - log.info(msg, SCRIPT_NAME, '.'.join(map(str, LIVE_VERSION))) - self.show_message(SCRIPT_NAME) + msg = '--- %s --- Live Version: %s ---' + log.info(msg, SCRIPT_INFO, '.'.join(map(str, LIVE_VERSION))) + self.show_message(SCRIPT_INFO) def disconnect(self): self._PushApcCombiner = None - self._macrobat = None + self.macrobat = None self._extra_prefs = None - self._cs_linker = None - self._track_actions = None - self._snap_actions = None - self._global_actions = None - self._device_actions = None - self._dr_actions = None - self._clip_actions = None - self._control_surface_actions = None - self._user_actions = None - self._control_component = None - self._user_variables = {} - self._play_seq_clips = {} - self._loop_seq_clips = {} - self._current_tracks = [] - super(ClyphX, self).disconnect() + self.cs_linker = None + self.track_actions = None + self.snap_actions = None + self.global_actions = None + self.device_actions = None + self.dr_actions = None + self.clip_actions = None + self.cs_actions = None + self.user_actions = None + self.control_component = None + self._user_variables = dict() + self._play_seq_clips = dict() + self._loop_seq_clips = dict() + self.current_tracks = [] + super().disconnect() @property def _is_debugging(self): @@ -128,22 +127,22 @@ def action_dispatch(self, tracks, xclip, action_name, args, ident): ''' if tracks: if action_name.startswith('SNAP'): - self._snap_actions.store_track_snapshot(tracks, xclip, ident, action_name, args) + self.snap_actions.store_track_snapshot(tracks, xclip, ident, action_name, args) elif action_name.startswith(('SURFACE', 'CS')): - self._control_surface_actions.dispatch_cs_action(tracks[0], xclip, ident, action_name, args) + self.cs_actions.dispatch_cs_action(tracks[0], xclip, ident, action_name, args) elif action_name.startswith('ARSENAL'): - self._control_surface_actions.dispatch_arsenal_action(tracks[0], xclip, ident, action_name, args) + self.cs_actions.dispatch_arsenal_action(tracks[0], xclip, ident, action_name, args) elif action_name.startswith('PUSH'): - self._control_surface_actions.dispatch_push_action(tracks[0], xclip, ident, action_name, args) + self.cs_actions.dispatch_push_action(tracks[0], xclip, ident, action_name, args) elif action_name.startswith('PXT'): - self._control_surface_actions.dispatch_pxt_action(tracks[0], xclip, ident, action_name, args) + self.cs_actions.dispatch_pxt_action(tracks[0], xclip, ident, action_name, args) elif action_name.startswith('MXT'): - self._control_surface_actions.dispatch_mxt_action(tracks[0], xclip, ident, action_name, args) + self.cs_actions.dispatch_mxt_action(tracks[0], xclip, ident, action_name, args) elif action_name in GLOBAL_ACTIONS: - getattr(self._global_actions, GLOBAL_ACTIONS[action_name])(tracks[0], xclip, ident, args) + GLOBAL_ACTIONS[action_name](self.global_actions, tracks[0], xclip, ident, args) elif action_name == 'PSEQ' and args== 'RESET': - for _, value in iteritems(self._play_seq_clips): - value[1] = -1 + for v in self._play_seq_clips.values(): + v[1] = -1 elif action_name == 'DEBUG': if isinstance(xclip, Live.Clip.Clip): xclip.name = str(xclip.name).upper().replace('DEBUG', 'Debugging Activated') @@ -151,12 +150,12 @@ def action_dispatch(self, tracks, xclip, action_name, args, ident): else: for t in tracks: if action_name in TRACK_ACTIONS: - getattr(self._track_actions, TRACK_ACTIONS[action_name])(t, xclip, ident, args) + TRACK_ACTIONS[action_name](self.track_actions, t, xclip, ident, args) elif action_name == 'LOOPER': if args and args.split()[0] in LOOPER_ACTIONS: - getattr(self._device_actions, LOOPER_ACTIONS[args.split()[0]])(t, xclip, ident, args) + LOOPER_ACTIONS[args.split()[0]](self.device_actions, t, xclip, ident, args) elif action_name in LOOPER_ACTIONS: - getattr(self._device_actions, LOOPER_ACTIONS[action_name])(t, xclip, ident, args) + LOOPER_ACTIONS[action_name](self.device_actions, t, xclip, ident, args) elif action_name.startswith('DEV'): device_action = self.get_device_to_operate_on(t, action_name, args) device_args = None @@ -164,12 +163,12 @@ def action_dispatch(self, tracks, xclip, action_name, args, ident): if len(device_action) > 1: device_args = device_action[1] if device_args and device_args.split()[0] in DEVICE_ACTIONS: - fn = getattr(self._device_actions, DEVICE_ACTIONS[device_args.split()[0]]) - fn(device_action[0], t, xclip, ident, device_args) + fn = DEVICE_ACTIONS[device_args.split()[0]] + fn(self.device_actions, device_action[0], t, xclip, ident, device_args) elif device_args and 'CHAIN' in device_args: - self._device_actions.dispatch_chain_action(device_action[0], t, xclip, ident, device_args) + self.device_actions.dispatch_chain_action(device_action[0], t, xclip, ident, device_args) elif action_name.startswith('DEV'): - self._device_actions.set_device_on_off(device_action[0], t, xclip, ident, device_args) + self.device_actions.set_device_on_off(device_action[0], t, xclip, ident, device_args) elif action_name.startswith('CLIP') and t in self.song().tracks: clip_action = self.get_clip_to_operate_on(t, action_name, args) clip_args = None @@ -177,23 +176,24 @@ def action_dispatch(self, tracks, xclip, action_name, args, ident): if len(clip_action) > 1: clip_args = clip_action[1] if clip_args and clip_args.split()[0] in CLIP_ACTIONS: - fn = getattr(self._clip_actions, CLIP_ACTIONS[clip_args.split()[0]]) - fn(clip_action[0], t, xclip, ident, clip_args.replace(clip_args.split()[0], '')) + # fn = getattr(self.clip_actions, CLIP_ACTIONS[clip_args.split()[0]]) + fn = CLIP_ACTIONS[clip_args.split()[0]] + fn(self.clip_actions, clip_action[0], t, xclip, ident, clip_args.replace(clip_args.split()[0], '')) elif clip_args and clip_args.split()[0].startswith('NOTES'): - self._clip_actions.do_clip_note_action(clip_action[0], t, xclip, ident, args) + self.clip_actions.do_clip_note_action(clip_action[0], t, xclip, ident, args) elif action_name.startswith('CLIP'): - self._clip_actions.set_clip_on_off(clip_action[0], t, xclip, ident, args) + self.clip_actions.set_clip_on_off(clip_action[0], t, xclip, ident, args) elif action_name.startswith('DR'): dr = self.get_drum_rack_to_operate_on(t) arg = args.split()[0] if dr and args: if arg in DR_ACTIONS: - getattr(self._dr_actions, DR_ACTIONS[arg])(dr, t, xclip, ident, args.strip()) + DR_ACTIONS[arg](self.dr_actions, dr, t, xclip, ident, args.strip()) elif 'PAD' in args: - self._dr_actions.dispatch_pad_action(dr, t, xclip, ident, args.strip()) - elif action_name in self._user_actions._action_dict: - user_action = self._user_actions._action_dict[action_name] - getattr(self._user_actions, user_action)(t, args) + self.dr_actions.dispatch_pad_action(dr, t, xclip, ident, args.strip()) + elif action_name in self.user_actions._action_dict: + user_action = self.user_actions._action_dict[action_name] + getattr(self.user_actions, user_action)(t, args) log.debug('action_dispatch triggered, ident=%s and track(s)=%s and action=%s and args=%s', ident, self.track_list_to_string(tracks), action_name, args) @@ -230,10 +230,10 @@ def handle_action_list_trigger(self, track, xtrigger): if name and name[0] == '[' and ']' in name: # snap action, so pass directly to snap component if ' || (' in name and isinstance(xtrigger, Live.Clip.Clip) and xtrigger.is_playing: - self._snap_actions.recall_track_snapshot(name, xtrigger) + self.snap_actions.recall_track_snapshot(name, xtrigger) # control reassignment, so pass directly to control component elif '[[' in name and ']]' in name: - self._control_component.assign_new_actions(name) + self.control_component.assign_new_actions(name) # standard trigger else: ident = name[name.index('['):name.index(']')+1].strip() @@ -368,8 +368,7 @@ def format_action_name(self, origin_track, origin_name): ('-' in result_name[:4] and '/' in result_name[4:]) or (result_name[0] == '"' and '"' in result_name[1:]) ): - track_data = self.get_track_to_operate_on(result_name) - result_track, result_name = track_data[0:2] + result_track, result_name = self.get_track_to_operate_on(result_name) args = '' name = result_name.split() if len(name) > 1: @@ -377,7 +376,7 @@ def format_action_name(self, origin_track, origin_name): result_name = result_name.replace(args, '') log.debug('format_action_name returning, track(s)=%s and action=%s and args=%s', self.track_list_to_string(result_track), result_name.strip(), args.strip()) - return {'track': result_track, 'action': result_name.strip(), 'args': args.strip()} + return dict(track=result_track, action=result_name.strip(), args=args.strip()) def handle_loop_seq_action_list(self, xclip, count): '''Handles sequenced action lists, triggered by xclip looping. @@ -525,8 +524,8 @@ def get_track_to_operate_on(self, origin_name): return (result_tracks, result_name) def get_track_index_by_name(self, name, tracks): - '''Gets the index(es) associated with the track name(s) specified - in name. + '''Gets the index(es) associated with the track name(s) + specified in name. ''' while '"' in name: track_name = name[name.index('"')+1:] @@ -535,8 +534,8 @@ def get_track_index_by_name(self, name, tracks): track_index = '' def_name = '' if ' AUDIO' in track_name or ' MIDI' in track_name: - # in Live GUI, default names are 'n Audio' or 'n MIDI', - # in API it's 'n-Audio' or 'n-MIDI' + # in Live GUI, default names are 'n Audio' or + # 'n MIDI', in API it's 'n-Audio' or 'n-MIDI' def_name = track_name.replace(' ', '-') for track in tracks: current_track_name = self.get_name(track.name) @@ -640,19 +639,15 @@ def get_user_settings(self, midi_map_handle): from text file and perform startup actions if any. ''' import sys + import os list_to_build = None ctrl_data = [] try: - mrs_path = '' - for path in sys.path: - if 'MIDI Remote Scripts' in path: - mrs_path = path - break - user_file = '{}{}UserSettings.txt'.format(mrs_path, FOLDER) + filepath = get_base_path('UserSettings.txt') if not self._user_settings_logged: - log.info('Attempting to read UserSettings file: %s', user_file) - for line in open(user_file): + log.info('Attempting to read UserSettings file: %s', filepath) + for line in open(filepath): line = self.get_name(line.rstrip('\n')).strip() if not line: continue @@ -683,13 +678,13 @@ def get_user_settings(self, midi_map_handle): self.enable_push_emulation(self._control_surfaces()) elif line.startswith('INCLUDE_NESTED_DEVICES_IN_SNAPSHOTS ='): include_nested = self.get_name(line[37:].strip()) - self._snap_actions._include_nested_devices = include_nested.startswith('ON') + self.snap_actions._include_nested_devices = include_nested.startswith('ON') elif line.startswith('SNAPSHOT_PARAMETER_LIMIT ='): try: limit = int(line[26:].strip()) except: limit = 500 - self._snap_actions._parameter_limit = limit + self.snap_actions._parameter_limit = limit elif line.startswith('PROCESS_XCLIPS_IF_TRACK_MUTED ='): self._process_xclips_if_track_muted = line.split('=')[1].strip() == 'TRUE' elif line.startswith('STARTUP_ACTIONS =') and not self._startup_actions_complete: @@ -699,9 +694,9 @@ def get_user_settings(self, midi_map_handle): self.schedule_message(2, partial(self.perform_startup_actions, action_list)) self._startup_actions_complete = True elif line.startswith('CSLINKER'): - self._cs_linker.parse_settings(line) + self.cs_linker.parse_settings(line) if ctrl_data: - self._control_component.get_user_control_settings(ctrl_data, midi_map_handle) + self.control_component.get_user_control_settings(ctrl_data, midi_map_handle) except: pass @@ -713,16 +708,11 @@ def enable_push_emulation(self, scripts): for script in scripts: script_name = script.__class__.__name__ if script_name == 'Push': - if True: - with script._component_guard(): - script._start_handshake_task = MockHandshakeTask() - script._handshake = MockHandshake() - if self._PushApcCombiner: - self._PushApcCombiner.set_up_scripts(self._control_surfaces()) - # legacy <9.5 code - # else: - # script._handshake._identification_timeout_task.kill() - # script._handshake._identification_timeout_task = Task.Task() + with script._component_guard(): + script._start_handshake_task = MockHandshakeTask() + script._handshake = MockHandshake() + if self._PushApcCombiner: + self._PushApcCombiner.set_up_scripts(self._control_surfaces()) break def start_debugging(self): @@ -730,19 +720,19 @@ def start_debugging(self): Live's log file to assist in troubleshooting. ''' self._is_debugging = True - log.info('------- ClyphX Log: Logging User Variables -------') - for key, value in iteritems(self._user_variables): + log.info('------- Logging User Variables -------') + for key, value in self._user_variables.items(): log.info('%s=%s', key, value) - log.info('------- ClyphX Log: Logging User Controls -------') - for key, value in iteritems(self._control_component._control_list): + log.info('------- Logging User Controls -------') + for key, value in self.control_component._control_list.items(): log.info('%s on_action=%s and off_action=%s', key, value['on_action'], value['off_action']) - log.info('------- ClyphX Log: Logging User Actions -------') - for key, value in iteritems(self._user_actions._action_dict): + log.info('------- Logging User Actions -------') + for key, value in self.user_actions._action_dict.items(): log.info('%s=%s', key, value) - log.info('------- ClyphX Log: Debugging Started -------') + log.info('------- Debugging Started -------') def track_list_to_string(self, track_list): '''Convert list of tracks to a string of track names or None if @@ -769,13 +759,13 @@ def setup_tracks(self): call Macrobat's get rack. ''' for t in self.song().tracks: - self._macrobat.setup_tracks(t) - if not (self._current_tracks and t in self._current_tracks): - self._current_tracks.append(t) + self.macrobat.setup_tracks(t) + if not (self.current_tracks and t in self.current_tracks): + self.current_tracks.append(t) XTrackComponent(self, t) for r in chain(self.song().return_tracks, (self.song().master_track,)): - self._macrobat.setup_tracks(r) - self._snap_actions.setup_tracks() + self.macrobat.setup_tracks(r) + self.snap_actions.setup_tracks() def get_name(self, name): '''Convert name to upper-case string or return blank string if @@ -788,23 +778,23 @@ def get_name(self, name): return name def _on_track_list_changed(self): - super(ClyphX, self)._on_track_list_changed() + super()._on_track_list_changed() self.setup_tracks() def connect_script_instances(self, instantiated_scripts): '''Pass connect scripts call to control component.''' - self._control_component.connect_script_instances(instantiated_scripts) - self._control_surface_actions.connect_script_instances(instantiated_scripts) + self.control_component.connect_script_instances(instantiated_scripts) + self.cs_actions.connect_script_instances(instantiated_scripts) if self._push_emulation: self.enable_push_emulation(instantiated_scripts) def build_midi_map(self, midi_map_handle): - '''Build user-defined list of midi messages for controlling + '''Build user-defined list of MIDI messages for controlling ClyphX track. ''' - super(ClyphX, self).build_midi_map(midi_map_handle) + super().build_midi_map(midi_map_handle) if self._user_settings_logged: - self._control_component.rebuild_control_map(midi_map_handle) + self.control_component.rebuild_control_map(midi_map_handle) else: self.get_user_settings(midi_map_handle) self._user_settings_logged = True @@ -814,8 +804,8 @@ def build_midi_map(self, midi_map_handle): def receive_midi(self, midi_bytes): '''Receive user-specified messages and send to control script. ''' - super(ClyphX, self).receive_midi(midi_bytes) - self._control_component.receive_midi(midi_bytes) + super().receive_midi(midi_bytes) + self.control_component.receive_midi(midi_bytes) def handle_sysex(self, midi_bytes): '''Handle sysex received from controller.''' diff --git a/src/clyphx/consts.py b/src/clyphx/consts.py index 79fb522..1272784 100644 --- a/src/clyphx/consts.py +++ b/src/clyphx/consts.py @@ -14,10 +14,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with ClyphX. If not, see . -# NOTE: Action names and their corresponding values can't contain a '/' or '-' -# within the first four chars like this 'EX/ONE', but 'EXMP/ONE' is okay. - from __future__ import unicode_literals +from builtins import map, dict import Live @@ -30,332 +28,191 @@ raise RuntimeError('Live releases earlier than 9.5 are not supported') -GLOBAL_ACTIONS = { - 'ASN': 'do_variable_assignment', - 'ADDAUDIO': 'create_audio_track', - 'ADDMIDI': 'create_midi_track', - 'INSAUDIO': 'insert_and_configure_audio_track', - 'INSMIDI': 'insert_and_configure_midi_track', - 'ADDRETURN': 'create_return_track', - 'ADDSCENE': 'create_scene', - 'DELSCENE': 'delete_scene', - 'DUPESCENE': 'duplicate_scene', - 'LOADDEV': 'load_device', - 'LOADM4L': 'load_m4l', - 'SWAP': 'swap_device_preset', - 'SREC': 'set_session_record', - 'SRECFIX': 'trigger_session_record', - 'SATM': 'set_session_automation_record', - 'B2A': 'set_back_to_arrange', - 'RPT': 'set_note_repeat', - 'SWING': 'adjust_swing', - 'BPM': 'adjust_tempo', - 'DEVFIRST': 'move_to_first_device', - 'DEVLAST': 'move_to_last_device', - 'DEVLEFT': 'move_to_prev_device', - 'DEVRIGHT': 'move_to_next_device', - 'FOCBRWSR': 'focus_browser', - 'FOCDETAIL': 'focus_detail', - 'FOCMAIN': 'focus_main', - 'GQ': 'adjust_global_quantize', - 'GRV': 'adjust_groove', - 'HZOOM': 'adjust_horizontal_zoom', - 'VZOOM': 'adjust_vertical_zoom', - 'UP': 'move_up', - 'DOWN': 'move_down', - 'LEFT': 'move_left', - 'RIGHT': 'move_right', - 'LOOP': 'do_loop_action', - 'LOC': 'do_locator_action', - 'LOCLOOP': 'do_locator_loop_action', - 'METRO': 'set_metronome', - 'MIDI': 'send_midi_message', - 'OVER': 'set_overdub', - 'PIN': 'set_punch_in', - 'POUT': 'set_punch_out', - 'REC': 'set_record', - 'REDO': 'set_redo', - 'UNDO': 'set_undo', - 'RESTART': 'restart_transport', - 'RQ': 'adjust_record_quantize', - 'RTRIG': 'retrigger_recording_clips', - 'SIG': 'adjust_time_signature', - 'SCENE': 'set_scene', - 'SHOWCLIP': 'show_clip_view', - 'SHOWDEV': 'show_track_view', - 'SHOWDETAIL': 'show_detail_view', - 'TGLBRWSR': 'toggle_browser', - 'TGLDETAIL': 'toggle_detail_view', - 'TGLMAIN': 'toggle_main_view', - 'STOPALL': 'set_stop_all', - 'SETCONT': 'set_continue_playback', - 'SETLOC': 'set_locator', - 'SETSTOP': 'set_stop_transport', - 'SETFOLD': 'set_fold_all', - 'SETJUMP': 'set_jump_all', - 'TAPBPM': 'set_tap_tempo', - 'UNARM': 'set_unarm_all', - 'UNMUTE': 'set_unmute_all', - 'UNSOLO': 'set_unsolo_all', - 'MAKE_DEV_DOC': 'make_instant_mapping_docs', -} - -TRACK_ACTIONS = { - 'ARM': 'set_arm', - 'MUTE': 'set_mute', - 'SOLO': 'set_solo', - 'MON': 'set_monitor', - 'XFADE': 'set_xfade', - 'SEL': 'set_selection', - 'ADDCLIP': 'create_clip', - 'DEL': 'delete_track', - 'DELDEV': 'delete_device', - 'DUPE': 'duplicate_track', - 'FOLD': 'set_fold', - 'PLAY': 'set_play', - 'PLAYL': 'set_play_w_legato', - 'PLAYQ': 'set_play_w_force_qntz', - 'PLAYLQ': 'set_play_w_force_qntz_and_legato', - 'STOP': 'set_stop', - 'JUMP': 'set_jump', - 'VOL': 'adjust_volume', - 'PAN': 'adjust_pan', - 'SEND': 'adjust_sends', - 'CUE': 'adjust_preview_volume', - 'XFADER': 'adjust_crossfader', - 'IN': 'adjust_input_routing', - 'INSUB': 'adjust_input_sub_routing', - 'OUT': 'adjust_output_routing', - 'OUTSUB': 'adjust_output_sub_routing', - 'NAME': 'set_name', - 'RENAMEALL': 'rename_all_clips', -} - -CLIP_ACTIONS = { - 'CENT': 'adjust_detune', - 'SEMI': 'adjust_transpose', - 'GAIN': 'adjust_gain', - 'CUE': 'adjust_cue_point', - 'END': 'adjust_end', - 'START': 'adjust_start', - 'GRID': 'adjust_grid_quantization', - 'TGRID': 'set_triplet_grid', - 'ENVINS': 'insert_envelope', - 'ENVCLR': 'clear_envelope', - 'ENVCAP': 'capture_to_envelope', - 'ENVSHOW': 'show_envelope', - 'ENVHIDE': 'hide_envelopes', - 'QNTZ': 'quantize', - 'EXTEND': 'duplicate_clip_content', - 'DEL': 'delete_clip', - 'DUPE': 'duplicate_clip', - 'CHOP': 'chop_clip', - 'SPLIT': 'split_clip', - 'WARPMODE': 'adjust_warp_mode', - 'LOOP': 'do_clip_loop_action', - 'SIG': 'adjust_time_signature', - 'WARP': 'set_warp', - 'NAME': 'set_clip_name'} - -DEVICE_ACTIONS = { - 'CSEL': 'adjust_selected_chain', - 'CS': 'adjust_chain_selector', - 'RESET': 'reset_params', - 'RND': 'randomize_params', - 'SEL': 'select_device', - 'SET': 'set_all_params', - 'P1': 'adjust_best_of_bank_param', - 'P2': 'adjust_best_of_bank_param', - 'P3': 'adjust_best_of_bank_param', - 'P4': 'adjust_best_of_bank_param', - 'P5': 'adjust_best_of_bank_param', - 'P6': 'adjust_best_of_bank_param', - 'P7': 'adjust_best_of_bank_param', - 'P8': 'adjust_best_of_bank_param', - 'B1': 'adjust_banked_param', - 'B2': 'adjust_banked_param', - 'B3': 'adjust_banked_param', - 'B4': 'adjust_banked_param', - 'B5': 'adjust_banked_param', - 'B6': 'adjust_banked_param', - 'B7': 'adjust_banked_param', - 'B8': 'adjust_banked_param', -} - -DR_ACTIONS = { - 'SCROLL': 'scroll_selector', - 'UNMUTE': 'unmute_all', - 'UNSOLO': 'unsolo_all' -} - -LOOPER_ACTIONS = { - 'LOOPER': 'set_looper_on_off', - 'REV': 'set_looper_rev', - 'OVER': 'set_looper_state', - 'PLAY': 'set_looper_state', - 'REC': 'set_looper_state', - 'STOP': 'set_looper_state', -} - -KEYWORDS = {'ON': 1, 'OFF': 0} +SCRIPT_NAME = 'ClyphX' -NOTE_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] -OCTAVE_NAMES = ['-2', '-1', '0', '1', '2', '3', '4', '5', '6', '7', '8'] +SCRIPT_VERSION = (2, 7, 0) -GQ_STATES = { - 'NONE': 0, - '8 BARS': 1, - '4 BARS': 2, - '2 BARS': 3, - '1 BAR': 4, - '1/2': 5, - '1/2T': 6, - '1/4': 7, - '1/4T': 8, - '1/8': 9, - '1/8T': 10, - '1/16': 11, - '1/16T': 12, - '1/32': 13, -} +SCRIPT_INFO = '{} v{}'.format(SCRIPT_NAME, '.'.join(map(str, SCRIPT_VERSION))) -RQ_STATES = { - 'NONE': 0, - '1/4': 1, - '1/8': 2, - '1/8T': 3, - '1/8 + 1/8T': 4, - '1/16': 5, - '1/16T': 6, - '1/16 + 1/16T': 7, - '1/32': 8, -} +KEYWORDS = dict(ON=1, OFF=0) -XFADE_STATES = { - 'A': 0, - 'OFF': 1, - 'B': 2, -} +NOTE_NAMES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') -MON_STATES = { - 'IN': 0, - 'AUTO': 1, - 'OFF': 2, -} +OCTAVE_NAMES = ('-2', '-1', '0', '1', '2', '3', '4', '5', '6', '7', '8') -LOOPER_STATES = { - 'STOP': 0.0, - 'REC': 1.0, - 'PLAY': 2.0, - 'OVER': 3.0, -} - - -R_QNTZ_STATES = { - '1/4': Live.Song.RecordingQuantization.rec_q_quarter, - '1/8': Live.Song.RecordingQuantization.rec_q_eight, - '1/8T': Live.Song.RecordingQuantization.rec_q_eight_triplet, - '1/8 + 1/8T': Live.Song.RecordingQuantization.rec_q_eight_eight_triplet, - '1/16': Live.Song.RecordingQuantization.rec_q_sixtenth, - '1/16T': Live.Song.RecordingQuantization.rec_q_sixtenth_triplet, - '1/16 + 1/16T': Live.Song.RecordingQuantization.rec_q_sixtenth_sixtenth_triplet, - '1/32': Live.Song.RecordingQuantization.rec_q_thirtysecond, -} - -CLIP_GRID_STATES = { - 'OFF': Live.Clip.GridQuantization.no_grid, - '8 BARS': Live.Clip.GridQuantization.g_8_bars, - '4 BARS': Live.Clip.GridQuantization.g_4_bars, - '2 BARS': Live.Clip.GridQuantization.g_2_bars, - '1 BAR': Live.Clip.GridQuantization.g_bar, - '1/2': Live.Clip.GridQuantization.g_half, - '1/4': Live.Clip.GridQuantization.g_quarter, - '1/8': Live.Clip.GridQuantization.g_eighth, - '1/16': Live.Clip.GridQuantization.g_sixteenth, - '1/32': Live.Clip.GridQuantization.g_thirtysecond, -} - -REPEAT_STATES = { - 'OFF': 1.0, - '1/4': 1.0, - '1/4T': 0.666666666667, - '1/8': 0.5, - '1/8T': 0.333333333333, - '1/16': 0.25, - '1/16T': 0.166666666667, - '1/32': 0.125, - '1/32T': 0.0833333333333, -} +ENV_TYPES = ('IRAMP', 'DRAMP', 'IPYR', 'DPYR', 'SQR', 'SAW') # TODO: mode 5? -WARP_MODES = { - 'BEATS': 0, - 'TONES': 1, - 'TEXTURE': 2, - 'RE-PITCH': 3, - 'COMPLEX': 4, - 'COMPLEX PRO': 6, -} - -AUDIO_DEVS = { - 'SIMPLE DELAY': 'Simple Delay', - 'OVERDRIVE': 'Overdrive', - 'LOOPER': 'Looper', - 'AUTO FILTER': 'Auto Filter', - 'EXTERNAL AUDIO EFFECT': 'External Audio Effect', - 'SATURATOR': 'Saturator', - 'PHASER': 'Phaser', - 'VINYL DISTORTION': 'Vinyl Distortion', - 'DYNAMIC TUBE': 'Dynamic Tube', - 'BEAT REPEAT': 'Beat Repeat', - 'MULTIBAND DYNAMICS': 'Multiband Dynamics', - 'CABINET': 'Cabinet', - 'AUDIO EFFECT RACK': 'Audio Effect Rack', - 'FLANGER': 'Flanger', - 'GATE': 'Gate', - 'REVERB': 'Reverb', - 'GRAIN DELAY': 'Grain Delay', - 'REDUX': 'Redux', - 'PING PONG DELAY': 'Ping Pong Delay', - 'SPECTRUM': 'Spectrum', - 'COMPRESSOR': 'Compressor', - 'VOCODER': 'Vocoder', - 'AMP': 'Amp', - 'GLUE COMPRESSOR': 'Glue Compressor', - 'EROSION': 'Erosion', - 'EQ THREE': 'EQ Three', - 'EQ EIGHT': 'EQ Eight', - 'RESONATORS': 'Resonators', - 'FREQUENCY SHIFTER': 'Frequency Shifter', - 'AUTO PAN': 'Auto Pan', - 'CHORUS': 'Chorus', - 'LIMITER': 'Limiter', - 'CORPUS': 'Corpus', - 'FILTER DELAY': 'Filter Delay', - 'UTILITY': 'Utility', -} - -INS_DEVS = { - 'TENSION': 'Tension', - 'EXTERNAL INSTRUMENT': 'External Instrument', - 'ELECTRIC': 'Electric', - 'INSTRUMENT RACK': 'Instrument Rack', - 'DRUM RACK': 'Drum Rack', - 'COLLISION': 'Collision', - 'IMPULSE': 'Impulse', - 'SAMPLER': 'Sampler', - 'OPERATOR': 'Operator', - 'ANALOG': 'Analog', - 'SIMPLER': 'Simpler', -} - -MIDI_DEVS = { - 'NOTE LENGTH': 'Note Length', - 'CHORD': 'Chord', - 'RANDOM': 'Random', - 'MIDI EFFECT RACK': 'MIDI Effect Rack', - 'SCALE': 'Scale', - 'PITCH': 'Pitch', - 'ARPEGGIATOR': 'Arpeggiator', - 'VELOCITY': 'Velocity', -} +WARP_MODES = dict(( + ('BEATS', 0), + ('TONES', 1), + ('TEXTURE', 2), + ('RE-PITCH', 3), + ('COMPLEX', 4), + ('COMPLEX PRO', 6), +)) + +# region STATES +GQ_STATES = dict(( + ('NONE', 0), + ('8 BAR', 1), + ('4 BAR', 2), + ('2 BAR', 3), + ('1 BAR', 4), + ('1/2', 5), + ('1/2T', 6), + ('1/4', 7), + ('1/4T', 8), + ('1/8', 9), + ('1/8T', 10), + ('1/16', 11), + ('1/16T', 12), + ('1/32', 13), + # alias + ('8 BARS', 1), + ('4 BARS', 2), + ('2 BARS', 3), +)) + +RQ_STATES = dict(( + ('NONE', 0), + ('1/4', 1), + ('1/8', 2), + ('1/8T', 3), + ('1/8 + 1/8T', 4), + ('1/16', 5), + ('1/16T', 6), + ('1/16 + 1/16T', 7), + ('1/32', 8), +)) + +XFADE_STATES = dict( + A = 0, + OFF = 1, + B = 2, +) + +MON_STATES = dict( + IN = 0, + AUTO = 1, + OFF = 2, +) + +LOOPER_STATES = dict( + STOP = 0.0, + REC = 1.0, + PLAY = 2.0, + OVER = 3.0, +) + +R_QNTZ_STATES = dict(( + ('1/4', Live.Song.RecordingQuantization.rec_q_quarter), + ('1/8', Live.Song.RecordingQuantization.rec_q_eight), + ('1/8T', Live.Song.RecordingQuantization.rec_q_eight_triplet), + ('1/8 + 1/8T', Live.Song.RecordingQuantization.rec_q_eight_eight_triplet), + ('1/16', Live.Song.RecordingQuantization.rec_q_sixtenth), + ('1/16T', Live.Song.RecordingQuantization.rec_q_sixtenth_triplet), + ('1/16 + 1/16T', Live.Song.RecordingQuantization.rec_q_sixtenth_sixtenth_triplet), + ('1/32', Live.Song.RecordingQuantization.rec_q_thirtysecond), +)) + +CLIP_GRID_STATES = dict(( + ('OFF', Live.Clip.GridQuantization.no_grid), + ('8 BAR', Live.Clip.GridQuantization.g_8_bars), + ('4 BAR', Live.Clip.GridQuantization.g_4_bars), + ('2 BAR', Live.Clip.GridQuantization.g_2_bars), + ('1 BAR', Live.Clip.GridQuantization.g_bar), + ('1/2', Live.Clip.GridQuantization.g_half), + ('1/4', Live.Clip.GridQuantization.g_quarter), + ('1/8', Live.Clip.GridQuantization.g_eighth), + ('1/16', Live.Clip.GridQuantization.g_sixteenth), + ('1/32', Live.Clip.GridQuantization.g_thirtysecond), + # alias + ('8 BARS', Live.Clip.GridQuantization.g_8_bars), + ('4 BARS', Live.Clip.GridQuantization.g_4_bars), + ('2 BARS', Live.Clip.GridQuantization.g_2_bars), +)) + +REPEAT_STATES = dict(( + ('OFF', 1.0), + ('1/4', 1.0), + ('1/4T', 0.666666666667), + ('1/8', 0.5), + ('1/8T', 0.333333333333), + ('1/16', 0.25), + ('1/16T', 0.166666666667), + ('1/32', 0.125), + ('1/32T', 0.0833333333333), +)) +# endregion + +# region DEVICES +_AUDIO_DEVS = ( + 'Simple Delay', + 'Overdrive', + 'Looper', + 'Auto Filter', + 'External Audio Effect', + 'Saturator', + 'Phaser', + 'Vinyl Distortion', + 'Dynamic Tube', + 'Beat Repeat', + 'Multiband Dynamics', + 'Cabinet', + 'Audio Effect Rack', + 'Flanger', + 'Gate', + 'Reverb', + 'Grain Delay', + 'Redux', + 'Ping Pong Delay', + 'Spectrum', + 'Compressor', + 'Vocoder', + 'Amp', + 'Glue Compressor', + 'Erosion', + 'EQ Three', + 'EQ Eight', + 'Resonators', + 'Frequency Shifter', + 'Auto Pan', + 'Chorus', + 'Limiter', + 'Corpus', + 'Filter Delay', + 'Utility', +) + +_INS_DEVS = ( + 'Tension', + 'External Instrument', + 'Electric', + 'Instrument Rack', + 'Drum Rack', + 'Collision', + 'Impulse', + 'Sampler', + 'Operator', + 'Analog', + 'Simpler', +) + +_MIDI_DEVS = ( + 'Note Length', + 'Chord', + 'Random', + 'MIDI Effect Rack', + 'Scale', + 'Pitch', + 'Arpeggiator', + 'Velocity', +) + +AUDIO_DEVS = dict((dev.upper(), dev) for dev in _AUDIO_DEVS) + +INS_DEVS = dict((dev.upper(), dev) for dev in _INS_DEVS) + +MIDI_DEVS = dict((dev.upper(), dev) for dev in _MIDI_DEVS) +# endregion diff --git a/src/clyphx/core/__init__.py b/src/clyphx/core/__init__.py new file mode 100644 index 0000000..12b3b9e --- /dev/null +++ b/src/clyphx/core/__init__.py @@ -0,0 +1,9 @@ +from __future__ import absolute_import, unicode_literals + +from .xcomponent import ( + ControlSurfaceComponent, + SessionComponent, + XComponent, +) + +from .parse import UserSettings diff --git a/src/clyphx/core/compat.py b/src/clyphx/core/compat.py new file mode 100644 index 0000000..d0db3e5 --- /dev/null +++ b/src/clyphx/core/compat.py @@ -0,0 +1,20 @@ +""" +Python 2.7/3 compatibility module. +""" +from __future__ import unicode_literals +from builtins import super + +import sys +import abc +from abc import abstractmethod + +try: + # the raven package is vendorized in Ableton + from raven.utils.six import iteritems, with_metaclass +except ImportError: + from six import iteritems + +if sys.version_info.major == 3: + ABC = abc.ABC +else: + ABC = abc.ABCMeta(b'ABC', (object,), {b'__slots__': ()}) diff --git a/src/clyphx/core/models.py b/src/clyphx/core/models.py new file mode 100644 index 0000000..fb3b165 --- /dev/null +++ b/src/clyphx/core/models.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import, unicode_literals + +from collections import namedtuple +from .utils import repr + + +Action = namedtuple('Action', ['track', 'actions', 'args']) + + +Command = namedtuple('Command', ['id', 'seq', 'start', 'stop']) + + +class UserControl(object): + ''' + + Args: + - name (str): A unique one-word identifier for the control. + - type (str): Message type: 'NOTE' or 'CC'. + - channel (str): MIDI channel (1 - 16). + - value (str): Note or CC value (0 - 127). + - actions (str, optional): Action List to perform when the control + sends an on. + ''' + __slots__ = ('name', 'type', 'channel', 'value', 'actions') + + def __init__(self, name, type, channel, value, actions=None): + self.name = name + self.type = type.lower() + self.channel = int(channel) + self.value = int(value) + # TODO: parse actions + self.actions = actions + self._validate() + + def _validate(self): + # TODO: check valid identifier + + if self.type not in {'note', 'cc'}: + raise ValueError("Message type must be 'NOTE' or 'CC'") + + if not (1 <= self.channel <= 16): + raise ValueError('MIDI channel must be an integer between 1 and 16') + + if not (0 <= self.value <= 127): + raise ValueError('NOTE or CC must be an integer between 0 and 127') + + __repr__ = repr diff --git a/src/clyphx/core.py b/src/clyphx/core/parse.py similarity index 60% rename from src/clyphx/core.py rename to src/clyphx/core/parse.py index e415e46..a06d09b 100644 --- a/src/clyphx/core.py +++ b/src/clyphx/core/parse.py @@ -1,18 +1,20 @@ from __future__ import absolute_import, unicode_literals, with_statement +from builtins import map import re import os import logging -from collections import namedtuple -log = logging.getLogger(__name__) - -Action = namedtuple('Action', ['track', 'actions', 'args']) +from .compat import ABC, abstractmethod, with_metaclass +from .models import Command, UserControl +from .utils import get_base_path -Command = namedtuple('Command', ['id', 'seq', 'start', 'stop']) +log = logging.getLogger(__name__) class Parser(object): + '''Command parser. + ''' command = re.compile(r'\[(?P[a-zA-Z0-9\_]*?)\]\s*?' r'(?P\([a-zA-Z]*?\))?\s*?' r'(?P\S.*?)\s*?$') @@ -35,50 +37,43 @@ def __call__(self, text): return Command(**cmd) -class UserControl(object): - ''' - Args: - - name (str): A unique one-word identifier for the control. - - type (str): Message type: 'NOTE' or 'CC'. - - channel (str): MIDI channel (1 - 16). - - value (str): Note or CC value (0 - 127). - - actions (str, optional): Action List to perform when the control - sends an on. - ''' - __slots__ = ('name', 'type', 'channel', 'value', 'actions') - - def __init__(self, name, type, channel, value, actions=None): - self.name = name - self.type = type.lower() - self.channel = int(channel) - self.value = int(value) - # TODO: parse actions - self.actions = actions - self._validate() - - def _validate(self): - # TODO: check valid identifier - - if self.type not in {'note', 'cc'}: - raise ValueError("Message type must be 'NOTE' or 'CC'") - - if not (1 <= self.channel <= 16): - raise ValueError('MIDI channel must be an integer between 1 and 16') - - if not (0 <= self.value <= 127): - raise ValueError('NOTE or CC must be an integer between 0 and 127') - - def __repr__(self): - # TODO: import from utils - return '{}({})'.format( - type(self).__name__, - ', '.join('{0}={1}'.format(k, getattr(self, k)) - for k in self.__slots__), - ) +# class Settings(ABC): +# '''Base class for settings file parsers. +# ''' +# @classmethod +# def from_file(cls, filepath): +# SECTIONS = r'^\*+?\s?([^\*]*?)\s?\*+\s*?$((?!^\*).*?)(?=\n\*|\Z)' +# CONTENT = re.compile(r'^(?! )[^\*#\n"]+$') + +# self = cls() + +# for m in re.findall(SECTIONS, open(filepath).read(), re.M | re.S): +# section, content = m +# try: +# method = self.methods[section] +# method(CONTENT.findall(content, re.M)) +# except KeyError: +# log.debug('unknown section in %s: %s', filepath, section) + +# return self + +# def _split_lines(self, content, lower=True): +# for line in map(str.lower if lower else str, content): +# try: +# yield tuple(x.strip() for x in line.split('=')) +# except Exception as e: +# log.error('Error parsing setting: %s (%s)', line, e) + +# # # TODO: make Pyy3 compatible +# # class __metaclass__(ABC): +# # @property +# # def methods(cls): +# # raise NotImplementedError class UserSettings(object): + def __init__(self): self.vars = dict() self.controls = dict() @@ -89,23 +84,22 @@ def __init__(self): def _parse_file(self): SECTIONS = r'^\*+?\s?([^\*]*?)\s?\*+\s*?$((?!^\*).*?)(?=\n\*|\Z)' - CONTENT = r'^(?! )[^\*#\n"]+$' - METHODS = { - '[USER VARIABLES]': self._user_vars, - '[USER CONTROLS]': self._user_controls, - '[EXTRA PREFS]': self._extra_prefs, - '[SNAPSHOT SETTINGS]': self._snapshot_settings, - '[CSLINKER]': self._cs_linker, - } - - folder = os.path.dirname(os.path.realpath(__file__)) - filepath = os.path.join(folder, 'UserSettings.txt') + CONTENT = re.compile(r'^(?! )[^\*#\n"]+$') + METHODS = dict(( + ('[USER VARIABLES]', self._user_vars), + ('[USER CONTROLS]', self._user_controls), + ('[EXTRA PREFS]', self._extra_prefs), + ('[SNAPSHOT SETTINGS]', self._snapshot_settings), + ('[CSLINKER]', self._cs_linker), + )) + + filepath = get_base_path('UserSettings.txt') for m in re.findall(SECTIONS, open(filepath).read(), re.M | re.S): section, content = m try: method = METHODS[section] - method(re.findall(CONTENT, content, re.M)) + method(CONTENT.findall(content, re.M)) except KeyError: pass @@ -118,7 +112,7 @@ def _split_lines(self, content, lower=True): def _user_vars(self, content): # TODO - self.vars = {k: v for k, v in self._split_lines(content, False)} + self.vars = dict((k, v) for k, v in self._split_lines(content, False)) def _user_controls(self, content): CONTROL = re.compile(r'^(?P[^,]*?)\s*?,\s*?' @@ -179,32 +173,3 @@ def _cs_linker(self, content): self.cs_linker[k] = v if v != 'none' else None else: log.warning('CS Linker setting unknown: %s = %s', k, v) - - -from _Framework.ControlSurfaceComponent import ControlSurfaceComponent - -class XComponent(ControlSurfaceComponent): - '''Control Surface base component. - ''' - def __init__(self, parent): - super(XComponent, self).__init__() - self._parent = parent - - def disconnect(self): - '''Called by the control surface on disconnect (app closed, - script closed). - ''' - self._parent = None - super(XComponent, self).disconnect() - - def on_enabled_changed(self): - '''Called when this script is enabled/disabled (by calling - set_enabled on it). - ''' - pass - - def update(self): - '''Called by the control surface on instantiation and in other - cases such as when exiting MIDI map mode. - ''' - pass diff --git a/src/clyphx/core/utils.py b/src/clyphx/core/utils.py new file mode 100644 index 0000000..d15623d --- /dev/null +++ b/src/clyphx/core/utils.py @@ -0,0 +1,51 @@ +import os +import logging +from collections import OrderedDict + +from .compat import iteritems + +log = logging.getLogger(__name__) + + +def get_python_info(): + '''Returns info about the Live Python runtime. + ''' + import sys + + version_info = OrderedDict((k, getattr(sys.version_info, k)) for k in + ('major', 'minor', 'micro', 'releaselevel', 'serial')) + + modules = OrderedDict((k, {'module': getattr(v, '__name__', None), + 'file': getattr(v, '__file__', None)}) + for k, v in sorted(iteritems(sys.modules))) + + return OrderedDict([ + ('version', sys.version), + ('version_info', version_info), + ('path', sys.path), + ('executable', sys.executable), + #('dllhandle', sys.dllhandle), + ('prefix', sys.prefix), + ('exec_prefix', sys.exec_prefix), + ('builtin_modules', sys.builtin_module_names), + ('modules', modules), + ]) + + +def show_python_info(): + import json + + log.info(json.dumps(get_python_info(), indent=4)) + + +def repr(self): + return '{}({})'.format( + type(self).__name__, + ', '.join('{}={}'.format(k, getattr(self, k)) + for k in self.__slots__), + ) + + +def get_base_path(*items): + here = os.path.dirname(os.path.realpath(__file__)) + return os.path.join(here, os.pardir, *items) diff --git a/src/clyphx/core/xcomponent.py b/src/clyphx/core/xcomponent.py new file mode 100644 index 0000000..5dc2922 --- /dev/null +++ b/src/clyphx/core/xcomponent.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import +from .compat import super + +from _Framework.ControlSurfaceComponent import ControlSurfaceComponent +from _Framework.SessionComponent import SessionComponent + + +class XComponent(ControlSurfaceComponent): + '''Control Surface base component. + ''' + def __init__(self, parent): + super().__init__() + self._parent = parent + + def disconnect(self): + '''Called by the control surface on disconnect (app closed, + script closed). + ''' + self._parent = None + super().disconnect() + + def on_enabled_changed(self): + '''Called when this script is enabled/disabled (by calling + set_enabled on it). + ''' + pass + + def _on_enabled_changed(self): + super().update() + + def update(self): + '''Called by the control surface on instantiation and in other + cases such as when exiting MIDI map mode. + ''' + pass + + def _update(self): + super().update() diff --git a/src/clyphx/cs_linker.py b/src/clyphx/cs_linker.py index cbde4fa..e8fcc7b 100644 --- a/src/clyphx/cs_linker.py +++ b/src/clyphx/cs_linker.py @@ -15,12 +15,12 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super import logging from functools import partial -from _Framework.ControlSurfaceComponent import ControlSurfaceComponent from _Framework.ControlSurface import ControlSurface -from _Framework.SessionComponent import SessionComponent +from .core import ControlSurfaceComponent, SessionComponent log = logging.getLogger(__name__) @@ -30,7 +30,7 @@ class CsLinker(ControlSurfaceComponent): ''' def __init__(self): - super(CsLinker, self).__init__() + super().__init__() self._slave_objects = [None, None] self._script_names = None self._horizontal_link = False @@ -40,10 +40,10 @@ def __init__(self): def disconnect(self): '''Extends standard to disconnect and remove slave objects.''' for obj in self._slave_objects: - if obj: + if obj is not None: obj.disconnect() self._slave_objects = None - super(CsLinker, self).disconnect() + super().disconnect() def update(self): pass @@ -150,7 +150,7 @@ def on_scene_list_changed(self): def _refresh_slave_objects(self): '''Refreshes offsets of slave objects.''' for obj in self._slave_objects: - if obj: + if obj is not None: obj._on_offsets_changed() diff --git a/src/clyphx/extra_prefs.py b/src/clyphx/extra_prefs.py index ebccddf..4a6f830 100644 --- a/src/clyphx/extra_prefs.py +++ b/src/clyphx/extra_prefs.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super import logging from functools import partial @@ -30,7 +31,7 @@ class ExtraPrefs(XComponent): __module__ = __name__ def __init__(self, parent, config): - super(ExtraPrefs, self).__init__(parent) + super().__init__(parent) self._show_highlight = config.get('navigation_highlight', True) self._exclusive_arm = config.get('exclusive_arm_on_select', False) self._exclusive_fold = config.get('exclusive_show_group_on_select', False) @@ -46,14 +47,14 @@ def disconnect(self): self._last_track = None self._clip_record_slot = None self._midi_clip_length_slot = None - super(ExtraPrefs, self).disconnect() + super().disconnect() def on_selected_track_changed(self): '''Handles navigation highlight, triggering exclusive arm/fold functions and removes/sets up listeners for clip-related functions. ''' - super(ExtraPrefs, self).on_selected_track_changed() + super().on_selected_track_changed() track = self.song().view.selected_track clip_slot = self.song().view.highlighted_clip_slot self.remove_listeners() @@ -167,5 +168,5 @@ def remove_listeners(self): self._midi_clip_length_slot = None def on_selected_scene_changed(self): - super(ExtraPrefs, self).on_selected_scene_changed() + super().on_selected_scene_changed() self.on_selected_track_changed() diff --git a/src/clyphx/instant_doc.py b/src/clyphx/instant_doc.py new file mode 100644 index 0000000..34ee6a2 --- /dev/null +++ b/src/clyphx/instant_doc.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8 -*- +# This file is part of ClyphX. +# +# ClyphX is free software: you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation, either version 2.1 of the License, or (at your option) +# any later version. +# +# ClyphX is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +# more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with ClyphX. If not, see . + +from __future__ import absolute_import, unicode_literals +from builtins import dict + +import os +import logging +from _Generic.Devices import DEVICE_DICT, DEVICE_BOB_DICT, BANK_NAME_DICT +from .core.compat import iteritems +from .consts import LIVE_VERSION + +log = logging.getLogger(__name__) + +#: Translation table between API names and friendly names. +DEV_NAME_TRANSLATION = dict( + UltraAnalog = 'Analog', + MidiArpeggiator = 'Arpeggiator', + AudioEffectGroupDevice = 'Audio Effect Rack', + MidiChord = 'Chord', + Compressor2 = 'Compressor', + DrumGroupDevice = 'Drum Rack', + Tube = 'Dynamic Tube', + Eq8 = 'EQ Eight', + FilterEQ3 = 'EQ Three', + LoungeLizard = 'Electric', + InstrumentImpulse = 'Impulse', + InstrumentGroupDevice = 'Instrument Rack', + MidiEffectGroupDevice = 'MIDI Effect Rack', + MidiNoteLength = 'Note Length', + MidiPitcher = 'Pitch', + MidiRandom = 'Random', + MultiSampler = 'Sampler', + MidiScale = 'Scale', + CrossDelay = 'Simple Delay', + OriginalSimpler = 'Simpler', + SpectrumAnalyzer = 'Spectrum', + StringStudio = 'Tension', + StereoGain = 'Utility', + MidiVelocity = 'Velocity', + Vinyl = 'Vinyl Distortion', +) + +TEMPLATE = '''\ +

Live Instant Mapping Info for Live {version}

+

+ The following document covers the parameter banks accessible via Live\'s + Instant Mapping feature for each built in device. This info also applies + to controlling device parameters via ClyphX\'s Device Actions. +

+ NOTE: The order of parameter banks is sometimes changed by + Ableton. If you find the information in this document to be incorrect, you + can recreate it with ClyphX by triggering an action named MAKE_DEV_DOC. + That will create a new version of this file in your user/home directory. +
+

Device Index

+ {index} +
+ {devices} + '' +''' + + +class InstantMappingMakeDoc(object): + '''Creates a HTML file in the user's home directory containing + information on the parameter banks defined for all Live devices in + Devices.pyc. + ''' + + def __init__(self): + log.info('InstantMappingMakeDoc initialized.') + self._create_html_file() + log.info('InstantMappingMakeDoc finished.') + + def _get_devices_info(self): + '''Returns a dict of dicts for each device containing its + friendly name, bob parameters and bank names/bank parameters if + applicable. + ''' + return dict((k, dict(name=DEV_NAME_TRANSLATION.get(k, k), + bob=DEVICE_BOB_DICT[k][0], + bank_names=BANK_NAME_DICT.get(k, ()) if len(v) > 1 else (), + banks=v if len(v) > 1 else ())) + for k, v in iteritems(DEVICE_DICT)) + + def _get_device_index(self, dev_dict): + '''Returns a sorted device index for quickly navigating the file. + ''' + return sorted('{0}
'.format(v['name']) + for v in dev_dict.values()) + + def _get_bank_params(self, bank): + return '
'.join('P{}: {}
'.format(i + 1, p) + for i, p in enumerate(bank) if p) + + def _get_banks_info(self, info): + '''Returns the bank name and its parameters.''' + template = 'B{}: {}
{}
' + + return (template.format(i + 1, b, self._get_bank_params(info['banks'][i])) + for i, b in enumerate(info['bank_names'])) + + def _get_device_info(self, info): + '''Writes info to the file for a device.''' + template = '''\ +

{device}

+ Best Of Banks +
{bob}
+ {banks} +
Back to Device Index
+ ''' + + return template.format( + device=info['name'], + bob=self._get_bank_params(info['bob']), + banks='\n'.join(self._get_banks_info(info)), + ) + + def _format_devices_info(self, dev_dict): + return [self._get_device_info(info) + for _, info in sorted(dev_dict.items(), + key=lambda (k, v): (v['name'], k))] + + def _create_html_file(self): + '''Creates an HTML file in the user's home directory. + ''' + dev_dict = self._get_devices_info() + html_file = os.path.join(os.path.expanduser('~'), + 'Live Instant Mapping Info.html') + + data = dict( + version = 'v{}.{}.{}'.format(*LIVE_VERSION), + index = ''.join(self._get_device_index(dev_dict)), + devices = '\n'.join(self._format_devices_info(dev_dict)), + ) + + try: + with open(html_file, 'w') as f: + f.write(TEMPLATE.format(**data)) + except IOError: + log.error('IOError: Unable to write file.') diff --git a/src/clyphx/instant_mapping_make_doc.py b/src/clyphx/instant_mapping_make_doc.py deleted file mode 100644 index 898005c..0000000 --- a/src/clyphx/instant_mapping_make_doc.py +++ /dev/null @@ -1,133 +0,0 @@ -# -*- coding: utf-8 -*- -# This file is part of ClyphX. -# -# ClyphX is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# ClyphX is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -# more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with ClyphX. If not, see . - -from __future__ import absolute_import, unicode_literals - -import os -import logging -from _Generic.Devices import DEVICE_DICT, DEVICE_BOB_DICT, BANK_NAME_DICT -from .consts import LIVE_VERSION - -log = logging.getLogger(__name__) - -#: Translation table between API names and friendly names. -DEV_NAME_TRANSLATION_TABLE = { - 'UltraAnalog': 'Analog', - 'MidiArpeggiator': 'Arpeggiator', - 'AudioEffectGroupDevice': 'Audio Effect Rack', - 'MidiChord': 'Chord', - 'Compressor2': 'Compressor', - 'DrumGroupDevice': 'Drum Rack', - 'Tube': 'Dynamic Tube', - 'Eq8': 'EQ Eight', - 'FilterEQ3': 'EQ Three', - 'LoungeLizard': 'Electric', - 'InstrumentImpulse': 'Impulse', - 'InstrumentGroupDevice': 'Instrument Rack', - 'MidiEffectGroupDevice': 'MIDI Effect Rack', - 'MidiNoteLength': 'Note Length', - 'MidiPitcher': 'Pitch', - 'MidiRandom': 'Random', - 'MultiSampler': 'Sampler', - 'MidiScale': 'Scale', - 'CrossDelay': 'Simple Delay', - 'OriginalSimpler': 'Simpler', - 'SpectrumAnalyzer': 'Spectrum', - 'StringStudio': 'Tension', - 'StereoGain': 'Utility', - 'MidiVelocity': 'Velocity', - 'Vinyl': 'Vinyl Distortion', -} - -#: Header of the HTML file. -HEADER = ( - '

Live Instant Mapping Info for Live v{}.{}.{}



' - 'The following document covers the parameter banks accessible via Live\'s ' - 'Instant Mapping feature for each built in device. This info also applies ' - 'to controlling device parameters via ClyphX\'s Device Actions.

' - 'NOTE: The order of parameter banks is sometimes changed by ' - 'Ableton. If you find the information in this document to be incorrect, you ' - 'can recreate it with ClyphX by triggering an action named MAKE_DEV_DOC. ' - 'That will create a new version of this file in your user/home directory.' - '
' -).format(*LIVE_VERSION) - - -class InstantMappingMakeDoc(object): - '''Creates a HTML file in the user's home directory containing - information on the parameter banks defined for all Live devices in - Devices.pyc. - ''' - - def __init__(self): - log.info('InstantMappingMakeDoc initialized.') - self._create_html_file(self._collect_device_infos()) - log.info('InstantMappingMakeDoc finished.') - - def _collect_device_infos(self): - '''Returns a dict of dicts for each device containing its - friendly name, bob parameters and bank names/bank parameters if - applicable. - ''' - dev_dict = {} - for k, v in DEVICE_DICT.iteritems(): - has_banks = len(v) > 1 - info = { - 'name': DEV_NAME_TRANSLATION_TABLE.get(k, k), - 'bob': DEVICE_BOB_DICT[k][0], - 'bank_names': BANK_NAME_DICT.get(k, ()) if has_banks else (), - 'banks': (v if has_banks else ()), - } - dev_dict[k] = info - return dev_dict - - def _create_html_file(self, dev_dict): - '''Creates an HTML file in the user's home directory. - ''' - html_file = os.path.join(os.path.expanduser('~'), - 'Live Instant Mapping Info.html') - try: - with open(html_file, 'w') as f: - f.write(HEADER) - f.write('

Device Index

') - f.write(''.join(self._get_device_index(dev_dict))) - f.write('
') - for key, value in sorted(dev_dict.iteritems(), - key=lambda (k, v): (v['name'], k)): - self._write_device_info(f, value) - f.write('') - except IOError: - log.error('IOError: Unable to write file.') - - def _get_device_index(self, dev_dict): - '''Returns a sorted device index for quickly navigating the file. - ''' - # return sorted(map('{name}
'.format_map, dev_dict.values())) - return sorted(['
{0}
'.format(v['name']) - for v in dev_dict.values()]) - - def _write_device_info(self, file, info): - '''Writes info to the file for a device.''' - file.write('

{0}

'.format(info['name'])) - self._write_bank_parameters(file, 'Best Of Banks', info['bob']) - for i, bank in enumerate(info['bank_names']): - self._write_bank_parameters(file, 'B{}: {}'.format(i + 1, bank), info['banks'][i]) - file.write('
Back to Device Index
') - - def _write_bank_parameters(self, file, bank_name, bank): - '''Writes the bank name and its parameters to the file.''' - params = ['P{}: {}
'.format(i + 1, p) for i, p in enumerate(bank) if p] - file.write('{}
{}
'.format(bank_name, '
'.join(params))) diff --git a/src/clyphx/m4l_browser.py b/src/clyphx/m4l_browser.py index 9ac22d3..215b36c 100644 --- a/src/clyphx/m4l_browser.py +++ b/src/clyphx/m4l_browser.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super, dict import Live from .core import XComponent @@ -32,12 +33,12 @@ class XM4LBrowserInterface(XComponent): __module__ = __name__ def __init__(self, parent): - super(XM4LBrowserInterface, self).__init__(parent) + super().__init__(parent) self._selected_tag = None self._selected_device = None self._selected_folder = None self._selected_item = None - self._browser = {} + self._browser = dict() def disconnect(self): self._selected_tag = None @@ -45,7 +46,7 @@ def disconnect(self): self._selected_folder = None self._selected_item = None self._browser = None - super(XM4LBrowserInterface, self).disconnect() + super().disconnect() def load_device(self): '''Loads the selected device if there is one.''' @@ -109,10 +110,10 @@ def get_browser_tags(self): if not self._browser: for tag in self.application().browser.tags: if tag.name in BROWSER_TAGS: - self._browser[tag.name] = { - 'tag': tag, - 'devices': self._create_devices_for_tag(tag), - } + self._browser[tag.name] = dict( + tag = tag, + devices = self._create_devices_for_tag(tag), + ) return BROWSER_TAGS def get_devices_for_tag(self, tag_name): @@ -150,48 +151,48 @@ def _create_devices_for_tag(self, tag): is needed for M4L tag, which only contains folders, and Drums tag, which ontains devices and folders. ''' - device_dict = {} + device_dict = dict() if tag.name == 'Max for Live': for child in tag.children: if child.is_folder: for device in child.children: if device.is_device: - device_dict[child.name] = { - 'device': device, - 'items': self._create_items_for_device(child), - 'folders': {}, - } + device_dict[child.name] = dict( + device = device, + items = self._create_items_for_device(child), + folders = dict(), + ) break else: for child in tag.children: if child.is_device: if tag.name == 'Drums': - device_dict[child.name] = { - 'device': child, - 'items': self._create_items_for_device(tag), - 'folders': {}, - } + device_dict[child.name] = dict( + device = child, + items = self._create_items_for_device(tag), + folders = dict(), + ) else: - device_dict[child.name] = { - 'device': child, - 'items': self._create_items_for_device(child), - 'folders': self._create_folders_for_device(child), - } + device_dict[child.name] = dict( + device = child, + items = self._create_items_for_device(child), + folders = self._create_folders_for_device(child), + ) if len(device_dict) == 1: - device_dict[' '] = {} + device_dict[' '] = dict() return device_dict def _create_items_for_device(self, device): '''Returns a dict of loadable items for the given device or folder. ''' - items = {c.name: c for c in device.children - if c.is_loadable and c.name != 'Drum Rack'} + items = dict((c.name, c) for c in device.children + if c.is_loadable and c.name != 'Drum Rack') if len(items) == 1: - items[' '] = {} + items[' '] = dict() return items def _create_folders_for_device(self, device): '''Creates dict of folders for the given device.''' - return {'{} >'.format(c.name): self._create_items_for_device(c) - for c in device.children if c.is_folder} + return dict(('{} >'.format(c.name), self._create_items_for_device(c)) + for c in device.children if c.is_folder) diff --git a/src/clyphx/macrobat/macrobat.py b/src/clyphx/macrobat/macrobat.py index 3381502..b4ce3b4 100644 --- a/src/clyphx/macrobat/macrobat.py +++ b/src/clyphx/macrobat/macrobat.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super import Live from ..core import XComponent @@ -35,17 +36,17 @@ class Macrobat(XComponent): __module__ = __name__ def __init__(self, parent): - super(Macrobat, self).__init__(parent) - self._current_tracks = [] + super().__init__(parent) + self.current_tracks = [] def disconnect(self): - self._current_tracks = [] - super(Macrobat, self).disconnect() + self.current_tracks = [] + super().disconnect() def setup_tracks(self, track): '''Setup component tracks on ini and track list changes.''' - if not track in self._current_tracks: - self._current_tracks.append(track) + if not track in self.current_tracks: + self.current_tracks.append(track) MacrobatTrackComponent(track, self._parent) @@ -55,7 +56,7 @@ class MacrobatTrackComponent(XComponent): __module__ = __name__ def __init__(self, track, parent): - super(MacrobatTrackComponent, self).__init__(parent) + super().__init__(parent) self._track = track self._track.add_devices_listener(self.setup_devices) self._current_devices = [] @@ -71,7 +72,7 @@ def disconnect(self): self.remove_devices(self._track.devices) self._track = None self._current_devices = [] - super(MacrobatTrackComponent, self).disconnect() + super().disconnect() def update(self): if self._track and self.song().view.selected_track == self._track: diff --git a/src/clyphx/macrobat/midi_rack.py b/src/clyphx/macrobat/midi_rack.py index 83bcd59..68d2eed 100644 --- a/src/clyphx/macrobat/midi_rack.py +++ b/src/clyphx/macrobat/midi_rack.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super import Live from ..core import XComponent @@ -28,7 +29,7 @@ class MacrobatMidiRack(XComponent): __module__ = __name__ def __init__(self, parent, rack, name): - super(MacrobatMidiRack, self).__init__(parent) + super().__init__(parent) self._macro_to_cc = [] self._macro_to_pc = [] self._macro_to_sysex = [] @@ -42,7 +43,7 @@ def disconnect(self): self._macro_to_pc = [] self._macro_to_sysex = [] self._sysex_list = [] - super(MacrobatMidiRack, self).disconnect() + super().disconnect() def setup_device(self, rack, name): ''' diff --git a/src/clyphx/macrobat/parameter_rack_template.py b/src/clyphx/macrobat/parameter_rack_template.py index 5ffed8c..13b44a6 100644 --- a/src/clyphx/macrobat/parameter_rack_template.py +++ b/src/clyphx/macrobat/parameter_rack_template.py @@ -15,7 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals -from raven.utils.six import iteritems +from builtins import super, dict, range import Live from ..core import XComponent @@ -27,9 +27,9 @@ class MacrobatParameterRackTemplate(XComponent): __module__ = __name__ def __init__(self, parent, rack, track): - super(MacrobatParameterRackTemplate, self).__init__(parent) + super().__init__(parent) self._on_off_param = [] - self._param_macros = {} + self._param_macros = dict() self._update_macro = 0 self._update_param = 0 self._track = track @@ -38,9 +38,9 @@ def __init__(self, parent, rack, track): def disconnect(self): self.remove_macro_listeners() self._on_off_param = [] - self._param_macros = {} + self._param_macros = dict() self._track = None - super(MacrobatParameterRackTemplate, self).disconnect() + super().disconnect() def setup_device(self, rack): '''Remove any current listeners and set up listener for on/off @@ -58,6 +58,11 @@ def on_off_changed(self): self._parent.schedule_message(1, self.do_reset) self._on_off_param[1] = self._on_off_param[0].value + def _set_param_macro_listeners(self, macro, param, index): + macro.add_value_listener(lambda i=index: self.macro_changed(i)) + param.add_value_listener(lambda i=index: self.param_changed(i)) + self._param_macros[index] = (macro, param) + def scale_macro_value_to_param(self, macro, param): return (((param.max - param.min) / 127.0) * macro.value) + param.min @@ -68,25 +73,21 @@ def get_drum_rack(self): '''For use with DR racks, get drum rack to operate on as well as the params of any simplers/samplers in the rack. ''' - drum_rack = {'devs_by_index': {}, 'devs_by_name': {}} + drum_rack = dict(devs_by_index=dict(), devs_by_name=dict()) if self._track and self._track.devices: for d in self._track.devices: if d.class_name == 'DrumGroupDevice': drum_rack['rack'] = d - rack_devices_by_index = {} - rack_devices_by_name = {} - for chain_index in range(len(d.chains)): - for device in d.chains[chain_index].devices: + by_index = drum_rack['devs_by_index'] + by_name = drum_rack['devs_by_name'] + for i, chain in enumerate(d.chains): + for device in chain.devices: if device.class_name in ('OriginalSimpler', 'MultiSampler'): - current_params = {} - for p in device.parameters: - current_params[str(p.name).upper()] = p - rack_devices_by_index[str(chain_index + 1)] = current_params - rack_devices_by_name[str(device.name)] = current_params + params = dict((str(p.name).upper(), p) for p in device.params) + by_index[str(i + 1)] = params + by_name[str(device.name)] = params break - drum_rack['devs_by_index'] = rack_devices_by_index - drum_rack['devs_by_name'] = rack_devices_by_name break return drum_rack @@ -112,14 +113,18 @@ def update_macro(self, arg=None): def remove_macro_listeners(self): for i in range(1, 9): - if i in self._param_macros: + try: + macro = self._param_macros[i] + except KeyError: + pass + else: m_listener = lambda index=i: self.macro_changed(index) p_listener = lambda index=i: self.param_changed(index) - if self._param_macros[i][0] and self._param_macros[i][0].value_has_listener(m_listener): - self._param_macros[i][0].remove_value_listener(m_listener) - if self._param_macros[i][1] and self._param_macros[i][1].value_has_listener(p_listener): - self._param_macros[i][1].remove_value_listener(p_listener) - self._param_macros = {} + if macro[0] and macro[0].value_has_listener(m_listener): + macro[0].remove_value_listener(m_listener) + if macro[1] and macro[1].value_has_listener(p_listener): + macro[1].remove_value_listener(p_listener) + self._param_macros = dict() if (self._on_off_param and self._on_off_param[0] and self._on_off_param[0].value_has_listener(self.on_off_changed)): @@ -162,7 +167,7 @@ def do_reset(self): self._update_macro = 0 self._tasks.kill() self._tasks.clear() - for k, v in iteritems(self._param_macros): + for k, v in self._param_macros.items(): if v[1] and not v[1].is_quantized and v[1].name != 'Chain Selector': v[1].value = v[1].default_value v[0].value = self.scale_param_value_to_macro(v[1]) diff --git a/src/clyphx/macrobat/parameter_racks.py b/src/clyphx/macrobat/parameter_racks.py index bbb081d..08b1c40 100644 --- a/src/clyphx/macrobat/parameter_racks.py +++ b/src/clyphx/macrobat/parameter_racks.py @@ -13,10 +13,12 @@ # # You should have received a copy of the GNU Lesser General Public License # along with ClyphX. If not, see . - -#---This module contains Learn, Chain Mix, DR, DR Multi, Receiver and Track Racks - +""" +This module contains Learn, Chain Mix, DR, DR Multi, Receiver and +Track Racks. +""" from __future__ import absolute_import, unicode_literals +from builtins import super, dict, range from functools import partial from itertools import chain @@ -24,7 +26,7 @@ from _Framework.SubjectSlot import Subject, SlotManager, subject_slot from .parameter_rack_template import MacrobatParameterRackTemplate -LAST_PARAM = {} +LAST_PARAM = dict() class MacrobatLearnRack(MacrobatParameterRackTemplate): @@ -34,21 +36,21 @@ class MacrobatLearnRack(MacrobatParameterRackTemplate): def __init__(self, parent, rack, track): self._rack = rack - #---delay adding listener to prevent issue with change on set load + # delay adding listener to prevent issue with change on set load parent.schedule_message( 8, partial(parent.song().view.add_selected_parameter_listener, self.on_selected_parameter_changed) ) - super(MacrobatLearnRack, self).__init__(parent, rack, track) + super().__init__(parent, rack, track) def disconnect(self): if self.song().view.selected_parameter_has_listener(self.on_selected_parameter_changed): self.song().view.remove_selected_parameter_listener(self.on_selected_parameter_changed) self._rack = None - super(MacrobatLearnRack, self).disconnect() + super().disconnect() def setup_device(self, rack): '''Set up macro 1 and learned param.''' - super(MacrobatLearnRack, self).setup_device(rack) + super().setup_device(rack) self._rack = rack try: @@ -58,12 +60,7 @@ def setup_device(self, rack): if self._rack and param: if self._rack.parameters[1].is_enabled and param.is_enabled: - index = 1 - m_listener = lambda i=index: self.macro_changed(i) - self._rack.parameters[1].add_value_listener(m_listener) - p_listener = lambda i=index: self.param_changed(i) - param.add_value_listener(p_listener) - self._param_macros[index] = (self._rack.parameters[1], param) + self._set_param_macro_listeners(self._rack.parameters[1], param, 1) self._tasks.add(self.get_initial_value) def on_selected_parameter_changed(self): @@ -81,54 +78,48 @@ class MacrobatChainMixRack(MacrobatParameterRackTemplate): __module__ = __name__ def __init__(self, parent, rack, track): - self._rack = {} - super(MacrobatChainMixRack, self).__init__(parent, rack, track) + self._rack = dict() + super().__init__(parent, rack, track) def disconnect(self): self._rack = None - super(MacrobatChainMixRack, self).disconnect() + super().disconnect() def setup_device(self, rack): '''Set up macros and rack chain params.''' - super(MacrobatChainMixRack, self).setup_device(rack) - self._rack = self.get_rack() - if self._rack: + super().setup_device(rack) + try: + self._rack = self.get_rack() + except ValueError: + pass + else: param_name = self._parent.get_name(rack.name[12:].strip()) + for i in range(1, 9): - chain_to_edit = {} macro = rack.parameters[i] - param = None if macro.is_enabled: chain_name = self._parent.get_name(macro.name) - if chain_name in self._rack: - chain_to_edit = self._rack[chain_name] - if param_name in chain_to_edit: - param = chain_to_edit[param_name] - if param and param.is_enabled: - m_listener = lambda index=i: self.macro_changed(index) - macro.add_value_listener(m_listener) - p_listener = lambda index=i: self.param_changed(index) - param.add_value_listener(p_listener) - self._param_macros[i] = (macro, param) + try: + param = self._rack[chain_name][param_name] + except (TypeError, KeyError): + pass + else: + if param.is_enabled: + self._set_param_macro_listeners(macro, param, i) self._tasks.add(self.get_initial_value) def get_rack(self): '''Get rack to operate on as well as the mixer params of its chains. ''' - rack_chains = {} if self._track and self._track.devices: for d in self._track.devices: if d.class_name.endswith('GroupDevice') and not d.class_name.startswith('Midi'): - for chain_index in range(len(d.chains)): - c = d.chains[chain_index] - rack_chains[str(chain_index + 1)] = { - 'VOL': c.mixer_device.volume, - 'PAN': c.mixer_device.panning, - 'MUTE': c.mixer_device.chain_activator, - } - break - return rack_chains + return dict((str(i + 1), dict(VOL=c.mixer_device.volume, + PAN=c.mixer_device.panning, + MUTE=c.mixer_device.chain_activator)) + for i, c in enumerate(d.chains)) + raise ValueError('Rack not found.') class MacrobatDRMultiRack(MacrobatParameterRackTemplate): @@ -137,21 +128,21 @@ class MacrobatDRMultiRack(MacrobatParameterRackTemplate): __module__ = __name__ def __init__(self, parent, rack, track): - self._drum_rack = {} - super(MacrobatDRMultiRack, self).__init__(parent, rack, track) + self._drum_rack = dict() + super().__init__(parent, rack, track) def disconnect(self): self._drum_rack = None - super(MacrobatDRMultiRack, self).disconnect() + super().disconnect() def setup_device(self, rack): '''Set up macros and drum rack params.''' - super(MacrobatDRMultiRack, self).setup_device(rack) + super().setup_device(rack) self._drum_rack = self.get_drum_rack() if self._drum_rack: param_name = self._parent.get_name(rack.name[11:].strip()) for i in range(1, 9): - drum_to_edit = {} + drum_to_edit = dict() macro = rack.parameters[i] param = None if macro.is_enabled: @@ -163,11 +154,7 @@ def setup_device(self, rack): if param_name in drum_to_edit: param = drum_to_edit[param_name] if param and param.is_enabled: - m_listener = lambda index=i: self.macro_changed(index) - macro.add_value_listener(m_listener) - p_listener = lambda index=i: self.param_changed(index) - param.add_value_listener(p_listener) - self._param_macros[i] = (macro, param) + self._set_param_macro_listeners(macro, param, i) self._tasks.add(self.get_initial_value) @@ -177,20 +164,20 @@ class MacrobatDRRack(MacrobatParameterRackTemplate): __module__ = __name__ def __init__(self, parent, rack, track): - self._drum_rack = {} - super(MacrobatDRRack, self).__init__(parent, rack, track) + self._drum_rack = dict() + super().__init__(parent, rack, track) def disconnect(self): self._drum_rack = None - super(MacrobatDRRack, self).disconnect() + super().disconnect() def setup_device(self, rack): '''Set up macros and drum rack params.''' - super(MacrobatDRRack, self).setup_device(rack) + super().setup_device(rack) self._drum_rack = self.get_drum_rack() if self._drum_rack: drum_name = rack.name[5:].strip() - drum_to_edit = {} + drum_to_edit = dict() if drum_name in self._drum_rack['devs_by_index']: drum_to_edit = self._drum_rack['devs_by_index'][drum_name] elif drum_name in self._drum_rack['devs_by_name']: @@ -203,11 +190,7 @@ def setup_device(self, rack): if name in drum_to_edit: param = drum_to_edit[name] if param and param.is_enabled: - m_listener = lambda index=i: self.macro_changed(index) - macro.add_value_listener(m_listener) - p_listener = lambda index=i: self.param_changed(index) - param.add_value_listener(p_listener) - self._param_macros[i] = (macro, param) + self._set_param_macro_listeners(macro, param, i) self._tasks.add(self.get_initial_value) @@ -217,11 +200,11 @@ class MacrobatReceiverRack(MacrobatParameterRackTemplate): __module__ = __name__ def __init__(self, parent, rack, track): - super(MacrobatReceiverRack, self).__init__(parent, rack, track) + super().__init__(parent, rack, track) def setup_device(self, rack): '''Set up receiver and send macros.''' - super(MacrobatReceiverRack, self).setup_device(rack) + super().setup_device(rack) receiver_macros = self.get_ident_macros(rack) if receiver_macros: self._sender_macros = [] @@ -231,15 +214,9 @@ def setup_device(self, rack): self.get_sender_macros(t.devices) if self._sender_macros: for r in receiver_macros: - index = 0 - for s in self._sender_macros: - index += 1 + for i, s in enumerate(self._sender_macros): if r[0] == s[0] and r[1].is_enabled and s[1].is_enabled: - r_listener = lambda index = index:self.macro_changed(index) - r[1].add_value_listener(r_listener) - s_listener = lambda index = index:self.param_changed(index) - s[1].add_value_listener(s_listener) - self._param_macros[index] = (r[1], s[1]) + self._set_param_macro_listeners(r[1], s[1], i + 1) self._tasks.add(self.get_initial_value) def get_sender_macros(self, dev_list): @@ -280,15 +257,15 @@ class MacrobatTrackRack(MacrobatParameterRackTemplate): def __init__(self, parent, rack, track): self._rack = rack - super(MacrobatTrackRack, self).__init__(parent, rack, track) + super().__init__(parent, rack, track) def disconnect(self): self._rack = None - super(MacrobatTrackRack, self).disconnect() + super().disconnect() def setup_device(self, rack): '''Setup macros and track mixer params.''' - super(MacrobatTrackRack, self).setup_device(rack) + super().setup_device(rack) for i in range(1, 9): macro = rack.parameters[i] param = None @@ -304,11 +281,7 @@ def setup_device(self, rack): elif name.startswith('PAN'): param = self._track.mixer_device.panning if param and param.is_enabled: - m_listener = lambda index=i: self.macro_changed(index) - macro.add_value_listener(m_listener) - p_listener = lambda index=i: self.param_changed(index) - param.add_value_listener(p_listener) - self._param_macros[i] = (macro, param) + self._set_param_macro_listeners(macro, param, i) self._tasks.add(self.get_initial_value) @@ -318,20 +291,20 @@ class MacrobatDRPadMixRack(MacrobatParameterRackTemplate): __module__ = __name__ def __init__(self, parent, rack, track): - self._drum_rack = {} + self._drum_rack = dict() self._rack = None self._selected_chain = None - super(MacrobatDRPadMixRack, self).__init__(parent, rack, track) + super().__init__(parent, rack, track) def disconnect(self): self._drum_rack = None self._rack = None self._selected_chain = None - super(MacrobatDRPadMixRack, self).disconnect() + super().disconnect() def setup_device(self, rack): '''Set up macros and drum rack params.''' - super(MacrobatDRPadMixRack, self).setup_device(rack) + super().setup_device(rack) self._rack = rack self._drum_rack = self.get_drum_rack() self._selected_chain = None @@ -354,12 +327,7 @@ def setup_device(self, rack): if s_index < num_sends: param = self._selected_chain.mixer_device.sends[s_index] if param and param.is_enabled: - macro = rack.parameters[i] - m_listener = lambda index=i: self.macro_changed(index) - macro.add_value_listener(m_listener) - p_listener = lambda index=i: self.param_changed(index) - param.add_value_listener(p_listener) - self._param_macros[i] = (macro, param) + self._set_param_macro_listeners(rack.parameters[i], param, i) self._tasks.add(self.get_initial_value) @subject_slot('selected_drum_pad') @@ -387,7 +355,7 @@ class CsWrapper(Subject, SlotManager): __subject_events__ = ('value',) def __init__(self, cs): - super(CsWrapper, self).__init__() + super().__init__() self._cs = cs self._max = 0 self._on_cs_value_changed.subject = self._cs @@ -431,12 +399,12 @@ class MacrobatChainSelectorRack(MacrobatParameterRackTemplate): def __init__(self, parent, rack, track): self._rack = rack self._wrapper = None - super(MacrobatChainSelectorRack, self).__init__(parent, rack, track) + super().__init__(parent, rack, track) def disconnect(self): self._rack = None self._wrapper = None - super(MacrobatChainSelectorRack, self).disconnect() + super().disconnect() def scale_macro_value_to_param(self, macro, param): return (((param.max - param.min) / 126.0) * macro.value) + param.min @@ -444,7 +412,7 @@ def scale_macro_value_to_param(self, macro, param): def setup_device(self, rack): '''Set up macro 1 and chain selector. ''' - super(MacrobatChainSelectorRack, self).setup_device(rack) + super().setup_device(rack) self._rack = rack if self._rack: macro = self._rack.parameters[1] @@ -454,12 +422,7 @@ def setup_device(self, rack): self._wrapper.max = len(self._rack.chains) if self._wrapper.max > 1: self._on_chains_changed.subject = self._rack - index = 1 - m_listener = lambda index = index:self.macro_changed(index) - macro.add_value_listener(m_listener) - p_listener = lambda index = index:self.param_changed(index) - self._wrapper.add_value_listener(p_listener) - self._param_macros[index] = (macro, self._wrapper) + self._set_param_macro_listeners(macro, self._wrapper, 1) self._tasks.add(self.get_initial_value) @subject_slot('chains') diff --git a/src/clyphx/macrobat/push_rack.py b/src/clyphx/macrobat/push_rack.py index 451b625..7f368ea 100644 --- a/src/clyphx/macrobat/push_rack.py +++ b/src/clyphx/macrobat/push_rack.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import with_statement, absolute_import, unicode_literals +from builtins import super from ..core import XComponent from ..consts import NOTE_NAMES @@ -27,7 +28,7 @@ class MacrobatPushRack(XComponent): __module__ = __name__ def __init__(self, parent, rack): - super(MacrobatPushRack, self).__init__(parent) + super().__init__(parent) self._rack = rack self._script = None self._push_ins = self._connect_to_push() @@ -38,7 +39,7 @@ def disconnect(self): self._rack = None self._script = None self._push_ins = None - super(MacrobatPushRack, self).disconnect() + super().disconnect() def update(self): self._push_ins = self._connect_to_push() diff --git a/src/clyphx/macrobat/rnr_rack.py b/src/clyphx/macrobat/rnr_rack.py index 82c4dec..4b0eabb 100644 --- a/src/clyphx/macrobat/rnr_rack.py +++ b/src/clyphx/macrobat/rnr_rack.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super import Live from functools import partial @@ -27,7 +28,7 @@ class MacrobatRnRRack(XComponent): __module__ = __name__ def __init__(self, parent, rack, name, track): - super(MacrobatRnRRack, self).__init__(parent) + super().__init__(parent) self._on_off_param = [] self._devices_to_operate_on = [] self._track = track @@ -38,7 +39,7 @@ def disconnect(self): self._on_off_param = [] self._devices_to_operate_on = [] self._track = None - super(MacrobatRnRRack, self).disconnect() + super().disconnect() def setup_device(self, rack, name): ''' @@ -51,7 +52,7 @@ def setup_device(self, rack, name): if p.name == 'Device On' and p.is_enabled: if not p.value_has_listener(self.on_off_changed): self._on_off_param = [p, name] - #---use this to get around device on/off switches + # use this to get around device on/off switches # getting turned on upon set load self._parent.schedule_message( 5, partial(p.add_value_listener, self.on_off_changed) diff --git a/src/clyphx/macrobat/sidechain_rack.py b/src/clyphx/macrobat/sidechain_rack.py index 712d7d8..71c84fb 100644 --- a/src/clyphx/macrobat/sidechain_rack.py +++ b/src/clyphx/macrobat/sidechain_rack.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super from functools import partial import Live @@ -27,7 +28,7 @@ class MacrobatSidechainRack(XComponent): __module__ = __name__ def __init__(self, parent, rack, track): - super(MacrobatSidechainRack, self).__init__(parent) + super().__init__(parent) self._last_meter_left_val = -1 self._last_meter_right_val = -1 self._last_midi_meter_val = -1 @@ -45,7 +46,7 @@ def disconnect(self): self._track.remove_output_meter_level_listener(self.midi_changed) self._track = None self._rack = None - super(MacrobatSidechainRack, self).disconnect() + super().disconnect() def setup_device(self): ''' diff --git a/src/clyphx/macrobat/user_config.py b/src/clyphx/macrobat/user_config.py index 6e89744..9e5476f 100644 --- a/src/clyphx/macrobat/user_config.py +++ b/src/clyphx/macrobat/user_config.py @@ -16,21 +16,21 @@ from __future__ import absolute_import, unicode_literals -# ***************************** [SETTINGS NOTES] ************************** +# ************************** [SETTINGS NOTES] ************************** # Please DO NOT change any of the spacing in this file. -# Please DO NOT change the name of this file or its file extension. When done -# making your changes to the [SETTINGS] below, just save the file. +# Please DO NOT change the name of this file or its file extension. When +# done making your changes to the [SETTINGS] below, just save the file. -# After saving this file, you will need to close/restart Live for your changes -# to take effect. +# After saving this file, you will need to close/restart Live for your +# changes to take effect. -# For Windows 7/Vista users, depending on how your privileges are set up, you -# may not be able to save changes you make to this file. You may receive an -# error such as Access Denied when trying to save. If this occurs, you will -# need todrag this file onto your desktop, then make your changes and save. -# Whendone, drag the file back into the ClyphX folder. +# For Windows users, depending on how your privileges are set up, you +# may not be able to save changes you make to this file. You may receive +# an error such as Access Denied when trying to save. If this occurs, +# you will need to drag this file onto your desktop, then make your +# changes and save. When done, drag the file back into the ClyphX folder. # ******************************* [SETTINGS] ******************************* @@ -38,7 +38,7 @@ # Below you can define a list of SysEx messages that can be sent from macros. # The entries in the list below are just examples and can be removed. -SYSEX_LIST = [#<------Do NOT remove this. +SYSEX_LIST = [ # <- Do NOT remove this. ('Motif Arp I/O', 'F0 43 10 6F 40 70 15 nn F7', 0, 1), ('Motif Arp Type', 'F0 43 10 6F 40 70 14 nn F7', 0, 127), @@ -46,28 +46,31 @@ ('Motif EQ Lo', 'F0 43 10 6F 40 70 31 nn F7', 0, 127), ('Motif EQ LoMid', 'F0 43 10 6F 40 70 32 nn F7', 0, 127), ('Motif EQ HiMid', 'F0 43 10 6F 40 70 33 nn F7', 0, 127), -('Motif EQ Hi', 'F0 43 10 6F 40 70 34 nn F7', 0, 127) +('Motif EQ Hi', 'F0 43 10 6F 40 70 34 nn F7', 0, 127), -]#<------Do NOT remove this. +] # <- Do NOT remove this. # Entry Format: -# ('Identifier', 'SysEx String', Min Value, Max Value) +# ('Identifier', 'SysEx String', Min Value, Max Value), # Identifier: -# A name to identify the SysEx string with. +# A name to identify the SysEx string with. # SysEx String: -# The SysEx string (in hexadecimal) to send. -# This must start with F0 and end with F7. -# All other values in the string should be within the range of 00 - 7F. -# nn represents the variable byte in the SysEx string, the one the macro will control. +# - The SysEx string (in hexadecimal) to send. +# - This must start with F0 and end with F7. +# - All other values in the should be within the range of 00 - 7F. +# - nn represents the variable byte in the SysEx string, the one the +# macro will control. # Min Value: -# The lowest value (in decimal) of the variable byte. Should be in the range of 0 - 127. +# - The lowest value (in decimal) of the variable byte. Should be in the +# range of 0 - 127. # Max Value: -# The highest value (in decimal) of the variable byte. Should be in the range of 0 - 127. +# - The highest value (in decimal) of the variable byte. Should be in +# the range of 0 - 127. # Notes: -# The Min and Max Value do not not have quotes around them. -# Except for the last entry in the list, every entry in the list should have a comma following it. +# - The Min and Max Value do not not have quotes around them. +# - Every entry in the list should have a comma following it. diff --git a/src/clyphx/push_apc_combiner.py b/src/clyphx/push_apc_combiner.py index 70a6719..0513dce 100644 --- a/src/clyphx/push_apc_combiner.py +++ b/src/clyphx/push_apc_combiner.py @@ -15,10 +15,10 @@ # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals +from builtins import super -from _Framework.SessionComponent import SessionComponent from ableton.v2.control_surface.components.session_ring import SessionRingComponent -from .core import XComponent +from .core import XComponent, SessionComponent class PushApcCombiner(XComponent): @@ -28,7 +28,7 @@ class PushApcCombiner(XComponent): __module__ = __name__ def __init__(self, parent): - super(PushApcCombiner, self).__init__(parent) + super().__init__(parent) self._push = None self._push_session = None self._apc = None @@ -40,7 +40,7 @@ def disconnect(self): self._push_session = None self._apc = None self._apc_session = None - super(PushApcCombiner, self).disconnect() + super().disconnect() def set_up_scripts(self, scripts): '''Remove current listeners, get Push/APC scripts, set up diff --git a/src/clyphx/push_mocks.py b/src/clyphx/push_mocks.py index d27bced..c5a0804 100644 --- a/src/clyphx/push_mocks.py +++ b/src/clyphx/push_mocks.py @@ -15,6 +15,7 @@ # along with ClyphX. If not, see . from __future__ import unicode_literals +from builtins import super from Push.handshake_component import HandshakeComponent @@ -38,7 +39,7 @@ class MockHandshake(HandshakeComponent): ''' def __init__(self, *a, **k): - super(MockHandshake, self).__init__(*a, **k) + super().__init__(*a, **k) self._on_identity_value.subject = None self._on_dongle_value.subject = None diff --git a/src/clyphx/user_actions.py b/src/clyphx/user_actions.py index 8cf4e56..bd00b0f 100644 --- a/src/clyphx/user_actions.py +++ b/src/clyphx/user_actions.py @@ -72,6 +72,7 @@ semi-colon(;), comma(,), percent sign(%), equals sign(=) ''' from __future__ import absolute_import, unicode_literals +from builtins import super import logging import Live @@ -91,7 +92,7 @@ class XUserActions(XComponent): def __init__(self, parent): # parent ClyphX initialization - super(XUserActions, self).__init__(parent) + super().__init__(parent) '''Below is the dictionary of actions that this script provides. @@ -105,10 +106,10 @@ def __init__(self, parent): You can remove the 2 example entries from the dictionary if you wish. ''' # DO NOT REMOVE THIS - self._action_dict = { - 'EX_ACTION_1': 'example_action_one', - 'EX_ACTION_2': 'example_action_two', - } + self._action_dict = dict( + EX_ACTION_1 = 'example_action_one', + EX_ACTION_2 = 'example_action_two', + ) def example_action_one(self, track, args): '''Example action that writes to Live's log file and then triggers diff --git a/src/clyphx/utils.py b/src/clyphx/utils.py deleted file mode 100644 index 69cd183..0000000 --- a/src/clyphx/utils.py +++ /dev/null @@ -1,37 +0,0 @@ -from raven.utils.six import iteritems - - -def get_python_info(serialize=True): - '''Returns info about the Live Python runtime. - ''' - import sys - import json - - version_info = {k: getattr(sys.version_info, k) for k in - ('major', 'minor', 'micro', 'releaselevel', 'serial')} - - modules = {k: {'module': getattr(v, '__name__', None), - 'file': getattr(v, '__file__', None)} - for k, v in iteritems(sys.modules)} - - info = { - 'version': sys.version, - 'version_info': version_info, - 'path': sys.path, - 'modules': modules, - 'builtin_modules': sys.builtin_module_names, - 'executable': sys.executable, - # 'dllhandle': sys.dllhandle, - 'prefix': sys.prefix, - 'exec_prefix': sys.exec_prefix, - } - - return json.dumps(info, indent=4) if serialize else info - - -def repr(self): - return '{}({})'.format( - type(self).__name__, - ', '.join('{}={}'.format(k, getattr(self, k)) - for k in self.__slots__), - ) diff --git a/src/clyphx/xtriggers.py b/src/clyphx/xtriggers.py index f3bc2c7..27242fe 100644 --- a/src/clyphx/xtriggers.py +++ b/src/clyphx/xtriggers.py @@ -14,18 +14,18 @@ # You should have received a copy of the GNU Lesser General Public License # along with ClyphX. If not, see . from __future__ import absolute_import, unicode_literals -from raven.utils.six import iteritems +from builtins import super, dict, range from functools import partial import logging import Live -from _Framework.ControlSurfaceComponent import ControlSurfaceComponent +from .core import XComponent from .action_list import ActionList log = logging.getLogger(__name__) -class XTrigger(ControlSurfaceComponent): +class XTrigger(XComponent): pass @@ -35,22 +35,14 @@ class XControlComponent(XTrigger): __module__ = __name__ def __init__(self, parent): - super(XControlComponent, self).__init__() - self._parent = parent - self._control_list = {} + super().__init__(parent) + self._control_list = dict() self._xt_scripts = [] def disconnect(self): - self._control_list = {} + self._control_list = dict() self._xt_scripts = [] - self._parent = None - ControlSurfaceComponent.disconnect(self) - - def on_enabled_changed(self): - pass - - def update(self): - pass + super().disconnect() def connect_script_instances(self, instantiated_scripts): '''Try to connect to ClyphX_XT instances.''' @@ -83,7 +75,7 @@ def assign_new_actions(self, string): x.assign_new_actions(string) ident = string[string.index('[')+2:string.index(']')].strip() actions = string[string.index(']')+2:].strip() - for c, v in iteritems(self._control_list): + for c, v in self._control_list.items(): if ident == v['ident']: new_actions = actions.split(',') on_action = '[{}] {}'.format(ident, new_actions[0]) @@ -122,7 +114,7 @@ def get_user_control_settings(self, data, midi_map_handle): '''Receives control data from user settings file and builds control dictionary. ''' - self._control_list = {} + self._control_list = dict() for d in data: status_byte = None channel = None @@ -150,12 +142,12 @@ def get_user_control_settings(self, data, midi_map_handle): except: pass if status_byte and channel is not None and ctrl_num is not None and on_action: - self._control_list[(status_byte + channel, ctrl_num)] = { - 'ident': ctrl_name, - 'on_action': on_action, - 'off_action': off_action, - 'name': ActionList(on_action), - } + self._control_list[(status_byte + channel, ctrl_num)] = dict( + ident = ctrl_name, + on_action = on_action, + off_action = off_action, + name = ActionList(on_action), + ) if status_byte == 144: fn = Live.MidiMap.forward_midi_note else: @@ -166,10 +158,12 @@ def rebuild_control_map(self, midi_map_handle): '''Called from main when build_midi_map is called.''' for key in self._control_list.keys(): if key[0] >= 176: + # forwards a CC msg to the receive_midi method Live.MidiMap.forward_midi_cc( self._parent._c_instance.handle(), midi_map_handle, key[0] - 176, key[1] ) else: + # forwards a NOTE msg to the receive_midi method Live.MidiMap.forward_midi_note( self._parent._c_instance.handle(), midi_map_handle, key[0] - 144, key[1] ) @@ -182,8 +176,7 @@ class XTrackComponent(XTrigger): __module__ = __name__ def __init__(self, parent, track): - super(XTrackComponent, self).__init__() - self._parent = parent + super().__init__(parent) self._track = track self._clip = None self._loop_count = 0 @@ -202,14 +195,7 @@ def disconnect(self): self._clip = None self._triggered_clips = [] self._triggered_lseq_clip = None - self._parent = None - super(XTrackComponent, self).disconnect() - - def on_enabled_changed(self): - pass - - def update(self): - pass + super().disconnect() def play_slot_index_changed(self): '''Called on track play slot index changes to set up clips to @@ -255,13 +241,15 @@ def on_timer(self): '''Continuous timer, calls main script if there are any triggered clips. ''' - if self._track and (not self._track.mute or self._parent._process_xclips_if_track_muted): + if self._track and (not self._track.mute or + self._parent._process_xclips_if_track_muted): if self._triggered_clips: for clip in self._triggered_clips: self._parent.handle_action_list_trigger(self._track, clip) self._triggered_clips = [] if self._triggered_lseq_clip: - self._parent.handle_loop_seq_action_list(self._triggered_lseq_clip, self._loop_count) + self._parent.handle_loop_seq_action_list(self._triggered_lseq_clip, + self._loop_count) self._triggered_lseq_clip = None def remove_loop_jump_listener(self): @@ -277,12 +265,11 @@ class XCueComponent(XTrigger): __module__ = __name__ def __init__(self, parent): - super(XCueComponent, self).__init__() - self._parent = parent + super().__init__(parent) self.song().add_current_song_time_listener(self.arrange_time_changed) self.song().add_is_playing_listener(self.arrange_time_changed) self.song().add_cue_points_listener(self.cue_points_changed) - self._x_points = {} + self._x_points = dict() self._x_point_time_to_watch_for = -1 self._last_arrange_position = -1 self._sorted_times = [] @@ -293,15 +280,8 @@ def disconnect(self): self.song().remove_current_song_time_listener(self.arrange_time_changed) self.song().remove_is_playing_listener(self.arrange_time_changed) self.song().remove_cue_points_listener(self.cue_points_changed) - self._x_points = {} - self._parent = None - super(XCueComponent, self).disconnect() - - def on_enabled_changed(self): - pass - - def update(self): - pass + self._x_points = dict() + super().disconnect() def cue_points_changed(self): '''Called on cue point changes to set up points to watch, cue @@ -359,5 +339,5 @@ def remove_cue_point_listeners(self): cp.remove_time_listener(self.cue_points_changed) if cp.name_has_listener(self.cue_points_changed): cp.remove_name_listener(self.cue_points_changed) - self._x_points = {} + self._x_points = dict() self._x_point_time_to_watch_for = -1 diff --git a/stubs/clyphx/actions/clip.pyi b/stubs/clyphx/actions/clip.pyi new file mode 100644 index 0000000..7413422 --- /dev/null +++ b/stubs/clyphx/actions/clip.pyi @@ -0,0 +1,12 @@ +from ..core import XComponent + +class XClipActions(XComponent): + # TODO + + def get_note_name_from_string(self, string: str) -> str: + ... + + def string_to_note(self, string: str) -> int: + ... + + # TODO diff --git a/tools/vscode.py b/tools/vscode.py index bfd7d7c..2634212 100644 --- a/tools/vscode.py +++ b/tools/vscode.py @@ -1,5 +1,3 @@ - - #! /usr/bin/env python3 from pathlib import Path from shutil import which @@ -51,23 +49,27 @@ def set_python_path(self): def set_lib_paths(self): key = 'osx' if self.env.mac else 'windows' - paths = ['${workspaceFolder}/src/clyphx'] - try: - path = self.env.resources / 'MIDI Remote Scripts' - paths.append(str(path)) - self._config.update({ - 'python.autoComplete.extraPaths': [str(path)] - }) - except: - pass + paths = [ + '${workspaceFolder}/src/clyphx', + self.env.resources / 'Python/lib', + *list(self.env.resources.joinpath('Python/site-packages').iterdir()), + self.env.resources / 'Python/abl.live', + self.env.resources / 'MIDI Remote Scripts', + ] self._config.update({ + 'python.autoComplete.extraPaths': list(map(str, paths)), + 'python.analysis.extraPaths': list(map(str, paths)), 'terminal.integrated.env.{}'.format(key): { 'PATH': os.pathsep.join(['${workspaceFolder}/.bin', '${env:PATH}']), - 'PYTHONPATH': os.pathsep.join(paths) + 'PYTHONPATH': os.pathsep.join(map(str, paths)), } }) + env = BASEDIR / '.env' + env.write_text('PYTHONPATH={}'.format(os.pathsep.join(map(str, paths)))) + log.warning('.env file saved in %s', env) + def set_linter(self): '''Add linting (flake8) configuration to VSCode. diff --git a/tools/win.ps1 b/tools/win.ps1 index 88a483f89ce2dbd166e41d47365a0d5ead608b40..03140208e7d3b8a3a248fb7cfbdfef960c1be56d 100644 GIT binary patch delta 507 zcmZWmJxc>Y5PjIh2q{9a5D`WUUMymMfi_A&&_)}JH1RH&Pfu>okQ9>MO4&aW@IR!t z_D}c|d^09lQI^@e-I@31&D+K9m&_L>|FU^lNzlOJK6aMCpf?aQgEbvFTCZFS2tAQL^ zxWWa_8DiNHvucRoCYVJ`3um-uN-bU7vR)01!YI}?r_PM}J7f^(G<~H0vrzW0#b#fP nQ5zeK71QJoN6ZIEh|?P3|KQhiG}u*r2@h0dpU_bqcs51fmI7Ka delta 7 OcmeyMa7=o`F