def sequence_to_midi(self, sequence, bpm: int = 80) -> pretty_midi.PrettyMIDI: pm = pretty_midi.PrettyMIDI(initial_tempo=bpm) instrument = pretty_midi.Instrument(0) pm.instruments.append(instrument) time_signature = pretty_midi.containers.TimeSignature(4, 4, 0.0) pm.time_signature_changes.append(time_signature) instrument_events = collections.defaultdict( lambda: collections.defaultdict(list)) time = 0 tick = 60 / bpm / (self.q / 4) for i, encoding in enumerate(sequence, 1): if encoding not in ('<START>', '<BAR>', '<END>', '<PAD>'): encoding = self.encoder.decode(encoding) if encoding: # empty means tick rest for pitch in encoding: instrument_events[(pitch, 9, True)]['notes'].append( pretty_midi.Note(100, pitch, time, time + tick)) time += tick for (instr_id, prog_id, is_drum) in sorted(instrument_events.keys(), reverse=True): if instr_id > 0: instrument = pretty_midi.Instrument( prog_id, is_drum, name=pretty_midi.note_number_to_drum_name(instr_id)) pm.instruments.append(instrument) instrument.program = prog_id instrument.notes = instrument_events[(instr_id, prog_id, is_drum)]['notes'] return pm
def visualize(self, ax_subplot, color: Union[NDArray, str], marker: str = "2"): """ Visualize the pattern. """ x_length = np.arange(0, self.pattern.shape[-1]) for note_row in self.pattern: values = [ note.pitch if note is not None else np.nan for note in note_row ] ax_subplot.scatter(x_length, values, color=color, marker=marker) ax_subplot.yaxis.set_major_formatter( FuncFormatter(lambda tick, pos: note_number_to_drum_name(tick))) ax_subplot.xaxis.set_major_locator(MultipleLocator(base=self.steps)) return ax_subplot
def synthesize_drum_instrument(instrument, fs=44100): ''' Synthesize a pretty_midi.Instrument object with drum sounds. Parameters ---------- instrument : pretty_midi.Instrument Instrument to synthesize Returns ------- synthesized : np.ndarray Audio data of the instrument synthesized ''' # Allocate audio data synthesized = np.zeros(int((instrument.get_end_time() + 1)*fs)) for note in instrument.notes: # Get the name of the drum drum_name = pretty_midi.note_number_to_drum_name(note.pitch) # Based on the drum name, synthesize using the tonal or noise functions if drum_name in ['Acoustic Bass Drum', 'Bass Drum 1']: d = tonal(fs, fs/2, 80, 8.) elif drum_name in ['Side Stick']: d = tonal(fs, fs/20, 400, 8.) elif drum_name in ['Acoustic Snare', 'Electric Snare']: d = .4*tonal(fs, fs/10, 200, 20.) + .6*noise(fs/10) elif drum_name in ['Hand Clap', 'Vibraslap']: d = .1*tonal(fs, fs/10, 400, 8.) + .9*noise(fs/10) elif drum_name in ['Low Floor Tom', 'Low Tom', 'Low Bongo', 'Low Conga', 'Low Timbale']: d = tonal(fs, fs/4, 120, 8.) elif drum_name in ['Closed Hi Hat', 'Cabasa', 'Maracas', 'Short Guiro']: d = noise(fs/20) elif drum_name in ['High Floor Tom', 'High Tom', 'Hi Bongo', 'Open Hi Conga', 'High Timbale']: d = tonal(fs, fs/4, 480, 4.) elif drum_name in ['Pedal Hi Hat', 'Open Hi Hat', 'Crash Cymbal 1', 'Ride Cymbal 1', 'Chinese Cymbal', 'Crash Cymbal 2', 'Ride Cymbal 2', 'Tambourine', 'Long Guiro', 'Splash Cymbal']: d = .8*noise(fs) elif drum_name in ['Low-Mid Tom']: d = tonal(fs, fs/4, 240, 4.) elif drum_name in ['Hi-Mid Tom']: d = tonal(fs, fs/4, 360, 4.) elif drum_name in ['Mute Hi Conga', 'Mute Cuica', 'Cowbell', 'Low Agogo', 'Low Wood Block']: d = tonal(fs, fs/10, 480, 4.) elif drum_name in ['Ride Bell', 'High Agogo', 'Claves', 'Hi Wood Block']: d = tonal(fs, fs/20, 960, 4.) elif drum_name in ['Short Whistle']: d = tonal(fs, fs/4, 480, 1.) elif drum_name in ['Long Whistle']: d = tonal(fs, fs, 480, 1.) elif drum_name in ['Mute Triangle']: d = tonal(fs, fs/10, 1960, 1.) elif drum_name in ['Open Triangle']: d = tonal(fs, fs, 1960, 1.) else: if drum_name is not '': # This should never happen print('Unexpected drum {}'.format(drum_name)) continue # Add in the synthesized waveform start = int(note.start*fs) synthesized[start:start+d.size] += d*note.velocity return synthesized
def plot_pianoroll(ax, pianoroll, is_drum=False, beat_resolution=None, downbeats=None, normalization='standard', preset='default', cmap='Blues', tick_loc=None, xtick='auto', ytick='octave', xticklabel='on', yticklabel='auto', direction='in', label='both', grid='both', grid_linestyle=':', grid_linewidth=.5): """ Plot a piano-roll given as a numpy array Parameters ---------- ax : matplotlib.axes.Axes object The :class:`matplotlib.axes.Axes` object where the piano-roll will be plotted on. pianoroll : np.ndarray The piano-roll to be plotted. The values should be in [0, 1] when `normalized` is False. - For 2D array, shape=(num_time_step, num_pitch). - For 3D array, shape=(num_time_step, num_pitch, num_channel), where channels can be either RGB or RGBA. is_drum : bool Drum indicator. True for drums. False for other instruments. Default to False. beat_resolution : int Resolution of a beat (in time step). Required and only effective when `xticklabel` is 'beat'. downbeats : list Indices of time steps that contain downbeats., i.e. the first time step of a bar. normalization : {'standard', 'auto', 'none'} The normalization method to apply to the piano-roll. Default to 'standard'. If `pianoroll` is binarized, use 'none' anyway. - For 'standard' normalization, the normalized values are given by N = P / 128, where P, N is the original and normalized piano-roll, respectively - For 'auto' normalization, the normalized values are given by N = (P - m) / (M - m), where P, N is the original and normalized piano-roll, respectively, and M, m is the maximum and minimum of the original piano-roll, respectively. - If 'none', no normalization will be applied to the piano-roll. In this case, the values of `pianoroll` should be in [0, 1] in order to plot it correctly. preset : {'default', 'plain', 'frame'} Preset themes for the plot. - In 'default' preset, the ticks, grid and labels are on. - In 'frame' preset, the ticks and grid are both off. - In 'plain' preset, the x- and y-axis are both off. cmap : `matplotlib.colors.Colormap` Colormap to use in :func:`matplotlib.pyplot.imshow`. Default to 'Blues'. Only effective when `pianoroll` is 2D. tick_loc : tuple or list List of locations to put ticks. Availables elements are 'bottom', 'top', 'left' and 'right'. If None, default to ('bottom', 'left'). xtick : {'auto', 'beat', 'step', 'off'} Use beat number or step number as ticks along the x-axis, or automatically set to 'beat' when `beat_resolution` is given and set to 'step', otherwise. Default to 'auto'. ytick : {'octave', 'pitch', 'off'} Use octave or pitch as ticks along the y-axis. Default to 'octave'. xticklabel : {'on', 'off'} Indicate whether to add tick labels along the x-axis. Only effective when `xtick` is not 'off'. yticklabel : {'auto', 'name', 'number', 'off'} If 'name', use octave name and pitch name (key name when `is_drum` is True) as tick labels along the y-axis. If 'number', use pitch number. If 'auto', set to 'name' when `ytick` is 'octave' and 'number' when `ytick` is 'pitch'. Default to 'auto'. Only effective when `ytick` is not 'off'. direction : {'in', 'out', 'inout'} Put ticks inside the axes, outside the axes, or both. Default to 'in'. Only effective when `xtick` and `ytick` are not both 'off'. label : {'x', 'y', 'both', 'off'} Add label to the x-axis, y-axis, both or neither. Default to 'both'. grid : {'x', 'y', 'both', 'off'} Add grid to the x-axis, y-axis, both or neither. Default to 'both'. grid_linestyle : str Will be passed to :meth:`matplotlib.axes.Axes.grid` as 'linestyle' argument. grid_linewidth : float Will be passed to :meth:`matplotlib.axes.Axes.grid` as 'linewidth' argument. """ if pianoroll.ndim not in (2, 3): raise ValueError("`pianoroll` must be a 2D or 3D numpy array") if pianoroll.shape[1] != 128: raise ValueError("The shape of `pianoroll` must be (num_time_step, " "128)") if xtick not in ('auto', 'beat', 'step', 'off'): raise ValueError("`xtick` must be one of {'auto', 'beat', 'step', " "'none'}") if xtick is 'beat' and beat_resolution is None: raise ValueError("`beat_resolution` must be a number when `xtick` " "is 'beat'") if ytick not in ('octave', 'pitch', 'off'): raise ValueError("`ytick` must be one of {octave', 'pitch', 'off'}") if xticklabel not in ('on', 'off'): raise ValueError("`xticklabel` must be 'on' or 'off'") if yticklabel not in ('auto', 'name', 'number', 'off'): raise ValueError("`yticklabel` must be one of {'auto', 'name', " "'number', 'off'}") if direction not in ('in', 'out', 'inout'): raise ValueError("`direction` must be one of {'in', 'out', 'inout'}") if label not in ('x', 'y', 'both', 'off'): raise ValueError("`label` must be one of {'x', 'y', 'both', 'off'}") if grid not in ('x', 'y', 'both', 'off'): raise ValueError("`grid` must be one of {'x', 'y', 'both', 'off'}") # plotting if pianoroll.ndim > 2: to_plot = pianoroll.transpose(1, 0, 2) else: to_plot = pianoroll.T if normalization == 'standard': to_plot = to_plot / 128. elif normalization == 'auto': max_value = np.max(to_plot) min_value = np.min(to_plot) to_plot = to_plot - min_value / (max_value - min_value) ax.imshow(to_plot, cmap=cmap, aspect='auto', vmin=0, vmax=1, origin='lower', interpolation='none') # tick setting if tick_loc is None: tick_loc = ('bottom', 'left') if xtick == 'auto': xtick = 'beat' if beat_resolution is not None else 'step' if yticklabel == 'auto': yticklabel = 'name' if ytick == 'octave' else 'number' if preset == 'plain': ax.axis('off') elif preset == 'frame': ax.tick_params(direction=direction, bottom='off', top='off', left='off', right='off', labelbottom='off', labeltop='off', labelleft='off', labelright='off') else: labelbottom = 'on' if xticklabel != 'off' else 'off' labelleft = 'on' if yticklabel != 'off' else 'off' ax.tick_params(direction=direction, bottom=('bottom' in tick_loc), top=('top' in tick_loc), left=('left' in tick_loc), right=('right' in tick_loc), labelbottom=labelbottom, labeltop='off', labelleft=labelleft, labelright='off') # x-axis if xtick == 'beat' and preset != 'frame': num_beat = pianoroll.shape[0] // beat_resolution xticks_major = beat_resolution * np.arange(0, num_beat) xticks_minor = beat_resolution * (0.5 + np.arange(0, num_beat)) xtick_labels = np.arange(1, 1 + num_beat) ax.set_xticks(xticks_major) ax.set_xticklabels('') ax.set_xticks(xticks_minor, minor=True) ax.set_xticklabels(xtick_labels, minor=True) ax.tick_params(axis='x', which='minor', width=0) # y-axis if ytick == 'octave': ax.set_yticks(np.arange(0, 128, 12)) if yticklabel == 'name': ax.set_yticklabels(['C{}'.format(i - 2) for i in range(11)]) elif ytick == 'step': ax.set_yticks(np.arange(0, 128)) if yticklabel == 'name': if is_drum: ax.set_yticklabels([ pretty_midi.note_number_to_drum_name(i) for i in range(128) ]) else: ax.set_yticklabels( [pretty_midi.note_number_to_name(i) for i in range(128)]) # axis labels if label == 'x' or label == 'both': if xtick == 'step' or xticklabel == 'off': ax.set_xlabel('time (step)') else: ax.set_xlabel('time (beat)') if label == 'y' or label == 'both': if is_drum: ax.set_ylabel('key name') else: ax.set_ylabel('pitch') # grid if grid != 'off': ax.grid(axis=grid, color='k', linestyle=grid_linestyle, linewidth=grid_linewidth) # downbeat boarder if downbeats is not None and preset != 'plain': for step in downbeats: ax.axvline(x=step, color='k', linewidth=1)
def plot_pianoroll( ax, pianoroll, is_drum=False, beat_resolution=None, downbeats=None, preset="default", cmap="Blues", xtick="auto", ytick="octave", xticklabel=True, yticklabel="auto", tick_loc=None, tick_direction="in", label="both", grid="both", grid_linestyle=":", grid_linewidth=0.5, ): """ Plot a pianoroll given as a numpy array. Parameters ---------- ax : matplotlib.axes.Axes object A :class:`matplotlib.axes.Axes` object where the pianoroll will be plotted on. pianoroll : np.ndarray A pianoroll to be plotted. The values should be in [0, 1] when data type is float, and in [0, 127] when data type is integer. - For a 2D array, shape=(num_time_step, num_pitch). - For a 3D array, shape=(num_time_step, num_pitch, num_channel), where channels can be either RGB or RGBA. is_drum : bool A boolean number that indicates whether it is a percussion track. Defaults to False. beat_resolution : int The number of time steps used to represent a beat. Required and only effective when `xtick` is 'beat'. downbeats : list An array that indicates whether the time step contains a downbeat (i.e., the first time step of a bar). preset : {'default', 'plain', 'frame'} A string that indicates the preset theme to use. - In 'default' preset, the ticks, grid and labels are on. - In 'frame' preset, the ticks and grid are both off. - In 'plain' preset, the x- and y-axis are both off. cmap : `matplotlib.colors.Colormap` The colormap to use in :func:`matplotlib.pyplot.imshow`. Defaults to 'Blues'. Only effective when `pianoroll` is 2D. xtick : {'auto', 'beat', 'step', 'off'} A string that indicates what to use as ticks along the x-axis. If 'auto' is given, automatically set to 'beat' if `beat_resolution` is also given and set to 'step', otherwise. Defaults to 'auto'. ytick : {'octave', 'pitch', 'off'} A string that indicates what to use as ticks along the y-axis. Defaults to 'octave'. xticklabel : bool Whether to add tick labels along the x-axis. Only effective when `xtick` is not 'off'. yticklabel : {'auto', 'name', 'number', 'off'} If 'name', use octave name and pitch name (key name when `is_drum` is True) as tick labels along the y-axis. If 'number', use pitch number. If 'auto', set to 'name' when `ytick` is 'octave' and 'number' when `ytick` is 'pitch'. Defaults to 'auto'. Only effective when `ytick` is not 'off'. tick_loc : tuple or list The locations to put the ticks. Availables elements are 'bottom', 'top', 'left' and 'right'. Defaults to ('bottom', 'left'). tick_direction : {'in', 'out', 'inout'} A string that indicates where to put the ticks. Defaults to 'in'. Only effective when one of `xtick` and `ytick` is on. label : {'x', 'y', 'both', 'off'} A string that indicates whether to add labels to the x-axis and y-axis. Defaults to 'both'. grid : {'x', 'y', 'both', 'off'} A string that indicates whether to add grids to the x-axis, y-axis, both or neither. Defaults to 'both'. grid_linestyle : str Will be passed to :meth:`matplotlib.axes.Axes.grid` as 'linestyle' argument. grid_linewidth : float Will be passed to :meth:`matplotlib.axes.Axes.grid` as 'linewidth' argument. """ if not HAS_MATPLOTLIB: raise ImportError( "matplotlib package is required for plotting supports.") if pianoroll.ndim not in (2, 3): raise ValueError("`pianoroll` must be a 2D or 3D numpy array") if pianoroll.shape[1] != 128: raise ValueError( "The length of the second axis of `pianoroll` must be 128.") if xtick not in ("auto", "beat", "step", "off"): raise ValueError( "`xtick` must be one of {'auto', 'beat', 'step', 'none'}.") if xtick == "beat" and beat_resolution is None: raise ValueError( "`beat_resolution` must be specified when `xtick` is 'beat'.") if ytick not in ("octave", "pitch", "off"): raise ValueError("`ytick` must be one of {octave', 'pitch', 'off'}.") if not isinstance(xticklabel, bool): raise TypeError("`xticklabel` must be bool.") if yticklabel not in ("auto", "name", "number", "off"): raise ValueError( "`yticklabel` must be one of {'auto', 'name', 'number', 'off'}.") if tick_direction not in ("in", "out", "inout"): raise ValueError( "`tick_direction` must be one of {'in', 'out', 'inout'}.") if label not in ("x", "y", "both", "off"): raise ValueError("`label` must be one of {'x', 'y', 'both', 'off'}.") if grid not in ("x", "y", "both", "off"): raise ValueError("`grid` must be one of {'x', 'y', 'both', 'off'}.") # plotting if pianoroll.ndim > 2: to_plot = pianoroll.transpose(1, 0, 2) else: to_plot = pianoroll.T if np.issubdtype(pianoroll.dtype, np.bool_) or np.issubdtype( pianoroll.dtype, np.floating): ax.imshow( to_plot, cmap=cmap, aspect="auto", vmin=0, vmax=1, origin="lower", interpolation="none", ) elif np.issubdtype(pianoroll.dtype, np.integer): ax.imshow( to_plot, cmap=cmap, aspect="auto", vmin=0, vmax=127, origin="lower", interpolation="none", ) else: raise TypeError("Unsupported data type for `pianoroll`.") # tick setting if tick_loc is None: tick_loc = ("bottom", "left") if xtick == "auto": xtick = "beat" if beat_resolution is not None else "step" if yticklabel == "auto": yticklabel = "name" if ytick == "octave" else "number" if preset == "plain": ax.axis("off") elif preset == "frame": ax.tick_params( direction=tick_direction, bottom=False, top=False, left=False, right=False, labelbottom=False, labeltop=False, labelleft=False, labelright=False, ) else: ax.tick_params( direction=tick_direction, bottom=("bottom" in tick_loc), top=("top" in tick_loc), left=("left" in tick_loc), right=("right" in tick_loc), labelbottom=(xticklabel != "off"), labelleft=(yticklabel != "off"), labeltop=False, labelright=False, ) # x-axis if xtick == "beat" and preset != "frame": num_beat = pianoroll.shape[0] // beat_resolution ax.set_xticks(beat_resolution * np.arange(num_beat) - 0.5) ax.set_xticklabels("") ax.set_xticks(beat_resolution * (np.arange(num_beat) + 0.5) - 0.5, minor=True) ax.set_xticklabels(np.arange(1, num_beat + 1), minor=True) ax.tick_params(axis="x", which="minor", width=0) # y-axis if ytick == "octave": ax.set_yticks(np.arange(0, 128, 12)) if yticklabel == "name": ax.set_yticklabels(["C{}".format(i - 2) for i in range(11)]) elif ytick == "step": ax.set_yticks(np.arange(0, 128)) if yticklabel == "name": if is_drum: ax.set_yticklabels([ pretty_midi.note_number_to_drum_name(i) for i in range(128) ]) else: ax.set_yticklabels( [pretty_midi.note_number_to_name(i) for i in range(128)]) # axis labels if label in ("x", "both"): if xtick == "step" or not xticklabel: ax.set_xlabel("time (step)") else: ax.set_xlabel("time (beat)") if label in ("y", "both"): if is_drum: ax.set_ylabel("key name") else: ax.set_ylabel("pitch") # grid if grid != "off": ax.grid(axis=grid, color="k", linestyle=grid_linestyle, linewidth=grid_linewidth) # downbeat boarder if downbeats is not None and preset != "plain": for step in downbeats: ax.axvline(x=step, color="k", linewidth=1)
def synthesize_drum_instrument(instrument, fs=44100): ''' Synthesize a pretty_midi.Instrument object with drum sounds. Parameters ---------- instrument : pretty_midi.Instrument Instrument to synthesize Returns ------- synthesized : np.ndarray Audio data of the instrument synthesized ''' # Allocate audio data synthesized = np.zeros(int((instrument.get_end_time() + 1) * fs)) for note in instrument.notes: # Get the name of the drum drum_name = pretty_midi.note_number_to_drum_name(note.pitch) # Based on the drum name, synthesize using the tonal or noise functions if drum_name in ['Acoustic Bass Drum', 'Bass Drum 1']: d = tonal(fs, fs / 2, 80, 8.) elif drum_name in ['Side Stick']: d = tonal(fs, fs / 20, 400, 8.) elif drum_name in ['Acoustic Snare', 'Electric Snare']: d = .4 * tonal(fs, fs / 10, 200, 20.) + .6 * noise(fs / 10) elif drum_name in ['Hand Clap', 'Vibraslap']: d = .1 * tonal(fs, fs / 10, 400, 8.) + .9 * noise(fs / 10) elif drum_name in [ 'Low Floor Tom', 'Low Tom', 'Low Bongo', 'Low Conga', 'Low Timbale' ]: d = tonal(fs, fs / 4, 120, 8.) elif drum_name in [ 'Closed Hi Hat', 'Cabasa', 'Maracas', 'Short Guiro' ]: d = noise(fs / 20) elif drum_name in [ 'High Floor Tom', 'High Tom', 'Hi Bongo', 'Open Hi Conga', 'High Timbale' ]: d = tonal(fs, fs / 4, 480, 4.) elif drum_name in [ 'Pedal Hi Hat', 'Open Hi Hat', 'Crash Cymbal 1', 'Ride Cymbal 1', 'Chinese Cymbal', 'Crash Cymbal 2', 'Ride Cymbal 2', 'Tambourine', 'Long Guiro', 'Splash Cymbal' ]: d = .8 * noise(fs) elif drum_name in ['Low-Mid Tom']: d = tonal(fs, fs / 4, 240, 4.) elif drum_name in ['Hi-Mid Tom']: d = tonal(fs, fs / 4, 360, 4.) elif drum_name in [ 'Mute Hi Conga', 'Mute Cuica', 'Cowbell', 'Low Agogo', 'Low Wood Block' ]: d = tonal(fs, fs / 10, 480, 4.) elif drum_name in [ 'Ride Bell', 'High Agogo', 'Claves', 'Hi Wood Block' ]: d = tonal(fs, fs / 20, 960, 4.) elif drum_name in ['Short Whistle']: d = tonal(fs, fs / 4, 480, 1.) elif drum_name in ['Long Whistle']: d = tonal(fs, fs, 480, 1.) elif drum_name in ['Mute Triangle']: d = tonal(fs, fs / 10, 1960, 1.) elif drum_name in ['Open Triangle']: d = tonal(fs, fs, 1960, 1.) else: if drum_name is not '': # This should never happen print('Unexpected drum {}'.format(drum_name)) continue # Add in the synthesized waveform start = int(note.start * fs) synthesized[start:start + d.size] += d * note.velocity return synthesized
def plot_pianoroll( ax: Axes, pianoroll: ndarray, is_drum: bool = False, resolution: Optional[int] = None, downbeats: Optional[Sequence[int]] = None, preset: str = "full", cmap: str = "Blues", xtick: str = "auto", ytick: str = "octave", xticklabel: bool = True, yticklabel: str = "auto", tick_loc: Sequence[str] = ("bottom", "left"), tick_direction: str = "in", label: str = "both", grid_axis: str = "both", grid_linestyle: str = ":", grid_linewidth: float = 0.5, **kwargs, ): """ Plot a piano roll. Parameters ---------- ax : :class:`matplotlib.axes.Axes` Axes to plot the piano roll on. pianoroll : ndarray, shape=(?, 128), (?, 128, 3) or (?, 128, 4) Piano roll to plot. For a 3D piano-roll array, the last axis can be either RGB or RGBA. is_drum : bool Whether it is a percussion track. Defaults to False. resolution : int Time steps per quarter note. Required if `xtick` is 'beat'. downbeats : list Boolean array that indicates whether the time step contains a downbeat (i.e., the first time step of a bar). preset : {'full', 'frame', 'plain'} Preset theme. For 'full' preset, ticks, grid and labels are on. For 'frame' preset, ticks and grid are both off. For 'plain' preset, the x- and y-axis are both off. Defaults to 'full'. cmap : str or :class:`matplotlib.colors.Colormap` Colormap. Will be passed to :func:`matplotlib.pyplot.imshow`. Only effective when `pianoroll` is 2D. Defaults to 'Blues'. xtick : {'auto', 'beat', 'step', 'off'} Tick format for the x-axis. For 'auto' mode, set to 'beat' if `resolution` is given, otherwise set to 'step'. Defaults to 'auto'. ytick : {'octave', 'pitch', 'off'} Tick format for the y-axis. Defaults to 'octave'. xticklabel : bool Whether to add tick labels along the x-axis. yticklabel : {'auto', 'name', 'number', 'off'} Tick label format for the y-axis. For 'name' mode, use pitch name as tick labels. For 'number' mode, use pitch number. For 'auto' mode, set to 'name' if `ytick` is 'octave' and 'number' if `ytick` is 'pitch'. Defaults to 'auto'. tick_loc : sequence of {'bottom', 'top', 'left', 'right'} Tick locations. Defaults to `('bottom', 'left')`. tick_direction : {'in', 'out', 'inout'} Tick direction. Defaults to 'in'. label : {'x', 'y', 'both', 'off'} Whether to add labels to x- and y-axes. Defaults to 'both'. grid_axis : {'x', 'y', 'both', 'off'} Whether to add grids to the x- and y-axes. Defaults to 'both'. grid_linestyle : str Grid line style. Will be passed to :meth:`matplotlib.axes.Axes.grid`. grid_linewidth : float Grid line width. Will be passed to :meth:`matplotlib.axes.Axes.grid`. **kwargs Keyword arguments to be passed to :meth:`matplotlib.axes.Axes.imshow`. """ # Plot the piano roll if pianoroll.ndim == 2: transposed = pianoroll.T elif pianoroll.ndim == 3: transposed = pianoroll.transpose(1, 0, 2) else: raise ValueError("`pianoroll` must be a 2D or 3D numpy array") img = ax.imshow( transposed, cmap=cmap, aspect="auto", vmin=0, vmax=1 if pianoroll.dtype == np.bool_ else 127, origin="lower", interpolation="none", **kwargs, ) # Format ticks and labels if xtick == "auto": xtick = "beat" if resolution is not None else "step" elif xtick not in ("beat", "step", "off"): raise ValueError( "`xtick` must be one of 'auto', 'beat', 'step' or 'off', not " f"{xtick}.") if yticklabel == "auto": yticklabel = "name" if ytick == "octave" else "number" elif yticklabel not in ("name", "number", "off"): raise ValueError( "`yticklabel` must be one of 'auto', 'name', 'number' or 'off', " f"{yticklabel}.") if preset == "full": ax.tick_params( direction=tick_direction, bottom=("bottom" in tick_loc), top=("top" in tick_loc), left=("left" in tick_loc), right=("right" in tick_loc), labelbottom=xticklabel, labelleft=(yticklabel != "off"), labeltop=False, labelright=False, ) elif preset == "frame": ax.tick_params( direction=tick_direction, bottom=False, top=False, left=False, right=False, labelbottom=False, labeltop=False, labelleft=False, labelright=False, ) elif preset == "plain": ax.axis("off") else: raise ValueError( f"`preset` must be one of 'full', 'frame' or 'plain', not {preset}" ) # Format x-axis if xtick == "beat" and preset != "frame": if resolution is None: raise ValueError( "`resolution` must not be None when `xtick` is 'beat'.") n_beats = pianoroll.shape[0] // resolution ax.set_xticks(resolution * np.arange(n_beats) - 0.5) ax.set_xticklabels("") ax.set_xticks(resolution * (np.arange(n_beats) + 0.5) - 0.5, minor=True) ax.set_xticklabels(np.arange(1, n_beats + 1), minor=True) ax.tick_params(axis="x", which="minor", width=0) # Format y-axis if ytick == "octave": ax.set_yticks(np.arange(0, 128, 12)) if yticklabel == "name": ax.set_yticklabels(["C{}".format(i - 2) for i in range(11)]) elif ytick == "step": ax.set_yticks(np.arange(0, 128)) if yticklabel == "name": if is_drum: ax.set_yticklabels( [note_number_to_drum_name(i) for i in range(128)]) else: ax.set_yticklabels( [note_number_to_name(i) for i in range(128)]) elif ytick != "off": raise ValueError( f"`ytick` must be one of 'octave', 'pitch' or 'off', not {ytick}.") # Format axis labels if label not in ("x", "y", "both", "off"): raise ValueError( f"`label` must be one of 'x', 'y', 'both' or 'off', not {label}.") if label in ("x", "both"): if xtick == "step" or not xticklabel: ax.set_xlabel("time (step)") else: ax.set_xlabel("time (beat)") if label in ("y", "both"): if is_drum: ax.set_ylabel("key name") else: ax.set_ylabel("pitch") # Plot the grid if grid_axis not in ("x", "y", "both", "off"): raise ValueError( "`grid` must be one of 'x', 'y', 'both' or 'off', not " f"{grid_axis}.") if grid_axis != "off": ax.grid( axis=grid_axis, color="k", linestyle=grid_linestyle, linewidth=grid_linewidth, ) # Plot downbeat boundaries if downbeats is not None: for downbeat in downbeats: ax.axvline(x=downbeat, color="k", linewidth=1) return img
def plot_pianoroll(ax, pianoroll, is_drum=False, beat_resolution=None, downbeats=None, preset='default', cmap='Blues', xtick='auto', ytick='octave', xticklabel=True, yticklabel='auto', tick_loc=None, tick_direction='in', label='both', grid='both', grid_linestyle=':', grid_linewidth=.5): """ Plot a piano-roll given as a numpy array. Parameters ---------- ax : matplotlib.axes.Axes object The :class:`matplotlib.axes.Axes` object where the piano-roll will be plotted on. pianoroll : np.ndarray The piano-roll to be plotted. The values should be in [0, 1] when data type is float, and in [0, 127] when data type is integer. - For a 2D array, shape=(num_time_step, num_pitch). - For a 3D array, shape=(num_time_step, num_pitch, num_channel), where channels can be either RGB or RGBA. is_drum : bool Drum indicator. True for drums. False for other instruments. Default to False. beat_resolution : int Resolution of a beat (in time step). Required and only effective when `xticklabel` is 'beat'. downbeats : list Indices of time steps that contain downbeats., i.e. the first time step of a bar. preset : {'default', 'plain', 'frame'} Preset themes for the plot. - In 'default' preset, the ticks, grid and labels are on. - In 'frame' preset, the ticks and grid are both off. - In 'plain' preset, the x- and y-axis are both off. cmap : `matplotlib.colors.Colormap` Colormap to use in :func:`matplotlib.pyplot.imshow`. Default to 'Blues'. Only effective when `pianoroll` is 2D. xtick : {'auto', 'beat', 'step', 'off'} Use beat number or step number as ticks along the x-axis, or automatically set to 'beat' when `beat_resolution` is given and set to 'step', otherwise. Default to 'auto'. ytick : {'octave', 'pitch', 'off'} Use octave or pitch as ticks along the y-axis. Default to 'octave'. xticklabel : bool Indicate whether to add tick labels along the x-axis. Only effective when `xtick` is not 'off'. yticklabel : {'auto', 'name', 'number', 'off'} If 'name', use octave name and pitch name (key name when `is_drum` is True) as tick labels along the y-axis. If 'number', use pitch number. If 'auto', set to 'name' when `ytick` is 'octave' and 'number' when `ytick` is 'pitch'. Default to 'auto'. Only effective when `ytick` is not 'off'. tick_loc : tuple or list List of locations to put ticks. Availables elements are 'bottom', 'top', 'left' and 'right'. If None, default to ('bottom', 'left'). tick_direction : {'in', 'out', 'inout'} Put ticks inside the axes, outside the axes, or both. Default to 'in'. Only effective when `xtick` and `ytick` are not both 'off'. label : {'x', 'y', 'both', 'off'} Add label to the x-axis, y-axis, both or neither. Default to 'both'. grid : {'x', 'y', 'both', 'off'} Add grid to the x-axis, y-axis, both or neither. Default to 'both'. grid_linestyle : str Will be passed to :meth:`matplotlib.axes.Axes.grid` as 'linestyle' argument. grid_linewidth : float Will be passed to :meth:`matplotlib.axes.Axes.grid` as 'linewidth' argument. """ if not HAS_MATPLOTLIB: raise ImportError("matplotlib package is required for plotting " "supports.") if pianoroll.ndim not in (2, 3): raise ValueError("`pianoroll` must be a 2D or 3D numpy array") if pianoroll.shape[1] != 128: raise ValueError("The shape of `pianoroll` must be (num_time_step, " "128)") if xtick not in ('auto', 'beat', 'step', 'off'): raise ValueError("`xtick` must be one of {'auto', 'beat', 'step', " "'none'}") if xtick == 'beat' and beat_resolution is None: raise ValueError("`beat_resolution` must be a number when `xtick` " "is 'beat'") if ytick not in ('octave', 'pitch', 'off'): raise ValueError("`ytick` must be one of {octave', 'pitch', 'off'}") if not isinstance(xticklabel, bool): raise TypeError("`xticklabel` must be of bool type") if yticklabel not in ('auto', 'name', 'number', 'off'): raise ValueError("`yticklabel` must be one of {'auto', 'name', " "'number', 'off'}") if tick_direction not in ('in', 'out', 'inout'): raise ValueError("`tick_direction` must be one of {'in', 'out'," "'inout'}") if label not in ('x', 'y', 'both', 'off'): raise ValueError("`label` must be one of {'x', 'y', 'both', 'off'}") if grid not in ('x', 'y', 'both', 'off'): raise ValueError("`grid` must be one of {'x', 'y', 'both', 'off'}") # plotting if pianoroll.ndim > 2: to_plot = pianoroll.transpose(1, 0, 2) else: to_plot = pianoroll.T if (np.issubdtype(pianoroll.dtype, np.bool_) or np.issubdtype(pianoroll.dtype, np.floating)): ax.imshow(to_plot, cmap=cmap, aspect='auto', vmin=0, vmax=1, origin='lower', interpolation='none') elif np.issubdtype(pianoroll.dtype, np.integer): ax.imshow(to_plot, cmap=cmap, aspect='auto', vmin=0, vmax=127, origin='lower', interpolation='none') else: raise TypeError("Unsupported data type for `pianoroll`") # tick setting if tick_loc is None: tick_loc = ('bottom', 'left') if xtick == 'auto': xtick = 'beat' if beat_resolution is not None else 'step' if yticklabel == 'auto': yticklabel = 'name' if ytick == 'octave' else 'number' if preset == 'plain': ax.axis('off') elif preset == 'frame': ax.tick_params(direction=tick_direction, bottom=False, top=False, left=False, right=False, labelbottom=False, labeltop=False, labelleft=False, labelright=False) else: ax.tick_params(direction=tick_direction, bottom=('bottom' in tick_loc), top=('top' in tick_loc), left=('left' in tick_loc), right=('right' in tick_loc), labelbottom=(xticklabel != 'off'), labelleft=(yticklabel != 'off'), labeltop=False, labelright=False) # x-axis if xtick == 'beat' and preset != 'frame': num_beat = pianoroll.shape[0] // beat_resolution xticks_major = beat_resolution * np.arange(0, num_beat) xticks_minor = beat_resolution * (0.5 + np.arange(0, num_beat)) xtick_labels = np.arange(1, 1 + num_beat) ax.set_xticks(xticks_major) ax.set_xticklabels('') ax.set_xticks(xticks_minor, minor=True) ax.set_xticklabels(xtick_labels, minor=True) ax.tick_params(axis='x', which='minor', width=0) # y-axis if ytick == 'octave': ax.set_yticks(np.arange(0, 128, 12)) if yticklabel == 'name': ax.set_yticklabels(['C{}'.format(i - 2) for i in range(11)]) elif ytick == 'step': ax.set_yticks(np.arange(0, 128)) if yticklabel == 'name': if is_drum: ax.set_yticklabels([ pretty_midi.note_number_to_drum_name(i) for i in range(128) ]) else: ax.set_yticklabels( [pretty_midi.note_number_to_name(i) for i in range(128)]) # axis labels if label == 'x' or label == 'both': if xtick == 'step' or not xticklabel: ax.set_xlabel('time (step)') else: ax.set_xlabel('time (beat)') if label == 'y' or label == 'both': if is_drum: ax.set_ylabel('key name') else: ax.set_ylabel('pitch') # grid if grid != 'off': ax.grid(axis=grid, color='k', linestyle=grid_linestyle, linewidth=grid_linewidth) # downbeat boarder if downbeats is not None and preset != 'plain': for step in downbeats: ax.axvline(x=step, color='k', linewidth=1)
#!/usr/local/bin/python import sys import pretty_midi midi_data = pretty_midi.PrettyMIDI('drums.mid') pr = midi_data.instruments[0].get_piano_roll().tolist() pr_len = len(pr[0]) new_pr = [] print pr_len for i in range(0,pr_len): tmp_pr = [] for j in range(0,127): if pr[j][i] > 0: if midi_data.instruments[0].is_drum: tmp_pr.append(pretty_midi.note_number_to_drum_name(j)) else: tmp_pr.append(pretty_midi.note_number_to_name(j)) new_pr.append(tmp_pr) print new_pr # Synthesize the resulting MIDI data using sine waves #audio_data = midi_data.synthesize() print pretty_midi.note_name_to_number("C3")