Source code for muspy.inputs.pitch

"""Pitch-based representation input interface."""
from operator import attrgetter
from typing import List

import numpy as np
from numpy import ndarray

from ..classes import DEFAULT_VELOCITY, Note, Track
from ..music import DEFAULT_RESOLUTION, Music


[docs]def from_pitch_representation( array: ndarray, resolution: int = DEFAULT_RESOLUTION, program: int = 0, is_drum: bool = False, use_hold_state: bool = False, default_velocity: int = DEFAULT_VELOCITY, ) -> Music: """Decode pitch-based representation into a Music object. Parameters ---------- array : ndarray Array in pitch-based representation to decode. resolution : int, default: `muspy.DEFAULT_RESOLUTION` (24) Time steps per quarter note. program : int, default: 0 (Acoustic Grand Piano) Program number, according to General MIDI specification [1]. Valid values are 0 to 127. is_drum : bool, default: False Whether it is a percussion track. use_hold_state : bool, default: False Whether to use a special state for holds. default_velocity : int, default: `muspy.DEFAULT_VELOCITY` (64) Default velocity value to use when decoding. Returns ------- :class:`muspy.Music` Decoded Music object. References ---------- [1] https://www.midi.org/specifications/item/gm-level-1-sound-set """ if not np.issubdtype(array.dtype, np.integer): raise TypeError("Array must be of type int.") # Find the note boundaries notes: List[Note] = [] diff = np.diff(array.flatten(), prepend=-1, append=-1) boundaries = np.nonzero(diff)[0] # Decode pitches if use_hold_state: is_awaiting_hold = False for start, end in zip(boundaries[:-1], boundaries[1:]): # Skip rests if array[start] == 128: is_awaiting_hold = False continue # Hold if array[start] == 129: # Skip a hold that does not follow any pitch if not is_awaiting_hold: continue notes[-1].duration += end - start is_awaiting_hold = False # Pitch else: note = Note( time=int(start), pitch=int(array[start]), duration=int(end - start), velocity=default_velocity, ) notes.append(note) is_awaiting_hold = True else: for start, end in zip(boundaries[:-1], boundaries[1:]): # Skip rests if array[start] == 128: continue # Pitch note = Note( time=int(start), pitch=int(array[start]), duration=int(end - start), velocity=default_velocity, ) notes.append(note) # Sort the notes notes.sort(key=attrgetter("time", "pitch", "duration", "velocity")) # Create the Track and Music objects track = Track(program=program, is_drum=is_drum, notes=notes) music = Music(resolution=resolution, tracks=[track]) return music