diff --git a/__init__.py b/__init__.py
index 62b5e275894e676449b4361c4a87859f7ecd38fc..8d0ef459a6ebe62a5a6851257b928fc0a5b93251 100644
--- a/__init__.py
+++ b/__init__.py
@@ -33,8 +33,8 @@ class PetalHero(Application):
         self.loaded = False
         self.blm = None
         self.fiba_sound = None
-        self.select = select.SelectView(self.app)
         self.after_score = False
+        self.select = None
         #self.blm_extra = bl00mbox.Channel("Petal Hero Extra")
         #self.blm_extra.background_mute_override = True
         
@@ -168,6 +168,8 @@ class PetalHero(Application):
 
         if self.input.buttons.app.middle.pressed:
             utils.play_go(self.app)
+            if not self.select:
+                self.select = select.SelectView(self.app)
             self.vm.push(self.select, ViewTransitionSwipeLeft())
 
     def on_enter(self, vm) -> None:
diff --git a/difficulty.py b/difficulty.py
index 05e26ef20f2740fee21436183f52db4c61bb81a1..1fb18f7bfa84028c17ec3825e5ca17a59f811513 100644
--- a/difficulty.py
+++ b/difficulty.py
@@ -52,6 +52,11 @@ class DifficultyView(BaseView):
         ctx.text_baseline = ctx.MIDDLE
 
         ctx.move_to(0, 0)
+        if not self.song.difficulties:
+            ctx.gray(0.0)
+            ctx.font_size = 24
+            ctx.text("No guitar track found!")
+
         for idx, diff in enumerate(self.song.difficulties):
             target = idx == self._sc.target_position()
             if target:
@@ -121,8 +126,9 @@ class DifficultyView(BaseView):
             
         if self.input.buttons.app.middle.pressed:
             utils.play_go(self.app)
-            media.stop()
-            self.vm.replace(loading.LoadingView(self.app, self.song, self.song.difficulties[self._sc.target_position()]), ViewTransitionBlend())
+            if self.song.difficulties:
+                media.stop()
+                self.vm.replace(loading.LoadingView(self.app, self.song, self.song.difficulties[self._sc.target_position()]), ViewTransitionBlend())
 
     def on_exit(self):
         super().on_exit()
