Skip to content

Commit a9b7664

Browse files
committed
Reimplemented Ableton port as a User Remote Script because previously used script wasn't feature complete
1 parent ccf5951 commit a9b7664

File tree

7 files changed

+103
-406
lines changed

7 files changed

+103
-406
lines changed

docs/ableton.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ of Ableton Live without getting into rewriting its complex built-in MIDI remote
44
MIDI Scripter has two ways to communicate directly with Ableton Live internals.
55
Both has own limitations, so some use cases may require to use both.
66

7-
## 1. Ableton MIDI Remote Script
7+
## 1. Ableton MIDI User Remote Script
88

9-
The idea behind this method is to use the existing Ableton Live remote
9+
The idea behind this method is to use Ableton Live user remote
1010
script and communicate with it by MIDI.
11-
[Special remote script](https://github.com/Maboroshy/midi-scripter/tree/master/extra/Ableton%20Remote%20Script) should be
11+
[Pre-mapped user remote script](https://github.com/Maboroshy/midi-scripter/tree/master/extra/Ableton%20Remote%20Script) should be
1212
installed to Ableton Live and assigned to virtual MIDI ports in its settings.
1313

1414
The script has its raw MIDI messages mapped to
@@ -19,9 +19,9 @@ All available events are listed in [API documentation][midiscripter.AbletonEvent
1919

2020
The messages are received and sent by [`AbletonIn`][midiscripter.AbletonIn] and
2121
[`AbletonOut`][midiscripter.AbletonOut] ports that are wrappers for virtual
22-
MIDI ports used for communication.
22+
MIDI ports used for communication. The script uses MIDI channel 15.
2323

24-
These ports should be declared with virtual MIDI port name as an argument.
24+
These ports should be declared with virtual MIDI port name as an argument.
2525

2626
Example: [Select armed track script with Ableton MIDI Remote Script](https://github.com/Maboroshy/midi-scripter/blob/master/examples/ableton_select_armed_track_with_remote_script)
2727

examples/ableton_select_armed_track_with_remote_script/readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Select Armed Track in Ableton Live with Remote Script
22

33
This script uses Ableton Live Remote script to select the armed track in
4-
Ableton Live.
4+
Ableton Live. The script uses MIDI channel 15.
55

66
## Prerequisites
77
- Install [Ableton Live Remote Script](https://github.com/Maboroshy/midi-scripter/tree/master/extra/Ableton%20Remote%20Script).
Binary file not shown.

extra/Ableton Remote Script/readme.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1-
# Ableton Remote Script
1+
Re# Ableton User Remote Script
22

3-
This script is a remote script for Ableton with MIDI messages pre-mapped to
3+
This script is a user remote script for Ableton with MIDI messages pre-mapped to
44
corresponding types of `AbletonMsg`.
55

6-
This script is based on [Ableton Live Custom Remote Script by
7-
laidlaw42](https://github.com/laidlaw42/ableton-live-midi-remote-scripts)
8-
with minimal changes.
9-
106
## Installation
117

128
1. Download [the .zip file](Ableton%20Remote%20Script.zip).
139
2. Create two virtual MIDI ports - for input and for output.
1410
3. Install the script following
15-
[the official guide](https://help.ableton.com/hc/en-us/articles/209072009-Installing-third-party-remote-scripts):
16-
copy the folder from .zip file to your Ableton Live User Library, assign
17-
virtual MIDI ports and enable the script.
11+
[the official guide](https://help.ableton.com/hc/en-us/articles/206240184-Creating-your-own-Control-Surface-script):
12+
copy the folder from .zip file to your User Remote Scripts folder,
13+
assign virtual MIDI ports and enable the script.
1814

1915
## Usage
2016

midiscripter/ableton_remote/ableton_msg.py

Lines changed: 25 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,34 @@
88

99

1010
class AbletonEvent(midiscripter.base.msg_base.AttrEnum):
11-
CLIP_LAUNCH = 'CLIP_LAUNCH'
12-
"""Clip launch by (x, y) index"""
13-
CUE_LEVEL = 'CUE_LEVEL'
11+
CUE_VOL = 'CUE_VOL'
1412
"""Cue level control"""
1513
CROSSFADER = 'CROSSFADER'
1614
"""Crossfader control"""
1715

1816
DEVICE_BANK = 'DEVICE_BANK'
19-
"""Select Device bank by index"""
17+
"""Select device encoder bank by index"""
18+
DEVICE_BANK_NEXT = 'DEVICE_BANK_NEXT'
19+
"""Select next device encoder bank"""
20+
DEVICE_BANK_PREV = 'DEVICE_BANK_PREV'
21+
"""Select previous device encoder bank"""
2022
DEVICE_LOCK = 'DEVICE_LOCK'
2123
"""Device lock (lock "blue hand")"""
22-
DEVICE_NAV_LEFT = 'DEVICE_NAV_LEFT'
23-
"""Device nav left"""
24-
DEVICE_NAV_RIGHT = 'DEVICE_NAV_RIGHT'
25-
"""Device nav right"""
26-
DEVICE_BANK_NAV_LEFT = 'DEVICE_BANK_NAV_LEFT'
27-
"""Device bank nav left"""
28-
DEVICE_BANK_NAV_RIGHT = 'DEVICE_BANK_NAV_RIGHT'
29-
"""Device bank nav right"""
3024
DEVICE_TOGGLE = 'DEVICE_TOGGLE'
3125
"""Toggle selected device on/off"""
32-
DRUM_PAD = 'DRUM_PAD'
33-
"""Drum pad Trigger by index"""
26+
27+
ENCODER = 'ENCODER'
28+
"""Parameters control"""
29+
30+
FORWARD = 'FORWARD'
31+
"""Fast forward"""
3432

3533
LOOP = 'LOOP'
3634
"""Loop on/off"""
3735

3836
MASTER_SEL = 'MASTER_SEL'
3937
"""Master track select"""
40-
MASTER_VOLUME = 'MASTER_VOLUME'
38+
MASTER_VOL = 'MASTER_VOL'
4139
"""Master track volume"""
4240
METRONOME = 'METRONOME'
4341
"""Metronome on/off"""
@@ -50,8 +48,6 @@ class AbletonEvent(midiscripter.base.msg_base.AttrEnum):
5048
OVERDUB = 'OVERDUB'
5149
"""Overdub on/off"""
5250

53-
PARAM_CONTROL = 'PARAM_CONTROL'
54-
"""Parameters control"""
5551
PLAY = 'PLAY'
5652
"""Global play"""
5753
PUNCH_IN = 'PUNCH_IN'
@@ -61,47 +57,13 @@ class AbletonEvent(midiscripter.base.msg_base.AttrEnum):
6157

6258
REC = 'REC'
6359
"""Global record"""
64-
REC_QUANT_TOGGLE = 'REC_QUANT_TOGGLE'
65-
"""Record quantization on/off"""
66-
REDO = 'REDO'
67-
"""Redo"""
68-
69-
SCENE_UP = 'SCENE_UP'
70-
"""Scene down"""
71-
SCENE_DOWN = 'SCENE_DOWN'
72-
"""Scene up"""
73-
SCENE_LAUNCH = 'SCENE_LAUNCH'
74-
"""Scene launch by index"""
75-
76-
SEEK_FWD = 'SEEK_FWD'
77-
"""Seek forward"""
78-
SEEK_RWD = 'SEEK_RWD'
79-
"""Seek rewind"""
80-
81-
SESSION_LEFT = 'SESSION_LEFT'
82-
"""Session left"""
83-
SESSION_RIGHT = 'SESSION_RIGHT'
84-
"""Session right"""
85-
SESSION_UP = 'SESSION_UP'
86-
"""Session up"""
87-
SESSION_DOWN = 'SESSION_DOWN'
88-
"""Session down"""
89-
90-
SEL_CLIP_LAUNCH = 'SEL_CLIP_LAUNCH'
91-
"""Selected clip launch"""
92-
SEL_SCENE_LAUNCH = 'SEL_SCENE_LAUNCH'
93-
"""Selected scene launch"""
94-
SEL_TRACK_MUTE = 'SEL_TRACK_MUTE'
95-
"""Mute Selected Track"""
96-
SEL_TRACK_REC = 'SEL_TRACK_REC'
97-
"""Arm Selected Track"""
98-
SEL_TRACK_SOLO = 'SEL_TRACK_SOLO'
99-
"""Solo Selected Track"""
60+
REWIND = 'REWIND'
61+
"""Rewind"""
10062

63+
SESSION_REC = 'SESSION_REC'
64+
"""Session record"""
10165
STOP = 'STOP'
10266
"""Global stop"""
103-
STOP_ALL_CLIPS = 'STOP_ALL_CLIPS'
104-
"""Stop all clips"""
10567

10668
TAP_TEMPO = 'TAP_TEMPO'
10769
"""Tap tempo"""
@@ -116,36 +78,23 @@ class AbletonEvent(midiscripter.base.msg_base.AttrEnum):
11678
"""Track right"""
11779
TRACK_MUTE = 'TRACK_MUTE'
11880
"""Track On/Off by index"""
119-
TRACK_PAN = 'TRACK_PAN'
120-
"""Track Pan"""
81+
TRACK_NEXT_8 = 'TRACK_NEXT_8'
82+
"""Select next 8 tracks to control with track controls"""
83+
TRACK_PREV_8 = 'TRACK_PREV_8'
84+
"""Select previous 8 tracks to control with track controls"""
12185
TRACK_SELECT = 'TRACK_SELECT'
12286
"""Track select by index"""
123-
TRACK_SEND = 'TRACK_SEND'
124-
"""Track Send"""
87+
TRACK_SEND_A = 'TRACK_SEND_A'
88+
"""Track Send A"""
89+
TRACK_SEND_B = 'TRACK_SEND_B'
90+
"""Track Send B"""
12591
TRACK_SOLO = 'TRACK_SOLO'
12692
"""Track solo by index"""
12793
TRACK_STOP = 'TRACK_STOP'
12894
"""Track clip stop by track index"""
12995
TRACK_VOL = 'TRACK_VOL'
13096
"""Track Volume"""
13197

132-
UNDO = 'UNDO'
133-
"""Undo"""
134-
135-
VIEW_DETAIL = 'VIEW_DETAIL'
136-
"""Detail view switch"""
137-
VIEW_CLIPTRACK = 'VIEW_CLIPTRACK'
138-
"""Clip/Track view switch"""
139-
140-
ZOOM_UP = 'ZOOM_UP'
141-
"""Session Zoom up"""
142-
ZOOM_DOWN = 'ZOOM_DOWN'
143-
"""Session Zoom down"""
144-
ZOOM_LEFT = 'ZOOM_LEFT'
145-
"""Session Zoom left"""
146-
ZOOM_RIGHT = 'ZOOM_RIGHT'
147-
"""Session Zoom right"""
148-
14998

15099
class AbletonMsg(midiscripter.base.msg_base.Msg):
151100
"""Ableton Live remote script event message"""

midiscripter/ableton_remote/ableton_port.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
from typing import overload, TYPE_CHECKING
22

3-
import midiscripter.midi
3+
from midiscripter.midi import MidiIn, MidiOut, ChannelMsg
44
from midiscripter.base.port_base import Input
55
from midiscripter.logger import log
66
from midiscripter.ableton_remote.ableton_msg import AbletonEvent, AbletonMsg
7-
from midiscripter.ableton_remote.remote_script_midi_mapping import (
8-
ableton_event_to_midi_map,
9-
midi_to_ableton_button_map,
10-
midi_to_ableton_slider_map,
11-
)
7+
from midiscripter.ableton_remote.remote_script_midi_mapping import ableton_event_to_midi_map, midi_to_ableton_button_map, midi_to_ableton_slider_map
128

139
if TYPE_CHECKING:
1410
from collections.abc import Container, Callable
1511
from midiscripter.base.msg_base import Msg
1612

1713

1814
# noinspection PyMethodOverriding
19-
class AbletonIn(midiscripter.midi.MidiIn):
15+
class AbletonIn(MidiIn):
2016
"""Receives MIDI messages from Ableton Live remote script
2117
and produces [`AbletonMsg`][midiscripter.AbletonMsg] objects.
2218
"""
@@ -68,7 +64,7 @@ def subscribe(
6864
return Input.subscribe(self, type, index, value) # bypassing MidiIn method
6965

7066

71-
class AbletonOut(midiscripter.midi.MidiOut):
67+
class AbletonOut(MidiOut):
7268
"""Sends [`AbletonMsg`][midiscripter.AbletonMsg] objects
7369
as MIDI message to Ableton Live remote script.
7470
"""
@@ -81,17 +77,20 @@ def __init__(self, proxy_midi_port_name: str, *, virtual: bool = False):
8177
"""
8278
super().__init__(proxy_midi_port_name, virtual=virtual)
8379

84-
def send(self, msg: AbletonMsg) -> None:
80+
def send(self, msg: AbletonMsg | ChannelMsg) -> None:
8581
"""Send message to Ableton remote script.
8682
8783
Args:
8884
msg: object to send
8985
"""
86+
if isinstance(msg, ChannelMsg):
87+
super().send(msg)
88+
super()._log_msg_sent(msg)
89+
return
90+
9091
try:
9192
if msg.index is None:
9293
midi_lead_attrs = ableton_event_to_midi_map[msg.type]
93-
elif isinstance(msg.index, tuple) and len(msg.index) == 2:
94-
midi_lead_attrs = ableton_event_to_midi_map[msg.type][msg.index[0]][msg.index[1]]
9594
else:
9695
midi_lead_attrs = ableton_event_to_midi_map[msg.type][msg.index]
9796

@@ -102,7 +101,7 @@ def send(self, msg: AbletonMsg) -> None:
102101
else:
103102
value = msg.value
104103

105-
super().send(midiscripter.midi.ChannelMsg(*midi_lead_attrs, value))
104+
super().send(ChannelMsg(*midi_lead_attrs, value))
106105
super()._log_msg_sent(msg)
107106

108107
except (IndexError, KeyError):

0 commit comments

Comments
 (0)