From 54badcf9b17cd399b8e3a7d7fc7fcf377c1ffedb Mon Sep 17 00:00:00 2001 From: Byron Mallett Date: Mon, 4 Jan 2016 23:29:29 +1300 Subject: [PATCH] Added message for tracking playing notes in midi tracks. --- .../ShowtimeBridge/LiveWrappers/LiveSong.py | 9 ++++ .../ShowtimeBridge/LiveWrappers/LiveTrack.py | 53 +++++++++++++++++++ .../ShowtimeBridge/ShowtimeBridge.py | 3 ++ 3 files changed, 65 insertions(+) diff --git a/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/LiveWrappers/LiveSong.py b/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/LiveWrappers/LiveSong.py index 76110ce..317290e 100644 --- a/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/LiveWrappers/LiveSong.py +++ b/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/LiveWrappers/LiveSong.py @@ -48,9 +48,11 @@ def register_methods(cls): # Outgoing # -------- def song_time_updated(self): + self.tick() meterLevels = {} for track in LiveTrack.instances(): if track: + # Batch track meters into one message if track.handle(): if track.handle().has_midi_output: Utils.truncate_float(track.handle().output_meter_level, 4) @@ -96,3 +98,10 @@ def update_hierarchy(self): Log.info("%s - Track list changed" % self.id()) tracks = list(itertools.chain(self.handle().tracks, self.handle().return_tracks)) LiveWrapper.update_hierarchy(self, LiveTrack, tracks) + + # --------- + # Utilities + # --------- + def tick(self): + for track in LiveTrack.instances(): + track.tick() \ No newline at end of file diff --git a/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/LiveWrappers/LiveTrack.py b/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/LiveWrappers/LiveTrack.py index d26b8c3..db6caea 100644 --- a/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/LiveWrappers/LiveTrack.py +++ b/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/LiveWrappers/LiveTrack.py @@ -1,4 +1,5 @@ from LiveClipslot import LiveClipslot +from LiveClip import LiveClip from LiveDevice import LiveDevice from LiveSend import LiveSend from LiveWrapper import * @@ -10,6 +11,9 @@ class LiveTrack(LiveWrapper): TRACK_METER = "track_meter" TRACK_STOP = "track_stop" TRACK_MIXER_SENDS_UPDATED = "track_sends_updated" + TRACK_PLAYING_NOTES = "track_playing_notes" + NOTE_ON = "on" + NOTE_OFF = "off" # ------------------- # Wrapper definitions @@ -42,6 +46,7 @@ def destroy_listeners(self): def register_methods(cls): cls.add_outgoing_method(LiveTrack.TRACK_METER) cls.add_outgoing_method(LiveTrack.TRACK_MIXER_SENDS_UPDATED) + cls.add_outgoing_method(LiveTrack.TRACK_PLAYING_NOTES) cls.add_incoming_method(LiveTrack.TRACK_STOP, ["id"], LiveTrack.stop_track) def to_object(self): @@ -98,3 +103,51 @@ def update_clips(self): def update_sends(self): Log.info("%s - Sends changed" % self.id()) LiveWrapper.update_hierarchy(self, LiveSend, self.handle().mixer_device.sends) + + def tick(self): + # Find current playing notes for midi clips on this track + if self.handle().has_midi_input and self.handle().playing_slot_index > -1: + # How far to look ahead to reduce perceived latency + offset = 0.0 + + cliphandle = self.handle().clip_slots[self.handle().playing_slot_index].clip + playposition = cliphandle.playing_position + offset + if not hasattr(self, "lastplaypos") or self.lastplaypos > playposition: + self.lastplaypos = 0.0 + + # Elapsed time between last tick and now for this clip + delta = playposition - self.lastplaypos + + # Get slice of notes from the clip based on current play time + # notes = cliphandle.get_notes(playposition - delta, 0, 0.2, 127) + notes = cliphandle.get_notes(self.lastplaypos, 0, delta, 127) + self.lastplaypos = playposition + + if len(notes) > 0: + changednotes = [] + + if not hasattr(self, "playingnotes"): + self.playingnotes = set() + + # Determine stopped notes + Log.info(self.playingnotes) + stoppednotes = [] + for note in self.playingnotes: + if note[1] + note[2] < playposition or note[1] < playposition: + stoppednotes.append(note) + # changednotes.append({"status": LiveTrack.NOTE_OFF, "note": note}) + + # Remove stopped notes outside of set + for note in stoppednotes: + self.playingnotes.remove(note) + + # Determine new notes not already playing + for note in notes: + if note not in self.playingnotes: + self.playingnotes.add(note) + changednotes.append({"status": LiveTrack.NOTE_ON, "note": note}) + + # Send note diff to showtime + if len(changednotes) > 0: + self.update(LiveTrack.TRACK_PLAYING_NOTES, changednotes) + diff --git a/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/ShowtimeBridge.py b/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/ShowtimeBridge.py index 209c45e..fb56a96 100644 --- a/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/ShowtimeBridge.py +++ b/Showtime_Live/Midi_Remote_Scripts/ShowtimeBridge/ShowtimeBridge.py @@ -5,6 +5,7 @@ from _Framework.ControlSurface import ControlSurface from ControlSurfaceComponents.LoopingEncoderElement import LoopingEncoderElement from LiveWrappers.LiveWrapper import LiveWrapper +from LiveWrappers.LiveSong import LiveSong from LiveNetworkEndpoint import LiveNetworkEndpoint from Logger import Log @@ -66,4 +67,6 @@ def update_display(self): def request_loop(self): self.endpoint.poll() + if len(LiveSong.instances()) > 0: + LiveSong.instances()[0].tick() LiveWrapper.process_deferred_actions()