11# @author: https://github.com/Alperencode
22# @date: 01 Aug 2022
3+ # @last update: 13 Oct 2022
34
4- # Importing modules
5- from tkinter import *
6- from music21 import *
7- from tkinter import ttk ,messagebox
8- import mido ,time ,threading ,math ,json ,os ,mido .backends .rtmidi
5+ # Importing midi directory
6+ from midi import *
97
108# Global variables
11- NOTES = ['C' , 'C#' , 'D' , 'D#' , 'E' , 'F' , 'F#' , 'G' , 'G#' , 'A' , 'A#' , 'B' ]
12- PURE_NOTES = ['C' , 'D' , 'E' , 'F' , 'G' , 'A' , 'B' ]
13- OTHER_NOTES = ['C#' , 'D#' , 'F#' , 'G#' , 'A#' ]
14- MESSAGE_TYPES = ['note_on' ,'note_off' ,'pitchwheel' ,'control_change' ]
15- OCTAVES = [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ]
169global_button_list = []
1710global_pitch_list = []
1811json_data = []
2316for _ in range (8 ):
2417 global_pitch_list .append ([0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ])
2518
26- def converter (value , control ):
27- """
28- Range converter function to convert pitch value (-8192/8191) to cent value (-100/100)
29- control: True -> convert to cent value, False -> convert to pitch value
30- """
31- if control :
32- new_value = (((value - (- 8192 )) * 200 ) / 16383 ) + (- 100 )
33- else :
34- new_value = (((value - (- 100 )) * 16383 ) / 200 ) + (- 8192 )
35- return int (new_value )
36-
3719class NoteButton :
3820 """
3921 This is the main class of the program which is basically responsible for sending the midi messages to the midi output.
4022 Detailed explanation has been provided in each function.
4123 """
42- global output ,app
4324
4425 # Static variables
4526 # counter is used to keep track of the number of buttons created
@@ -54,7 +35,7 @@ class NoteButton:
5435 pitch = [0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ]
5536 control_change = mido .Message ('control_change' , control = 1 , value = 0 )
5637
57- def __init__ (self ,note_name ,octave = 5 ,velocity = 64 ):
38+ def __init__ (self ,note_name ,octave = 5 ,velocity = 64 , output = None , app = None ):
5839 """
5940 Initializing function of the class which takes 1 required argument and 2 optional arguments.
6041 note_name: name of the note
@@ -70,39 +51,41 @@ def __init__(self,note_name,octave=5,velocity=64):
7051 self .__saved_pitch = 0
7152 self .__entry_box_number = NoteButton .counter
7253 self .__entry_box = None
54+ self .output = output
55+ self .app = app
7356
7457 # buttons are used to trigger 3 basic events: send note_on, wait for 100 miliseconds and send note_off
7558 # so command argument is used with lambda to trigger more than one function
7659 if note_name in PURE_NOTES :
7760 # Making note button white if the note is a pure note
7861 button = Button (
79- app ,text = note_name ,
80- command = lambda : (self .set_pitch (self .get_saved_pitch ()), self .send_note_on (), self .send_pitch_wheel () , time .sleep (0.1 ), self .send_note_off ()),
62+ self . app ,text = note_name ,
63+ command = lambda : (self .set_pitch (self .get_saved_pitch ()), self .set_velocity ( 64 ), self . send_note_on (), self .send_pitch_wheel () , time .sleep (0.1 ), self .send_note_off ()),
8164 width = 10 ,height = 15 ,bg = "white" ,fg = "#241f1f" ,activebackground = "white" ,activeforeground = "#241f1f" ,font = ("Arial" , 10 , "bold" ))
8265 else :
8366 # Making note button black if the note is not a pure note
8467 button = Button (
85- app ,text = note_name ,
86- command = lambda : (self .set_pitch (self .get_saved_pitch ()), self .send_note_on (), self .send_pitch_wheel () , time .sleep (0.1 ), self .send_note_off ()),
68+ self . app ,text = note_name ,
69+ command = lambda : (self .set_pitch (self .get_saved_pitch ()), self .set_velocity ( 64 ), self . send_note_on (), self .send_pitch_wheel () , time .sleep (0.1 ), self .send_note_off ()),
8770 width = 4 ,height = 7 ,bg = "#241f1f" ,fg = "white" ,activebackground = "#241f1f" ,activeforeground = "white" ,font = ("Arial" , 10 , "bold" ))
8871
8972 # Placing label (note text) and entry box in the GUI
9073 if note_name in PURE_NOTES :
9174 NoteButton .label_counter += 1.5
9275
93- label = Label (app , text = note_name )
76+ label = Label (self . app , text = note_name )
9477 label .place (x = 10 , y = 10 + (NoteButton .label_counter * 20 ))
9578
96- self .__entry_box = Entry (app , width = 5 )
79+ self .__entry_box = Entry (self . app , width = 5 )
9780 self .__entry_box .place (x = 50 , y = 10 + (NoteButton .label_counter * 20 ))
9881 self .__entry_box .bind ('<Return>' , lambda event : self .set_saved_pitch (self .__entry_box .get ()))
9982 else :
10083 NoteButton .label_counter += 1.5
10184
102- label = Label (app , text = note_name )
85+ label = Label (self . app , text = note_name )
10386 label .place (x = 100 , y = 10 + (NoteButton .label_counter * 20 ))
10487
105- self .__entry_box = Entry (app , width = 5 )
88+ self .__entry_box = Entry (self . app , width = 5 )
10689 self .__entry_box .place (x = 130 , y = 10 + (NoteButton .label_counter * 20 ))
10790 self .__entry_box .bind ('<Return>' , lambda event : self .set_saved_pitch (self .__entry_box .get ()))
10891
@@ -193,29 +176,29 @@ def send_note_on(self):
193176 """
194177
195178 NoteButton .last_pressed_note = self .get_note_name ()
196- output .send ( NoteButton .control_change )
179+ self . output .send ( NoteButton .control_change )
197180
198181 # Label will put an extra space if note is a pure note
199182 if self .get_note_name () in PURE_NOTES :
200- Label (app , text = f"Sending { self .get_note_name ()} octave { self .get_octave ()} with \n { self .__pitch_value } pitch and { self .get_velocity ()} velocity" ,font = ("Arial" ,12 ,"bold" )).place (x = 200 , y = 40 )
183+ Label (self . app , text = f"Sending { self .get_note_name ()} octave { self .get_octave ()} with \n { self .__pitch_value } pitch and { self .get_velocity ()} velocity" ,font = ("Arial" ,12 ,"bold" )).place (x = 200 , y = 40 )
201184 else :
202- Label (app , text = f"Sending { self .get_note_name ()} octave { self .get_octave ()} with \n { self .__pitch_value } pitch and { self .get_velocity ()} velocity" ,font = ("Arial" ,12 ,"bold" )).place (x = 200 , y = 40 )
185+ Label (self . app , text = f"Sending { self .get_note_name ()} octave { self .get_octave ()} with \n { self .__pitch_value } pitch and { self .get_velocity ()} velocity" ,font = ("Arial" ,12 ,"bold" )).place (x = 200 , y = 40 )
203186
204187 # Sending midi signal
205- output .send ( mido .Message ('note_on' , note = note_to_number (self .get_note_name (), self .get_octave ()), velocity = self .get_velocity ()) )
188+ self . output .send ( mido .Message ('note_on' , note = note_to_number (self .get_note_name (), self .get_octave ()), velocity = self .get_velocity ()) )
206189
207190 def send_pitch_wheel (self ):
208191 """Sending converted pitch value to the midi device"""
209192 sending_value = converter (self .get_pitch (), False )
210- output .send ( mido .Message ('pitchwheel' , pitch = sending_value ) )
193+ self . output .send ( mido .Message ('pitchwheel' , pitch = sending_value ) )
211194
212195 def send_note_off (self ):
213196 """Sending note_off message to the midi device"""
214- output .send ( mido .Message ('note_off' , note = note_to_number (self .get_note_name (), self .get_octave ()), velocity = self .get_velocity ()) )
215- output .send ( NoteButton .control_change )
197+ self . output .send ( mido .Message ('note_off' , note = note_to_number (self .get_note_name (), self .get_octave ()), velocity = self .get_velocity ()) )
198+ self . output .send ( NoteButton .control_change )
216199
217200 @staticmethod
218- def change_control ():
201+ def change_control (output ):
219202 """Sending control_change message to the midi device"""
220203 output .send (NoteButton .control_change )
221204
@@ -461,20 +444,6 @@ def default_labels():
461444
462445 save_new_button = Button (app , text = "Save New" , command = init_set_screen , width = 7 , height = 1 ).place (x = 495 , y = 250 )
463446
464- def note_to_number (note : str , octave : int ):
465- """Converting passed note name and octave to Midi note number"""
466- note = NOTES .index (note ) + 4
467- note += (12 * octave )
468-
469- return note - 16
470-
471- def number_to_note (number : int ):
472- """Converting passed Midi note number to note name and octave"""
473- note = NOTES [(number % 12 )- 4 ]
474- octave = math .floor ((number + 8 )/ 12 )
475-
476- return [note , octave ]
477-
478447def coming_note (msg ):
479448 """
480449 Core function for configuring incoming midi messages,
@@ -532,24 +501,16 @@ def coming_note(msg):
532501 break
533502 elif msg .type == 'control_change' :
534503 NoteButton .control_change = msg
535- threading .Thread (target = lambda : NoteButton .change_control ()).start ()
504+ threading .Thread (target = lambda : NoteButton .change_control (output )).start ()
536505
537506def close_program ():
538507 """
539508 Closes the program and saves json data if there is any.
540509 This function is also checking if there is any json file created to prevent overriding the old file.
541510 """
542511 global app ,note_bool ,json_data
543- if json_data :
544- files = os .listdir ()
545- json_files = [f for f in files if f .endswith ('.json' )]
546- if json_files :
547- last_number = int ((json_files [- 1 ].split ("." )[0 ])[- 1 ])
548- with open (f"pitch_data{ last_number + 1 } .json" , "w" ) as fp :
549- json .dump (json_data , fp , indent = 4 )
550- else :
551- with open (f"pitch_data1.json" , "w" ) as fp :
552- json .dump (json_data , fp , indent = 4 )
512+
513+ save_json (json_data )
553514 app .destroy ()
554515 note_bool = False
555516
@@ -609,7 +570,7 @@ def port_select_screen():
609570 port_screen .mainloop ()
610571
611572def main ():
612- global app ,global_button_list
573+ global app ,global_button_list , output
613574
614575 # Initializing the port selection window
615576 port_select_screen ()
@@ -624,10 +585,10 @@ def main():
624585
625586 # Creating the note buttons
626587 for item in PURE_NOTES :
627- global_button_list .append (NoteButton (item ))
588+ global_button_list .append (NoteButton (item , output = output , app = app ))
628589 NoteButton .label_counter = 0
629590 for item in OTHER_NOTES :
630- global_button_list .append (NoteButton (item ))
591+ global_button_list .append (NoteButton (item , output = output , app = app ))
631592
632593 # Initializing the main window label and widgets
633594 default_labels ()
0 commit comments