Beispiel #1
0
def part_midi(f, key=None):
    fname = f.split(os.path.sep)[-1]
    _midi = PrettyMIDI(f)
    # part
    for ind, instrument in enumerate(_midi.instruments):
        midi = PrettyMIDI(f)
        n = len(midi.instruments)
        # sound
        for ind2, inst in enumerate(midi.instruments):
            if ind2 != ind:
                inst.program = subSound
            else:
                inst.program = mainSound
        if instrument.name:
            name = instrument.name
        elif len(names) == n:
            name = names[ind]
        else:
            name = str(ind)
        if n != len(volumes):
            continue
        # volume
        for inst, value in zip(midi.instruments, volumes):
            if instrument is inst:
                value += volumeDiff
            inst.control_changes.append(
                ControlChange(number=7, value=value, time=0.001))
        midi.write(
            os.path.join(outdir,
                         fname.rsplit(".", 1)[0] + "_" + name + ".mid"))
Beispiel #2
0
def _render_midi(
    midifile: pretty_midi.PrettyMIDI,
    basename: str,
    delete_wave: bool = True,
) -> None:
    """
    Requires system packages: fluidsynth fluid-soundfont-gm lame
    http://wootangent.net/2010/11/converting-midi-to-wav-or-mp3-the-easy-way/
    """
    filename_mid = basename + ".mid"
    filename_wav = basename + ".wav"
    filename_mp3 = basename + ".mp3"
    midifile.write(filename_mid)

    print("\n *** Rendering MIDI")
    os.system(
        "fluidsynth -F {} /usr/share/sounds/sf2/FluidR3_GM.sf2 {}".format(
            filename_wav,
            filename_mid,
        ))

    print("\n *** Converting to MP3")
    os.system("lame --preset standard {} {}".format(
        filename_wav,
        filename_mp3,
    ))

    if delete_wave:
        os.system("rm {}".format(filename_wav))
Beispiel #3
0
    def write_midi(self, score_events, midi_program, pitch_low_passband=0, pitch_high_passband=127, time_offset=0.0):
        """
        Given a sequence of NoteEvents, calculate the MIDI ticks for each note and write these to a MIDI file.

        PARAMETERS:
            score_events (list): list of ScoreEvents
            midi_program (int): MIDI program to use for the MIDI track
            pitch_low_passband (int): discard any pitches with midi numbers less than this bound
            pitch_high_passband (int): discard any pitches with midi numbers greater than this bound
            time_offset (float): amount of time (s) to delay MIDI note events
        """

        midi = PrettyMIDI(resolution=MidiIO.DEFAULT_PPQ, initial_tempo=MidiIO.DEFAULT_TEMPO)
        instr = Instrument(program=midi_program)
        for n in score_events:
            if pitch_low_passband <= n.midi_number <= pitch_high_passband:
                note = MidiNote(
                    velocity=MidiIO.DEFAULT_VELOCITY, pitch=n.midi_number,
                    start=(n.onset_ts + time_offset), end=(n.offset_ts + time_offset)
                )
                instr.notes.append(note)
        midi.instruments.append(instr)
        midi.remove_invalid_notes()

        directory = os.path.split(self._path)[0]
        if not os.path.exists(directory):
            os.makedirs(directory)
        midi.write(self._path)
Beispiel #4
0
def get_beat_time(
        pm: PrettyMIDI,
        beat_division=4
) -> Tuple[ndarray, ndarray, ndarray, List[int], List[int]]:
    beats = pm.get_beats()

    beats = np.unique(beats, axis=0)

    divided_beats = []
    for i in range(len(beats) - 1):
        for j in range(beat_division):
            divided_beats.append((beats[i + 1] - beats[i]) / beat_division *
                                 j + beats[i])
    divided_beats.append(beats[-1])
    divided_beats = np.unique(divided_beats, axis=0)

    beat_indices = []
    for beat in beats:
        beat_indices.append(np.argwhere(divided_beats == beat)[0][0])

    down_beats = pm.get_downbeats()
    if divided_beats[-1] > down_beats[-1]:
        down_beats = np.append(
            down_beats, down_beats[-1] - down_beats[-2] + down_beats[-1])

    down_beats = np.unique(down_beats, axis=0)

    down_beat_indices = []
    for down_beat in down_beats:

        down_beat_indices.append(np.argmin(np.abs(down_beat - divided_beats)))

    return np.array(divided_beats), np.array(beats), np.array(
        down_beats), beat_indices, down_beat_indices
