@@ -5,7 +5,7 @@ from pathlib import Path
|
||||
from generator.score import copy_source_voices, make_arranged_document, make_measure, reidentify, slugify, source_staff, write_json
|
||||
from generator.musicxml import load_musicxml
|
||||
from generator.practice.guitar import transpose_guitar_practice
|
||||
from generator.transposition import TARGET_KEYS, transpose_document
|
||||
from generator.transposition import TARGET_KEYS
|
||||
|
||||
def practice_document(document: dict) -> dict:
|
||||
staff = source_staff(document)
|
||||
@@ -29,7 +29,7 @@ def practice_document(document: dict) -> dict:
|
||||
staff,
|
||||
staff_id="staff-arranged-practice",
|
||||
staff_name="Practice",
|
||||
role="vocal",
|
||||
role="guitar",
|
||||
arrangement_part_type="practice",
|
||||
measures=measures,
|
||||
tuplets=tuplets,
|
||||
@@ -41,47 +41,27 @@ def practice_document(document: dict) -> dict:
|
||||
def key_file_name(key: str) -> str:
|
||||
return slugify(key.replace("#", "sharp")) + ".json"
|
||||
|
||||
def generate_chord_practice(source_path: Path, source_root: Path, output_dir: Path) -> int:
|
||||
return generate_key_variants(source_path, source_root, output_dir, 'chord')
|
||||
|
||||
def generate_melody_practice(source_path: Path, source_root: Path, output_dir: Path) -> int:
|
||||
return generate_key_variants(source_path, source_root, output_dir, 'melody')
|
||||
|
||||
def generate_key_variants(source_path: Path, source_root: Path, output_dir: Path, practice_type: str) -> int:
|
||||
def generate_key_variants(source_path: Path, output_dir: Path) -> int:
|
||||
base_document = practice_document(load_musicxml(source_path))
|
||||
relative = source_path.relative_to(source_root)
|
||||
lesson_group = relative.parent.name
|
||||
exercise_id = slugify(source_path.stem)
|
||||
count = 0
|
||||
for key in TARGET_KEYS:
|
||||
try:
|
||||
variant = transpose_guitar_practice(base_document, key) if practice_type == 'chord' else transpose_document(base_document, key)
|
||||
variant = transpose_guitar_practice(base_document, key)
|
||||
except ValueError as error:
|
||||
raise ValueError(f'{source_path}, target key {key}: {error}') from error
|
||||
write_json(output_dir / practice_type / lesson_group / exercise_id / key_file_name(key), variant)
|
||||
write_json(output_dir / source_path.stem / key_file_name(key), variant)
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def generate_practice_scores(source_dir: Path, output_dir: Path) -> int:
|
||||
if not source_dir.exists():
|
||||
return 0
|
||||
sources = sorted(source_dir.rglob('*.xml'))
|
||||
destinations: dict[Path, Path] = {}
|
||||
identities: dict[str, Path] = {}
|
||||
for path in sources:
|
||||
if not source_dir.is_dir():
|
||||
raise ValueError(f'practice source directory not found: {source_dir}')
|
||||
sources = sorted(source_dir.glob('*.xml'))
|
||||
for path in source_dir.rglob('*.xml'):
|
||||
parts = path.relative_to(source_dir).parts
|
||||
if len(parts) != 3 or parts[0] not in ('chord', 'melody'):
|
||||
raise ValueError(f'{path}: practice source must be stored as practice/<chord|melody>/<lesson_group>/<exercise>.xml')
|
||||
destination = Path(parts[0]) / parts[1] / slugify(path.stem)
|
||||
if destination in destinations:
|
||||
raise ValueError(f'{path}: practice output path collides with {destinations[destination]}: {destination}')
|
||||
identity = '-'.join(destination.parts)
|
||||
if identity in identities:
|
||||
raise ValueError(f'{path}: practice exercise ID collision with {identities[identity]}: {identity}')
|
||||
identities[identity] = path
|
||||
destinations[destination] = path
|
||||
count = 0
|
||||
for destination, path in destinations.items():
|
||||
practice_type = destination.parts[0]
|
||||
count += generate_key_variants(path, source_dir / practice_type, output_dir, practice_type)
|
||||
return count
|
||||
if len(parts) != 1 and (len(parts) != 2 or parts[0] != 'songs'):
|
||||
raise ValueError(f'{path}: practice sources belong directly under the data directory')
|
||||
for path in sources:
|
||||
if len(path.stem) != 3 or not path.stem.isascii() or not path.stem.isdecimal() or int(path.stem) == 0:
|
||||
raise ValueError(f'{path}: practice sources require a three-digit positive filename such as 001.xml')
|
||||
return sum(generate_key_variants(path, output_dir) for path in sources)
|
||||
|
||||
Reference in New Issue
Block a user