예제 #1
0
def limiting_notes(block):
    r"""
    Given a block, generate all the limiting notes and the index of the chord
    they belong to.

    Note that, for the sake of predictability, notes within the same chord are
    generated based on their string order: limiting notes on a high string are
    yielded before limiting notes on a low string within the same chord.
    >>> limiting_notes(['E|-0', 'B|-0', 'G|-0', 'D|-0', 'A|-0', 'E|-0']).next()
    (('E', 2), 0)
    """
    chord_index = 0
    for chord in chords(block):
        for string_num in sorted(chord.keys()):
            note = fret2note(chord[string_num], string_num)
            if is_limiting(note):
                yield (note, chord_index)
        chord_index += 1
예제 #2
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)
예제 #3
0
 def test_over_one_octave(self):
     expected = ('A', 4)
     self.assertEqual(notemappings.fret2note(14, 2), expected)
예제 #4
0
 def test_one_octave(self):
     expected = ('E', 3)
     self.assertEqual(notemappings.fret2note(12, 5), expected)
예제 #5
0
 def test_open_string(self):
     expected = ('E', 2)
     self.assertEqual(notemappings.fret2note(0, 5), expected)
예제 #6
0
def chord_contexts(block):
    r"""
    Generate all the successive chord "contexts" that occur in the piece.

    There is *one context for each chord*.
    A context consists of:
    
       #. an ordered sequence of up to `CHORDS_BEFORE_CURRENT` preceding chords
       #. a current chord
       #. up to `CHORDS_AFTER_CURRENT` successor chords
       #. a mapping from notes to (fret, string) pairs indicating the notes'
          most recent occurrence
       #. the instance index of the chord context; this is useful to calculate
          the weight of a possible limiting note that follows
       #. the first limiting note that follows the current chord, if any

    The preceding, current and successor chords are given in physical form.
    **Note that in actual use, the successor chords can only be theoretical.**

    Note that the tuple returned consists of copies of the data, so it can
    safely be modified by client code.
    """
    chord = 0
    chord_deque = []
    generator = chords(block)
    limit_generator = limiting_notes(block)
    # first set up the chords that come after the first "current" chord
    while chord <= CHORDS_AFTER_CURRENT:
        try:
            generated = generator.next()
            chord_deque.append(generated)
            chord += 1
        except StopIteration:
            break
    # now act like a shift register, but start with first "current" chord
    chord = 0  # the index in the deque - not the instance yielded
    yielded = 0  # the instance yielded, so monotonically increasing
    recent_notes = {}
    limiting_note = None
    while chord < len(chord_deque):
        current_chord = chord_deque[chord]
        LOG.debug("Yielding current: {current_chord}".format(**locals()))
        while True:
            try:
                limiting_note = limit_generator.next()
                if limiting_note[1] > yielded:
                    break
            except StopIteration:
                limiting_note = None
                break
        yield (
            chord_deque[0:chord],
            dict(current_chord),
            chord_deque[chord + 1 :],
            dict(recent_notes),
            yielded,
            limiting_note,
        )
        yielded += 1
        for string_num in current_chord:
            note = fret2note(current_chord[string_num], string_num)
            LOG.debug("Memorizing note/fret for {note}".format(**locals()))
            recent_notes[note] = (current_chord[string_num], string_num)
        if chord < CHORDS_BEFORE_CURRENT:
            chord += 1
        else:
            del (chord_deque[0])
        try:
            generated = generator.next()
            chord_deque.append(generated)
        except StopIteration:
            pass
    LOG.debug("Returning from context generator.")
예제 #7
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