diff --git a/readme.py b/readme.py
index 1de3855ba4b5af60700aaae836ff10db4e29e7e6..63998f3e9e1e8aaacf4d2038df160b3c87202fe9 100644
--- a/readme.py
+++ b/readme.py
@@ -3,8 +3,10 @@ import os
 
 README = """Put your Petal Hero songs here.
 
-Petal Hero is compatible with songs for Frets on Fire, but with
-one caveat: you need to mix audio tracks and save them as MP3.
+Petal Hero is compatible with songs for Frets on Fire, FoFiX, Performous,
+Phase Shift and Clone Hero (MIDI) that contain a guitar track, but with one
+caveat: you need to mix audio tracks and save them as MP3.
+
 This should do:
 
   sox -m *.ogg -c 1 -C 128 -r 48k song.mp3 norm -3
diff --git a/select.py b/select.py
index 9a5317cb28fb1bcd157dbd80f869fba0fdbbb9fd..6c597dea6df651da14a3cd28a5543d327f3ab820 100644
--- a/select.py
+++ b/select.py
@@ -13,6 +13,21 @@ import difficulty
 import songinfo
 import utils
 
+class LazySong(songinfo.SongInfo):
+    def __init__(self, dirpath):
+        self.dirpath = dirpath
+        self.loaded = False
+        
+    def load(self):
+        if not self.loaded:
+            try:
+                super().__init__(self.dirpath)
+            except Exception as e:
+                print(f"Failed to read the song from {inipath}: {e}")
+            self.loaded = True
+        return self
+
+
 def discover_songs(path: str):
     path = path.rstrip("/")
     try:
@@ -37,13 +52,10 @@ def discover_songs(path: str):
         except Exception:
             continue
 
-        try:
-            s = songinfo.SongInfo(dirpath)
-        except Exception as e:
-            print(f"Failed to read the song from {inipath}: {e}")
-            continue
-        
-        inipath = dirpath + "/diffs.pet"
+        s = LazySong(dirpath)
+        songs.append(s)
+
+        inipath = dirpath + "/.diff.pet"
         try:
             st = os.stat(inipath)
             if not stat.S_ISREG(st[0]):
@@ -51,8 +63,6 @@ def discover_songs(path: str):
         except Exception:
             to_process.append(s)
 
-        songs.append(s)
-
     return songs, to_process
 
 
@@ -81,6 +91,7 @@ class SelectView(BaseView):
         ctx.restore()
         
         if self.processing_now:
+            self.processing_now.load()
             self.processing_now.getDifficulties()
             self.processing_now.saveDifficulties()
             self.processing_now = None
@@ -144,6 +155,7 @@ class SelectView(BaseView):
 
             distance = self._sc.current_position() - idx
             if abs(distance) <= 3:
+                song.load()
                 xpos = 0.0
                 ctx.font_size = 24 - abs(distance) * 3
                 if target and (width := ctx.text_width(song.name)) > 220:
@@ -209,7 +221,8 @@ class SelectView(BaseView):
         if pos > len(self.songs) - 1: pos = len(self.songs) - 1
 
         if pos != cur_target:
-            media.load(self.songs[pos].dirName + "/song.mp3")
+            song = self.songs[pos].load()
+            media.load(song.dirName + "/song.mp3")
 
         if media.get_position() == media.get_duration():
             media.seek(0)
@@ -229,7 +242,8 @@ class SelectView(BaseView):
 
     def play(self):
         if self.songs:
-            media.load(self.songs[self._sc.target_position()].dirName + "/song.mp3")
+            song = self.songs[self._sc.target_position()].load()
+            media.load(song.dirName + "/song.mp3")
         else:
             media.stop()
 
diff --git a/song.py b/song.py
index 3bbea0d28387cc2a60d8017cde6a29199c67b9fb..a5fdb9cb9556b781570f1c7aa4551040fb2a2b8d 100644
--- a/song.py
+++ b/song.py
@@ -66,6 +66,7 @@ class SongView(BaseView):
         self.events_in_margin = set()
         self.petal_events = [set() for i in range(5)]
         self.last_played = 0
+        self.last_time = 0
         
         self.good = 0.0
         self.bad = 0.0
@@ -318,6 +319,7 @@ class SongView(BaseView):
                 media.load(self.song.dirName + '/song.mp3')
 
         if self.song and self.started:
+            # TODO: handle delay specified in song.ini
             self.time = media.get_time() * 1000 + AUDIO_DELAY
 
         if self.input.buttons.app.middle.pressed:
@@ -333,9 +335,13 @@ class SongView(BaseView):
 
         if self.input.buttons.app.right.pressed:
             self.demo_mode = not self.demo_mode
+            if self.demo_mode:
+                media.set_volume(1.0)
 
         if self.paused:
             return
+        
+        delta_time = self.time - self.last_time
 
         self.good = max(0, self.good - delta_ms / self.data.period)
         self.bad = max(0, self.bad - delta_ms / 500)
@@ -345,7 +351,7 @@ class SongView(BaseView):
             self.petal_events[i].clear()
 
         earlyMargin       = 60000.0 / self.data.bpm / 3.5
-        lateMargin        = 60000.0 / self.data.bpm / 3.5 + delta_ms
+        lateMargin        = 60000.0 / self.data.bpm / 3.5 + delta_time
 
         self.notes.clear()
         self.events_in_margin.clear()
@@ -359,7 +365,7 @@ class SongView(BaseView):
             if isinstance(event, midireader.Note):
                 if event.time <= self.time <= event.time + event.length:
                     self.notes.add(event.number)
-                if event.time - earlyMargin <= self.time <= event.time + lateMargin:
+                if self.time - lateMargin <= event.time <= self.time + earlyMargin:
                     self.events_in_margin.add(event)
                     if not event.played:
                         self.petal_events[event.number].add(event)
@@ -369,7 +375,7 @@ class SongView(BaseView):
                     if not self.demo_mode:
                         self.missed[event.number] = 1.0
                         self.miss = 1.0
-                        if event.time >= self.last_played:
+                        if event.time >= self.last_played and not self.demo_mode:
                             media.set_volume(0.25)
                 if event.played and event.time + event.length - lateMargin > self.time:
                     p = 4 if event.number == 0 else event.number - 1
@@ -391,21 +397,28 @@ class SongView(BaseView):
                     self.bad = 1.0
                     self.streak = 0
                 else:
-                    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] = 50
-                    if event.time > self.laststreak:
+                    main_event = min(events, key=lambda x: abs(self.time - x.time))
+                    # mark event as played, and also previous events since the last think
+                    main_event.played = True
+                    for event in events:
+                        if event.played: continue
+                        if event.time <= main_event.time - delta_ms:
+                            event.played = True
+                            if event.time > self.laststreak:
+                                # TODO: correctly handle chords
+                                self.streak += 1
+                    
+                    if main_event.time > self.laststreak:
                         self.streak += 1
                         self.laststreak = event.time
                         if self.debug:
                             print(self.time - event.time)
-                    self.petals[petal] = event
+
+                    self.laststreak = main_event.time
+                    self.led_override[petal] = 50
+                    self.petals[petal] = main_event
                     self.good = 1.0
-                    self.last_played = event.time
+                    self.last_played = main_event.time
                     media.set_volume(1.0)
 
             if not pressed:
@@ -419,6 +432,8 @@ class SongView(BaseView):
         leds.update()
         #gc.collect()
         #print("think", gc.mem_alloc() - mem)
+        
+        self.last_time = self.time
 
     def on_enter(self, vm: Optional[ViewManager]) -> None:
         super().on_enter(vm)
@@ -427,7 +442,7 @@ class SongView(BaseView):
             return
         if self.app:
             media.load(self.app.path + '/sounds/start.mp3')
-            utils.volume(self.app, 10000)
+            utils.volume(self.app, 8000)
         leds.set_slew_rate(238)
             
     def on_enter_done(self):
diff --git a/songinfo.py b/songinfo.py
index 23d174a317dc5f03e9fca850ce8f3b46b46bab25..53f484ab2fa16c3ac448c00366eb8dabe664c412 100644
--- a/songinfo.py
+++ b/songinfo.py
@@ -3,20 +3,41 @@ import os
 import midi
 import midireader
 
-from midireader import difficulties, noteSet
+from midireader import difficulties, noteSet, noteMap
 
 class MidiInfoReader(midi.MidiOutStream):
-  __slots__ = ("notes", )
-    
+  __slots__ = ("difficulties", "nTracks", "ignored", "trackNo")
+      
   # We exit via this exception so that we don't need to read the whole file in
   class Done(Exception): pass
   
   def __init__(self):
     super().__init__()
-    self.notes = set()
+    self.difficulties = set()
+    self.ignored = False
+    self.nTracks = 0
+    self.trackNo = -1
+    
+  def start_of_track(self, track):
+    self.trackNo = track
+
+  def header(self, format, nTracks, division):
+    self.nTracks = nTracks
+
+  def sequence_name(self, val):
+    name = ''.join(list(map(chr, val)))
+    self.ignored = name != "PART GUITAR" and self.nTracks > 2
+    if self.difficulties:
+      raise MidiInfoReader.Done()
+    return self.ignored
 
   def note_on(self, channel, note, velocity):
-    self.notes.add(note)
+    if not self.ignored:
+      if not note in noteMap:
+        return
+      self.difficulties.add(difficulties[noteMap[note][0]])
+      if len(self.difficulties) == len(difficulties):
+        raise MidiInfoReader.Done()
 
 class SongInfo(object):
   def __init__(self, dirName):
@@ -54,7 +75,7 @@ class SongInfo(object):
     if self._difficulties is not None:
       return self._difficulties
 
-    diffFileName = os.path.join(os.path.dirname(self.fileName), "diffs.pet")
+    diffFileName = os.path.join(os.path.dirname(self.fileName), ".diff.pet")
     try:
       with open(diffFileName, "rb") as f:
         self._difficulties = []
@@ -78,17 +99,7 @@ class SongInfo(object):
     except MidiInfoReader.Done:
       pass
     
-    diffset = set()
-    for note in info.notes:
-      if not note in noteSet:
-          continue
-      track, number = midireader.noteMap[note]
-      diff = difficulties[track]
-      if not diff in diffset:
-          diffset.add(diff)
-          if len(diffset) == len(difficulties):
-              break
-    self._difficulties = list(diffset)
+    self._difficulties = list(info.difficulties)
     self._difficulties.sort(key = lambda a: a.id, reverse=True)
     #except Exception as e:
     #  print(e)
@@ -96,10 +107,10 @@ class SongInfo(object):
     return self._difficulties
 
   def saveDifficulties(self):
-    if self._difficulties is None:
+    if not self._difficulties:
       return
     try:
-      diffFileName = os.path.join(os.path.dirname(self.fileName), "diffs.pet")
+      diffFileName = os.path.join(os.path.dirname(self.fileName), ".diff.pet")
       with open(diffFileName, "wb") as f:
         for diff in self._difficulties:
           f.write(bytes([diff.id]))