def play(self): from midi.slice import EventStreamSlice from jazzparser.utils.midi import play_stream slc = EventStreamSlice(self.midi.midi_stream, self.start, self.end) strm = slc.to_event_stream() return play_stream(strm)
def _play(mid, start, end): """ Play the selection """ if start is None: start = 0 # Trim the midi trimmed = EventStreamSlice(mid, start, end).to_event_stream(repeat_playing=False) # Show info about the clip print "Start tick: %s" % start print "End tick: %s" % end print "First event tick: %s" % _ticks_to_ticks(mid, start) print "Last event tick: %s" % _ticks_to_ticks(mid, end, before=True) start_time = float(_get_time(mid, start)) / 1000.0 print "Start time: %ss" % start_time print "Last event time: %ss" % (float(_get_time(mid, end, before=True)) / 1000.0) print print "Playing MIDI. Hit ctrl+C to stop" # Record playing time timer = ExecutionTimer() try: play_stream(trimmed, block=True) except KeyboardInterrupt: length = timer.get_time() print "\nPlayed for %.2f seconds (stopped ~%.2fs)" % ( length, start_time + length)
def get_slices(self): """ Get a list of L{midi.slice.EventStreamSlice}s corresponding to the chunks that this midi stream will be divided into. This includes all midi events, not just note-ons. """ from midi.slice import EventStreamSlice tick_unit = int(self.stream.resolution * self.time_unit) if len(self.stream.trackpool) == 0: end_time = 0 else: end_time = max(self.stream.trackpool).tick slices = [ EventStreamSlice(self.stream, chunk_start, chunk_start + tick_unit - 1) for chunk_start in range(self.tick_offset, end_time, tick_unit) ] return slices
def from_stream(stream, time_unit=4, tick_offset=0, name=None, only_notes=True, truncate=None, gold=None, sequence_index=None): """ Creates a L{SegmentedMidiInput} from a midi event stream. @type only_notes: bool @param only_notes: if True, only includes note-on/note-off events in the segments. If False, the stream will be sliced so that each segment repeats things like program change events at the beginning. Including only notes, however, makes the preprocessing very much faster """ # Divide the stream up into slices of the right size # Number of ticks in each slice tick_unit = int(stream.resolution * time_unit) if len(stream.trackpool) == 0: end_time = 0 else: end_time = max(stream.trackpool).tick if only_notes: from midi import EventStream, NoteOnEvent, NoteOffEvent, EndOfTrackEvent # Only include notes in the stream # This is much simpler and faster than the alternative events = [ev for ev in list(sorted(stream.trackpool)) if \ type(ev) in [NoteOnEvent, NoteOffEvent]] events = iter(events) try: current_event = events.next() # Get up to the start point in the stream while current_event.tick < tick_offset: current_event = events.next() except StopIteration: # Got to the end of the stream before we even started inputs = [] else: inputs = [] for chunk_start in range(tick_offset, end_time, tick_unit): chunk_end = chunk_start + tick_unit slc = EventStream() slc.add_track() slc.format = stream.format slc.resolution = stream.resolution slc.segment_start = chunk_start # Add all the note events in this time period try: while current_event.tick < chunk_end: slc.add_event(current_event) current_event = events.next() # Add the end of track event eot = EndOfTrackEvent() eot.tick = chunk_end slc.add_event(eot) except StopIteration: # Reached the end of the stream inputs.append(slc) break inputs.append(slc) else: # Use slices to do all the necessary repetition of ongoing events from midi.slice import EventStreamSlice start_times = range(tick_offset, end_time, tick_unit) # First slice starts at the offset value slices = [ EventStreamSlice(stream, chunk_start, chunk_start + tick_unit) for chunk_start in start_times ] inputs = [slc.to_event_stream(repeat_playing=False, cancel_playing=False) \ for slc in slices] # Associate the start time with each segment for slc, start_time in zip(inputs, start_times): slc.segment_start = start_time # Remove empty segments from the start and end current = 0 # There's always one event - the end of track while len(inputs[current].trackpool) < 2: current += 1 inputs = inputs[current:] # And the end current = len(inputs) - 1 while len(inputs[current].trackpool) < 2: current -= 1 inputs = inputs[:current + 1] if truncate is not None: inputs = inputs[:truncate] return SegmentedMidiInput(inputs, time_unit=time_unit, tick_offset=tick_offset, name=name, stream=stream, gold=gold, sequence_index=sequence_index)
def main(): usage = "%prog [options] <midi-input>" description = "Trims a MIDI file to the required start and end points. "\ "By default, plays the trimmed MIDI (for testing) and can also write "\ "it out to a file." parser = OptionParser(usage=usage, description=description) parser.add_option( "-s", "--start", dest="start", action="store", help="start point, in ticks as 'x', or in seconds as 'xs'") parser.add_option("-e", "--end", dest="end", action="store", help="end point (formatted as -s)") parser.add_option( "-o", "--output", dest="output", action="store", help= "MIDI file to output to. If given, output is stored instead of being played" ) options, arguments = parser.parse_args() if len(arguments) == 0: print >> sys.stderr, "You must specify a MIDI file" sys.exit(1) mid = read_midifile(arguments[0]) def _time_to_ticks(time, before=False): # Find the tick time of the first event after the given time # or the last event before it mstime = int(time * 1000) if time is not None: previous = min(mid.trackpool) for ev in sorted(mid.trackpool): # Look for the first event after the time if ev.msdelay >= mstime: if before: # Return the previous event's tick return previous.tick else: # Return this event's tick return ev.tick previous = ev return max(mid.trackpool).tick def _ticks_to_ticks(ticks, before=False): # Find the tick time of the first event after the given time # or the last event before it if ticks is not None: previous = min(mid.trackpool) for ev in sorted(mid.trackpool): # Look for the first event after the time if ev.tick >= ticks: if before: # Return the previous event's tick return previous.tick else: # Return this event's tick return ev.tick previous = ev return max(mid.trackpool).tick def _get_time(ticks, before=False): # Find the event time of the first event after the given tick time # or the last event before it previous = min(mid.trackpool) if ticks is not None: for ev in sorted(mid.trackpool): # Look for the first event after the time in ticks if ev.tick >= ticks: if before: # Return the previous event's time return previous.msdelay else: # Return this event's time return ev.msdelay previous = ev return max(mid.trackpool).msdelay def _parse_time(val, before=False): if val.endswith("s"): # Value in seconds # Convert to ticks return _time_to_ticks(float(val[:-1]), before=before) else: return int(val) # Work out start and end points if options.start is not None: start = _parse_time(options.start, before=False) else: start = 0 if options.end is not None: end = _parse_time(options.end, before=True) else: end = None if end is not None and start > end: print "Start time of %d ticks > end time of %d ticks" % (start, end) sys.exit(1) # Cut the stream to the desired start and end slc = EventStreamSlice(mid, start, end) trimmed_mid = slc.to_event_stream(repeat_playing=False) # Print out some info print "Start tick: %s" % start print "End tick: %s" % end print print "First event tick: %s" % _ticks_to_ticks(start) print "Last event tick: %s" % _ticks_to_ticks(end, before=True) print print "Start time: %ss" % (float(_get_time(start)) / 1000.0) print "Last event time: %ss" % (float(_get_time(end, before=True)) / 1000.0) print print "%d events" % len(trimmed_mid.trackpool) # Record playing time timer = ExecutionTimer() if options.output is None: # Play the output by default try: play_stream(trimmed_mid, block=True) except KeyboardInterrupt: print "\nPlayed for %.2f seconds" % timer.get_time() else: # Output to a file outfile = os.path.abspath(options.output) write_midifile(trimmed_mid, outfile) print "Output written to %s" % outfile
def main(): usage = "%prog [options] <midi-input>" description = ( "Trims a MIDI file to the required start and end points. " "By default, plays the trimmed MIDI (for testing) and can also write " "it out to a file." ) parser = OptionParser(usage=usage, description=description) parser.add_option( "-s", "--start", dest="start", action="store", help="start point, in ticks as 'x', or in seconds as 'xs'" ) parser.add_option("-e", "--end", dest="end", action="store", help="end point (formatted as -s)") parser.add_option( "-o", "--output", dest="output", action="store", help="MIDI file to output to. If given, output is stored instead of being played", ) options, arguments = parser.parse_args() if len(arguments) == 0: print >> sys.stderr, "You must specify a MIDI file" sys.exit(1) mid = read_midifile(arguments[0]) def _time_to_ticks(time, before=False): # Find the tick time of the first event after the given time # or the last event before it mstime = int(time * 1000) if time is not None: previous = min(mid.trackpool) for ev in sorted(mid.trackpool): # Look for the first event after the time if ev.msdelay >= mstime: if before: # Return the previous event's tick return previous.tick else: # Return this event's tick return ev.tick previous = ev return max(mid.trackpool).tick def _ticks_to_ticks(ticks, before=False): # Find the tick time of the first event after the given time # or the last event before it if ticks is not None: previous = min(mid.trackpool) for ev in sorted(mid.trackpool): # Look for the first event after the time if ev.tick >= ticks: if before: # Return the previous event's tick return previous.tick else: # Return this event's tick return ev.tick previous = ev return max(mid.trackpool).tick def _get_time(ticks, before=False): # Find the event time of the first event after the given tick time # or the last event before it previous = min(mid.trackpool) if ticks is not None: for ev in sorted(mid.trackpool): # Look for the first event after the time in ticks if ev.tick >= ticks: if before: # Return the previous event's time return previous.msdelay else: # Return this event's time return ev.msdelay previous = ev return max(mid.trackpool).msdelay def _parse_time(val, before=False): if val.endswith("s"): # Value in seconds # Convert to ticks return _time_to_ticks(float(val[:-1]), before=before) else: return int(val) # Work out start and end points if options.start is not None: start = _parse_time(options.start, before=False) else: start = 0 if options.end is not None: end = _parse_time(options.end, before=True) else: end = None if end is not None and start > end: print "Start time of %d ticks > end time of %d ticks" % (start, end) sys.exit(1) # Cut the stream to the desired start and end slc = EventStreamSlice(mid, start, end) trimmed_mid = slc.to_event_stream(repeat_playing=False) # Print out some info print "Start tick: %s" % start print "End tick: %s" % end print print "First event tick: %s" % _ticks_to_ticks(start) print "Last event tick: %s" % _ticks_to_ticks(end, before=True) print print "Start time: %ss" % (float(_get_time(start)) / 1000.0) print "Last event time: %ss" % (float(_get_time(end, before=True)) / 1000.0) print print "%d events" % len(trimmed_mid.trackpool) # Record playing time timer = ExecutionTimer() if options.output is None: # Play the output by default try: play_stream(trimmed_mid, block=True) except KeyboardInterrupt: print "\nPlayed for %.2f seconds" % timer.get_time() else: # Output to a file outfile = os.path.abspath(options.output) write_midifile(trimmed_mid, outfile) print "Output written to %s" % outfile
def main(): usage = "%prog [options] <midi-input1> [<midi-input2> ...]" description = "Interactive routine for cutting MIDI files. May take "\ "multiple MIDI files as input" parser = OptionParser(usage=usage, description=description) parser.add_option( "-o", "--output", dest="output_dir", action="store", help= "directory to send MIDI output to. If not given, they will be sent to a subdirectory 'cut' of that containing the first input" ) parser.add_option( "--fragment", dest="fragment", action="store", type="float", help= "length in seconds of fragment to play when asked to play a beginning or ending. Default: 3secs", default=3.0) parser.add_option( "--overwrite", dest="overwrite", action="store_true", help= "by default, we skip processing any files where there's a file with the same name in the output directory. This forces us to overwrite them" ) parser.add_option( "--ignore", dest="ignore", action="store", help= "file containing a list of filenames (not paths), one per line: any input files matching these names will be ignored and inputs marked as 'ignore' will be added to the list" ) parser.add_option( "--segfile", dest="segfile", action="store", help= "output a list of the MIDI files that get written by this script (just the base filename) in the format of segmidi input lists. The list will contain a basic set of default segmentation parameters. Use play_bulk_chunks to validate these. If the file exists, it will be appended" ) options, arguments = parser.parse_args() fragment = options.fragment if len(arguments) == 0: print >> sys.stderr, "You must specify at least one MIDI file" sys.exit(1) # Read in all the MIDI inputs filenames = arguments print "Processing %d inputs" % len(filenames) if options.ignore: if os.path.exists(options.ignore): # Existing list # Open the file to read in the current list and add to it ignore_file = open(options.ignore, 'r+a') ignore_list = [ filename.strip("\n") for filename in ignore_file.readlines() ] print "Loaded ignore list from %s" % options.ignore else: # No existing list # Open the file so we can write new entries ignore_file = open(options.ignore, 'w') ignore_list = [] print "Created new ignore list in %s" % options.ignore else: ignore_file = None ignore_list = [] if options.segfile: # Open the file for writing segmidi parameters to segfile = open(options.segfile, 'a') segcsv = csv.writer(segfile) else: segfile = None try: # Set up the output directory if options.output_dir: output_dir = options.output_dir else: # Get the directory of the first input file output_dir = os.path.join(os.path.dirname(filenames[0]), "cut") check_directory(output_dir, is_dir=True) print "Outputing MIDI files to %s" % output_dir print for filename in filenames: basename = os.path.basename(filename) # Ignore any files in the ignore list if basename in ignore_list: print "Skipping input %s, as it's in the ignore list" % basename continue out_filename = os.path.join(output_dir, os.path.basename(filename)) # Check whether the output file already exists if os.path.exists(out_filename): if options.overwrite: # Just warn print "WARNING: writing out this input will overwrite an existing file" else: # Don't continue with this input print "Skipping input %s, since output file already exists" % filename continue start = 0 end = None print "\n####################################" print "Processing input: %s" % filename # Read in the midi file try: mid = read_midifile(filename) except Exception, err: print "Error reading in midi file %s: %s" % (filename, err) continue print "Output will be written to: %s" % out_filename # Start by playing the whole thing _play(mid, start, end) try: while True: # Print the header information print "\n>>>>>>>>>>>>>>>>>>>>>>" if end is None: end_str = "open" else: end_str = "%d ticks" % end print "Start: %d ticks. End: %s" % (start, end_str) print ">>>>>>>>>>>>>>>>>>>>>>" print "Set start time (s); set end time (e)" print "Play all (p); play beginning ([); play end (], optional length)" print "Write out and proceed (w); add to ignore list (i); skip to next (n); exit (x)" # Get a command from the user try: command = raw_input(">> ") except KeyboardInterrupt: # I quite often send an interrupt by accident, meaning # to stop the playback, but just after it's stopped # itself print "Ignored keyboard interrupt" continue command = command.strip() if command.lower() == "p": # Play within the selection again _play(mid, start, end) elif command.lower() == "n": break elif command.lower() == "i": # Add the filename to the ignore list if ignore_file: ignore_file.write("%s\n" % os.path.basename(filename)) else: print "No ignore file loaded: could not add this file to the list" break elif command.lower() == "x": sys.exit(0) elif command.lower().startswith("s"): time = command[1:].strip() if len(time) == 0: print "Specify a start tick (T) or time (Ts)" continue start = _parse_time(mid, time) elif command.lower().startswith("e"): time = command[1:].strip() if len(time) == 0: print "Specify an end tick (T) or time (Ts)" continue end = _parse_time(mid, time, before=True) elif command == "[": # Play the opening few seconds start_secs = _get_time(mid, start) / 1000.0 frag_end = _time_to_ticks(mid, fragment + start_secs) _play(mid, start, frag_end) elif command.startswith("]"): length = command[1:].strip() if len(length): frag_length = float(length) else: frag_length = fragment # Play the last few seconds end_secs = _get_time(mid, end) / 1000.0 frag_start = _time_to_ticks( mid, max(0.0, end_secs - frag_length), before=True) _play(mid, frag_start, end) elif command == "w": # Write the file out if start is None: start = 0 # Trim the midi trimmed = EventStreamSlice( mid, start, end).to_event_stream(repeat_playing=False) # Write it out write_midifile(trimmed, out_filename) if segfile is not None: # Also output a row to the segmidi index SegmentedMidiBulkInput.writeln(segcsv, basename) print "Output written to %s" % out_filename # Continue to the next input break else: print "Unknown command: %s" % command continue except EOFError: # User hit ctrl+D: continue to next input print "Continuing to next input..." continue else: