Esempio n. 1
0
def _best_sane_chord(preceding, current, following,
                     recent_notes, chord_index, limiting, net):
    r"""
    Given a current theoretical chord, find the best plausible form for it.

    * `preceding` is a chronological list of preceding chords in physical form.
    * `current` is the theoretical chord to be instantiated.
    * `following` is a chronological list of following chords.
    * `recent_notes` is a mapping from theoretical notes to physical notes;
       it indicates what the most recent instance of a particular note was.
    * `chord_index` is the chord number of `current`, e.g. 0 if `current` is
       the first chord.
    * `limiting` is the first "limiting" note part of a chord with an index
       strictly greater than `chord_index`.
    * `net` is the trained neural net that makes the final decision.
    """
    min_error = None
    best_config = None
    for sane in sane_chords(current):
        # TODO move "transformation" to sane_chords where it belongs
        transformed_sane = {}
        for sane_entry in sane:
            string_index = STANDARD_STRINGS.index(sane_entry[1])
            transformed_sane[string_index] = sane_entry[0]
        LOG.debug('Transformed: {transformed_sane}'.format(**locals()))
        error = _sane_chord_error(preceding, transformed_sane, following,
                                  recent_notes, chord_index, limiting, net)
        if min_error is None or error < min_error:
            min_error = error
            best_config = transformed_sane
            for elem in best_config:
                if elem > 24:
                    LOG.warning('Outside of fret range!')
    return best_config
Esempio n. 2
0
def _fret_string_combos(note):
    r"""
    Given a (theoretical) `note`, return all supported fret/string combos,
    along with flags indicating whether the returned values are usable.

    This assumes that a note can occur in no more than six locations.

    To ease testing, entries go from low strings to high strings.
    >>> _fret_string_combos(('E', 3))
    (12, 5, 1, 7, 4, 1, 2, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    """
    result_builder = []
    for location in frets(note):
        result_builder.append(location[0])
        result_builder.append(STANDARD_STRINGS.index(location[1]))
        result_builder.append(1)
    missing_values = 18 - len(result_builder)
    result_builder.extend([0] * missing_values)
    return tuple(result_builder)
Esempio n. 3
0
def annotate(midi_path, net):
    r"""
    Given a path to a generated midi,
    yield the best configuration for each chord.
    """
    preceding = []
    recent_notes = {}
    for (current, following, current_index, limiting) in midi_contexts(midi_path):
        # sane_chords actually uses note lists instead of canonical chords
        # this is okay for single-note melodies, though

        # TODO adjust called code so that this whole hack is not necessary
        LOG.debug('Following: {f}'.format(f=following))
        following_notelists = []
        for theor_following in following:
            theor_notelist = []
            for key in theor_following.keys():
                theor_notelist.extend([key] * theor_following[key])
            following_notelists.append(theor_notelist)
        LOG.debug('Following notelists: {f}'.format(f=following_notelists))
        phys_following = [next(sane_chords(successor)) for successor in following_notelists]
        LOG.debug('Phys following: {p}'.format(p=phys_following))
        phys_transformed = []
        for frozen in phys_following:
            el = next(iter(frozen))  # only one, anyway
            phys_transformed.append({STANDARD_STRINGS.index(el[1]): el[0]})
        LOG.debug('Phys transformed: {p}'.format(p=phys_transformed))

        best_config = _best_sane_chord(preceding, current, phys_transformed,
                                       recent_notes, current_index,
                                       limiting, net)
        yield best_config
        if len(preceding) >= CHORDS_BEFORE_CURRENT:
            del(preceding[0])
        preceding.append(best_config)
        for string_num in best_config:
            fret = best_config[string_num]
            recent_notes[fret2note(fret, string_num)] = (string_num, fret)
