This commit is contained in:
khodowany
2026-10-04 15:51:18 -05:00
parent 405066eb43
commit f234dd6d51
54 changed files with 2104 additions and 330 deletions
+9
View File
@@ -0,0 +1,9 @@
# Independent source excerpts
These are **evaluation-only original-song inputs**, not expected solos, production vocabulary, or the four target solos. They are deliberately outside `data/` and are never loaded by ordinary generation or required tests. Source provenance is beside each XML. The original 1917/1918 editions are from Johns Hopkins' Levy collection.
The manually transcribed first four chorus measures retain the vocal rhythm, including tied values. Chord symbols are our declared harmonic reductions of the piano score. These short excerpts do not establish whole-form generalization. Preceding pickups/verses and piano figuration are outside the excerpt scope. Original scans remain in ignored `build/composer_repair/405066e/evaluation_sources/` for review.
St. Louis Blues was considered, but the institutional original-score download was unavailable (403). Hindustan supplies the third independent source instead; no target arrangement was used to reconstruct missing music.
Run `python -m audition.transfer_review --output build/transfer_review` through the repository Python runner. This runs the normal independent generators; no tuning/selection result flows back into production.
@@ -0,0 +1,13 @@
{
"title": "After You\u2019ve Gone",
"published": 1918,
"source": "https://levysheetmusic.mse.jhu.edu/collection/151/004",
"printed_page": 3,
"scope": "First four full chorus measures; preceding pickup omitted from this excerpt, not silently folded into the chorus. Not a complete standard/form transcription.",
"harmony": "Independently authored chord-symbol reduction of the original piano harmony; ornamental piano bass motion is not transcribed. Not a claim of a definitive modern jazz chart.",
"pitch": "Original vocal treble encoding, sounding; no guitar octave shift.",
"tempo": "108 quarter notes/minute is a test setting, not inferred performance evidence.",
"provenance": "Manual source-melody transcription from the original published score, after production vocabulary was authored. No target solo input.",
"rights": "Original 1917/1918 US published composition, public-domain age in the United States. New transcription/reduction: MIT. Original scans are not redistributed. Other jurisdictions not assessed.",
"archive_rights": "https://www.library.jhu.edu/policies/rights-and-reproductions/"
}
@@ -0,0 +1,37 @@
<?xml version="1.0"?><score-partwise version="4.0"><work><work-title>After You’ve Gone</work-title></work><part-list><score-part id="P1"><part-name>Source melody</part-name></score-part></part-list><part id="P1">
<measure number="1">
<attributes><divisions>4</divisions><key><fifths>0</fifths></key><time><beats>4</beats><beat-type>4</beat-type></time><clef><sign>G</sign><line>2</line></clef></attributes><direction><direction-type><metronome><beat-unit>quarter</beat-unit><per-minute>108</per-minute></metronome></direction-type><sound tempo="108"/></direction>
<harmony><root><root-step>F</root-step></root><kind>major</kind></harmony>
<note><pitch><step>A</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>C</step><alter>0</alter><octave>5</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>D</step><alter>0</alter><octave>5</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>E</step><alter>0</alter><octave>5</octave></pitch><duration>2</duration><tie type="start"/><type>eighth</type><notations><tied type="start"/></notations></note>
<note><pitch><step>E</step><alter>0</alter><octave>5</octave></pitch><duration>8</duration><tie type="stop"/><type>half</type><notations><tied type="stop"/></notations></note>
</measure>
<measure number="2">
<harmony><root><root-step>F</root-step></root><kind>minor</kind></harmony>
<note><rest/><duration>2</duration><type>eighth</type></note>
<note><pitch><step>D</step><alter>0</alter><octave>5</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>E</step><alter>0</alter><octave>5</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>D</step><alter>0</alter><octave>5</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>E</step><alter>0</alter><octave>5</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>D</step><alter>0</alter><octave>5</octave></pitch><duration>6</duration><type>quarter</type><dot/></note>
</measure>
<measure number="3">
<harmony><root><root-step>C</root-step></root><kind>major</kind></harmony>
<note><pitch><step>E</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>G</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>A</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>B</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><tie type="start"/><type>eighth</type><notations><tied type="start"/></notations></note>
<note><pitch><step>B</step><alter>0</alter><octave>4</octave></pitch><duration>8</duration><tie type="stop"/><type>half</type><notations><tied type="stop"/></notations></note>
</measure>
<measure number="4">
<harmony><root><root-step>E</root-step></root><kind>dominant</kind></harmony>
<note><rest/><duration>2</duration><type>eighth</type></note>
<note><pitch><step>A</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>B</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>A</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>B</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>A</step><alter>0</alter><octave>4</octave></pitch><duration>6</duration><type>quarter</type><dot/></note>
</measure>
</part></score-partwise>
@@ -0,0 +1,13 @@
{
"title": "Hindustan",
"published": 1918,
"source": "https://levysheetmusic.mse.jhu.edu/collection/152/084",
"printed_page": 3,
"scope": "First four full chorus measures; preceding pickup omitted from this excerpt, not silently folded into the chorus. Not a complete standard/form transcription.",
"harmony": "Independently authored chord-symbol reduction of the original piano harmony; ornamental piano bass motion is not transcribed. Not a claim of a definitive modern jazz chart.",
"pitch": "Original vocal treble encoding, sounding; no guitar octave shift.",
"tempo": "108 quarter notes/minute is a test setting, not inferred performance evidence.",
"provenance": "Manual source-melody transcription from the original published score, after production vocabulary was authored. No target solo input.",
"rights": "Original 1917/1918 US published composition, public-domain age in the United States. New transcription/reduction: MIT. Original scans are not redistributed. Other jurisdictions not assessed.",
"archive_rights": "https://www.library.jhu.edu/policies/rights-and-reproductions/"
}
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0"?><score-partwise version="4.0"><work><work-title>Hindustan</work-title></work><part-list><score-part id="P1"><part-name>Source melody</part-name></score-part></part-list><part id="P1">
<measure number="1">
<attributes><divisions>4</divisions><key><fifths>0</fifths></key><time><beats>4</beats><beat-type>4</beat-type></time><clef><sign>G</sign><line>2</line></clef></attributes><direction><direction-type><metronome><beat-unit>quarter</beat-unit><per-minute>108</per-minute></metronome></direction-type><sound tempo="108"/></direction>
<harmony><root><root-step>C</root-step></root><kind>major</kind></harmony>
<note><pitch><step>C</step><alter>0</alter><octave>5</octave></pitch><duration>16</duration><tie type="start"/><type>whole</type><notations><tied type="start"/></notations></note>
</measure>
<measure number="2">
<harmony><root><root-step>C</root-step></root><kind>major</kind></harmony>
<note><pitch><step>C</step><alter>0</alter><octave>5</octave></pitch><duration>8</duration><tie type="stop"/><type>half</type><notations><tied type="stop"/></notations></note>
<note><pitch><step>A</step><alter>0</alter><octave>4</octave></pitch><duration>8</duration><type>half</type></note>
</measure>
<measure number="3">
<harmony><root><root-step>G</root-step></root><kind>dominant</kind></harmony>
<note><pitch><step>G</step><alter>0</alter><octave>4</octave></pitch><duration>16</duration><tie type="start"/><type>whole</type><notations><tied type="start"/></notations></note>
</measure>
<measure number="4">
<harmony><root><root-step>G</root-step></root><kind>dominant</kind></harmony>
<note><pitch><step>G</step><alter>0</alter><octave>4</octave></pitch><duration>8</duration><tie type="stop"/><type>half</type><notations><tied type="stop"/></notations></note>
<note><pitch><step>E</step><alter>0</alter><octave>4</octave></pitch><duration>4</duration><type>quarter</type></note>
<note><pitch><step>G</step><alter>0</alter><octave>4</octave></pitch><duration>4</duration><type>quarter</type></note>
</measure>
</part></score-partwise>
@@ -0,0 +1,13 @@
{
"title": "Indiana",
"published": 1917,
"source": "https://levysheetmusic.mse.jhu.edu/collection/152/200",
"printed_page": 3,
"scope": "First four full chorus measures; preceding pickup omitted from this excerpt, not silently folded into the chorus. Not a complete standard/form transcription.",
"harmony": "Independently authored chord-symbol reduction of the original piano harmony; ornamental piano bass motion is not transcribed. Not a claim of a definitive modern jazz chart.",
"pitch": "Original vocal treble encoding, sounding; no guitar octave shift.",
"tempo": "108 quarter notes/minute is a test setting, not inferred performance evidence.",
"provenance": "Manual source-melody transcription from the original published score, after production vocabulary was authored. No target solo input.",
"rights": "Original 1917/1918 US published composition, public-domain age in the United States. New transcription/reduction: MIT. Original scans are not redistributed. Other jurisdictions not assessed.",
"archive_rights": "https://www.library.jhu.edu/policies/rights-and-reproductions/"
}
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0"?><score-partwise version="4.0"><work><work-title>Indiana</work-title></work><part-list><score-part id="P1"><part-name>Source melody</part-name></score-part></part-list><part id="P1">
<measure number="1">
<attributes><divisions>4</divisions><key><fifths>1</fifths></key><time><beats>4</beats><beat-type>4</beat-type></time><clef><sign>G</sign><line>2</line></clef></attributes><direction><direction-type><metronome><beat-unit>quarter</beat-unit><per-minute>108</per-minute></metronome></direction-type><sound tempo="108"/></direction>
<harmony><root><root-step>G</root-step></root><kind>major</kind></harmony>
<note><pitch><step>D</step><alter>0</alter><octave>5</octave></pitch><duration>8</duration><tie type="start"/><type>half</type><notations><tied type="start"/></notations></note>
<note><pitch><step>D</step><alter>0</alter><octave>5</octave></pitch><duration>3</duration><tie type="stop"/><type>eighth</type><dot/><notations><tied type="stop"/></notations></note>
<note><pitch><step>E</step><alter>0</alter><octave>5</octave></pitch><duration>1</duration><type>16th</type></note>
<note><pitch><step>D</step><alter>0</alter><octave>5</octave></pitch><duration>3</duration><type>eighth</type><dot/></note>
<note><pitch><step>B</step><alter>0</alter><octave>4</octave></pitch><duration>1</duration><type>16th</type></note>
</measure>
<measure number="2">
<harmony><root><root-step>A</root-step></root><kind>dominant</kind></harmony>
<note><pitch><step>A</step><alter>0</alter><octave>4</octave></pitch><duration>4</duration><type>quarter</type></note>
<note><pitch><step>E</step><alter>0</alter><octave>4</octave></pitch><duration>8</duration><type>half</type></note>
<note><pitch><step>F</step><alter>1</alter><octave>4</octave></pitch><duration>3</duration><type>eighth</type><dot/></note>
<note><pitch><step>G</step><alter>0</alter><octave>4</octave></pitch><duration>1</duration><type>16th</type></note>
</measure>
<measure number="3">
<harmony><root><root-step>D</root-step></root><kind>dominant</kind></harmony>
<note><pitch><step>B</step><alter>0</alter><octave>4</octave></pitch><duration>6</duration><type>quarter</type><dot/></note>
<note><pitch><step>G</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
<note><pitch><step>B</step><alter>0</alter><octave>4</octave></pitch><duration>6</duration><type>quarter</type><dot/></note>
<note><pitch><step>A</step><alter>0</alter><octave>4</octave></pitch><duration>2</duration><type>eighth</type></note>
</measure>
<measure number="4">
<harmony><root><root-step>G</root-step></root><kind>major</kind></harmony>
<note><pitch><step>G</step><alter>0</alter><octave>4</octave></pitch><duration>8</duration><tie type="start"/><type>half</type><notations><tied type="start"/></notations></note>
<note><pitch><step>G</step><alter>0</alter><octave>4</octave></pitch><duration>3</duration><tie type="stop"/><type>eighth</type><dot/><notations><tied type="stop"/></notations></note>
<note><pitch><step>G</step><alter>0</alter><octave>4</octave></pitch><duration>1</duration><type>16th</type></note>
<note><pitch><step>F</step><alter>1</alter><octave>4</octave></pitch><duration>3</duration><type>eighth</type><dot/></note>
<note><pitch><step>F</step><alter>0</alter><octave>4</octave></pitch><duration>1</duration><type>16th</type></note>
</measure>
</part></score-partwise>
+177
View File
@@ -0,0 +1,177 @@
"""Independent musical fixtures; optional target scores are never read."""
import dataclasses
from fractions import Fraction as F
import unittest
from generator.solo import groups, scoring, types, harmonic, themes, realization, profiles
from tests.test_solo_development import note
from tests.test_solo_passages import choices
from tests.test_solo_themes import melody
import tests.solo_fixtures
import tests.rhythm_fixtures
def evaluated(notes,harmony,**kwargs):
motif=types.Motif('authored',(),F(4),(),'test only')
plan=types.PhrasePlan('test',F(),F(4),motif,'continue','thematic_recomposition',None,(41,76),F(1),F(),
(types.ActivitySpan(F(),F(4),'active',F(1),F()),),ending='connected',closure='open')
draft=types.MelodicDraft('test',F(),F(4),tuple(notes),(),(),(),(),None,'continue')
return scoring.musical(plan,draft,notes,types.ContinuationState(''),harmony,**kwargs)
class RhythmGroups(unittest.TestCase):
def test_complete_triplet_can_be_displaced_without_offsetting_binary_tail(self):
pattern=((F(1,2),F(5,6)),(F(5,6),F(7,6)),(F(7,6),F(3,2)),(F(3,2),F(5,2)))
result=groups.describe(pattern)
self.assertEqual(result[0].family,'triplet')
self.assertEqual(result[-1].family,'binary')
self.assertEqual(result[0].displacement,F(1,2))
def test_partial_triplet_does_not_drift_into_binary(self):
self.assertFalse(groups.valid(((F(),F(1,3)),(F(1,3),F(5,6)))))
self.assertFalse(groups.valid(((F(1,3),F(4,3)),)))
self.assertEqual(groups.describe(((F(1,3),F(4,3)),),imported=True)[0].family,'source')
def test_relative_rhythm_identity_does_not_count_absolute_onsets(self):
original=melody(((0,1,'c'),(1,2,'e'),(2,3,'d')))
self.assertEqual(len(set(groups.signature(original))),1)
moved=tuple(dataclasses.replace(n,start=n.start+13,end=n.end+13) for n in original)
self.assertEqual(groups.signature(original),groups.signature(moved))
def test_broadening_never_cuts_inside_tuplet(self):
pattern=((F(),F(1,3)),(F(1,3),F(2,3)),(F(2,3),F(1)))
self.assertEqual(groups.broaden(pattern,F(1,2),F(4)),())
class EvaluationContract(unittest.TestCase):
def test_sum_and_configured_weights_are_authoritative(self):
h=harmonic.Harmony(choices(('C7',)),profiles.Instrument(reference=48))
ns=(note(0,1,'g'),note(2,3,'f'),note(3,4,'e'))
base=evaluated(ns,h,travel=2000)
self.assertTrue(base.valid)
self.assertAlmostEqual(base.total,sum(base.contributions.values()))
for key,value in base.raw.items():
changed=evaluated(ns,h,travel=2000,weights={**base.weights,key:base.weights[key]+1})
self.assertAlmostEqual(changed.total-base.total,value)
self.assertGreater(base.raw['activity'],0)
self.assertGreater(base.raw['harmony'],0)
self.assertGreater(base.raw['register'],0)
def test_ties_are_partition_invariant_and_silence_cannot_win(self):
h=harmonic.Harmony(choices(('C7',)))
whole=(note(0,4,'e',event_id='a'),)
split=(note(0,2,'e',event_id='a'),note(2,4,'e',event_id='a',continuation=True))
self.assertEqual(evaluated(whole,h),evaluated(split,h))
self.assertFalse(evaluated((),h).valid)
def test_computational_end_is_not_a_required_chord_member(self):
h=harmonic.Harmony(choices(('C7',)))
ns=(note(0,1,'g'),note(1,2,'e'),note(2,3,'f'),note(3,4,'a'))
self.assertTrue(evaluated(ns,h).valid)
class PendingTreatments(unittest.TestCase):
def test_complete_route_can_cross_a_search_frontier(self):
from tests.test_independent_generation import TypedExpressionContracts
fixture=TypedExpressionContracts();fixture.setUp();notes,contract=fixture.make()
partial=fixture.h.evaluate(notes[:2],until=F(2),complete=False,expressions=(contract,))
self.assertTrue(partial.valid,partial.error)
self.assertEqual(partial.pending,(contract,))
self.assertFalse(fixture.h.evaluate(notes[:2],until=F(3),expressions=(contract,)).valid)
complete=fixture.h.evaluate(notes,until=F(4),complete=True,expressions=(contract,))
self.assertTrue(complete.valid);self.assertFalse(complete.pending)
class Reachability(unittest.TestCase):
def test_all_development_operations_are_requested(self):
notes=melody(((0,1,'c'),(1,2,'e'),(2,3,'d'),(3,4,'e')))
home=types.RealizedMotif('home','source',notes,F(4),'introduce','contour',signature=notes)
state=types.ContinuationState('',home=home,notes=notes,last=notes[-1])
ops=themes.operation_choices(home,state,F(4),F(4),False)
self.assertTrue({'answer','extend','fragment','continue','return'}.issubset(ops))
h=harmonic.Harmony(choices(('CM7','CM7')))
context=realization.SearchContext()
for op in ops:
candidates=themes.unit_candidates(themes.home_reference(home,op),F(4),F(8),state,h,
tests.solo_fixtures.library(),tests.rhythm_fixtures.library(),'4/4',context,home=home,operation=op)
self.assertTrue(candidates,op)
self.assertGreater(context.diagnostics['operations'][op]['valid'],0)
if __name__=='__main__':unittest.main()
class InputFirewall(unittest.TestCase):
def test_read_write_handles_cannot_bypass_boundary(self):
import tempfile
from pathlib import Path
from generator.input_audit import composition
with tempfile.TemporaryDirectory() as folder:
forbidden=Path(folder)/'target.xml';forbidden.write_text('not musical input')
for mode in ('r','r+'):
with self.assertRaisesRegex(ValueError,'Forbidden composition input'):
with composition(Path('data'),'electric_solo'):
with forbidden.open(mode) as stream:stream.read()
class RetrievalIntegrity(unittest.TestCase):
def test_diversity_optimization_preserves_stable_old_order(self):
from generator.solo.sequences import diverse
values=((3,'b'),(1,'a'),(1,'b'),(1,'a'),(2,'c'),(4,'a'))
def original(items,limit):
families={}
for item in sorted(items,key=lambda v:v[0]):families.setdefault(item[1],[]).append(item)
out=[];i=0
while len(out)<limit:
layer=[g[i] for g in families.values() if len(g)>i]
if not layer:break
out.extend(sorted(layer,key=lambda v:v[0])[:limit-len(out)]);i+=1
return tuple(out)
for limit in range(9):
self.assertEqual(diverse(values,lambda v:v[0],lambda v:v[1],limit),original(values,limit))
def test_seed_cutoff_preserves_different_feature_contracts(self):
from generator.solo import analysis
from tests.test_solo import song
seeds=themes.seeds(analysis.analyze(song(count=12)),F(4),'independent-test')
self.assertGreater(len({s.treatment for s in seeds}),1)
class NonzeroContributions(unittest.TestCase):
def test_rhythm_destination_repetition_are_operational(self):
from generator.solo import phrasing
h=harmonic.Harmony(choices(('C7',)),profiles.Instrument(reference=55))
ns=(note(0,F(1,2),'g'),note(1,F(3,2),'c',octave=5),note(3,4,'e'))
motif=types.Motif('fixture',(),F(4),(),'independently authored')
plan=types.PhrasePlan('fixture',F(),F(4),motif,'continue','thematic_recomposition',None,(41,76),F(1),F(),
(types.ActivitySpan(F(),F(4),'active',F(1),F()),),ending='connected',closure='open')
anchor=types.Anchor('arrival',F(3),F(3),ns[-1].pitch,h.members(F(3),F(4)),(41,76),F(1),'intermediate',h.at(F(3)).chord,hold_until=F(4))
draft=types.MelodicDraft('fixture',F(),F(4),ns,(),(),(),(anchor,),None,'continue')
old=tuple(dataclasses.replace(n,start=n.start-4,end=n.end-4) for n in ns)
incoming=types.ContinuationState('',history=(phrasing.heard(old,'continue'),))
base=scoring.musical(plan,draft,ns,incoming,h)
self.assertTrue(base.valid,base.error)
for key in ('rhythm','destination','repetition'):
self.assertGreater(base.raw[key],0,key)
changed=scoring.musical(plan,draft,ns,incoming,h,weights={**base.weights,key:0})
self.assertAlmostEqual(base.total-changed.total,base.contributions[key])
class PreparedSuspensions(unittest.TestCase):
def test_preparation_and_delayed_resolution_are_required(self):
h=harmonic.Harmony(choices(('Gm7','C7')))
held=note(F(5,2),F(9,2),'f',event_id='held')
target=note(F(9,2),6,'e',event_id='resolution')
contract=types.ExpressionContract('prepared','suspension','prepared_step_down',(held,),target,'C7',boundary=F(4))
self.assertTrue(h.evaluate((held,target),expressions=(contract,)).valid)
self.assertFalse(h.evaluate((held,target)).valid)
changed=dataclasses.replace(held,start=F(15,4))
self.assertFalse(h.evaluate((changed,target),expressions=(dataclasses.replace(contract,events=(changed,)),)).valid)
self.assertFalse(h.evaluate((held,),expressions=(contract,)).valid)
def test_suspension_rule_is_reachable_from_provisional_phrase(self):
from generator.solo import expressions
h=harmonic.Harmony(choices(('Gm7','C7')))
ns=(note(3,4,'f',event_id='held'),note(4,6,'e',event_id='resolution'))
motif=types.Motif('fixture',ns,F(8),(),'independent suspension')
plan=types.PhrasePlan('fixture',F(),F(8),motif,'continue','thematic_recomposition',None,(41,76),F(1),F(),ending='connected',closure='open')
draft=types.MelodicDraft('fixture',F(),F(8),ns,(),(),(),(),None,'continue')
phrase=types.Phrase(plan,(),0,draft=draft)
candidates=[(notes,c) for notes,c in expressions.proposals(phrase,h) if c.kind=='suspension']
self.assertTrue(candidates)
self.assertTrue(any(h.evaluate(notes,expressions=(c,)).valid for notes,c in candidates))
+3 -3
View File
@@ -68,11 +68,11 @@ class SoloInstrumentAndIdentity(unittest.TestCase):
self.assertTrue(any(p.string_number==1 for p in placement.positions(65,electric)))
self.assertTrue(any(p.string_number==1 for p in placement.positions(65,acoustic)))
def test_sounding_melody_reference_and_octave_relation(self):
def test_soft_overlapping_registers_do_not_inherit_a_mandatory_octave(self):
source=analysis.analyze(generator.parts.prepare_document(song(count=4,chords=('CM7',))))
acoustic=profiles.for_part('acoustic',source);electric=profiles.for_part('electric',source)
self.assertEqual(electric.reference-acoustic.reference,12)
self.assertEqual(electric.reference,profiles.weighted_center(source.melody)+12)
self.assertLess(electric.reference-acoustic.reference,12)
self.assertEqual(acoustic.melody_range,electric.melody_range)
self.assertEqual(acoustic.cost(acoustic.reference+4),0)
self.assertGreater(acoustic.cost(acoustic.reference+9),0)
+3 -1
View File
@@ -122,7 +122,9 @@ class SoloTests(unittest.TestCase):
self.assertTrue(all(p.draft.source_phrase for p in result.phrases))
self.assertFalse(any(d.treatment.startswith('quote') for p in result.phrases for d in p.decisions))
original = result.phrases[0].plan
explicit = dataclasses.replace(original, treatment='exact_quote', target=None, anchor_options=(), gestures=(), passage_id='', passage_start=None, passage_end=None)
explicit = dataclasses.replace(original, treatment='exact_quote', end=original.start+original.motif.duration,
target=None, anchor_options=(), gestures=(), passages=(),landmarks=(),release=fractions.Fraction(),ending='connected',closure='open',
passage_id='', passage_start=None, passage_end=None)
first = generator.solo.realization.compose(explicit, source['staves'][0]['measures'], result.choices, tests.solo_fixtures.library(), tests.rhythm_fixtures.library(), result.tempo)
quote = next(d for d in first.decisions if d.treatment.startswith('quote'))
self.assertEqual(len(quote.notes), len(first.plan.motif.notes))
+1 -2
View File
@@ -188,8 +188,7 @@ class DraftCompositionTests(unittest.TestCase):
self.assertTrue(draft.transformations)
previous = phrase.state.last
def test_source_family_survives_phrase_variations(self):
first, second = self.solo.phrases
self.assertEqual(first.state.home.id, second.state.home.id)
self.assertEqual(len({p.state.home.id for p in self.solo.phrases}),1)
for phrase in self.solo.phrases:
self.assertTrue(all(t.home == phrase.state.home.id for t in phrase.draft.transformations))
self.assertTrue(phrase.state.home.chord_degrees)
+8 -5
View File
@@ -235,7 +235,7 @@ class EnclosureRefinement(unittest.TestCase):
class EnclosureCompositionAPI(unittest.TestCase):
def test_disable_only_removes_post_selection_ornaments(self):
def test_disable_joint_expressions_selects_a_valid_strict_alternative(self):
from generator.solo import analysis
import tests.rhythm_fixtures
doc=song(count=8,chords=('Gm7','C7','FM7','BbM7','Em7b5','A7','Dm7','Dm7'))
@@ -244,12 +244,15 @@ class EnclosureCompositionAPI(unittest.TestCase):
kwargs=dict(scales=tests.scale_fixtures.library(),sequences=tests.solo_fixtures.library(),rhythms=tests.rhythm_fixtures.library())
decorated=composer.compose(*args,**kwargs)
strict=composer.compose(*args,**kwargs,enclosures_enabled=False)
self.assertEqual(enclosures.strict(decorated).notes(),strict.notes())
self.assertEqual([p.state.home for p in decorated.phrases],[p.state.home for p in strict.phrases])
self.assertEqual([p.anchors for p in decorated.phrases],[p.anchors for p in strict.phrases])
# A jointly composed color/anticipation can change phrase selection. Its
# disabled alternative need not be the selected decorated phrase minus
# notes; both versions must retain the same form and valid sounding spans.
self.assertEqual(decorated.end,strict.end)
composer.validate(decorated)
composer.validate(strict)
self.assertTrue(harmonic.Harmony(strict.choices).evaluate(strict.notes()).valid)
self.assertTrue(all(not p.draft.enclosures for p in strict.phrases))
self.assertGreater(decorated.diagnostics["expression_candidates"],0)
self.assertGreater(sum(v['proposed'] for v in decorated.diagnostics['expression_operations'].values()),0)
self.assertTrue(all(not p.draft.expressions for p in strict.phrases))
if __name__=='__main__':unittest.main()
+6 -5
View File
@@ -77,11 +77,12 @@ class CompleteRouteTests(unittest.TestCase):
library = sequences.Library((sequences.Sequence('descending', (-1, -1)), sequences.Sequence('wrong', (1, 1))))
template = notes(('g', 'c', 'e'))
result = routes.candidates(template, {'n0', 'n2'}, harmony, library, (55, 79))
self.assertEqual(len(result), 1)
self.assertEqual([n.pitch.number() for n in result[0].notes], [67, 65, 64])
self.assertEqual(result[0].sources, ('descending',))
self.assertEqual(result[0].destination, template[-1])
self.assertTrue(harmony.evaluate(result[0].notes).valid)
# Explicit repeated-attack primitives can also complete a chord traversal.
# The authored route itself must survive intact, without nearest-note repair.
selected=next(r for r in result if r.sources==('descending',))
self.assertEqual([n.pitch.number() for n in selected.notes], [67, 65, 64])
self.assertTrue(all(r.destination==template[-1] for r in result))
self.assertTrue(all(harmony.evaluate(r.notes).valid for r in result))
def test_route_cannot_repair_a_forbidden_fixed_pitch(self):
harmony = harmonic.Harmony(passages.choices(('C7',)))
library = sequences.Library((sequences.Sequence('descending', (-1, -1)),))
+2 -2
View File
@@ -26,8 +26,8 @@ class SustainedPhrasing(unittest.TestCase):
whole=(note(0,1,'g'),note(1,F(3,2),'f'),note(F(3,2),4,'e'))
split=(whole[0],whole[1],dataclasses.replace(whole[2],end=2),note(2,4,'e',continuation=True))
incoming=types.ContinuationState('')
self.assertEqual(phrasing.evaluate(whole,incoming,harmony,F(),F(4),F(4)),
phrasing.evaluate(split,incoming,harmony,F(),F(4),F(4)))
from tests.test_composer_repair import evaluated
self.assertEqual(evaluated(whole,harmony),evaluated(split,harmony))
def test_route_cache_preserves_concrete_provenance_and_uncached_selection(self):
from generator.solo import routes,sequences,policy
+8 -4
View File
@@ -93,14 +93,18 @@ class ThemeProduction(unittest.TestCase):
for phrase in self.solo.phrases:
self.assertTrue(drafts.contract_valid(phrase.draft,composer.phrase_notes(phrase))[0])
self.assertFalse(similarity.near_copy(phrase.refined.notes,themes.source_notes(self.analysis),phrase.plan.start,phrase.plan.end)['near_copy'])
self.assertEqual(self.solo.phrases[0].plan.closure,'closed')
self.assertTrue(all(a.duration>=2 for a in self.solo.phrases[0].anchors))
self.assertIn(self.solo.phrases[0].plan.closure,('open','closed'))
self.assertTrue(all(p.evaluation and p.score==p.evaluation.total for p in self.solo.phrases))
self.assertEqual(self.solo.phrases[-1].anchors[-1].role,'terminal')
self.assertTrue(any(t.operation in ('develop_contour','develop_rhythm','continue','return') for p in self.solo.phrases for t in p.draft.transformations))
def test_library_routes_match_the_actual_complete_theme(self):
self.assertTrue(any(p.routes for p in self.solo.phrases))
for phrase in self.solo.phrases:
plain=composer.compose(self.analysis,self.document['staves'][0]['measures'],'acoustic',F(32),stage='plain',**self.kw)
# Joint rhythm/expression edits can replace every original route in this
# particular fixture. Require construction to use complete routes, then
# verify all retained provenance in both the plain and edited realizations.
self.assertTrue(any(p.routes for p in plain.phrases))
for phrase in (*plain.phrases,*self.solo.phrases):
heard={n.event_id:n for n in composer.phrase_notes(phrase)}
for route in phrase.routes:
for note in route.notes: