Final Implementation Details and Reflection
ø
Final Implementation Details
Our final implementation, in terms of hardware, changed a bit from conception to now. The main difference is that there is no start/stop button (ran out of room on the teensy in terms of touch pins and did not have time to implement a button) and there is an optometer plugged in to an analog read that codes for different octaves. Otherwise, the original 7 notes and sharp/flat switches are maintained.
On the software side, the pre-programmed songs play upon initial run of the program to reflect the lack of start/stop button. Programming of the song was facilitated by a python library that turned midi files into midi objects. The objects were then parsed for the information needed by the teensy usbMIDI library, namely note numbers and timings. As, due to memory issues, the entire song would not play, a python script was run to split the sequences of notes and timings into 103 arrays of size 10. This worked much better. The code is copied below this blog post.
Reflections
We definitely learned how to extend existing circuit designs to suit our needs. Furthermore, we learned more about MIDI file structure. Notes in MIDI run from 1-127 mapping C0 to C9. It is also divided into a series of tracks and events that control the sequencing of the notes. The timing is done with ticks, which are relative timings based on a resolution. The higher the resolution the more sampling. Knowing this structure was crucial in writing the scripts that made the arrays to run the songs.
In our implementation, we employed knowledge on circuitry, teensy, and compositional fundamentals to create our project. More practical application with the circuitry and with composition writing material would have helped.
Division of Work
Julio mostly did the implementation of the keyboard as well as writing the code to turn the compositions into runnable code. Sasha wrote the compositions for this as well as helped with some aspects of the code.
import midi
import serial
import time
# Class Needs:
# take midi input
# output text file of note sequences
# output text file of note durations
class Song(object):
def __init__(self, midi_file):
self.midi_file = midi_file
self.pattern = midi.read_midifile(self.midi_file)
def get_bpm(self):
for track in self.pattern:
for event in track:
if type(event) is midi.events.SetTempoEvent:
return event.get_bpm()
break
break
def get_timings_and_notes(self, bpm):
#MIDI does things timing relative ticks
#Find how much time per tick using resolution
#MIDI timings in microseconds but we need milliseconds
milli_per_tick = (((60 * 1000000) / bpm)/self.pattern.resolution)/1000
timings = list()
notes = list()
for track in self.pattern:
for event in track:
if type(event) is midi.events.NoteOnEvent:
notes.append(event.data[0])
if type(event) is midi.events.NoteOffEvent:
timings.append(event.tick * milli_per_tick)
return timings,notes
def get_file(self):
self.pattern = midi.read_midifile(self.midi_file)
timings,notes = self.get_timings_and_notes(float(self.get_bpm()))
filename = self.midi_file[self.midi_file.rfind('/')+1:self.midi_file.rfind('.')] + str(0) + ".txt"
f = open(filename, "w+")
array_num = 0
f.write("int notes_1" + str(array_num) + "[] = ")
nums = str()
for x in range(len(notes)):
nums += str(notes[x]) + ","
if x == 0:
f.write('{')
# nums += str(notes[x]) + ","
if (x + 1) % 10 == 0:
array_num += 1
f.write(nums[:-1] + '};\nint notes_1' + str(array_num) + '[] =')
nums = str()
f.write('{')
f.write('\n')
array_num = 0
f.write("float times_1" + str(array_num) + "[] = ")
times = str()
for x in range(len(timings)):
times += str(timings[x]) + ","
if x == 0:
f.write('{')
# times += str(timings[x]) + ","
if (x + 1) % 10 == 0:
array_num += 1
f.write(times[:-1] + '};\nfloat times_1' + str(array_num) + '[] =')
times = str()
f.write('{')
f.close()
def main():
song_1 = Song("/Users/juliomendez/Downloads/es_track1.mid")
song_1.get_file()
timings, notes = song_1.get_timings_and_notes(song_1.get_bpm())
ser = serial.Serial('/dev/cu.usbmodem3136991', 9600)
for x in range(len(timings)):
ser.write(timings[x])
time.sleep(.001)
ser.write(notes[x])
f = open("play.txt", "w+")
for x in range(103):
f.write("play(notes_1"+str(x)+", times_1" + str(x) + ");\n")
if __name__ == '__main__':
main()