Esempio n. 4
0
def _sample_data(preceding, current_chord, following, recent_notes,
                 current_chord_index, limiting_note):
    r"""
    Transform a given chord context into an input-output pair for an ANN.

    Given a dataset and a chord context, this function yields pairs of
    input and output values *for each note in the current chord*.

    `recent_notes` is a dict-like object which, for each (theoretical)
    note, specifies the most recent fret and string values (bundled in
    an indexed sequence).

    `limiting_note`, if any value, is a tuple whose elements are:
    
       #. the next (theoretical) limiting note,
          i.e. the first note that comes after `current_chord` and
          which imposes strict constraints on where it can be played
       #. the chord index of that note

    Chords belonging to the chord context are specified in their physical
    form. This includes the current chord and successor chords.
    """

    LOG.debug('Current chord: {current}'.format(current=current_chord))
    highest_note_chord = _highest_note_chord(current_chord)
    lowest_note_chord = _lowest_note_chord(current_chord)
    avg_frets = _avg_frets(preceding)
    preceding_notes = _pad_physical_notes(_adjacent_notes(preceding, 4), 4)
    following_notes = _pad_physical_notes(_adjacent_notes(following, 6, fwd=True), 6)
    LOG.debug('Following notes: {f}'.format(f=following_notes))
    following_notes = [fret2note(phys[1], phys[0]) for phys in list(following_notes)]
    if limiting_note:
        to_limiting_note = limiting_note[1] - current_chord_index
        minimal_fret_limiting_note = None
        for fret in frets(limiting_note[0]):
            if minimal_fret_limiting_note is None or fret[0] < minimal_fret_limiting_note:
                minimal_fret_limiting_note = fret[0]
                minimal_string_limiting_note = STANDARD_STRINGS.index(fret[1])
        limiting_note_flag = 1
    else:
        to_limiting_note = 1000 # just use something so big it is irrelevant
        minimal_fret_limiting_note = 0
        minimal_string_limiting_note = 0
        limiting_note_flag = 0
    if preceding:
        num_notes_prev_chord = len(preceding[-1])
    else:
        num_notes_prev_chord = 0
    if len(preceding) > 1:
        num_notes_prev_chord_2 = len(preceding[-2])
    else:
        num_notes_prev_chord_2 = 0
    if following:
        num_notes_next_chord = len(following[0])
    else:
        num_notes_next_chord = 0
    if len(following) > 1:
        num_notes_next_chord_2 = len(following[1])
    else:
        num_notes_next_chord_2 = 0
    for string_num in sorted(current_chord.keys()):
        note = fret2note(current_chord[string_num], string_num)
        recent_note = recent_notes.get(note, (0, 0))
        fret_string_combos = _fret_string_combos(note)
        bits = [0] * SUPPORTED_FRETS
        bits[current_chord[string_num]] = 1  # fret alone is fine
        inputs = (# 1: notes in current chord
                  len(current_chord.keys()),
                  # 2-3: current note octave and value
                  int(note[1]),
                  NOTE_SEQUENCE.index(note[0]),
                  # 4-5: highest note in chord's octave and value
                  highest_note_chord[1],
                  NOTE_SEQUENCE.index(highest_note_chord[0]),
                  # 6-7: lowest note in chord's octave and value
                  lowest_note_chord[1],
                  NOTE_SEQUENCE.index(lowest_note_chord[0]),
                  # 8-10: average fret for three most recent chords
                  avg_frets[0],
                  avg_frets[1],
                  avg_frets[2],
                  # 11-12: fret and string for most recent occurrence note
                  recent_note[0],
                  recent_note[1],
                  # 13-20: string and fret for four most recent notes
                  preceding_notes[0][0],
                  preceding_notes[0][1],
                  preceding_notes[1][0],
                  preceding_notes[1][1],
                  preceding_notes[2][0],
                  preceding_notes[2][1],
                  preceding_notes[3][0],
                  preceding_notes[3][1],
                  # 21: number of chords played since last four notes
                  _chords_for_notes(4, preceding, fwd=False),
                  # 22-23: number of notes in previous two chords
                  num_notes_prev_chord,
                  num_notes_prev_chord_2,
                  # 24-29: booleans indicating whether string are in use
                  int(0 in current_chord),
                  int(1 in current_chord),
                  int(2 in current_chord),
                  int(3 in current_chord),
                  int(4 in current_chord),
                  int(5 in current_chord),
                  # 30-47: all string/fret combinations for the current note
                  # **ADDED** each third entry indicates whether combo is real
                  # **ADDED** supports up to 24 real frets, so 6 locations
                  fret_string_combos[0],
                  fret_string_combos[1],
                  fret_string_combos[2],
                  fret_string_combos[3],
                  fret_string_combos[4],
                  fret_string_combos[5],
                  fret_string_combos[6],
                  fret_string_combos[7],
                  fret_string_combos[8],
                  fret_string_combos[9],
                  fret_string_combos[10],
                  fret_string_combos[11],
                  fret_string_combos[12],
                  fret_string_combos[13],
                  fret_string_combos[14],
                  fret_string_combos[15],
                  fret_string_combos[16],
                  fret_string_combos[17],
                  # 48-49: number of notes in next two chords
                  num_notes_next_chord,
                  num_notes_next_chord_2,
                  # 50-61: octave and note for next six notes
                  NOTE_SEQUENCE.index(following_notes[0][0]),
                  following_notes[0][1],
                  NOTE_SEQUENCE.index(following_notes[1][0]),
                  following_notes[1][1],
                  NOTE_SEQUENCE.index(following_notes[2][0]),
                  following_notes[2][1],
                  NOTE_SEQUENCE.index(following_notes[3][0]),
                  following_notes[3][1],
                  NOTE_SEQUENCE.index(following_notes[4][0]),
                  following_notes[4][1],
                  NOTE_SEQUENCE.index(following_notes[5][0]),
                  following_notes[5][1],
                  # 62: number of chords between current note and next six notes
                  _chords_for_notes(6, following, fwd=True),
                  # 63-65: fret/string furthest from body for limiting note
                  minimal_fret_limiting_note,
                  minimal_string_limiting_note,
                  limiting_note_flag,
                  # 66: number of chords until next limiting note
                  to_limiting_note,
               )
        sample = (inputs, tuple(bits))
        LOG.debug('Yielding sample: {sample}'.format(sample=sample))
        yield sample