Beispiel #5
0
def extract_pianos(msd_id: str) -> List[PrettyMIDI]:
    """
  Extracts a list of PrettyMIDI instance of all the separate piano tracks
  from the given MSD id.

  :param msd_id: the MSD id
  :return: the list of PrettyMIDI instances of the separate piano tracks
  """
    os.makedirs(args.path_output_dir, exist_ok=True)
    midi_md5 = get_matched_midi_md5(msd_id, MSD_SCORE_MATCHES)
    midi_path = get_midi_path(msd_id, midi_md5, args.path_dataset_dir)
    pm = PrettyMIDI(midi_path)
    pm.instruments = [
        instrument for instrument in pm.instruments
        if instrument.program in PIANO_PROGRAMS and not instrument.is_drum
    ]
    pm_pianos = []
    if len(pm.instruments) > 1:
        for piano_instrument in pm.instruments:
            pm_piano = copy.deepcopy(pm)
            pm_piano_instrument = Instrument(program=piano_instrument.program)
            pm_piano.instruments = [pm_piano_instrument]
            for note in piano_instrument.notes:
                pm_piano_instrument.notes.append(note)
            pm_pianos.append(pm_piano)
    else:
        pm_pianos.append(pm)
    for index, pm_piano in enumerate(pm_pianos):
        if len(pm_piano.instruments) != 1:
            raise Exception(f"Invalid number of piano {msd_id}: "
                            f"{len(pm_piano.instruments)}")
        if pm_piano.get_end_time() > 1000:
            raise Exception(f"Piano track too long {msd_id}: "
                            f"{pm_piano.get_end_time()}")
    return pm_pianos
Beispiel #6
0
def midi_to_list(filename: str, start_time: float, duration: float) -> List[int]:
    pm = PrettyMIDI(filename)

    pm.adjust_times(np.array([start_time, start_time + duration]), np.array([0., duration]))

    # ensure one midi contain only on instrument
    note_seq = NoteSeq(notes=pm.instruments[0].notes)
    event_seq = EventSeq.from_note_seq(note_seq)
    return event_seq.to_list()
Beispiel #7
0
def all_midi(f, key=None):
    midi = PrettyMIDI(f)
    fname = f.split(os.path.sep)[-1]
    n = len(midi.instruments)
    for ind, instrument in enumerate(midi.instruments):
        # sound
        instrument.program = mainSound
        if n == len(volumes):
            instrument.control_changes.append(
                ControlChange(number=7, value=volumes[ind], time=0.001))
    midi.write(os.path.join(outdir, fname.rsplit(".", 1)[0] + "_all.mid"))
 def __init__(self, midi_path='', phrase=None, meta=None, note_shift=0, **kwargs):
     try:
         self.midi_path = None
         self.note_shift = note_shift
         self.midi = PrettyMIDI(midi_path)
         self.melo = self.midi.instruments[0]
     except:
         self.midi_path = midi_path
         self.melo = self.__load_pop909_melo()
     self.meta = meta
     self.phrase = phrase
Beispiel #9
0
def write_midi(note_sequence, output_dir, filename):

    #make output directory
    pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)

    #generate midi
    midi = PrettyMIDI()
    piano_track = Instrument(program=0, is_drum=False, name=filename)
    piano_track.notes = note_sequence
    midi.instruments.append(piano_track)
    output_name = output_dir + f"{filename}.midi"
    midi.write(output_name)
Beispiel #10
0
def midi_to_spec_otf(midi: pm.PrettyMIDI,
                     spec_params: dict,
                     sound_font_path=None) -> np.ndarray:
    """MIDI to Spectrogram (on the fly)

       Synthesizes a MIDI with fluidsynth and extracts a spectrogram.
       The spectrogram is directly returned
    """
    processor = spectrogram_processor(spec_params)

    def render_audio(midi_file_path, sound_font):
        """
        Render midi to audio
        """

        # split file name and extention
        name, extention = midi_file_path.rsplit(".", 1)

        # set file names
        audio_file = name + ".wav"

        # audio_file = tempfile.TemporaryFile('w+b')

        # synthesize midi file to audio
        cmd = "fluidsynth -F %s -O s16 -T wav %s %s 1> /dev/null" % (
            audio_file, sound_font, midi_file_path)

        os.system(cmd)
        return audio_file

    mid_path = os.path.join(tempfile.gettempdir(), str(time.time()) + '.mid')

    with open(mid_path, 'wb') as f:
        midi.write(f)

    audio_path = render_audio(mid_path, sound_font=sound_font_path)

    spec = processor.process(audio_path).T

    if spec_params.get('norm', False):
        spec = librosa.util.normalize(spec,
                                      norm=2,
                                      axis=0,
                                      threshold=0.01,
                                      fill=False)

    # compute spectrogram
    spec = np.expand_dims(spec, 0)

    os.remove(mid_path)
    os.remove(audio_path)

    return spec
