score.py - ValueError: math domain error
Opened this issue · 4 comments
I was revisiting code that I don't recall giving this error before. I recently updated to SCAMP version 0.8.9.5.
You may recall helping me with a pseudo-Celtic random song generator. That's what's crapping out. It plays the tune, but upon finishing it yields the following when trying to generate the score:
Traceback (most recent call last):
File "./celtic.py", line 99, in <module>
main()
File "./celtic.py", line 93, in main
performance.to_score(title="Beating the Cat",
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/performance.py", line 1094, in to_score
return Score.from_performance(
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 934, in from_performance
return Score.from_quantized_performance(
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 956, in from_quantized_performance
staff_group = StaffGroup.from_quantized_performance_part(part)
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 1344, in from_quantized_performance_part
return StaffGroup._from_measure_voice_grid(
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 1559, in _from_measure_voice_grid
[
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 1560, in <listcomp>
Staff._from_measure_bins_of_voice_lists(
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 1654, in _from_measure_bins_of_voice_lists
return cls([Measure.from_list_of_performance_voices(measure_content, time_signature, show_time_signature)
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 1654, in <listcomp>
return cls([Measure.from_list_of_performance_voices(measure_content, time_signature, show_time_signature)
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 1762, in from_list_of_performance_voices
voices.append(Voice.from_performance_voice(*voice_content))
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 1926, in from_performance_voice
processed_contents = Voice._recombine_processed_beats(processed_beats, measure_quantization)
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 2163, in _recombine_processed_beats
recombined_division_points = Voice._try_all_sub_recombinations(
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 2185, in _try_all_sub_recombinations
recombo, _ = _get_best_recombination_given_beat_hierarchy(
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 216, in _get_best_recombination_given_beat_hierarchy
best_subgroup_option, best_subgroup_score = _get_best_subgroup_recombination_option(
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 242, in _get_best_subgroup_recombination_option
assert all(_is_single_note_viable_grouping(x, max_dots=engraving_settings.max_dots_allowed)
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 242, in <genexpr>
assert all(_is_single_note_viable_grouping(x, max_dots=engraving_settings.max_dots_allowed)
File "/home/kjcole/.local/lib/python3.8/site-packages/scamp/score.py", line 175, in _is_single_note_viable_grouping
if Fraction(math.log2(length_in_subdivisions / dot_multiplier)).limit_denominator().denominator == 1:
ValueError: math domain error
Hey Kevin!
Can you post the script that's not working? It's a little hard to tell without seeing your code, but it looks like somehow it's getting a negative note length or something.
GitHub doesn't like Python attachments. (I suppose I could have just changed the extension, but here it is.)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# "Beating the Cat"
# Decomposed by Kevin Cole <ubuntourist@hacdc.org> 2021.02.19 (kjc)
#
# Tuning info courtesy of http://www.hotpipes.com/tuning.html
#
# Sorta 6/8?
#
from random import choice, randint, shuffle, sample, uniform
from scamp import *
from scamp_extensions.pitch import Scale
s = Session(tempo=120)
woodblock = s.new_part("woodblock")
bagpipe = s.new_part("bagpipe")
bodhran = s.new_part("bodhran",
soundfont="soundfonts/Bodhran") # Bodhran.sf2
# G4 A4 B4 C#5 D5 E5 F#5 G5 A5
chanter = (67, 69, 71, 73, 74, 76, 78, 79, 81) # A4 is the "key note"
scale = Scale.from_pitches(chanter)
def rickity(duration=32, shift=0):
"""Clickity-clackity, rickity-tickity, pseudo-bones"""
pitches = ( 75, 73, 67, 77, 70, 65)
volumes = ( 0.75, 0.75, 0.75, 1.0, 0.75, 0.75)
lengths = ((1.0 / 3.0),) * 6 # 0.333333..., 0.333333..., ...
while current_clock().beat() < duration:
for pitch, volume, length in zip(pitches, volumes, lengths):
woodblock.play_note(pitch + shift, volume, length)
def boombah(duration=32):
"""Boom-bah of the bodhran"""
notes = ((60, 58, 58, 60, 58, 58),
(60, 58) * 2,
(60, 58))
vols = ((0.75, 0.75, 0.75, 1.0, 0.75, 0.75),
(1.0, 0.75,) * 2,
(1.0, 0.75))
durs = (((1.0 / 3.0),) * 6, # 0.3333..., 0.3333..., ...
((2.0 / 3.0), (1.0 / 3.0)) * 2, # 0.6666..., 0.3333..., 0.6666...
((4.0 / 3.0), (2.0 / 3.0))) # 1.3333..., 0.6666
wait(4)
while current_clock().beat() < duration:
pattern = randint(0, 2)
for note, vol, dur in zip(notes[pattern],
vols[pattern],
durs[pattern]):
bodhran.play_note(note, vol, dur)
def pipe(duration=32):
"""The pipes, the pipes, are droning"""
# Drones chord
#
drones = (57, 57, 45) # A2, A2, A3 (tenor, tenor, bass)
wait(8)
drone = bagpipe.start_chord(drones, 0.8)
wait(4)
note = choice(chanter)
while current_clock().beat() < duration:
dur = randint(1, 6) * (1.0 / 3.0)
bagpipe.play_note(note, 1.0, dur)
leading = note
while abs(note - leading) in (0, 1, 2, 3, 6, 8, 10, 11, 13, 14, 15):
note = choice(chanter)
# Deflate the bag
#
drone.end()
bagpipe.play_note([81, 76], [0.6, 0], 1.0, blocking=False)
bagpipe.play_note([57, 52], [0.6, 0], 1.0, blocking=False)
bagpipe.play_note([45, 40], [0.8, 0], 1.0)
def main():
"""The main attraction"""
s.start_transcribing()
s.fork(rickity, args=(42, 30,))
s.fork(boombah, args=(44,))
s.fork(pipe, args=(40,))
s.wait_for_children_to_finish()
if s.is_transcribing():
performance = s.stop_transcribing()
performance.to_score(title="Beating the Cat",
composer="Decomposer: Kevin Cole",
time_signature="6/8").show_xml()
if __name__ == "__main__":
main()
Well, I tracked down the source of the issue! You're doing something very strange by accident here: the boombah part is using triplets, but in the context of 6/8 time. This creates a weird situation for the quantizer, since it's trying to make sense of 4/3 of a quarter note within dotted-quarter-note beats. I need to fix something here for sure, but I'm guessing that the triplets may not be what you're really intending?