def testHistogram(self): histo = statistics.Histogram('name_123', [1, 2, 10]) self.assertEqual(histo.counters, {float('-inf'): 0, 1: 0, 2: 0, 10: 0}) histo.increment(1) self.assertEqual(histo.counters, {float('-inf'): 0, 1: 1, 2: 0, 10: 0}) histo.increment(3, 3) self.assertEqual(histo.counters, {float('-inf'): 0, 1: 1, 2: 3, 10: 0}) histo.increment(20) histo.increment(100) self.assertEqual(histo.counters, {float('-inf'): 0, 1: 1, 2: 3, 10: 2}) histo.increment(0) histo.increment(-10) self.assertEqual(histo.counters, {float('-inf'): 2, 1: 1, 2: 3, 10: 2}) histo_2 = statistics.Histogram('name_123', [1, 2, 10]) histo_2.increment(0, 4) histo_2.increment(2, 10) histo_2.increment(10, 1) histo.merge_from(histo_2) self.assertEqual(histo.counters, { float('-inf'): 6, 1: 1, 2: 13, 10: 3 }) histo_3 = statistics.Histogram('name_123', [1, 2, 7]) with self.assertRaisesRegex( statistics.MergeStatisticsError, r'Histogram buckets do not match. ' r'Expected \[-inf, 1, 2, 10\], got \[-inf, 1, 2, 7\]'): histo.merge_from(histo_3) class ABC(object): pass with self.assertRaises(statistics.MergeStatisticsError): histo.merge_from(ABC()) self.assertEqual( str(histo), 'name_123:\n [-inf,1): 6\n [1,2): 1\n [2,10): 13\n [10,inf): 3' ) histo_copy = histo.copy() self.assertEqual(histo_copy.counters, { float('-inf'): 6, 1: 1, 2: 13, 10: 3 }) self.assertEqual(histo_copy.name, 'name_123')
def extract_polyphonic_sequences( quantized_sequence, start_step=0, min_steps_discard=None, max_steps_discard=None): """Extracts a polyphonic track from the given quantized NoteSequence. Currently, this extracts only one polyphonic sequence from a given track. Args: quantized_sequence: A quantized NoteSequence. start_step: Start extracting a sequence at this time step. Assumed to be the beginning of a bar. min_steps_discard: Minimum length of tracks in steps. Shorter tracks are discarded. max_steps_discard: Maximum length of tracks in steps. Longer tracks are discarded. Returns: poly_seqs: A python list of PolyphonicSequence instances. stats: A dictionary mapping string names to `statistics.Statistic` objects. """ sequences_lib.assert_is_relative_quantized_sequence(quantized_sequence) stats = dict((stat_name, statistics.Counter(stat_name)) for stat_name in ['polyphonic_tracks_discarded_too_short', 'polyphonic_tracks_discarded_too_long', 'polyphonic_tracks_discarded_more_than_1_program']) steps_per_bar = sequences_lib.steps_per_bar_in_quantized_sequence( quantized_sequence) # Create a histogram measuring lengths (in bars not steps). stats['polyphonic_track_lengths_in_bars'] = statistics.Histogram( 'polyphonic_track_lengths_in_bars', [0, 1, 10, 20, 30, 40, 50, 100, 200, 500, 1000]) # Allow only 1 program. programs = set() for note in quantized_sequence.notes: programs.add(note.program) if len(programs) > 1: stats['polyphonic_tracks_discarded_more_than_1_program'].increment() return [], stats.values() # Translate the quantized sequence into a PolyphonicSequence. poly_seq = PolyphonicSequence(quantized_sequence, start_step=start_step) poly_seqs = [] num_steps = poly_seq.num_steps if min_steps_discard is not None and num_steps < min_steps_discard: stats['polyphonic_tracks_discarded_too_short'].increment() elif max_steps_discard is not None and num_steps > max_steps_discard: stats['polyphonic_tracks_discarded_too_long'].increment() else: poly_seqs.append(poly_seq) stats['polyphonic_track_lengths_in_bars'].increment( num_steps // steps_per_bar) return poly_seqs, stats.values()
def extract_polyphonic_sequences(quantized_sequence, start_step=0, min_steps_discard=None, max_steps_discard=None, mod_writer=None): mw = mod_writer sequences_lib.assert_is_relative_quantized_sequence(quantized_sequence) stats = dict([(stat_name, statistics.Counter(stat_name)) for stat_name in [ 'polyphonic_tracks_discarded_too_short', 'polyphonic_tracks_discarded_too_long', 'polyphonic_tracks_discarded_more_than_1_program' ]]) steps_per_bar = sequences_lib.steps_per_bar_in_quantized_sequence( quantized_sequence) # Create a histogram measuring lengths (in bars not steps). stats['polyphonic_track_lengths_in_bars'] = statistics.Histogram( 'polyphonic_track_lengths_in_bars', [0, 1, 10, 20, 30, 40, 50, 100, 200, 500, 1000]) # Allow only 1 program. programs = set() for note in quantized_sequence.notes: programs.add(note.program) if len(programs) > 1: stats['polyphonic_tracks_discarded_more_than_1_program'].increment() return [], stats.values() filename = 'quantized_sequence' mw.write(mw.model_dir, filename, quantized_sequence) poly_seq = PolyphonicSequence(quantized_sequence, start_step=start_step, mod_writer=mw) quantized_poly_ns = poly_seq.to_sequence() quantized_poly_ns.filename = quantized_sequence.filename mw.write(mw.model_dir, 'quantized_poly_ns', quantized_poly_ns) poly_seqs = [] num_steps = poly_seq.num_steps if min_steps_discard is not None and num_steps < min_steps_discard: stats['polyphonic_tracks_discarded_too_short'].increment() elif max_steps_discard is not None and num_steps > max_steps_discard: stats['polyphonic_tracks_discarded_too_long'].increment() else: poly_seqs.append(poly_seq) stats['polyphonic_track_lengths_in_bars'].increment(num_steps // steps_per_bar) # pdb.set_trace() return poly_seqs, stats.values()
def extract_melodies(quantized_sequence, search_start_step=0, min_bars=7, max_steps_truncate=None, max_steps_discard=None, gap_bars=1.0, min_unique_pitches=5, ignore_polyphonic_notes=True, pad_end=False, filter_drums=True): """Extracts a list of melodies from the given quantized NoteSequence. This function will search through `quantized_sequence` for monophonic melodies in every track at every time step. Once a note-on event in a track is encountered, a melody begins. Gaps of silence in each track will be splitting points that divide the track into separate melodies. The minimum size of these gaps are given in `gap_bars`. The size of a bar (measure) of music in time steps is computed from the time signature stored in `quantized_sequence`. The melody is then checked for validity. The melody is only used if it is at least `min_bars` bars long, and has at least `min_unique_pitches` unique notes (preventing melodies that only repeat a few notes, such as those found in some accompaniment tracks, from being used). After scanning each instrument track in the quantized sequence, a list of all extracted Melody objects is returned. Args: quantized_sequence: A quantized NoteSequence. search_start_step: Start searching for a melody at this time step. Assumed to be the first step of a bar. min_bars: Minimum length of melodies in number of bars. Shorter melodies are discarded. max_steps_truncate: Maximum number of steps in extracted melodies. If defined, longer melodies are truncated to this threshold. If pad_end is also True, melodies will be truncated to the end of the last bar below this threshold. max_steps_discard: Maximum number of steps in extracted melodies. If defined, longer melodies are discarded. gap_bars: A melody comes to an end when this number of bars (measures) of silence is encountered. min_unique_pitches: Minimum number of unique notes with octave equivalence. Melodies with too few unique notes are discarded. ignore_polyphonic_notes: If True, melodies will be extracted from `quantized_sequence` tracks that contain polyphony (notes start at the same time). If False, tracks with polyphony will be ignored. pad_end: If True, the end of the melody will be padded with NO_EVENTs so that it will end at a bar boundary. filter_drums: If True, notes for which `is_drum` is True will be ignored. Returns: melodies: A python list of Melody instances. stats: A dictionary mapping string names to `statistics.Statistic` objects. Raises: NonIntegerStepsPerBarException: If `quantized_sequence`'s bar length (derived from its time signature) is not an integer number of time steps. """ sequences_lib.assert_is_relative_quantized_sequence(quantized_sequence) # TODO(danabo): Convert `ignore_polyphonic_notes` into a float which controls # the degree of polyphony that is acceptable. melodies = [] stats = dict([(stat_name, statistics.Counter(stat_name)) for stat_name in [ 'polyphonic_tracks_discarded', 'melodies_discarded_too_short', 'melodies_discarded_too_few_pitches', 'melodies_discarded_too_long', 'melodies_truncated' ]]) # Create a histogram measuring melody lengths (in bars not steps). # Capture melodies that are very small, in the range of the filter lower # bound `min_bars`, and large. The bucket intervals grow approximately # exponentially. stats['melody_lengths_in_bars'] = statistics.Histogram( 'melody_lengths_in_bars', [ 0, 1, 10, 20, 30, 40, 50, 100, 200, 500, min_bars // 2, min_bars, min_bars + 1, min_bars - 1 ]) instruments = set([n.instrument for n in quantized_sequence.notes]) steps_per_bar = int( sequences_lib.steps_per_bar_in_quantized_sequence(quantized_sequence)) for instrument in instruments: instrument_search_start_step = search_start_step # Quantize the track into a Melody object. # If any notes start at the same time, only one is kept. while 1: melody = Melody() try: melody.from_quantized_sequence( quantized_sequence, instrument=instrument, search_start_step=instrument_search_start_step, gap_bars=gap_bars, ignore_polyphonic_notes=ignore_polyphonic_notes, pad_end=pad_end, filter_drums=filter_drums) except PolyphonicMelodyException: stats['polyphonic_tracks_discarded'].increment() break # Look for monophonic melodies in other tracks. except events_lib.NonIntegerStepsPerBarException: raise # Start search for next melody on next bar boundary (inclusive). instrument_search_start_step = ( melody.end_step + (search_start_step - melody.end_step) % steps_per_bar) if not melody: break # Require a certain melody length. if len(melody) < melody.steps_per_bar * min_bars: stats['melodies_discarded_too_short'].increment() continue # Discard melodies that are too long. if max_steps_discard is not None and len( melody) > max_steps_discard: stats['melodies_discarded_too_long'].increment() continue # Truncate melodies that are too long. if max_steps_truncate is not None and len( melody) > max_steps_truncate: truncated_length = max_steps_truncate if pad_end: truncated_length -= max_steps_truncate % melody.steps_per_bar melody.set_length(truncated_length) stats['melodies_truncated'].increment() # Require a certain number of unique pitches. note_histogram = melody.get_note_histogram() unique_pitches = np.count_nonzero(note_histogram) if unique_pitches < min_unique_pitches: stats['melodies_discarded_too_few_pitches'].increment() continue # TODO(danabo) # Add filter for rhythmic diversity. stats['melody_lengths_in_bars'].increment( len(melody) // melody.steps_per_bar) melodies.append(melody) return melodies, list(stats.values())
def extract_pianoroll_sequences(quantized_sequence, start_step=0, min_steps_discard=None, max_steps_discard=None, max_steps_truncate=None): """Extracts a polyphonic track from the given quantized NoteSequence. Currently, this extracts only one pianoroll from a given track. Args: quantized_sequence: A quantized NoteSequence. start_step: Start extracting a sequence at this time step. Assumed to be the beginning of a bar. min_steps_discard: Minimum length of tracks in steps. Shorter tracks are discarded. max_steps_discard: Maximum length of tracks in steps. Longer tracks are discarded. Mutually exclusive with `max_steps_truncate`. max_steps_truncate: Maximum length of tracks in steps. Longer tracks are truncated. Mutually exclusive with `max_steps_discard`. Returns: pianoroll_seqs: A python list of PianorollSequence instances. stats: A dictionary mapping string names to `statistics.Statistic` objects. Raises: ValueError: If both `max_steps_discard` and `max_steps_truncate` are specified. """ if (max_steps_discard, max_steps_truncate).count(None) == 0: raise ValueError( 'Only one of `max_steps_discard` and `max_steps_truncate` can be ' 'specified.') sequences_lib.assert_is_relative_quantized_sequence(quantized_sequence) # pylint: disable=g-complex-comprehension stats = dict((stat_name, statistics.Counter(stat_name)) for stat_name in [ 'pianoroll_tracks_truncated_too_long', 'pianoroll_tracks_discarded_too_short', 'pianoroll_tracks_discarded_too_long', 'pianoroll_tracks_discarded_more_than_1_program' ]) # pylint: enable=g-complex-comprehension steps_per_bar = sequences_lib.steps_per_bar_in_quantized_sequence( quantized_sequence) # Create a histogram measuring lengths (in bars not steps). stats['pianoroll_track_lengths_in_bars'] = statistics.Histogram( 'pianoroll_track_lengths_in_bars', [0, 1, 10, 20, 30, 40, 50, 100, 200, 500, 1000]) # Allow only 1 program. programs = set() for note in quantized_sequence.notes: programs.add(note.program) if len(programs) > 1: stats['pianoroll_tracks_discarded_more_than_1_program'].increment() return [], list(stats.values()) # Translate the quantized sequence into a PianorollSequence. pianoroll_seq = PianorollSequence(quantized_sequence=quantized_sequence, start_step=start_step) pianoroll_seqs = [] num_steps = pianoroll_seq.num_steps if min_steps_discard is not None and num_steps < min_steps_discard: stats['pianoroll_tracks_discarded_too_short'].increment() elif max_steps_discard is not None and num_steps > max_steps_discard: stats['pianoroll_tracks_discarded_too_long'].increment() else: if max_steps_truncate is not None and num_steps > max_steps_truncate: stats['pianoroll_tracks_truncated_too_long'].increment() pianoroll_seq.set_length(max_steps_truncate) pianoroll_seqs.append(pianoroll_seq) stats['pianoroll_track_lengths_in_bars'].increment(num_steps // steps_per_bar) return pianoroll_seqs, list(stats.values())
def extract_drum_tracks(quantized_sequence, search_start_step=0, min_bars=7, max_steps_truncate=None, max_steps_discard=None, gap_bars=1.0, pad_end=False, ignore_is_drum=False): """Extracts a list of drum tracks from the given quantized NoteSequence. This function will search through `quantized_sequence` for drum tracks. A drum track can span multiple "tracks" in the sequence. Only one drum track can be active at a given time, but multiple drum tracks can be extracted from the sequence if gaps are present. Once a note-on drum event is encountered, a drum track begins. Gaps of silence will be splitting points that divide the sequence into separate drum tracks. The minimum size of these gaps are given in `gap_bars`. The size of a bar (measure) of music in time steps is computed form the time signature stored in `quantized_sequence`. A drum track is only used if it is at least `min_bars` bars long. After scanning the quantized NoteSequence, a list of all extracted DrumTrack objects is returned. Args: quantized_sequence: A quantized NoteSequence. search_start_step: Start searching for drums at this time step. Assumed to be the beginning of a bar. min_bars: Minimum length of drum tracks in number of bars. Shorter drum tracks are discarded. max_steps_truncate: Maximum number of steps in extracted drum tracks. If defined, longer drum tracks are truncated to this threshold. If pad_end is also True, drum tracks will be truncated to the end of the last bar below this threshold. max_steps_discard: Maximum number of steps in extracted drum tracks. If defined, longer drum tracks are discarded. gap_bars: A drum track comes to an end when this number of bars (measures) of no drums is encountered. pad_end: If True, the end of the drum track will be padded with empty events so that it will end at a bar boundary. ignore_is_drum: Whether accept notes where `is_drum` is False. Returns: drum_tracks: A python list of DrumTrack instances. stats: A dictionary mapping string names to `statistics.Statistic` objects. Raises: NonIntegerStepsPerBarException: If `quantized_sequence`'s bar length (derived from its time signature) is not an integer number of time steps. """ drum_tracks = [] stats = dict([(stat_name, statistics.Counter(stat_name)) for stat_name in [ 'drum_tracks_discarded_too_short', 'drum_tracks_discarded_too_long', 'drum_tracks_truncated' ]]) # Create a histogram measuring drum track lengths (in bars not steps). # Capture drum tracks that are very small, in the range of the filter lower # bound `min_bars`, and large. The bucket intervals grow approximately # exponentially. stats['drum_track_lengths_in_bars'] = statistics.Histogram( 'drum_track_lengths_in_bars', [ 0, 1, 10, 20, 30, 40, 50, 100, 200, 500, min_bars // 2, min_bars, min_bars + 1, min_bars - 1 ]) steps_per_bar = int( sequences_lib.steps_per_bar_in_quantized_sequence(quantized_sequence)) # Quantize the track into a DrumTrack object. # If any notes start at the same time, only one is kept. while 1: drum_track = DrumTrack() try: drum_track.from_quantized_sequence( quantized_sequence, search_start_step=search_start_step, gap_bars=gap_bars, pad_end=pad_end, ignore_is_drum=ignore_is_drum) except events_lib.NonIntegerStepsPerBarException: raise search_start_step = ( drum_track.end_step + (search_start_step - drum_track.end_step) % steps_per_bar) if not drum_track: break # Require a certain drum track length. if len(drum_track) < drum_track.steps_per_bar * min_bars: stats['drum_tracks_discarded_too_short'].increment() continue # Discard drum tracks that are too long. if max_steps_discard is not None and len( drum_track) > max_steps_discard: stats['drum_tracks_discarded_too_long'].increment() continue # Truncate drum tracks that are too long. if max_steps_truncate is not None and len( drum_track) > max_steps_truncate: truncated_length = max_steps_truncate if pad_end: truncated_length -= max_steps_truncate % drum_track.steps_per_bar drum_track.set_length(truncated_length) stats['drum_tracks_truncated'].increment() stats['drum_track_lengths_in_bars'].increment( len(drum_track) // drum_track.steps_per_bar) drum_tracks.append(drum_track) return drum_tracks, stats.values()
def extract_performances( quantized_sequence, start_step=0, min_events_discard=None, max_events_truncate=None, max_steps_truncate=None, num_velocity_bins=0, split_instruments=False, note_performance=False): """Extracts one or more performances from the given quantized NoteSequence. Args: quantized_sequence: A quantized NoteSequence. start_step: Start extracting a sequence at this time step. min_events_discard: Minimum length of tracks in events. Shorter tracks are discarded. max_events_truncate: Maximum length of tracks in events. Longer tracks are truncated. max_steps_truncate: Maximum length of tracks in quantized time steps. Longer tracks are truncated. num_velocity_bins: Number of velocity bins to use. If 0, velocity events will not be included at all. split_instruments: If True, will extract a performance for each instrument. Otherwise, will extract a single performance. note_performance: If True, will create a NotePerformance object. If False, will create either a MetricPerformance or Performance based on how the sequence was quantized. Returns: performances: A python list of Performance or MetricPerformance (if `quantized_sequence` is quantized relative to meter) instances. stats: A dictionary mapping string names to `statistics.Statistic` objects. """ sequences_lib.assert_is_quantized_sequence(quantized_sequence) stats = dict([(stat_name, statistics.Counter(stat_name)) for stat_name in ['performances_discarded_too_short', 'performances_truncated', 'performances_truncated_timewise', 'performances_discarded_more_than_1_program', 'performance_discarded_too_many_time_shift_steps', 'performance_discarded_too_many_duration_steps']]) if sequences_lib.is_absolute_quantized_sequence(quantized_sequence): steps_per_second = quantized_sequence.quantization_info.steps_per_second # Create a histogram measuring lengths in seconds. stats['performance_lengths_in_seconds'] = statistics.Histogram( 'performance_lengths_in_seconds', [5, 10, 20, 30, 40, 60, 120]) else: steps_per_bar = sequences_lib.steps_per_bar_in_quantized_sequence( quantized_sequence) # Create a histogram measuring lengths in bars. stats['performance_lengths_in_bars'] = statistics.Histogram( 'performance_lengths_in_bars', [1, 10, 20, 30, 40, 50, 100, 200, 500]) if split_instruments: instruments = set(note.instrument for note in quantized_sequence.notes) else: instruments = set([None]) # Allow only 1 program. programs = set() for note in quantized_sequence.notes: programs.add(note.program) if len(programs) > 1: stats['performances_discarded_more_than_1_program'].increment() return [], stats.values() performances = [] for instrument in instruments: # Translate the quantized sequence into a Performance. if note_performance: try: performance = NotePerformance( quantized_sequence, start_step=start_step, num_velocity_bins=num_velocity_bins, instrument=instrument) except NotePerformanceTooManyTimeShiftSteps: stats['performance_discarded_too_many_time_shift_steps'].increment() continue except NotePerformanceTooManyDurationSteps: stats['performance_discarded_too_many_duration_steps'].increment() continue elif sequences_lib.is_absolute_quantized_sequence(quantized_sequence): performance = Performance(quantized_sequence, start_step=start_step, num_velocity_bins=num_velocity_bins, instrument=instrument) else: performance = MetricPerformance(quantized_sequence, start_step=start_step, num_velocity_bins=num_velocity_bins, instrument=instrument) if (max_steps_truncate is not None and performance.num_steps > max_steps_truncate): performance.set_length(max_steps_truncate) stats['performances_truncated_timewise'].increment() if (max_events_truncate is not None and len(performance) > max_events_truncate): performance.truncate(max_events_truncate) stats['performances_truncated'].increment() if min_events_discard is not None and len(performance) < min_events_discard: stats['performances_discarded_too_short'].increment() else: performances.append(performance) if sequences_lib.is_absolute_quantized_sequence(quantized_sequence): stats['performance_lengths_in_seconds'].increment( performance.num_steps // steps_per_second) else: stats['performance_lengths_in_bars'].increment( performance.num_steps // steps_per_bar) return performances, stats.values()
def extract_melodies(quantized_sequence, min_bars=7, gap_bars=1.0, min_unique_pitches=5, ignore_polyphonic_notes=True): """Extracts a list of melodies from the given QuantizedSequence object. This function will search through `quantized_sequence` for monophonic melodies in every track at every time step. Once a note-on event in a track is encountered, a melody begins. Gaps of silence in each track will be splitting points that divide the track into separate melodies. The minimum size of these gaps are given in `gap_bars`. The size of a bar (measure) of music in time steps is computed from the time signature stored in `quantized_sequence`. The melody is then checked for validity. The melody is only used if it is at least `min_bars` bars long, and has at least `min_unique_pitches` unique notes (preventing melodies that only repeat a few notes, such as those found in some accompaniment tracks, from being used). After scanning each instrument track in the NoteSequence, a list of all the valid melodies is returned. Args: quantized_sequence: A sequences_lib.QuantizedSequence object. min_bars: Minimum length of melodies in number of bars. Shorter melodies are discarded. gap_bars: A melody comes to an end when this number of bars (measures) of silence is encountered. min_unique_pitches: Minimum number of unique notes with octave equivalence. Melodies with too few unique notes are discarded. ignore_polyphonic_notes: If True, melodies will be extracted from `quantized_sequence` tracks that contain polyphony (notes start at the same time). If False, tracks with polyphony will be ignored. Returns: melodies: A python list of MonophonicMelody instances. stats: A dictionary mapping string names to `statistics.Statistic` objects. """ # TODO(danabo): Convert `ignore_polyphonic_notes` into a float which controls # the degree of polyphony that is acceptable. melodies = [] stats = dict([(stat_name, statistics.Counter(stat_name)) for stat_name in ['polyphonic_tracks_discarded', 'melodies_discarded_too_short', 'melodies_discarded_too_few_pitches']]) # Create a histogram measuring melody lengths (in bars not steps). # Capture melodies that are very small, in the range of the filter lower # bound `min_bars`, and large. The bucket intervals grow approximately # exponentially. stats['melody_lengths_in_bars'] = statistics.Histogram( 'melody_lengths_in_bars', [0, 1, 10, 20, 30, 40, 50, 100, 200, 500, min_bars // 2, min_bars, min_bars + 1, min_bars - 1]) for track in quantized_sequence.tracks: start = 0 # Quantize the track into a MonophonicMelody object. # If any notes start at the same time, only one is kept. while 1: melody = MonophonicMelody() try: melody.from_quantized_sequence( quantized_sequence, track=track, start_step=start, gap_bars=gap_bars, ignore_polyphonic_notes=ignore_polyphonic_notes) except PolyphonicMelodyException: stats['polyphonic_tracks_discarded'].increment() break # Look for monophonic melodies in other tracks. start = melody.end_step if not melody: break # Require a certain melody length. stats['melody_lengths_in_bars'].increment( len(melody) // melody.steps_per_bar) if len(melody) - 1 < melody.steps_per_bar * min_bars: stats['melodies_discarded_too_short'].increment() continue # Require a certain number of unique pitches. note_histogram = melody.get_note_histogram() unique_pitches = np.count_nonzero(note_histogram) if unique_pitches < min_unique_pitches: stats['melodies_discarded_too_few_pitches'].increment() continue # TODO(danabo) # Add filter for rhythmic diversity. melodies.append(melody) return melodies, stats.values()
def extract_performances(quantized_sequence, start_step=0, min_events_discard=None, max_events_truncate=None, num_velocity_bins=0): """Extracts a performance from the given quantized NoteSequence. Currently, this extracts only one performance from a given track. Args: quantized_sequence: A quantized NoteSequence. start_step: Start extracting a sequence at this time step. min_events_discard: Minimum length of tracks in events. Shorter tracks are discarded. max_events_truncate: Maximum length of tracks in events. Longer tracks are truncated. num_velocity_bins: Number of velocity bins to use. If 0, velocity events will not be included at all. Returns: performances: A python list of Performance instances. stats: A dictionary mapping string names to `statistics.Statistic` objects. """ sequences_lib.assert_is_absolute_quantized_sequence(quantized_sequence) stats = dict([(stat_name, statistics.Counter(stat_name)) for stat_name in [ 'performances_discarded_too_short', 'performances_truncated', 'performances_discarded_more_than_1_program' ]]) steps_per_second = quantized_sequence.quantization_info.steps_per_second # Create a histogram measuring lengths (in bars not steps). stats['performance_lengths_in_seconds'] = statistics.Histogram( 'performance_lengths_in_seconds', [5, 10, 20, 30, 40, 60, 120]) # Allow only 1 program. programs = set() for note in quantized_sequence.notes: programs.add(note.program) if len(programs) > 1: stats['performances_discarded_more_than_1_program'].increment() return [], stats.values() performances = [] # Translate the quantized sequence into a Performance. performance = Performance(quantized_sequence, start_step=start_step, num_velocity_bins=num_velocity_bins) if (max_events_truncate is not None and len(performance) > max_events_truncate): performance.truncate(max_events_truncate) stats['performances_truncated'].increment() if min_events_discard is not None and len( performance) < min_events_discard: stats['performances_discarded_too_short'].increment() else: performances.append(performance) stats['performance_lengths_in_seconds'].increment( performance.num_steps // steps_per_second) return performances, stats.values()