class UtilsTests(unittest.TestCase):
    test_get_note = {'C': 60,'C': 0, 'G': 127, 'E': 64, 'A#': 70};
    test_get_note_number = {0: 60, 1:85,2:122,3:99,4:16,5:41,6:90,7:55,8:44,9:21,10:70,11:83};
    test_get_midi_number = {0:['C',-1],60:['C',4],12:['C',0],120:['C',9],127:['G',9]};
    test_normalize_scale = [['G', 'C##', 'Bb', 'B##b', 'A#', 'D', 'B#'],['C','D', 'G', 'A#']];
    test_calc_scale_cases = [];

    def setUp(self):
        self.utils = Utils();
        utils_for_tests = UtilsForTests();
        self.test_calc_scale_cases = utils_for_tests.loadTestCorpus('test_corpus/test_calc_scale_corpus');

    def test_getNote(self):
        for note, midi_note in self.test_get_note.iteritems():
            self.assertEqual(note, self.utils.getNote(midi_note));
            
    def test_getNoteNumber(self):
        for note_number, midi_note in self.test_get_note_number.iteritems():
            self.assertEqual(note_number, self.utils.getNoteNumber(midi_note));
    
    def test_getMidiNumber(self):
        for midi_note, note_octave in self.test_get_midi_number.iteritems():
            self.assertEqual(midi_note, self.utils.getMidiNumber(note_octave[0],note_octave[1]));
            
    def test_getNoteAndOctave(self):
        for midi_note, note_octave in self.test_get_midi_number.iteritems():
            self.assertEqual(note_octave[0], self.utils.getNoteAndOctave(midi_note)[0]);
            self.assertEqual(note_octave[1], self.utils.getNoteAndOctave(midi_note)[1]);
            
    def test_normalizeScale(self):
        norm_scale = self.utils.normalizeScale(self.test_normalize_scale[0]);
        self.assertSequenceEqual(norm_scale, self.test_normalize_scale[1]);
    
    def test_calcScale(self):
        for case in self.test_calc_scale_cases:
            scale = self.utils.calcScale(case[1],case[0]);
            self.assertListEqual(scale, case[2]);