Beispiel #11
0
def pitch_lists_to_midi_file(pitch_lists, midi_path):
    midi = PrettyMIDI()
    ins = Instrument(0)
    cursor = 0
    unit_length = 0.125
    for pitch_list in pitch_lists:
        for pitch in pitch_list:
            if pitch != 0:
                ins.notes.append(Note(start=cursor, end=cursor + unit_length, pitch=pitch, velocity=60))
            cursor += unit_length
    midi.instruments.append(ins)
    midi.write(midi_path)
Beispiel #12
0
def read_midi(filename: str, start_time: float, duration: float) -> PrettyMIDI:
    """

    :param filename:
    :param start_time: in seconds
    :param duration: in seconds
    :return:
    """
    pm = PrettyMIDI(filename)

    pm.adjust_times([start_time, start_time + duration], [0., duration])

    return pm
Beispiel #13
0
def read_midi_files(path: Union[Path, str],
                    regex: str,
                    extra: bool = True) -> List[Tuple[PrettyMIDI, int]]:
    """
    Reads mid files in given folder and returns a list of PrettyMIDI.
    For all following directories use **/*.*.
    """
    folder = Path(path)
    if extra:
        return [(PrettyMIDI(file.as_posix()), int(file.parent.stem) if
                 file.parent.stem != 'extra' else int(file.parent.parent.stem))
                for file in folder.glob(regex)]
    return [(PrettyMIDI(file.as_posix()), int(file.parent.stem))
            for file in folder.glob(regex) if file.parent.stem != 'extra']
Beispiel #14
0
def create_drum_pretty_midi(
    onset_matrix: np.ndarray,
    vel_matrix: np.ndarray,
    q_note_res: int = Q_NOTE_RES,
    bpm: float = 120.0,
    playback_midi_map: List[int] = CHRISTHETREE_PLAYBACK_MIDI_MAP_8
) -> PrettyMIDI:
    pm = PrettyMIDI(initial_tempo=bpm)

    res = (60.0 / bpm) / q_note_res
    drum_inst = Instrument(0, is_drum=True)
    n_beats = vel_matrix.shape[0]

    for beat_idx in range(n_beats):
        onset_vals = onset_matrix[beat_idx, :]
        onsets = np.nonzero(onset_vals >= 0.5)[0]
        vels = vel_matrix[beat_idx, :]

        for onset in onsets:
            pitch = playback_midi_map[onset]
            vel = int((vels[onset] * 127) + 0.5)
            _add_note(drum_inst, pitch, vel, res * beat_idx,
                      (res * beat_idx) + 0.2)

    pm.instruments.append(drum_inst)
    return pm
Beispiel #15
0
def listen_pitches(midi_pitch: list, time, instrument=0):
    midi = PrettyMIDI()
    ins = Instrument(instrument)
    for i in midi_pitch:
        ins.notes.append(Note(pitch=i, start=0, end=time, velocity=80))
    midi.instruments.append(ins)
    listen(midi)
    def to_midi(self, tempo=120, instrument=PIANO, tonic=None, lib=None):
        if not self.progression:
            Logging.error("Progression not assigned!")
            return None
        if not tonic:
            tonic = self.meta['tonic']
        midi = PrettyMIDI()
        unit_length = 30 / tempo
        ins = Instrument(instrument)
        if not self.saved_in_source_base:
            current_pos = 0
            for i in self.get_chord_progression():
                memo = -1
                length = 0
                for j in i:
                    if j == memo:
                        length += unit_length
                    else:
                        if memo != -1:
                            for pitch in memo.to_midi_pitch(
                                    tonic=self.__key_changer(
                                        self.meta['tonic'], memo.root, tonic)):
                                note = Note(pitch=pitch,
                                            velocity=80,
                                            start=current_pos,
                                            end=current_pos + length)
                                ins.notes.append(note)
                        current_pos += length
                        length = unit_length
                        memo = j
                for pitch in memo.to_midi_pitch(tonic=self.__key_changer(
                        self.meta['tonic'], memo.root, tonic)):
                    note = Note(pitch=pitch,
                                velocity=80,
                                start=current_pos,
                                end=current_pos + length)
                    ins.notes.append(note)
                current_pos += length

        else:
            if lib is None:
                lib = pickle_read('lib')
            try:
                all_notes = lib[self.meta['source']]
            except:
                Logging.error(
                    'Progression with source name {n} '
                    'cannot be find in library! '
                    'Call set_in_lib(in_lib=False) to generate MIDI '
                    'by progression list itself'.format(n=self.meta['source']))
                return False
            for note in all_notes:
                ins.notes.append(
                    Note(start=note[0] * unit_length,
                         end=note[1] * unit_length,
                         pitch=note[2] + str_to_root[tonic],
                         velocity=note[3]))

        midi.instruments.append(ins)
        return midi
