multilingualprogramming 0.7.0__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/PKG-INFO +1 -1
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/__main__.py +229 -1
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/linear_manifest.py +96 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/midi_capture.py +156 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/midi_manifest.py +110 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/opcode_ontology.py +290 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/process_capabilities.py +140 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/process_core.py +1034 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/process_field_projection.py +257 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/process_graph_projection.py +295 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/process_migration.py +87 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/process_program.py +100 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/process_projection.py +279 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/process_sequence_projection.py +248 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/process_static_projection.py +200 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/projection_capabilities.py +37 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/runtime_builtins.py +123 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/semantic_core.py +213 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/sonic_capture.py +183 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/sonic_projection.py +134 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/spatial_capture.py +182 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/spatial_manifest.py +145 -0
- multilingualprogramming-0.8.0/multilingualprogramming/codegen/volumetric_manifest.py +104 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator.py +196 -5
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_core.py +202 -24
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_expression.py +110 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_manifest.py +39 -1
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_orchestrator.py +20 -2
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_runtime.py +715 -2
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_support.py +11 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/lexer/lexer.py +46 -1
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/parser/ast_nodes.py +7 -1
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/usm/builtins_aliases.json +342 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/version.py +1 -1
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming.egg-info/PKG-INFO +1 -1
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming.egg-info/SOURCES.txt +20 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/LICENSE +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/README.md +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/__init__.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/__init__.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/build_orchestrator.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/encoding_guard.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/executor.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/python_generator.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/repl.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/ui_lowering.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wasm_generator.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_loop.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_match.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_oop.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_print.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_generator_sequence.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/codegen/wat_ir_adapter.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/core/__init__.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/core/effects.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/core/ir_nodes.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/core/semantic_analyzer.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/core/semantic_lowering.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/core/types.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/core/validators.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/datetime/__init__.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/datetime/date_parser.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/datetime/mp_date.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/datetime/mp_datetime.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/datetime/mp_time.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/datetime/resource_loader.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/exceptions.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/imports.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/keyword/__init__.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/keyword/keyword_registry.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/keyword/keyword_validator.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/keyword/language_pack_validator.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/lexer/__init__.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/lexer/source_reader.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/lexer/token.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/lexer/token_types.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/numeral/__init__.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/numeral/abstract_numeral.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/numeral/complex_numeral.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/numeral/fraction_numeral.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/numeral/mp_numeral.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/numeral/numeral_converter.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/numeral/roman_numeral.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/numeral/unicode_numeral.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/parser/__init__.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/parser/ast_printer.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/parser/error_messages.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/parser/lexer.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/parser/parser.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/parser/surface_normalizer.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/datetime/eras.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/datetime/formats.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/datetime/months.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/datetime/weekdays.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/parser/error_messages.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/repl/commands.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/usm/keywords.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/usm/operators.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/usm/schema.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/usm/surface_patterns.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/resources/usm/ui_lowering.json +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/__init__.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/ai_runtime.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/ai_types.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/anthropic_provider.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/backend_selector.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/channel.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/inference_cache.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/memory_store.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/model_registry.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/multimodal_runtime.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/numeric_primitives.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/observability.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/ollama_provider.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/openai_provider.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/placement.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/prompt_optimizer.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/python_fallbacks.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/reactive.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/retrieval_runtime.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/semantic_match.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/swarm.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/runtime/tool_runtime.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/source_extensions.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/unicode_string.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/wasm/loader.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/wasm/tuple_abi.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/wasm/tuple_memory.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming.egg-info/dependency_links.txt +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming.egg-info/entry_points.txt +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming.egg-info/requires.txt +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming.egg-info/top_level.txt +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/pyproject.toml +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/setup.cfg +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/setup.py +0 -0
- {multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/tests/tests.py +0 -0
{multilingualprogramming-0.7.0 → multilingualprogramming-0.8.0}/multilingualprogramming/__main__.py
RENAMED
|
@@ -49,6 +49,16 @@ from multilingualprogramming.source_extensions import (
|
|
|
49
49
|
find_package_init,
|
|
50
50
|
has_source_extension,
|
|
51
51
|
)
|
|
52
|
+
from multilingualprogramming.codegen.linear_manifest import build_linear_manifest_file
|
|
53
|
+
from multilingualprogramming.codegen.midi_manifest import build_midi_manifest_file
|
|
54
|
+
from multilingualprogramming.codegen.opcode_ontology import write_ontology_manifest
|
|
55
|
+
from multilingualprogramming.codegen.semantic_core import build_semantic_core_file
|
|
56
|
+
from multilingualprogramming.codegen.process_program import build_process_core_file
|
|
57
|
+
from multilingualprogramming.codegen.sonic_projection import build_sonic_manifest_file
|
|
58
|
+
from multilingualprogramming.codegen.spatial_manifest import build_spatial_manifest_file
|
|
59
|
+
from multilingualprogramming.codegen.volumetric_manifest import (
|
|
60
|
+
build_volumetric_manifest_file,
|
|
61
|
+
)
|
|
52
62
|
from multilingualprogramming.version import __version__
|
|
53
63
|
|
|
54
64
|
|
|
@@ -367,6 +377,101 @@ def cmd_build_ui_bundle(args):
|
|
|
367
377
|
print(f"[WARN] {diag}")
|
|
368
378
|
|
|
369
379
|
|
|
380
|
+
def cmd_spatial_build(args):
|
|
381
|
+
"""Build a spatial JSON manifest from a Multilingual source file."""
|
|
382
|
+
manifest = build_spatial_manifest_file(
|
|
383
|
+
args.file,
|
|
384
|
+
args.out,
|
|
385
|
+
language=args.lang or "en",
|
|
386
|
+
)
|
|
387
|
+
print(f"[PASS] {args.out}")
|
|
388
|
+
print(f"[spatial] {manifest['kind']} entities={len(manifest['entities'])}")
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def cmd_polymodal_build(args):
|
|
392
|
+
"""Build a modality-free semantic core manifest from Multilingual source."""
|
|
393
|
+
core = build_semantic_core_file(
|
|
394
|
+
args.file,
|
|
395
|
+
args.out,
|
|
396
|
+
language=args.lang or "en",
|
|
397
|
+
)
|
|
398
|
+
print(f"[PASS] {args.out}")
|
|
399
|
+
print(f"[polymodal] {core['kind']} entities={len(core['entities'])}")
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def cmd_process_build(args):
|
|
403
|
+
"""Build a semantic-core-v1 process manifest from Multilingual source."""
|
|
404
|
+
core = build_process_core_file(
|
|
405
|
+
args.file,
|
|
406
|
+
args.out,
|
|
407
|
+
language=args.lang or "en",
|
|
408
|
+
)
|
|
409
|
+
state = core.get("state", {})
|
|
410
|
+
# A lattice program stores loci; a sequence (string-rewriting) program
|
|
411
|
+
# stores an ordered sequence. Report whichever this core carries.
|
|
412
|
+
if "sequence" in state:
|
|
413
|
+
size = f"symbols={len(state['sequence'])}"
|
|
414
|
+
else:
|
|
415
|
+
size = f"loci={len(state.get('loci', []))}"
|
|
416
|
+
print(f"[PASS] {args.out}")
|
|
417
|
+
print(
|
|
418
|
+
f"[process] {core['kind']} "
|
|
419
|
+
f"topology={core['topology'].get('kind')} "
|
|
420
|
+
f"schedule={core['schedule'].get('kind')} {size}"
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def cmd_sonic_build(args):
|
|
425
|
+
"""Build a sonic JSON manifest from a Multilingual source file."""
|
|
426
|
+
manifest = build_sonic_manifest_file(
|
|
427
|
+
args.file,
|
|
428
|
+
args.out,
|
|
429
|
+
language=args.lang or "en",
|
|
430
|
+
)
|
|
431
|
+
print(f"[PASS] {args.out}")
|
|
432
|
+
print(f"[sonic] {manifest['kind']} voices={len(manifest['voices'])}")
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def cmd_ontology_export(args):
|
|
436
|
+
"""Write the shared opcode ontology as JSON for browser runtimes."""
|
|
437
|
+
manifest = write_ontology_manifest(args.out)
|
|
438
|
+
print(f"[PASS] {args.out}")
|
|
439
|
+
print(f"[ontology] {manifest['kind']} opcodes={len(manifest['opcodes'])}")
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def cmd_linear_build(args):
|
|
443
|
+
"""Build a 1D linear JSON manifest from a Multilingual source file."""
|
|
444
|
+
manifest = build_linear_manifest_file(
|
|
445
|
+
args.file,
|
|
446
|
+
args.out,
|
|
447
|
+
language=args.lang or "en",
|
|
448
|
+
)
|
|
449
|
+
print(f"[PASS] {args.out}")
|
|
450
|
+
print(f"[linear] {manifest['kind']} marks={len(manifest['marks'])}")
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def cmd_volumetric_build(args):
|
|
454
|
+
"""Build a 3D volumetric JSON manifest from a Multilingual source file."""
|
|
455
|
+
manifest = build_volumetric_manifest_file(
|
|
456
|
+
args.file,
|
|
457
|
+
args.out,
|
|
458
|
+
language=args.lang or "en",
|
|
459
|
+
)
|
|
460
|
+
print(f"[PASS] {args.out}")
|
|
461
|
+
print(f"[volumetric] {manifest['kind']} marks={len(manifest['marks'])}")
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def cmd_midi_build(args):
|
|
465
|
+
"""Build a MIDI JSON manifest from a Multilingual source file."""
|
|
466
|
+
manifest = build_midi_manifest_file(
|
|
467
|
+
args.file,
|
|
468
|
+
args.out,
|
|
469
|
+
language=args.lang or "en",
|
|
470
|
+
)
|
|
471
|
+
print(f"[PASS] {args.out}")
|
|
472
|
+
print(f"[midi] {manifest['kind']} events={len(manifest['events'])}")
|
|
473
|
+
|
|
474
|
+
|
|
370
475
|
def cmd_ir(args):
|
|
371
476
|
"""Lower a source file to semantic IR and print a summary."""
|
|
372
477
|
program = _parse_program_from_file(args.file, args.lang)
|
|
@@ -576,7 +681,7 @@ def _maybe_dispatch_direct_file_run(argv):
|
|
|
576
681
|
return True
|
|
577
682
|
|
|
578
683
|
|
|
579
|
-
def main(): # pylint: disable=too-many-statements
|
|
684
|
+
def main(): # pylint: disable=too-many-statements,too-many-locals,too-many-branches
|
|
580
685
|
"""Run the CLI entry point and dispatch subcommands."""
|
|
581
686
|
argv = sys.argv[1:]
|
|
582
687
|
if _maybe_dispatch_direct_file_run(argv):
|
|
@@ -739,6 +844,113 @@ def main(): # pylint: disable=too-many-statements
|
|
|
739
844
|
help="Output directory for generated artifacts (default: build/ui)",
|
|
740
845
|
)
|
|
741
846
|
|
|
847
|
+
spatial_build_parser = subparsers.add_parser(
|
|
848
|
+
"spatial-build",
|
|
849
|
+
help="Build a fixed-semantic spatial JSON manifest",
|
|
850
|
+
)
|
|
851
|
+
spatial_build_parser.add_argument("file", help="Path to the source file")
|
|
852
|
+
spatial_build_parser.add_argument(
|
|
853
|
+
"--lang", default=None,
|
|
854
|
+
help="Source language code (e.g., en, fr, hi). Auto-detect if omitted.",
|
|
855
|
+
)
|
|
856
|
+
spatial_build_parser.add_argument(
|
|
857
|
+
"--out", default="program.spatial.json",
|
|
858
|
+
help="Output JSON manifest path (default: program.spatial.json)",
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
polymodal_build_parser = subparsers.add_parser(
|
|
862
|
+
"polymodal-build",
|
|
863
|
+
help="Build a modality-free semantic core manifest",
|
|
864
|
+
)
|
|
865
|
+
polymodal_build_parser.add_argument("file", help="Path to the source file")
|
|
866
|
+
polymodal_build_parser.add_argument(
|
|
867
|
+
"--lang", default=None,
|
|
868
|
+
help="Source language code (e.g., en, fr, hi). Auto-detect if omitted.",
|
|
869
|
+
)
|
|
870
|
+
polymodal_build_parser.add_argument(
|
|
871
|
+
"--out", default="program.semantic.json",
|
|
872
|
+
help="Output JSON manifest path (default: program.semantic.json)",
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
process_build_parser = subparsers.add_parser(
|
|
876
|
+
"process-build",
|
|
877
|
+
help="Build a semantic-core-v1 process manifest (dynamics as data)",
|
|
878
|
+
)
|
|
879
|
+
process_build_parser.add_argument("file", help="Path to the source file")
|
|
880
|
+
process_build_parser.add_argument(
|
|
881
|
+
"--lang", default=None,
|
|
882
|
+
help="Source language code (e.g., en, fr, hi). Auto-detect if omitted.",
|
|
883
|
+
)
|
|
884
|
+
process_build_parser.add_argument(
|
|
885
|
+
"--out", default="program.v1.json",
|
|
886
|
+
help="Output JSON manifest path (default: program.v1.json)",
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
sonic_build_parser = subparsers.add_parser(
|
|
890
|
+
"sonic-build",
|
|
891
|
+
help="Build a sonic projection JSON manifest",
|
|
892
|
+
)
|
|
893
|
+
sonic_build_parser.add_argument("file", help="Path to the source file")
|
|
894
|
+
sonic_build_parser.add_argument(
|
|
895
|
+
"--lang", default=None,
|
|
896
|
+
help="Source language code (e.g., en, fr, hi). Auto-detect if omitted.",
|
|
897
|
+
)
|
|
898
|
+
sonic_build_parser.add_argument(
|
|
899
|
+
"--out", default="program.sonic.json",
|
|
900
|
+
help="Output JSON manifest path (default: program.sonic.json)",
|
|
901
|
+
)
|
|
902
|
+
|
|
903
|
+
ontology_export_parser = subparsers.add_parser(
|
|
904
|
+
"ontology-export",
|
|
905
|
+
help="Write the shared opcode ontology JSON (for browser runtimes)",
|
|
906
|
+
)
|
|
907
|
+
ontology_export_parser.add_argument(
|
|
908
|
+
"--out", default="ontology.json",
|
|
909
|
+
help="Output JSON path (default: ontology.json)",
|
|
910
|
+
)
|
|
911
|
+
|
|
912
|
+
linear_build_parser = subparsers.add_parser(
|
|
913
|
+
"linear-build",
|
|
914
|
+
help="Build a 1D linear timeline JSON manifest",
|
|
915
|
+
)
|
|
916
|
+
linear_build_parser.add_argument("file", help="Path to the source file")
|
|
917
|
+
linear_build_parser.add_argument(
|
|
918
|
+
"--lang", default=None,
|
|
919
|
+
help="Source language code (e.g., en, fr, hi). Auto-detect if omitted.",
|
|
920
|
+
)
|
|
921
|
+
linear_build_parser.add_argument(
|
|
922
|
+
"--out", default="program.linear.json",
|
|
923
|
+
help="Output JSON manifest path (default: program.linear.json)",
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
volumetric_build_parser = subparsers.add_parser(
|
|
927
|
+
"volumetric-build",
|
|
928
|
+
help="Build a 3D volumetric JSON manifest",
|
|
929
|
+
)
|
|
930
|
+
volumetric_build_parser.add_argument("file", help="Path to the source file")
|
|
931
|
+
volumetric_build_parser.add_argument(
|
|
932
|
+
"--lang", default=None,
|
|
933
|
+
help="Source language code (e.g., en, fr, hi). Auto-detect if omitted.",
|
|
934
|
+
)
|
|
935
|
+
volumetric_build_parser.add_argument(
|
|
936
|
+
"--out", default="program.volumetric.json",
|
|
937
|
+
help="Output JSON manifest path (default: program.volumetric.json)",
|
|
938
|
+
)
|
|
939
|
+
|
|
940
|
+
midi_build_parser = subparsers.add_parser(
|
|
941
|
+
"midi-build",
|
|
942
|
+
help="Build a MIDI event JSON manifest",
|
|
943
|
+
)
|
|
944
|
+
midi_build_parser.add_argument("file", help="Path to the source file")
|
|
945
|
+
midi_build_parser.add_argument(
|
|
946
|
+
"--lang", default=None,
|
|
947
|
+
help="Source language code (e.g., en, fr, hi). Auto-detect if omitted.",
|
|
948
|
+
)
|
|
949
|
+
midi_build_parser.add_argument(
|
|
950
|
+
"--out", default="program.midi.json",
|
|
951
|
+
help="Output JSON manifest path (default: program.midi.json)",
|
|
952
|
+
)
|
|
953
|
+
|
|
742
954
|
# ir subcommand
|
|
743
955
|
ir_parser = subparsers.add_parser(
|
|
744
956
|
"ir", help="Show the semantic IR for a source file"
|
|
@@ -805,6 +1017,22 @@ def main(): # pylint: disable=too-many-statements
|
|
|
805
1017
|
cmd_build_wasm_bundle(args)
|
|
806
1018
|
elif args.command == "build-ui-bundle":
|
|
807
1019
|
cmd_build_ui_bundle(args)
|
|
1020
|
+
elif args.command == "spatial-build":
|
|
1021
|
+
cmd_spatial_build(args)
|
|
1022
|
+
elif args.command == "polymodal-build":
|
|
1023
|
+
cmd_polymodal_build(args)
|
|
1024
|
+
elif args.command == "process-build":
|
|
1025
|
+
cmd_process_build(args)
|
|
1026
|
+
elif args.command == "sonic-build":
|
|
1027
|
+
cmd_sonic_build(args)
|
|
1028
|
+
elif args.command == "ontology-export":
|
|
1029
|
+
cmd_ontology_export(args)
|
|
1030
|
+
elif args.command == "linear-build":
|
|
1031
|
+
cmd_linear_build(args)
|
|
1032
|
+
elif args.command == "volumetric-build":
|
|
1033
|
+
cmd_volumetric_build(args)
|
|
1034
|
+
elif args.command == "midi-build":
|
|
1035
|
+
cmd_midi_build(args)
|
|
808
1036
|
elif args.command == "ir":
|
|
809
1037
|
cmd_ir(args)
|
|
810
1038
|
elif args.command == "explain":
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: 2026 John Samuel <johnsamuelwrites@gmail.com>
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""Linear (1D timeline) projection of a polymodal program.
|
|
8
|
+
|
|
9
|
+
Peer of the spatial (2D) and sonic projections. Takes the modality-free
|
|
10
|
+
semantic core and produces a one-dimensional manifest: each entity
|
|
11
|
+
becomes a positioned mark along a single axis. Phase determines the
|
|
12
|
+
position; opcode determines the glyph and color.
|
|
13
|
+
|
|
14
|
+
Adding 1D as a peer projection (rather than a parameterization of the
|
|
15
|
+
2D spatial projection) is deliberate. The 1D rendering primitives
|
|
16
|
+
(dot, segment, pulse, ramp, wave, fork, join, band, shift, double)
|
|
17
|
+
do not generalize from 2D shapes -- "ring" and "membrane" have no
|
|
18
|
+
natural 1D analog. Peer projections force the equivalence test to
|
|
19
|
+
exercise the dimensionality axis rather than silently treating
|
|
20
|
+
"spatial" as a monolith.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
from multilingualprogramming.codegen import opcode_ontology
|
|
30
|
+
from multilingualprogramming.codegen.projection_capabilities import capability_contract
|
|
31
|
+
from multilingualprogramming.codegen.semantic_core import build_semantic_core
|
|
32
|
+
|
|
33
|
+
MANIFEST_KIND = "linear-seed-v0"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def build_linear_manifest(
|
|
37
|
+
source: str,
|
|
38
|
+
language: str = "en",
|
|
39
|
+
source_path: str = "",
|
|
40
|
+
) -> dict[str, Any]:
|
|
41
|
+
"""Execute Multilingual source and return a validated linear manifest."""
|
|
42
|
+
core = build_semantic_core(source, language=language, source_path=source_path)
|
|
43
|
+
marks = [_mark_from_semantic_entity(entity) for entity in core["entities"]]
|
|
44
|
+
return {
|
|
45
|
+
"kind": MANIFEST_KIND,
|
|
46
|
+
"version": 0,
|
|
47
|
+
"source_language": language,
|
|
48
|
+
"source": source_path,
|
|
49
|
+
"capabilities": capability_contract(
|
|
50
|
+
projection=MANIFEST_KIND,
|
|
51
|
+
preserves=["id", "opcode", "intensity", "phase", "channel"],
|
|
52
|
+
derived=[],
|
|
53
|
+
lossy=["signal"],
|
|
54
|
+
ambiguous=[],
|
|
55
|
+
inverse="view-only",
|
|
56
|
+
),
|
|
57
|
+
"tempo_bpm": 96,
|
|
58
|
+
"bar_seconds": 4.0,
|
|
59
|
+
"marks": marks,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def build_linear_manifest_file(
|
|
64
|
+
source_path: str | Path,
|
|
65
|
+
output_path: str | Path,
|
|
66
|
+
language: str = "en",
|
|
67
|
+
) -> dict[str, Any]:
|
|
68
|
+
"""Build and write a linear manifest from a Multilingual source file."""
|
|
69
|
+
src = Path(source_path)
|
|
70
|
+
out = Path(output_path)
|
|
71
|
+
manifest = build_linear_manifest(
|
|
72
|
+
src.read_text(encoding="utf-8"),
|
|
73
|
+
language=language,
|
|
74
|
+
source_path=str(src),
|
|
75
|
+
)
|
|
76
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
77
|
+
out.write_text(
|
|
78
|
+
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
|
|
79
|
+
encoding="utf-8",
|
|
80
|
+
)
|
|
81
|
+
return manifest
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _mark_from_semantic_entity(entity: dict[str, Any]) -> dict[str, Any]:
|
|
85
|
+
op = opcode_ontology.get(entity["opcode"])
|
|
86
|
+
return {
|
|
87
|
+
"id": entity["id"],
|
|
88
|
+
"index": int(entity["index"]),
|
|
89
|
+
"opcode": op.code,
|
|
90
|
+
"name": op.name,
|
|
91
|
+
"glyph": op.linear.glyph,
|
|
92
|
+
"color": op.linear.color,
|
|
93
|
+
"position": round(float(entity["phase"]) % 1.0, 4),
|
|
94
|
+
"intensity": round(float(entity["intensity"]), 4),
|
|
95
|
+
"channel": int(entity["channel"]),
|
|
96
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: 2026 John Samuel <johnsamuelwrites@gmail.com>
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""Inverse MIDI projection: observed MIDI events -> semantic core.
|
|
8
|
+
|
|
9
|
+
The forward MIDI projection emits identity labels for manifests, but a
|
|
10
|
+
real MIDI input stream does not carry ``opcode`` or ``name``. This
|
|
11
|
+
module recovers semantic identity from the observable MIDI fields the
|
|
12
|
+
ontology owns: role and pitch.
|
|
13
|
+
|
|
14
|
+
MIDI capture is deliberately partial:
|
|
15
|
+
|
|
16
|
+
- ``bus`` events are silent routing markers, not recoverable input.
|
|
17
|
+
- ``program`` events in the current ontology have base velocity 0, so
|
|
18
|
+
intensity cannot be recovered.
|
|
19
|
+
- events whose velocity has clipped to 127 cannot recover the original
|
|
20
|
+
intensity exactly.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from dataclasses import dataclass
|
|
26
|
+
from typing import Any, Iterable
|
|
27
|
+
|
|
28
|
+
from multilingualprogramming.codegen import opcode_ontology
|
|
29
|
+
from multilingualprogramming.codegen.semantic_core import CORE_KIND
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class ObservedMidiEvent:
|
|
34
|
+
"""A MIDI event as it would arrive from a MIDI input boundary."""
|
|
35
|
+
|
|
36
|
+
index: int
|
|
37
|
+
role: str
|
|
38
|
+
pitch: int
|
|
39
|
+
velocity: int
|
|
40
|
+
channel: int
|
|
41
|
+
start_offset: float
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _ontology_signature(op: opcode_ontology.Opcode) -> tuple[str, int]:
|
|
45
|
+
return (op.midi.role, op.midi.pitch)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def invertible_opcodes() -> set[int]:
|
|
49
|
+
"""Opcodes whose identity can be recovered from MIDI observation."""
|
|
50
|
+
signature_counts: dict[tuple[str, int], int] = {}
|
|
51
|
+
for op in opcode_ontology.OPCODES:
|
|
52
|
+
signature = _ontology_signature(op)
|
|
53
|
+
signature_counts[signature] = signature_counts.get(signature, 0) + 1
|
|
54
|
+
return {
|
|
55
|
+
op.code
|
|
56
|
+
for op in opcode_ontology.OPCODES
|
|
57
|
+
if op.midi.role != "bus"
|
|
58
|
+
and op.midi.velocity > 0
|
|
59
|
+
and signature_counts[_ontology_signature(op)] == 1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def observe_event(event: dict[str, Any]) -> ObservedMidiEvent:
|
|
64
|
+
"""Strip semantic identity labels from a forward-projected MIDI event."""
|
|
65
|
+
return ObservedMidiEvent(
|
|
66
|
+
index=int(event["index"]),
|
|
67
|
+
role=str(event["role"]),
|
|
68
|
+
pitch=int(event["pitch"]),
|
|
69
|
+
velocity=int(event["velocity"]),
|
|
70
|
+
channel=int(event["channel"]),
|
|
71
|
+
start_offset=float(event["start_offset"]),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _opcode_from_observation(
|
|
76
|
+
observed: ObservedMidiEvent,
|
|
77
|
+
) -> opcode_ontology.Opcode:
|
|
78
|
+
signature = (observed.role, observed.pitch)
|
|
79
|
+
matches = [
|
|
80
|
+
op for op in opcode_ontology.OPCODES if _ontology_signature(op) == signature
|
|
81
|
+
]
|
|
82
|
+
if not matches:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"No ontology opcode matches MIDI observation {signature!r} "
|
|
85
|
+
f"on event index {observed.index}"
|
|
86
|
+
)
|
|
87
|
+
if len(matches) > 1:
|
|
88
|
+
names = ", ".join(op.name for op in matches)
|
|
89
|
+
raise ValueError(
|
|
90
|
+
f"MIDI observation {signature!r} on event index {observed.index} is "
|
|
91
|
+
f"ambiguous: matches {names}."
|
|
92
|
+
)
|
|
93
|
+
op = matches[0]
|
|
94
|
+
if op.midi.role == "bus":
|
|
95
|
+
raise ValueError(
|
|
96
|
+
f"MIDI event index {observed.index} resolves to bus opcode {op.name!r}; "
|
|
97
|
+
"silent routing events are not recoverable authoring input."
|
|
98
|
+
)
|
|
99
|
+
if op.midi.velocity <= 0:
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f"MIDI event index {observed.index} resolves to opcode {op.name!r} "
|
|
102
|
+
"with zero base velocity; intensity cannot be recovered."
|
|
103
|
+
)
|
|
104
|
+
return op
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _intensity_from_velocity(
|
|
108
|
+
op: opcode_ontology.Opcode,
|
|
109
|
+
observed: ObservedMidiEvent,
|
|
110
|
+
) -> float:
|
|
111
|
+
if not 0 <= observed.velocity <= 127:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
f"MIDI event index {observed.index} velocity must be in 0..127"
|
|
114
|
+
)
|
|
115
|
+
if observed.velocity == 127:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"MIDI event index {observed.index} is clipped at velocity 127; "
|
|
118
|
+
"original intensity is lossy."
|
|
119
|
+
)
|
|
120
|
+
return observed.velocity / op.midi.velocity
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def capture_semantic_core(
|
|
124
|
+
observed: Iterable[ObservedMidiEvent],
|
|
125
|
+
*,
|
|
126
|
+
source_language: str = "en",
|
|
127
|
+
source_path: str = "",
|
|
128
|
+
) -> dict[str, Any]:
|
|
129
|
+
"""Invert observed MIDI events into a semantic-core manifest."""
|
|
130
|
+
entities: list[dict[str, Any]] = []
|
|
131
|
+
for event in observed:
|
|
132
|
+
op = _opcode_from_observation(event)
|
|
133
|
+
intensity = _intensity_from_velocity(op, event)
|
|
134
|
+
entities.append(
|
|
135
|
+
{
|
|
136
|
+
"index": event.index,
|
|
137
|
+
"opcode": op.code,
|
|
138
|
+
"name": op.name,
|
|
139
|
+
"intensity": intensity,
|
|
140
|
+
"signal": 0.0,
|
|
141
|
+
"phase": event.start_offset,
|
|
142
|
+
"channel": event.channel,
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
return {
|
|
146
|
+
"kind": CORE_KIND,
|
|
147
|
+
"version": 0,
|
|
148
|
+
"source_language": source_language,
|
|
149
|
+
"source": source_path,
|
|
150
|
+
"ontology": [
|
|
151
|
+
{"code": op.code, "name": op.name}
|
|
152
|
+
for op in opcode_ontology.OPCODES
|
|
153
|
+
],
|
|
154
|
+
"entities": entities,
|
|
155
|
+
"relations": [],
|
|
156
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#
|
|
2
|
+
# SPDX-FileCopyrightText: 2026 John Samuel <johnsamuelwrites@gmail.com>
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
"""MIDI projection of a polymodal program.
|
|
8
|
+
|
|
9
|
+
Peer of the linear / spatial / volumetric / sonic projections. Where
|
|
10
|
+
those projections describe continuous shapes (along 1/2/3 spatial axes
|
|
11
|
+
or in audio), MIDI describes the program as a sequence of discrete
|
|
12
|
+
events: note on/off, control change, program change, percussion hits.
|
|
13
|
+
|
|
14
|
+
This is the modality that stress-tests whether the ontology is
|
|
15
|
+
genuinely modality-free. If our opcode primitives only mapped cleanly
|
|
16
|
+
to continuous-shape projections, MIDI would be where that cracked --
|
|
17
|
+
because MIDI's contract is fundamentally event-flavored rather than
|
|
18
|
+
field-flavored. Each opcode commits to a MIDI role
|
|
19
|
+
(``note``, ``drum``, ``cc``, ``program``, ``bus``) via its
|
|
20
|
+
:class:`MidiHint`, and the projection here turns the semantic core
|
|
21
|
+
into a flat list of MIDI events ready for a piano-roll renderer or
|
|
22
|
+
Web MIDI output.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import json
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
from multilingualprogramming.codegen import opcode_ontology
|
|
32
|
+
from multilingualprogramming.codegen.projection_capabilities import capability_contract
|
|
33
|
+
from multilingualprogramming.codegen.semantic_core import build_semantic_core
|
|
34
|
+
|
|
35
|
+
MANIFEST_KIND = "midi-seed-v0"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def build_midi_manifest(
|
|
39
|
+
source: str,
|
|
40
|
+
language: str = "en",
|
|
41
|
+
source_path: str = "",
|
|
42
|
+
) -> dict[str, Any]:
|
|
43
|
+
"""Execute Multilingual source and return a validated MIDI manifest."""
|
|
44
|
+
core = build_semantic_core(source, language=language, source_path=source_path)
|
|
45
|
+
events = [_event_from_semantic_entity(entity) for entity in core["entities"]]
|
|
46
|
+
return {
|
|
47
|
+
"kind": MANIFEST_KIND,
|
|
48
|
+
"version": 0,
|
|
49
|
+
"source_language": language,
|
|
50
|
+
"source": source_path,
|
|
51
|
+
"capabilities": capability_contract(
|
|
52
|
+
projection=MANIFEST_KIND,
|
|
53
|
+
preserves=["id", "opcode", "phase", "channel"],
|
|
54
|
+
derived=["intensity"],
|
|
55
|
+
lossy=["signal", "intensity when velocity clips or base velocity is zero"],
|
|
56
|
+
ambiguous=[],
|
|
57
|
+
inverse="partial",
|
|
58
|
+
),
|
|
59
|
+
"tempo_bpm": 96,
|
|
60
|
+
"bar_seconds": 4.0,
|
|
61
|
+
"events": events,
|
|
62
|
+
"relations": core["relations"],
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def build_midi_manifest_file(
|
|
67
|
+
source_path: str | Path,
|
|
68
|
+
output_path: str | Path,
|
|
69
|
+
language: str = "en",
|
|
70
|
+
) -> dict[str, Any]:
|
|
71
|
+
"""Build and write a MIDI manifest from a Multilingual source file."""
|
|
72
|
+
src = Path(source_path)
|
|
73
|
+
out = Path(output_path)
|
|
74
|
+
manifest = build_midi_manifest(
|
|
75
|
+
src.read_text(encoding="utf-8"),
|
|
76
|
+
language=language,
|
|
77
|
+
source_path=str(src),
|
|
78
|
+
)
|
|
79
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
out.write_text(
|
|
81
|
+
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
|
|
82
|
+
encoding="utf-8",
|
|
83
|
+
)
|
|
84
|
+
return manifest
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _event_from_semantic_entity(entity: dict[str, Any]) -> dict[str, Any]:
|
|
88
|
+
op = opcode_ontology.get(entity["opcode"])
|
|
89
|
+
intensity = float(entity["intensity"])
|
|
90
|
+
|
|
91
|
+
# Bus voices are silent under the forward MIDI projection just as
|
|
92
|
+
# they are silent under sonic. The event is still emitted so peer
|
|
93
|
+
# projections agree on entity count and ordering -- runtimes simply
|
|
94
|
+
# do not transmit it.
|
|
95
|
+
if op.midi.role == "bus":
|
|
96
|
+
velocity = 0
|
|
97
|
+
else:
|
|
98
|
+
velocity = max(0, min(127, round(op.midi.velocity * intensity)))
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
"id": entity["id"],
|
|
102
|
+
"index": int(entity["index"]),
|
|
103
|
+
"opcode": op.code,
|
|
104
|
+
"name": op.name,
|
|
105
|
+
"role": op.midi.role,
|
|
106
|
+
"pitch": op.midi.pitch,
|
|
107
|
+
"velocity": velocity,
|
|
108
|
+
"channel": int(entity["channel"]),
|
|
109
|
+
"start_offset": round(float(entity["phase"]) % 1.0, 4),
|
|
110
|
+
}
|