<commit_before>from voluptuous import Schema, Required, All, Length, Range, MultipleInvalid
from threading import Lock
import yaml
import logging
import random
import time
import utils
import sys
import traceback
from copy import copy
from Queue import Queue
from string import ascii_lowercase
from numpy import rot90
from music21 import note
from athenaCL.libATH import grammar
from athenaCL.libATH import markov

lock = Lock()

class Generator(object):
    OFFSET = 0
    VALUES = 1
    
    def __init__(self, filename, config):
        """
        >>> g = Generator("../resources/presets/default.yml", {})
        >>> g.state["instrument"]
        23
        >>> g.state["bpm"]
        110
        """        
        # The state of the generator describes what the generator will generate.
        # It follows the AthenaCL standard.
        self.file = filename
        
        # The Notebuffer is where generated notes are stored before being sent to 
        # the synthesizer
        self.queue = Queue()
        
        # This variable gives the size of the queue. This is required because Mac OS X
        # does not support queue.qsize()
        self.size = 0
        self.size_lock = Lock()
        self.rhythm_lock = Lock()
        
        self.schema = Schema({
            Required('instrument'): All(int, Range(min=1, max=32)),
            Required('bpm'): All(int, Range(30, 210)),
            Required('path'): {
                Required('pattern'): All(list, Length(8)),
                Required('order'): str
            },
            Required('pitch'): {
                Required('pattern'): [
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8))
                ],
                Required('order'): str
            },
            Required('rhythm'): {
                Required('pattern'): [
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8))
                ],
                Required('order'): str,
                Required('dividor') : int
            },
            Required('amplitude'): {
                Required('pattern'): All(list, Length(8)),
                Required('order'): str
            }
        })
        
        self.loadState()
        
        # This is used to say if the music generation is active or not.
        self.active = True
        
        # This is used by the gui for playing/pausing.
        self.playing = False
    
    def loadState(self):
        """
        Loads the application generator state.
        """
        # This is the range of the selector for each feature.
        self.feature_sizes = { 'pitch' : 8, 'path' : 8, 'amplitude' : 8, 'rhythm' : 4}
        self.state = self.readState()
    
    def readState(self):
        return self.readStateFromFile(self.file)
        
    def readStateFromFile(self, file):
        # Read algorithm state
        try:
            with open(file) as f:
                state = yaml.load(f)
        except IOError:
            t, v, tb = sys.exc_info()
            print v
            traceback.print_tb(tb)
            sys.exit(1)
        
        # Check that preset file is well-formed.
        try:
            self.__checkStateFormat(state)
        except MultipleInvalid:
            t, v, tb = sys.exc_info()
            print v
            traceback.print_tb(tb)
            sys.exit(1)
        
        # Fills the blanks for all of the non markov stuff.
        for attributeName in ['rhythm', 'amplitude', 'path', 'pitch']:
            # Assign the order engine
            self.__computeOrderEng(state, attributeName)
        
        state['rhythm']['pattern'] = self.serialize_rhythm(state['rhythm']['pattern'])
        state['pitch']['pattern'] = self.serialize_pitch(state['pitch']['pattern'])
        
        # Indicates currently selected path on Path Map. Value can be 0-7.
        state['path']['selected'] = 0
        state['rhythm']['row_idx'] = 0
        return state
    
    def __checkStateFormat(self, state):
        """
        Checks that the input state object is a dictionary valid
        which follows the expected format.
        """
        self.schema(state)
    
    def __computeOrderEng(self, parameters, attributeName):
        """
        This method computes the order parameter of some of the attributes of the state
        """
        if attributeName not in ["path", "rhythm", "pitch", "amplitude"]:
            raise Exception("Attempting to assign an order to an invalid attribute!")
        else:
            size = self.feature_sizes[attributeName]
            # If not already set, put a random value here.
            if parameters[attributeName]['order'] == 'cyclic':
                parameters[attributeName]['order_eng'] = CyclicGenerator(size)
            elif parameters[attributeName]['order'] == 'random':
                parameters[attributeName]['order_eng'] = UniformRandomGenerator(size)
            else:
                raise Exception("Order type %s is invalid!", parameters[attributeName]['order'])
        return
    
    def update(self, attribute, value):
        """
        This method updates the current state of the generator. Expected attribute/value
        combination are given below in the format:
        
            attribute : expected value
        
        List :
            "bpm" : int
            "instrument" : int
            "* order" : str
            "path select" : int
            "path pattern" : int
            "rhythm pattern %d" : (row) list
            "pitch pattern %d" : (column) list
            "amplitude pattern %d" : float
        """
        try:
            if attribute == "bpm":
                self.state['bpm'] = value
            elif attribute == "instrument":
                self.state['instrument'] = value + 1
                self.__change_instrument(value)
            elif attribute == "path order":
                self.__updateOrder('path', value)
            elif attribute.startswith('path pattern'):
                selected_path = self.state['path']['selected']
                self.state['path']['pattern'][selected_path] = value
            elif attribute == 'path select':
                self.state['path']['selected'] = value
            elif attribute == "rhythm order":
                self.__updateOrder('rhythm', value)
            elif attribute.startswith("rhythm pattern"):
                idx = int(attribute.split(" ")[-1])
                cur = 0
                while cur < 8 and value[cur] == 0:
                    cur += 1
                offset = cur
                values = []
                while cur < 8:
                    cur += 1
                    val = cur - 1
                    while cur < 8 and value[cur] == 0:
                        cur += 1
                    values.append(cur - val)
                self.rhythm_lock.acquire() # Hot code which needs to be locked.
                self.state['rhythm']['pattern'][idx] = (offset, values)
                self.rhythm_lock.release()
            elif attribute.startswith("rhythm dividor"):
                self.state['rhythm']['dividor'] = value
            elif attribute.startswith("pitch pattern"):
                idx = int(attribute.split(" ")[-1])
                pitch_column = []
                for i in xrange(11):
                    if value[i] == 1:
                        pitch_column.append(5-i)
                self.state['pitch']['pattern'][idx] = pitch_column
            elif attribute.startswith("pitch order"):
                self.__updateOrder('pitch', value)
            elif attribute.startswith('amplitude pattern'):
                idx = int(attribute.split(" ")[-1])
                self.state['amplitude']['pattern'][idx] = value
            elif attribute.startswith("amplitude order"):
                self.__updateOrder('amplitude', value)
            else:
                # Should not happen
                raise Exception("generator.update() called with invalid attribute {}".format(attribute))
            return True
        except:
            t, v, tb = sys.exc_info()
            print t
            print v
            traceback.print_tb(tb)
            return False
    
    def apply_algorithm(self, category, algorithm):
        if algorithm == 'shiftLeft':
            self.shift_left(category)
        elif algorithm == 'shiftRight':
            self.shift_right(category)
        elif algorithm == 'shiftUp':
            self.shift_up(category)
        elif algorithm == 'shiftDown':
            self.shift_down(category)
        elif algorithm == 'retrograde':
            self.retrograde(category)
        elif algorithm == 'inverse':
            self.inverse(category)
        elif algorithm == 'retrograde-inverse':
            self.retrogradeInverse(category)
        elif algorithm == 'raiseOctave':
            self.raiseOctave(category)
        elif algorithm == 'lowerOctave':
            self.lowerOctave(category)
    
    def shift_left(self, category):
        if category == 'rhythm':
            rhythm = self.deserialize_rhythm(self.state['rhythm']['pattern'])
            rhythm = rot90(rhythm, 3) # Rotate because pitches are column based.
            rhythm = rhythm.tolist()
            rhythm = self.__rotate(rhythm, -1)
            rhythm = rot90(rhythm) # Rotate back.
            rhythm = rhythm.tolist()
            self.state['rhythm']['pattern'] = self.serialize_rhythm(rhythm)
        elif category == 'pitch':
            pitch = self.deserialize_pitch(self.state['pitch']['pattern'])
            pitch = rot90(pitch, 3) # Rotate because pitches are column based.
            pitch = pitch.tolist()
            pitch = self.__rotate(pitch, -1)
            pitch = rot90(pitch) # Rotate back.
            pitch = pitch.tolist()
            self.state['pitch']['pattern'] = self.serialize_pitch(pitch)
        else:
            self.state[category]['pattern'] = self.__rotate(self.state[category]['pattern'], -1)
    
    def shift_right(self, category):
        if category == 'rhythm':
            rhythm = self.deserialize_rhythm(self.state['rhythm']['pattern'])
            rhythm = rot90(rhythm, 3) # Rotate because pitches are column based.
            rhythm = rhythm.tolist()
            rhythm = self.__rotate(rhythm, 1)
            rhythm = rot90(rhythm) # Rotate back.
            rhythm = rhythm.tolist()
            self.state['rhythm']['pattern'] = self.serialize_rhythm(rhythm)
        elif category == 'pitch':
            pitch = self.deserialize_pitch(self.state['pitch']['pattern']) # Deserialize
            pitch = rot90(pitch, 3) # Rotate because pitches are column based.
            pitch = pitch.tolist()
            pitch = self.__rotate(pitch, 1)
            pitch = rot90(pitch) # Rotate back.
            pitch = pitch.tolist()
            self.state['pitch']['pattern'] = self.serialize_pitch(pitch)
        else:
            self.state[category]['pattern'] = self.__rotate(self.state[category]['pattern'], 1)

    def shift_up(self, category):
        if category == 'path': # raise path chromatically (interval = 1)
            self.transposePath(1)
        elif category == 'rhythm': # shift rhythm patten upward vertically
            rhythm = self.deserialize_rhythm(self.state['rhythm']['pattern']) # read rhythm
            rhythm = self.__rotate(rhythm, -1)
            self.state['rhythm']['pattern'] = self.serialize_rhythm(rhythm) # write rhythm
        elif category == 'pitch': # shift pitch patten upward vertically
            pitch = self.deserialize_pitch(self.state['pitch']['pattern']) # read pitch
            pitch = self.__rotate(pitch, -1)
            self.state['pitch']['pattern'] = self.serialize_pitch(pitch) # write pitch
        elif category == 'amplitude': # increase all amplitudes
            self.adjustAmplitude(0.1)

    def shift_down(self, category):
        if category == 'path': # lower path chromatically (interval = -1)
            self.transposePath(-1)
        elif category == 'rhythm': # shift rhythm patten downward vertically
            rhythm = self.deserialize_rhythm(self.state['rhythm']['pattern']) # read rhythm
            rhythm = self.__rotate(rhythm, 1)
            self.state['rhythm']['pattern'] = self.serialize_rhythm(rhythm) # write rhythm
        elif category == 'pitch': # shift pitch patten downward vertically
            pitch = self.deserialize_pitch(self.state['pitch']['pattern']) # read pitch
            pitch = self.__rotate(pitch, 1)
            self.state['pitch']['pattern'] = self.serialize_pitch(pitch) # write pitch
        elif category == 'amplitude': # decrease all amplitudes
            self.adjustAmplitude(-0.1)

    def retrograde(self, category):
        if category == 'rhythm':
            rhythm = self.deserialize_rhythm(self.state['rhythm']['pattern']) # deserialize
            [subList.reverse() for subList in rhythm] # reverse the mother list
            self.state['rhythm']['pattern'] = self.serialize_rhythm(rhythm) # serialize
        elif category == 'pitch':
            pitch = self.deserialize_pitch(self.state['pitch']['pattern']) # deserialize
            [subList.reverse() for subList in pitch] # reverse the mother list
            self.state['pitch']['pattern'] = self.serialize_pitch(pitch) # serialize
        else:
            # import pdb; pdb.set_trace()
            self.state[category]['pattern'].reverse()

    def inverse(self, category): #TODO: How to deal with any silent pitch?
        if category == 'path':
            path = self.state['path']['pattern'] # read path
            length = len(path)
            new_path = []
            new_path_inversion = []
            for i in range(length):
                new_path.append(note.Note(path[i]).pitch.midi) # convert pitch names to MIDI pitch numbers
                if i == 0: # initial note
                    new_path_inversion.append(new_path[i]) # keep the same pitch
                else: # all other notes
                    new_path_inversion.append(new_path_inversion[i-1]-(new_path[i]-new_path[i-1])) # inverse transposition
            if self.examineOverflow(new_path_inversion, length, 0, 127) is True: # all MIDI pitch numbers are within 0~127
                for i in range(length):                
                    path[i] = note.Note(new_path_inversion[i]).nameWithOctave # convert MIDI pitch numbers to pitch names
                self.state['path']['pattern'] = path # write path

    def retrogradeInverse(self, category):
        if category == 'path':
            self.inverse(category) # inverse before retrograde
            self.state['path']['pattern'].reverse() # retrograde after inverse

    def raiseOctave(self, category):
        if category == 'path':
            self.transposePath(12) # raise path by octave (interval = 12)
    
    def lowerOctave(self, category):
        if category == 'path':
            self.transposePath(-12) # lower path by octave (interval = -12)

    def transposePath(self, interval):
        path = self.state['path']['pattern'] # read path
        length = len(path)
        new_path = []            
        for i in range(length):
            new_path[i] = note.Note(path[i]).pitch.midi + interval # convert pitch names to MIDI pitch numbers and transpose
        if self.examineOverflow(new_path, length, 0, 127) is True: # all MIDI pitch numbers are within 0~127
            for i in range(length):
                path[i] = note.Note(new_path[i]).nameWithOctave # convert MIDI pitch numbers to pitch names
            self.state['path']['pattern'] = path # wirte path

    def adjustAmplitude(self, volume):
        amplitude = self.state['amplitude']['pattern'] # read amplitude
        length = len(amplitude)
        for i in range(length):
            amplitude[i] = amplitude[i] + volume # adjust amplitude
        if self.examineOverflow(amplitude, length, 0, 1) is True: # all amplitudes are within 0~1
            self.state['amplitude']['pattern'] = amplitude # write amplitude
    
    def examineOverflow(self, dataList, length, minimum, maximum):
        for i in range(length):
            if dataList[i] >= minimum and dataList[i] <= maximum: # grant permission
                permission = True
            else: # deny permission if any elemnt beyond limits
                permission = False
                break
        return permission

    def __rotate(self, l,n):
        return l[-n:] + l[:-n]        
   
    def __updateOrder(self, attribute, value):
        self.state[attribute]['order'] = value
        size = self.feature_sizes[attribute]
        if value == "cyclic":
            self.state[attribute]['order_eng'] = CyclicGenerator(size)
        elif value == "random":
            self.state[attribute]['order_eng'] = UniformRandomGenerator(size)
        else:
            print "Unknown order: %s !" % value<commit_msg>modularize all four shift algorithms<commit_after>from voluptuous import Schema, Required, All, Length, Range, MultipleInvalid