Beispiel #17
0
 def convert2midi(self):
     midi = PrettyMIDI(resolution=STATE_RESOLUTION,
                       initial_tempo=STATE_TEMP)
     inst = Instrument(1, False, 'Note_Seqce')
     inst.notes = copy.deepcopy(self.notes)
     midi.instruments.append(inst)
     return midi
Beispiel #18
0
def occupation_polyphony_rate(pm: PrettyMIDI) -> Tuple[ndarray, ndarray]:
    occupation_rate = []
    polyphony_rate = []

    total_roll = pm.get_piano_roll()
    for instrument in pm.instruments:
        piano_roll = instrument.get_piano_roll()
        if piano_roll.shape[1] == 0:
            occupation_rate.append(0)
        else:
            occupation_rate.append(
                np.count_nonzero(np.any(piano_roll, 0)) / total_roll.shape[1])
        if np.count_nonzero(np.any(piano_roll, 0)) == 0:
            polyphony_rate.append(0)
        else:
            polyphony_rate.append(
                np.count_nonzero(np.count_nonzero(piano_roll, 0) > 1) /
                np.count_nonzero(np.any(piano_roll, 0)))

    occupation_rate = np.array(occupation_rate)
    zero_idx = np.where(occupation_rate < 0.01)[0]
    if len(zero_idx) > 0:
        occupation_rate[zero_idx] = 0

    occupation_rate = occupation_rate[:, np.newaxis]

    polyphony_rate = np.array(polyphony_rate)
    zero_idx = np.where(polyphony_rate < 0.01)[0]
    if len(zero_idx) > 0:
        polyphony_rate[zero_idx] = 0

    polyphony_rate = polyphony_rate[:, np.newaxis]

    return occupation_rate, polyphony_rate
def extract_drums(midi_path: str) -> Optional[PrettyMIDI]:
    """
  Extracts a PrettyMIDI instance of all the merged drum tracks
  from the given MIDI path.

  :param midi_path: the path to the MIDI file
  :return: the PrettyMIDI instance of the merged drum tracks
  """
    os.makedirs(args.path_output_dir, exist_ok=True)
    pm = PrettyMIDI(midi_path)
    pm_drums = copy.deepcopy(pm)
    pm_drums.instruments = [
        instrument for instrument in pm_drums.instruments if instrument.is_drum
    ]
    if len(pm_drums.instruments) > 1:
        # Some drum tracks are split, we can merge them
        drums = Instrument(program=0, is_drum=True)
        for instrument in pm_drums.instruments:
            for note in instrument.notes:
                drums.notes.append(note)
        pm_drums.instruments = [drums]
    if len(pm_drums.instruments) != 1:
        raise Exception(f"Invalid number of drums {midi_path}: "
                        f"{len(pm_drums.instruments)}")
    return pm_drums
Beispiel #20
0
def listen(midi: PrettyMIDI, path=None, out=None):
    if not fs_exist:
        return False
    if not path:
        path = string.STATIC_DIR + "audio/"
    midi.write(path + "__listen__.mid")
    fs = FluidSynth(sound_font=string.STATIC_DIR + 'default_sound_font.sf2')
    try:
        os.makedirs(path)
    except:
        pass
    if out is None:
        out = time.strftime("%H_%M_%S", time.localtime()) + ".wav"
    fs.midi_to_audio(path + "__listen__.mid", path + out)
    os.remove(path + "__listen__.mid")
    return True
