diff --git a/loading.py b/loading.py
index 7688dda92ce2f46e4f77e73d976f3ac6da5cb529..72b11dfb270e1135d4cd408796ed57210181b328 100644
--- a/loading.py
+++ b/loading.py
@@ -26,3 +26,4 @@ class LoadingView(BaseView):
         if self.vm.transitioning or not self.vm.is_active(self): return
         gc.collect()
         self.vm.replace(song.SongView(self.app, self.song, self.difficulty), ViewTransitionBlend())
+        gc.collect()
diff --git a/midi/EventDispatcher.py b/midi/EventDispatcher.py
index 9014d265497b794f6c057de96003e960ac955634..5e5530284ef9dcb60f49e0345376932c765dded1 100644
--- a/midi/EventDispatcher.py
+++ b/midi/EventDispatcher.py
@@ -53,18 +53,20 @@ class EventDispatcher:
 
 
     def start_of_track(self, current_track):
+        pass
         "Triggers the start of track event"
         
         # I do this twice so that users can overwrite the 
         # start_of_track event handler without worrying whether the 
         # track number is updated correctly.
-        self.outstream.set_current_track(current_track)
-        self.outstream.start_of_track(current_track)
+        #self.outstream.set_current_track(current_track)
+        #self.outstream.start_of_track(current_track)
         
     
     def sysex_event(self, data):
         "Dispatcher for sysex events"
-        self.outstream.sysex_event(data)
+        pass
+        #self.outstream.sysex_event(data)
     
     
     def eof(self):
@@ -78,8 +80,9 @@ class EventDispatcher:
         
         
     def reset_time(self):
+        pass
         "Updates relative/absolute time."
-        self.outstream.reset_time()
+        #self.outstream.reset_time()
         
         
     # Event dispatchers for similar types of events
@@ -104,8 +107,10 @@ class EventDispatcher:
         elif (NOTE_OFF & 0xF0) == hi_nible:
             note, velocity = data
             stream.note_off(channel, note, velocity)
+            
+        return
         
-        elif (AFTERTOUCH & 0xF0) == hi_nible:
+        if (AFTERTOUCH & 0xF0) == hi_nible:
             note, velocity = data
             stream.aftertouch(channel, note, velocity)
         
@@ -137,21 +142,22 @@ class EventDispatcher:
 
 
     def continuous_controllers(self, channel, controller, value):
-    
+        pass
         "Dispatches channel messages"
 
-        stream = self.outstream
+        #stream = self.outstream
         
         # I am not really shure if I ought to dispatch continuous controllers
         # There's so many of them that it can clutter up the OutStream 
         # classes.
         
         # So I just trigger the default event handler
-        stream.continuous_controller(channel, controller, value)
+        #stream.continuous_controller(channel, controller, value)
 
 
 
     def system_commons(self, common_type, common_data):
+        return
     
         "Dispatches system common messages"
         
@@ -185,6 +191,13 @@ class EventDispatcher:
         
         stream = self.outstream
         
+        if meta_type == TEMPO:
+            b1, b2, b3 = toBytes(data)
+            # uses 3 bytes to represent time between quarter 
+            # notes in microseconds
+            stream.tempo((b1<<16) + (b2<<8) + b3)
+        return        
+        
         # SEQUENCE_NUMBER = 0x00 (00 02 ss ss (seq-number))
         if meta_type == SEQUENCE_NUMBER:
             number = readBew(data)
diff --git a/midi/MidiFileParser.py b/midi/MidiFileParser.py
index 97f28553cc1689688b2028c54de516ad6a9d7891..cc9ea037e677aea263848f2f2cad45afb920cc88 100644
--- a/midi/MidiFileParser.py
+++ b/midi/MidiFileParser.py
@@ -62,7 +62,6 @@ class MidiFileParser:
         self.dispatch.header(self.format, self.nTracks, self.division)
 
 
-
     def parseMTrkChunk(self):
         
         "Parses a track chunk. This is the most important part of the parser."
@@ -120,35 +119,6 @@ class MidiFileParser:
                 if not meta_length: return
                 dispatch.meta_event(meta_type, meta_data)
                 if meta_type == END_OF_TRACK: return
