Page 8 of 8

Re: Push 2 Scales

Posted: Wed Nov 11, 2020 9:30 am
by MauriceNorris
Just confirming that this also works with Live 11 beta.

Note however that, although that the newly added scales will still show up in Push, they won't be present in the new 'Scales' list shown in the new MIDI clip editor.

Re: Push 2 Scales

Posted: Mon Dec 21, 2020 5:36 am
by torbenscharling
MauriceNorris wrote:
Wed Nov 11, 2020 9:30 am
Just confirming that this also works with Live 11 beta.

Note however that, although that the newly added scales will still show up in Push, they won't be present in the new 'Scales' list shown in the new MIDI clip editor.
You spoke too soon I think, I repeatedly get this error and/or just a blank screen on the push and nothing but the dot on the pitch bend lighting up in the latest beta 11.0b22 :(

Any anyone please look into why this is happening all of a sudden? Almost looks like a deliberate thing done by Ableton to stop us from upgrading the push like that or what? This looks like deliberate tampering to make it not work all of a sudden, but please prove me wrong or find a way to circumvent this nonsense. Thanks :)

Image

Re: Push 2 Scales

Posted: Mon Jan 04, 2021 12:44 am
by [jur]
L11 now uses v3 of Python, and 3rd party scripts need to be updated to work with it.

Re: Push 2 Scales

Posted: Sun Jan 17, 2021 8:55 pm
by MauriceNorris
torbenscharling wrote:
Mon Dec 21, 2020 5:36 am

