patternpythonMinor
Creating lists of events (pitches, velocities, delta-times, durations) from a MIDI track
Viewed 0 times
durationsvelocitiestrackeventscreatingmidiliststimesfromdelta
Problem
I am trying to understand good design patterns in Python and I cannot think of a way to break this huge function into smaller parts without making the code cluttered, overly complex or plain ugly.
I didn't want to clutter my question by posting the whole file. This function itself is already very large, but the class has only two methods:
pitches, velocities, deltas, durations, and intervals are all
```
def parse_midi(self):
# on_notes dictionary holds note_on events until corresponding note_of event is encountered
on_notes = {}
time = 0
previous_pitch = -1
tempos = []
delta = 0
for message in self.track_in:
time += message.time
delta += message.time
# There are also MetaMessages in a midi file, such as comments, track names, etc.
# We just ignore them
if isinstance(message, mido.Message) and message.type in ["note_on", "note_off"]:
# some midi files use note_on events with 0 velocity instead of note_oof events
# so we check if velocity > 0
if message.velocity > 0 and message.type == "note_on":
on_notes[message.note] = time
self.pitches.add_event(message.note)
self.velocities.add_event(message.velocity)
self.deltas.add_event(delta)
delta = 0
if previous_pitch == -1:
self.intervals.add_event(0)
else:
self.intervals.add_event(message.note - previous_pitch)
else:
# KeyError means note_off came without a prior associated note_on event!"
I didn't want to clutter my question by posting the whole file. This function itself is already very large, but the class has only two methods:
parse_midi() and generate_midi(file_name, file_length).pitches, velocities, deltas, durations, and intervals are all
MarkovChain objects. MarkovChain is a simple class with methods: add_event(event), generate_markov_dictionary(), and get_next_event(previous_event). MarkovChain.src_events is a list of events to generate the Markov chain from. It is a simple implementation of first order Markov chains.```
def parse_midi(self):
# on_notes dictionary holds note_on events until corresponding note_of event is encountered
on_notes = {}
time = 0
previous_pitch = -1
tempos = []
delta = 0
for message in self.track_in:
time += message.time
delta += message.time
# There are also MetaMessages in a midi file, such as comments, track names, etc.
# We just ignore them
if isinstance(message, mido.Message) and message.type in ["note_on", "note_off"]:
# some midi files use note_on events with 0 velocity instead of note_oof events
# so we check if velocity > 0
if message.velocity > 0 and message.type == "note_on":
on_notes[message.note] = time
self.pitches.add_event(message.note)
self.velocities.add_event(message.velocity)
self.deltas.add_event(delta)
delta = 0
if previous_pitch == -1:
self.intervals.add_event(0)
else:
self.intervals.add_event(message.note - previous_pitch)
else:
# KeyError means note_off came without a prior associated note_on event!"
Solution
Your notion of average tempo makes little sense. A track with four minutes of ♩=120 followed by a 12-second coda of ♩=60 would have a reported average tempo of 90. A more reasonable average would be to divide the total number of beats by the elapsed time.
$$\frac{120 \frac{\textrm{beats}}{\textrm{min}} × 4\ \textrm{min} +
60 \frac{\textrm{beats}}{\textrm{min}} × 0.2\ \textrm{min}}
{4.2\ \textrm{min}} \approx 117 \frac{\textrm{beats}}{\textrm{min}}
$$
$$\frac{120 \frac{\textrm{beats}}{\textrm{min}} × 4\ \textrm{min} +
60 \frac{\textrm{beats}}{\textrm{min}} × 0.2\ \textrm{min}}
{4.2\ \textrm{min}} \approx 117 \frac{\textrm{beats}}{\textrm{min}}
$$
Context
StackExchange Code Review Q#85389, answer score: 2
Revisions (0)
No revisions yet.