Exemple #2
0
class Gui(Frame):
    
    transpose = 0;
    scale_to_map = [];
    mapped_scale = {};
    
    auto_mode = False;
    button_state_map = {};
    
    event_types = (NOTE_ON, NOTE_OFF);
    
    button_color_state_map_black = {True:'#00CCA9',False:'#2b3633'};
    button_color_state_map_white = {True:'#00CCA9',False:'#FFFFCD'};
    
    def __init__(self,Master=None,*pos,**kw):
        self.utils = Utils();
        self.cache = ScaleCache();
        self.mapper = Mapper();
        self.load_init_button_states();
        
        ############### GUI ############### 
        apply(Frame.__init__,(self,Master),kw)
        self._Frame4 = Frame(self)
        self._Frame4.pack(expand='yes',fill='both',side='left')
        self._Frame3 = Frame(self,background='#016678')
        self._Frame3.pack(expand='yes',fill='both',side='left')
        self._Frame29 = Frame(self._Frame4,background='#016678')
        self._Frame29.pack(expand='yes',fill='both',side='top')
        self._ListboxNote = Listbox(self._Frame29,background='#014455'
            ,foreground='#CCFFDE',height='4',highlightthickness='0'
            ,selectborderwidth='5',selectmode='extended',width='6')
        self._ListboxNote.pack(expand='yes',fill='both',side='left')
        self._Frame28 = Frame(self._Frame4,background='#016678')
        self._Frame28.pack(expand='yes',fill='both',side='top')
        self._ListboxScale = Listbox(self._Frame28,background='#014455'
            ,foreground='#CCFFDE',height='5',highlightthickness='0'
            ,selectmode='extended',width='6')
        self._ListboxScale.pack(expand='yes',fill='both',side='left')
        self._Frame17 = Frame(self._Frame3)
        self._Frame17.pack(expand='yes',fill='both',side='top')
        self._LabelStatus = Label(self._Frame17,background='#FFFFCD'
            ,relief='groove',state='disabled',text='This is test text.')
        self._LabelStatus.pack(expand='yes',fill='both',side='top')
        self._Frame11 = Frame(self._Frame3,background='#016678')
        self._Frame11.pack(expand='yes',fill='both',side='top')
        self._Frame10 = Frame(self._Frame3,background='#016678')
        self._Frame10.pack(side='top')
        self._Frame19 = Frame(self._Frame3)
        self._Frame19.pack(side='top')
        self._Frame18 = Frame(self._Frame3)
        self._Frame18.pack(expand='yes',fill='both',side='top')
        self._Frame32 = Frame(self._Frame11,background='#016678')
        self._Frame32.pack(side='left')
        self._MenuInput = Menubutton(self._Frame32,activebackground='#3b3c3c'
            ,activeforeground='#CCFFDE',background='#FFFFCD',menu='_MenuInputs'
            ,text='Input',width='10')
        self._MenuInput.pack(expand='yes',fill='both',side='top')
        self._Frame33 = Frame(self._Frame11,background='#016678')
        self._Frame33.pack(expand='yes',fill='x',side='left')
        self._ButtonScanMidi = Button(self._Frame33,activebackground='#3b3c3c'
            ,activeforeground='#CCFFDE',background='#2b3633'
            ,command=self._on__ButtonScanMidi_command,foreground='#CCFFDE'
            ,text='Scan')
        self._ButtonScanMidi.pack(expand='yes',fill='x',side='top')
        self._Frame31 = Frame(self._Frame11,background='#016678')
        self._Frame31.pack(expand='yes',fill='both',side='left')
        self._MenuOutput = Menubutton(self._Frame31,activebackground='#3b3c3c'
            ,activeforeground='#CCFFDE',background='#FFFFCD',text='Output'
            ,width='10')
        self._MenuOutput.pack(expand='yes',fill='x',side='top')
        self._Frame22 = Frame(self._Frame10,background='#016678',width='1')
        self._Frame22.pack(expand='yes',fill='both',side='left')
        self._Frame24 = Frame(self._Frame10,background='#016678'
            ,highlightbackground='Black',width='20')
        self._Frame24.pack(expand='yes',fill='both',side='left')
        self._ButtonCSharp = Button(self._Frame24,activebackground='#3b3c3c'
            ,activeforeground='#CCFFDE',background='#2b3633'
            ,command=self._on__ButtonCSharp_command,foreground='#CCFFDE'
            ,height='2',relief='flat',text='C#',width='1')
        self._ButtonCSharp.pack(expand='yes',side='left')
        self._Frame13 = Frame(self._Frame10,background='#016678',width='10')
        self._Frame13.pack(expand='yes',fill='both',side='left')
        self._Frame14 = Frame(self._Frame10)
        self._Frame14.pack(expand='yes',fill='both',side='left')
        self._ButtonDSharp = Button(self._Frame14,activebackground='#3b3c3c'
            ,activeforeground='#CCFFDE',background='#2b3633'
            ,command=self._on__ButtonDSharp_command,foreground='#CCFFDE'
            ,height='2',text='D#',width='1')
        self._ButtonDSharp.pack(expand='yes',fill='both',side='left')
        self._Frame7 = Frame(self._Frame10,background='#016678',width='50')
        self._Frame7.pack(expand='yes',fill='both',side='left')
        self._Frame8 = Frame(self._Frame10,background='#016678')
        self._Frame8.pack(expand='yes',fill='both',side='left')
        self._ButtonFSharp = Button(self._Frame8,activebackground='#3b3c3c'
            ,activeforeground='#CCFFDE',background='#2b3633'
            ,command=self._on__ButtonFSharp_command,foreground='#CCFFDE'
            ,height='2',text='F#',width='1')
        self._ButtonFSharp.pack(expand='yes',fill='both',side='left')
        self._Frame15 = Frame(self._Frame10,background='#016678',width='10')
        self._Frame15.pack(expand='yes',fill='both',side='left')
        self._Frame26 = Frame(self._Frame10)
        self._Frame26.pack(expand='yes',fill='both',side='left')
        self._ButtonGSharp = Button(self._Frame26,activebackground='#3b3c3c'
            ,activeforeground='#CCFFDE',background='#2b3633'
            ,command=self._on__ButtonGSharp_command,foreground='#CCFFDE'
            ,height='2',text='G#',width='1')
        self._ButtonGSharp.pack(expand='yes',fill='both',side='left')
        self._Frame12 = Frame(self._Frame10,background='#016678',width='10')
        self._Frame12.pack(expand='yes',fill='both',side='left')
        self._Frame16 = Frame(self._Frame10)
        self._Frame16.pack(expand='yes',fill='both',side='left')
        self._ButtonASharp = Button(self._Frame16,activebackground='#3b3c3c'
            ,activeforeground='#CCFFDE',background='#2b3633'
            ,command=self._on__ButtonASharp_command,foreground='#CCFFDE'
            ,height='2',text='A#',width='1')
        self._ButtonASharp.pack(expand='yes',fill='both',side='left')
        self._Frame30 = Frame(self._Frame10,background='#016678')
        self._Frame30.pack(expand='yes',fill='both',side='left')
        self._Frame21 = Frame(self._Frame19,background='#016678',width='20')
        self._Frame21.pack(expand='yes',fill='both',side='left')
        self._ButtonC = Button(self._Frame21,activebackground='#ffffff'
            ,activeforeground='#2b3633',background='#FFFFCD',borderwidth='3'
            ,command=self._on__ButtonC_command,height='2',text='C')
        self._ButtonC.pack(expand='yes',fill='both',side='left')
        self._Frame25 = Frame(self._Frame19)
        self._Frame25.pack(expand='yes',fill='both',side='left')
        self._ButtonD = Button(self._Frame25,activebackground='#ffffff'
            ,activeforeground='#2b3633',background='#FFFFCD'
            ,command=self._on__ButtonD_command,text='D')
        self._ButtonD.pack(expand='yes',fill='both',side='left')
        self._Frame20 = Frame(self._Frame19)
        self._Frame20.pack(expand='yes',fill='both',side='left')
        self._ButtonE = Button(self._Frame20,activebackground='#ffffff'
            ,activeforeground='#2b3633',background='#FFFFCD'
            ,command=self._on__ButtonE_command,text='E')
        self._ButtonE.pack(expand='yes',fill='both',side='right')
        self._Frame1 = Frame(self._Frame19)
        self._Frame1.pack(expand='yes',fill='both',side='left')
        self._ButtonF = Button(self._Frame1,activebackground='#ffffff'
            ,activeforeground='#2b3633',background='#FFFFCD'
            ,command=self._on__ButtonF_command,text='F')
        self._ButtonF.pack(expand='yes',fill='both',side='right')
        self._Frame27 = Frame(self._Frame19)
        self._Frame27.pack(expand='yes',fill='both',side='left')
        self._ButtonG = Button(self._Frame27,activebackground='#ffffff'
            ,activeforeground='#2b3633',background='#FFFFCD'
            ,command=self._on__ButtonG_command,text='G')
        self._ButtonG.pack(expand='yes',fill='both',side='left')
        self._Frame2 = Frame(self._Frame19)
        self._Frame2.pack(expand='yes',fill='both',side='left')
        self._ButtonA = Button(self._Frame2,activebackground='#ffffff'
            ,activeforeground='#2b3633',background='#FFFFCD'
            ,command=self._on__ButtonA_command,text='A')
        self._ButtonA.pack(expand='yes',fill='both',side='right')
        self._Frame6 = Frame(self._Frame19)
        self._Frame6.pack(expand='yes',fill='both',side='left')
        self._ButtonB = Button(self._Frame6,activebackground='#ffffff'
            ,activeforeground='#2b3633',background='#FFFFCD'
            ,command=self._on__ButtonB_command,text='B')
        self._ButtonB.pack(expand='yes',fill='both',side='right')
        self._Frame5 = Frame(self._Frame19,background='#016678')
        self._Frame5.pack(expand='yes',fill='both',side='left')
        self._Frame23 = Frame(self._Frame18,background='#016678')
        self._Frame23.pack(expand='yes',fill='both',side='left')
        self._ScaleTrans = Scale(self._Frame23,background='#016678'
            ,bigincrement=1,borderwidth='0',command=self._on__ScaleTrans_command
            ,foreground='#2b3633',from_=-12,highlightbackground='#3b3c3c'
            ,highlightcolor='#3b3c3c',highlightthickness='0'
            ,label='Transpose(cents)',orient='horizontal',resolution=1
            ,sliderlength='50',tickinterval=0,to=12)
        self._ScaleTrans.pack(expand='yes',fill='x',side='left')
        self._Frame9 = Frame(self._Frame18,background='#016678')
        self._Frame9.pack(expand='yes',fill='both',side='left')
        self._ButtonAutoMode = Button(self._Frame9,activebackground='#ffffff'
            ,activeforeground='#2b3633',background='#FFFFCD',text='Magic mode'
            ,width='7')
        self._ButtonAutoMode.pack(expand='yes',side='bottom')
        self._Frame34 = Frame(self._Frame18,background='#016678')
        self._Frame34.pack(expand='yes',fill='both',side='left')
        self._ButtonQuit = Button(self._Frame34,activebackground='#BB1167'
            ,background='#CD0045',command=self._on__ButtonQuit_command
            ,text='Quit')
        self._ButtonQuit.pack(expand='yes',side='left')
        ############### GUI ###############
    
        self._MenuInput.menu  =  Menu(self._MenuInput, tearoff = 0 );
        self._MenuInput["menu"]  =  self._MenuInput.menu;
            
        self._MenuOutput.menu  =  Menu(self._MenuOutput, tearoff = 0 );
        self._MenuOutput["menu"]  =  self._MenuOutput.menu;
    
        self.midi_in = None;
        self.midi_out = None;
        self.check_midi_ports();
        
        self._ButtonAutoMode.config(command=self.process_auto_mode_change);

        self.showMessage("Loading...");
        for note in self.utils.getNotes():
            self._ListboxNote.insert(END, note);
            
        for scale in self.utils.getAvailableScales():
            self._ListboxScale.insert(END, scale);
            
        self.current_scale = [10];
        self.current_note = [0];
        self.process_note_change(self.current_note);
        
        self.showMessage("Ready to work, sir!");
        
        self.current_note = ();
        self.current_scale = ();   
        self.poll_lists(); # start polling the list
        
    def process_auto_mode_change(self):
        if self.auto_mode:
            self.auto_mode = False;
        else:
            self.auto_mode = True;
            self.showMessage("Magic Mode!");
        
    def check_midi_ports(self):
        if self.midi_in is not None:
            self.midi_in.close_port();
        if self.midi_out is not None:
            self.midi_out.close_port();
        
        self.midi_in = rtmidi.MidiIn();
        self.midi_out = rtmidi.MidiOut();
        
        ports_in = self.midi_in.ports;
        ports_out = self.midi_out.ports;
        
        self._MenuInput.menu.delete(0,END);
        self._MenuOutput.menu.delete(0,END);
                
        if len(ports_in) == 0:
            self._MenuInput.config(text="No input devices");
        else:
            for port_index in range(0, len(ports_in)):
                self._MenuInput.menu.add_command(label=ports_in[port_index], command=lambda port_index=port_index: self.midi_in_device_select_callback(port_index));
            self.midi_in_device_select_callback(0);
               
        if len(ports_out) == 0:
            self._MenuInput.config(text="No output devices");
        else:
            for port_index in range(0, len(ports_out)):
                self._MenuOutput.menu.add_command(label=ports_out[port_index], command=lambda port_index=port_index: self.midi_out_device_select_callback(port_index));
            self.midi_out_device_select_callback(0);
            
    def midi_out_device_select_callback(self, port_index):
        self.midi_out.close_port();
        self.midi_out.open_port(port_index);
        self.updateMenuTitle(port_index, self._MenuOutput, self._MenuOutput.menu);
        
    def midi_in_device_select_callback(self, port_index):
        self.midi_in.close_port();
        self.midi_in.open_port(port_index);
        self.midi_in.callback = self.midi_callback
        self.updateMenuTitle(port_index, self._MenuInput, self._MenuInput.menu);
    
    def updateMenuTitle(self, index, menu_button, menu):
        menu_button.config(text=menu.entrycget(index, "label"));
        
    def poll_lists(self):
        now_note = self._ListboxNote.curselection();
        now_scale = self._ListboxScale.curselection();
        if len(now_note) != 0 and now_note != self.current_note:
            self.process_note_change(now_note)
            self.current_note = now_note
        if len(now_scale) != 0 and now_scale != self.current_scale:
            self.process_scale_change(now_scale)
            self.current_scale = now_scale
        self.after(250, self.poll_lists)
        
    def midi_callback(self, message, time_stamp):
        event_types = (NOTE_ON, NOTE_OFF)
        if (message[0] & 0xF0) in event_types:
            self.showCurrentNote(message);
            if self.auto_mode:
                if(message[1] < 60):
                    note_scale = self.utils.getNoteAndOctave(message[1]);
                    if len(self._ListboxScale.curselection()) == 0:
                        self.showMessage("Select scale first!");
                    else:
                        scale = self.utils.getAvailableScales()[self._ListboxScale.curselection()[0]];
                        self.mapped_scale = self.cache.getScaleFromCache(note_scale[0],scale);
                        self.showMessage("Magic mode scale:%s-%s"%(note_scale[0],scale));
                        self.midi_out.send_message(message);
                else:
                    self.send_modif_massage(message);
            else:
                self.send_modif_massage(message);
        else:
            self.midi_out.send_message(message);
            
    def send_modif_massage(self, message):
        note_octave = self.utils.getNoteAndOctave(message[1]);
        message[1] = self.utils.getMidiNumber(self.mapped_scale[note_octave[0]], note_octave[1]);
        message[1] += self.transpose;
        self.midi_out.send_message(message);
        
    
    def showCurrentNote(self, message):
        note_octave = self.utils.getNoteAndOctave(message[1]);
        note_name = note_octave[0];
        octave = note_octave[1];
        if (message[0] & 0xF0) in self.event_types:
            self.showMessage("Note: %s, Octave: %s, Vel: %s"%(note_name,octave,message[2]));
            
    def _on__ButtonScanMidi_command(self,Event=None):
        self.check_midi_ports();

    def process_note_change(self, note):
        if len(note) != 0 and len(self.current_scale) != 0:
            note_name = notes.int_to_note(note[0]);
            scale_name = self.utils.getAvailableScales()[self.current_scale[0]];
            self.scale_to_map = self.mapper.getScaleToMap(note_name, scale_name);
            self.mapped_scale = self.cache.getScaleFromCache(note_name, scale_name);
            self.show_scale_to_buttons();
            self.auto_mode = False;
            self.showMessage(note_name + " - " + scale_name);
     
    def process_scale_change(self, scale):
        if len(scale) != 0 and len(self.current_note) != 0:
            note_name = notes.int_to_note(self.current_note[0]);
            scale_name = self.utils.getAvailableScales()[scale[0]];
            self.scale_to_map = self.mapper.getScaleToMap(note_name, scale_name);
            self.mapped_scale = self.cache.getScaleFromCache(note_name, scale_name);
            self.show_scale_to_buttons();
            self.auto_mode = False;
            self.showMessage(note_name + " - " + scale_name);
            
    def show_scale_to_buttons(self):
        for note in self.utils.getNotes():
            button_name = note.replace('#','Sharp');
            button_name = "self._Button" + button_name;
            
            if note in self.scale_to_map:
                self.button_state_map[note] = True 
                if '#' in note:
                    button_color = self.button_color_state_map_black[True];
                else:
                    button_color = self.button_color_state_map_white[True];
            else:
                self.button_state_map[note] = False
                if '#' in note:
                    button_color = self.button_color_state_map_black[False];
                else:
                    button_color = self.button_color_state_map_white[False];
            eval(button_name + ".configure(bg = '" + button_color + "')");

    def load_init_button_states(self):
        for note in self.utils.getNotes():
            self.button_state_map[note] = False;
            
    def process_button_change(self, note):
        new_state = self.update_scale_to_map(note);
        self.button_state_map[note] = new_state;
        self.set_custom_mapped_scale();
        #self.searchForScale(self.scale_to_map)
        self.auto_mode = False;
        return new_state;
    
    def searchForScale(self, scale_to_search):
        for note in self.utils.getNotes():
            for scale in self.utils.getAvailableScales():
                if set(scale_to_search) == set(self.mapper.getScaleToMap(note, scale)):
                    print note,scale;
                    print hash(set(scale_to_search))
                    print hash(set(self.mapper.getScaleToMap(note, scale)))
            
    def update_scale_to_map(self, note):
        if self.button_state_map[note] is True:
            self.scale_to_map.remove(note);
            return False;
        else:
            self.scale_to_map.append(note);
            return True;

    def set_custom_mapped_scale(self):
        if len(self.scale_to_map) != 0:
            self.mapped_scale = self.mapper.getCustomMap(self.scale_to_map);
            self.showMessage("Cutom scale: \n");
            
    def showMessage(self, message):
        self._LabelStatus.config(text = message);

    def _on__ButtonASharp_command(self,Event=None):
        new_state = self.process_button_change('A#');
        self._ButtonASharp.configure(bg = self.button_color_state_map_black[new_state]);

    def _on__ButtonA_command(self,Event=None):
        new_state = self.process_button_change('A');
        self._ButtonA.configure(bg = self.button_color_state_map_white[new_state]);

    def _on__ButtonB_command(self,Event=None):
        new_state = self.process_button_change('B');
        self._ButtonB.configure(bg = self.button_color_state_map_white[new_state]);

    def _on__ButtonCSharp_command(self,Event=None):
        new_state = self.process_button_change('C#');
        self._ButtonCSharp.configure(bg = self.button_color_state_map_black[new_state]);

    def _on__ButtonC_command(self,Event=None):
        new_state = self.process_button_change('C');
        self._ButtonC.configure(bg = self.button_color_state_map_white[new_state]);

    def _on__ButtonDSharp_command(self,Event=None):
        new_state = self.process_button_change('D#');
        self._ButtonDSharp.configure(bg = self.button_color_state_map_black[new_state]);

    def _on__ButtonD_command(self,Event=None):
        new_state = self.process_button_change('D');
        self._ButtonD.configure(bg = self.button_color_state_map_white[new_state]);

    def _on__ButtonE_command(self,Event=None):
        new_state = self.process_button_change('E');
        self._ButtonE.configure(bg = self.button_color_state_map_white[new_state]);

    def _on__ButtonFSharp_command(self,Event=None):
        new_state = self.process_button_change('F#');
        self._ButtonFSharp.configure(bg = self.button_color_state_map_black[new_state]);

    def _on__ButtonF_command(self,Event=None):
        new_state = self.process_button_change('F');
        self._ButtonF.configure(bg = self.button_color_state_map_white[new_state]);

    def _on__ButtonGSharp_command(self,Event=None):
        new_state = self.process_button_change('G#');
        self._ButtonGSharp.configure(bg = self.button_color_state_map_black[new_state]);

    def _on__ButtonG_command(self,Event=None):
        new_state = self.process_button_change('G');
        self._ButtonG.configure(bg = self.button_color_state_map_white[new_state]);

    def _on__ButtonQuit_command(self,Event=None):
        if self.midi_in is not None:
            self.midi_in.close_port();
        if self.midi_out is not None:
            self.midi_out.close_port();
        exit(0);

    def _on__ScaleTrans_command(self,Event=None):
        self.transpose = self._ScaleTrans.get();
        