-
-
-            # Is it a sysex_event ??
-            elif status == SYSTEM_EXCLUSIVE:
-                # ignore sysex events
-                sysex_length = raw_in.readVarLen()
-                # don't read sysex terminator
-                sysex_data = raw_in.nextSlice(sysex_length-1)
-                # only read last data byte if it is a sysex terminator
-                # It should allways be there, but better safe than sorry
-                if raw_in.readBew(move_cursor=0) == END_OFF_EXCLUSIVE:
-                    eo_sysex = raw_in.readBew()
-                dispatch.sysex_event(sysex_data)
-                # the sysex code has not been properly tested, and might be fishy!
-
-
-            # is it a system common event?
-            elif hi_nible == 0xF0: # Hi bits are set then
-                data_sizes = {
-                    MTC:1,
-                    SONG_POSITION_POINTER:2,
-                    SONG_SELECT:1,
-                }
-                data_size = data_sizes.get(hi_nible, 0)
-                common_data = raw_in.nextSlice(data_size)
-                common_type = lo_nible
-                dispatch.system_common(common_type, common_data)
-            
-
             # Oh! Then it must be a midi event (channel voice message)
             else:
                 data_sizes = {
diff --git a/midireader.py b/midireader.py
index 26a16e263866783fd75d6b3bff111737e3a3a524..ea725a9e601652596e9db2ed3830b292abf3d168 100644
--- a/midireader.py
+++ b/midireader.py
@@ -68,7 +68,7 @@ class Tempo(Event):
     return "<%d bpm>" % self.bpm
 
 class Track:
-  granularity = 50
+  granularity = 500
   
   def __init__(self):
     self.events = []
@@ -83,12 +83,12 @@ class Track:
       self.events[t].add(event)
     self.allEvents.add(event)
 
-  def getEvents(self, startTime, endTime):
-    t1, t2 = [int(x) for x in [startTime / self.granularity, endTime / self.granularity]]
+  def getEvents(self, startTime, endTime, events):
+    t1, t2 = [int(x) for x in [startTime / self.granularity, endTime / self.granularity + 1]]
     if t1 > t2:
       t1, t2 = t2, t1
 
-    events = set()
+    events.clear()
     for t in range(max(t1, 0), min(len(self.events), t2)):
       for event in self.events[t]:
         events.add(event)
diff --git a/song.py b/song.py
index 6d039a33363b30d8c243d66478846cc648ace614..50fe07781ec1310862d44b689bca52b412d8e798 100644
--- a/song.py
+++ b/song.py
@@ -6,6 +6,7 @@ import math
 import media
 import leds
 import sys_display
+from micropython import const
 
 if __name__ == '__main__':
     import sys
@@ -16,10 +17,12 @@ import midireader
 import utils
 import flower
 import score
+import gc
 
-AUDIO_DELAY = -90
-VIDEO_DELAY = 60 - AUDIO_DELAY
-RADIUS = 22
+AUDIO_DELAY = const(-90)
+VIDEO_DELAY = const(60 - AUDIO_DELAY)
+RADIUS = const(22)
+tau = const(6.283185307179586)
 
 class SongView(BaseView):
     def __init__(self, app, song, difficulty):
@@ -41,7 +44,7 @@ class SongView(BaseView):
         self.started = False
         self.time = -self.delay
         self.flower = flower.Flower(0)
-        self.events = []
+        self.events = set()
         self.petals = [None] * 5
         self.demo_mode = False
         self.fps = False
@@ -54,13 +57,19 @@ class SongView(BaseView):
         self.led_override = [0] * 5
         self.laststreak = -1
         self.scoreview = None
+        self.notes = set()
+        self.events_in_margin = set()
+        self.petal_events = [set() for i in range(5)]
         
         self.good = 0.0
         self.bad = 0.0
         self.missed = [0.0] * 5
         self.miss = 0.0
 
+        self.oldmem = 0
+
     def draw(self, ctx: Context) -> None:
+        #mem = gc.mem_alloc()
         self.time += VIDEO_DELAY
         
         other = int(self.time / 2 / self.data.period) % 2
@@ -71,7 +80,8 @@ class SongView(BaseView):
                 
         ctx.gray(0.25)
         
-        for i in [1, 0]:
+        i = 1
+        while i >= 0:
             #ctx.gray(0.4 + i * 0.12 + (1-(self.time/2 / self.data.period) % 1) * 0.12)
             #ctx.line_width = 1.75 + i * 0.12 + (1-(self.time/2 / self.data.period) % 1) * 0.12
             ctx.gray((0.1 if other ^ (i == 1) else 0.0) + self.miss * 0.15)
@@ -83,6 +93,7 @@ class SongView(BaseView):
                 else:
                     ctx.arc(0, 0, RADIUS + pos, 0, tau, 0)
                     ctx.fill()
+            i -= 1
         self.time -= VIDEO_DELAY
         
         ctx.line_width = 2
@@ -115,7 +126,7 @@ class SongView(BaseView):
             utils.circle(ctx, 0, 0, RADIUS)
         
         ctx.save()
-        ctx.rotate(tau / 10 + tau / 5)
+        ctx.rotate(const(tau / 10 + tau / 5))
         start = self.time + self.data.period * 4
         stop = self.time
         for i in range(5):
@@ -212,8 +223,13 @@ class SongView(BaseView):
             ctx.font_size = 16
             ctx.move_to(0, 105)
             ctx.text(f"{sys_display.fps():.2f}")
+        #print("draw", gc.mem_alloc() - mem)
 
     def think(self, ins: InputState, delta_ms: int) -> None:
+        #mem = gc.mem_alloc()
+        #print(mem - self.oldmem)
+        #self.oldmem = mem
+        
         super().think(ins, delta_ms)
 
         if self.first_think:
@@ -275,24 +291,27 @@ class SongView(BaseView):
         self.miss = max(0, self.miss - delta_ms / self.data.period)
         for i in range(5):
             self.missed[i] = max(0, self.missed[i] - delta_ms / 1500)
+            self.petal_events[i].clear()
 
         earlyMargin       = 60000.0 / self.data.bpm / 3.5
         lateMargin        = 60000.0 / self.data.bpm / 3.5
 
-        notes = set()
-        events_in_margin = set()
+        self.notes.clear()
+        self.events_in_margin.clear()
 
         if self.app and not self.finished:
-            self.events = self.data.track.getEvents(self.time - self.data.period / 2, self.time + self.data.period * 4)
+            self.data.track.getEvents(self.time - self.data.period / 2, self.time + self.data.period * 4, self.events)
         else:
-            self.events = []
+            self.events.clear()
         
         for event in self.events:
             if isinstance(event, midireader.Note):
                 if event.time <= self.time <= event.time + event.length:
-                    notes.add(event.number)
+                    self.notes.add(event.number)
                 if event.time - earlyMargin <= self.time <= event.time + lateMargin:
-                    events_in_margin.add(event)
+                    self.events_in_margin.add(event)
+                    if not event.played:
+                        self.petal_events[event.number].add(event)
                 if event.time + lateMargin < self.time and not event.played and not event.missed:
                     event.missed = True
                     self.streak = 0
@@ -309,7 +328,7 @@ class SongView(BaseView):
         for petal in range(5):
             p = 4 if petal == 0 else petal - 1
             pressed = ins.captouch.petals[p*2].pressed
-            events = set(filter(lambda x: x.number == petal and not x.played, events_in_margin))
+            events = self.petal_events[petal]
 
             self.led_override[petal] = max(0, self.led_override[petal] - delta_ms)
 
@@ -319,7 +338,11 @@ class SongView(BaseView):
                     self.bad = 1.0
                     self.streak = 0
                 else:
-                    event = sorted(events, key = lambda x: x.time)[0]
+                    event = events.pop()
+                    for e in events:
+                        if e.time < event.time:
+                            event = e
+                    #event = sorted(events, key = lambda x: x.time)[0]
                     event.played = True
                     self.led_override[petal] = 120
                     if event.time > self.laststreak:
@@ -334,11 +357,12 @@ class SongView(BaseView):
                 self.petals[petal] = None
 
             active = self.petals[petal] is not None
-            d = 1.0 if (active and self.petals[petal].time + self.petals[petal].length >= self.time) or self.led_override[petal] else (0.15 if pressed else (1.0 if petal in notes and self.demo_mode else 0.069))
+            d = 1.0 if (active and self.petals[petal].time + self.petals[petal].length >= self.time) or self.led_override[petal] else (0.15 if pressed else (1.0 if petal in self.notes and self.demo_mode else 0.069))
             if d:
                 utils.petal_leds(petal, d)
 
         leds.update()
+        #print("think", gc.mem_alloc() - mem)
 
     def on_enter(self, vm: Optional[ViewManager]) -> None:
         super().on_enter(vm)
diff --git a/utils.py b/utils.py
index eaec8da317d15bcbb4ce90563bc59f212cf70301..d456ba06846c1482cde3965d090c6234292f8a65 100644
--- a/utils.py
+++ b/utils.py
@@ -3,7 +3,11 @@ import leds
 import random
 import os
 
-dim = lambda x, y: tuple(map(lambda x: x * y, x))
+def dim(color, val):
+    res = []
+    for i in range(len(color)):
+        res.append(color[i] * val)
+    return res
 
 def background(ctx):
     ctx.linear_gradient(-120, -120, 120, 120)
@@ -34,9 +38,8 @@ def petal_leds(petal, val, color = None):
     if not color:
         color = PETAL_COLORS[petal]
     color = dim(color, val)
-    start = -11 + petal * 8
-    for i in range(start, start + 7):
-        led = i
+    for i in range(7):
+        led = i - 11 + petal * 8
         if led < 0:
             led += 40
         leds.set_rgb(led, *color)