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.
+
+
+ {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 = '''\
+
+ 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('')
- 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(''.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 88a483f..0314020 100644
Binary files a/tools/win.ps1 and b/tools/win.ps1 differ