Exemple #3
0
class MainForm(QtGui.QMainWindow):
    
    NOTE_OFF = 0x80
    NOTE_ON = 0x90

    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)
        
        self.utils = Utils();
        self.cache = ScaleCache();
        self.mapper = Mapper();
        
        self.scale_to_map = [];
        self.mapped_scale = {};
        self.transpose = 0;
        self.autoScale = False;

        self.ui = Ui_MainWindow();
        self.ui.setupUi(self);
        
        self.loadAvailableScales();
        self.loadInitScale();
        
        self.midi_in = None;
        self.midi_out = None;
        self.check_midi_ports();

        self.ui.statusBar.showMessage("Ready for rock'N'roll!",1000 * 20);
    
        
    def loadAvailableScales(self):
        for scale in self.utils.getAllAvailableScales():
            self.ui.listWidgetScales.addItem(scale);
            
    def loadInitScale(self):
        self.ui.listWidgetNotes.setCurrentRow(0);
        self.ui.listWidgetScales.setCurrentRow(0);
        result = self.cache.getScaleFromCache(str(self.ui.listWidgetNotes.item(0).text()),str(self.ui.listWidgetScales.item(0).text()));
        self.setNewScale(result);
    
                    
    @QtCore.pyqtSlot(str)
    def on_listWidgetScales_currentTextChanged(self, scale):
        currNote = self.ui.listWidgetNotes.currentItem();
        if currNote is not None:
            result = self.cache.getScaleFromCache(str(currNote.text()), str(scale));
            self.setNewScale(result);
            self.ui.statusBar.showMessage("Mapped scale: %s-%s"%(str(currNote.text()),str(scale)), 1000 * 5);

        
    @QtCore.pyqtSlot(str)
    def on_listWidgetNotes_currentTextChanged(self, note):
        currScale = self.ui.listWidgetScales.currentItem();
        if currScale is not None:
            result = self.cache.getScaleFromCache(str(note), str(currScale.text()));
            self.setNewScale(result);    
            self.autoScale = False;
            self.ui.pushButtonMagic.setChecked(False);
            self.ui.statusBar.showMessage("Mapped scale: %s-%s"%(str(note),str(currScale.text())), 1000 * 5);

            
    def setNewScale(self, scales):
        self.scale_to_map = scales['scale_to_map'];
        self.mapped_scale = scales['mapped_scale'];
        self.showOnKeys();
        
    def showOnKeys(self):
        for key in self.utils.getNotes():
            replacedKey = key.replace('#','S');
            if key in self.scale_to_map:
                new_state = True;               
            else:
                new_state = False;
            eval('self.ui.pushButton' + replacedKey + ".setChecked(" + str(new_state) + ")");
            
    def setNewKeyState(self, key, state):
        if state:
            self.scale_to_map.append(key);
            self.mapped_scale = self.mapper.getMap(self.scale_to_map);
        else:
            if key in self.scale_to_map:
                self.scale_to_map.remove(key);
                if len(self.scale_to_map) != 0:
                    self.mapped_scale = self.mapper.getMap(self.scale_to_map);
        
        self.autoScale = False;
        self.ui.pushButtonMagic.setChecked(False);
        self.ui.statusBar.showMessage("Custom scale: " + str(self.scale_to_map), 1000 * 5);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonC_clicked(self, status):
        self.setNewKeyState('C', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonCS_clicked(self, status):
        self.setNewKeyState('C#', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonD_clicked(self, status):
        self.setNewKeyState('D', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonDS_clicked(self, status):
        self.setNewKeyState('D#', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonE_clicked(self, status):
        self.setNewKeyState('E', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonF_clicked(self, status):
        self.setNewKeyState('F', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonFS_clicked(self, status):
        self.setNewKeyState('F#', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonG_clicked(self, status):
        self.setNewKeyState('G', status);
    
    @QtCore.pyqtSlot(bool)
    def on_pushButtonGS_clicked(self, status):
        self.setNewKeyState('G#', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonA_clicked(self, status):
        self.setNewKeyState('A', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonAS_clicked(self, status):
        self.setNewKeyState('A#', status);
        
    @QtCore.pyqtSlot(bool)
    def on_pushButtonB_clicked(self, status):
        self.setNewKeyState('B', status);
        
    @QtCore.pyqtSlot(int)
    def on_spinBoxTranspose_valueChanged(self, value):
        self.transpose = value;
        
    @QtCore.pyqtSlot()
    def on_pushButtonScan_clicked(self):
        self.check_midi_ports();
        
    def check_midi_ports(self):
        if self.midi_in is not None:
            self.midi_in.close_port();
        if self.midi_out is not None:
            self.midi_out.close_port();
        
        self.midi_in = rtmidi.MidiIn();
        self.midi_out = rtmidi.MidiOut();
        
        ports_in = self.midi_in.ports;
        ports_out = self.midi_out.ports;
        
        self.ui.listWidgetInput.clear();
        self.ui.listWidgetOutput.clear();
                
        if len(ports_in) == 0:
            self.ui.listWidgetInput.addItem("No input devices");
        else:
            for port_index in range(0, len(ports_in)):
                self.ui.listWidgetInput.addItem(ports_in[port_index]);
            self.on_listWidgetInput_currentRowChanged(0);
               
        if len(ports_out) == 0:
            self.ui.listWidgetOutput.addItem("No input devices");
        else:
            for port_index in range(0, len(ports_out)):
                self.ui.listWidgetOutput.addItem(ports_out[port_index]);
            self.on_listWidgetOutput_currentRowChanged(0);
            
        self.ui.statusBar.showMessage("Scan completed!",1000*5);
            
    @QtCore.pyqtSlot(bool)
    def on_pushButtonMagic_clicked(self, state):
        self.autoScale = state;
        self.ui.statusBar.showMessage("Magic mode: %s"%(state),1000*5);
        
    @QtCore.pyqtSlot()
    def on_pushButtonPanic_clicked(self):
        message = [self.NOTE_OFF,0,0];
        for i in range(0, 127):
            message[1] = i;
            self.midi_out.send_message(message);
            
    @QtCore.pyqtSlot(int)
    def on_listWidgetInput_currentRowChanged(self, port):
        self.midi_in.close_port();
        self.midi_in.open_port(port);
        self.midi_in.callback = self.midi_callback
        self.ui.statusBar.showMessage("%s port opened!"%(self.ui.listWidgetInput.item(port).text()),1000*5);
            
    @QtCore.pyqtSlot(int)
    def on_listWidgetOutput_currentRowChanged(self, port):
        self.midi_out.close_port();
        self.midi_out.open_port(port);
        self.ui.statusBar.showMessage("%s port opened!"%(self.ui.listWidgetOutput.item(port).text()),1000*5);

        
    def midi_callback(self, message, time_stamp):
        event_types = (self.NOTE_ON, self.NOTE_OFF)
        if (message[0] & 0xF0) in event_types:
            if self.autoScale:
                if(message[1] < 60):
                    note_scale = self.utils.getNoteAndOctave(message[1]);
                    scale = str(self.ui.listWidgetScales.currentItem().text());
                    
                    result = self.cache.getScaleFromCache(note_scale[0], scale);
                    self.scale_to_map = result['scale_to_map'];
                    self.mapped_scale = result['mapped_scale'];
                
                    #self.ui.statusBar.showMessage("Magic mode scale:%s-%s"%(note_scale[0],scale),1000*5);
                    self.midi_out.send_message(message);
                else:
                    self.send_modif_massage(message);
            else:
                self.send_modif_massage(message);
        else:
            self.midi_out.send_message(message);
            
    def send_modif_massage(self, message):
        note_octave = self.utils.getNoteAndOctave(message[1]);
        message[1] = self.utils.getMidiNumber(self.mapped_scale[note_octave[0]], note_octave[1]);
        message[1] += self.transpose;
        self.midi_out.send_message(message);
    
    @QtCore.pyqtSlot()
    def on_pushButtonExit_clicked(self):
        self.closeOpenPorts();
        self.close();
        
    def closeOpenPorts(self):
        if self.midi_in is not None:
            self.midi_in.close_port();
        if self.midi_out is not None:
            self.midi_out.close_port();
            
    def __exit__(self):
        self.closeOpenPorts();