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
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.")
Exemple #3
0
 def test_empty_block(self, mock_cross_section):
     mock_cross_section.return_value = (None, None)
     expected = []
     for pair in zip(expected, parse.chords('')):
         self.assertEqual(pair[1], pair[0])
Exemple #4
0
 def test_sequence(self, mock_cross_section):
     mock_cross_section.side_effect = [({0 : 3, 2 : 2}, 5),
                                       ({0 : 2}, 13) , None]
     expected = [{0 : 3, 2 : 2}, {0 : 2},]
     for pair in zip(expected, parse.chords(self.block)):
         self.assertEqual(pair[1], pair[0])