Polyrhythm Transcription Issue

Description:

Hello everyone!

I’ve run into an issue with changing durations in Scamp. For most fractions, I am not having an issue, but for fractions with a denominator of 5, 6, or 7, I am running into problems. The MIDI playback is fine, but I am unable to transcribe everything and create a PDF, getting the error message:

Exception: can not attach VoiceNumber(n=1, leak=False) to containers: Tuplet(‘5:4’, "fqs’8 fqs’8 eqf’16”)

I have set max_divisor=16 but am still running into this issue. Does anyone have recommendations for how I can create polyrhythms more complicated than triplets? The problem is in line 62 and starts when j = 14 (line 54)

Thank you!

Error printout:

Traceback (most recent call last):
  File "/Users/johndoe/Scamp Polyrhythm Help.py", line 62, in <module>
    s.stop_transcribing().to_score(title= mensurations[j], max_divisor=16).show()
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/scamp/score.py", line 652, in show
    abjad().show(self.to_abjad(wrap_as_file=True, non_score_blocks=non_score_blocks, **lilypond_file_args))
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/scamp/score.py", line 1203, in to_abjad
    return ScoreComponent.to_abjad(self, wrap_as_file=wrap_as_file, non_score_blocks=non_score_blocks,
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/scamp/score.py", line 519, in to_abjad
    return self._to_abjad_lilypond_file(non_score_blocks=non_score_blocks, **lilypond_file_args)
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/scamp/score.py", line 543, in _to_abjad_lilypond_file
    abjad_object = self._to_abjad()
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/scamp/score.py", line 971, in _to_abjad
    abjad_score = abjad().Score([part._to_abjad() for part in self.parts])
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/scamp/score.py", line 971, in <listcomp>
    abjad_score = abjad().Score([part._to_abjad() for part in self.parts])
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/scamp/score.py", line 1592, in _to_abjad
    contents = [measure._to_abjad(source_id_dict) for measure in self.measures]
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/scamp/score.py", line 1592, in <listcomp>
    contents = [measure._to_abjad(source_id_dict) for measure in self.measures]
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/scamp/score.py", line 1729, in _to_abjad
    abjad().attach(abjad().VoiceNumber(n=i+1), abjad_voice[0])
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/abjad/bind.py", line 1125, in attach
    result = _unsafe_attach(
  File "/Users/johndoe/Library/Python/3.10/lib/python/site-packages/abjad/bind.py", line 799, in _unsafe_attach
    raise Exception(message)
Exception: can not attach VoiceNumber(n=1, leak=False) to containers: Tuplet('5:4', "fqs'8 fqs'8 eqf'16")

The full code that resulted in the error:

from scamp import *

s = Session()
s.tempo = 10000
s.fast_forward_in_beats(1000)
engraving_settings.show_microtonal_annotations = True

voice = s.new_part("choir aah")

def is_constant(p1, p2):
    if abs(p1 - p2) % 12 in [(0, 0.14), (2.9564, 3.3564), (3.7231, 4.0531), (6.8796, 7.1596), (7.9469, 8.2769), (8.6436, 9.0436)]:
        return True
    else:
        return False


class Melody:
    def __init__(self, notes, speed):
        self.notes = notes
        self.speed = speed  
cantus_firmus = Melody([62, 65.5, 63.5, 62, 65.5, 69, 67, 71, 69, 67, 63.5, 65.5, 63.5, 62], [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0])
counterpoint1 = Melody([62, 65.5, 63.5, 62, 65.5, 69, 67, 71, 69, 67, 63.5, 65.5, 63.5, 62], [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0])


def oneroundofmensuration(x, y):
    i = 0
    while i < len(counterpoint1.notes):
        counterpoint1.notes[i] += x
        counterpoint1.speed[i] *= y
        i += 1
    def playcantus_firmus():
        k = 0
        while k < len(cantus_firmus.notes):
            voice.play_note(cantus_firmus.notes[k], 1.0, cantus_firmus.speed[k])
            k += 1
    def playcounterpoint1():
        k = 0
        while k < len(counterpoint1.notes):
            voice.play_note(counterpoint1.notes[k], 1.0, counterpoint1.speed[k])
            k += 1
    fork(playcantus_firmus)
    fork(playcounterpoint1)
    wait_for_children_to_finish()
    j = 0
    while j < len(counterpoint1.notes):
        counterpoint1.notes[j] -= x
        counterpoint1.speed[j] /= y
        j += 1

transpositions = [0, 3, 4, 7, 8, 9, 12, 15, 16, 19, 20, 21, 24, -3, -4, -7, -8, -9, -12, -15, -16, -19, -20, -21, -24]
mensurations = [2/1, 3/1, 4/1, 5/1, 6/1, 7/1, 3/2, 5/2, 7/2, 4/3, 5/3, 7/3, 5/4, 7/4, 6/5, 7/5, 7/6, 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 2/3, 2/5, 2/7, 3/4, 3/5, 3/7, 4/5, 4/7, 5/6, 5/7, 6/7]

i = 0
j = 14

while j < len(transpositions):
    while i < len(transpositions):
        s.start_transcribing()
        print(mensurations[j])
        oneroundofmensuration(transpositions[i], mensurations[j])
        print("place5", mensurations[j])
        s.stop_transcribing().to_score(title= mensurations[j], max_divisor=16).show()
        print("place6", mensurations[j])
        i += 1
    i = 0
    j += 1