Beispiel #21
0
def array_to_pm(array,
                fs=DEFAULT_FS,
                velocity=DEFAULT_VELOCITY,
                pitch_range=DEFAULT_PITCH_RANGE):

    pm = PrettyMIDI()
    inst = Instrument(1)
    pm.instruments.append(inst)

    last_notes = {}
    last_state = np.zeros(array.shape[1]) > 0.5

    for i, step in enumerate(array):
        now = i / fs
        step = step > 0.5
        changed = step != last_state
        for pitch in np.where(changed)[0]:
            if step[pitch]:
                last_notes[pitch] = Note(velocity, pitch + pitch_range.start,
                                         now, None)
                inst.notes.append(last_notes[pitch])
            else:
                last_notes[pitch].end = now
                del last_notes[pitch]
        last_state = step

    now = (i + 1) / fs
    for note in last_notes.values():
        note.end = now

    return pm
Beispiel #22
0
 def to_midi(self, program=DEFAULT_SAVING_PROGRAM,
             resolution=DEFAULT_RESOLUTION, tempo=DEFAULT_TEMPO):
     midi = PrettyMIDI(resolution=resolution, initial_tempo=tempo)
     inst = Instrument(program, False, 'NoteSeq')
     inst.notes = copy.deepcopy(self.notes)
     midi.instruments.append(inst)
     return midi
def convert_midi_files():
    """Save a multi-track piano-roll converted from a MIDI file to target
    dataset directory and update MIDI information to `midi_dict`"""
    converter_root_dir = 'E:/MIDI_converted'
    root_dir = 'E:/free_MIDI'
    midi_collection = get_midi_collection()
    for midi in midi_collection.find({'Converted': False}):
        genre = midi['Genre']
        name = midi['Name']
        performer = midi['Performer']
        filepath = root_dir + '/' + genre + '/' + name + ' - ' + performer + '.mid'
        try:
            midi_name = os.path.splitext(os.path.basename(filepath))[0]
            multitrack = Multitrack(beat_resolution=24, name=midi_name)

            pm = PrettyMIDI(filepath)
            midi_info = get_midi_info(pm)
            multitrack = Multitrack(filepath)
            merged = get_merged(multitrack)
            os.chdir(converter_root_dir)
            if not os.path.exists(converter_root_dir + '/' + genre):
                os.mkdir(converter_root_dir + '/' + genre)
            converter_path = converter_root_dir + '/' + genre + '/' + midi_name + '.npz'
            # merged.save(converter_path)
            print(get_midi_info(pm))
            '''
            midi_collection.update_one(
                {'_id', midi['_id']},
                {'$set' :{'Converted': True}}
            )
            '''
            # print([midi_name, midi_info])
        except:
            print(filepath)
            print(traceback.format_exc())
def extract_drums(msd_id: str) -> Optional[PrettyMIDI]:
  """
  Extracts a PrettyMIDI instance of all the merged drum tracks
  from the given MSD id.

  :param msd_id: the MSD id
  :return: the PrettyMIDI instance of the merged drum tracks
  """
  os.makedirs(args.path_output_dir, exist_ok=True)
  midi_md5 = get_matched_midi_md5(msd_id, MSD_SCORE_MATCHES)
  midi_path = get_midi_path(msd_id, midi_md5, args.path_dataset_dir)
  pm = PrettyMIDI(midi_path)
  pm_drums = copy.deepcopy(pm)
  pm_drums.instruments = [instrument for instrument in pm_drums.instruments
                          if instrument.is_drum]
  if len(pm_drums.instruments) > 1:
    # Some drum tracks are split, we can merge them
    drums = Instrument(program=0, is_drum=True)
    for instrument in pm_drums.instruments:
      for note in instrument.notes:
        drums.notes.append(note)
    pm_drums.instruments = [drums]
  if len(pm_drums.instruments) != 1:
    raise Exception(f"Invalid number of drums {msd_id}: "
                    f"{len(pm_drums.instruments)}")
  return pm_drums
Beispiel #25
0
 def __init__(self, path):
     pm = PrettyMIDI(path)
     pm.remove_invalid_notes()
     self.midi = pm
     self.tempo = pm.estimate_tempo()
     self.beat_times = pm.get_beats()
     self.bar_times = pm.get_downbeats()
     self.end_time = pm.get_end_time()
     self.instruments = pm.instruments