from threading import Lock
import yaml
import logging
import random
import time
import utils
import sys
import traceback
from copy import copy
from Queue import Queue
from string import ascii_lowercase
from numpy import rot90
from music21 import note
from athenaCL.libATH import grammar
from athenaCL.libATH import markov

lock = Lock()

class Generator(object):
    OFFSET = 0
    VALUES = 1
    
    def __init__(self, filename, config):
        """
        >>> g = Generator("../resources/presets/default.yml", {})
        >>> g.state["instrument"]
        23
        >>> g.state["bpm"]
        110
        """        
        # The state of the generator describes what the generator will generate.
        # It follows the AthenaCL standard.
        self.file = filename
        
        # The Notebuffer is where generated notes are stored before being sent to 
        # the synthesizer
        self.queue = Queue()
        
        # This variable gives the size of the queue. This is required because Mac OS X
        # does not support queue.qsize()
        self.size = 0
        self.size_lock = Lock()
        self.rhythm_lock = Lock()
        
        self.schema = Schema({
            Required('instrument'): All(int, Range(min=1, max=32)),
            Required('bpm'): All(int, Range(30, 210)),
            Required('path'): {
                Required('pattern'): All(list, Length(8)),
                Required('order'): str
            },
            Required('pitch'): {
                Required('pattern'): [
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8))
                ],
                Required('order'): str
            },
            Required('rhythm'): {
                Required('pattern'): [
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8)),
                    All(list, Length(8))
                ],
                Required('order'): str,
                Required('dividor') : int
            },
            Required('amplitude'): {
                Required('pattern'): All(list, Length(8)),
                Required('order'): str
            }
        })
        
        self.loadState()
        
        # This is used to say if the music generation is active or not.
        self.active = True
        
        # This is used by the gui for playing/pausing.
        self.playing = False
    
    def loadState(self):
        """
        Loads the application generator state.
        """
        # This is the range of the selector for each feature.
        self.feature_sizes = { 'pitch' : 8, 'path' : 8, 'amplitude' : 8, 'rhythm' : 4}
        self.state = self.readState()
    
    def readState(self):
        return self.readStateFromFile(self.file)
        
    def readStateFromFile(self, file):
        # Read algorithm state
        try:
            with open(file) as f:
                state = yaml.load(f)
        except IOError:
            t, v, tb = sys.exc_info()
            print v
            traceback.print_tb(tb)
            sys.exit(1)
        
        # Check that preset file is well-formed.
        try:
            self.__checkStateFormat(state)
        except MultipleInvalid:
            t, v, tb = sys.exc_info()
            print v
            traceback.print_tb(tb)
            sys.exit(1)
        
        # Fills the blanks for all of the non markov stuff.
        for attributeName in ['rhythm', 'amplitude', 'path', 'pitch']:
            # Assign the order engine
            self.__computeOrderEng(state, attributeName)
        
        state['rhythm']['pattern'] = self.serialize_rhythm(state['rhythm']['pattern'])
        state['pitch']['pattern'] = self.serialize_pitch(state['pitch']['pattern'])
        
        # Indicates currently selected path on Path Map. Value can be 0-7.
        state['path']['selected'] = 0
        state['rhythm']['row_idx'] = 0
        return state
    
    def __checkStateFormat(self, state):
        """
        Checks that the input state object is a dictionary valid
        which follows the expected format.
        """
        self.schema(state)
    
    def __computeOrderEng(self, parameters, attributeName):
        """
        This method computes the order parameter of some of the attributes of the state
        """
        if attributeName not in ["path", "rhythm", "pitch", "amplitude"]:
            raise Exception("Attempting to assign an order to an invalid attribute!")
        else:
            size = self.feature_sizes[attributeName]
            # If not already set, put a random value here.
            if parameters[attributeName]['order'] == 'cyclic':
                parameters[attributeName]['order_eng'] = CyclicGenerator(size)
            elif parameters[attributeName]['order'] == 'random':
                parameters[attributeName]['order_eng'] = UniformRandomGenerator(size)
            else:
                raise Exception("Order type %s is invalid!", parameters[attributeName]['order'])
        return
    
    def update(self, attribute, value):
        """
        This method updates the current state of the generator. Expected attribute/value
        combination are given below in the format:
        
            attribute : expected value
        
        List :
            "bpm" : int
            "instrument" : int
            "* order" : str
            "path select" : int
            "path pattern" : int
            "rhythm pattern %d" : (row) list
            "pitch pattern %d" : (column) list
            "amplitude pattern %d" : float
        """
        try:
            if attribute == "bpm":
                self.state['bpm'] = value
            elif attribute == "instrument":
                self.state['instrument'] = value + 1
                self.__change_instrument(value)
            elif attribute == "path order":
                self.__updateOrder('path', value)
            elif attribute.startswith('path pattern'):
                selected_path = self.state['path']['selected']
                self.state['path']['pattern'][selected_path] = value
            elif attribute == 'path select':
                self.state['path']['selected'] = value
            elif attribute == "rhythm order":
                self.__updateOrder('rhythm', value)
            elif attribute.startswith("rhythm pattern"):
                idx = int(attribute.split(" ")[-1])
                cur = 0
                while cur < 8 and value[cur] == 0:
                    cur += 1
                offset = cur
                values = []
                while cur < 8:
                    cur += 1
                    val = cur - 1
                    while cur < 8 and value[cur] == 0:
                        cur += 1
                    values.append(cur - val)
                self.rhythm_lock.acquire() # Hot code which needs to be locked.
                self.state['rhythm']['pattern'][idx] = (offset, values)
                self.rhythm_lock.release()
            elif attribute.startswith("rhythm dividor"):
                self.state['rhythm']['dividor'] = value
            elif attribute.startswith("pitch pattern"):
                idx = int(attribute.split(" ")[-1])
                pitch_column = []
                for i in xrange(11):
                    if value[i] == 1:
                        pitch_column.append(5-i)
                self.state['pitch']['pattern'][idx] = pitch_column
            elif attribute.startswith("pitch order"):
                self.__updateOrder('pitch', value)
            elif attribute.startswith('amplitude pattern'):
                idx = int(attribute.split(" ")[-1])
                self.state['amplitude']['pattern'][idx] = value
            elif attribute.startswith("amplitude order"):
                self.__updateOrder('amplitude', value)
            else:
                # Should not happen
                raise Exception("generator.update() called with invalid attribute {}".format(attribute))
            return True
        except:
            t, v, tb = sys.exc_info()
            print t
            print v
            traceback.print_tb(tb)
            return False
    
    def apply_algorithm(self, category, algorithm):
        if algorithm == 'shiftLeft':
            self.shift_left(category)
        elif algorithm == 'shiftRight':
            self.shift_right(category)
        elif algorithm == 'shiftUp':
            self.shift_up(category)
        elif algorithm == 'shiftDown':
            self.shift_down(category)
        elif algorithm == 'retrograde':
            self.retrograde(category)
        elif algorithm == 'inverse':
            self.inverse(category)
        elif algorithm == 'retrograde-inverse':
            self.retrogradeInverse(category)
        elif algorithm == 'raiseOctave':
            self.raiseOctave(category)
        elif algorithm == 'lowerOctave':
            self.lowerOctave(category)
    
    def shift_left(self, category):
        if category == 'rhythm':
            self.shiftRhythmHorizontally(self, 1) # shift rhythm patten left-hand horzontally
        elif category == 'pitch':
            self.shiftPitchHorizontally(self, 1) # shift pitch patten left-hand horzontally
        else:
            self.state[category]['pattern'] = self.__rotate(self.state[category]['pattern'], -1)
    
    def shift_right(self, category):
        if category == 'rhythm':
            self.shiftRhythmHorizontally(self, -1) # shift rhythm patten right-hand horzontally
        elif category == 'pitch':
            self.shiftPitchHorizontally(self, -1) # shift pitch patten right-hand horzontally
        else:
            self.state[category]['pattern'] = self.__rotate(self.state[category]['pattern'], 1)

    def shift_up(self, category):
        if category == 'path':
            self.transposePath(1) # raise path chromatically (interval = 1)
        elif category == 'rhythm':
            self.shiftRhythmVertically(self, 1) # shift rhythm patten upward vertically
        elif category == 'pitch':
            self.shiftPitchVertically(self, 1) # shift pitch patten upward vertically
        elif category == 'amplitude':
            self.adjustAmplitude(0.1) # increase all amplitudes

    def shift_down(self, category):
        if category == 'path':
            self.transposePath(-1) # lower path chromatically (interval = -1)
        elif category == 'rhythm':
            self.shiftRhythmVertically(self, -1) # shift rhythm patten downward vertically
        elif category == 'pitch':
            self.shiftPitchVertically(self, -1) # shift pitch patten downward vertically
        elif category == 'amplitude':
            self.adjustAmplitude(-0.1) # decrease all amplitudes

    def retrograde(self, category):
        if category == 'rhythm':
            rhythm = self.deserialize_rhythm(self.state['rhythm']['pattern']) # deserialize
            [subList.reverse() for subList in rhythm] # reverse the mother list
            self.state['rhythm']['pattern'] = self.serialize_rhythm(rhythm) # serialize
        elif category == 'pitch':
            pitch = self.deserialize_pitch(self.state['pitch']['pattern']) # deserialize
            [subList.reverse() for subList in pitch] # reverse the mother list
            self.state['pitch']['pattern'] = self.serialize_pitch(pitch) # serialize
        else:
            # import pdb; pdb.set_trace()
            self.state[category]['pattern'].reverse()

    def inverse(self, category): #TODO: How to deal with any silent pitch?
        if category == 'path':
            path = self.state['path']['pattern'] # read path
            length = len(path)
            new_path = []
            new_path_inversion = []
            for i in range(length):
                new_path.append(note.Note(path[i]).pitch.midi) # convert pitch names to MIDI pitch numbers
                if i == 0: # initial note
                    new_path_inversion.append(new_path[i]) # keep the same pitch
                else: # all other notes
                    new_path_inversion.append(new_path_inversion[i-1]-(new_path[i]-new_path[i-1])) # inverse transposition
            if self.examineOverflow(new_path_inversion, length, 0, 127) is True: # all MIDI pitch numbers are within 0~127
                for i in range(length):                
                    path[i] = note.Note(new_path_inversion[i]).nameWithOctave # convert MIDI pitch numbers to pitch names
                self.state['path']['pattern'] = path # write path

    def retrogradeInverse(self, category):
        if category == 'path':
            self.inverse(category) # inverse before retrograde
            self.state['path']['pattern'].reverse() # retrograde after inverse

    def raiseOctave(self, category):
        if category == 'path':
            self.transposePath(12) # raise path by octave (interval = 12)
    
    def lowerOctave(self, category):
        if category == 'path':
            self.transposePath(-12) # lower path by octave (interval = -12)

    def shiftRhythmHorizontally(self, direction):
        rhythm = self.deserialize_rhythm(self.state['rhythm']['pattern']) # read rhythm and deserialize
        rhythm = rot90(rhythm, 3) # Rotate because pitches are column based.
        rhythm = rhythm.tolist()
        rhythm = self.__rotate(rhythm, direction) # directions: left = 1; right = -1
        rhythm = rot90(rhythm) # Rotate back.
        rhythm = rhythm.tolist()
        self.state['rhythm']['pattern'] = self.serialize_rhythm(rhythm) # serialize and write rhythm

    def shiftPitchHorizontally(self, direction):
        pitch = self.deserialize_pitch(self.state['pitch']['pattern']) # read pitch and deserialize
        pitch = rot90(pitch, 3) # Rotate because pitches are column based.
        pitch = pitch.tolist()
        pitch = self.__rotate(pitch, direction) # directions: left = 1; right = -1
        pitch = rot90(pitch) # Rotate back.
        pitch = pitch.tolist()
        self.state['pitch']['pattern'] = self.serialize_pitch(pitch) # serialize and write pitch

    def shiftRhythmVertically(self, direction):
        rhythm = self.deserialize_rhythm(self.state['rhythm']['pattern']) # read rhythm
        rhythm = self.__rotate(rhythm, direction) # directions: up = 1; down = -1
        self.state['rhythm']['pattern'] = self.serialize_rhythm(rhythm) # write rhythm

    def shiftPitchVertically(self, direction):
        pitch = self.deserialize_pitch(self.state['pitch']['pattern']) # read pitch
        pitch = self.__rotate(pitch, direction) # directions: up = 1; down = -1
        self.state['pitch']['pattern'] = self.serialize_pitch(pitch) # write pitch

    def transposePath(self, interval):
        path = self.state['path']['pattern'] # read path
        length = len(path)
        new_path = []            
        for i in range(length):
            new_path[i] = note.Note(path[i]).pitch.midi + interval # convert pitch names to MIDI pitch numbers and transpose
        if self.examineOverflow(new_path, length, 0, 127) is True: # all MIDI pitch numbers are within 0~127
            for i in range(length):
                path[i] = note.Note(new_path[i]).nameWithOctave # convert MIDI pitch numbers to pitch names
            self.state['path']['pattern'] = path # wirte path

    def adjustAmplitude(self, volume):
        amplitude = self.state['amplitude']['pattern'] # read amplitude
        length = len(amplitude)
        for i in range(length):
            amplitude[i] = amplitude[i] + volume # adjust amplitude
        if self.examineOverflow(amplitude, length, 0, 1) is True: # all amplitudes are within 0~1
            self.state['amplitude']['pattern'] = amplitude # write amplitude
    
    def examineOverflow(self, dataList, length, minimum, maximum):
        for i in range(length):
            if dataList[i] >= minimum and dataList[i] <= maximum: # grant permission
                permission = True
            else: # deny permission if any elemnt beyond limits
                permission = False
                break
        return permission

    def __rotate(self, l, n):
        return l[n:] + l[:n]        
   
    def __updateOrder(self, attribute, value):
        self.state[attribute]['order'] = value
        size = self.feature_sizes[attribute]
        if value == "cyclic":
            self.state[attribute]['order_eng'] = CyclicGenerator(size)
        elif value == "random":
            self.state[attribute]['order_eng'] = UniformRandomGenerator(size)
        else:
            print "Unknown order: %s !" % value
{'fraction_non_alphanumeric': 0.1122502628811777, 'fraction_numerical': 0.00759434513377731, 'mean_word_length': 1.9393028846153846, 'pattern_counts': {'":': 12, '<': 11, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2047626', 'n_tokens_mistral': 9207, 'n_tokens_neox': 8738, 'n_words': 2453}