Task : Build a glove with vibrators for the each finger where when the midi file is on the play and a note comes up its corresponding finger number is extracted and given the signal to the vibrator.
What is XML file?
XML stands for Extensible Markup Language, which is a markup language used for structuring and organizing data. XML files are text files that contain data in a hierarchical and structured format. XML is often used for representing data that needs to be exchanged between different systems, as it provides a standard format for data interchange.
In an XML file, data is structured using tags, which are enclosed in angle brackets "<" and ">". Each tag describes a piece of data and can have attributes and values. XML files also typically have a root element, which is the top-level element that contains all other elements in the file.
Here's an example of what an XML file might look like:
Music XML
MusicXML is a file format for representing Western musical notation in a digital form. It is a standard format that allows music to be exchanged between different music notation software and other applications. MusicXML files can contain information about the notes, rhythms, chords, lyrics, and other elements of a musical score.
The MusicXML format was developed by Recordare LLC and was first released in 2004. It is now supported by many music notation software applications, including Finale, Sibelius, MuseScore, Dorico, and others.
MusicXML files use XML syntax to represent musical notation. The files can contain information about the structure of a musical score, including measures, staves, and parts. They can also include information about musical symbols, such as notes, rests, and dynamics.
Here's an example of what a MusicXML file might look like:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 3.1 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="3.1">
<part-list>
<score-part id="P1">
<part-name>Music</part-name>
</score-part>
</part-list>
<part id="P1">
<measure number="1">
<attributes>
<divisions>4</divisions>
<key>
<fifths>0</fifths>
<mode>major</mode>
</key>
<time>
<beats>4</beats>
<beat-type>4</beat-type>
</time>
<clef>
<sign>G</sign>
<line>2</line>
</clef>
</attributes>
<note>
<pitch>
<step>C</step>
<octave>4</octave>
</pitch>
<duration>4</duration>
<type>whole</type>
<stem>up</stem>
</note>
</measure>
</part>
</score-partwise>
import music21
import numpy as np
import threading
import time
from playsound import playsound
import serial
arduino = serial.Serial(port='COM8', baudrate=115200, timeout=.1)
time.sleep(5)
def write_data(x):
arduino.write(bytearray(x))
xmlpath = 'Piano Fingering.musicxml'
score = music21.converter.parse(xmlpath)
import xml.etree.ElementTree as ET
def get_right_hand_notes(note_list):
right_list = []
for note in note_list:
if (note.findall('staff')[0]).text=='1' and len(note.findall('rest'))==0:
right_list.append(note)
return right_list
def get_unique_notes(note_list):
count1 = 0
count2 = 0
count1 = len(note_list)
for note in note_list:
if len(note.findall('chord'))>0:
count2+=1
unique_notes = count1 - count2
return unique_notes
def get_finger_list(filename):
mytree = ET.parse(filename)
myroot = mytree.getroot()
measure_list = 0
for child in myroot:
if child.tag=='part':
measure_list = child
break
N_measures = len(measure_list)
# print(f'Score contains {N_measures} Measures \n')
finger_list = []
for measure_number in range(len(measure_list)):
measure = measure_list[measure_number]
all_note_list = measure.findall('note')
rh_note_list = get_right_hand_notes(all_note_list)
n_unique_notes = get_unique_notes(rh_note_list)
measure_finger_list = []
chord_finger_list = []
for i in range(len(rh_note_list)):
note = rh_note_list[i]
if len(note.findall('chord'))>0:
chord_finger_list.append(note.findall('notations')[0].findall('technical')[0].findall('fingering')[0].text)
else:
if i>0:
measure_finger_list.append(chord_finger_list)
chord_finger_list = []
chord_finger_list.append(note.findall('notations')[0].findall('technical')[0].findall('fingering')[0].text)
if len(chord_finger_list)>0:
measure_finger_list.append(chord_finger_list)
if len(measure_finger_list)>0:
finger_list = finger_list + measure_finger_list
return finger_list
right_hand = score.parts[0]
print(score.parts[0])
note_duration = [] # Duration attribute
measure_offest = [] #
notes_offest = []
for i in range(1,len(right_hand[:])):
measure = right_hand.measure(i)
print(measure)
measure_offest.append( float(measure.offset) )
for notes in measure.flat.notes:
print(notes.articulations, notes.duration, measure.offset, notes.offset)#, notes.seconds)
note_duration.append( float(str(notes.duration).split()[1].split('>')[0]) )
notes_offest.append( float(notes.offset)+float(measure.offset) )
Total_counts = int( max(measure_offest)*len(measure_offest) / (len(measure_offest)-1)/min(note_duration) )
right_hand_counts = np.zeros((5,Total_counts), dtype=np.int8)
note_offset_counts = np.round(np.array(notes_offest)/min(note_duration)).astype(int)
note_duration_counts = np.round(np.array(note_duration)/min(note_duration)).astype(int)
finger_number = get_finger_list(xmlpath)
for i in range(len(note_offset_counts)):
for j in finger_number[i]:
right_hand_counts[int(j)-1, note_offset_counts[i] : note_offset_counts[i]+note_duration_counts[i] ] = 1
tempo_markings = score.flat.getElementsByClass(music21.tempo.TempoIndication)
# Get the BPM from the first tempo marking (if present)
if len(tempo_markings) > 0:
bpm = tempo_markings[0].getQuarterBPM()
print("BPM = ",bpm)
time_per_beat = 60/bpm
print("time per beat = ",time_per_beat)
time_per_shortest_note = time_per_beat*min(note_duration)
print("time per shortest note = ",time_per_shortest_note)
def play_midi():
"""Thread worker function"""
print('Worker thread 1 started')
# display(Audio(out_wav, autoplay=True, rate=44100))
playsound("out.mp3")
print('Worker thread 1 finished')
def serial_out():
"""Thread worker function"""
print('Worker thread 2 started')
# time.sleep(7)
for i in range(Total_counts):
print(bytearray(list(right_hand_counts[:,i])))
write_data(list(right_hand_counts[:,i]))
time.sleep(time_per_shortest_note)
# end_time = time.monotonic()
# print(end_time-start_time)
write_data([0,0,0,0,0])
print('Worker thread 2 finished')
# Create a new thread
thread1 = threading.Thread(target=play_midi)
thread2 = threading.Thread(target=serial_out)
# Start the thread
thread1.start()
thread2.start()
# Wait for the thread to finish
thread1.join()
thread2.join()
print('Main thread finished')