Beispiel #26
0
 def get_midi_file_list(self):
     midi_list = []
     for root, dirs, files in os.walk(self._midi_dir):
         for file in files:
             if fnmatch.fnmatch(file, "*.mid"):
                 midi_filename = os.path.join(root, file)
                 pretty_midi = PrettyMIDI(midi_filename)
                 midi_list.append(pretty_midi)
     return midi_list
 def _get_qpm(self, pm: PrettyMIDI):
     """
     Returns the first tempo change that is not zero, raises exception
     if not found or multiple tempo present.
     """
     if self._qpm:
         return self._qpm
     qpm = None
     for tempo_change in pm.get_tempo_changes():
         if tempo_change.min() and tempo_change.max() and tempo_change.min(
         ) == tempo_change.max():
             if qpm:
                 raise Exception(
                     "Multiple tempo changes are not supported " +
                     str(pm.get_tempo_changes()))
             qpm = tempo_change.min()
     if not qpm:
         raise Exception("Unknown qpm in: " + str(pm.get_tempo_changes()))
     return qpm
Beispiel #28
0
def test_encode_decode():
    mid_seq = MidiSequencer.from_file(PrettyMIDI("audio/rock.mid"))
    encoded = mid_seq.encode()
    decoded = MidiSequencer.decode(encoded)
    for mid_row, decoded_row in zip(mid_seq.pattern, decoded.pattern):
        for mid_note, decoded_note in zip(mid_row, decoded_row):
            if mid_note is None and decoded_note is None:
                assert mid_note is None
                assert decoded_note is None
            else:
                assert mid_note.pitch == decoded_note.pitch
                assert mid_note.start == decoded_note.start
Beispiel #29
0
def dump_midi(data, note_sets, path):
    midi_file = PrettyMIDI(resolution=220, initial_tempo=120)
    track = Instrument(0)
    time = 0

    # Shift first timing to 0
    #time -= note_sets['timing'][data[0][0]] * 30

    for note in data:
        # <padding> == 0
        if note[0] == 0:
            continue
        time += note_sets['timing'][note[0]] * 15 / 120
        track.notes.append(
            Note(velocity=100,
                 start=time,
                 end=time + note_sets['duration'][note[1]] * 15 / 120,
                 pitch=note_sets['pitch'][note[2]]))
        #print(track.notes[-1])
    midi_file.instruments.append(track)
    midi_file.write(path)
Beispiel #30
0
def write_to_midi(note_sequences, output_dir, n_to_write=None):

    if len(note_sequences) == 0:
        print("No note sequences to write out...")
        return
    #number of sequences to write out as MIDI files
    if n_to_write is None:
        n_to_write = len(note_sequences)
    #make the output directory if it doesn't already exist
    pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)
    for i in range(n_to_write):
        #for note_sequence in note_sequences:
        midi = PrettyMIDI(initial_tempo=80)
        piano = Instrument(program=0, is_drum=False, name="test{}".format(i))
        piano.notes = note_sequences[i]
        midi.instruments.append(piano)
        output_name = output_dir + "/test{}.midi".format(i)
        #with open(output_name, 'w')
        midi.write(output_name)
    print("Piano data successfully extracted from midis, navigate to {} to listen"\
            .format(output_dir))
Beispiel #31
0
def section(pm, start, end, index=None, exclude_drum=False):
    section_midi = PrettyMIDI()
    for i, instrument in enumerate(pm.instruments):
        if exclude_drum and instrument.is_drum: continue
        if index != None and i not in index: continue
        section_instrument = pretty_midi.Instrument(instrument.program)
        section_instrument.notes = [
            note for note in instrument.notes if start <= note.end < end
        ]
        section_midi.instruments.append(section_instrument)

    return section_midi