You spoke too soon I think, I repeatedly get this error and/or just a blank screen on the push and nothing but the dot on the pitch bend lighting up in the latest beta 11.0b22 :(
Hmm, that's strange. I'm on b23 and am not getting this error at all. The modified melodic_pattern.py is still working fine.

Re: Push 2 Scales

Posted: Wed Jan 20, 2021 3:13 am
by torbenscharling
MauriceNorris wrote:
Sun Jan 17, 2021 8:55 pm
torbenscharling wrote:
Mon Dec 21, 2020 5:36 am

You spoke too soon I think, I repeatedly get this error and/or just a blank screen on the push and nothing but the dot on the pitch bend lighting up in the latest beta 11.0b22 :(
Hmm, that's strange. I'm on b23 and am not getting this error at all. The modified melodic_pattern.py is still working fine.
I'm on 23 now and still does it..Push 2 logo on the push and ableton says Script could not be loaded

Did you compile it fresh with the 11 install or just copy paste it over from the one in the 10 pushbase?

If it works on yours why does it keep saying remote script doesnt work on mine?

Works fine on ableton 10 beta still...same push, same machine..doesn't work on 11 beta 22 and 11 beta 23..

I don't know much about compiling pyc files and stuff like that I just want additional scales to work like they do on 10, since I payed for the upgrade to 11, so far I'm stuck with the info that it needs to be updated to python 3 but if that's not the case, the original "dev" doesn't have to be bothered making a new .pyc or if I can just change something manually in it, but I need to know where to go from here to fix this issue.

Re: Push 2 Scales

Posted: Wed Jan 20, 2021 2:59 pm
by MauriceNorris
@torbenscharling

I didn't compile it, no. You don't need to. Ableton does the compiling for you (from the .py file)

I just copied the melodic_pattern.py from the Live 10 to Live 11.

Are you copying the .py or the .pyc file to Ableton 11? If it's the .pyc then this will almost certainly produce an error as this is the file that Ableton 10 has already compiled. Ableton needs to 'see' the .py file and then compile it itself.

You're welcome to use my melodic_pattern.py though if you like? You can download it here:
https://drive.google.com/file/d/1Sm-0Ze ... sp=sharing

Just make sure you backup your original melodic_pattern.pyc (which I'm guessing you already have) and delete the melodic_pattern.pyc script that isn't working for you. If it's working for you (and I can't see any reason it shouldn't) you should just be able to copy the part where you define the scales from your old script into mine.

Hope this helps.

Re: Push 2 Scales

Posted: Thu Jan 21, 2021 2:59 am
by torbenscharling
MauriceNorris wrote:
Wed Jan 20, 2021 2:59 pm
@torbenscharling

I didn't compile it, no. You don't need to. Ableton does the compiling for you (from the .py file)

I just copied the melodic_pattern.py from the Live 10 to Live 11.

Are you copying the .py or the .pyc file to Ableton 11? If it's the .pyc then this will almost certainly produce an error as this is the file that Ableton 10 has already compiled. Ableton needs to 'see' the .py file and then compile it itself.

You're welcome to use my melodic_pattern.py though if you like? You can download it here:
https://drive.google.com/file/d/1Sm-0Ze ... sp=sharing

Just make sure you backup your original melodic_pattern.pyc (which I'm guessing you already have) and delete the melodic_pattern.pyc script that isn't working for you. If it's working for you (and I can't see any reason it shouldn't) you should just be able to copy the part where you define the scales from your old script into mine.

Hope this helps.
Indeed it does help tremendously !!! THANKS SO MUCH !! This has been bugging the h... out of me cause I couldn't get ableton support on this so I had no idea if I could safely upgrade to 11 and had to do it in time to get the 20% discount so now I got both bases covered so I'm ready for when 11 is released (apart from reading the damn manual lol) anyway thanks again, now I don't have to worry so much about what happened. But just in case anyone has a clue what went wrong (I'm pretty sure I did the same, copying over the .py file) so here it is copy pasted (the one giving me the error consistently):

# uncompyle6 version 3.7.0
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.16 (v2.7.16:413a49145e, Mar 4 2019, 01:37:19) [MSC v.1500 64 bit (AMD64)]
# Embedded file name: c:\Jenkins\live\output\Live\win_64_static\Release\python-bundle\MIDI Remote Scripts\pushbase\melodic_pattern.py
# Compiled at: 2020-01-07 14:24:16
from __future__ import absolute_import, print_function, unicode_literals
from ableton.v2.base import NamedTuple, lazy_attribute, memoize, find_if
from . import consts
from .matrix_maps import FEEDBACK_CHANNELS
import Live
CIRCLE_OF_FIFTHS = tuple([ 7 * k % 12 for k in range(12) ])
ROOT_NOTES = CIRCLE_OF_FIFTHS[0:6] + CIRCLE_OF_FIFTHS[-1:5:-1]
NOTE_NAMES = (u'C', u'D\u266d', u'D', u'E\u266d', u'E', u'F', u'G\u266d', u'G', u'A\u266d',
u'A', u'B\u266d', u'B')

def pitch_index_to_string(index):
if 0 <= index < 128:
return NOTE_NAMES[(index % 12)] + str(index / 12 - 2)
return consts.CHAR_ELLIPSIS


class Scale(NamedTuple):
name = ''
notes = []

def to_root_note(self, root_note):
return Scale(name=NOTE_NAMES[root_note], notes=[ root_note + x for x in self.notes ])

@memoize
def scale_for_notes(self, notes):
return [ self.to_root_note(b) for b in notes ]

def __unicode__(self):
return self.name

def __str__(self):
return unicode(self).encode('utf-8')

def __eq__(self, other):
if isinstance(other, Scale):
return self.name == other.name and self.notes == other.notes
return False


EXTRA_SCALES = (
("ionian pentatonic", (0, 4, 5, 7, 11)),
("mixolydian pent.", (0, 4, 5, 7, 10)),
("ritusen", (0, 2, 5, 7, 9)),
("egyptian", (0, 2, 5, 7, 10)),
("neo. major pent.", (0, 4, 5, 6, 10)),
("vietnamese 1", (0, 3, 5, 7, 8)),
("lydian pentatonic", (0, 4, 6, 7, 11)),
("malkos raga", (0, 3, 5, 8, 10)),
("locrian pentatonic", (0, 3, 5, 6, 10)),
("minor six pent.", (0, 3, 5, 7, 9)),
("flat six pent.", (0, 2, 4, 7, 8)),
("scriabin", (0, 1, 4, 7, 9)),
("whole tone pent.", (0, 4, 6, 8, 10)),
("lydian #5P pent.", (0, 4, 6, 8, 11)),
("lydian dom. pent.", (0, 4, 6, 7, 10)),
("minor #7M pent.", (0, 3, 5, 7, 11)),
("sup. locrian pent.", (0, 3, 4, 6, 10)),
("minor hexatonic", (0, 2, 3, 5, 7, 11)),
("augmented", (0, 3, 4, 7, 8, 11)),
("major blues", (0, 2, 3, 4, 7, 9)),
("piongio", (0, 2, 5, 7, 9, 10)),
("prometheus neo.", (0, 1, 4, 6, 9, 10)),
("prometheus", (0, 2, 4, 6, 9, 10)),
("mystery #1", (0, 1, 4, 6, 8, 10)),
("6-tone symmetric", (0, 1, 4, 5, 8, 9)),
("locrian major", (0, 2, 4, 5, 6, 8, 10)),
("dbl. harm. lydian", (0, 1, 4, 6, 7, 8, 11)),
("locrian #2", (0, 2, 3, 5, 6, 8, 10)),
("mixolydian b6", (0, 2, 4, 5, 7, 8, 10)),
("dorian b2", (0, 1, 3, 5, 7, 9, 10)),
("ultralocrian", (0, 1, 3, 4, 6, 8, 9)),
("locrian 6", (0, 1, 3, 5, 6, 9, 10)),
("augmented hep.", (0, 3, 4, 5, 7, 8, 11)),
("lydian diminished", (0, 2, 3, 6, 7, 9, 11)),
("lead. whole tone", (0, 2, 4, 6, 8, 10, 11)),
("lydian minor", (0, 2, 4, 6, 7, 8, 10)),
("balinese", (0, 1, 3, 5, 7, 8, 11)),
("neopolitan major", (0, 1, 3, 5, 7, 9, 11)),
("hungarian major", (0, 3, 4, 6, 7, 9, 10)),
("oriental", (0, 1, 4, 5, 6, 9, 10)),
("flamenco", (0, 1, 3, 4, 6, 7, 10)),
("todi raga", (0, 1, 3, 6, 7, 8, 11)),
("persian", (0, 1, 4, 5, 6, 8, 11)),
("enigmatic", (0, 1, 4, 6, 8, 10, 11)),
("major augmented", (0, 2, 4, 5, 8, 9, 11)),
("lydian #9", (0, 3, 4, 6, 7, 9, 11)),
("purvi raga", (0, 1, 4, 5, 6, 7, 8, 11)),
("spanish hept.", (0, 1, 3, 4, 5, 7, 8, 10)),
("bebop", (0, 2, 4, 5, 7, 9, 10, 11)),
("bebop minor", (0, 2, 3, 4, 5, 7, 9, 10)),
("bebop major", (0, 2, 4, 5, 7, 8, 9, 11)),
("bebop locrian", (0, 1, 3, 5, 6, 7, 8, 10)),
("minor bebop", (0, 2, 3, 5, 7, 8, 10, 11)),
("ichikosucho", (0, 2, 4, 5, 6, 7, 9, 11)),
("minor six dim.", (0, 2, 3, 5, 7, 8, 9, 11)),
("kafi raga", (0, 3, 4, 5, 7, 9, 10, 11)),
("composite blues", (0, 2, 3, 4, 5, 6, 7, 9, 10))
)

SCALES = tuple([ Scale(name=x[0], notes=x[1]) for x in Live.Song.get_all_scales_ordered() + EXTRA_SCALES ])

def scale_by_name(name):
return find_if(lambda m: m.name == name, SCALES)


class NoteInfo(NamedTuple):
index = None
channel = 0
color = 'NoteInvalid'


class MelodicPattern(NamedTuple):
steps = [
0, 0]
scale = range(12)
root_note = 0
origin = [0, 0]
chromatic_mode = False
width = None
height = None

@lazy_attribute
def extended_scale(self):
if self.chromatic_mode:
first_note = self.scale[0]
return range(first_note, first_note + 12)
else:
return self.scale

@property
def is_aligned(self):
return not self.origin[0] and not self.origin[1] and abs(self.root_note) % 12 == self.extended_scale[0]

def note(self, x, y):
if not self._boundary_reached(x, y):
channel = y % len(FEEDBACK_CHANNELS) + FEEDBACK_CHANNELS[0]
return self._get_note_info(self._octave_and_note(x, y), self.root_note, channel)
return NoteInfo()

def __getitem__(self, i):
root_note = self.root_note
if root_note <= -12:
root_note = 0 if self.is_aligned else -12
return self._get_note_info(self._octave_and_note_linear(i), root_note)

def _boundary_reached(self, x, y):
return self.width is not None and x >= self.width or self.height is not None and y >= self.height

def _octave_and_note_by_index(self, index):
scale = self.extended_scale
scale_size = len(scale)
octave = index / scale_size
note = scale[(index % scale_size)]
return (octave, note)

def _octave_and_note(self, x, y):
index = self.steps[0] * (self.origin[0] + x) + self.steps[1] * (self.origin[1] + y)
return self._octave_and_note_by_index(index)

def _color_for_note(self, note):
if note == self.scale[0]:
return 'NoteBase'
else:
if note in self.scale:
return 'NoteScale'
return 'NoteNotScale'

def _get_note_info(self, (octave, note), root_note, channel=0):
note_index = 12 * octave + note + root_note
if 0 <= note_index <= 127:
return NoteInfo(index=note_index, channel=channel, color=self._color_for_note(note))
else:
return NoteInfo()

def _octave_and_note_linear(self, i):
origin = self.origin[0] or self.origin[1]
index = origin + i
return self._octave_and_note_by_index(index)

Re: Push 2 Scales

Posted: Thu Jan 21, 2021 9:19 am
by MauriceNorris
@torbenscharling

No worries! Glad you got it working in the end :)

There do seem to be quite a few differences in the script that wasn't working for you and the one that is, although I don't know enough about Python to know what these differences actually mean!

Here's the working one for reference if anyone else needs it:

Code: Select all

    # uncompyle6 version 3.6.4
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.17 (default, Sep 30 2020, 13:38:04) 
# [GCC 7.5.0]
# Embedded file name: /Users/versonator/Jenkins/live/output/Live/mac_64_static/Release/python-bundle/MIDI Remote Scripts/pushbase/melodic_pattern.py
# Compiled at: 2020-11-06 11:19:13
from __future__ import absolute_import, print_function, unicode_literals
from __future__ import division
from builtins import str
from builtins import range
from past.utils import old_div
from ableton.v2.base import NamedTuple, lazy_attribute, memoize, find_if
from . import consts
from .matrix_maps import FEEDBACK_CHANNELS
import Live
CIRCLE_OF_FIFTHS = tuple([ 7 * k % 12 for k in range(12) ])
ROOT_NOTES = CIRCLE_OF_FIFTHS[0:6] + CIRCLE_OF_FIFTHS[-1:5:-1]
NOTE_NAMES = (u'C', u'D\u266d', u'D', u'E\u266d', u'E', u'F', u'G\u266d', u'G', u'A\u266d',
              u'A', u'B\u266d', u'B')

def pitch_index_to_string(index):
    if index is not None and 0 <= index < 128:
        return NOTE_NAMES[(index % 12)] + str(old_div(index, 12) - 2)
    else:
        return consts.CHAR_ELLIPSIS


class Scale(NamedTuple):
    name = ''
    notes = []

    def to_root_note(self, root_note):
        return Scale(name=NOTE_NAMES[root_note], notes=[ root_note + x for x in self.notes ])

    @memoize
    def scale_for_notes(self, notes):
        return [ self.to_root_note(b) for b in notes ]

    def __unicode__(self):
        return self.name

    def __str__(self):
        return str(self.name)

    def __eq__(self, other):
        if isinstance(other, Scale):
            return self.name == other.name and self.notes == other.notes
        return False

    def __hash__(self):
        return hash((self.name,))

EXTRA_SCALES = (
("Ionian Pentatonic", (0, 4, 5, 7, 11)),
("Mixolydian Pent.", (0, 4, 5, 7, 10)),
("Ritusen", (0, 2, 5, 7, 9)),
("Egyptian", (0, 2, 5, 7, 10)),
("Neo. Major Pent.", (0, 4, 5, 6, 10)),
("Vietnamese 1", (0, 3, 5, 7, 8)),
("Lydian Pentatonic", (0, 4, 6, 7, 11)),
("Malkos Raga", (0, 3, 5, 8, 10)),
("Locrian Pentatonic", (0, 3, 5, 6, 10)),
("Minor Six Pent.", (0, 3, 5, 7, 9)),
("Flat Six Pent.", (0, 2, 4, 7, 8)),
("Scriabin", (0, 1, 4, 7, 9)),
("Whole Tone Pent.", (0, 4, 6, 8, 10)),
("Lydian #5P Pent.", (0, 4, 6, 8, 11)),
("Lydian Dom. Pent.", (0, 4, 6, 7, 10)),
("Minor #7M Pent.", (0, 3, 5, 7, 11)),
("Sup. Locrian Pent.", (0, 3, 4, 6, 10)),
("Minor Hexatonic", (0, 2, 3, 5, 7, 11)),
("Augmented", (0, 3, 4, 7, 8, 11)),
("Major Blues", (0, 2, 3, 4, 7, 9)),
("Piongio", (0, 2, 5, 7, 9, 10)),
("Prometheus Neo.", (0, 1, 4, 6, 9, 10)),
("Prometheus", (0, 2, 4, 6, 9, 10)),
("Mystery #1", (0, 1, 4, 6, 8, 10)),
("6-tone Symmetric", (0, 1, 4, 5, 8, 9)),
("Locrian Major", (0, 2, 4, 5, 6, 8, 10)),
("Dbl. Harm. Lydian", (0, 1, 4, 6, 7, 8, 11)),
("Locrian #2", (0, 2, 3, 5, 6, 8, 10)),
("Mixolydian B6", (0, 2, 4, 5, 7, 8, 10)),
("Dorian B2", (0, 1, 3, 5, 7, 9, 10)),
("Ultralocrian", (0, 1, 3, 4, 6, 8, 9)),
("Locrian 6", (0, 1, 3, 5, 6, 9, 10)),
("Augmented Hep.", (0, 3, 4, 5, 7, 8, 11)),
("Lydian Diminished", (0, 2, 3, 6, 7, 9, 11)),
("Lead. Whole Tone", (0, 2, 4, 6, 8, 10, 11)),
("Lydian Minor", (0, 2, 4, 6, 7, 8, 10)),
("Balinese", (0, 1, 3, 5, 7, 8, 11)),
("Neopolitan Major", (0, 1, 3, 5, 7, 9, 11)),
("Hungarian Major", (0, 3, 4, 6, 7, 9, 10)),
("Oriental", (0, 1, 4, 5, 6, 9, 10)),
("Flamenco", (0, 1, 3, 4, 6, 7, 10)),
("Todi Raga", (0, 1, 3, 6, 7, 8, 11)),
("Persian", (0, 1, 4, 5, 6, 8, 11)),
("Enigmatic", (0, 1, 4, 6, 8, 10, 11)),
("Major Augmented", (0, 2, 4, 5, 8, 9, 11)),
("Lydian #9", (0, 3, 4, 6, 7, 9, 11)),
("Purvi Raga", (0, 1, 4, 5, 6, 7, 8, 11)),
("Spanish Hept.", (0, 1, 3, 4, 5, 7, 8, 10)),
("Bebop", (0, 2, 4, 5, 7, 9, 10, 11)),
("Bebop Minor", (0, 2, 3, 4, 5, 7, 9, 10)),
("Bebop Major", (0, 2, 4, 5, 7, 8, 9, 11)),
("Bebop Locrian", (0, 1, 3, 5, 6, 7, 8, 10)),
("Minor Bebop", (0, 2, 3, 5, 7, 8, 10, 11)),
("Ichikosucho", (0, 2, 4, 5, 6, 7, 9, 11)),
("Minor Six Dim.", (0, 2, 3, 5, 7, 8, 9, 11)),
("Kafi Raga", (0, 3, 4, 5, 7, 9, 10, 11)),
("Composite Blues", (0, 2, 3, 4, 5, 6, 7, 9, 10))
)


SCALES = tuple([ Scale(name=x[0], notes=x[1]) for x in Live.Song.get_all_scales_ordered() + EXTRA_SCALES ])

def scale_by_name(name):
    return find_if(lambda m: m.name == name, SCALES)


class NoteInfo(NamedTuple):
    index = None
    channel = 0
    color = 'NoteInvalid'


class MelodicPattern(NamedTuple):
    steps = [
     0, 0]
    scale = list(range(12))
    root_note = 0
    origin = [0, 0]
    chromatic_mode = False
    width = None
    height = None

    @lazy_attribute
    def extended_scale(self):
        if self.chromatic_mode:
            first_note = self.scale[0]
            return list(range(first_note, first_note + 12))
        else:
            return self.scale

    @property
    def is_aligned(self):
        return not self.origin[0] and not self.origin[1] and abs(self.root_note) % 12 == self.extended_scale[0]

    def note(self, x, y):
        if not self._boundary_reached(x, y):
            channel = y % len(FEEDBACK_CHANNELS) + FEEDBACK_CHANNELS[0]
            return self._get_note_info(self._octave_and_note(x, y), self.root_note, channel)
        return NoteInfo()

    def __getitem__(self, i):
        root_note = self.root_note
        if root_note <= -12:
            root_note = 0 if self.is_aligned else -12
        return self._get_note_info(self._octave_and_note_linear(i), root_note)

    def _boundary_reached(self, x, y):
        return self.width is not None and x >= self.width or self.height is not None and y >= self.height

    def _octave_and_note_by_index(self, index):
        scale = self.extended_scale
        scale_size = len(scale)
        octave = old_div(index, scale_size)
        note = scale[(index % scale_size)]
        return (
         octave, note)

    def _octave_and_note(self, x, y):
        index = self.steps[0] * (self.origin[0] + x) + self.steps[1] * (self.origin[1] + y)
        return self._octave_and_note_by_index(index)

    def _color_for_note(self, note):
        if note == self.scale[0]:
            return 'NoteBase'
        else:
            if note in self.scale:
                return 'NoteScale'
            return 'NoteNotScale'

    def _get_note_info(self, octave_note, root_note, channel=0):
        octave, note = octave_note
        note_index = 12 * octave + note + root_note
        if 0 <= note_index <= 127:
            return NoteInfo(index=note_index, channel=channel, color=self._color_for_note(note))
        else:
            return NoteInfo()

    def _octave_and_note_linear(self, i):
        origin = self.origin[0] or self.origin[1]
        index = origin + i
        return self._octave_and_note_by_index(index)
 

Re: Push 2 Scales

Posted: Wed Aug 02, 2023 3:55 am
by jschunick
I recently purchased a push 3 (standalone) and was hoping to add more scales to the menu. I found this thread and followed the steps....however, nothing changes on the push 3 when I restart ableton / power cycle the push 3.

I decompiled the melodic_pattern.pyc file and created a new melodic_pattern.py file with the following:

Code: Select all

# decompyle3 version 3.9.0
# Python bytecode version base 3.7.0 (3394)
# Decompiled from: Python 3.9.5 (tags/v3.9.5:0a7dcbd, May  3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)]
# Embedded file name: ..\..\..\output\Live\win_64_static\Release\python-bundle\MIDI Remote Scripts\pushbase\melodic_pattern.py
# Compiled at: 2023-03-03 08:20:15
# Size of source mod 2**32: 4653 bytes
from __future__ import absolute_import, division, print_function, unicode_literals
from builtins import range, str
from past.utils import old_div
import Live
from ableton.v2.base import NamedTuple, find_if, lazy_attribute, memoize
from . import consts
from .matrix_maps import FEEDBACK_CHANNELS
CIRCLE_OF_FIFTHS = tuple([7 * k % 12 for k in range(12)])
ROOT_NOTES = CIRCLE_OF_FIFTHS[0:6] + CIRCLE_OF_FIFTHS[-1:5:-1]
NOTE_NAMES = ('C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B')

def pitch_index_to_string(index):
    if index is not None:
        if 0<= index < 128:
            return NOTE_NAMES[index % 12] + str(old_div(index, 12) - 2)
    return consts.CHAR_ELLIPSIS


class Scale(NamedTuple):
    name = ''
    notes = []

    def to_root_note(self, root_note):
        return Scale(name=(NOTE_NAMES[root_note]),
          notes=[root_note + x for x in self.notes])

    @memoize
    def scale_for_notes(self, notes):
        return [self.to_root_note(b) for b in notes]

    def __unicode__(self):
        return self.name

    def __str__(self):
        return str(self.name)

    def __eq__(self, other):
        if isinstance(other, Scale):
            return self.name == other.name and self.notes == other.notes
        return False

    def __hash__(self):
        return hash((self.name,))

EXTRA_SCALES = (
("Ionian Pentatonic", (0, 4, 5, 7, 11)),
("Mixolydian Pent.", (0, 4, 5, 7, 10)),
("Ritusen", (0, 2, 5, 7, 9)),
("Egyptian", (0, 2, 5, 7, 10)),
("Neo. Major Pent.", (0, 4, 5, 6, 10)),
("Vietnamese 1", (0, 3, 5, 7, 8)),
("Lydian Pentatonic", (0, 4, 6, 7, 11)),
("Malkos Raga", (0, 3, 5, 8, 10)),
("Locrian Pentatonic", (0, 3, 5, 6, 10)),
("Minor Six Pent.", (0, 3, 5, 7, 9)),
("Flat Six Pent.", (0, 2, 4, 7, 8)),
("Scriabin", (0, 1, 4, 7, 9)),
("Whole Tone Pent.", (0, 4, 6, 8, 10)),
("Lydian #5P Pent.", (0, 4, 6, 8, 11)),
("Lydian Dom. Pent.", (0, 4, 6, 7, 10)),
("Minor #7M Pent.", (0, 3, 5, 7, 11)),
("Sup. Locrian Pent.", (0, 3, 4, 6, 10)),
("Minor Hexatonic", (0, 2, 3, 5, 7, 11)),
("Augmented", (0, 3, 4, 7, 8, 11)),
("Major Blues", (0, 2, 3, 4, 7, 9)),
("Piongio", (0, 2, 5, 7, 9, 10)),
("Prometheus Neo.", (0, 1, 4, 6, 9, 10)),
("Prometheus", (0, 2, 4, 6, 9, 10)),
("Mystery #1", (0, 1, 4, 6, 8, 10)),
("6-tone Symmetric", (0, 1, 4, 5, 8, 9)),
("Locrian Major", (0, 2, 4, 5, 6, 8, 10)),
("Dbl. Harm. Lydian", (0, 1, 4, 6, 7, 8, 11)),
("Locrian #2", (0, 2, 3, 5, 6, 8, 10)),
("Mixolydian B6", (0, 2, 4, 5, 7, 8, 10)),
("Dorian B2", (0, 1, 3, 5, 7, 9, 10)),
("Ultralocrian", (0, 1, 3, 4, 6, 8, 9)),
("Locrian 6", (0, 1, 3, 5, 6, 9, 10)),
("Augmented Hep.", (0, 3, 4, 5, 7, 8, 11)),
("Lydian Diminished", (0, 2, 3, 6, 7, 9, 11)),
("Lead. Whole Tone", (0, 2, 4, 6, 8, 10, 11)),
("Lydian Minor", (0, 2, 4, 6, 7, 8, 10)),
("Balinese", (0, 1, 3, 5, 7, 8, 11)),
("Neopolitan Major", (0, 1, 3, 5, 7, 9, 11)),
("Hungarian Major", (0, 3, 4, 6, 7, 9, 10)),
("Oriental", (0, 1, 4, 5, 6, 9, 10)),
("Flamenco", (0, 1, 3, 4, 6, 7, 10)),
("Todi Raga", (0, 1, 3, 6, 7, 8, 11)),
("Persian", (0, 1, 4, 5, 6, 8, 11)),
("Enigmatic", (0, 1, 4, 6, 8, 10, 11)),
("Major Augmented", (0, 2, 4, 5, 8, 9, 11)),
("Lydian #9", (0, 3, 4, 6, 7, 9, 11)),
("Purvi Raga", (0, 1, 4, 5, 6, 7, 8, 11)),
("Spanish Hept.", (0, 1, 3, 4, 5, 7, 8, 10)),
("Bebop", (0, 2, 4, 5, 7, 9, 10, 11)),
("Bebop Minor", (0, 2, 3, 4, 5, 7, 9, 10)),
("Bebop Major", (0, 2, 4, 5, 7, 8, 9, 11)),
("Bebop Locrian", (0, 1, 3, 5, 6, 7, 8, 10)),
("Minor Bebop", (0, 2, 3, 5, 7, 8, 10, 11)),
("Ichikosucho", (0, 2, 4, 5, 6, 7, 9, 11)),
("Minor Six Dim.", (0, 2, 3, 5, 7, 8, 9, 11)),
("Kafi Raga", (0, 3, 4, 5, 7, 9, 10, 11)),
("Composite Blues", (0, 2, 3, 4, 5, 6, 7, 9, 10))
)


SCALES = tuple([ Scale(name=x[0], notes=x[1]) for x in Live.Song.get_all_scales_ordered() + EXTRA_SCALES ])

#SCALES = tuple([Scale(name=(x[0]), notes=(x[1])) for x in Live.Song.get_all_scales_ordered()])

def scale_by_name(name):
    return find_if(lambda m: m.name == name
, SCALES)


class NoteInfo(NamedTuple):
    index = None
    channel = 0
    color = 'NoteInvalid'


class MelodicPattern(NamedTuple):
    steps = [
     0, 0]
    scale = list(range(12))
    root_note = 0
    origin = [0, 0]
    chromatic_mode = False
    width = None
    height = None

    @lazy_attribute
    def extended_scale(self):
        if self.chromatic_mode:
            first_note = self.scale[0]
            return list(range(first_note, first_note + 12))
        return self.scale

    @property
    def is_aligned(self):
        return not self.origin[0] and not self.origin[1] and abs(self.root_note) % 12 == self.extended_scale[0]

    def note(self, x, y):
        if not self._boundary_reached(x, y):
            channel = y % len(FEEDBACK_CHANNELS) + FEEDBACK_CHANNELS[0]
            return self._get_note_info(self._octave_and_note(x, y), self.root_note, channel)
        return NoteInfo()

    def __getitem__(self, i):
        root_note = self.root_note
        if root_note <= -12:
            root_note = 0 if self.is_aligned else -12
        return self._get_note_info(self._octave_and_note_linear(i), root_note)

    def _boundary_reached(self, x, y):
        return (self.width is not None) and (x >= self.width) or (self.height is not None and y >= self.height)

    def _octave_and_note_by_index(self, index):
        scale = self.extended_scale
        scale_size = len(scale)
        octave = old_div(index, scale_size)
        note = scale[index % scale_size]
        return (
         octave, note)

    def _octave_and_note(self, x, y):
        index = self.steps[0] * (self.origin[0] + x) + self.steps[1] * (self.origin[1] + y)
        return self._octave_and_note_by_index(index)

    def _color_for_note(self, note):
        if note == self.scale[0]:
            return 'NoteBase'
        if note in self.scale:
            return 'NoteScale'
        return 'NoteNotScale'

    def _get_note_info(self, octave_note, root_note, channel=0):
        octave, note = octave_note
        note_index = 12 * octave + note + root_note
        if 0<= note_index <= 127:
            return NoteInfo(index=note_index,
              channel=channel,
              color=(self._color_for_note(note)))
        return NoteInfo()

    def _octave_and_note_linear(self, i):
        origin = self.origin[0] or self.origin[1]
        index = origin + i
        return self._octave_and_note_by_index(index)
My music production setup is currently in Windows, so I put the file in

Code: Select all

C:\ProgramData\Ableton\Live 11 Suite\Resources\MIDI Remote Scripts\pushbase
and made sure to rename the old melodic_pattern.pyc to melodic_pattern.pyc_old.

I've tried power cycling and restarting the software multiple times, but haven't had any luck getting new scales to appear. Has anyone successfully done this with a Push 3 standalone and the latest version of Live 11? BTW, I am currently using the push 3 in controller mode for this mod, so I would expect it to work similarly to Push 2. Any help would be appreciated... :?

Re: Push 2 Scales

Posted: Mon Oct 16, 2023 12:03 am
by Hamcat
Works in live 11 thank you !!
Also, I'd like to change the layout (4ths, 3ds...)
Can I do this ?

Re: Push 2 Scales

Posted: Mon Oct 16, 2023 3:31 pm
by elbows
jschunick wrote:
Wed Aug 02, 2023 3:55 am
I've tried power cycling and restarting the software multiple times, but haven't had any luck getting new scales to appear. Has anyone successfully done this with a Push 3 standalone and the latest version of Live 11? BTW, I am currently using the push 3 in controller mode for this mod, so I would expect it to work similarly to Push 2. Any help would be appreciated... :?
All the files are in a different place for the Push 3. But when I tried changing them it still didnt work, if I add new scales then the whole thing crashes. Maybe its because the Push 3 has to have code in Ableton Live which will do the MPE pitch bends between notes in the scales on the pad grid, and this involves code on the non-python side of things which we cant modify, and that perhaps that code goes wrong when unexpected scales are included in the list generated on the python side of things. However this is just one theory, there could be a different issue.

I cant tell you where the Push 3 version is on Windows. On mac they are within a bundle called Push3 inside a folder called Helpers.

Re: Push 2 Scales

Posted: Tue Mar 05, 2024 8:23 pm
by CryptoJimBo
Does not work with Live 12. Any help would be greatly appreciated.