Beispiel #32
0
def identify_key(midi_filename, command_line_print = True, save_results = True, measure_value = 1):
  """ Runs key identification algorithm

  :parameters:
    - midi_filename : String of name of existing midi file to process.
    - command_line_print : Boolean to allow for printing results to command line.
    - save_results : Boolean to allow saving the approximated key and the names of those keys to a .npz file matching the midi files name
    - measure_value : int > 0 that sets how many beats the program will process per approximation.
    """

  song = PrettyMIDI(midi.read_midifile(midi_filename))

  #get beat locations for slices
  beats = song.get_beats() #output is an np.ndarray
  times = beats.flatten()
  sectionBeats = True
  #create slices of chroma data to process including summing them up
  #output: every "measure" of beats

  if measure_value< 1 or measure_value >times.size or measure_value == None:
    sectionBeats = False
    print "WARNING: measure_value selected less than 1 or greater than beat size.  Will do one approximation for the whole song."



  key_weight, names = load_keys()
  #get Chroma features
  chroma = song.get_chroma()
  #normalize chroma features
  chroma /= (chroma.max( axis = 0 ) + (chroma.max( axis = 0 ) == 0))

  # first case
  if sectionBeats:
    chroma_slice = chroma[:,0:round(times[0])*100]
  else:
    chroma_slice = chroma
  # Sum rows to find intensity of each note.
  vec = np.sum(chroma_slice, axis=1)
  keys_approx = get_approx_key(vec, key_weight)

  #for each slice, get approximated key and score into 2 column array (key, score)
  #possiblymay need to use indices of key names instead of actual keys
  #chroma time indices have a resolution of 10 ms

  times_used = np.array([[times[0]]])

  if sectionBeats:
    for t in range(1, times.size-1,measure_value):
    #make chroma slice based on time
      if times.size -t < measure_value:
        chroma_slice = chroma[:,round(times[t]*100):round(times[t+1]*100)]
      # print chroma_slice
      # vec = make_chroma_vector(chroma_slice)
        vec = np.sum(chroma_slice, axis=1)
      # vec = vec[::-1]
      else:
        chroma_slice = chroma[:,round(times[t]*100):round(times[t+1]*100)]
      # print chroma_slice
      # vec = make_chroma_vector(chroma_slice)
        vec = np.sum(chroma_slice, axis=1)
      # vec = vec[::-1]
      apr = get_approx_key(vec, key_weight)
      #if the score isn't 0 (which happens in silence), add the approximated key to the list
      if not apr[0,1] == 0:
        keys_approx = np.vstack((keys_approx, apr))
        times_used = np.vstack((times_used, np.array([[times[t]]])))


  # DUMMIED OUT CODE FOR FUTURE IMPLEMENTATION
  # final_keys = np.array([ [ keys_approx[0,0],times[0,0] ] ]) #mark first
  # print final_keys
  #
  # #iterate through rows of array- if there is a change, get % difference in scores for each key and use threshold to figure
  # #if it is a key change.  mark key change in final 2 column array of (key, time start)
  # threshold = .15 #experimental value
  #
  # if times.size > 1:
  #   print "going thru removal loop"
  #   for t in range (1, keys_approx.shape[0]):
  #     current = keys_approx[t,0]
  #     prev = keys_approx[t-1,0]
  #     if not current == prev: #if not equal to previous, check % difference of scores
  #       print "In key change check"
  #       score1 = keys_approx[t,1] #score of key of this time slice
  #       # print score1
  #       vec = make_chroma_vector(chroma, round(times[0,t])*100,round(times[0,t+1])*100 )
  #       # print vec
  #       score2 = get_key_score(vec, key_weight, prev) #score it would have gotten with last input key
  #       # print score2
  #       diff = abs(score1-score2)/(score1+score2)
  #       print diff
  #       if diff > threshold:
  #         arr = np.array([[keys_approx[t,0], times[t,0] ]])
  #         # print arr
  #         print "Key change at index: ", times[t,0]
  #         final_keys = np.vstack((final_keys, arr))
  #       else:
  #         print "difference not large enough to constitute key change "

  keys_approx = final_keys
  e = keys_approx.shape[0]
  if command_line_print:
    for i in range(0, e):
      key_int = int(keys_approx[i,0])
      print "In key: ",names[0,key_int],"at time: ", times_used[i,0]
  if save_results:
    filename = os.path.basename(midi_filename)
    filename_raw = os.path.splitext(filename)[0]
    # if not os.path.exists(directory):
    dirname = "Results_of_key_approximation"
    if not os.path.exists(dirname):
      os.makedirs(dirname)
    np.savez(dirname+"/"+filename_raw+"_key_approx-vars", keys_approx = keys_approx, names = names)
    file_results = open(dirname+"/"+filename_raw+"_key_approx-text_form.txt",'w')
    file_results.write(filename_raw+"\n")
    for i in range(0, e):
      key_int = int(keys_approx[i,0])
      file_results.write("In key: "+names[0,key_int]+" at time: "+ str(times_used[i,0])+"\n")
    file_results.close()
  return keys_approx, names