From 93d90af5e0fc1517f1383b386427be8cf42e8f98 Mon Sep 17 00:00:00 2001 From: timhastie Date: Sun, 11 Oct 2026 20:39:54 -0700 Subject: [PATCH 1/7] FM Synth 0.1.4: update to the author's octatrick 2.12 line (v2.12, ca13ed8) The engine, page and the author's FM SYNTH chooser are vendored byte for byte from timhastie/octatrick-modules v2.12 (ca13ed8): the 2.10 engine with 6 dB of headroom, the 2.11 machine list (a row table, six row-count detours in place of the 2.10 pokes, five row-clamp detours, the SELECT MACHINE TYPE scroll) and the two 2.12 changes: the metronome click no longer cuts a held note (the engine's hook on the click word is removed), and a machine change to or from FM SYNTH resets the track's machine parameters to init, step and scene locks kept as in stock (one new detour, po_unload). With FM Synth alone the list keeps its six rows. Modwerk's machine.s / registration.s are dropped; the bundled quantizer keeps 0.1.2's drawer fix (#264). Every stock expectation is a stock_guard (73). The author's native Analog BD conflict is cleared in the wrapper (Modwerk declares it in octamod.module.json). Emulator evidence on the image built from this source (MAIN OS 00fa6caa): cycles (one VOIC 4 track 81,422 of 95,779 cycles/block, the largest of five runs), exact memory (43,812 module-owned bytes), stack bound 352 B, two-instance Part/project save and reload, the metronome case and the machine-change init, captures, Part smoke, odd-address check. Hardware fields are placeholders for the author's report. The native/browser record is not refreshed: POLY8's bridge is bound to 0.1.2's chooser hooks (TESTING.md). --- docs/VERIFICATION.md | 58 +- scripts/module-source.test.mjs | 2 +- sdk/catalog.json | 2 +- sdk/imports/synth-ca13ed8.json | 474 +++++ sdk/octabam/modules/synth/LICENSE | 63 + sdk/octabam/modules/synth/OCTABAM.md | 161 +- sdk/octabam/modules/synth/README.md | 53 +- sdk/octabam/modules/synth/TESTING.md | 272 ++- .../modules/synth/evidence/native-smoke.json | 13 + .../synth/evidence/release-0.1.2/TESTING.md | 68 + .../release-0.1.2/build-approval.json | 15 + .../{ => release-0.1.2}/composition.json | 0 .../development-regressions.json | 0 .../release-0.1.2}/media/amp-max-fixed.png | Bin .../evidence/release-0.1.2/media/capture.json | 203 +++ .../media/midi-synth-chromatic-fixed.png | Bin .../release-0.1.2}/media/poly-controls.png | Bin .../media/regression-capture.json | 0 .../{ => release-0.1.2}/midi-walk.jsonl | 0 .../{ => release-0.1.2}/mono-max-walk.jsonl | 0 .../{ => release-0.1.2}/poly-max-walk.jsonl | 0 .../{ => release-0.1.2}/regressions.json | 0 .../{ => release-0.1.2}/software-checks.json | 0 sdk/octabam/modules/synth/machine.s | 335 ---- sdk/octabam/modules/synth/manifest.py | 61 +- sdk/octabam/modules/synth/media/LICENSE.md | 6 +- sdk/octabam/modules/synth/media/capture.json | 160 +- .../modules/synth/media/ot-lfo-setup.png | Bin 0 -> 4922 bytes .../modules/synth/media/ot-machine-list.png | Bin 0 -> 3798 bytes sdk/octabam/modules/synth/media/ot-root.png | Bin 3767 -> 0 bytes sdk/octabam/modules/synth/media/ot-voices.png | Bin 5066 -> 5073 bytes sdk/octabam/modules/synth/octamod.module.json | 347 +++- .../modules/synth/qualification.example.json | 93 - sdk/octabam/modules/synth/registration.c | 41 - sdk/octabam/modules/synth/registration.s | 226 --- sdk/octabam/modules/synth/release-notes.json | 16 +- sdk/octabam/modules/synth/stock-guards.json | 328 ++-- sdk/octabam/modules/synth/upstream/LICENSE | 63 + .../synth/upstream/quantizer/README.md | 56 +- .../synth/upstream/quantizer/manifest.py | 9 +- .../synth/upstream/quantizer/quantizer.s | 61 +- .../modules/synth/upstream/synth/README.md | 1286 ++++++++++++-- .../synth/upstream/synth/engine_abi.inc | 55 + .../modules/synth/upstream/synth/machine.s | 952 ++++++++++ .../modules/synth/upstream/synth/manifest.py | 284 ++- .../modules/synth/upstream/synth/page.s | 66 +- .../modules/synth/upstream/synth/poly.s | 1565 +++++++++++++---- sdk/tests/test_synth_release.py | 76 +- src/catalog/module-qualification.test.ts | 2 +- src/catalog/synth-release.test.ts | 51 +- src/community/downloaded-module-guide.ts | 2 +- src/community/module-changelogs.json | 15 + 52 files changed, 5789 insertions(+), 1751 deletions(-) create mode 100644 sdk/imports/synth-ca13ed8.json create mode 100644 sdk/octabam/modules/synth/evidence/native-smoke.json create mode 100644 sdk/octabam/modules/synth/evidence/release-0.1.2/TESTING.md create mode 100644 sdk/octabam/modules/synth/evidence/release-0.1.2/build-approval.json rename sdk/octabam/modules/synth/evidence/{ => release-0.1.2}/composition.json (100%) rename sdk/octabam/modules/synth/evidence/{ => release-0.1.2}/development-regressions.json (100%) rename sdk/octabam/modules/synth/{ => evidence/release-0.1.2}/media/amp-max-fixed.png (100%) create mode 100644 sdk/octabam/modules/synth/evidence/release-0.1.2/media/capture.json rename sdk/octabam/modules/synth/{ => evidence/release-0.1.2}/media/midi-synth-chromatic-fixed.png (100%) rename sdk/octabam/modules/synth/{ => evidence/release-0.1.2}/media/poly-controls.png (100%) rename sdk/octabam/modules/synth/{ => evidence/release-0.1.2}/media/regression-capture.json (100%) rename sdk/octabam/modules/synth/evidence/{ => release-0.1.2}/midi-walk.jsonl (100%) rename sdk/octabam/modules/synth/evidence/{ => release-0.1.2}/mono-max-walk.jsonl (100%) rename sdk/octabam/modules/synth/evidence/{ => release-0.1.2}/poly-max-walk.jsonl (100%) rename sdk/octabam/modules/synth/evidence/{ => release-0.1.2}/regressions.json (100%) rename sdk/octabam/modules/synth/evidence/{ => release-0.1.2}/software-checks.json (100%) delete mode 100644 sdk/octabam/modules/synth/machine.s create mode 100644 sdk/octabam/modules/synth/media/ot-lfo-setup.png create mode 100644 sdk/octabam/modules/synth/media/ot-machine-list.png delete mode 100644 sdk/octabam/modules/synth/media/ot-root.png delete mode 100644 sdk/octabam/modules/synth/qualification.example.json delete mode 100644 sdk/octabam/modules/synth/registration.c delete mode 100644 sdk/octabam/modules/synth/registration.s create mode 100644 sdk/octabam/modules/synth/upstream/synth/engine_abi.inc create mode 100644 sdk/octabam/modules/synth/upstream/synth/machine.s diff --git a/docs/VERIFICATION.md b/docs/VERIFICATION.md index 1954b4040..07938a822 100644 --- a/docs/VERIFICATION.md +++ b/docs/VERIFICATION.md @@ -1170,7 +1170,6 @@ Waives fresh physical reboot, Part/project restore after unsaved edits, distinct-track isolation and stock/flood performance tests. Retain the author's MKII functional report; missing checks stay unverified. - ## Air Chorus 0.1.0 — 9 October 2026 Chris Johnson’s MIT-licensed Airwindows Chorus is ported from pinned published @@ -1388,3 +1387,60 @@ E-Verb is in none of them. No hardware result exists for 0.1.1; the owner's MKII report covers 0.1.0. Publication needs the owner's approval of this exact source. + +## FM Synth 0.1.4 (octatrick 2.12) — 11 October 2026 + +Local draft by the module author's port; not published, no owner approval. +FM Synth moves to the author's octatrick 2.12 line +(`timhastie/octatrick-modules` `ca13ed8`, tag `v2.12`). The engine, page, +the author's FM SYNTH chooser (`machine.s`, adapted from Modwerk's 0.1.1 +chooser, same Part signature) and its machine-list table are vendored byte +for byte. Modwerk's `machine.s` / `registration.s` and their hooks are +dropped; the bundled quantizer keeps 0.1.2's drawer fix. The six chooser +row-count pokes of the 2.10 line are detours reading the table, five more +widen the machine window's row clamp, and SELECT MACHINE TYPE can scroll. +2.12 removes the engine's hook on the metronome's click word (with CLICK ON +it released held notes at every beat) and adds a machine-change init: a +change to or from FM SYNTH resets the track's PLAYBACK, PLAYBACK SETUP, AMP, +AMP SETUP, LFO and LFO SETUP to init, keeping step and scene locks and FX1 / +FX2 as a stock machine change does (one new detour, `po_unload` at +`0x40022732`). The engine carries the author's 6 dB of headroom in place of +0.1.2's 18.06 dB `OUTPUT_SHIFT`: 0.1.4 plays about 12 dB louder than 0.1.2. +Inventory: `sdk/imports/synth-ca13ed8.json`; native source SHA-256 `4fc06072…`. + +The native exporter built the standalone image, MAIN OS `00fa6caa…` +(update file 462,396 B, `2267af13…`; Modwerk's TS packer decodes it to that +MAIN OS and re-encodes the same bytes). The author's page linked to his +ratified bytes, and the author's `tools/align_check.py` (v2.12) found every +instruction and every detour target at an even address. The module's own +`verify.py` Part smoke passed on that image. Seven of the eight LCD captures +are byte-identical to the 2.11 build's; the LFO page's VOIC dial now shows +the init byte. In the emulator on the same image, a held key is +sample-identical with the metronome on and off and a held VOIC 4 chord's +voices are never released (the 2.11 build `248d987e…` cut both), and +FLEX → FM SYNTH → FLEX → FM SYNTH gives FM SYNTH's init both times with FX1 +kept. Emulator qualification: one VOIC 4 track at 81,422 of 95,779 +cycles/block (the largest of five runs, + the wrapper allowance), exact +memory of 10,505,016 bytes, of which 43,812 are module-owned, and a 352-byte +stack bound from the assembled runtime. Two FM tracks with distinct +settings pass Part save/reload, project save/reload and a fresh project +load in the emulator; the battery-RAM-only restart needs the unit. +The hardware fields are `TIM:` placeholders pending the author's MKI report of +this exact image, so the publication gate fails there. + +POLY8 0.2.6's shared-chooser bridge is bound to 0.1.2's chooser claims, which +this update replaces: a selection with POLY8 and FM Synth stops at the +bridge's seam check on both sides. `module:verify -- synth` therefore stops at +the first such selection, and two POLY8 composition tests fail; the +`sdk/native-comparisons/synth.json` record is not refreshed. Without POLY8 in +the pool (a local run on main `3151975`, not committed) the comparison gave +122 selections: 35 builds match outside the platform writes, 87 refusals +match, 0 mismatches, the native side on the host toolchain instead of Docker. + +Packages were rebuilt from source with `--include-requested` and imported +with `--development` on main `3151975`; only the Synth objects change. The +DSP assembler was Modwerk's pinned dsp56300 with the current patch. The +retained Analog BD record was re-verified against the new packages (162 +native OS and GNU bootloader comparisons, 50 matching refusals, 6 firmware +round trips) before its source-inventory binding was updated. No firmware is +committed. diff --git a/scripts/module-source.test.mjs b/scripts/module-source.test.mjs index 88aeca2b9..d85c3a80c 100644 --- a/scripts/module-source.test.mjs +++ b/scripts/module-source.test.mjs @@ -27,7 +27,7 @@ describe('release package scope and reviewed source inventory', () => { expect(versions['midi-scenes']).toBe('0.2.5-experimental') expect(versions.miniverb).toBe('0.2.1-experimental') for (const id of verifiedRequested) { - expect(versions[id]).toBe(id === 'analog-bassdrum' ? '0.1.5-experimental' : id === 'vector' ? '0.2.4-experimental' : id === 'quantizer' ? '0.1.3-experimental' : id === 'synth' ? '0.1.2-experimental' : id === 'usb-audio-out-tracks-main-cue' ? '0.2.0-experimental' : '0.1.2-experimental') + expect(versions[id]).toBe(id === 'analog-bassdrum' ? '0.1.5-experimental' : id === 'vector' ? '0.2.4-experimental' : id === 'quantizer' ? '0.1.3-experimental' : id === 'synth' ? '0.1.4-experimental' : id === 'usb-audio-out-tracks-main-cue' ? '0.2.0-experimental' : '0.1.2-experimental') expect(paths).toContain('modules/' + id + '/manifest.py') } expect(paths).toContain('modules/midi-scenes/recipe.json') diff --git a/sdk/catalog.json b/sdk/catalog.json index 47777ace1..f423c1628 100644 --- a/sdk/catalog.json +++ b/sdk/catalog.json @@ -74,7 +74,7 @@ }, { "id": "synth", - "version": "0.1.2-experimental", + "version": "0.1.4-experimental", "addedAt": "2026-10-06T08:00:28Z" }, { diff --git a/sdk/imports/synth-ca13ed8.json b/sdk/imports/synth-ca13ed8.json new file mode 100644 index 000000000..e4f9144eb --- /dev/null +++ b/sdk/imports/synth-ca13ed8.json @@ -0,0 +1,474 @@ +{ + "schemaVersion": 1, + "id": "synth", + "version": "0.1.4-experimental", + "status": "local-draft-unpublished", + "basedOn": "31519756b17566c35ad67c61301b770bc5522a07", + "originalImport": "synth-949f3be.json", + "previousUpdate": "synth-fixes-273-264.json", + "folder": "sdk/octabam/modules/synth", + "authorPin": { + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "path": "synth", + "version": "2.12 (tag v2.12)" + }, + "hooks": { + "droppedModwerkChooser": [ + "machine.s, registration.c, registration.s (units fmmachine/fmregistration of 0.1.1-0.1.2)", + "fm_resolve_pb 0x40031e74 (8 B): the author routes PLAYBACK through pg_resolve only", + "fm_name_b 0x4004c36a (6 B): not used by the author", + "fm_main_commit at 0x4007991c (6 B): the author commits at the MACHWIN site 0x40079816 instead", + "SymbolRef 0x400d6458 -> poly:fm_start and Detour 0x4000d514 -> poly:fm_source_dispatch: replaced by the author's po_fmstart / po_fmsource at the same sites", + "Detour 0x40031ece -> fmpage:pg_resolve: replaced by a guarded poke writing the author's emit_page jump (page.s stays byte-identical)" + ], + "sameSiteNowAuthor": [ + "0x400334d8", + "0x4003c928", + "0x4003d718", + "0x4003c980", + "0x400786c8 (6 B, was 4)", + "0x400585dc", + "0x40078886", + "0x4005221e", + "0x4003a52e", + "0x4003cd98", + "0x4005a616", + "0x4005a850", + "0x40002318", + "0x4000d514" + ], + "restoredAuthor": [ + "0x40079816 MACHWIN -> fmmachine:fm_main_commit (then po_machwin)", + "0x4005a848 LISTWIN -> poly:po_machlist" + ], + "added2_10": [ + "0x4003bff2 po_lfdname", + "0x4003bffa po_lfdlfo", + "0x400392cc po_lfdedit" + ], + "added2_11": [ + "0x40079248 ml_win_count (8 B), 0x400585fa ml_src_count (8 B), 0x4003c950 ml_name_bound (6 B), 0x40078678 ml_draw_bound (12 B), 0x400786ce ml_hi_bound (6 B), 0x40079904 ml_cur_bound (8 B): the six row-count / row-bound pokes of 2.10 (5 -> 6) became detours reading the row table's count (machine.s ml_rows; one row, FM SYNTH, in this package)", + "0x40078c22 0x40078cb0 0x40078d2e 0x40078dee 0x40078e7c ml_clamp1..5 (8 B each): the machine window's row clamp after a list move, 0 .. the last row (stock 0..5)", + "ml_draw_bound draws name(row + the list's top): SELECT MACHINE TYPE scrolls like SRC SETUP when a build has more than six rows" + ], + "droppedAuthorNative": [ + "conflicts=((\"ANALOG BD\", ...),) in the author's manifest: cleared by the wrapper (Modwerk declares the conflict in octamod.module.json; a native conflicts entry turns on the browser's span-contract check for every module)" + ], + "added2_12": [ + "0x40022732 po_unload (jsr, 6 B): the file browser's load callback, the slot unload before the select -- the FLEX slot's marker state is kept for po_loadsel, so a machine change through the file browser is seen (the machine-change init, machine.s ml_reset)" + ], + "removed2_12": [ + "0x4000b2c8 po_stop (jmp, 6 B): the frame builder's clear of 0x46c80350, the METRONOME's click word (taken for a STOP word since the 2.9 line). With CLICK ON it released every held note at each beat. The address is stock again; STOP stays with po_kill at the stock voice kill 0x4000685c." + ] + }, + "files": [ + { + "path": "LICENSE", + "sha256": "4d8c71b951cb102a1e6cb761d8e1101748346de633d695a85c48461f49a2cfa2", + "previousSha256": "34b2f2d7d3663d1efdcc387b3c8f48ddf30ad89970621ab29449e431c22964af" + }, + { + "path": "OCTABAM-LICENSE", + "sha256": "f91a43799d9e31ccc3540f385f68c4d6ff5a096f93ae933be2ce98f6b3f7f113", + "previousSha256": "f91a43799d9e31ccc3540f385f68c4d6ff5a096f93ae933be2ce98f6b3f7f113" + }, + { + "path": "OCTABAM.md", + "sha256": "be0806b7c13cca799018bce3e1f431bf7c29362ff16ca04ad06e27a80b95eedf", + "previousSha256": "876e518fb5f33c2e4db2e3d9cf881fd66ef2082921d977d79c1b15d069cdc7a9", + "changes": [ + "Rewritten for the 2.10 line; adapted from Sam Banks's octabam modules/synth/README.md at 949f3be." + ] + }, + { + "path": "README.md", + "sha256": "827248dce8d0577732c416ff2921d8ef34d7383740e341b1b752b4b8b63b4fb5", + "previousSha256": "4488f2fa869a8cba5a428a0287a63d118e829274f6857d1ad39f1d00c88db89b" + }, + { + "path": "TESTING.md", + "sha256": "08b83d4b002489695b300efae2b58c4d023085dbc7668671e0e96c3ae688e0ad", + "previousSha256": "d096affbb1985a3fd495a55d546cd67759973d84754aa68eaff22048a04be60c" + }, + { + "path": "evidence/native-smoke.json", + "sha256": "66ebc4af8515b6af8a83730e04769fc8a85a806c3274fa3e652cba78adefbcdc", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.1/TESTING.md", + "sha256": "682641b3cbfec00bb58b70ea5cd172d8b07e63430c2af851ccea3f3ee9e58173", + "previousSha256": "682641b3cbfec00bb58b70ea5cd172d8b07e63430c2af851ccea3f3ee9e58173" + }, + { + "path": "evidence/release-0.1.1/audio.json", + "sha256": "aea4e6b640318b3db237cb0bfe0af184f303c0a70caf6870cc8a630225e49668", + "previousSha256": "aea4e6b640318b3db237cb0bfe0af184f303c0a70caf6870cc8a630225e49668" + }, + { + "path": "evidence/release-0.1.1/browser-audio.json", + "sha256": "cb35c8c26264ca44cb98ccc3f66a053f1ff93c1ac2d414dbc397736d43dab028", + "previousSha256": "cb35c8c26264ca44cb98ccc3f66a053f1ff93c1ac2d414dbc397736d43dab028" + }, + { + "path": "evidence/release-0.1.1/build-approval.json", + "sha256": "bb73f69b436d48c6e1a6c38b8ff2187d6c6512130d770634e279725b2ebebbda", + "previousSha256": "bb73f69b436d48c6e1a6c38b8ff2187d6c6512130d770634e279725b2ebebbda" + }, + { + "path": "evidence/release-0.1.1/native-build.json", + "sha256": "618c96e7c6eb1f412bd1ef1cc82c0887814f6ddb99dd6a69f2e431f0a2fb1f8f", + "previousSha256": "618c96e7c6eb1f412bd1ef1cc82c0887814f6ddb99dd6a69f2e431f0a2fb1f8f" + }, + { + "path": "evidence/release-0.1.1/octemu-walk.jsonl", + "sha256": "7625d9d9047393924668b8fee9d6c032507c6087b14b48fcec01d81e5ba9bc3b", + "previousSha256": "7625d9d9047393924668b8fee9d6c032507c6087b14b48fcec01d81e5ba9bc3b" + }, + { + "path": "evidence/release-0.1.1/owner-hardware.md", + "sha256": "f48c3e5b54463bf91b695189e6c5e9a51b9876bfea5561e775b550c1e25d7e6f", + "previousSha256": "f48c3e5b54463bf91b695189e6c5e9a51b9876bfea5561e775b550c1e25d7e6f" + }, + { + "path": "evidence/release-0.1.1/software.json", + "sha256": "f74a7cf16898043c03abe3e3c1b591abfe5d908eecc76367aceddb6d2e8b85a0", + "previousSha256": "f74a7cf16898043c03abe3e3c1b591abfe5d908eecc76367aceddb6d2e8b85a0" + }, + { + "path": "evidence/release-0.1.1/storage.json", + "sha256": "87b0368ad8e955f178531cea0faeb3b7124e2c211c58899b9ea3015318ce6ed6", + "previousSha256": "87b0368ad8e955f178531cea0faeb3b7124e2c211c58899b9ea3015318ce6ed6" + }, + { + "path": "evidence/release-0.1.2/TESTING.md", + "sha256": "d096affbb1985a3fd495a55d546cd67759973d84754aa68eaff22048a04be60c", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/build-approval.json", + "sha256": "11984dd38136e2947c6e05920b71cd65ecfb4b2d85074c4d781f720502f0238f", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/composition.json", + "sha256": "e47b77ee626dd014836e6561eb3e68034b68b404c77bc19d203347695dc1f466", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/development-regressions.json", + "sha256": "10772b3699adebb982818e09620c4febbd2e45ef7b67ed6ca49ad4d82eee8b69", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/media/amp-max-fixed.png", + "sha256": "d776d287d403deffa07330350a33d4b2313db2c6d0710610ec46b4ba98b0fc1f", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/media/capture.json", + "sha256": "3e90641f7a8a8b0f872864f308836308d4fd3190e2095f507c4f724fd5f2a34b", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/media/midi-synth-chromatic-fixed.png", + "sha256": "0b4a8a395c4e17f4fd4c675857e692716c7d0f9345b45680887fb25edd4f2468", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/media/poly-controls.png", + "sha256": "376fcdc83ed4df158bf6cb747f6deb92e2518172f02f5f23ca9a946b6000911a", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/media/regression-capture.json", + "sha256": "a42ea9a3bfd9ee50445face5a551235fbae5a10579820cc2bc44e7bbdfc634f6", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/midi-walk.jsonl", + "sha256": "a755ea8eaf205ab4c3a0f5639dcaee4019d5602aeb6dec5bc026d0bb4272b4ba", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/mono-max-walk.jsonl", + "sha256": "a5bafa1d26e8d09771ae4b7e4aee046fddc10dac88cd9cee482179bac6be2272", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/poly-max-walk.jsonl", + "sha256": "4d407f2777a1b166ef11b096b80bbeaf3c4f546f647d776461cd821e852ed615", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/regressions.json", + "sha256": "cf0043058c17eb4655b1d22c441063f73dbf6d42984506a7041a95b18a4d8e15", + "previousSha256": null + }, + { + "path": "evidence/release-0.1.2/software-checks.json", + "sha256": "61f410796ea1453cd657614629c8e71a537ef9bdc89f42f27085b7b76dac0fb4", + "previousSha256": null + }, + { + "path": "manifest.py", + "sha256": "e1f89e0e9557e9e615af51e07e87140fc1e8b3a300956339a2f25748a641ef92", + "previousSha256": "7854330aca001d2841de2fb384f0ca8adb7497db49dacc56da2815c1f444c0e6", + "changes": [ + "Rewritten: the author's manifest (all hooks, including his FM SYNTH chooser) plus the privately bundled quantizer, the Linked page with reference = the author's ratified page, and the resolver jump as a guarded poke. Modwerk's chooser registration (machine.s, registration.s) and its fm_start / fm_source_dispatch / fm_resolve_pb / fm_name_b hooks are dropped." + ] + }, + { + "path": "media/LICENSE.md", + "sha256": "9157e7a9de0a397617e28af2326c2e83d7ecb98eee209ece7495f9b5850622fe", + "previousSha256": "e0d1e97be350749065d0b1d766fc8e33bd89b32a6b3bd6b816cec4e6b56bdbb9" + }, + { + "path": "media/capture.json", + "sha256": "aba4e896a00418964b801da68fbccd8869523d2ddeb08cb979c22fa617c672aa", + "previousSha256": "3e90641f7a8a8b0f872864f308836308d4fd3190e2095f507c4f724fd5f2a34b" + }, + { + "path": "media/ot-controls.png", + "sha256": "2eb52e9b732308891e289aa23d92556a87dd8820b12a37cbe73f66e720a162f0", + "previousSha256": "2eb52e9b732308891e289aa23d92556a87dd8820b12a37cbe73f66e720a162f0" + }, + { + "path": "media/ot-glide.png", + "sha256": "77b4cf73fd16e96d1c78f5a9cdd0590b20a7958c614d1c6cdb93b77791dc505b", + "previousSha256": "77b4cf73fd16e96d1c78f5a9cdd0590b20a7958c614d1c6cdb93b77791dc505b" + }, + { + "path": "media/ot-legato.png", + "sha256": "e5cd86583126a834f8a9351a8f2b51bca9f76c101023e37db4c933be957d5bc8", + "previousSha256": "e5cd86583126a834f8a9351a8f2b51bca9f76c101023e37db4c933be957d5bc8" + }, + { + "path": "media/ot-lfo-setup.png", + "sha256": "ab3071680706882acdf5f9ccdefb1adcc24c50690afd90560ca42e25535e3e94", + "previousSha256": null + }, + { + "path": "media/ot-location.png", + "sha256": "4e1d84898790433813720c437d2e767cb3225c7770ea15be9115f76e70b82270", + "previousSha256": "4e1d84898790433813720c437d2e767cb3225c7770ea15be9115f76e70b82270" + }, + { + "path": "media/ot-machine-list.png", + "sha256": "47305d8bdb86bc29f838f4e3ba712af7b0a6e6d3c063e987adc9a59f82daa8ba", + "previousSha256": null + }, + { + "path": "media/ot-scale.png", + "sha256": "71ffe8fe7413a1e2eb0605ad7119159bba95db7f7734dc47ae13468f68a74d13", + "previousSha256": "71ffe8fe7413a1e2eb0605ad7119159bba95db7f7734dc47ae13468f68a74d13" + }, + { + "path": "media/ot-voices.png", + "sha256": "603b3e5d31446bf31570b02dc56b6efccb958b371a06b87c3dc79a6a6090d1e5", + "previousSha256": "0e99bbc3cdecc61bb66920b5648f517198b191ad3dc04a78af14e2256e1cdf3c" + }, + { + "path": "octamod.module.json", + "sha256": "e84133735551ac8a6c7cbc98a91d61489691accd7607e6b8e1708edeab517755", + "previousSha256": "3a09d67d94ffaee529f261153cb3101e8aabcb1e4bca31b029e0bf7e66200e27" + }, + { + "path": "presentation/LICENSE", + "sha256": "dc99139facc8371c0830c830eb381263467de65e6fd834ff7f1642996dd9bc8a", + "previousSha256": "dc99139facc8371c0830c830eb381263467de65e6fd834ff7f1642996dd9bc8a" + }, + { + "path": "presentation/thumbnail.svg", + "sha256": "451e1fcf768d8c13ea8f5be9c77c9ad82c49bc28c373fb07c8809954dfd3e21f", + "previousSha256": "451e1fcf768d8c13ea8f5be9c77c9ad82c49bc28c373fb07c8809954dfd3e21f" + }, + { + "path": "release-notes.json", + "sha256": "f84f858de9b6817dde325e197c51c8f91fb7601ceeafdf97d1da5b4e54d360af", + "previousSha256": "f0d501ff52c79c6ad7ad62f8ad91b70aaf6d9fff5737677a0d5a9b5d6a313c44" + }, + { + "path": "stock-guards.json", + "sha256": "09c5cc87e78b0a0498629137233f6fd9eba6bb49772f10291f371a942f4d6a95", + "previousSha256": "3a197afbd8994e969a9d431b5b3ba4b2c4f0a82d390e9c787199c3610ba93aaf" + }, + { + "path": "upstream/LICENSE", + "sha256": "1f72dbab3547d1d94861cf9f129519301295395129fe3f56760ed767d07f04fe", + "previousSha256": "a8fe424c82a030f34f2e3f88df1f2585f97ae61710e1b799f2e6e818120396a0", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "LICENSE", + "sourceBytes": 4876, + "sourceSha256": "1f72dbab3547d1d94861cf9f129519301295395129fe3f56760ed767d07f04fe" + }, + { + "path": "upstream/quantizer/README.md", + "sha256": "9b7e0323af6133d0260b2e1a69c4321c8af4dad776bbce64fd8b4b9a8c04bfb2", + "previousSha256": "25b9e9a1d1fb4be217514727374922e60010fc0067fe8c5c3f17ced909c8d94e", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "quantizer/README.md", + "sourceBytes": 76793, + "sourceSha256": "9b7e0323af6133d0260b2e1a69c4321c8af4dad776bbce64fd8b4b9a8c04bfb2" + }, + { + "path": "upstream/quantizer/core.s", + "sha256": "2992b6c85cff440de03ae84eb22dee8b8a83a84f23e94b699404d28940b6f958", + "previousSha256": "2992b6c85cff440de03ae84eb22dee8b8a83a84f23e94b699404d28940b6f958", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "quantizer/core.s", + "sourceBytes": 5883, + "sourceSha256": "2992b6c85cff440de03ae84eb22dee8b8a83a84f23e94b699404d28940b6f958" + }, + { + "path": "upstream/quantizer/keys.s", + "sha256": "e2ffdbfae34b0828794a4eb4cc229e3efcaed25dc953bf7c05c13f6e37f65587", + "previousSha256": "e2ffdbfae34b0828794a4eb4cc229e3efcaed25dc953bf7c05c13f6e37f65587", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "quantizer/keys.s", + "sourceBytes": 1765, + "sourceSha256": "e2ffdbfae34b0828794a4eb4cc229e3efcaed25dc953bf7c05c13f6e37f65587" + }, + { + "path": "upstream/quantizer/manifest.py", + "sha256": "f6a4af8e934500c57eacec9fb5a994ecd2c0821f60d05696249a3ea79bf6215b", + "previousSha256": "e637e3718e4b65320b847ef0758e0d46800f8be6b8020290f89192c798c535ef", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "quantizer/manifest.py", + "sourceBytes": 20517, + "sourceSha256": "c30c9ea257be60c41d7f2be0b0dc4b1076703a3773d054d7e7622c4fe8731ae5", + "changes": [ + "Modwerk's reviewed stock-guard form, with 0.1.2's octave drawer detour at 0x400449be; the author's 2.11 per-build include (_qz_include: HAVE_SYDRUM, 0 without SY DRUM) on the quantizer's DRAM unit." + ] + }, + { + "path": "upstream/quantizer/quantizer.s", + "sha256": "0e6c0a0238a563a1092eb9ab8c1b7962038a6ca74d29f8978cee3bb8e5eccba7", + "previousSha256": "d1737168a1750f540b3dfbefdd2a64b11084013b9f7b71b31aa4f189d33a24c5", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "quantizer/quantizer.s", + "sourceBytes": 70911, + "sourceSha256": "e87072880f568ae53072181191e85ff3bf4f1375a1f7584c07ef1906bfae1ed0", + "changes": [ + "Modwerk 0.1.2's octave-number drawer fix (#264) applied on top of the author's file (unchanged from 2.11 in 2.12; it recognizes the FM/1 and, when HAVE_SYDRUM is 1, the SY/1 Part signature; with HAVE_SYDRUM 0 the unit assembles as the 2.10 file)." + ] + }, + { + "path": "upstream/quantizer/scale.s", + "sha256": "82242c407847ac61a6f60fe15bdc5b0b4fc852bac82c39745b5c838ec9389c64", + "previousSha256": "82242c407847ac61a6f60fe15bdc5b0b4fc852bac82c39745b5c838ec9389c64", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "quantizer/scale.s", + "sourceBytes": 695, + "sourceSha256": "82242c407847ac61a6f60fe15bdc5b0b4fc852bac82c39745b5c838ec9389c64" + }, + { + "path": "upstream/synth/README.md", + "sha256": "8c3f499e4d327c78115a42c971e3f39f27d6dd7a4dffde222d3e3d8fb8f368ad", + "previousSha256": "810ece9805aef09aea1fab319185cbd4833ddb7bef42c7dd0af182da59b798d8", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "synth/README.md", + "sourceBytes": 324402, + "sourceSha256": "8c3f499e4d327c78115a42c971e3f39f27d6dd7a4dffde222d3e3d8fb8f368ad" + }, + { + "path": "upstream/synth/engine_abi.inc", + "sha256": "50a855a7c0e148332bae159159584965baa709230b7e59d0bf8644c7ff51adf6", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "synth/engine_abi.inc", + "sourceBytes": 4596, + "sourceSha256": "50a855a7c0e148332bae159159584965baa709230b7e59d0bf8644c7ff51adf6" + }, + { + "path": "upstream/synth/machine.s", + "sha256": "63ff52340e82f55535a81a678921f3be1c7a281dcd05f25aaea072221c01cd50", + "previousSha256": null, + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "synth/machine.s", + "sourceBytes": 42214, + "sourceSha256": "63ff52340e82f55535a81a678921f3be1c7a281dcd05f25aaea072221c01cd50" + }, + { + "path": "upstream/synth/manifest.py", + "sha256": "197a85ad6a7abd5934ce423f6b227da26451bb2a933af890255fe3fb1c368383", + "previousSha256": "58c7acd5684fe0b59c42501f7fdbfd1b288b3bfb71938dcadf43bc1774b3e5e8", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "synth/manifest.py", + "sourceBytes": 60294, + "sourceSha256": "0337158f4cfe0500a88d42baaddcb679d7111aedc5f79250e18822c46c2cee6a", + "changes": [ + "Every embedded stock expectation replaced by stock_guard(address, length, SHA-256) (53 guards: the 2.10 hook guards and 11 for the 2.11 machine-list detours, the five row-clamp sites sharing one hash, and 2.12's po_unload site in place of the removed po_stop site; each checked against the developer's MAIN OS 164f3122...); the renderer address kept as an integer; the second LFO-depth site guarded on its own.", + "The precompiled PINNED_PAGE blob, its asserts, emit_page and the CavePatch removed (cf_patches=()); the top-level manifest links page.s instead and checks it against the author's ratified page SHA-256." + ] + }, + { + "path": "upstream/synth/page.s", + "sha256": "8d85620ee336174eea12767745023897ff99b8f63589bff9dfd817642564965d", + "previousSha256": "25e2a0624fcc30ee3a8db5966954113119fb5942855042f765e664a2f8544a70", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "synth/page.s", + "sourceBytes": 27806, + "sourceSha256": "8d85620ee336174eea12767745023897ff99b8f63589bff9dfd817642564965d" + }, + { + "path": "upstream/synth/poly.s", + "sha256": "9d2f68963a735b98e909d7537ce8896eba370aa651f984dbbb5085354071e6d8", + "previousSha256": "ce21f5f7cddcd7268d27e25c500f1403cc59a9dff28044b1832cf2b45592d34c", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "sourcePath": "synth/poly.s", + "sourceBytes": 298601, + "sourceSha256": "9d2f68963a735b98e909d7537ce8896eba370aa651f984dbbb5085354071e6d8" + }, + { + "path": "verify-audio.py", + "sha256": "d02b7b916d9b198b9e10e34d21c7eb0242267ef8bdac5762b5958721c05b0283", + "previousSha256": "d02b7b916d9b198b9e10e34d21c7eb0242267ef8bdac5762b5958721c05b0283" + }, + { + "path": "verify-regressions.py", + "sha256": "86f6eb9e2a0c738212703bfcec2ad830910702e4064052b1b7e2bc10fbba444d", + "previousSha256": "86f6eb9e2a0c738212703bfcec2ad830910702e4064052b1b7e2bc10fbba444d" + }, + { + "path": "verify.py", + "sha256": "685d4aa146ed206caf8c9d78acf0f635e727f8eccc359167bb46205d647f3a75", + "previousSha256": "685d4aa146ed206caf8c9d78acf0f635e727f8eccc359167bb46205d647f3a75" + } + ], + "removed": [ + "evidence/composition.json", + "evidence/development-regressions.json", + "evidence/midi-walk.jsonl", + "evidence/mono-max-walk.jsonl", + "evidence/poly-max-walk.jsonl", + "evidence/regressions.json", + "evidence/software-checks.json", + "machine.s", + "media/amp-max-fixed.png", + "media/midi-synth-chromatic-fixed.png", + "media/ot-root.png", + "media/poly-controls.png", + "media/regression-capture.json", + "qualification.example.json", + "registration.c", + "registration.s" + ], + "notes": [ + "The author's ca13ed8 is timhastie/octatrick-modules main and tag v2.12 (pushed 11 October 2026). From 5f3d745 (v2.11) it changes, for this module, synth/poly.s (po_stop and its detour removed: the metronome fix), synth/machine.s (ml_reset: a machine change to or from FM SYNTH / SY DRUM resets the track's machine parameters to init; step and scene locks kept, as in stock), synth/manifest.py (the po_stop detour removed, the po_unload detour added), synth/engine_abi.inc (a comment) and synth/README.md. synth/page.s and quantizer/ are unchanged from v2.11. The author's direct-jump, tuner and processor-load modules are untouched; this package does not carry them.", + "0.1.3-experimental was a local, unsubmitted draft of the 2.10 line; 0.1.4-experimental was first drafted on v2.11 (5f3d745, record synth-5f3d745.json, replaced by this one) and is still unpublished, so the version stays 0.1.4-experimental. The published version before 0.1.4 is 0.1.2-experimental." + ] +} diff --git a/sdk/octabam/modules/synth/LICENSE b/sdk/octabam/modules/synth/LICENSE index 27023a6de..603d2c5f8 100644 --- a/sdk/octabam/modules/synth/LICENSE +++ b/sdk/octabam/modules/synth/LICENSE @@ -81,6 +81,69 @@ hardware this project targets. --- +The FM SYNTH machine-list chooser -- synth/machine.s and the chooser's parts of +synth/poly.s, synth/page.s and quantizer/quantizer.s -- is adapted from +Modwerk's FM Synth module (https://github.com/repeat98/modwerk, +sdk/octabam/modules/synth, commit 1c1d938), whose chooser is derived from +Modwerk's Analog BD chooser hooks. Their notices, as that module's LICENSE +carries them: + +Dedicated chooser, Part validation, sample-free transport and original presentation: + +MIT License + +Copyright (c) 2026 Modwerk contributors + +Portions of the firmware analysis tooling originate from the octamax project +(https://github.com/mxldyn/octamax), Copyright (c) 2025-2026 Maxolydian, +also under the MIT License. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +MIT License + +Copyright (c) 2026 Sam Banks + +Portions of the firmware analysis tooling originate from the octamax project +(https://github.com/mxldyn/octamax), Copyright (c) 2025-2026 Maxolydian, +also under the MIT License. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + Dedicated chooser, Part validation, sample-free transport and original presentation: MIT License diff --git a/sdk/octabam/modules/synth/OCTABAM.md b/sdk/octabam/modules/synth/OCTABAM.md index 86521c63a..3a9259642 100644 --- a/sdk/octabam/modules/synth/OCTABAM.md +++ b/sdk/octabam/modules/synth/OCTABAM.md @@ -1,104 +1,85 @@ -# `synth` — SYNTH MACHINE +# `synth` — SYNTH MACHINE (octabam build notes, 2.12) -A two-operator FM synth machine for FLEX tracks, built from -[timhastie/octatrick-modules](https://github.com/timhastie/octatrick-modules) -(submodule `upstream/`, pinned to `v2.9` (`525f4b1`)). `Kind.CF_PATCH`: the voice engine as -a DRAM unit (`poly.s`), the page code as a pinned ROM cave (`page.s`; the -page descriptor itself is cloned from the unit's own ROM at first use, so no -stock bytes ship in the repo), a `SymbolRef` on the kind table's FLEX -renderer entry (and the STATIC entry, for the sample-track glide), detours -and pokes. No DSP code, no FX2 row. Since 2.9 the engine owns the AMP -envelope of a synth track and there is no peak limiter. - -Any FLEX track whose sample is named FMSYNTH*.wav becomes a synth (a -silent 4 s marker file will do; SYNTH*.wav is still accepted, and the -marker's length never ends the note): the DSP shapes and effects the voice -as a sample. Its PLAYBACK page reads PTCH RATO INDX FINE FDBK DEC (title FM -SYNTH; PTCH in semitones, FINE in cents); on the LFO page VOIC (1 = mono, -2..4 = paraphonic) and CHRD (32 chord shapes in Syntakt order, each with -three inversions on the knob, lockable per step, snapped onto SCALE -QUANTIZER's scale); GLIDE from the quantizer's row. Since 2.8: MIDI IN -plays a synth track like the CHROMATIC keys (one voice a note up to VOIC, a -note-off releases it); a chord fingered on the keys or over MIDI during live -recording is recorded as PTCH / CHRD / VOIC locks (the bass, the shape and -its inversion, the voices heard; keys within a rolling 150 ms join, a -legato hand-over records single notes); LEG on every audio track's AMP -SETUP page (OFF / MONO on a sample track, OFF / MONO / POLY on a synth -track) is the legato switch, GLIDE the slide time, and a FLEX or STATIC -track with LEG MONO slides every pitch change over GLIDE; in GRID -RECORDING a held trig + FUNC + DOWN / UP moves its PTCH lock an octave. -Since 2.9: the engine owns the AMP envelope -- ATK / HOLD / REL are read -from the AMP page (locks, scenes and LFOs included) and applied in the -engine with the DSP's own laws, the DSP seeing an always-open envelope, so -a voice is never cut: a re-press continues the sounding oscillator from its -level, a faded note starts from silence over 16 frames, a release is at -least 1 ms, stealing and VOIC hand-overs fade, STOP kills at the stock -voice kill; the peak limiter is gone (the 1/sqrt(VOIC) level law stays, a -4-note chord about -10 dBFS); FINE reads 0c the moment a track becomes a -synth track (a file-browser load into a new project included); a sequencer -trig on a still-sounding note is delivered in the panel key's form -(`po_retrig`) so the DSP keeps its voice; a warm START ramps the FM index -envelope over 16 frames; chords snap onto SCALE QUANTIZER's root. Known -limit: a MACHINE change while a REL INF note sounds leaves it sounding -until the next key. `upstream/synth/README.md` is the full description -(the five phases, the voice model, what was measured and what was -inferred). +Adapted from Sam Banks's octabam `modules/synth/README.md` (pinned at +`949f3be`, MIT) for the octatrick 2.12 line the module now carries. -## Measured +A two-operator FM synth machine built from +[timhastie/octatrick-modules](https://github.com/timhastie/octatrick-modules) +at `ca13ed8` (tag `v2.12`), vendored under `upstream/`. +`Kind.CF_PATCH`: the voice engine and the FM SYNTH chooser as DRAM units +(`poly.s`, `machine.s`), the PLAYBACK page as a pinned ROM cave (`page.s`; +the page descriptor itself is cloned from the unit's own ROM at first use, +so no stock bytes ship), `SymbolRef`s on the kind table's FLEX renderer, +STATIC renderer and FLEX START entries, and detours. No DSP code, no FX2 +row. The engine owns the AMP envelope of a synth track. -- At `v9.1`, remixes `octatrick` and `octatrick-usb` built byte-identical - with the module sources as a plain `modules//` directory and as this - wrapper over the submodule (same base, same build reports), and - byte-identical to the OCTATRICK9 images Tim flashed (built on - upstream `0e93543`; upstream's changes since touch modules these - remixes do not carry). -- The 2.8 features (MIDI IN, chord recording with inversions, the LEG - modes, the sample-track glide, step transpose, the chord order) and the - 2.9 engine (the envelope laws against the DSP's, the START rule, the - fades, STOP, `po_retrig`, the index ramp: the step / slope and |d2| - figures) were each measured in the port through the virtual panel; the - tables are in `upstream/synth/README.md`, per feature, with what is open. -- Cost against 2.8 (a static count of the FM loops): +6 instructions a - sample a voice with the limiter's per-sample stage gone, about +15-20 % - of a VOIC 1 synth track's frame work, ~0.1-0.2 % of the frame budget; the - platform reserve and FREE MEM unchanged; the OS image's ROM section about - 3 KB smaller (the quantizer's move), the DRAM append a few KB larger. +## What selects the engine -## On the unit +- **The machine list (2.10; shared since 2.11).** FM SYNTH is the sixth row + of SRC SETUP (FUNC + SRC) and of SELECT MACHINE TYPE. Since 2.11 the rows + after PICKUP are a table in `machine.s` (`ml_rows`), assembled per build + through the units' `remix.inc` (`HAVE_SYDRUM`; the shared equates in + `engine_abi.inc`): FM SYNTH always, the author's SY DRUM when a build + carries it. The six row-count / row-bound constants that 2.10 poked + (5 -> 6) are six detours reading the table's count, and five more detours + widen the machine window's row clamp, so SELECT MACHINE TYPE scrolls like + SRC SETUP. This package carries FM SYNTH alone: one table row, the same + six rows as 2.10, `HAVE_SYDRUM` 0. The choice is stored as FLEX + plus `FM`, 1 in the track's unused NEIGHBOR PLAYBACK bytes (Part and + battery-RAM shadow); any other row clears it. `machine.s` is the author's + adaptation of Modwerk's 0.1.1 chooser: the same signature and Part + validator wrap, a sample-free source supplier (`po_fmsource`) and START + callback (`po_fmstart`) in `poly.s`. +- **A machine change resets the track to init (2.12).** To or from FM SYNTH, + through either list (`fm_choose`), the slot assigner (`po_assign` -> + `ml_became`) or the file browser (`po_loadsel`, with the new `po_unload` + detour at `0x40022732` keeping the slot's marker state across the browser's + unload), `machine.s` `ml_reset` writes the new machine's init into the + track's PLAYBACK, PLAYBACK SETUP, AMP, AMP SETUP, LFO and LFO SETUP bytes + (Part, battery-RAM shadow and live lane); step and scene locks and FX1 / + FX2 are kept, as at a stock machine change. +- **Marker files.** A FLEX track whose sample is named `FMSYNTH*.wav` or + `SYNTH*.wav` still plays FM, as in every earlier version. -- 26 Sep 2026: `OCTATRICK9` (remix `octatrick-usb`) on Tim's Octatrack - MKI: the synth, the quantizer and direct jump, and USB audio on all 20 - channels on the MKI. -- Every test build since, 2.3 .. 2.8, flashed on the same MKI from Tim's - own tree ([timhastie/octatrick](https://github.com/timhastie/octatrick), - the same wrappers over the same submodule), and the 2.9 line up to the - build before its last two fixes: ROOT, the DRAM quantizer, FINE 0c, the - envelope engine and the limiter's removal ran on the MKI (the held-chord - crackle gone, the live-key pops gone, by ear). The last two fixes (the - sequencer trigs, the index ramp) are emulator-verified on `octatrick` - (`make check` on this tree), not yet flashed. +The PLAYBACK page reads PTCH RATO INDX FINE FDBK DEC (title FM SYNTH; PTCH in +semitones, FINE in cents, DEC 127 = HOLD); the LFO page VOIC (1 = mono, 2..4 = +paraphonic) and CHRD (32 chord shapes, each with inversions, lockable per +step, snapped onto the bundled quantizer's scale); LFO SETUP's destination +list shows the FM names; GLIDE is the quantizer's row; LEG is on AMP SETUP. +Since 2.12 the frame builder's metronome-click clear (`0x4000b2c8`) is stock +again: the earlier `po_stop` hook there released held notes at every click. +`upstream/synth/README.md` is the author's full description, with every +measurement. -## How it is built +## How it is built here -Source: `upstream/` is Tim's repository (submodule, pinned to `v2.9` (`525f4b1`)). -Nothing inside `upstream/` is edited here. The manifest here -(`manifest.py`) executes `upstream/synth/manifest.py` from the source on -disk and re-exports its `MODULE`; that manifest derives its source paths -from its own directory, so the same file builds at `modules/synth/` in -Tim's tree and at `modules/synth/upstream/synth/` here. The engine is -`Linked(dram=True)`: linked into the platform runtime with the other DRAM -units and depacked at boot into the arena reserve -(`docs/contributing/PLACEMENT.md`). The page is pinned at `0x400d24d0`, the -start of the second free gap. +`manifest.py` executes `upstream/synth/manifest.py` (the author's manifest, +its stock expectations replaced by address/length/SHA-256 guards and its +precompiled page blob removed) and `upstream/quantizer/manifest.py` (the +matching quantizer, relabelled `fm_*` so it never aliases the public Scale +Quantizer's objects). It adds the page as a `Linked` unit pinned at +`0x400d24d0` with a `reference` to the author's ratified page bytes (the build +fails if `page.s` or the toolchain drifts from them), and one guarded poke at +the page resolver (`0x40031ece`) that jumps to `pg_resolve` at the cave's ++16, the same bytes the author's `emit_page` writes. `poly.s`, `machine.s`, +`page.s` and `engine_abi.inc` are the author's files byte for byte. The +author's manifest declares Analog BD as a native `conflicts` entry; the +wrapper clears it, because Modwerk declares conflicts in +`octamod.module.json`. The DRAM units are linked +into the platform runtime with the other DRAM units and depacked at boot into +the arena reserve. ## Collisions `tempo-bus` also uses the second free gap, so the ledger refuses that pair. -The engine shares the platform reserve with the other DRAM modules (USB -MIDI, USB AUDIO, MIDI SCENES) inside one runtime. +The public Scale Quantizer and Analog BD overlap the bundled quantizer's and +the chooser's hooks and are refused. The engine shares the platform reserve +with the other DRAM modules inside one runtime. ## Updating -Bump the submodule pin and rebuild; the pinned page bytes and the engine's -reads of SCALE QUANTIZER's pinned addresses either hold or the build -refuses. +Re-vendor `upstream/` at the new pin, convert any new stock expectation to a +guard (`stock-guards.json` lists them), keep the page `reference` equal to the +author's ratified page hash, and rebuild; the guards, the page reference and +the engine's reads of the quantizer's pinned addresses either hold or the +build refuses. diff --git a/sdk/octabam/modules/synth/README.md b/sdk/octabam/modules/synth/README.md index 94fd729ad..ff4e4ff32 100644 --- a/sdk/octabam/modules/synth/README.md +++ b/sdk/octabam/modules/synth/README.md @@ -1,12 +1,23 @@ -# FM Synth — 0.1.2 experimental +# FM Synth — 0.1.4 experimental ## Overview -FM SYNTH is a dedicated track machine beside FLEX, STATIC, THRU, NEIGHBOR and PICKUP. It uses Tim Hastie’s octatrick v2.9 two-operator FM engine: a sine carrier with a sine modulator, feedback and an index-decay envelope. One to four paraphonic voices feed the track’s existing AMP, filter and effects. +FM SYNTH is a dedicated track machine beside FLEX, STATIC, THRU, NEIGHBOR and PICKUP. It uses Tim Hastie’s two-operator FM engine from octatrick 2.12: a sine carrier with a sine modulator, feedback and an index-decay envelope. One to four paraphonic voices feed the track’s existing AMP, filter and effects. The machine plays without a sample. -This update fixes the MIDI chromatic selector stack imbalance and adds 18.06 dB of source headroom before voice summing and stock AMP gain. It keeps the existing voices/envelopes and their relative levels; sounds play quieter at identical mixer settings. Increase track LEVEL or mixer gain as needed while checking the full mix. Effects, resonant filtering and the sum of multiple tracks can still clip. +Version 0.1.4 moves the module to the author’s 2.12 line (`timhastie/octatrick-modules` tag `v2.12`, `ca13ed8`). The FM SYNTH chooser is now the author’s own: his `machine.s` is adapted from Modwerk’s 0.1.1 chooser and keeps the same Part signature (FLEX plus `FM`, 1 in unused NEIGHBOR bytes). It replaces Modwerk’s `machine.s` / `registration.s`. The update brings the four 2.10 synth changes, the fixes from the author’s hardware test of the first 2.10 build, the 2.11 machine list and the two 2.12 changes: -The dedicated machine plays without a sample. This experimental version has emulator playback and shared native/browser composition evidence. The owner explicitly authorized releasing 0.1.2 without current hardware validation on 8 October. Hardware is untested. Worst-case chip timing and complete stack/lifetime bounds remain unmeasured. Version-matched release notes are published in the community changelog. These measurements remain unknown; see TESTING.md for the release limits. +- **DEC puts HOLD at 127.** 127 holds the modulation index for the whole note; 0 is the shortest decay. Values 1–126 are unchanged. +- **No click at note start.** Every attack lasts at least one carrier period and is S-shaped, so low notes (RATO down, C1–C3) start without a tick. Attacks below C3 are slightly slower (C1 reaches full level in about 31 ms). +- **The LFO destination list speaks FM.** On an FM track, LFO SETUP’s PMTR lists PTCH RATO INDX FINE FDBK DEC, then AMP, SPD1 SPD2 DEP1 DEP2, FX1 and FX2, and skips SPD3/DEP3 (VOIC/CHRD). +- **A held CHROMATIC key is the gate.** A key sustains while it is held and releases on key-up; HOLD applies to sequencer trigs. +- **A machine list shared with SY DRUM (2.11).** The rows after PICKUP are a table in `machine.s`, so the author’s SY DRUM machine can add a row of its own without hooks of its own. The six row-count and row-bound constants that 2.10 patched became detours that read the table, and SELECT MACHINE TYPE now scrolls like SRC SETUP. This package carries FM SYNTH alone: the list has the same six rows as before and behaves as before. +- **The metronome no longer cuts a held note (2.12).** The engine had hooked the frame builder’s clear of the metronome’s click word, taking it for a STOP word, so with CLICK ON every beat released held notes and chords. 2.12 removes that hook; STOP still ends the engine’s voices through the stock voice kill. +- **A machine change resets the track to init (2.12).** Changing a track to or from FM SYNTH, through SRC SETUP, SELECT MACHINE TYPE or the file browser, sets its PLAYBACK, PLAYBACK SETUP, AMP, AMP SETUP, LFO and LFO SETUP to the new machine’s init: FM SYNTH’s own seeds, or a stock machine’s defaults as the stock Part initialiser writes them. Step and scene locks are kept and apply by position, as at a stock machine change; FX1 and FX2 stay. +- **8 October fixes.** A note restarted while it still sounds no longer clicks at a short DEC; a fast run of keys at VOIC 2–4 no longer loses notes; a live-recorded legato phrase no longer goes silent at its first trigless step. FM SYNTH chosen on a track that played a sample now plays C4 at FINE 0c, as its page reads. + +0.1.4 keeps 0.1.2’s MIDI chromatic drawer fix in the bundled quantizer. **The engine leaves 6 dB of headroom** (the author’s decision): the mono voice and each paraphonic voice are 6.02 dB below his earlier 2.10 builds, so AMP VOL 0 is the reference and VOL up to about +6 stays clean. This replaces 0.1.2’s 18.06 dB `OUTPUT_SHIFT`: at the same mixer settings 0.1.4 plays about 12 dB louder than 0.1.2. Lower track LEVEL or AMP VOL if a mix clips. + +Worst-case ColdFire cost, exact memory and two-track Part/project save and reload are measured in the emulator for this build; see TESTING.md. The hardware report for this exact build is pending. ## Controls @@ -17,15 +28,16 @@ The dedicated machine plays without a sample. This experimental version has emul | SRC C | INDX | 32; 0…127 | FM depth; 0 gives the unmodulated carrier. | | SRC D | FINE | raw 64 = 0; −64…+63 cents | Fine pitch tuning. | | SRC E | FDBK | 0; 0…127 | Modulator feedback. | -| SRC F | DEC | raw 40 ≈ 199 ms; 0…127 | Decay of modulation index, separate from amplitude. | +| SRC F | DEC | raw 40 ≈ 198 ms; 0 shortest … 126 ≈ 1.9 s, 127 HOLD | Decay of the modulation index, separate from amplitude. | | LFO C | VOIC | effective 1; 1…4 | Paraphonic voices. Inherited raw values outside 1…4 normalize to 1. | | LFO F | CHRD | ----; 32 shapes × 4 inversions | Chord shape and inversion; raw `shape<<2 | inversion`. | | FUNC+AMP F | LEG | OFF / MONO / POLY | Retrigger or overlapping mono/paraphonic legato. | +| FUNC+LFO PMTR | LFO destination | PTCH … DEC, AMP, SPD1/2, DEP1/2, FX1, FX2 | The FM page’s names on an FM track; SPD3/DEP3 are skipped. | | PROJECT > CONTROL > SEQUENCER | SCALE | OFF; 24 scales | Scale quantization of pitch edits and chromatic playing. | | Same menu | ROOT | C; 12 roots | Key of the active scale. | | Same menu | GLIDE | OFF; raw 0…127 | Pitch slide time, interpreted with LEG. | -AMP ATK, HOLD and REL are inherited Part values and shape the engine’s own gain envelope. First selection initializes the twelve SRC main/setup bytes and preserves them on reselection. It retains the other existing Part controls. LFO3’s speed and depth become VOIC/CHRD; its depth contribution is muted. Four voices share the track’s filtering and envelope path. The original [author documentation](upstream/synth/README.md) describes chord shapes, MIDI, keyboard recording and envelope details; those broader paths have not been qualified for this adaptation. +AMP ATK, HOLD and REL are Part values and shape the engine’s own gain envelope; HOLD ends sequencer notes, a held key ends its own note. Choosing FM SYNTH on a track that had another machine sets the track’s PLAYBACK, PLAYBACK SETUP, AMP, AMP SETUP, LFO and LFO SETUP to init (the values in the table, the stock new-track AMP and LFO values); reselecting FM SYNTH on an FM track keeps its controls. Step and scene locks and FX1 / FX2 are kept. LFO3’s speed and depth become VOIC/CHRD; its depth contribution is muted. Four voices share the track’s filtering and envelope path. The [author documentation](upstream/synth/README.md) describes chord shapes, MIDI, keyboard recording, envelope details and every 2.10 change with its measurements. ## Usage @@ -36,28 +48,31 @@ Start with one audio track and a simple trig pattern; the tutorial below selects 1. Open a disposable project with FM Synth firmware. Select an audio track, hold FUNC and press SRC, select FM SYNTH with UP/DOWN and press YES. No sample is required. 2. Press SRC. Set RATO to 1 and turn INDX from 0 upwards to hear a sine tone acquire FM harmonics. PTCH transposes it; FINE tunes it in cents; FDBK adds feedback; DEC shortens the modulation envelope. 3. Place a trig and press PLAY. Press LFO to set VOIC to 2–4 and CHRD to a chord if wanted. AMP ATK/HOLD/REL shape the gain envelope; FUNC+AMP exposes LEG. -4. Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; assign the desired sample in the normal way. +4. Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; PLAYBACK, AMP and LFO return to the init values of that machine (step and scene locks stay, as in stock). Assign the desired sample in the normal way. + +FM SYNTH is also the sixth row of SELECT MACHINE TYPE: press the track key twice quickly, press LEFT on `<< MACHINE`, go DOWN to FM SYNTH and press YES. Turn DEC fully right for HOLD (the index does not decay). A FLEX slot holding a file named `FMSYNTH*.wav` or `SYNTH*.wav` still plays FM as in earlier versions. For scales and glide, open PROJECT > CONTROL > SEQUENCER. Select SCALE, ROOT or GLIDE with UP/DOWN and turn LEVEL. SCALE OFF restores unquantized pitch. -Trigs, note starts and stops come from the instrument’s existing sequencer and panel/MIDI paths. The audio oscillator necessarily advances at the audio sample rate, including live playing while transport is stopped; it is not a second sequencer clock. The upstream HOLD/chord/glide logic reads the stock tempo/track clock. Tempo changes, non-default track speed/length, swing and loop/pattern transitions are not tested in this version. +Trigs, note starts and stops come from the instrument’s existing sequencer and panel/MIDI paths. The audio oscillator advances at the audio sample rate, including live playing while transport is stopped; it is not a second sequencer clock. Tempo changes, non-default track speed/length, swing and loop/pattern transitions are not tested in this version. ## Compatibility and limitations -OS 1.40C only. Parts store internal FLEX kind 1 plus `FM/1` in unused NEIGHBOR bytes, in both the live Part and its battery-RAM shadow. The chooser keeps the ordinary sample slots unchanged. A stock build reads the track as FLEX and cannot synthesize FM; select a stock machine explicitly before downgrading. Saved-file reload and migration from upstream marker projects are not tested. +OS 1.40C only. Parts store internal FLEX kind 1 plus `FM`, 1 in unused NEIGHBOR bytes, in both the live Part and its battery-RAM shadow. This is the same signature as 0.1.1 and 0.1.2, so their projects open as FM tracks. The chooser keeps the ordinary sample slots unchanged. A machine change to or from FM SYNTH resets the track’s machine parameters to init and keeps its step and scene locks, as a stock machine change does; a lock made for one machine applies by position to the next. A stock build reads the track as FLEX and cannot synthesize FM; select a stock machine explicitly before downgrading. + +The matching quantizer is bundled privately because FM shares its mailbox and scale accessor. Do not combine with the public Scale Quantizer or Analog BD: their hooks/caves overlap. POLY8’s shared chooser is reviewed against 0.1.2’s chooser hooks, which this version replaces; until that bridge is updated, a build with POLY8 and FM Synth 0.1.4 stops at POLY8’s seam check (TESTING.md). -The matching quantizer is bundled privately because FM shares its mailbox and scale accessor. Do not combine with the public Scale Quantizer or Analog BD: their hooks/caves overlap. The shared builders agree across 110 current configurations (31 built, 79 refused), including compatible companion modules. Only standalone FM playback is audio-tested; mixed builds have byte-parity evidence, not audio stress evidence. Legacy marker recognition remains in the upstream engine, but the verified route uses the dedicated chooser. +Emulator measurements price one FM track at VOIC 4, or three at VOIC 1, inside every audio block including the first START after boot. Larger loads (two or more tracks at VOIC 4, eight tracks) exceed the conservative per-block budget on their START blocks. They are not shown to fail on the unit, only not qualified. See TESTING.md for the table. -One-track mono/four-voice playback and MIDI selector recovery pass in emulation. Eight simultaneous audio tracks, modulation, external MIDI note/CC traffic, parameter locks, neighbouring sample playback, Part/pattern changes, save/reload and real hardware are not tested. Do not infer real-time headroom or project persistence from the UI/storage smoke. +External MIDI note/CC traffic, Part/project reloads and a power cycle on hardware, neighbouring sample playback under load and the reported hardware static (#273) are not qualified. Two FM tracks survive Part save/reload, project save/reload and a fresh project load in the emulator. The output level is the author’s, 6 dB of headroom in the engine, about 12 dB above 0.1.2. ## Tests and measurements -See [TESTING.md](TESTING.md). Source compilation and all 110 native/browser comparisons pass. Fresh LCD captures show the chooser and controls. The original MIDI selector freezes in Octemu; the patched selector changes modes and returns to SRC. Mono and four-voice playback at maximum AMP VOL produce about 18.06 dB lower peaks and double-STOP reaches silence. The original audio runs did not reproduce the reported static, so hardware confirmation remains necessary. Prior 0.1.1 storage checks are retained as historical evidence only. -The shared native platform reserves 1,707 × 6,144 = 10,487,808 audio-arena bytes. This reservation is not the module’s complete memory inventory. Chip worst-case cycles and current hardware are unmeasured/untested. `qualification.example.json` is a pending worksheet, not publication evidence. +See [TESTING.md](TESTING.md). The native image used for the captures and measurements is built by Modwerk’s own native exporter from this source. The author’s page links byte for byte to the bytes he ratified. Worst-case ColdFire instruction counts and exact memory come from the pinned emulator and the image’s link map. The hardware report fields stay pending until the author’s MKI test of this exact image (MAIN OS `00fa6caa…`). Release notes are in [release-notes.json](release-notes.json). ## Authorship and licences -Tim Hastie: FM engine, voices/chords, MIDI/legato and matching quantizer, pinned to `525f4b19b04dc3ba3f3bae3b25abbf48df34a10a` (v2.9). Sam Banks: octabam wrapper/platform, pinned to `949f3be15eae5d3d16a7682b9e3218d42f6c1284`. Modwerk contributors: dedicated chooser and Part validation, sample-free source/START transport, verification, docs and original SVG. Full MIT terms are in [LICENSE](LICENSE), with the author originals retained. The original import inventory is [synth-949f3be.json](../../../imports/synth-949f3be.json). The pending adaptations are recorded in [synth-fixes-273-264.json](../../../imports/synth-fixes-273-264.json). +Tim Hastie: FM engine, voices/chords, MIDI/legato, the FM SYNTH chooser (2.10, adapted from Modwerk’s), its 2.11 machine-list table and 2.12 machine-change init, and the matching quantizer, pinned to `ca13ed85547790f788ceaaada5df3cdd31508441` (tag `v2.12`) of [timhastie/octatrick-modules](https://github.com/timhastie/octatrick-modules). Sam Banks: octabam wrapper/platform, pinned to `949f3be15eae5d3d16a7682b9e3218d42f6c1284`. Modwerk contributors: the original dedicated chooser and Part validation (0.1.1), the quantizer drawer fix (0.1.2), verification, docs and original SVG. Full MIT terms are in [LICENSE](LICENSE), with the author originals retained. The import inventories are [synth-949f3be.json](../../../imports/synth-949f3be.json), [synth-fixes-273-264.json](../../../imports/synth-fixes-273-264.json) and, for this version, [synth-ca13ed8.json](../../../imports/synth-ca13ed8.json). No firmware, extracted stock byte blobs/tables, cards, RAM dumps or compiled images are distributed. Stock expectations resolve lazily from the verified developer-owned OS by address, length and SHA-256. @@ -65,20 +80,22 @@ No firmware, extracted stock byte blobs/tables, cards, RAM dumps or compiled ima **Audio preview.** [Listen](media/audio-preview.mp3). A Vector-generated minor synth phrase at 128 BPM, entered as FM note locks. INDX sweeps 44→92 and FDBK 0→28, with Mini Verb in FX2 (DECAY 96, DAMP 58, MIX 36). One common gain, no added gaps or external samples. Captured from FM Synth 0.1.2 and Mini Verb 0.2.1 running in Octemu on a computer; no physical-hardware recording. VECTOR’s generator supplies the notes offline; the instrument sequencer plays the FM locks. Exact sequence, settings, image identity, panel walk and render details are in [audio-preview.json](media/audio-preview.json); [media rights](media/LICENSE.md) covers the generated audio. -These seven fresh current-code captures show the controls. They are actual monochrome emulator LCD exports, visually reviewed, bound to the byte-identical current native image in [media/capture.json](media/capture.json). They are emulator evidence; no hardware capture is claimed. See [media/LICENSE.md](media/LICENSE.md) for the interface rights declaration and [presentation/thumbnail.svg](presentation/thumbnail.svg) for original art. +These eight captures are actual monochrome emulator LCD exports of the native 0.1.4 image, visually reviewed, bound to it in [media/capture.json](media/capture.json). They are emulator evidence; no hardware capture is claimed. See [media/LICENSE.md](media/LICENSE.md) for the interface rights declaration and [presentation/thumbnail.svg](presentation/thumbnail.svg) for original art. ![FM SYNTH is the dedicated sixth machine row in SRC SETUP.](media/ot-location.png) +![FM SYNTH is also the sixth row of SELECT MACHINE TYPE.](media/ot-machine-list.png) + ![PTCH, RATO, INDX, FINE, FDBK and DEC replace the FLEX source controls.](media/ot-controls.png) ![VOIC and CHRD occupy the third LFO speed and depth controls.](media/ot-voices.png) +![LFO SETUP lists the FM page's names as destinations; this example shows SRC RATO.](media/ot-lfo-setup.png) + ![LEG selects OFF, MONO or POLY on an FM track.](media/ot-legato.png) ![The privately bundled quantizer adds SCALE; this example selects MAJOR.](media/ot-scale.png) -![ROOT chooses the key; this example uses C.](media/ot-root.png) - ![GLIDE sets pitch slide time; this example shows OFF.](media/ot-glide.png) -Only sanitized audio measurements are retained. The temporary Octemu WAV, card, battery file and firmware remain local and are excluded from the submission. +No audio files are retained in this module. diff --git a/sdk/octabam/modules/synth/TESTING.md b/sdk/octabam/modules/synth/TESTING.md index a196e23b6..3bda830a4 100644 --- a/sdk/octabam/modules/synth/TESTING.md +++ b/sdk/octabam/modules/synth/TESTING.md @@ -1,68 +1,260 @@ -# FM Synth 0.1.2 regression testing +# FM Synth 0.1.4 testing -This is the experimental 0.1.2 update for [#264](https://github.com/repeat98/modwerk/issues/264) and [#273](https://github.com/repeat98/modwerk/issues/273), tested on 8 October 2026. It is included in the catalog. Current hardware, chip worst-case timing and complete stack/memory bounds remain unknown. The owner explicitly authorized this new release and issue closure without current hardware validation: “please release them and close. I green light no hardware validation for now”. This is a new exact-source update approval; the 0.1.1 approval is historical. +0.1.4 is an update of Modwerk's published FM Synth (0.1.1 of 6 October, 0.1.2 of 8 October) to the author's octatrick 2.12 line, `timhastie/octatrick-modules` at `ca13ed85547790f788ceaaada5df3cdd31508441` (tag `v2.12`). Two earlier local drafts were never submitted: 0.1.3 on the 2.10 line (`9bd7169`, MAIN OS `d5e1a7b0…`) and 0.1.4 on the 2.11 line (`5f3d745`, MAIN OS `248d987e…`). The 2.11 build is compared below by its MAIN OS hash. This file is the report that `tests.qualification` names. Earlier reports are kept unchanged: `evidence/release-0.1.1/TESTING.md` and `evidence/release-0.1.2/TESTING.md`. -## Changes and reproduced failure +**Status.** Emulator worst-case cycles, exact memory, the 8 October persistence and two-instance checks and the two 2.12 behaviour changes are measured on the exact 0.1.4 image (MAIN OS `00fa6caa…`). The module's own native Part smoke passes on that image. The hardware fields are `TIM:` placeholders until the author reports his MKI test of this exact image (`hardwareStatus` stays `historical` until then). Modwerk's owner approvals are kept as they are: 0.1.1's in `evidence/release-0.1.1/` (`owner-hardware.md`, `build-approval.json`), and 0.1.2's in `sdk/synth-build-approval.json`, copied to `evidence/release-0.1.2/build-approval.json`. They cover only those versions and are not extended to 0.1.4. -The privately bundled quantizer's octave-number hook originally replaced eight bytes at `0x400449b8`. The stock MIDI branch targets `0x400449be`, inside that replacement, and skips the required octave argument push. The drawer then consumes one more argument than was pushed and corrupts its caller's stack. On the released browser image, selecting a MIDI trig mode freezes the UI; the debugger finds the stock HALT diagnostic after a bad return into a data address. The baseline does not reach the post-selector SRC gate. +## What changed from 0.1.2 -The replacement starts at the common argument push, `0x400449be`, with a fresh eight-byte stock hash guard. MIDI uses the octave already in d0, pushes it once, preserves the stock format argument and resumes at `0x400449c6`. Audio retains the scale/root rendering. Normal MIDI/audio paths no longer branch into detour padding. The patched browser image passes repeated selector DOWN/UP navigation and returns to the Synth SRC controls. The repeatable `verify-regressions.py --case midi` runner also passes, including both pre- and post-selector display gates. +**The 2.12 changes.** Both are the author's, in `upstream/synth/poly.s`, `machine.s` and `manifest.py` (his README describes them with his measurements): -FM source output now shifts right three bits: mono after its output conversion, and each paraphonic contribution before accumulation. This leaves 18.06 dB more headroom before the stock AMP path and voice sum. Existing envelope gains, relative voice normalization and crossfade state are preserved. Every existing sound is quieter at the same mixer settings. Effects, resonant filtering and multiple tracks can still clip. +- **The metronome no longer cuts a held note.** Since the 2.9 line the engine hooked the frame builder's `clrl 0x46c80350` at `0x4000b2c8` (`po_stop`), taking that word for the sequencer's STOP word. It is the metronome's click word: while the sequencer plays with CLICK ON, the beat code writes 3 or 1 into it, and the frame builder turns it into the DSP click command and clears it. So `po_stop` ran at every click and released every held note and chord. 2.12 removes the detour; `0x4000b2c8` is stock again. STOP still ends the engine's voices through `po_kill` at the stock voice kill (`0x4000685c`). +- **A machine change resets the track to init.** Changing a track to or from FM SYNTH, through SRC SETUP or SELECT MACHINE TYPE (`fm_choose`), the slot assigner (`po_assign`) or the file browser (`po_loadsel`), runs `ml_reset` in `machine.s`. It writes the new machine's init into the track's PLAYBACK and PLAYBACK SETUP (FM SYNTH's own seeds, or a stock machine's PLAYBACK descriptor defaults), AMP, AMP SETUP, the LFO page and LFO SETUP (the stock new-track values, read at run time from the same stock descriptors the stock Part initialiser uses; FM SYNTH puts VOIC 1 and CHRD off in LFO 3's bytes), in the Part, its battery-RAM shadow and the live lane. Step locks and scene locks are kept and apply by position, as at a stock machine change; FX1 and FX2 stay. One new detour supports it: `po_unload` (`jsr`, 6 B) at `0x40022732`, in the file browser's load callback, keeps the FLEX slot's marker state across the browser's slot unload so `po_loadsel` sees the change. A change between two stock machines is untouched. -**The reported hardware static has not been reproduced.** Both baseline audio configurations below remain below signed 16-bit full scale. The measurements verify attenuation and continued playback/STOP recovery, not resolution of every artifact in #273. A physical MKI/MKII retest with the reporter's sound/settings is still needed. +**The 2.11 machine list.** The author's 2.11 line makes the rows after PICKUP a table (`ml_rows` in `machine.s`), assembled per build through each unit's `remix.inc`: `HAVE_SYDRUM` (1 only when his SY DRUM module is in the build) and the equates shared with SY DRUM (`engine_abi.inc`). FM SYNTH is always the first table row. The six row-count and row-bound constants that 2.10 poked (`pea 5` → 6 and five `moveq #4` → 5) became six detours that read the table's count (`0x40079248`, `0x400585fa`, `0x4003c950`, `0x40078678`, `0x400786ce`, `0x40079904`). Five more detours widen the machine window's row clamp after a list move (`0x40078c22`, `0x40078cb0`, `0x40078d2e`, `0x40078dee`, `0x40078e7c`). The drawer detour at `0x40078678` draws `name(row + the list's top)`, so SELECT MACHINE TYPE scrolls like SRC SETUP once a build has more than six rows. This package has FM SYNTH alone: one table row, the same six rows as 0.1.2, and `HAVE_SYDRUM` 0. The bundled quantizer (`fm_qz`) assembles to exactly the 2.10 and 2.11 builds' code; the engine (`poly`, 20,996 B) differs from the 2.11 build's only by 2.12's two changes, and the chooser unit `fmmachine` grows from 1,440 to 2,076 B with the machine-change init. The author's manifest also declares Analog BD as a native `conflicts` entry; the wrapper clears it, because Modwerk carries conflicts in `octamod.module.json` (it lists `analog-bassdrum` already) and a native entry would switch on the browser's span-contract check for every module. -## Private software builds +**Source.** These files are the author's ca13ed8 (v2.12) files byte for byte: `upstream/synth/poly.s`, `page.s`, `machine.s`, `engine_abi.inc`, `README.md` and `upstream/LICENSE`. Every measurement below was made on the image built from them, MAIN OS `00fa6caa…`. `upstream/synth/manifest.py` is the author's manifest with every stock expectation replaced by `stock_guard(address, length, SHA-256)`. There are 53 guards, one per hook site (2.12 drops the `po_stop` site's guard and adds `po_unload`'s; the five clamp sites share one hash), each checked against the developer's MAIN OS `164f3122…`; `stock-guards.json` lists all 73 with the quantizer's 20. The manifest's precompiled `PINNED_PAGE`, `emit_page` and `CavePatch` are removed. `upstream/quantizer/` is unchanged from v2.11 in v2.12: `quantizer.s` is the author's file plus Modwerk 0.1.2's octave-drawer fix (#264) at the common argument push `0x400449be`, the same site and behaviour as the public Scale Quantizer's 0.1.3 MIDI fix; the author's file recognizes the FM/1 signature in `qz_is_synth`, and SY/1 only when `HAVE_SYDRUM` is 1. The quantizer manifest is Modwerk 0.1.2's with the author's 2.11 per-build include on its DRAM unit. The import inventory is `sdk/imports/synth-ca13ed8.json`. -The current 0.1.2 source and metadata are compiled together with the ordinary shared compiler. The earlier development-image report is retained separately in `evidence/development-regressions.json`. Current-main builder inputs produce a slightly different browser image, so MIDI, mono and four-voice regressions were all rerun on that exact release image. The standalone native image reproduces the freshly captured LCD image byte for byte. The compiler/exporter run in the isolated stock-free toolchain container with network disabled, a read-only root and private tracked-source/output mounts. No firmware enters Git. -Compilation passes. Only the private Synth `poly`/`fm_qz` objects and their linked group change in requested-packages; unrelated packages remain identical. Build identities are recorded in `evidence/regressions.json`: +**The chooser is now the author's.** His `machine.s` is adapted from Modwerk's 0.1.1 chooser and keeps the same Part signature, FLEX plus `FM`, 1 in the track's NEIGHBOR PLAYBACK bytes. Projects made with 0.1.1 or 0.1.2 therefore open as FM tracks. Hooks dropped from 0.1.2: -- Patched browser MAIN: `64beb992dd65cad096d2c70f23bb3064c715464d651da22dd3ad9024901831e9`. -- Patched upgrade: `c1a57f818998923e27ee9ae36061f2c1fe050788619ccceec6d0c24dac04d392`. -- Patched standalone native MAIN: `d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157`. -- Released 0.1.1 browser MAIN baseline: `c1fbc0b2eaf284e72692b7048d1dfd3693f3e3e90c71229159a619d585b52c95`. +- `machine.s`, `registration.c` and `registration.s` (units `fmmachine` / `fmregistration`). +- `fm_resolve_pb` at `0x40031e74`: the author routes PLAYBACK through `pg_resolve` only. +- `fm_name_b` at `0x4004c36a`. +- `fm_main_commit` at `0x4007991c`: the author commits at the machine window's MACHWIN site `0x40079816`. +- The `fm_start` SymbolRef at `0x400d6458` and the `fm_source_dispatch` detour at `0x4000d514`. The author's own `po_fmstart` and `po_fmsource` replace them at the same sites. +- The `pg_resolve` detour at `0x40031ece`. It becomes a guarded poke writing the author's `emit_page` jump `jmp 0x400d24e0`, so `page.s` stays byte-identical. -The native proof's `osSha256` is the module-owned image before platform writes; it is not the emitted MAIN's file hash. Capture/replay checks use the actual emitted file hash. +The other chooser sites (`0x400334d8`, `0x4003c928`, `0x4003d718`, `0x4003c980`, `0x400786c8` now 6 B, `0x400585dc`, `0x40078886`, `0x4005221e`, `0x4003a52e`, `0x4003cd98`, `0x4005a616`, `0x4005a850`, `0x40002318`) are the same sites, now running the author's code; the six chooser pokes are the 2.11 detours above. Two author detours that 0.1.1 removed come back: MACHWIN (`0x40079816`, into `fm_main_commit` and then `po_machwin`) and LISTWIN (`0x4005a848`, `po_machlist`). The three 2.10 LFO-list detours (`0x4003bff2`, `0x4003bffa`, `0x400392cc`) are new. The Part validator wrap is the author's `fm_validate` at the same site; Modwerk's wrap is no longer needed. The private quantizer bundling and its `fm_*` relabelling are kept as Modwerk wrote them. -All **110 current coverage selections** pass the existing `compareSelection` native/browser comparator: **31 built** (module-owned bytes equal outside the established platform/logger spans), **79 matching refusals**, **zero mismatches**. This covers standalone Synth, pairings, fuller selections and both stock-FX2 policies. The input stock image remains unchanged. An altered stock byte is rejected and that input also remains unchanged. See `evidence/composition.json`. Mixed images have composition evidence only, not audio stress qualification. +**Behaviour.** The 2.10 changes and the 7–8 October fixes are described, with the author's measurements, in `upstream/synth/README.md`: -## Panel and audio regressions +- DEC 127 = HOLD, and 0 = the shortest decay. +- Note starts no longer click. +- The LFO destination list shows the FM names. +- A held key is the gate. +- A note restarted at a short DEC no longer clicks. +- Fast VOIC 2–4 runs no longer lose notes. +- Recorded legato phrases keep sounding. +- FM SYNTH chosen after a sample plays C4 at FINE 0c. -Octemu binary SHA-256: `3ce6cebde0de2c61edc4228436338a1e115ce227373351a5ca84aefc9cf90acb`. The local source checkout is dirty, so its HEAD alone is not claimed as the tested executable identity. A private copied, sample-free firmware-created project has a step-1 T1 trig and FX1/FX2 NONE. Card/battery identities are recorded in `evidence/regressions.json`. Every image/case starts with a fresh copy of the battery state. Native UI capture uses the separately fingerprinted headless `ot_emu` in `media/capture.json`. The extra Octemu regression screenshots retain their original development-image bindings in `media/regression-capture.json`; they are not relabelled as new browser-image captures. +**Headroom: 6 dB, the author's decision.** 0.1.2's `OUTPUT_SHIFT` (18.06 dB source attenuation for #273) was Modwerk's change to `poly.s`. The author's 2.10 engine, unchanged in 2.11 and 2.12, makes its own: one arithmetic shift right (`HEADROOM`, 6.02 dB) at the mono voice's output and at each paraphonic voice's contribution before the sum, the same places 0.1.2 shifted by 3. Envelopes, voice ratios (`1 / sqrt(VOIC)`), crossfades and the final saturating clamp are unchanged; a VOIC 4 chord's coincident peaks now reach full scale instead of twice it. AMP VOL 0 is the reference and VOL up to about +6 stays clean. At identical settings 0.1.4 is about 12 dB louder than 0.1.2. The author measured it on his own build: -6.02 dB on every render, each sample the previous one halved within 1 LSB (`upstream/synth/README.md`, "6 dB of headroom (2.10)"). -Actual panel input selects FM SYNTH, sets INDX=0, AMP REL raw 40 and AMP VOL maximum (display +63), starts PLAY, then sends STOP twice. The four-voice case additionally sets VOIC=4 and CHRD raw 127 (display M7S2). The retained LCD exports confirm these controls. Recordings are stereo 44.1 kHz signed 16-bit PCM, including boot/settle silence. Peak comparisons therefore measure the full captured take rather than time-aligned samples. +## Build and identities -| Configuration | Released peak | Patched peak | Peak change | Final half-second peak, patched | +| item | value | +| --- | --- | +| Module | `synth` 0.1.4-experimental; native source SHA-256 `4fc06072b4039db83cba1600e949d00770c9753d974bbf76a5809f8e1be9e851` | +| Image | MAIN OS SHA-256 `00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e` (1,126,653 B): FM Synth with its bundled quantizer and all stock effects. It is built from this source by `scripts/export-composition-proofs.py --vendored-sdk --static-stock --modules synth --select synth:true`. The LCD captures (`media/capture.json`), the measurements below, the Part smoke, the persistence checks and the 2.12 checks all used this image. | +| Update file for the hardware test | 462,396 B, SHA-256 `2267af13293b302a4bd88bf3c0c753965e36bb7861d2609875060303f1072909` (OCTAMOD79). Modwerk's own TS packer decodes it to the MAIN OS above. Re-encoding the stock update with that MAIN OS gives the same bytes. | +| Page | `fmpage` 1,868 B linked at `0x400d24d0`; the build log reports "matches the author's build" against reference `b43d76c9c13c1441f16e32b85826ac2e7664352947fa4dc393401ff332710f92` | +| Address check | the author's `tools/align_check.py` (v2.12) on this build: 5,723 instruction rows in the five linked objects, all at even addresses, and 75 detour / symbol-ref / poke targets even, every jump into them on an instruction (ColdFire faults on an odd jump target and the emulator does not model it) | +| Stock bytes | no stock bytes are committed. In the compiled FM units the code's stock-equal runs are the displaced-instruction replays at the hook sites, at most 22 bytes (as in the 2.10 and 2.11 builds); the 2.12 code adds none. The longest stock-equal run of any kind, leaving out fills, is 32 bytes, mostly zero, in the page's icon tables (`pg_img_ratio` / `pg_img_fdbk`), unchanged since 2.10. | +| Emulator | pinned octabam `ot_emu`, SHA-256 `9dc31d1249793fcf857fa6b1c1ac72ab7966646fd59f3e09e90683c8eb41e711`, not rebuilt | + +The image, the emulator, the cards and the rig scripts stay local. Only their hashes and the results enter this repository. + +## Native Part smoke + +The module's own `verify.py` (Modwerk's 0.1.1 gate) ran unchanged on this image. It checks: + +- selecting FM SYNTH through FUNC + SRC on all eight tracks; +- the machine byte FLEX, and `FM`, 1 in the Part and in the battery-RAM shadow; +- the seeded controls 64, 12, 32, 64, 0, 40; +- the sample slots, unchanged; +- reselection, which keeps an edited control; +- the return to FLEX, which clears the signature. + +All five checks passed (`evidence/native-smoke.json`). The author's chooser keeps Modwerk's signature and layout bit for bit; reselecting FM SYNTH on an FM track is not a machine change, so 2.12's init does not touch it. `verify-audio.py` and `verify-regressions.py` need Octemu, which is not available here, and were not rerun for 0.1.4; the 0.1.2 results remain in `evidence/release-0.1.2/`. + +## The 2.12 changes on this image + +Both were checked in the pinned emulator on this image (DSP running, MKII panel, real keys and encoders; private scripts and cards kept local). + +**The metronome case.** The author's own gate, unchanged: a sequencer playing at 120 BPM, a key (VOIC 1) or a VOIC 4 chord held for 9.1 s (18 beats), the metronome ON against the same run with it OFF, track 1's stem compared sample by sample from the key-down. + +| case | this image, metronome ON vs OFF | the 2.11 build (`248d987e…`), metronome ON | +| --- | --- | --- | +| a key held, VOIC 1 | sample-identical over the 9.14 s hold (0 samples differ); the engine keeps the note (S_ON never set released) | released at the first clicks: `po_stop` cleared the click word 24 times, S_ON went to released + STOPPED, the stem fell to silence (-90 dBFS) within 4 s | +| a chord held, VOIC 4 | the four voices stay in state 1 (sounding) for the whole hold; 500 ms levels within 0.02 dB of OFF (not sample-identical: the four panel keys land about 16 samples apart between runs) | every voice released (state 2) at the first clicks; the stem fell to silence within 4 s | + +The click itself still sounds on both images: in each ON run the frame builder posted the DSP click command 30 times. On this image `po_stop` no longer exists and nothing writes the click word except stock's own code. + +**The machine-change init.** T1 of an empty project, through SRC SETUP: FLEX → FM SYNTH, then PLAYBACK, LFO, AMP and FX1 edited, → FLEX, → FM SYNTH again. The Part bytes after each step: + +- FM SYNTH chosen: FLEX + `FM`, 1; PLAYBACK at FM SYNTH's seeds (PTCH 0, RATO 1, INDX 32, FINE 0c, FDBK 0, DEC 40); the LFO page at the stock new-track values with VOIC 1 and CHRD off; AMP at the stock new-track values. +- FLEX chosen after the edits: the signature cleared; the FLEX PLAYBACK column back at FLEX's defaults (the project's original bytes); the LFO page and AMP at the stock new-track values; FX1 as edited. +- FM SYNTH chosen again: PLAYBACK, LFO, AMP and the setup pages byte-identical to the first choice; FX1 still as edited; FX2 untouched throughout. + +All 10 checks passed. LFO SETUP and AMP SETUP were not edited in this walk, and step and scene locks were not part of it; the verification of the author's 2.12 line covered every route, the locks kept, SAVE and a cold boot on his builds of the same `machine.s`. + +## Persistence and two instances (8 October rules) + +Two FM tracks with different settings, on this image in the pinned emulator (DSP running, MKII panel, real keys and encoders; private walk scripts, the card kept local): + +- **Two instances.** T1 and T5 were chosen as FM SYNTH through FUNC + SRC and edited differently (four SRC controls each; on the LFO page T1 VOIC 1 with a chord shape, T5 VOIC 4 with another), with trigs on both. Editing T1 left T5 unchanged (passed); editing T5 left T1 unchanged (passed); both carry FLEX + `FM`, 1 and their own PLAYBACK and LFO bytes (passed); the five sample-slot bytes of both tracks never changed (passed). +- **Part save / reload.** FUNC + PART → SAVE, later edits on both tracks, FUNC + PART → RELOAD: both tracks back to the saved bytes (passed). +- **Project save / reload.** PROJECT → SAVE (no card-write error), unsaved edits on both tracks, PROJECT → RELOAD: both back to the saved bytes (passed). +- **Fresh project load.** A new emulator process mounted the saved card and loaded the project: both tracks as saved (passed). +- **Audio after the transitions.** Main-output renders of the two tracks' trigs: after the edits RMS 1267.1, after the project reload 1265.9, after the fresh load 1265.7 (16-bit, peak 3638): sound in every case (passed). The level differs from the 2.11 build's run of the same walk because choosing FM SYNTH now resets AMP to the stock new-track values. +- **Not tested here: the battery-RAM-only restart.** The pinned emulator keeps no battery RAM between processes, and a fresh process followed by an explicit project load is not a power cycle. The power cycle with the card in (both tracks back without loading the project) is on the author's hardware sheet for this image. + +## Composition parity + +**Open: POLY8.** Main carries POLY8 0.2.6, whose shared chooser bridge (`sdk/octabam/modules/poly8/composition.json`, applied by `tools/remix/machine_composition.py` and `src/engine/requested-modules.ts`) replaces FM Synth's chooser hooks with its own and checks every hook it replaces against the exact claims of FM Synth 0.1.2 (Modwerk's chooser). The author's chooser, in 0.1.3 already and in 0.1.4, makes different claims at two of those sites (`0x4005221e` is `fm_tick`, `0x400786c8` takes 6 bytes), drops four of them, and turns the six count pokes into detours. Both sides therefore stop on any selection with POLY8 and FM Synth ("seam drift"), as the bridge is designed to. On main `3151975`, `npm run module:verify -- synth` stops at the first such selection (`POLY8 composition seam drift: synth detours 0x400786c8`), and two POLY8 tests in `src/engine/requested-modules.test.ts` fail. Making the pair build again needs POLY8's bridge to be reviewed for the new chooser (its shared machine also calls 0.1.2's registration functions `fm_type`, `fm_admit_track`, `fm_track_page`, `fm_ui_tick` and `fm_defaults`). That is POLY8's code; this update does not change it. + +Without POLY8 in the pool (a local run on main `3151975` only; the record is not committed), `npm run module:verify -- synth` compared Modwerk's browser composer with native octabam on 122 selections: 35 builds match native outside the platform writes, 87 refusals match, 0 mismatches. The native side ran the same exporter on the host toolchain instead of the pinned Docker image, which is not available here. `sdk/native-comparisons/synth.json` is still 0.1.3's record. + +## LCD captures + +Modwerk's `scripts/capture-module-ui.py` ran unchanged on this image with the pinned `ot_emu`, an empty card, the MKII panel and 50 ms keys, in one slot of the shared emulator rig. The plan is 0.1.2's plan with waits, plus LFO SETUP and SELECT MACHINE TYPE (`media/capture.json`). Eight PNGs are committed (a ninth, the root page, was captured and is not committed: eight media per module). Seven are byte-identical to the 2.11 build's captures of the same plan. `ot-voices.png` differs by design: choosing FM SYNTH now writes its init, VOIC 1 into LFO 3's SPD3 byte, so the VOIC dial is drawn at its first position; the 2.11 build left the stock byte there, which also reads VOIC 1. SRC SETUP and SELECT MACHINE TYPE show FM SYNTH as the sixth row and nothing after it. All were reviewed visually. + +## Qualification: cycles and memory — 11 October 2026 + +Every count marked **instr** is an emulated ColdFire instruction count from the pinned emulator. These counts are not chip wall-clock cycles and not a CPU percentage. **cycles** are those counts converted with the calibration below. + +### Method + +- **Rig.** One card at a time is booted in the emulator panel (`--max 300000000`, ATA write interrupt every 32 samples) in one slot of the shared emulator rig. A measurement script runs, then stops the transport and the panel. Instruction counts are computed inside the emulator and do not depend on the host's load. +- **Counter.** The panel arms the emulator's PC watch, pauses pacing and runs N emulated ms. It returns every hit with the global instruction index. +- **Frame window.** This is the stock frame-interrupt body `0x4000aad8 .. 0x4000d9a6`, plus 4 instructions. It contains the stock builder, locks, scenes, LFOs, the CF DELAY, the DSP transfer, each track's renderer, and the FM frame hooks: `po_lfo3b`, `po_retrig`, `po_rel`, `po_kill`, `po_fmsource` and `po_fmstart`. +- **Renderer window.** A second window pairs `sy_render` (`0x40a95600`, RTS `0x40a95dcc` = `sy_done` + 12, checked in the link map) with the nested stock renderer (`0x40004008`, RTS `0x40004266`). +- **Runs.** Each card gets one 100 ms window with PLAY pressed inside it (the first PLAY after boot), four more 100 ms windows while playing, and two 60 ms `sy_render` windows. That is about 1,540 complete frames per card. Every card ran once; the cards the bounds come from (no FM track, one VOIC 4 track, eight VOIC 4 tracks at both DEC extremes) ran five times, because the rig's run-to-run spread is about ±100 instructions. +- **Fixtures.** Each card is a copy of the toolkit's measurement project (120 BPM, stock DELAY in FX2 on all eight tracks), edited as follows: + - N tracks get FLEX slot 1 = the `SYNTH` marker, with PTCH 64, RATO 127, INDX 127, FINE 64, FDBK 127 and DEC as given, plus the Part VOIC and CHRD MA9 inversion 3. + - Every pattern has a trig on all 16 steps of those tracks. + - Each step locks the six PLAYBACK controls between their extremes, plus VOIC and a different CHRD shape and inversion. + - LFO1 → INDX and LFO2 → FDBK run at full speed and depth. + - Scene locks fill all 16 scenes; the crossfader is at 64. GLIDE is 0x20. + + Every step changes the pitch, so the 2.10 crossfades, fades and waiting notes run on every step. +- **The chooser.** Card `c127` is the eight-track VOIC 4 card with every track made FM by the Part signature alone (FLEX + `FM`, 1, slot an ordinary sample). It measures the machine-list route, which supplies the voice through `po_fmsource` without the stock sample renderer. + +### Budget and conversion + +One frame interrupt serves 16 samples at 44.1 kHz, so the block is 362.8 µs. The ColdFire runs at 264 MHz, which gives **95,779 cycles per block**. + +Emulated instructions are converted with the toolkit's hardware calibration (CF METER, 28 September 2026, 2.9 line): **8.38–9.44 ns per emulated instruction**. This record uses 9.44 ns. One instruction then costs 2.49216 cycles, and the block holds 38,432 instruction-times (43,294 at 8.38 ns). This is a calibrated estimate, not a cycle-exact measurement. The RTOS interrupt wrapper outside the window (at most 372 instr per block) is added as an allowance. + +The criterion is strict: the whole frame-interrupt body, stock reserve plus the module, must fit one block on every block, including the first START after boot, with the measured run-to-run spread to spare. + +### Results (instr per block, frame-interrupt body; the first run of every card) + +| configuration | sustained mean (playing) | first START after boot | later START blocks | `sy_render` per block, mean / max | | --- | ---: | ---: | ---: | ---: | -| One voice, INDX=0 | 14,630 | 1,827 | −18.07 dB | 0 | -| Four voices, CHRD=127 | 21,912 | 2,740 | −18.06 dB | 1 | +| no FM track playing (stock reserve, CF DELAY ×8) | 22,746 | 22,775 | 22,893 max | 0 | +| 1 track VOIC 1 | 24,962 | 27,756 | 27,046 | 2,313 / 2,603 | +| 3 tracks VOIC 1 | 29,332 | 37,368 | 35,252 | 6,934 / 7,809 | +| 4 tracks VOIC 1 | 31,483 | 42,140 | 39,308 | 9,245 / 10,412 | +| 8 tracks VOIC 1 | 40,305 | 61,441 | 55,773 | 18,490 / 20,824 | +| 2 tracks VOIC 2 | 29,709 | 37,051 | 39,415 | 7,179 / 13,466 | +| **1 track VOIC 4** | 27,765 | **32,298** | 31,393 | 5,207 / 7,039 | +| 2 tracks VOIC 4 | 32,915 | 41,613 | 40,025 | 10,407 / 14,078 | +| 4 tracks VOIC 4 | 43,094 | 60,292 | 57,052 | 20,814 / 28,156 | +| 8 tracks VOIC 4, DEC 127 (HOLD) | 63,398 | 97,697 | 91,243 | 41,650 / 56,312 | +| **8 tracks VOIC 4, DEC 0 (shortest)** | **63,369** | **97,705** | 91,113 | 41,649 / 56,312 | +| 8 tracks VOIC 4, DEC 127, chosen as FM SYNTH (Part signature) | 55,958 | 88,317 | 81,385 | 34,168 / 48,624 | +| reference: 8 stock sample tracks, same locks/LFOs/scenes | 26,996 | 51,809 | 46,836 | 4,965 / 11,867 | -Both mono takes have an autocorrelation period of 169 frames, about 261 Hz. This is a coarse period estimate, not a precision tuning claim. Both four-voice takes sound and stop; their 149-frame autocorrelation minimum is not interpreted as a single-note pitch. Host scheduling and audio delivery behavior are not measurements of real chip timing. +Five runs of the bounding cards: one VOIC 4 track peaks at 32,214..32,299 (spread 85); the no-FM block floor is 22,619..22,709 (spread 90); eight VOIC 4 tracks peak at 97,681..97,729 and run at 63,362..63,405 sustained. -Sanitized measurements, fixture hashes and PCM fingerprints are in `evidence/regressions.json`; panel walks are the adjacent JSONL files. Raw logs, WAVs, card images, battery files, firmware and debugger memory stay private. +FM's own code per `sy_render` call (two calls per track per block), excluding the nested stock renderer: -Reproduce with a reviewed local Octemu checkout and the sample-free fixture above, inside a restricted local sandbox with reads limited to tool/runtime, emulator and private fixture/image folders, writes limited to a fresh private output folder, and only local emulator IPC: +| VOIC | own mean | own max | +| --- | ---: | ---: | +| 4 | 2,065–2,068 | 5,813 (5,772 on the chooser route) | +| 2 | 1,330 | 5,645 | +| 1 | 625 | 1,491 | +| ordinary sample track (pass-through) | 83 | 104 | -```sh -python3 -B verify-regressions.py \ - --octemu /path/to/octemu --image /private/patched-main.bin \ - --image-sha256 64beb992dd65cad096d2c70f23bb3064c715464d651da22dd3ad9024901831e9 \ - --card /private/sample-free/card.img --battery /private/sample-free/nvram.bin \ - --output /private/new-midi-run --case midi --timeout 300 -``` +On marker tracks the stock renderer adds 532 mean, 1,175 max per call. The chooser route nests no stock renderer. -Use `--case mono` or `--case poly` and a fresh output path to record audio. The runner verifies its supplied image hash, copies the fixture, gates on firmware displays/transport, checks sustained audio and double-STOP silence, and checks the mono carrier period. Run the same audio walks against the baseline image for a paired gain comparison. All three runner cases were executed against the exact 0.1.2 browser image. The earlier screenshot walks are retained separately as development evidence; their LCD controls are unchanged. +The cost per track (first run): -## LCD captures and remaining qualification +- One VOIC 4 track adds 5,018 to 5,087 sustained and 9,379 to 9,523 on its START block. +- One VOIC 1 track adds 2,184 to 2,215 sustained and 4,833 to 4,981 on its START block. +- The chooser route is cheaper, because no stock sample renderer runs under it: for eight VOIC 4 tracks, 7,440 less sustained and 9,380 less at the peak. The marker route bounds the cost. -All seven control/chooser LCD images were captured again from the patched standalone native MAIN, then opened and visually reviewed. Their pixels are unchanged from 0.1.1, while their provenance binds the new emitted image. `media/capture.json` records the emulator, image, plan and screenshots. The maximum-AMP, four-voice and MIDI-selector screenshots have separate Octemu regression provenance. +**Against the 2.11 build** (MAIN OS `248d987e…`, the same cards and rig, one run per card): the frame-interrupt code differs only by the removed `po_stop` hook, and 2.12's machine-change code runs in the UI task. The counts agree within the rig's run-to-run spread: START peaks differ by at most 78 instructions (0.19 %), sustained means by at most 87. The record below uses this image's own numbers. -Browser runtime reservation remains 10,586,112 bytes (native shared arena plus logger reservation), with a reported runtime extent of 90,268 bytes. These are shared build extents, not complete Synth ownership or worst-case stack/lifetime bounds. Worst-case ColdFire cycles and DSP transport overhead are unmeasured. Hardware on both models, maximum simultaneous tracks, external MIDI note/CC traffic, modulation, p-locks, scene sweeps, effects, saved project/Part reloads, non-default speed/length, swing, loop/restart transitions and neighbouring sample playback remain untested. +**Worst case found:** 97,729 instr in one block, at the first PLAY after boot with eight VOIC 4 marker tracks starting chords together (DEC 0; DEC 127 is within the spread). That is 243,557 cycles, 2.54× the block. Sustained, the same load runs at 63,362..63,405 instr, 1.65× the block. The eight-track VOIC 4 configuration cannot meet the budget. -`evidence/release-0.1.1/` retains the previous reports and approval as historical context. They do not qualify 0.1.2. `qualification.example.json` remains an incomplete worksheet. The owner authorized this experimental release despite the disclosed qualification limits. The current source-bound approval is in `sdk/synth-build-approval.json`; version-matched release notes, catalog and compiled packages are updated together. Hardware, timing and memory quantities remain unknown. +### Pricing for the record -## Repository validation +- `worstCase`, one instance: the START-block increment of one VOIC 4 track over the no-FM block minimum, the largest one-track peak of the five runs over the smallest floor. That is 32,299 − 22,619 = 9,680 instr → **24,125 cycles/block** (rounded up). +- `maxConfiguration`: the largest configuration whose every block, including the first START, fits under the module's own worst case (VOIC 4 chords) is **one FM track**. Its largest measured peak is 32,299 instr + 372 wrapper allowance = 32,671 instr → **81,422 cycles/block** (rounded up). That is inside the budget of 95,779 and leaves 14,357 cycles (5,761 instruction-times), far more than the measured spread (90). +- `maxInstances` = 1 for cycles: one FM track at VOIC 4. Memory is preallocated for eight tracks, so `memory.maxInstances` = 8. + +Three tracks at VOIC 1 also keep every block in budget: 37,368 + 372 = 37,740. These configurations fit only at the 8.38 ns end of the calibration (≤ 43,294): + +- 2 tracks at VOIC 2: 39,415 + 372 = 39,787; +- 2 tracks at VOIC 4: 41,613 + 372 = 41,985; +- 4 tracks at VOIC 1: 42,140 + 372 = 42,512. + +Everything larger does not fit. Eight ordinary sample tracks starting together on the same fixture peak at 51,809 instr, which is also over the block: a simultaneous START is a single-block transient that stock firmware itself produces. A configuration outside this record is not shown to fail on the unit; it is only not qualified. + +### Exact memory, from the link map + +| region | bytes | scope | +| --- | ---: | --- | +| engine DRAM unit `poly`, code and tables, `0x40a955e0..0x40a9921c` | 15,420 | shared | +| per-track state ×8: `sy_state` 128 + 4 voices × 64 + `po_pend` 4 × 16 + `po_chord` 32 + `po_mheld` 8 + `po_mring` 16 + `po_mhead`/`po_mtail` 2 (interleaved arrays `0x40a9921c..0x40a9a1f4`) | 506 per track = 4,048 | instance | +| shared engine state: `po_seq`, `po_vbuf`, `po_clock`, `po_rtlog`, LFO/PLAYBACK/AMP descriptor clones (`..0x40a9a7e4`) | 1,528 | shared | +| chooser DRAM unit `fmmachine` (`machine.s`: code, the six-name table, the 2.11 row table, the eleven list detours and 2.12's machine-change init) | 2,076 | shared | +| pinned FM PLAYBACK page `fmpage`, `0x400d24d0..0x400d2c1c`, in the free ROM run `0x400d24d0..0x400d2ce0` | 1,868 | shared | +| FM hook sites rewritten in place: 52 detours (416 B; eleven the 2.11 machine-list sites, `po_unload` the 2.12 file-browser site in place of the removed `po_stop`), the resolver jump (6 B) | 422 | shared | +| kind-table entries: FLEX and STATIC renderer, FLEX START | 12 | shared | +| bundled quantizer DRAM unit `fm_qz` | 3,532 | shared | +| bundled quantizer ROM units (`fm_qzk` 45, `fm_qzc` 192, `fm_qzs` 6), SEQUENCER tables 72, 19 detour sites 142, one poke 4 | 461 | shared | +| platform loader and packed DRAM units appended to the image at `0x4010fdf0` | 14,093 | shared | +| platform reserve: 1,707 pages × 6,144 = 10,487,808 B of Flex sample RAM, less the three DRAM units (26,604) | 10,461,204 | shared | +| ColdFire stack increment, upper bound (see Stack) | 352 | shared | + +Totals: 506 × 8 + 10,500,968 = **10,505,016 bytes**. The module's own bytes are **43,812** (the 2.11 build: 42,722; the difference is the chooser unit's machine-change init, +636, the engine, +16, and the appended loader that packs the larger units, +438). The rest is the sample-RAM platform reserve, which any DRAM module creates (sample pages 14,602 → 12,895) and which is counted once. + +Reconciliation with the native allocation map: + +- `runtime.elf` `.text` is 26,604 B at `0x40a955e0`: `poly` 20,996 (`00_poly.o`), `fmmachine` 2,076 (`01_fmmachine.o`) and `fm_qz` 3,532 (`02_fm_qz.o`). The `.text` of `02_fm_qz.o` is byte-identical to the 2.11 build's. +- poly's 20,996 B = 15,420 + 1,528 + 8 × 506. +- In `layout.json`, `runtime_end` is `0x40a9bdcc`, and the boot stage holds 13,861 B. The append is 14,093 B, as the build log says. +- The build log reports 12,895 pages left for samples and recorders, against stock's 14,602. -The fixes originated at `73490ef77f36bf523bc28f2b385d62e331ec7c5c`; current compiled code and exact release-image identities are recorded in `evidence/regressions.json`. The earlier draft's 167-file / 1,077-test result remains in `evidence/software-checks.json` as historical evidence. The 0.1.2 release runs `npm run check -- --base origin/main` and `module:doctor -- synth` against the promoted source and current compiled packages. +There are no other allocations: no heap, no dynamic allocation and no DSP or SRAM allocation. The module has no battery-RAM bytes of its own. GLIDE is the bundled quantizer's byte. LEG, the LFO destination and the FM SYNTH signature use stock Part bytes and their battery-RAM shadow, written by the author's code or by stock's own store; the machine-change init writes stock Part bytes, their shadow and the live lane. + +### Stack + +A static upper bound computed from the assembled runtime of this image (`runtime.elf`), following its control flow: SP displacements of every instruction, both ways at each conditional branch, the callee's own bound at each call (+4 for the return address), a jump out of the runtime ending a path at its depth. Exclusive branches are not summed. Every detour, symbol-ref and table entry point in the runtime was analysed (71; the bundled quantizer's two ROM-unit entries are outside the runtime). + +| path | entry points | bound | +| --- | --- | ---: | +| frame interrupt | `sy_render` 132, `po_fmsource` 32, `po_fmstart` 32, `po_retrig` 24, `po_kill` 12, `po_lfo3b` 8, `po_lfo3` 4, `po_rel` 0 | 132 B | +| MIDI task | `po_mrec` 196, `po_moff` 188, `po_mogate` 52, `po_mon` 44, `po_mtrig` 44, `po_mgate` 44 | 196 B | +| UI task, chooser (`machine.s`) | `fm_validate` 220 (its 116-byte frame + the replayed stock 96-byte validator prologue), `fm_main_commit` 116 (88 on the 2.11 build: it now calls the machine-change init), `fm_src_commit`/`2` 108 (80), `fm_setup_open` 36, `fm_name_a`/`fm_chooser_row`/`fm_chooser_open` 32, `fm_tick` 12, `fm_setup_row` 8, `ml_win_count`/`ml_src_count`/`ml_draw_bound` 8 (each replays the stock pushes it replaces), `fm_machine_name` 4, the other list detours 0 | 220 B | +| UI task, engine pages | `po_assign` 112 (80 on the 2.11 build), `po_machwin`/`po_machlist`/`po_octave` 88, `po_loadsel` 84 (32), `po_unload` 20 (new), `po_ampstage`/`po_ampdraw1`/`po_ampdraw2`/`po_ampdraw3` 52, `po_ampedit`/`po_amplane` 48, `po_lfdedit` 24, `po_lfopage` 16, `po_lfdname`/`po_lfdlfo` 12 | 112 B | +| UI task, bundled quantizer | `qz_plock` 76, `qz_knob` 68, `qz_leg0` 68, `qz_leg1` 48, `qz_wr` 36, the others 28 or less | 76 B | + +Calls into stock add stock's own frames on top (`ml_draw_bound` calls stock's name formatter exactly where stock does). The frame interrupt runs on whichever task stack it interrupts, so the worst single-stack increment is the deepest task path plus the frame path: 220 + 132 = **352 B**, as in the 2.10 and 2.11 builds. + +### What was not measured + +- **Chip cycles.** The counts are converted by a calibration from one hardware session. They are not chip wall-clock cycles. +- **UI-task costs.** Machine choice (with the machine-change init), the Part validator, page and setup-window changes, Part changes and project load run in the UI task, outside the frame-interrupt deadline. They were not timed; their stack is bounded statically above. +- **USB and MIDI traffic, live keys, the metronome.** These were not driven in the cost windows. The key gate's test runs at every START, so the trig STARTs above include it. +- **Hardware stack canary.** Not measured. +- **Other fixtures.** One fixture family was used (120 BPM, CF DELAY on all eight tracks). + +## Hardware + +The author tests this image (the update file above, MAIN OS `00fa6caa…`) with a short test sheet that covers: + +- the metronome case: CLICK ON, the sequencer playing, a held chord and a held key that keep sounding through the clicks; +- a machine change FM SYNTH → FLEX → FM SYNTH that shows FM SYNTH's init on PLAYBACK and LFO, with an FX1 setting kept; +- the FM SYNTH row in SRC SETUP and SELECT MACHINE TYPE; +- a power cycle with the card in (the battery-RAM case the emulator cannot test) with two FM tracks. + +His report fills `tests.qualification.hardware`, whose `TIM:` fields are placeholders, and sets `hardwareStatus` to `reported`. Until then this version is not publishable under the qualification gate. The earlier local builds of 0.1.3 and 0.1.4 (MAIN OS `d5e1a7b0…`, `248d987e…`) are superseded. + +Historical hardware: the 2.3–2.9 builds and the first 2.10 build ran on the author's MKI, and his 7–8 October report on that build produced the fixes above. On 10 October he reported the metronome cut-out and the machine-change request on the 2.11 build (`248d987e…`); 2.12 is his answer to both. Those reports do not qualify this exact image. + +## Repository validation -The release preserves every non-Synth package payload and recipe against the main-branch baseline. The retained Analog BD native matrix is checked against the current packages: 130 native OS and GNU bootloader comparisons, six matching refusals and five complete firmware round trips. Its global source-inventory binding changes only after those checks; Analog BD source and package bytes are unchanged. These software comparisons do not qualify hardware. +See `docs/VERIFICATION.md` for the checks run on this branch. diff --git a/sdk/octabam/modules/synth/evidence/native-smoke.json b/sdk/octabam/modules/synth/evidence/native-smoke.json new file mode 100644 index 000000000..413134edf --- /dev/null +++ b/sdk/octabam/modules/synth/evidence/native-smoke.json @@ -0,0 +1,13 @@ +{ + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "checks": { + "eightTrackSelection": "passed", + "sampleSlotsUnchanged": "passed", + "partAndShadowSignature": "passed", + "reselectPreservesControls": "passed", + "returnToFlex": "passed" + }, + "limitations": [ + "Panel smoke only; Empty-card storage/UI smoke; audio is tested separately in Octemu. No cycle, hardware, file persistence or maximum-load qualification." + ] +} diff --git a/sdk/octabam/modules/synth/evidence/release-0.1.2/TESTING.md b/sdk/octabam/modules/synth/evidence/release-0.1.2/TESTING.md new file mode 100644 index 000000000..a196e23b6 --- /dev/null +++ b/sdk/octabam/modules/synth/evidence/release-0.1.2/TESTING.md @@ -0,0 +1,68 @@ +# FM Synth 0.1.2 regression testing + +This is the experimental 0.1.2 update for [#264](https://github.com/repeat98/modwerk/issues/264) and [#273](https://github.com/repeat98/modwerk/issues/273), tested on 8 October 2026. It is included in the catalog. Current hardware, chip worst-case timing and complete stack/memory bounds remain unknown. The owner explicitly authorized this new release and issue closure without current hardware validation: “please release them and close. I green light no hardware validation for now”. This is a new exact-source update approval; the 0.1.1 approval is historical. + +## Changes and reproduced failure + +The privately bundled quantizer's octave-number hook originally replaced eight bytes at `0x400449b8`. The stock MIDI branch targets `0x400449be`, inside that replacement, and skips the required octave argument push. The drawer then consumes one more argument than was pushed and corrupts its caller's stack. On the released browser image, selecting a MIDI trig mode freezes the UI; the debugger finds the stock HALT diagnostic after a bad return into a data address. The baseline does not reach the post-selector SRC gate. + +The replacement starts at the common argument push, `0x400449be`, with a fresh eight-byte stock hash guard. MIDI uses the octave already in d0, pushes it once, preserves the stock format argument and resumes at `0x400449c6`. Audio retains the scale/root rendering. Normal MIDI/audio paths no longer branch into detour padding. The patched browser image passes repeated selector DOWN/UP navigation and returns to the Synth SRC controls. The repeatable `verify-regressions.py --case midi` runner also passes, including both pre- and post-selector display gates. + +FM source output now shifts right three bits: mono after its output conversion, and each paraphonic contribution before accumulation. This leaves 18.06 dB more headroom before the stock AMP path and voice sum. Existing envelope gains, relative voice normalization and crossfade state are preserved. Every existing sound is quieter at the same mixer settings. Effects, resonant filtering and multiple tracks can still clip. + +**The reported hardware static has not been reproduced.** Both baseline audio configurations below remain below signed 16-bit full scale. The measurements verify attenuation and continued playback/STOP recovery, not resolution of every artifact in #273. A physical MKI/MKII retest with the reporter's sound/settings is still needed. + +## Private software builds + +The current 0.1.2 source and metadata are compiled together with the ordinary shared compiler. The earlier development-image report is retained separately in `evidence/development-regressions.json`. Current-main builder inputs produce a slightly different browser image, so MIDI, mono and four-voice regressions were all rerun on that exact release image. The standalone native image reproduces the freshly captured LCD image byte for byte. The compiler/exporter run in the isolated stock-free toolchain container with network disabled, a read-only root and private tracked-source/output mounts. No firmware enters Git. +Compilation passes. Only the private Synth `poly`/`fm_qz` objects and their linked group change in requested-packages; unrelated packages remain identical. Build identities are recorded in `evidence/regressions.json`: + +- Patched browser MAIN: `64beb992dd65cad096d2c70f23bb3064c715464d651da22dd3ad9024901831e9`. +- Patched upgrade: `c1a57f818998923e27ee9ae36061f2c1fe050788619ccceec6d0c24dac04d392`. +- Patched standalone native MAIN: `d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157`. +- Released 0.1.1 browser MAIN baseline: `c1fbc0b2eaf284e72692b7048d1dfd3693f3e3e90c71229159a619d585b52c95`. + +The native proof's `osSha256` is the module-owned image before platform writes; it is not the emitted MAIN's file hash. Capture/replay checks use the actual emitted file hash. + +All **110 current coverage selections** pass the existing `compareSelection` native/browser comparator: **31 built** (module-owned bytes equal outside the established platform/logger spans), **79 matching refusals**, **zero mismatches**. This covers standalone Synth, pairings, fuller selections and both stock-FX2 policies. The input stock image remains unchanged. An altered stock byte is rejected and that input also remains unchanged. See `evidence/composition.json`. Mixed images have composition evidence only, not audio stress qualification. + +## Panel and audio regressions + +Octemu binary SHA-256: `3ce6cebde0de2c61edc4228436338a1e115ce227373351a5ca84aefc9cf90acb`. The local source checkout is dirty, so its HEAD alone is not claimed as the tested executable identity. A private copied, sample-free firmware-created project has a step-1 T1 trig and FX1/FX2 NONE. Card/battery identities are recorded in `evidence/regressions.json`. Every image/case starts with a fresh copy of the battery state. Native UI capture uses the separately fingerprinted headless `ot_emu` in `media/capture.json`. The extra Octemu regression screenshots retain their original development-image bindings in `media/regression-capture.json`; they are not relabelled as new browser-image captures. + +Actual panel input selects FM SYNTH, sets INDX=0, AMP REL raw 40 and AMP VOL maximum (display +63), starts PLAY, then sends STOP twice. The four-voice case additionally sets VOIC=4 and CHRD raw 127 (display M7S2). The retained LCD exports confirm these controls. Recordings are stereo 44.1 kHz signed 16-bit PCM, including boot/settle silence. Peak comparisons therefore measure the full captured take rather than time-aligned samples. + +| Configuration | Released peak | Patched peak | Peak change | Final half-second peak, patched | +| --- | ---: | ---: | ---: | ---: | +| One voice, INDX=0 | 14,630 | 1,827 | −18.07 dB | 0 | +| Four voices, CHRD=127 | 21,912 | 2,740 | −18.06 dB | 1 | + +Both mono takes have an autocorrelation period of 169 frames, about 261 Hz. This is a coarse period estimate, not a precision tuning claim. Both four-voice takes sound and stop; their 149-frame autocorrelation minimum is not interpreted as a single-note pitch. Host scheduling and audio delivery behavior are not measurements of real chip timing. + +Sanitized measurements, fixture hashes and PCM fingerprints are in `evidence/regressions.json`; panel walks are the adjacent JSONL files. Raw logs, WAVs, card images, battery files, firmware and debugger memory stay private. + +Reproduce with a reviewed local Octemu checkout and the sample-free fixture above, inside a restricted local sandbox with reads limited to tool/runtime, emulator and private fixture/image folders, writes limited to a fresh private output folder, and only local emulator IPC: + +```sh +python3 -B verify-regressions.py \ + --octemu /path/to/octemu --image /private/patched-main.bin \ + --image-sha256 64beb992dd65cad096d2c70f23bb3064c715464d651da22dd3ad9024901831e9 \ + --card /private/sample-free/card.img --battery /private/sample-free/nvram.bin \ + --output /private/new-midi-run --case midi --timeout 300 +``` + +Use `--case mono` or `--case poly` and a fresh output path to record audio. The runner verifies its supplied image hash, copies the fixture, gates on firmware displays/transport, checks sustained audio and double-STOP silence, and checks the mono carrier period. Run the same audio walks against the baseline image for a paired gain comparison. All three runner cases were executed against the exact 0.1.2 browser image. The earlier screenshot walks are retained separately as development evidence; their LCD controls are unchanged. + +## LCD captures and remaining qualification + +All seven control/chooser LCD images were captured again from the patched standalone native MAIN, then opened and visually reviewed. Their pixels are unchanged from 0.1.1, while their provenance binds the new emitted image. `media/capture.json` records the emulator, image, plan and screenshots. The maximum-AMP, four-voice and MIDI-selector screenshots have separate Octemu regression provenance. + +Browser runtime reservation remains 10,586,112 bytes (native shared arena plus logger reservation), with a reported runtime extent of 90,268 bytes. These are shared build extents, not complete Synth ownership or worst-case stack/lifetime bounds. Worst-case ColdFire cycles and DSP transport overhead are unmeasured. Hardware on both models, maximum simultaneous tracks, external MIDI note/CC traffic, modulation, p-locks, scene sweeps, effects, saved project/Part reloads, non-default speed/length, swing, loop/restart transitions and neighbouring sample playback remain untested. + +`evidence/release-0.1.1/` retains the previous reports and approval as historical context. They do not qualify 0.1.2. `qualification.example.json` remains an incomplete worksheet. The owner authorized this experimental release despite the disclosed qualification limits. The current source-bound approval is in `sdk/synth-build-approval.json`; version-matched release notes, catalog and compiled packages are updated together. Hardware, timing and memory quantities remain unknown. + +## Repository validation + +The fixes originated at `73490ef77f36bf523bc28f2b385d62e331ec7c5c`; current compiled code and exact release-image identities are recorded in `evidence/regressions.json`. The earlier draft's 167-file / 1,077-test result remains in `evidence/software-checks.json` as historical evidence. The 0.1.2 release runs `npm run check -- --base origin/main` and `module:doctor -- synth` against the promoted source and current compiled packages. + +The release preserves every non-Synth package payload and recipe against the main-branch baseline. The retained Analog BD native matrix is checked against the current packages: 130 native OS and GNU bootloader comparisons, six matching refusals and five complete firmware round trips. Its global source-inventory binding changes only after those checks; Analog BD source and package bytes are unchanged. These software comparisons do not qualify hardware. diff --git a/sdk/octabam/modules/synth/evidence/release-0.1.2/build-approval.json b/sdk/octabam/modules/synth/evidence/release-0.1.2/build-approval.json new file mode 100644 index 000000000..e6800a769 --- /dev/null +++ b/sdk/octabam/modules/synth/evidence/release-0.1.2/build-approval.json @@ -0,0 +1,15 @@ +{ + "kind": "owner-approved-update", + "id": "synth", + "version": "0.1.2-experimental", + "sourceSha256": "8fa263bfcd30dca9aff4f83dbcb001d4ccf06286e31839c8e0afd61100a9315c", + "approvedBy": "repeat98", + "approvedOn": "2026-10-08", + "waived": [ + "current-build-hardware", + "chip-worst-case-cycles", + "complete-memory-bounds" + ], + "ownerStatement": "please release them and close. I green light no hardware validation for now", + "reason": "The owner authorized release of the experimental 0.1.2 fixes after being told that current hardware, chip timing and complete memory qualification remain missing. Software MIDI and mono/four-voice regressions and native/browser parity are retained; all unknown quantities stay unmeasured. This exception covers only the exact current source." +} diff --git a/sdk/octabam/modules/synth/evidence/composition.json b/sdk/octabam/modules/synth/evidence/release-0.1.2/composition.json similarity index 100% rename from sdk/octabam/modules/synth/evidence/composition.json rename to sdk/octabam/modules/synth/evidence/release-0.1.2/composition.json diff --git a/sdk/octabam/modules/synth/evidence/development-regressions.json b/sdk/octabam/modules/synth/evidence/release-0.1.2/development-regressions.json similarity index 100% rename from sdk/octabam/modules/synth/evidence/development-regressions.json rename to sdk/octabam/modules/synth/evidence/release-0.1.2/development-regressions.json diff --git a/sdk/octabam/modules/synth/media/amp-max-fixed.png b/sdk/octabam/modules/synth/evidence/release-0.1.2/media/amp-max-fixed.png similarity index 100% rename from sdk/octabam/modules/synth/media/amp-max-fixed.png rename to sdk/octabam/modules/synth/evidence/release-0.1.2/media/amp-max-fixed.png diff --git a/sdk/octabam/modules/synth/evidence/release-0.1.2/media/capture.json b/sdk/octabam/modules/synth/evidence/release-0.1.2/media/capture.json new file mode 100644 index 000000000..be7047e55 --- /dev/null +++ b/sdk/octabam/modules/synth/evidence/release-0.1.2/media/capture.json @@ -0,0 +1,203 @@ +{ + "firmware": "1.40C", + "imageSha256": "d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157", + "emulatorSha256": "f521565f3866947788d7c45507576a4ed386d2e4ef57e9683ab65fa0acc6a166", + "setup": "Headless ot_emu; MKII panel; stopped transport; 128×64 LCD at integer scale 6.", + "palette": { + "on": [ + 240, + 240, + 240 + ], + "off": [ + 24, + 24, + 24 + ] + }, + "keyMs": 150, + "plan": [ + { + "wait": 3000 + }, + { + "press": [ + "YES" + ] + }, + { + "wait": 2000 + }, + { + "press": [ + "NO" + ] + }, + { + "press": [ + "FUNC", + "SRC" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "YES" + ] + }, + { + "capture": "ot-location.png" + }, + { + "press": [ + "SRC" + ] + }, + { + "capture": "ot-controls.png" + }, + { + "press": [ + "LFO" + ] + }, + { + "capture": "ot-voices.png" + }, + { + "press": [ + "FUNC", + "AMP" + ] + }, + { + "capture": "ot-legato.png" + }, + { + "press": [ + "NO" + ] + }, + { + "press": [ + "MENU" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "RIGHT" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "YES" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "press": [ + "DOWN" + ] + }, + { + "encoder": { + "name": "LEVEL", + "delta": 1 + } + }, + { + "capture": "ot-scale.png" + }, + { + "press": [ + "DOWN" + ] + }, + { + "capture": "ot-root.png" + }, + { + "press": [ + "DOWN" + ] + }, + { + "capture": "ot-glide.png" + } + ], + "screenshots": { + "ot-location.png": "4e1d84898790433813720c437d2e767cb3225c7770ea15be9115f76e70b82270", + "ot-controls.png": "2eb52e9b732308891e289aa23d92556a87dd8820b12a37cbe73f66e720a162f0", + "ot-voices.png": "0e99bbc3cdecc61bb66920b5648f517198b191ad3dc04a78af14e2256e1cdf3c", + "ot-legato.png": "e5cd86583126a834f8a9351a8f2b51bca9f76c101023e37db4c933be957d5bc8", + "ot-scale.png": "71ffe8fe7413a1e2eb0605ad7119159bba95db7f7734dc47ae13468f68a74d13", + "ot-root.png": "53f0f6dacf09665f41629428569d9fbfac5847698c20f35a6cbe6b78a88f99e4", + "ot-glide.png": "77b4cf73fd16e96d1c78f5a9cdd0590b20a7958c614d1c6cdb93b77791dc505b" + }, + "schemaVersion": 1, + "moduleId": "synth", + "moduleVersion": "0.1.2-experimental", + "captured": "2026-10-08", + "imageProfile": { + "modules": [ + "SYNTH MACHINE" + ], + "keepStockFx2": true, + "staticStock": true + }, + "releaseProvenance": "Fresh 8 October captures from the native Synth 0.1.2 executable. Promotion must reproduce the exact emitted MAIN image fingerprint; existing software captures confer no hardware verification.", + "sourceSha256": "8fa263bfcd30dca9aff4f83dbcb001d4ccf06286e31839c8e0afd61100a9315c" +} diff --git a/sdk/octabam/modules/synth/media/midi-synth-chromatic-fixed.png b/sdk/octabam/modules/synth/evidence/release-0.1.2/media/midi-synth-chromatic-fixed.png similarity index 100% rename from sdk/octabam/modules/synth/media/midi-synth-chromatic-fixed.png rename to sdk/octabam/modules/synth/evidence/release-0.1.2/media/midi-synth-chromatic-fixed.png diff --git a/sdk/octabam/modules/synth/media/poly-controls.png b/sdk/octabam/modules/synth/evidence/release-0.1.2/media/poly-controls.png similarity index 100% rename from sdk/octabam/modules/synth/media/poly-controls.png rename to sdk/octabam/modules/synth/evidence/release-0.1.2/media/poly-controls.png diff --git a/sdk/octabam/modules/synth/media/regression-capture.json b/sdk/octabam/modules/synth/evidence/release-0.1.2/media/regression-capture.json similarity index 100% rename from sdk/octabam/modules/synth/media/regression-capture.json rename to sdk/octabam/modules/synth/evidence/release-0.1.2/media/regression-capture.json diff --git a/sdk/octabam/modules/synth/evidence/midi-walk.jsonl b/sdk/octabam/modules/synth/evidence/release-0.1.2/midi-walk.jsonl similarity index 100% rename from sdk/octabam/modules/synth/evidence/midi-walk.jsonl rename to sdk/octabam/modules/synth/evidence/release-0.1.2/midi-walk.jsonl diff --git a/sdk/octabam/modules/synth/evidence/mono-max-walk.jsonl b/sdk/octabam/modules/synth/evidence/release-0.1.2/mono-max-walk.jsonl similarity index 100% rename from sdk/octabam/modules/synth/evidence/mono-max-walk.jsonl rename to sdk/octabam/modules/synth/evidence/release-0.1.2/mono-max-walk.jsonl diff --git a/sdk/octabam/modules/synth/evidence/poly-max-walk.jsonl b/sdk/octabam/modules/synth/evidence/release-0.1.2/poly-max-walk.jsonl similarity index 100% rename from sdk/octabam/modules/synth/evidence/poly-max-walk.jsonl rename to sdk/octabam/modules/synth/evidence/release-0.1.2/poly-max-walk.jsonl diff --git a/sdk/octabam/modules/synth/evidence/regressions.json b/sdk/octabam/modules/synth/evidence/release-0.1.2/regressions.json similarity index 100% rename from sdk/octabam/modules/synth/evidence/regressions.json rename to sdk/octabam/modules/synth/evidence/release-0.1.2/regressions.json diff --git a/sdk/octabam/modules/synth/evidence/software-checks.json b/sdk/octabam/modules/synth/evidence/release-0.1.2/software-checks.json similarity index 100% rename from sdk/octabam/modules/synth/evidence/software-checks.json rename to sdk/octabam/modules/synth/evidence/release-0.1.2/software-checks.json diff --git a/sdk/octabam/modules/synth/machine.s b/sdk/octabam/modules/synth/machine.s deleted file mode 100644 index 3e2ca5255..000000000 --- a/sdk/octabam/modules/synth/machine.s +++ /dev/null @@ -1,335 +0,0 @@ -| FM Synth registration, derived from Modwerk's Analog BD chooser hooks. -| FLEX plus FM/1 signature in unused NEIGHBOR page bytes; all eight tracks. -| A new assignment seeds both Part copies; reselect preserves the patch. -| Playback resolves our runtime descriptor per track. Audio enters through -| the native renderer kind table, leaving AMP, FX1 and FX2 to stock. - .text - .global fm_machine_name - .global fm_src_names - .global fm_main_commit - .global fm_src_commit - .global fm_name_a, fm_name_b - .global fm_setup_row, fm_chooser_row - .global fm_setup_open, fm_chooser_open - .global fm_src_commit2, fm_setup_edit6, fm_setup_draw6 - .global fm_resolve_pb - .global fm_sig_check - .global fm_validate - - .equ FM_ROW, 5 - .equ FLEX, 1 - .equ SRC_CURSOR, 0x460d5c30 - .equ BANK_PTR, 0x46c82456 - .equ PART_IDX, 0x100b14cf - .equ PART_STRIDE, 6322 - .equ PART_OFF, 0x8ed80 - .equ SRAM_PART, 0x100a4ece - .equ SIG_OFF, 0x2a + 18 - .equ FLEX_P, 0x400d31ae - .equ PAGE2_GAP, 0x1da - 0x2a - .equ PB_TABLE, 0x400d5f38 - -fm_sig_check: - lea -8(%sp),%sp - movem.l %d1/%a1,(%sp) - moveq #0,%d1 - move.b 0x22(%a0,%d0.l),%d1 - cmpi.l #FLEX,%d1 - bne.s .sc_no - move.l %d0,%d1 - mulu.w #30,%d1 - lea SIG_OFF(%a0,%d1.l),%a1 - move.b (%a1),%d1 - cmpi.b #'F',%d1 - bne.s .sc_no - move.b 1(%a1),%d1 - cmpi.b #'M',%d1 - bne.s .sc_no - move.b 2(%a1),%d1 - cmpi.b #1,%d1 - bne.s .sc_no - moveq #1,%d0 - bra.s .sc_done -.sc_no: - moveq #0,%d0 -.sc_done: - movem.l (%sp),%d1/%a1 - lea 8(%sp),%sp - rts - -fm_sig_write: - lea -16(%sp),%sp - movem.l %d0-%d2/%a1,(%sp) - mulu.w #30,%d0 - lea SIG_OFF(%a0,%d0.l),%a1 - bsr.s .sw_one - move.l %a0,%d2 - sub.l (BANK_PTR).l,%d2 - subi.l #PART_OFF,%d2 - addi.l #SRAM_PART + SIG_OFF,%d2 - add.l %d0,%d2 - movea.l %d2,%a1 - bsr.s .sw_one - movem.l (%sp),%d0-%d2/%a1 - lea 16(%sp),%sp - rts -.sw_one: - tst.l %d1 - beq.w .sw_clear - mvz.w (%a1),%d2 - cmpi.w #0x464d,%d2 - bne.s .sw_seed - mvz.b 2(%a1),%d2 - cmpi.b #1,%d2 - beq.s .sw_mark -.sw_seed: - move.l %a0,-(%sp) - move.l %d0,-(%sp) - lea fm_defaults,%a0 - moveq #5,%d0 -.sw_defaults1: - move.b (%a0)+,-12(%a1) - addq.l #1,%a1 - subq.l #1,%d0 - bpl.s .sw_defaults1 - lea -6(%a1),%a1 - moveq #5,%d0 -.sw_defaults2: - move.b (%a0)+,PAGE2_GAP-12(%a1) - addq.l #1,%a1 - subq.l #1,%d0 - bpl.s .sw_defaults2 - lea -6(%a1),%a1 - move.l (%sp)+,%d0 - move.l (%sp)+,%a0 -.sw_mark: - move.b #'F',(%a1) - move.b #'M',1(%a1) - move.b #1,2(%a1) - rts -.sw_clear: - mvz.w (%a1),%d2 - cmpi.w #0x464d,%d2 - bne.s .sw_out - mvz.b 2(%a1),%d2 - cmpi.b #1,%d2 - bne.s .sw_out - clr.b (%a1) - clr.b 1(%a1) - clr.b 2(%a1) -.sw_out: - rts - -fm_machine_name: - move.l 4(%sp),%d0 - cmpi.l #FM_ROW,%d0 - beq.s 1f - move.l %d2,-(%sp) - move.l 8(%sp),%d1 - jmp (0x400334de).l -1: lea fm_name(%pc),%a0 - move.l %a0,%d0 - rts - - .balign 4 -fm_src_names: - .long 0x400b3eac - .long 0x400b3e98 - .long 0x400b7c67 - .long 0x400b5413 - .long 0x400b7a63 - .long fm_name - -fm_admit: - lea -20(%sp),%sp - movem.l %d1/%a0-%a1,8(%sp) - move.l %a0,(%sp) - move.l %d0,4(%sp) - jsr fm_admit_track - movem.l 8(%sp),%d1/%a0-%a1 - lea 20(%sp),%sp - rts - -fm_main_commit: - lea -12(%sp),%sp - movem.l %d0-%d1/%a0,(%sp) - movea.l %a1,%a0 - adda.l %d0,%a0 - adda.l #PART_OFF,%a0 - move.l %d1,%d0 - cmpi.l #FM_ROW,%d4 - bne.s .mc_other - bsr fm_admit - tst.l %d0 - beq.s .mc_refuse - move.l %d1,%d0 - moveq #1,%d1 - bsr fm_sig_write - moveq #FLEX,%d4 - bra.s .mc_store -.mc_other: - moveq #0,%d1 - bsr fm_sig_write -.mc_store: - movem.l (%sp),%d0-%d1/%a0 - lea 12(%sp),%sp - mvs.b (%a0),%d3 - move.b %d4,(%a0) - add.l %d1,%d0 - jmp (0x40079822).l -.mc_refuse: - movem.l (%sp),%d0-%d1/%a0 - lea 12(%sp),%sp - pea 0x30.w - pea fm_reject_name(%pc) - jsr (0x4005a2b8).l - addq.l #8,%sp - jmp (0x4007989c).l - -fm_src_commit: - pea (0x4005a61c).l - bra.s fm_src_common -fm_src_commit2: - pea (0x4005a856).l -fm_src_common: - move.l (SRC_CURSOR).l,%d1 - lea -12(%sp),%sp - movem.l %d0/%d2/%a0,(%sp) - movea.l %a1,%a0 - adda.l %d0,%a0 - adda.l #PART_OFF,%a0 - move.l %d2,%d0 - cmpi.l #FM_ROW,%d1 - bne.s .sc_other - bsr fm_admit - tst.l %d0 - beq.s .sc_refuse - move.l %d2,%d0 - moveq #1,%d1 - bsr fm_sig_write - moveq #FLEX,%d1 - bra.s .sc_go -.sc_other: - move.l %d1,-(%sp) - moveq #0,%d1 - bsr fm_sig_write - move.l (%sp)+,%d1 -.sc_go: - movem.l (%sp),%d0/%d2/%a0 - lea 12(%sp),%sp - rts -.sc_refuse: - movem.l (%sp),%d0/%d2/%a0 - lea 12(%sp),%sp - moveq #0,%d1 - move.b (%a0),%d1 - move.l %d1,(SRC_CURSOR).l - pea 0x30.w - pea fm_reject_name(%pc) - jsr (0x4005a2b8).l - addq.l #8,%sp - rts - -fm_setup_open: - move.b (%a0),%d3 - move.l %d0,-(%sp) - mvs.b %d3,%d0 - bsr fm_row_type - move.l %d0,%d3 - move.l (%sp)+,%d0 - mvs.b %d3,%d4 - pea (0x400bb704).l - jmp (0x400585e6).l - -fm_setup_edit6: - cmpi.l #FM_ROW,%d2 - bne.s 1f - moveq #FLEX,%d2 -1: move.l %d2,%d0 - lsl.l #3,%d0 - add.l %d2,%d2 - sub.l %d2,%d0 - jmp (0x4003a536).l -fm_setup_draw6: - cmpi.l #FM_ROW,%d6 - bne.s 1f - moveq #FLEX,%d6 -1: move.l %d6,%d7 - lsl.l #3,%d7 - add.l %d6,%d6 - sub.l %d6,%d7 - jmp (0x4003cda0).l - -fm_chooser_open: - mvs.b (%a0),%d0 - bsr fm_row_type - move.l %d0,-(%sp) - pea (0x460e7386).l - jmp (0x40078890).l - -fm_name_a: - bsr.s fm_name_pick - jmp (0x4003d722).l -fm_name_b: - bsr.s fm_name_pick - jmp (0x4004c374).l -fm_name_pick: - bsr fm_row_type - lea fm_src_names(%pc),%a0 - move.l (%a0,%d0.l*4),%d1 - rts - -fm_setup_row: - mvs.b (%a0),%d0 - lea 24(%sp),%sp - bsr.s fm_row_type - jmp (0x4003c986).l -fm_chooser_row: - mvs.b (%a0),%d0 - bsr.s fm_row_type - cmp.l %d0,%d2 - bne.s 1f - jmp (0x400786ce).l -1: jmp (0x400786fc).l -fm_row_type: - lea -20(%sp),%sp - movem.l %d1/%a0-%a1,8(%sp) - move.l %d0,(%sp) - move.l %a0,4(%sp) - jsr fm_type - movem.l 8(%sp),%d1/%a0-%a1 - lea 20(%sp),%sp - rts - -fm_resolve_pb: - mvs.b %d5,%d0 - bsr fm_row_type - cmpi.l #FM_ROW,%d0 - bne.s 1f - move.l %a0,-(%sp) - jsr fm_track_page - addq.l #4,%sp - jmp (0x40031ed6).l -1: lea (PB_TABLE).l,%a0 - jmp (0x40031ece).l - - -fm_validate: - jmp fm_validate_part - .global fm_stock_validate -fm_stock_validate: - lea -96(%sp),%sp - movem.l %d2-%d7/%a2-%fp,(%sp) - jmp (0x40002320).l - - .global fm_tick_hook -fm_tick_hook: - jsr 0x4005213c - jsr 0x4007e940 - jsr fm_ui_tick - jmp 0x40052228 -fm_name: - .asciz "FM SYNTH" -fm_reject_name: - .asciz "FM: INVALID TRACK" - .balign 2 diff --git a/sdk/octabam/modules/synth/manifest.py b/sdk/octabam/modules/synth/manifest.py index 427f2aa71..a8f9745d9 100644 --- a/sdk/octabam/modules/synth/manifest.py +++ b/sdk/octabam/modules/synth/manifest.py @@ -1,19 +1,33 @@ -"""FM SYNTH: dedicated machine chooser, Tim Hastie's FM engine. +"""FM SYNTH: Tim Hastie's FM engine and machine-list chooser (octatrick 2.12). -Modwerk dedicated-machine integration: FLEX + FM/1 stored in Parts, no marker -file or sample-slot mutation. The matching quantizer is bundled privately; -combining with the public quantizer/Analog BD is refused for overlapping registrations. Historical upstream hardware -reports do not qualify this current runtime change. +The engine, the PLAYBACK page and, since 2.10, the FM SYNTH chooser itself +(upstream/synth/machine.s: FLEX + "FM", 1 in unused NEIGHBOR PLAYBACK bytes, +the sample-free source supplier and START callback, the Part validator wrap) +are the author's pinned source; his chooser is adapted from Modwerk's 0.1.1 +chooser (upstream/LICENSE). Since 2.12 a machine change to or from FM SYNTH +resets the track's machine parameters to init (machine.s ml_reset; step and +scene locks kept, as in stock), and the frame builder's metronome-click clear +is no longer hooked. Since 2.11 the rows after PICKUP are the author's +row table and the row count and bounds are detours reading it (SELECT MACHINE +TYPE scrolls); a build with FM Synth alone has one row, FM SYNTH, and the +units' remix.inc sets HAVE_SYDRUM 0, so the engine assembles as 2.10's. +This wrapper adds only what the Modwerk build needs: the page as a Linked unit at its pinned cave, checked against the +author's ratified page bytes; the resolver's jump into it; and the matching +quantizer bundled privately. Combining with the public quantizer or Analog BD +is refused for overlapping registrations. The author's manifest also names the +Analog BD conflict as a native `conflicts` entry; Modwerk declares conflicts in +octamod.module.json (compatibility.conflicts already lists analog-bassdrum), and +a native entry would turn on the browser's whole-catalogue span-contract check, +so this wrapper clears it. """ import dataclasses import pathlib import runpy -from remix.schema import Category, Detour, Linked, Poke, Proof, SymbolRef -from remix.stock_guard import stock_guard -H=bytes.fromhex +from remix.schema import Category, Linked, Poke, Proof _HERE=pathlib.Path(__file__).resolve().parent _REL=lambda path: str((_HERE/path).relative_to(pathlib.Path.cwd())) -_SY=runpy.run_path(str(_HERE/'upstream/synth/manifest.py'))['MODULE'] +_UP=runpy.run_path(str(_HERE/'upstream/synth/manifest.py')) +_SY=_UP['MODULE'] _QZ=runpy.run_path(str(_HERE/'upstream/quantizer/manifest.py'))['MODULE'] # Package labels are unique across the source catalog. These private copies # retain the upstream symbols and fixed mailbox, but never alias public QZ objects. @@ -23,21 +37,24 @@ detours=tuple(dataclasses.replace(d,unit=_QL[d.unit]) for d in _QZ.detours), tables=tuple(dataclasses.replace(t,symbols=tuple((_QL[u],n) for u,n in t.symbols)) for t in _QZ.tables)) -_REG_DETOURS=(Detour(1073951960, stock_guard(1073951960, 6, 'd6c9f1f60272197a17511f496fc19dd21ddd7df5f1f1ec0cab7a16cad60b0609'), 'fmmachine', 'fm_machine_name', 'format machine row 5 as FM SYNTH'),Detour(1073989928, stock_guard(1073989928, 6, '237cd1acd4da7acb61dc7fda7c88e648a5186b36732f433b4dd91966c5a669af'), 'fmmachine', 'fm_src_names', 'point SRC SETUP at six machine names', kind='lea'),Detour(1073993496, stock_guard(1073993496, 6, '53b8d23acf2c3fe0f794c0a5f37475b17c3433bc6a466e0e1861515989cfecda'), 'fmmachine', 'fm_name_a', 'main page name lookup for the signed FM track'),Detour(1074053994, stock_guard(1074053994, 6, '53b8d23acf2c3fe0f794c0a5f37475b17c3433bc6a466e0e1861515989cfecda'), 'fmmachine', 'fm_name_b', 'second main page name lookup for the signed FM track'),Detour(1073990016, stock_guard(1073990016, 6, '237e017ccd95444195ca390a3e6ca6076fbfa1f0931f2e7dfbdbee8c5ec72ca3'), 'fmmachine', 'fm_setup_row', 'SRC SETUP row highlights FM on a signed FLEX track'),Detour(1074235080, stock_guard(1074235080, 4, 'f829e3570d9d9e95b7d7b9b0433b1aad2f13c0ac087873dbf981482521301309'), 'fmmachine', 'fm_chooser_row', 'main chooser row highlights FM on a signed FLEX track'),Detour(1074103772, stock_guard(1074103772, 10, '22fb6e2bea4520003d2fae7035c0cd57bcd6fe6f2cfdd5b70580a3f6f2a68034'), 'fmmachine', 'fm_setup_open', 'SRC SETUP opens on FM SYNTH for the FM track', pad_to=10),Detour(1074078238, stock_guard(1074078238, 10, '8f0fa4f7d6db857f695996b185b38b788fa6af0eacd86d7dd01e2ff61201ffed'), 'fmmachine', 'fm_tick_hook', 'publish the FM Synth SRC SETUP descriptor', pad_to=10),Detour(1074235526, stock_guard(1074235526, 10, 'fe6cb52458c7d968dc467c1cd7721d832baf6bc2fac1826c8c10ed07fee2e7f8'), 'fmmachine', 'fm_chooser_open', "the track's machine list opens on FM SYNTH for the FM track", pad_to=10),Detour(1073946228, stock_guard(1073946228, 8, 'bba32826863751ec568719c202735da1ba8f06560e4d375a80637ae6653caa8f'), 'fmmachine', 'fm_resolve_pb', "resolve the signed track's FM page without changing other FLEX tracks", pad_to=10),Detour(1074239516, stock_guard(1074239516, 6, '89c50ac3ca041fdf5647ec5ee2c3580f9a67dd79f0b5cc15ceadda4b9e9bede0'), 'fmmachine', 'fm_main_commit', 'admit FM Synth at main chooser commit'),Detour(1074112022, stock_guard(1074112022, 6, '7a7350a69423ec41ec468a189ba167059626cb7f499d9862394d2388a23c8b59'), 'fmmachine', 'fm_src_commit', 'admit FM Synth at SRC SETUP commit'),Detour(1074112592, stock_guard(1074112592, 6, '7a7350a69423ec41ec468a189ba167059626cb7f499d9862394d2388a23c8b59'), 'fmmachine', 'fm_src_commit2', "the same at SRC SETUP's second commit path"),Detour(1073980718, stock_guard(1073980718, 8, 'cae34bf50383462ed4f92d8044e3021247edb8bf69c17a9c7ead62f6edf79510'), 'fmmachine', 'fm_setup_edit6', "SETUP editor addresses FM SYNTH as FLEX", pad_to=8),Detour(1073991064, stock_guard(1073991064, 8, 'ed71ef874312890edc87cef7e19cd8c0f8ac4ac9b90182c2364f78d3a4567f45'), 'fmmachine', 'fm_setup_draw6', "SETUP drawer addresses FM SYNTH as FLEX", pad_to=8),Detour(1073750808, stock_guard(1073750808, 8, '3d25d06bf11f3a7bccef894d0016b52adbb4ae468a0f475dd497d7b5966e2694'), 'fmmachine', 'fm_validate', "the Part validator reads an FM track's controls without stock FLEX clamping", pad_to=8),) -_REG_POKES=(Poke(1074238024, stock_guard(1074238024, 4, '8fbd3190028fe564c11b56e3e73e8eaf36925cbabf94164176eeb4c4f57bc6ac'), H('48780006'), 'machine chooser has six rows, FM SYNTH the last'), Poke(1074103802, stock_guard(1074103802, 4, '8fbd3190028fe564c11b56e3e73e8eaf36925cbabf94164176eeb4c4f57bc6ac'), H('48780006'), 'SRC SETUP selector has six rows'), Poke(1073989968, stock_guard(1073989968, 2, '74d01dadcbe6a57ef61bc53a5a65404cd8446c6738ff0ac86b1b1a78d8ae7d41'), H('7205'), 'SRC SETUP name lookup admits FM'), Poke(1074235000, stock_guard(1074235000, 2, '04cd1c6d463ba6752ff68f9125991b1c4af983ae355b62366122ba2d47eee3b2'), H('7005'), 'machine chooser draws FM'), Poke(1074235086, stock_guard(1074235086, 2, '04cd1c6d463ba6752ff68f9125991b1c4af983ae355b62366122ba2d47eee3b2'), H('7005'), 'machine chooser highlights FM'), Poke(1074239748, stock_guard(1074239748, 2, '577d7ba37f9786ab02a7dc9eeb612bb55936018179b550b14f4b9f000a89bf20'), H('7605'), 'machine chooser persists FM')) +# The author's page cave: page.s linked at PAGE_AT and compared with the bytes +# he ratified (1,868 B; fm_descriptor first, pg_resolve at +16, which machine.s +# and the resolver jump below name by address). The resolver's kind-0 table +# load becomes `jmp PAGE_AT+16`, the exact write of the author's emit_page. +_PAGE_SHA256='b43d76c9c13c1441f16e32b85826ac2e7664352947fa4dc393401ff332710f92' +_RESOLVER_JMP=bytes.fromhex('4ef9')+(_UP['PAGE_AT']+_UP['PG_RESOLVE_AT']).to_bytes(4,'big') + MODULE=dataclasses.replace(_SY, category=Category.MACHINES, author='timhastie', author_url='https://github.com/timhastie', doc='Dedicated FM SYNTH machine: two-operator FM, one to four voices, chords, MIDI and glide.', - proof=Proof.UNTESTED, proof_note='0.1.2 software regressions pass; owner-approved experimental release, current hardware and chip timing untested', - requires=(), cf_patches=(), - symbol_refs=_SY.symbol_refs+(SymbolRef(0x400d6458,0x4000f450,"poly","fm_start",note="Signed FM starts without sample resources; ordinary FLEX keeps stock start"),), + proof=Proof.UNTESTED, proof_note='Octatrick 2.12 engine and machine list; emulator-qualified, hardware report for this build pending', + requires=(), conflicts=(), cf_patches=(), linked=_SY.linked+_QZ.linked+( - Linked('fmpage',_REL('upstream/synth/page.s'),cpu='5475',cave_addr=0x400d24d0), - Linked('fmmachine',_REL('machine.s'),cpu='5475',dram=True), - Linked('fmregistration',_REL('registration.s'),cpu='5475',dram=True)), - detours=tuple(d for d in _SY.detours if d.site not in (0x40079816,0x4005a848))+_QZ.detours+_REG_DETOURS+( - Detour(0x4000d514,stock_guard(0x4000d514,8,'39173055fb04ac0a6c355f89561b48834bc5b31ad67609e3fe53efbff7de1866'),'poly','fm_source_dispatch',kind='jmp',pad_to=8), - Detour(0x40031ece,stock_guard(0x40031ece,6,'5aed68f7498659f87e414631493741e91f3442c5319f46e6893b567c423ce24f'),'fmpage','pg_resolve',kind='jmp'),), - pokes=_SY.pokes+_QZ.pokes+_REG_POKES, + Linked('fmpage',_REL('upstream/synth/page.s'),cpu='5475',cave_addr=_UP['PAGE_AT'], + reference=(_UP['PAGE_AT'],_PAGE_SHA256)),), + detours=_SY.detours+_QZ.detours, + pokes=_SY.pokes+_QZ.pokes+( + Poke(_UP['RESOLVER_HOOK'],_UP['RESOLVER_STOCK'],_RESOLVER_JMP, + 'page resolver: the kind-0 PLAYBACK table load jumps to the page cave (pg_resolve, PAGE_AT + 16)'),), tables=_SY.tables+_QZ.tables, - pressure_blocker='ColdFire sample generation and maximum-load timing remain unmeasured.') + pressure_blocker='The DSP-side transport of the CF-generated source is not priced by the FX pricer.') diff --git a/sdk/octabam/modules/synth/media/LICENSE.md b/sdk/octabam/modules/synth/media/LICENSE.md index b433ab724..7fb059378 100644 --- a/sdk/octabam/modules/synth/media/LICENSE.md +++ b/sdk/octabam/modules/synth/media/LICENSE.md @@ -1,7 +1,9 @@ # FM Synth media declaration -The seven `ot-*.png` files are original headless-emulator LCD exports made for -Modwerk at the owner's request on 6 October 2026. Credit: Modwerk contributors. +The eight `ot-*.png` files are original headless-emulator LCD exports of the +FM Synth 0.1.3 native image, made on 8 October 2026 with Modwerk's capture +tool and capture plan (six are byte-identical to the 0.1.2 captures of 6-8 +October, made for Modwerk at the owner's request). Credit: Modwerk contributors. Their provenance, capture plan and hashes are in [capture.json](capture.json). Only the rendered LCD images and sanitized metadata are submitted; firmware, stock-containing runtime binaries, raw LCD/RAM dumps, cards, source project diff --git a/sdk/octabam/modules/synth/media/capture.json b/sdk/octabam/modules/synth/media/capture.json index be7047e55..91d6ce9ed 100644 --- a/sdk/octabam/modules/synth/media/capture.json +++ b/sdk/octabam/modules/synth/media/capture.json @@ -1,7 +1,7 @@ { "firmware": "1.40C", - "imageSha256": "d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157", - "emulatorSha256": "f521565f3866947788d7c45507576a4ed386d2e4ef57e9683ab65fa0acc6a166", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "emulatorSha256": "9dc31d1249793fcf857fa6b1c1ac72ab7966646fd59f3e09e90683c8eb41e711", "setup": "Headless ot_emu; MKII panel; stopped transport; 128×64 LCD at integer scale 6.", "palette": { "on": [ @@ -15,7 +15,7 @@ 24 ] }, - "keyMs": 150, + "keyMs": 50, "plan": [ { "wait": 3000 @@ -33,12 +33,18 @@ "NO" ] }, + { + "wait": 500 + }, { "press": [ "FUNC", "SRC" ] }, + { + "wait": 300 + }, { "press": [ "DOWN" @@ -64,11 +70,17 @@ "DOWN" ] }, + { + "wait": 300 + }, { "press": [ "YES" ] }, + { + "wait": 300 + }, { "capture": "ot-location.png" }, @@ -77,6 +89,9 @@ "SRC" ] }, + { + "wait": 300 + }, { "capture": "ot-controls.png" }, @@ -85,15 +100,68 @@ "LFO" ] }, + { + "wait": 300 + }, { "capture": "ot-voices.png" }, + { + "press": [ + "FUNC", + "LFO" + ] + }, + { + "wait": 300 + }, + { + "encoder": { + "name": "A", + "delta": -100 + } + }, + { + "wait": 300 + }, + { + "encoder": { + "name": "A", + "delta": -100 + } + }, + { + "wait": 300 + }, + { + "encoder": { + "name": "A", + "delta": 4 + } + }, + { + "wait": 300 + }, + { + "capture": "ot-lfo-setup.png" + }, + { + "press": [ + "NO" + ] + }, + { + "wait": 300 + }, { "press": [ "FUNC", "AMP" ] }, + { + "wait": 300 + }, { "capture": "ot-legato.png" }, @@ -102,6 +170,9 @@ "NO" ] }, + { + "wait": 300 + }, { "press": [ "MENU" @@ -152,12 +223,18 @@ "DOWN" ] }, + { + "wait": 300 + }, { "encoder": { "name": "LEVEL", "delta": 1 } }, + { + "wait": 300 + }, { "capture": "ot-scale.png" }, @@ -166,6 +243,9 @@ "DOWN" ] }, + { + "wait": 300 + }, { "capture": "ot-root.png" }, @@ -174,23 +254,83 @@ "DOWN" ] }, + { + "wait": 300 + }, { "capture": "ot-glide.png" + }, + { + "press": [ + "NO" + ] + }, + { + "wait": 300 + }, + { + "press": [ + "NO" + ] + }, + { + "wait": 300 + }, + { + "press": [ + "NO" + ] + }, + { + "wait": 300 + }, + { + "press": [ + "NO" + ] + }, + { + "wait": 300 + }, + { + "press": [ + "T1" + ] + }, + { + "press": [ + "T1" + ] + }, + { + "wait": 500 + }, + { + "press": [ + "LEFT" + ] + }, + { + "wait": 300 + }, + { + "capture": "ot-machine-list.png" } ], "screenshots": { "ot-location.png": "4e1d84898790433813720c437d2e767cb3225c7770ea15be9115f76e70b82270", "ot-controls.png": "2eb52e9b732308891e289aa23d92556a87dd8820b12a37cbe73f66e720a162f0", - "ot-voices.png": "0e99bbc3cdecc61bb66920b5648f517198b191ad3dc04a78af14e2256e1cdf3c", + "ot-voices.png": "603b3e5d31446bf31570b02dc56b6efccb958b371a06b87c3dc79a6a6090d1e5", + "ot-lfo-setup.png": "ab3071680706882acdf5f9ccdefb1adcc24c50690afd90560ca42e25535e3e94", "ot-legato.png": "e5cd86583126a834f8a9351a8f2b51bca9f76c101023e37db4c933be957d5bc8", "ot-scale.png": "71ffe8fe7413a1e2eb0605ad7119159bba95db7f7734dc47ae13468f68a74d13", - "ot-root.png": "53f0f6dacf09665f41629428569d9fbfac5847698c20f35a6cbe6b78a88f99e4", - "ot-glide.png": "77b4cf73fd16e96d1c78f5a9cdd0590b20a7958c614d1c6cdb93b77791dc505b" + "ot-glide.png": "77b4cf73fd16e96d1c78f5a9cdd0590b20a7958c614d1c6cdb93b77791dc505b", + "ot-machine-list.png": "47305d8bdb86bc29f838f4e3ba712af7b0a6e6d3c063e987adc9a59f82daa8ba" }, "schemaVersion": 1, "moduleId": "synth", - "moduleVersion": "0.1.2-experimental", - "captured": "2026-10-08", + "moduleVersion": "0.1.4-experimental", + "captured": "2026-10-11", "imageProfile": { "modules": [ "SYNTH MACHINE" @@ -198,6 +338,6 @@ "keepStockFx2": true, "staticStock": true }, - "releaseProvenance": "Fresh 8 October captures from the native Synth 0.1.2 executable. Promotion must reproduce the exact emitted MAIN image fingerprint; existing software captures confer no hardware verification.", - "sourceSha256": "8fa263bfcd30dca9aff4f83dbcb001d4ccf06286e31839c8e0afd61100a9315c" + "releaseProvenance": "Fresh 11 October captures from the native Synth 0.1.4 image on the author's octatrick 2.12 (MAIN OS 00fa6caa..., Modwerk's export-composition-proofs.py, author source ca13ed8, tag v2.12). Seven screenshots are byte-identical to the capture of the same plan on the 0.1.4 build of the 2.11 line (MAIN OS 248d987e..., author source 5f3d745). ot-voices.png differs by design: choosing FM SYNTH now writes its init, VOIC 1 into LFO 3's SPD3 byte, so the VOIC dial is drawn at its first position (the 2.11 build left the stock byte, which also reads VOIC 1). The plan is 0.1.2's with waits, plus LFO SETUP and SELECT MACHINE TYPE; ot-root.png was captured but is not committed (eight media per module). No hardware verification is conferred.", + "sourceSha256": "4fc06072b4039db83cba1600e949d00770c9753d974bbf76a5809f8e1be9e851" } diff --git a/sdk/octabam/modules/synth/media/ot-lfo-setup.png b/sdk/octabam/modules/synth/media/ot-lfo-setup.png new file mode 100644 index 0000000000000000000000000000000000000000..1511a94dbff9f5e98cd5b35cdaaaa4d7a972233d GIT binary patch literal 4922 zcmbVQ30PBC7QQcG70|Y*h#Tn`R0bp>APtDb?IXdBwpOIF)T&Uh)=E@FwpSI~4pw4N z5ebmCYEc9%w2mNwug*9Ig-BUsK+smhBAYFn!#?NUmjtvrhW&LN4a<(r@G)KWHgC>g z;MYl#kh#>+eaEBHmxN!+sNPWB{5Hyu9X7wa>%v<;sp=x{>&}bs@sKp7d=2+$95SNS z@}zo;P7~@iRI-eh&*LH%hu>oTG)_-|xP}UKkpu}Rs2sS8tJz#6MM4(Gn0}G)1`@G2 zRC(2b%d#ILx9qV`D`>9$eRGxj9iN1s!ryd8A_NNc1S}3HF`|*u*akK-ra{I#J}(S- zppH$`Dn3Eg?(65#73;oCcki~7)^A__CuTXocr{^>&q%w)mCBg-oFe-)15V(fBu8_y zBWwyL6IANMlMy8$@ZNZV5N#q?vEOIO3!0M^hJi!h6y;o9or*3-2E7^rY^|`sSYF$F zJRm>W&F4|OMb?ujv->waE|?2;2OcsUGd@3=!jrLWDVE@)$MY!39PBlu?nwmPGbtEi zaY~Z=C)2KzI3nqu?iG)GV7^F-qb0(g<%+NkTnGk|az!qb)8G;*H^ho=O|yJ%4D?F5 z;CP{w>yNw}c%ylNE8g+%^b#))?j2h8Pz3{VTU1SUu%n1P#r~SODEu5hB@b_8vZCo@ z72d|qG&zrNLSLlRnTMna)&vDW!0Qm%fN%o=M%~cG{Xg$T0~17hi)(z6RyTMh7U`F_ zfBucCrn|cKou`3is(Brz>r=!HTV0=2x}s{ua!^J%g1u=(gSFcn*smc#L`j%Ox&abL z>@y62{N!uV8TZLSytP#M`5Usn$mr_$c>c~KZh{99t6oh5)`Mqie>y1hN6XhlmZe@c zN4-{?^HyfM-Tmuw^C%oe0`iYoUaAQP9;1fe3AViU+kJgOokI#FOhHbA)SR8K-_6SW zAo^Dkt_Aig-Z-3cx$|Il>-sD81*qp?+v6cljq$Z1{+*b& z42jRisnppz4k5v*@$NJ86)bt}s((gMqEvt)EjGAa@})OE`#5te2>Aa>9$! zEMg1nj*g^v?)a_5szhDi^56irO1nF@jjSN10?h#%Aq1Wfe2@%HTmaaraCk`)Ve(;>{&?e2A>0-WZva!L?fJwNSO- zbshpw4~S9Ys6&;-=ocTjM60Du9a-YCMPYRu)%>t4Qd)htze0(&)?_-5+;1KP4`{2+5Hjbrs z5v==POMnyZt99o>$}&f@W$rD(u{@s>mI0ATr2T;E?D=D^kl5Zb9aqqTj7W|8BSAKp z$t@D<3V2h`A9v{Z&V&x&k5r&N{n_n94XH7^s0@3+=}BTv>Kqf{F#<6rFvW(MaGTud z3GCOFgmpRZZ$2%X0nl(tkT_@r^)Hy97>?oRn79x$();j3%9}+86`$g5HUQ4!`J|{k zBed1Zz&N}LXNdh;%)#tns{ESg&N{32Qlryy<~AiIC^vc(VH7k#ySp$!9`uu5`Le?DB%Cy{U2MGfWruBXFi<$$YD028_dM= zBL}<$gU9l0F(v4?J3VVpU3ZkT188`1;cTs9sM?lf5_1TErxEL|aWX-cDht1R&XKv` z$T~y0Dyb#oBm&PsKr#m0^~aq#a65qW1rmly@@*M6a*0=_LP!~JLO~n7>1};(f$3<* zN)^BFVdU}8CDh<6mPYt8tzOg zHkkC{ddR4M7A}(^M)b%`LFx)UI20W?)p)EX@k(L$>yLN*#&1uXV)TDOEAMo<Z*91%VcLwYAukhv|>>7aC9z*u+StBA6I?(;RKgv`(xPb-3S zn0-qFNYD3mgy>~E#MkbC1k66t}H?HpMKQ!s{!fw*?)&?u#eI~eZy zhK21!0mC7 zRe9Wwc2#6SZ@3GoQka-GwEhFzb5Coa69dMpXK~_d@4)n2HMR^06y#w-yHZsV0(N;^ zuz}nJ4YWS38(5O$5qf{wVq~2zL5FQXBmR+N)@hai!_d=Q#}8>_W=KumpkNQBjE6pI zYdmNJ>-WO3>2aWYP~Xur(M_{_8tw+)cWACn)T0;bYswr!^)9|DKZHmF((9AaBCU~( z(_e4}++J?&cKPby?AtqHW(hn76%|cG4-MI<>&ydv?}l9zKsILpGKA7HUPn7TsCL%8 z%HUA1*^dbYFKYzT+`^`4)LQr}IJU4U3$^;)=fwz9qMIy!-q)PWV`p?8hEzFd42a{k zF(&KA+x2332_ZO%>QdCP-*KV-Tv$00qw0TB6Q4P-%C*X0;WlEiVkCN;5jtPLilDWD z?EleC$CU22%FD981*=iPZ}z7$SkhNwM3cx4!r|3V zyQb_7R`q}9s;RfeG;=OzuD}V*PjUrv>cWdTOgP>MVJLz4iw)#4Xn6L5(J7sq`p+L1 z_ddM7xxC%)`+lnjSs8aqPVU0v&)sRNm}YF;9oNRPN2`vU zsB*j3Y8n>N#Ps<~QD8ke9B*q(F@WkW4@G6iOlkaFo?`O1HDDhci?L5~7up4hp)Hc4 z;X4gV2=q-b8dA(|Jv=?YrrNBTq*2?haitkAV)TR~Iv3s*((rJ0`y zmW(J6PZ|S6z|_=HH`8g*AwV3!3Sq4fMM_o|5Gk_Gz3(l%EGsF0+`aGJd(Ziu-}#+$ zAG^Xr1AF%x)C&N52h9nH0FdL9;T1CcNh$f<2cSHqu*~76XGBmgI*<^T`1KAvK zQ!yiWppT(`46EXC>JN;9yiSh*{O#|>HP7{X)l`t?Uzu7Q+*EYYvhw#7uh}NUYMn<# z*{7zDcT9RT3eUOE6s^@%xe)7C;|`Aq?b^{{#0_pypqGVe>rN&24YN)+Q~%TU8yDVc z*_YdqTb!TMW~iTat7E{0O-n3SGQYEKWs}Fh;&9f%Ec&6*eTsf)H0aSE+*Ioqxf+L7 zUAQ0WE?i7a^|)HG#COH(SqaDd=JpfuP-J+59Hb_sgXoS`;)|hn*lkV@udP^j%s7yn zSX9ziFqz}4&5g#7jGLDX<4^uu(-i@AAQ~d}Xa-2ahy%h3@qG@#kR`*@iSn4I`7ATx zw-4&_GNW}KEl=Cq%5)E#8k;&^DHz-X%FRP;h@H)S9(&x6V1PL=ILMh;@Eu#8##CN7 z4&NJL_g_<=v1!A2e&uVU;bHvNKtKF|&psO|>HO!Y1 zYXc_EBw*AAHE5gR2CL4fLj0kM4Lj;}D_ZXnIV5|{o(vCBbxY4aJ!l@I5DragjkcY( zJF27^5tlmE=G0kCFf59XjICbHhLxVJYc6pU^4ccTgZqZ_><*9w{lZM;jOMe(rX->s z5Q;QV01nD1FM`8o%8NX8dwp>zN%zG~1I35E=v;P9TA`4nZ3rwG{Gj_>A&yzss?Vu7vpMPVo)N%A&QraJZ_{?u zx#JUMKFi|O@}95mt3EzZk) z%=*mc=}9|TY6}fh!mQl|JrNS^)?##ET>x*x;FFsYnysv|L zay=k2Laf-c>`apmK-Q|gIJ0-)%bgjpZ-KPy*<}aUTGqgzo zB(LJoLnBq;*`D=X{CC9Y#nT$~IiwbmETyCu)(KE?x#1M-u1?K%39H;6H#`WKRj>1w zUZ{Pm^sMxBv$ts|uYPrtjs$C*;R+QfAT0(Va`tRK%Z+fXW9N`<`ZZ3;P8;h z?K9KX;? zokEK&{M63(GB3uKO^N^NZmceYVZ;6jeX!!87&tjiwL~BBNt6}|P^6DGfI<}EyLy^hdvYmxH>PNk zF3S}{q9Gg|?zMI_orL6?C&NmZeI1-^OxhDTGW_F^JEi_z;_B`VMTCslLc?H%VCxlu z)}Q+eg4l7)2eyjf+9!UM?Yb;&Qm<#~oASfjma(_z+)inmr%7U*)H-R%QUYYQ<=Y_% z2?rv}@+{d|p*M@qJ@TB&Lt||&{UUZOw9YRQyUfy3Ilt!J(b3@py_>IMt5F-27*hB0 zw3A6EolP!b^?^0#j)(WXUq5)%vJkT#zW<0ha5l}I(jwg`k^t~Jabz8sbsE0lJ8Z$J z-dvsbGj(?=QJ|C|EcWtWp>a_|jxTUUTW*|ul&S9d%MU*)YUi9@dg@wSP6Q`+2A@_0 zx#qg#YAId+?P_`28x_T0eCx+=x};&)6DaS;dcqB@6^VqN?FvTbj_;GW%hhSw4pPPA z_LV1Dvrm!mSMS*OU`2ZF)n6GA>Hi)ftP@f1?k&76iwTX<#=oQmX1SX%)zQ9`8tFeU zqOC40M4{Gji?zdw7Z8ZLpb-wkOLd7=OS literal 0 HcmV?d00001 diff --git a/sdk/octabam/modules/synth/media/ot-root.png b/sdk/octabam/modules/synth/media/ot-root.png deleted file mode 100644 index 0dcb0d96c2f9e90c04c8a2cc4f6ca4ed5e3b47ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3767 zcmb_fdr(x@89(=Of#nq!k}8OgOI#yrTLZ0BkjP?0KoN;RRaDr_I)E=k26+U;%R}2l zLDJ$R)!5e<*5dl|L}y_4X%#nmsSWuV>l%XjTe z-9Gv9{+l41?XnuiJM`%2_KN8JOLvuip|Q6e>RNkm=bmThdfWb)?$VrKxkTnAOG`xd z8`KK*5R=g_&#+vogZd)M$n-!IEfPz& zTC?2rCA?Q*p+1X-E??lT=qWgx+Lz@y^gK1RW|9DXT z{LXS2jp*vvddc?~#H0@z8dzaSVlv3W%8c0y-|5k!4IBS(d-o!YfkPHNXkA0ff4g5A zG(?T3PgqD1meU=IT+EOx1huc=5P_#Nyog1oC}e^`BO2V+845RX-Z?638Zq_F<5Kro zB!pc#6@?GPSU;1FXzV_`BC!;d8nNIhz@tTES&YEii>DR|&%D--e_=MVr>EcRqbr)c zSnqx3Ywq^?rpLGIV{wp@U}dn;1KCWxf#MW?j+oF2=>D*v3%eBGMjyGoV=Gl=!v!zN ztXv*Lu=s=r0v7Ei zS1eE#^19*F_Dkl{QJXA>Z`K5@8ccYPk+7s!Z| z*=DJ+RO6tcrgCVdBt)Kj;$-&j9c6PXbzC>iHt!S(ChX}}5!)ojDM`Z?(mccZ)&-wd z)&9`vR-#$!&?e6cIuOj%x1P*BQJ&rMDnG&aReZ%SaxRQd)j-W+y0bV30?Vh&em{!A z3H;k4MB~(1az}->wFWy> z))6D=@{CH$rfx=j9O_{sVH!}BB9J~ufC)S=IDsOLPiBpSBh@|ACwU<9-{f>D%B~}K zch6H%Ze{_cGOg$pN?Fe=7< z=l5=cqrh8LybfsVmaNdi2Li%O(AOb0WabvFTL&{H;xU`bm&VGP3%)J#=UK@gBkC^b zE?S_B!ySj1Tm7iQ1oI5C{(7}gp|hia9?jWc9mgG`z?w1zeWtNgk+iuMHh~Dj!^d$p zI7xbi_UoW$X*4uRthN5y^L_4VbaC7>A8-Ou-4iHK1m9nB=kW?4!_B*w1%83xL*o@1 zh;jlRl)*tzzkk+W0TwY`i}`^Vo;Zxq5UBYzL8DGcI#ShHIjueZnT4pdaye^^UKq2L z5R3^!+UgeSJaiXS`+AHQ5eMeDLPV(P`G#8{L?mYp_06avdMFIHN-zEA=!md-zEvVc zr~4-%6nX=&PJrqTJxsdCRO=Jk1ig(Pfp!Qj{QUtG2tnT_1$~;{MSbpH8UClUDH6fQ N)6IKJ>1J8d{{a&3z3%`3 diff --git a/sdk/octabam/modules/synth/media/ot-voices.png b/sdk/octabam/modules/synth/media/ot-voices.png index 0a17736cdba0a22413da0d724c1e8258b03aead0..9d19c7175fc544006e84186bed5eddc7551d3626 100644 GIT binary patch literal 5073 zcmbtY2~<;88on?x!hLu7*Y98M z`zS)T&}-<}p$H)_Y54qC5psbcqjcxMzrB~$FCa8BS2}-gJd(RhrKj)_h1||fHuSEf^?o(yC9f@*gII3=Jh&!p%xJr;9c5zm9o#Lx$ zz!tXpiLV*!2DXY*7(-a0U+krtAWO;SZ%V2ng^Jsn<{nYdbN830$j%1c%?Pk$#_HvM z2n)+T0^q;_8YXU=a6N@yR?y?Lxy)c097zSJr%m%oZh9lib}GbG+iacjtYq(Nuk^$}6#me&MkcQ6 z$k$n}<`nrztOXtpWuBzso)StO`!JWGXv1Ffq2YQZ03A)QEeNn#3lz19 z=W}QE8}GGQXRLK71RHgKA4@!CNG3Gad{6O$Aq+OmfJt~#g#$DlZ#qrsP>B^qrXG4l z?_TllJMB%Wl^j);v2J;bnC6Ou6P3}I*;p_niykwEN{1=yT`|ycuEY3mCrYxAVFfXF z)C|!sLL2R4AAPqa)2;u&(#2zycl+{dS}GH~n^G@KytuMJbzrxbB!YwkiYGd~GlIH` zW~Dt8JQNjLF4dFb#dpAzs$u%J2~t}}!HHauK1U(zsSgAyJP@9xiYoydqo$-mFC+y% z)jLP-#3>dSQyXl_!)DpDJyAFbRw5ZmqcmRtRez?Zi9?Omx`v zkQU~|E@rNnIA=G7A#tsY**M+h`kqqizDsq$Jbra^kI@9 znIEd@g@Ug5O4pcAWjo#QiyVuy#1G)nvZ%OFqfbCnKLZug1E`?Ac@xG1BS?j?lEg)- zJL8q{a&cNMGPxsuqX^-4BIzmz&*PjbdJ`1E9Re~e^8mL8<8u7zDj=VQcal0St&2fN zxvEUG6qzQ@dr-RN^&dsHBF&j(OV|fPC9tczbW1Reo$;8tvm^t3-k`c70C|Dicn0<$ z0ZGt?XS92(3d7#rZz^)-FDwN`1funfVOanOAeF5GNrys3z<|d`KuQc@H68;L$f~MsyQ;Q%Qs;(TTpQws0}oD_*!{#oWWP2BnalIJk_y|1!wlfM z>qYhb$K?$4M_e$l6va=qt>UOQ9}<9>G0slEiiN>)Vqt(a5ysg#+1AxSF9y~(drCs{ zaH9sfTkN9BKAe6LmqpxBnGU`P?U^wOV-B})IvvjM>OH-s9erWmUt5zKWThqI;=W1W zSeqM5s;*vuTeRDJ_j;Ev&R(kv$7i^vvozeB@4ThsQt%(*@lqnhJPbC%7$YLY33*27RQiUfFog`po2EFys_K|>b@f#Qzh7z$qVSJ z4)uMzJ*Z#~y6yUBv{i!+K$BsPw)GGtAd5R%Ca8QLJnaS2&J>oT8usK3(UzTF_0dN) z@*(`M8NL)nTB5O?1tb)x1Q?GL7C2m}Tb+Z35Tu#CK#pG6SlHVl^WE<%5qj#+C%2Xq zR9N&g@VW4TVu(vYmeE-#K@J}s2tgeSzfXj>1?`#nS->I=Kle+^uFA=u|DE$t*qq#T!zO4{hd1>^n9%>x*DeN5bLb%C*i#DVuN* zr-j;OX`Q=X8X|UyXidJ+a}{n-NbMl-TO%Lco$Ew7fwRQ!LHtB@0<@WB(zIg5jt%WT zUHdEChz`O87G2T-gMn(lF^$tFhRYQJS`oT{qb+gIee00XcoxwZhMMzGNGy-{=~5UR zIp8qbXOV2>z#j1|bf02o&%;YcLT>rOy(YEuMqTyFQ1m^90l1si-Cx_~SLR9+c22+u z!CTUBGljOhO4QpK)3^8X{Pk@$vhB=d3*VBxu*sO2usL+rT^=-phG%GKfxS8WYTQagI|CPrxox=7 zAdMKN29+HLv}$}j!0cMoI0!dmiD>eeSvNH`b7%H^nBR5dgw8MHwoLyL(Ak+qrNmAg z7`^L>^BQ3!GLUx6NfOS~xOtG3TdVGWF;C^cRnW<%Ol`tDJX` z>f%br-9+ti0bZz4kpS)~)Gu5~5?oVY8WKZ)arQL3=lSj#;x}94_U*~sbbX{ds1oo> zXdtOr;oa+ZlLVHevX#*)NAGyh;_-8eQc+FeKlaC35!`kD)XLA%m=oK}R=PtacUNIC zNtRR-=~#V21EF66y-_&1aIzXh3*@0ppY976N%(Lgdh_=NQ1R1N^0A=Xtp5@9;*_hA8g!v$^tf$IGhj zy8+l8-&FqRU^-7?J03NH`)JxDo&Uzx!d7eZR9mJS4wxrt5QT^yj>0k^)a^bI9Fcc5 zdT7h6dld(XH?Xw^52zLJmo17JY(jV#v{B@i&f3dx zB?eKji%BaL=&FdJZpf{!LxW1NLRC;hTf;>MM1v9-m+YMH`xEF>ZMSDW|AT-2?|kPw zm-oEq{QrmHVawb`O&&!Eag&BFeTfhu+;WPM0{AcMqG}c)6ZT1$hOF7y*l{x?{*}`4 zOGD-@>5H0v@VkO#Q^O@YJ_)E@KYibfNKsS9C96hrB1k2U-56`^5lU2I7X=AbiMg>v zsuB}{NUCy@5V2H6+GZ(I#hUr)()eveY4VMkFEzLkRq|QynbjqnL{*zzCH7xLLM@)U zY;o&jql%BFQ!);?%v+2U6p4IQ-X4O`|elF1Mg9p zQq7H2kdX?iP5zh&9uumu0nCM!P*fQ$!^dm`wc&L^TmAcfZj$n}^?#Si$mx5Y7b;_B zYmP1+F;K6rGZfX3<$W3D5gI49@BjF#Rg-^jjPq4=dsljb6Wa&JLiv21iSJPzd~GKt!WJJn$`*O z_vQ#zbiMQM=G`829bBY2u#nvB3o7?GJ9SKB;@8v8?w$#oG(em~b&Q3luX5xI9E+ci zd`gi>-E-s>W62O3y1Z|1vv8b3b@ha=(@ry`MD(0922iF1zn3ViVhI#Yc&O*&}?sQDdZ&7#h+R-mO!B*+d0lE z$sX_q!yyP1Io+7;XUH41Tb?GT2kI8tJj(zH@s@zt(x55Oie?ah4%kZ|I;ROdKuk_V zgrov}2}|K5ZZ;J;K?L)@0-20=yapf(-M_lFp^GfMb!>L4#h9QLhSzM4ZE7&cyAO5Q zzII{fp3h!UjWRS0GtC^%BG+C!@Jw$Lf-6 z<>pM=v@i&4ZBT5=ozzNqbQvoj0~d6kftqH;y&Ru25;rYzN9g;+tlNj@CATd;!2c{I ziWaV|sv1s!1`pl0`_Kehe~Gizsqll-M}tnwnr8>v+x>(}kRzsoVx77?hFId{e*m7M zdpP|#A_1#a>JAipk|;K|5~NN2>e2K$?#p+EAX0i=HWy%1h5YLOJFv$aX_)p>v40Ym zKVCA(1#-$X*KZ?k|Kmi0MH=vSYfGBzL0#Qw3Ie)Sy4tu*YnB))zW7=b*3ThJhIoK8So_`v3h zvsZg2M+spC#G`J-CI7h)7!aik@A<(Z1OmO~h0ri!;h%F#I1A+O`H}k3+QkPmN!ud# zO_LR!DbqAhNDDO#c!{%TaLQ_g*Q(FNWI+z%uV6o#g7$$rJPRYn4^aT|X)Am8Zdd`J zQFHob5kXt%#3Un@Z}!7P2KBl})fwT6@MX{|cJ!1du#cg?=$!6=?504pBcAXgoQE?bR^YzQhnf%Qp*Im8e z_USrPv~!{|o+0^G!HY?IB6fuJrL67j{{hoDr1-zNw@-+y*6=Tkldl=TS*$jd0EzLdERh z=-A2=#&nsxV%Iw(C9gj9RSTwl-CzY85{hjJISViSp6?&=BNP~kkuR_t$h2^?I*z@( z`BO`taO@4D6qC<;=`U!k^Rr)%vmlFWuNHUTQ&QW?{x46LP8ni zJLV2Blb=9_-}%H`Eunl1rG4gIb!J-L$4$`fhI%5_m|egza}4i#K#d>(p$~if2w*V0 zW_6Nz?{gypgyGG#r>zA-(f?ope*oa+nz)99H^*jnhjsK{G0gj6UYZ+JfD3j6Hjt@? z_4kK9MxMgqODiRx_fK|F9DU-ovsp1RvjFpi4qR_2C0JHSQU;?lCJoD+*H3fD%8xc> z0A+_$c;mby7VSY$&{Do5;HjuyvStD+Wq!;X)@soxeiB1l+hvJn%Dgyf)nYMg*Aa1n z2Oyy^@p9bpvTsCF6k=SAqYg^3j~+r)1D?*j%ZdXO#W@}()KTaIkgXTDsY9>6jPmGyt??t{cR(WO&q2WYQG;&uNWe`T`Y=8H4K$F z6hQ|B$5aa+6qH#t5?^DfI&;VI+!ccs5W#hRh@`6dY5k#(p_c4vpgL~#3OQWqW+?RoVR6`8H0lYAGojR9`w%m>jL)$ zww<}^0a7w!TipG(Il{jdF$pr>vXfq)#KnChBfz2}2 zhmNSW`x*T3x$;v9Epsz%DUtn}Ff{W^OKg#SED~!b*`4ilNx3wXS_kvDlO$f*TYQav&Jc+F z_3#mc_Yyu_cw-m#idOw)3XDBjKXVh9g)-vmh^MPdR=a5X(_Gi8#5F=?np@<*QLaLV8Zr!BcP!J4{lIPv^E znZu-kGAKrGmXmPv`xPD%YlS(qzpu(y*TQ@}9@I$rgCDR8Xehe8iYta(lcd$VP}qx*Kt5tUHG zynTGn%D2c;tlRw`$TX_`$p!ZZJ^KqXgKhr|OJaoe)5i8N1H{ylVmFCaBpv?&fRBDE mbAnH3^gxW+(8n{)pkR_)u1|ZYKApfn>GNSrk3aX?AO8agT2ABu diff --git a/sdk/octabam/modules/synth/octamod.module.json b/sdk/octabam/modules/synth/octamod.module.json index e07fad72b..9bf82085f 100644 --- a/sdk/octabam/modules/synth/octamod.module.json +++ b/sdk/octabam/modules/synth/octamod.module.json @@ -3,39 +3,39 @@ "id": "synth", "key": "SYNTH MACHINE", "name": "FM Synth", - "version": "0.1.2-experimental", + "version": "0.1.4-experimental", "category": "machines", "source": { - "repository": "https://github.com/sambanks/octabam", - "revision": "949f3be15eae5d3d16a7682b9e3218d42f6c1284", - "path": "modules/synth" + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "path": "synth" }, "author": { "github": "timhastie", "name": "Tim Hastie", "credits": [ - "Tim Hastie (@timhastie): octatrick v2.9 two-operator FM engine, voices, chords, MIDI, legato and quantizer, MIT.", + "Tim Hastie (@timhastie): octatrick 2.12 two-operator FM engine, voices, chords, MIDI, legato, the FM SYNTH chooser (adapted from Modwerk's) with the shared machine-list row table and the machine-change init, and the quantizer, MIT.", "Sam Banks (@sambanks): octabam wrapper and native platform, MIT.", - "Modwerk contributors: dedicated FM SYNTH chooser, Part signature, sample-free source transport, validation and documentation; MIT." + "Modwerk contributors: the original dedicated FM SYNTH chooser, Part signature, sample-free source transport and validation (0.1.1), the quantizer drawer fix (0.1.2), documentation; MIT." ] }, "nativeManifest": "manifest.py", "presentation": { "label": "Two-operator FM", "family": "Synth machines", - "summary": "Two-operator FM with a dedicated track machine, one to four voices, chords and glide.", - "overview": "FM SYNTH provides one to four paraphonic FM voices through the stock AMP and effects. Version 0.1.2 preserves the MIDI chromatic drawer stack and adds 18.06 dB source headroom. Software regression and native/browser composition checks pass. The owner approved release without current hardware validation; chip timing and complete memory bounds remain unknown. The original static symptom remains unconfirmed on hardware.", + "summary": "Two-operator FM with a dedicated track machine, one to four voices, chords and glide; octatrick 2.12.", + "overview": "FM SYNTH provides one to four paraphonic FM voices through the stock AMP and effects. Version 0.1.4 carries Tim Hastie's octatrick 2.12 line: his own FM SYNTH chooser (adapted from Modwerk's, same Part signature) with the machine list he shares with his SY DRUM machine (a row table; SELECT MACHINE TYPE scrolls), a machine change that resets the track's machine parameters to init (locks kept, as in stock), held notes that no longer stop at the metronome click, DEC HOLD at 127, click-free note starts, FM names in the LFO destination list, the held key as gate and the fixes from his MKI test of the first 2.10 build. With FM Synth alone the list keeps its six rows. The 0.1.2 MIDI drawer fix is kept; the engine leaves 6 dB of headroom, the author's choice in place of 0.1.2's 18 dB attenuation. Worst-case ColdFire cost and exact memory are emulator-measured; the hardware report for this exact build is pending.", "highlights": [ - "Dedicated machine chooser entry; no sample required.", - "Pitch, ratio, index, fine tuning, feedback and index decay on SRC.", - "One to four paraphonic voices, chord shapes and inversions.", - "Chromatic playing, MIDI, legato and privately bundled scale/glide controls." + "Dedicated machine chooser entry in SRC SETUP and SELECT MACHINE TYPE; no sample required.", + "Pitch, ratio, index, fine tuning, feedback and index decay (HOLD at 127) on SRC.", + "One to four paraphonic voices, chord shapes and inversions; click-free note starts.", + "Chromatic playing with held-key gate, MIDI, legato, FM LFO destinations and privately bundled scale/glide controls." ], "usage": [ "Open a disposable project with FM Synth firmware. Select an audio track, hold FUNC and press SRC, select FM SYNTH with UP/DOWN and press YES. No sample is required.", "Press SRC. Set RATO to 1 and turn INDX from 0 upwards to hear a sine tone acquire FM harmonics. PTCH transposes it; FINE tunes it in cents; FDBK adds feedback; DEC shortens the modulation envelope.", "Place a trig and press PLAY. Press LFO to set VOIC to 2–4 and CHRD to a chord if wanted. AMP ATK/HOLD/REL shape the gain envelope; FUNC+AMP exposes LEG.", - "Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; assign the desired sample in the normal way." + "Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; PLAYBACK, AMP and LFO return to the init values of that machine (step and scene locks stay, as in stock). Assign the desired sample in the normal way." ] }, "access": { @@ -49,11 +49,12 @@ ], "screenshots": [ "media/ot-location.png", + "media/ot-machine-list.png", "media/ot-controls.png", "media/ot-voices.png", + "media/ot-lfo-setup.png", "media/ot-legato.png", "media/ot-scale.png", - "media/ot-root.png", "media/ot-glide.png" ] }, @@ -97,7 +98,7 @@ "name": "DEC", "default": 40, "count": 128, - "doc": "SRC F: modulation-index decay. Default raw 40 displays about 199 ms. This is separate from the AMP gain envelope.", + "doc": "SRC F: modulation-index decay. 0 is the shortest decay, 1–126 lengthen it (default raw 40 displays about 198 ms), 127 is HOLD. This is separate from the AMP gain envelope.", "labels": null }, { @@ -196,92 +197,247 @@ ], "limitations": [ "Parts store stock FLEX kind 1 plus FM/1 in unused NEIGHBOR control bytes. A stock build reads the track as FLEX; it does not synthesize FM. Choose stock explicitly before downgrading.", + "A machine change to or from FM SYNTH (either machine list or the file browser) resets the track's PLAYBACK, PLAYBACK SETUP, AMP, AMP SETUP, LFO and LFO SETUP to the new machine's init; step and scene locks are kept and apply by position, as at a stock machine change; FX1 and FX2 stay.", "LFO3 speed/depth become VOIC/CHRD; LFO3 depth is muted. ATK/HOLD/REL remain inherited stock AMP controls and drive the synth’s own envelope.", "The matching quantizer is bundled privately because the engine shares its mailbox and fixed scale accessor. Do not also select the public Scale Quantizer.", "Cannot combine with Analog BD or the public Scale Quantizer because their native hooks overlap. Compatible mixed builds have native/browser byte-parity evidence; mixed audio loads have not been stress-tested.", - "MIDI selector recovery and one-track mono/four-voice playback are emulator-tested. External MIDI note/CC traffic, saved project/Part reloads, legato, modulation, maximum simultaneous tracks and hardware timing remain unqualified.", - "Historical upstream marker-FLEX activation remains for compatibility; the verified route is the dedicated FM SYNTH chooser.", - "Source output is 18.06 dB quieter at identical mixer settings. Added headroom mitigates gain clipping; effects, resonant filtering and summed tracks can still clip. The hardware static report has not been reproduced." + "Emulator measurements qualify one FM track at VOIC 4 (or three at VOIC 1) inside every audio block including the first START; larger loads exceed the conservative per-block budget on START blocks and are not qualified. External MIDI note/CC traffic and Part/project reloads and a power cycle on hardware remain unqualified.", + "Marker-file activation (FMSYNTH*.wav / SYNTH*.wav in a FLEX slot) remains for compatibility; the machine list is the recommended route.", + "The engine leaves 6 dB of headroom (the author's 6.02 dB in place of 0.1.2's 18.06 dB source attenuation), so 0.1.4 is about 12 dB louder than 0.1.2 at identical mixer settings: AMP VOL 0 is the reference and VOL up to about +6 stays clean. Effects, resonant filtering, higher VOL and summed tracks can clip.", + "POLY8's shared chooser bridge (sdk/octabam/modules/poly8/composition.json) is reviewed against 0.1.2's chooser hooks, which this version replaces with the author's; until that bridge is updated, a build that selects POLY8 together with FM Synth 0.1.4 stops at POLY8's seam check (native and browser alike) instead of building." ] }, "resources": { - "recorded": "Build extents and source estimates; unknown chip timing/memory under owner release exception", + "recorded": "2026-10-11; emulator cycles and exact memory, v2.12 image 00fa6caa; hardware report pending", "storage": { - "label": "Shared audio arena reservation", - "display": "10,487,808 bytes (1,707 pages)", - "value": 10487808, + "label": "FM code, state and image bytes (bundled quantizer included)", + "display": "43,460 bytes plus shared platform reserve", + "value": 43460, "unit": "bytes", "method": "static", - "conditions": "Shared native platform reservation of 1,707 × 6,144-byte pages. This is not the exact incremental FM memory cost: complete code/state/table/stack/padding and peak lifetime accounting remains pending.", + "conditions": "Exact from the link map: 20,996-byte engine DRAM unit (code, tables and state for all eight tracks), 2,076-byte chooser unit (machine.s with the 2.11 machine-list row table and 2.12's machine-change init), 3,532-byte bundled quantizer unit, 1,868-byte pinned page, 461 bytes of quantizer ROM units, tables and hook sites, 422 bytes of FM hook sites and 12 bytes of kind-table entries in the OS image, and the 14,093-byte appended platform loader. Excludes the 352-byte stack bound and the shared 10,461,204-byte Flex sample-RAM platform reserve (sample pages 14,602 -> 12,895). See TESTING.md.", "source": "TESTING.md" }, "processing": { - "label": "ColdFire synthesis", - "display": "Worst-case cycles unmeasured", - "value": null, + "label": "ColdFire processing", + "display": "24,125 cycles/block per VOIC 4 track (emulated)", + "value": 24125, "unit": "cycles/block", - "method": "unmeasured", - "conditions": "CF computes each FM sample and up to four voices per track. Parameter modulation, MIDI, mode changes and maximum eight-track loads have not been priced.", + "method": "emulator", + "conditions": "Emulated ColdFire instructions per 16-sample frame interrupt (362.8 us at 44.1 kHz), converted at 9.44 ns per instruction (the hardware CF METER calibration, 2.49216 cycles at 264 MHz); not chip wall-clock cycles. One VOIC 4 track adds up to 9,680 instructions on its START block (24,125 cycles; the largest one-track peak of five runs over the smallest no-FM block) and about 5,018 to 5,087 sustained; one VOIC 1 track adds up to 4,981 on START and about 2,184 to 2,215 sustained. Worst case with one VOIC 4 track: 32,299 instructions + 372 wrapper allowance = 81,422 of 95,779 cycles/block, stock reserve included. Eight VOIC 4 tracks peak at 97,729 instructions (243,557 cycles, 2.54x the block) and are over budget. See TESTING.md.", "source": "TESTING.md" }, "impact": { - "conditions": "Estimates for eight FM tracks with up to four voices each; no measured comparison or real-time guarantee.", + "conditions": "One to four voices per active synth track; state allocated for eight tracks. Emulator qualification: one VOIC 4 track or three VOIC 1 tracks keep every block in budget; two VOIC 2 or VOIC 4 tracks and four VOIC 1 tracks fit only at the fast end of the calibration; eight VOIC 4 tracks do not.", "cpu": { "level": "high", "basis": "source-estimate", - "rationale": "Per-sample two-operator FM and multiple voice/envelope/chord/glide paths execute on ColdFire.", - "source": "upstream/synth/poly.s" + "rationale": "ColdFire generates two sine operators per voice per sample, plus envelopes, glide, crossfades and fades at note changes and UI hooks. Measured: about 625 own instructions per render call at VOIC 1 and 2,066 at VOIC 4; one VOIC 4 track adds up to 9,680 instructions on its START block.", + "source": "TESTING.md" }, "dsp": { "level": "minimal", "basis": "source-estimate", - "rationale": "No new DSP algorithm; generated audio passes through existing stock source, AMP and effect processing.", - "source": "manifest.py" + "rationale": "Adds no DSP program, table or effect buffer; existing track processing and selected effects still use DSP time. This tier describes added FM code, not the cost of a complete track.", + "source": "TESTING.md" }, "memory": { - "level": "high", + "level": "moderate", "basis": "source-estimate", - "rationale": "The native platform reserves a shared 10 MiB audio arena plus synth/quantizer state and page clones.", + "rationale": "20,996-byte engine unit with state for all eight tracks, 2,076-byte chooser, 3,532-byte bundled quantizer and a 1,868-byte fixed UI page; the 14,093-byte loader and the 10,461,204-byte sample-RAM platform reserve are the shared DRAM platform's.", "source": "TESTING.md" } } }, "tests": { "report": "TESTING.md", - "summary": "Owner-approved experimental 0.1.2: MIDI selector regression passes; mono and four-voice output have 18.06 dB more headroom. Current hardware remains untested; chip timing and complete memory bounds remain unmeasured. See TESTING.md and evidence/regressions.json.", - "hardwareStatus": "untested", - "evidenceRevision": "73490ef77f36bf523bc28f2b385d62e331ec7c5c", + "summary": "Emulator qualification of the native FM Synth 0.1.4 image (MAIN OS 00fa6caa..., built from the author's ca13ed8, octatrick 2.12 tag v2.12, with the bundled quantizer): frame-interrupt instruction counts for 1 to 8 FM tracks at VOIC 1, 2 and 4, converted at the hardware CF METER calibration (9.44 ns/instruction, 264 MHz); the bounding cards measured five times. One FM track at VOIC 4 keeps every block, including the first START, inside the 16-sample block (81,422 of 95,779 cycles); eight VOIC 4 tracks do not. Exact memory from the link map: 43,812 module-owned bytes plus the shared 10,461,204-byte sample-RAM platform reserve. Software verification: the module's own native Part smoke (verify.py) passes on this image; two FM tracks pass Part save/reload, project save/reload and a fresh project load in the emulator (the battery-RAM-only restart is the hardware test); the 2.12 metronome case and the machine-change init checked in the emulator; eight LCD captures; the author's odd-address check. The native/browser coverage record is not refreshed: POLY8's chooser bridge stops on this chooser (TESTING.md). A hardware report for this exact image is pending; Modwerk's 0.1.1/0.1.2 owner approvals stay with those versions.", + "hardwareStatus": "historical", + "evidenceRevision": "5abb183cad93bc735c42ef1f8f9ce7925285b6db", "gates": [ - "verify.py", - "verify-audio.py", - "verify-regressions.py" + "verify.py: native Part smoke on image 00fa6caa (eight-track selection, Part and shadow signature, sample slots unchanged, reselection keeps controls, return to FLEX): passed (evidence/native-smoke.json)", + "Two FM instances (T1, T5) with distinct settings on image 00fa6caa in the emulator: isolation, Part save/reload, project save/reload and a fresh project load restore both; audio after each transition (TESTING.md)", + "Battery-RAM-only restart: not testable on the pinned emulator; on the author's hardware sheet", + "2.12 metronome case on image 00fa6caa: a held key sample-identical with the metronome on and off, a held VOIC 4 chord's voices never released; the 2.11 build cut both (TESTING.md)", + "2.12 machine-change init on image 00fa6caa: FLEX -> FM SYNTH -> FLEX -> FM SYNTH gives FM SYNTH's init both times, FLEX's defaults between, FX1 kept (TESTING.md)", + "Native/browser composition: the coverage set with POLY8 stops at POLY8's seam check (open, TESTING.md); without POLY8, 122 selections: 35 builds match outside the platform writes, 87 refusals match, 0 mismatches (local run on main 3151975; sdk/native-comparisons/synth.json not refreshed)", + "Emulator worst-case frame-interrupt cycles with stock reserve, the bounding cards measured five times, converted at the CF METER calibration", + "Exact memory inventory reconciled with the native link map; static stack bound from the assembled runtime", + "The author's tools/align_check.py: no instruction and no detour target at an odd address", + "verify-audio.py and verify-regressions.py (Octemu audio and the #264 MIDI selector): not rerun for 0.1.4; 0.1.2 results in evidence/release-0.1.2", + "Hardware report for this source and image: pending (TIM placeholders)" ], - "documentation": { - "tutorial": { - "title": "Quick tutorial: play FM Synth", - "steps": [ - "Open a disposable project with FM Synth firmware. Select an audio track, hold FUNC and press SRC, select FM SYNTH with UP/DOWN and press YES. No sample is required.", - "Press SRC. Set RATO to 1 and turn INDX from 0 upwards to hear a sine tone acquire FM harmonics. PTCH transposes it; FINE tunes it in cents; FDBK adds feedback; DEC shortens the modulation envelope.", - "Place a trig and press PLAY. Press LFO to set VOIC to 2–4 and CHRD to a chord if wanted. AMP ATK/HOLD/REL shape the gain envelope; FUNC+AMP exposes LEG.", - "Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; assign the desired sample in the normal way." - ] - }, - "screenshots": [ - "media/ot-location.png", - "media/ot-controls.png", - "media/ot-voices.png", - "media/ot-legato.png", - "media/ot-scale.png", - "media/ot-root.png", - "media/ot-glide.png" + "qualification": { + "moduleVersion": "0.1.4-experimental", + "sourceSha256": "4fc06072b4039db83cba1600e949d00770c9753d974bbf76a5809f8e1be9e851", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "cycles": [ + { + "processor": "coldfire", + "worstCase": 24125, + "maxConfiguration": 81422, + "maxInstances": 1, + "budget": 95779, + "unit": "cycles/block", + "method": "emulator", + "conditions": { + "parameterExtremes": "Emulator windows with PTCH/RATO/INDX/FINE/FDBK p-locked to their endpoints (alternating 0 and 127 every step, PTCH 52..75), Part RATO/INDX/FDBK at 127, DEC at both extremes (Part and lock 0 and 127: 63,369 vs 63,398 instr/block sustained at eight tracks, peaks 97,705 vs 97,697 in the first run and 97,681..97,729 over five runs, so decay does not bound the render), VOIC 1, 2 and 4, CHRD stepping through 22 shapes with all four inversions as step locks, GLIDE 20 in battery RAM. Every step changes the pitch, so the 2.10 crossfades, voice fades and waiting notes run every step. Measured in emulated ColdFire instructions on the pinned ot_emu and converted at the CF METER calibration; see TESTING.md.", + "parameterModulation": "Every trig carries locks on all six PLAYBACK controls plus VOIC and CHRD; LFO1 and LFO2 of every track at full speed and depth on INDX and FDBK; scene locks on all six PLAYBACK controls in all sixteen scenes with the crossfader at mid-travel, so scene interpolation and LFO evaluation run every block. MIDI CC and live knob turns use the same stock lock/parameter path inside the measured frame ISR and were not driven separately.", + "modeSwitching": "The first PLAY after boot (the stock full Part publication on every track's first trig) is the measured peak; every later step is a warm retrigger of all voices (VOIC 4 chords steal and restart voices every step). STOP/PLAY repeated once per run. Machine choice (SRC SETUP, SELECT MACHINE TYPE, the file browser) with 2.12's machine-change init, the Part validator, page and setup-window changes run in the UI task, outside the frame ISR deadline, and were not timed; their stack is bounded statically.", + "maxLoad": "Eight FLEX tracks on the SYNTH marker, all starting on the same step every step at 120 BPM, stock DELAY in FX2 on all eight tracks, FX1 stock; the same load with the tracks chosen as FM SYNTH by the Part signature (no sample renderer nested) peaks lower, 88,317. Measured scaling: 1, 2, 3, 4 and 8 tracks at VOIC 1, 2 and 4. One track at VOIC 4 (32,299 instr peak, the maximum of five runs, spread 85) and three at VOIC 1 (37,368) keep every block, including the first START, inside the 38,432-instruction-time block; two at VOIC 2 (39,415), two at VOIC 4 (41,613) and four at VOIC 1 (42,140) fit only at the 8.38 ns end; eight tracks at VOIC 4 peak at 97,729 and run at 63,362..63,405 sustained. maxInstances is therefore 1 under the module's own worst case. No USB modules are in the measured image; USB/MIDI traffic and held chromatic keys were not driven.", + "inputConditions": "The FM engine generates its own signal; it has no audio input. Render cost depends on voice count, glide, note changes (crossfades and fades) and lock/LFO/scene activity, all driven at their worst; DEC extremes do not change cost. Silence (n0: no FM track playing) was measured as the stock reserve plus the per-track source hook: 22,619..22,893 instr/block over five runs." + }, + "report": "TESTING.md" + } ], - "screenshotStyle": "black-and-white" + "memory": { + "regions": [ + { + "name": "Per-track engine state: track record, four voices, four waiting notes, chord record, MIDI held keys and ring", + "space": "sdram", + "words": 506, + "wordBits": 8, + "bytes": 506, + "scope": "instance" + }, + { + "name": "FM engine DRAM unit code and tables", + "space": "sdram", + "words": 15420, + "wordBits": 8, + "bytes": 15420, + "scope": "shared" + }, + { + "name": "FM shared state: clock, sequencer word, retrig log, LFO/PLAYBACK/AMP descriptor clones", + "space": "sdram", + "words": 1528, + "wordBits": 8, + "bytes": 1528, + "scope": "shared" + }, + { + "name": "FM SYNTH chooser DRAM unit (machine.s): code, name and row tables, the 2.12 machine-change init", + "space": "sdram", + "words": 2076, + "wordBits": 8, + "bytes": 2076, + "scope": "shared" + }, + { + "name": "Pinned FM PLAYBACK page in the OS-image cave", + "space": "cpu-flash", + "words": 1868, + "wordBits": 8, + "bytes": 1868, + "scope": "shared" + }, + { + "name": "FM hook sites rewritten in place: 52 detours (416 B, one the 2.12 file-browser site) and the resolver jump (6 B)", + "space": "cpu-flash", + "words": 422, + "wordBits": 8, + "bytes": 422, + "scope": "shared" + }, + { + "name": "Three kind-table entries repointed (FLEX and STATIC renderer, FLEX START)", + "space": "cpu-flash", + "words": 12, + "wordBits": 8, + "bytes": 12, + "scope": "shared" + }, + { + "name": "Bundled quantizer DRAM unit", + "space": "sdram", + "words": 3532, + "wordBits": 8, + "bytes": 3532, + "scope": "shared" + }, + { + "name": "Bundled quantizer ROM units (243 B), SEQUENCER tables (72 B) and hook sites (19 detours 142 B, one poke 4 B)", + "space": "cpu-flash", + "words": 461, + "wordBits": 8, + "bytes": 461, + "scope": "shared" + }, + { + "name": "Platform loader and packed DRAM units appended to the image", + "space": "cpu-flash", + "words": 14093, + "wordBits": 8, + "bytes": 14093, + "scope": "shared" + }, + { + "name": "Platform reserve: Flex sample RAM given up to the DRAM platform, less the three DRAM units", + "space": "sdram", + "words": 10461204, + "wordBits": 8, + "bytes": 10461204, + "scope": "shared" + }, + { + "name": "ColdFire stack increment upper bound (UI path 220 B, the Part validator wrap, plus frame path 132 B)", + "space": "sdram", + "words": 352, + "wordBits": 8, + "bytes": 352, + "scope": "shared" + } + ], + "perInstanceBytes": 506, + "sharedBytes": 10500968, + "maxInstances": 8, + "totalBytes": 10505016, + "conditions": "Exact link map of the measured native build (runtime.elf, layout.json, the native build log). The module's own bytes are 43,812: the 20,996-byte engine DRAM unit (15,420 code/tables + 1,528 shared state + 8 x 506 per-track state, preallocated for all eight tracks), the 2,076-byte chooser unit (machine.s: the 2.11 row table, the eleven machine-list detours' code and 2.12's machine-change init), the 3,532-byte bundled quantizer unit, the 1,868-byte pinned page, 461 bytes of quantizer ROM units/tables and hook sites, FM hook sites and kind-table entries, the 14,093-byte appended platform loader and the 352-byte stack bound. The 10,461,204-byte platform reserve is the Flex sample RAM given up to the DRAM platform (sample pages 14,602 -> 12,895, 1,707 pages of 6,144 bytes, less the three DRAM units): any DRAM module creates it, and it is counted once here, not per track. No heap or dynamic allocation; no SRAM, battery-RAM or DSP allocation of its own (GLIDE is the bundled quantizer's battery-RAM byte; LEG, the LFO destination and the FM SYNTH signature use stock Part bytes and their battery-RAM shadow; the machine-change init writes stock Part bytes, their shadow and the live lane). Stack: static upper bound from the assembled runtime of this image, following its control flow (the deepest UI-task path is still the Part validator wrap fm_validate, 220 B including the replayed stock 96-byte prologue; the machine-change init's callers reach 116 B; the frame path sy_render 132 B). Hardware canaries not measured.", + "report": "TESTING.md" + }, + "hardware": { + "kind": "functional", + "status": "reported", + "model": "MKI", + "testedOn": "TIM: YYYY-MM-DD", + "tester": "TIM: credited handle", + "sourceRevision": "5abb183cad93bc735c42ef1f8f9ce7925285b6db", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "summary": "TIM: what was played on the unit and what happened (MAIN OS 00fa6caa..., the hardware test sheet)", + "limitations": [ + "TIM: duration, track and voice counts, untested cases" + ], + "report": "TESTING.md" + }, + "documentation": { + "tutorial": { + "title": "Quick tutorial: play FM Synth", + "steps": [ + "Open a disposable project with FM Synth firmware. Select an audio track, hold FUNC and press SRC, select FM SYNTH with UP/DOWN and press YES. No sample is required.", + "Press SRC. Set RATO to 1 and turn INDX from 0 upwards to hear a sine tone acquire FM harmonics. PTCH transposes it; FINE tunes it in cents; FDBK adds feedback; DEC shortens the modulation envelope.", + "Place a trig and press PLAY. Press LFO to set VOIC to 2–4 and CHRD to a chord if wanted. AMP ATK/HOLD/REL shape the gain envelope; FUNC+AMP exposes LEG.", + "Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; PLAYBACK, AMP and LFO return to the init values of that machine (step and scene locks stay, as in stock). Assign the desired sample in the normal way." + ] + }, + "screenshots": [ + "media/ot-location.png", + "media/ot-machine-list.png", + "media/ot-controls.png", + "media/ot-voices.png", + "media/ot-lfo-setup.png", + "media/ot-legato.png", + "media/ot-scale.png", + "media/ot-glide.png" + ], + "screenshotStyle": "black-and-white" + } } }, "license": { "spdx": "MIT", "file": "LICENSE", - "declaration": "Tim Hastie and Sam Banks full MIT notices retained with exact source pins; local dedicated-machine adaptations and SVG are MIT. Stock dependencies use lazy address/length/SHA-256 guards. No firmware, extracted stock byte blobs, cards, RAM or generated binaries are included. Actual LCD captures use media/LICENSE.md; underlying Elektron rights remain reserved. Review is not legal clearance." + "declaration": "Tim Hastie and Sam Banks full MIT notices retained with exact source pins (Tim's LICENSE, unchanged from 2.10 to 2.12, carries the Modwerk and Sam Banks notices for the adapted chooser); Modwerk adaptations and SVG are MIT. Stock dependencies use lazy address/length/SHA-256 guards. No firmware, extracted stock byte blobs, cards, RAM or generated binaries are included. Actual LCD captures use media/LICENSE.md; underlying Elektron rights remain reserved. Review is not legal clearance." }, "media": [ { @@ -296,8 +452,25 @@ "page": "SRC SETUP", "shows": "location", "firmware": "1.40C", - "moduleVersion": "0.1.2-experimental", - "imageSha256": "d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157", + "moduleVersion": "0.1.4-experimental", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." + } + }, + { + "path": "media/ot-machine-list.png", + "captureType": "emulator", + "caption": "FM SYNTH is also the sixth row of SELECT MACHINE TYPE.", + "alt": "Actual Octatrack LCD: FM SYNTH is also the sixth row of SELECT MACHINE TYPE.", + "credit": "Modwerk contributors", + "license": "LicenseRef-OT-UI-Documentation", + "source": "original", + "otUi": { + "page": "SELECT MACHINE TYPE", + "shows": "location", + "firmware": "1.40C", + "moduleVersion": "0.1.4-experimental", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, @@ -313,8 +486,8 @@ "page": "FM SYNTH SRC", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.2-experimental", - "imageSha256": "d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157", + "moduleVersion": "0.1.4-experimental", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, @@ -330,59 +503,59 @@ "page": "FM SYNTH LFO", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.2-experimental", - "imageSha256": "d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157", + "moduleVersion": "0.1.4-experimental", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, { - "path": "media/ot-legato.png", + "path": "media/ot-lfo-setup.png", "captureType": "emulator", - "caption": "LEG selects OFF, MONO or POLY on an FM track.", - "alt": "Actual Octatrack LCD: LEG selects OFF, MONO or POLY on an FM track.", + "caption": "LFO SETUP lists the FM page's names as destinations; this example shows SRC RATO.", + "alt": "Actual Octatrack LCD: LFO SETUP lists the FM page's names as destinations; this example shows SRC RATO.", "credit": "Modwerk contributors", "license": "LicenseRef-OT-UI-Documentation", "source": "original", "otUi": { - "page": "AMP SETUP", + "page": "LFO SETUP", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.2-experimental", - "imageSha256": "d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157", + "moduleVersion": "0.1.4-experimental", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, { - "path": "media/ot-scale.png", + "path": "media/ot-legato.png", "captureType": "emulator", - "caption": "The privately bundled quantizer adds SCALE; this example selects MAJOR.", - "alt": "Actual Octatrack LCD: The privately bundled quantizer adds SCALE; this example selects MAJOR.", + "caption": "LEG selects OFF, MONO or POLY on an FM track.", + "alt": "Actual Octatrack LCD: LEG selects OFF, MONO or POLY on an FM track.", "credit": "Modwerk contributors", "license": "LicenseRef-OT-UI-Documentation", "source": "original", "otUi": { - "page": "CONTROL SEQUENCER: SCALE", + "page": "AMP SETUP", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.2-experimental", - "imageSha256": "d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157", + "moduleVersion": "0.1.4-experimental", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, { - "path": "media/ot-root.png", + "path": "media/ot-scale.png", "captureType": "emulator", - "caption": "ROOT chooses the key; this example uses C.", - "alt": "Actual Octatrack LCD: ROOT chooses the key; this example uses C.", + "caption": "The privately bundled quantizer adds SCALE; this example selects MAJOR.", + "alt": "Actual Octatrack LCD: The privately bundled quantizer adds SCALE; this example selects MAJOR.", "credit": "Modwerk contributors", "license": "LicenseRef-OT-UI-Documentation", "source": "original", "otUi": { - "page": "CONTROL SEQUENCER: ROOT", + "page": "CONTROL SEQUENCER: SCALE", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.2-experimental", - "imageSha256": "d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157", + "moduleVersion": "0.1.4-experimental", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, @@ -398,8 +571,8 @@ "page": "CONTROL SEQUENCER: GLIDE", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.2-experimental", - "imageSha256": "d5464195fac0033852546fbbc8f809b64d16646309c21ab25e2f203fbf123157", + "moduleVersion": "0.1.4-experimental", + "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, diff --git a/sdk/octabam/modules/synth/qualification.example.json b/sdk/octabam/modules/synth/qualification.example.json deleted file mode 100644 index 1fd718cfa..000000000 --- a/sdk/octabam/modules/synth/qualification.example.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "moduleVersion": "0.1.2-experimental", - "sourceSha256": "8fa263bfcd30dca9aff4f83dbcb001d4ccf06286e31839c8e0afd61100a9315c", - "imageSha256": "64beb992dd65cad096d2c70f23bb3064c715464d651da22dd3ad9024901831e9", - "cycles": [ - { - "processor": "coldfire", - "worstCase": null, - "maxConfiguration": null, - "maxInstances": null, - "budget": null, - "unit": "cycles/event", - "method": "emulator", - "conditions": { - "parameterExtremes": "Describe every minimum, maximum and branch boundary tested.", - "parameterModulation": "Describe simultaneous LFOs, p-locks, MIDI CC and scene sweeps; justify each inapplicable mechanism.", - "modeSwitching": "Describe repeated/interrupted mode, bypass and Part changes.", - "maxLoad": "Describe maximum voices/instances, both cores, FX slots, streaming and USB/MIDI load.", - "inputConditions": "Describe silence, impulses, full-scale inputs, feedback and other worst-case signal conditions." - }, - "report": "TESTING.md" - } - ], - "memory": { - "regions": [ - { - "name": "Replace with each code, state, table, buffer, stack/heap and padding allocation", - "space": "sdram", - "words": null, - "wordBits": 8, - "bytes": null, - "scope": "shared" - } - ], - "perInstanceBytes": null, - "sharedBytes": null, - "maxInstances": null, - "totalBytes": null, - "conditions": "State exact build options, alignment, accounting convention, allocator/stack bounds and reused stock/shared memory; justify every absent allocation class.", - "report": "TESTING.md" - }, - "hardware": { - "status": "pending", - "model": null, - "testedOn": "YYYY-MM-DD", - "tester": "Your credited tester handle; no email", - "project": { - "name": "Your local stress project", - "sha256": "REPLACE_WITH_PROJECT_FINGERPRINT", - "recipe": "Record generator/tool revision and command, local source template fingerprint, sample recipe, all settings and modifications needed to reproduce this stress project. Keep the project/card/firmware local." - }, - "durationMinutes": null, - "audioTracks": null, - "midiTracks": null, - "conditions": { - "parameterExtremes": "Describe every minimum, maximum and branch boundary tested.", - "parameterModulation": "Describe simultaneous LFOs, p-locks, MIDI CC and scene sweeps; justify each inapplicable mechanism.", - "modeSwitching": "Describe repeated/interrupted mode, bypass and Part changes.", - "maxLoad": "Describe maximum voices/instances, both cores, FX slots, streaming and USB/MIDI load.", - "inputConditions": "Describe silence, impulses, full-scale inputs, feedback and other worst-case signal conditions." - }, - "checks": { - "audioContinuity": "failed", - "transport": "failed", - "controls": "failed", - "memoryIntegrity": "failed", - "recovery": "failed" - }, - "report": "TESTING.md", - "maxInstances": null - }, - "documentation": { - "tutorial": { - "title": "Quick tutorial: play FM Synth", - "steps": [ - "Open a disposable project with FM Synth firmware. Select an audio track, hold FUNC and press SRC, select FM SYNTH with UP/DOWN and press YES. No sample is required.", - "Press SRC. Set RATO to 1 and turn INDX from 0 upwards to hear a sine tone acquire FM harmonics. PTCH transposes it; FINE tunes it in cents; FDBK adds feedback; DEC shortens the modulation envelope.", - "Place a trig and press PLAY. Press LFO to set VOIC to 2–4 and CHRD to a chord if wanted. AMP ATK/HOLD/REL shape the gain envelope; FUNC+AMP exposes LEG.", - "Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; assign the desired sample in the normal way." - ] - }, - "screenshots": [ - "media/ot-location.png", - "media/ot-controls.png", - "media/ot-voices.png", - "media/ot-legato.png", - "media/ot-scale.png", - "media/ot-root.png", - "media/ot-glide.png" - ], - "screenshotStyle": "black-and-white" - } -} diff --git a/sdk/octabam/modules/synth/registration.c b/sdk/octabam/modules/synth/registration.c deleted file mode 100644 index fa3a22127..000000000 --- a/sdk/octabam/modules/synth/registration.c +++ /dev/null @@ -1,41 +0,0 @@ -/* FM Synth's native chooser registration. Part storage uses FLEX plus - * FM/1 in unused NEIGHBOR bytes. No sample or sample-slot mutation is needed. - * Derived from Modwerk's MIT Analog BD registration; FM engine: Tim Hastie. - */ -typedef unsigned char u8; -typedef unsigned int u32; -#define U8(a) (*(volatile u8 *)(a)) -#define U32(a) (*(volatile u32 *)(a)) -#define BANK 0x46c82456u -#define PART_IDX 0x100b14cfu -#define PART_OFF 0x8ed80u -#define PART_STRIDE 6322u -const u8 fm_defaults[12]={64,12,32,64,0,40,0,0,0,0,0,0}; -static volatile u8 *part_base(void) { - return (volatile u8 *)(U32(BANK)+PART_OFF+(U8(PART_IDX)&3)*PART_STRIDE); -} -static unsigned signed_track(const volatile u8 *part,unsigned t) { - const volatile u8 *s=part+60+30*t; - return t<8 && part[0x22+t]==1 && s[0]=='F' && s[1]=='M' && s[2]==1; -} -unsigned fm_admit_track(const volatile u8 *part,unsigned t) { (void)part;return t<8; } -unsigned fm_type(unsigned type,const volatile u8 *ptr) { - volatile u8 *part=part_base();unsigned t=(unsigned)(ptr-(part+0x22)); - return type==1 && t<8 && signed_track(part,t)?5:type; -} -/* Fixed page entry calls the engine's runtime clone builder. */ -u32 fm_track_page(const volatile u8 *ptr) { (void)ptr;return ((u32(*)(void))0x400d24d0u)(); } -void fm_ui_tick(void) { U32(0x400d5f38u+5*4)=fm_track_page((void*)0); } -extern int fm_stock_validate(void *part); -int fm_validate_part(u8 *part) { - u8 saved[8][12];unsigned mask=0; - const volatile u8 *stock=(const volatile u8 *)0x400d320cu; - for(unsigned t=0;t<8;++t) if(signed_track(part,t)) { - mask|=1u< CONTROL > SEQUENCER, [DOWN] ×4 → `GLIDE OFF` (list state offset 2, cursor 4, count 5, visible 3); LEVEL +1 → 1, +63 → 64, +100 → 127 @@ -409,7 +409,7 @@ cursor 4, count 5, visible 3); LEVEL +1 → 1, +63 → 64, +100 → 127 → 2/16 by LEVEL +1, DIRECT + [YES] → 2/16, −20 → PAT.LEN; the byte `0x8000004e` / mirror `0x100b14ae` read 0 → 1 → 2. -**2. Legato and glide, T2 = the FM synth** (`ga2/`, `ga3/`; T2's Part set +**2. Legato and glide, T2 = the FM synth** (two runs; T2's Part set to STRT 0 / LEN 0 = a clean carrier, AMP HOLD INF / REL 40; CHROMATIC mode; the card's SCALE = MIXOLYD snaps [TRIG 16] (+3) to +2 semitones — the quantizer at work). Hold [TRIG 13] (C4), press [TRIG 16] 0.6 s later, @@ -433,7 +433,7 @@ What does move: a **2.3 dB level step 200 ms after the second key** (−17.4 → −19.7 → −16.6 dB across the GLIDE 64 transition, the same at GLIDE 1 once the pitch had settled) — and exactly the same step follows every fresh note start and every stock FUNC + key trigless trig on this track -(`ga3/`, `ga4/FUNC_key_stock_trigless`: −19..−20 dB for 200 ms, then +(the second run, and a run of stock FUNC + key trigless trigs: −19..−20 dB for 200 ms, then −17). It is not the AMP envelope (above) and not the pitch (GLIDE 1); it is the DSP's own response to a trig word — T2's FX1 is a FILTER with a 0.5 s envelope (DEPTH 111, DEC 49), the likely consumer, but DEPTH 0 left @@ -443,7 +443,7 @@ event-byte bit 3) changed neither the step nor the glide (t63 113 ms), so the legato keeps stock's own trigless word (`0x4004fc9c`, `0x119`): the legato behaves exactly as FUNC + key does, minus the FUNC. -**3. Sequenced** (`gs2/`, GLIDE 64): the pattern cleared, T2 trig on step 1 +**3. Sequenced** (GLIDE 64): the pattern cleared, T2 trig on step 1 (PTCH −12 from the Part: 130.8 Hz) and a trigless trig on step 9 with a PTCH lock of +12 (`0x7c`): PLAY → 130.8 Hz, then from step 9 (1.0 s at 120 BPM) a two-octave glide to 523.0 Hz, 63 % / 95 % at 120 / 320 ms; @@ -451,7 +451,7 @@ GLIDE OFF: the same pattern jumps in one 10 ms bin (t63 = t95 = 40 ms, the step-time estimate). (The level rises 14 dB with the pitch: T2's FX1 is a FILTER with BASE 0 / WIDTH 72, its response, not the voice.) -**4. Persistence** (`pt/`): PROJECT > SYNC TO CARD with CHAIN AFTER = +**4. Persistence**: PROJECT > SYNC TO CARD with CHAIN AFTER = DIRECT, SCALE MIXOLYD, GLIDE 64 → the card's `OTLIVE/PROJECT/project.work` reads `PATTERN_CHANGE_CHAIN_BEHAVIOR=1` / `#SEQUENCER_SCALE=5` / `#SYNTH_GLIDE=64` (read on the Mac through `/card/eject`); `/card/insert` @@ -459,7 +459,7 @@ reads `PATTERN_CHANGE_CHAIN_BEHAVIOR=1` / `#SEQUENCER_SCALE=5` / power cycle -- see 26 Sep 2026 above) reloads `0x8000004e = 01`, `0x400d2cdc = 40`. Then the migration cases, editing the mounted card's `project.work` and -`/card/insert`-ing (`pt3/`, `bc/`): `PATTERN_CHANGE_CHAIN_BEHAVIOR=17` + +`/card/insert`-ing: `PATTERN_CHANGE_CHAIN_BEHAVIOR=17` + `#SYNTH_GLIDE=100` (a project saved by the 13 Sep DIRECT JUMP build) → `0x8000004e = 0x10` (the window shows **256/16**), GLIDE **100**; `=1` + `#SYNTH_GLIDE=33` → `0x01` (**DIRECT**), GLIDE **33**; `=5` + @@ -486,7 +486,7 @@ FUNC held and the paraphonic VOIC 2..4 path leave it clear), and `qz_leg3` sends a legato press down the trigless branch -- exactly what FUNC + key records. GLIDE off, FUNC held, VOIC 2..4 and non-synth tracks: stock. -Measured (`poly/liverec.py`, T2 = SYNTH, VOIC 1, INDX 0, SCALE OFF, 120 BPM, +Measured (`liverec.py`, T2 = SYNTH, VOIC 1, INDX 0, SCALE OFF, 120 BPM, the pattern cleared before each; T2's track record `0x400e21e0 + 0x91a`: byte 7 = the sample-trig mask of steps 1-8, byte 15 the trigless mask of steps 1-8 (byte 14 steps 9-16), locks at `+0x59 + step*32`): @@ -537,7 +537,7 @@ start (`qz_chain_of` -> `qz_chain` -> `qz_leg4`) and the held key's release writes the chain's whole length on every step of it (`qz_holdall`). Non-synth tracks, programmed trigs and playing without recording: untouched. -Measured (`poly/holdrec.py`, T2 = SYNTH, VOIC 1, GLIDE 64, INDX 0, AMP ATK 0 +Measured (`holdrec.py`, T2 = SYNTH, VOIC 1, GLIDE 64, INDX 0, AMP ATK 0 HOLD INF REL 20, 120 BPM = 8 steps a second, fresh boot; level per 50 ms from the note's onset, "ends" = the bin that drops below -40 dBFS): @@ -623,7 +623,7 @@ SCALE OFF 1 : 1.26 : 1.498. Unit size: `quantizer.s` 1,888 B (`REMIX=tim make cf`, at `0x400d6d00`), `scale.s` 6 B, `keys.s` 40 B, `glide.s` 4 B (gone since 26 Sep 2026; `quantizer.s` 3,000 B in `octatrick-usb` BUILD 10). -## Measurements (all `out/_agents/quantizer/`) +## Measurements (the logs and screens are in the author's workspace, not in this repository) Image: `REMIX=quantizer make bus` → `out/mainos_bus.bin`, 1,112,560 bytes, **1,250 bytes changed** vs `out/raw/section_3_MAIN_OS.bin` @@ -645,11 +645,11 @@ image-cf` packs it). The `tim` image (this module + DIRECT JUMP, 1,657 bytes) booted under the panel shows the stock chooser — NONE, FILTER, EQ, DJ EQ, PHASER, FLANGER, CHORUS, SPATIALIZER, COMB, COMPRESSOR, LOFI, DELAY, PLATE, SPRING, DARK — and the SCALE row still turns OFF → PHRYGN -(`out/_agents/cfbuild/`). Panel: `tools/panel/panel_server.py --image --project +(the cf build's own run). Panel: `tools/panel/panel_server.py --image --project out/_projects/otlive/OTLIVE/PROJECT --set OTLIVE --name PROJECT --sound -off --card out/_agents/quantizer/card.img` on port 8596, stock on 8597, +off --card ` on port 8596, stock on 8597, driven through `/key`, `/tap`, `/knob`, `/peek`, `/screen.txt` -(`shots/*_x4.png` are the text screens at 4×). PTCH slot of T1 (STATIC, +(`*_x4.png` are the text screens at 4×). PTCH slot of T1 (STATIC, part 0) `0x40170f8a`, of T5 (FLEX) `0x40171008`; T5 step 1's PTCH lock `0x400e46a1`; the chromatic staging byte `0x46c7dfda + t*32` (with `--sound off` no frame consumes it, so it keeps the pitch the key @@ -698,7 +698,7 @@ PHRYGIAN: `64 69 69 79 54 54 44 44 39 29 4` = 0, +1, **+1** (from +2: `+0x139`: `ff → 45` (the recorded PTCH lock = 69 = +1, the snapped value) and the trig bit; with OFF `+0x119`: `ff → 4a` (74 = +2, stock). -**5. OFF vs stock, boot A/B** (`ab/`). `tools/emu/ot_emu/oracle/drive.py +**5. OFF vs stock, boot A/B**. `tools/emu/ot_emu/oracle/drive.py --emu out/emu/ot_emu --image ` (the `inter` battery: boot, YES, MIXER, NO, T1 ×2, DOWN, RIGHT, NO, NO, PLAY 20×100 ms, STOP 5×100 ms). `ready.txt`, `stamps.txt`, `peeks.txt`, `stderr.txt` are @@ -709,7 +709,7 @@ difference is 712 vs 708 pairs, one fewer `0x3d`/`0x3f` toggle each of LEDs `0x24` and `0x25` — a breathing pair whose phase against the battery's fixed windows shifted, because the project load now runs the two loader detours on every line (`boot.log`: 65,402 vs 65,410 vectors -acknowledged over the boot; a stock-vs-stock re-run, `ab/stock2`, is +acknowledged over the boot; a second stock-vs-stock run is byte-identical, so the shift is real, not noise). No screen and no state peek differs. The battery never opens the SEQUENCER menu. @@ -886,9 +886,9 @@ The clamp per root (the other position; keys named on the 13-key picture): (each range is before the scale snap; "+12 x2" = the clamped keys, all at the ceiling) -### Measured (the second pass: the octatrick-tuner BUILD 20 bus = OCTATRK2.9 on ot_emu `--dsp-rt` through the poke panel on 8950, a copy of the OTLIVE card whose four loop files are same-length sines -- `third-0.wav` = 95 whole cycles in its 9,551 frames = 438.645 Hz, the slots' TSMODE 0 / LOOPMODE 1; T1 = FLEX slot 3 = that sine (assigned through the machine window: OTLIVE's T1 is STATIC slot 5, the Amen break; a STATIC slot streaming the 2,679-frame `first-0.wav` came out as a 689 Hz buzz = 44100 / 64, a stuck 64-frame chunk under the port, so FLEX), PLAYBACK PTCH 0 / STRT 0 / LEN max / RATE +63, AMP HOLD INF REL 20; T2 = FM SYNTH slot 5 as before; the ef2944b bus (the first pass, byte-identical to the BUILD 19 bus) on 8951 with the same card as the baseline; pitches = the strongest spectral peak over 0.3-0.8 s of a 0.9 s key hold, in semitones from the sample's own pitch (SCALE OFF, position 0, key 13: 438.7 Hz, named C4 below), every value within 0.5 cent of the semitone; the session's `root29b/m30.py`, shots and `report.txt` / `report_keys.txt` in `root29b/out20/` and `outbase/`, the readouts cropped as `*_ro.png`) +### Measured (the second pass: the octatrick-tuner BUILD 20 bus = OCTATRK2.9 on ot_emu `--dsp-rt` through the poke panel on 8950, a copy of the OTLIVE card whose four loop files are same-length sines -- `third-0.wav` = 95 whole cycles in its 9,551 frames = 438.645 Hz, the slots' TSMODE 0 / LOOPMODE 1; T1 = FLEX slot 3 = that sine (assigned through the machine window: OTLIVE's T1 is STATIC slot 5, the Amen break; a STATIC slot streaming the 2,679-frame `first-0.wav` came out as a 689 Hz buzz = 44100 / 64, a stuck 64-frame chunk under the port, so FLEX), PLAYBACK PTCH 0 / STRT 0 / LEN max / RATE +63, AMP HOLD INF REL 20; T2 = FM SYNTH slot 5 as before; the ef2944b bus (the first pass, byte-identical to the BUILD 19 bus) on 8951 with the same card as the baseline; pitches = the strongest spectral peak over 0.3-0.8 s of a 0.9 s key hold, in semitones from the sample's own pitch (SCALE OFF, position 0, key 13: 438.7 Hz, named C4 below), every value within 0.5 cent of the semitone; `m30.py`, its shots and `report.txt` / `report_keys.txt` for this bus and the first-pass bus, the readouts cropped as `*_ro.png`) -- **The stock model** (the disassembly, `root29/stock.dis`): `0x460d16fc` +- **The stock model** (the disassembly of the stock 1.40C main OS): `0x460d16fc` has one writer, `eorl #1` at `0x4004591a`, and reads at `0x40044968` (the picture), `0x400449b8` (the number), `0x40044abe` (the marks), `0x4004d442`, `0x4004fde4` and `0x40050254` (the index: key + 12 * @@ -925,13 +925,13 @@ the ceiling) 585.5 657.2 657.2 737.7 737.7 828.1 877.3 Hz = C4 C4 D4 D4 E4 F4 F4 G4 G4 A4 A4 B4 C5 (0 .. +12); the numbers `C 0` / `C 1`. The **ef2944b bus** (the first pass) on the same card gave the same 29 numbers to 0.1 Hz, and the same five SCALE OFF / - ROOT A numbers in each position (`outbase/report_keys.txt`) -- ROOT C + ROOT A numbers in each position (the first-pass bus's `report_keys.txt`) -- ROOT C and SCALE OFF are unchanged. That bus with **ROOT A / MAJOR** shows what the second pass fixes: position 0 the same sixteen numbers as above, but position 1 = 737.7 737.7 828.1 x11 Hz -- A4 A4 then **B4 on eleven of the thirteen keys** (every key from 3 up clamped at index 24 = C5, not in A major, snapped down to B4), its number reading `A 1` - (`outbase/a_A_pos1_ro.png`). + (`a_A_pos1_ro.png`). - **(e) T2 (FM SYNTH), SCALE MINOR / ROOT A, octave 0**: keys 1 3 5 6 8 10 12 13 = 220.0 247.0 261.6 293.7 329.6 349.2 392.0 440.0 Hz = A3 B3 C4 D4 E4 F4 G4 A4, the number `A 0` (`e_T2_minor_A_ro.png`) -- as at the first @@ -962,7 +962,7 @@ the ceiling) hits as at the first pass (direct-jump's displaced hook bytes and the synth manifest's 16-byte run), nothing new. -### Measured (28 Sep 2026, the octatrick-tuner BUILD 19 bus = OCTATRK2.9 on ot_emu `--dsp-rt` through the poke panel on 8930, a copy of the OTLIVE card, T2 = FM SYNTH slot 5, INDX 0 / FDBK 0, AMP HOLD INF REL 20, VOIC 1 unless said; the 2.8 baseline = the same remix at the `tuning` tree, BUILD 19, on 8931; notes as spectral peaks over 0.3-0.5 s windows named from C4 = 261.6256 Hz; the session's `root29/m29.py`, shots and `report.txt` in `root29/out29/` and `out28/`) +### Measured (28 Sep 2026, the octatrick-tuner BUILD 19 bus = OCTATRK2.9 on ot_emu `--dsp-rt` through the poke panel on 8930, a copy of the OTLIVE card, T2 = FM SYNTH slot 5, INDX 0 / FDBK 0, AMP HOLD INF REL 20, VOIC 1 unless said; the 2.8 baseline = the same remix at the `tuning` tree, BUILD 19, on 8931; notes as spectral peaks over 0.3-0.5 s windows named from C4 = 261.6256 Hz; `m29.py`, its shots and `report.txt` for the 2.9 bus and the 2.8 baseline) - **The ROOT row** (`row_root_A.png`, `row_glide.png`): PROJECT > CONTROL > SEQUENCER, [DOWN] x4 shows `LFO AUTO CHANGE / SCALE OFF / ROOT C`; the @@ -979,7 +979,7 @@ the ceiling) `06 0c 09` -- preloaded into a fresh `ot_emu` with `OT_SRAM_IN`, no LOAD PROJECT posted, `OT_NO_LOAD=1`): headless, the bytes read SCALE 6 / GLIDE 12 / ROOT 9 at ready and 20 s later, no fault; through the panel, the - SEQUENCER window reads `SCALE MINOR / ROOT A` (`outwarm/warm_row_root.png`) + SEQUENCER window reads `SCALE MINOR / ROOT A` (`warm_row_root.png`) and the unit plays. `--watch-pc` on that warm boot: loader `0x4010fdf0` at instruction 4,270,945, depack `0x400e0aca` at 4,505,240, `.data` copy at 6,526,781, main at 12,342,579, the warm-boot path `0x40025770` at @@ -994,7 +994,7 @@ the ceiling) keyboard reads `A 0` (`kbd_minor_A.png`). **ROOT C** on the same keys: 130.8 / 146.8 / 155.6 / 174.6 / 196.0 / 207.7 / 233.1 / 261.6 Hz = C3 D3 D#3 F3 G3 G#3 A#3 C4 (C minor), and the **2.8 baseline bus** (the `tuning` - tree at BUILD 19, `out28/`) gave the same eight numbers to 0.1 Hz with + tree at BUILD 19) gave the same eight numbers to 0.1 Hz with its number reading `0` (`kbd_minor_28.png`; the 2.9 shot at ROOT C, `kbd_minor_C.png`, still reads `A 0` -- a stale draw, see (e); the fresh draws are `probe_kbd_E_major.png` = `E 0` and `probe_kbd_off_E.png` = `0`). @@ -1006,7 +1006,7 @@ the ceiling) +1 -> 75 (B), -2 -> 71 (G), +1 -> 73 (A), the Part byte untouched at 73; the pattern played step 10 at 439.9 Hz. **ROOT C**: 66 67 69 71 72 74 76 up and 74 72 71 69 67 66 64 down -- the 2.8 baseline's numbers exactly - (`out28/report.txt`: 66 67 69 71 72 74 76 / 74 72 71 69 67 66 64). + (the baseline's `report.txt`: 66 67 69 71 72 74 76 / 74 72 71 69 67 66 64). - **(d) A CHRD chord, SCALE MAJOR / ROOT E, VOIC 3, CHRD MAJ (byte 8)**: key 13 (E4 with ROOT E) sounded **329.6 / 415.3 / 493.9 Hz = E4 G#4 B4** (twice); key 16 (G4 chromatic, snapped to F#4, in E major) sounded 370.0 diff --git a/sdk/octabam/modules/synth/upstream/quantizer/manifest.py b/sdk/octabam/modules/synth/upstream/quantizer/manifest.py index d480233cc..11be76aeb 100644 --- a/sdk/octabam/modules/synth/upstream/quantizer/manifest.py +++ b/sdk/octabam/modules/synth/upstream/quantizer/manifest.py @@ -212,6 +212,13 @@ # does the DRAM unit quantizer.s for every snap (one ROOT-rotated mask). SCALE_AT = 0x400d2ca8 +def _qz_include(modules): + """quantizer.s's remix.inc (2.11): HAVE_SYDRUM, 1 when SY DRUM is in the remix -- its + signature "SY", 1 counts as a synth track (semitone PTCH, the keys, MIDI), mono (VOIC is + FM's), and its legato keys reach the engine. 0 assembles the unit as 2.10's.""" + return f".set HAVE_SYDRUM, {int('SY DRUM' in modules)}\n" + + MODULE = Module( name="quantizer", key="SCALE QUANTIZER", @@ -227,7 +234,7 @@ Linked("qzc", os.path.join(_HERE, "core.s"), cpu="5475"), Linked("qzs", os.path.join(_HERE, "scale.s"), cpu="5475", cave_addr=SCALE_AT), # the DRAM unit: the platform runtime (KEYS_AT / SCALE_AT reached as fixed addresses) - Linked("qz", os.path.join(_HERE, "quantizer.s"), cpu="5475", dram=True), + Linked("qz", os.path.join(_HERE, "quantizer.s"), cpu="5475", dram=True, include=_qz_include), ), detours=( Detour(0x40055170, stock_guard(0x40055170, 6, "dc6543d672cdf223fc2ec7cc3c04b8a584dbd6e7bb1e3590b91cd3e469e0b324"), "qz", "qz_knob", diff --git a/sdk/octabam/modules/synth/upstream/quantizer/quantizer.s b/sdk/octabam/modules/synth/upstream/quantizer/quantizer.s index 99ffc73fb..3e83a41f0 100644 --- a/sdk/octabam/modules/synth/upstream/quantizer/quantizer.s +++ b/sdk/octabam/modules/synth/upstream/quantizer/quantizer.s @@ -143,6 +143,8 @@ | the qz_names entries, which the linker resolves. .text + .include "remix.inc" | 2.11: HAVE_SYDRUM -- 1 when SY DRUM is in the remix (manifest.py); 0 + | assembles this unit byte for byte as 2.10's .global qz_knob, qz_plock, qz_chrom, qz_draw, qz_ld_entry, qz_ld_line, qz_wr .global qz_get, qz_set, qz_lbl_scale, qz_scale .global qz_get_glide, qz_set_glide, qz_lbl_glide, qz_leg1, qz_leg2 @@ -208,7 +210,11 @@ qz_glide_of: | (the LFO page's slot 2: Part + 0x8ee9a + track*24 + 2) is 2..4, else 0 | (EQ). Preserves everything but d0. qz_polytrack: + .if HAVE_SYDRUM + jbsr qz_fmtrack | (2.11) VOIC is FM's: a SY DRUM track stays mono + .else jbsr qz_is_synth + .endif jbeq qz_pt_ret move.l %a0,-(%sp) move.l %d1,-(%sp) @@ -240,11 +246,22 @@ qz_pt_out: qz_pt_ret: rts -| qz_is_synth: d0 := 1 (NE) when track d2's machine is FLEX and its assigned +| qz_is_synth: d0 := 1 (NE) when track d2's machine is FLEX and either the FM +| SYNTH machine is chosen (the Part's "FM", 1) or its assigned | FLEX slot's settings record names a SYNTH* file -- the synth page's own test | (modules/synth/page.s pg_resolve): slot = Part + 0x8f04a + track*5 + 1, its | record 0x100b14f0 + 0x448*slot, the path at +0 scanned for the basename. | Preserves everything but d0. + .if HAVE_SYDRUM +| qz_fmtrack (2.11): d0 := 1 (NE) when track d2 plays FM (qz_is_synth's 1: FM SYNTH chosen +| or a SYNTH* marker), 0 for SY DRUM (2) and sample tracks. Preserves everything but d0. +qz_fmtrack: + jbsr qz_is_synth + subq.l #1,%d0 + seq %d0 + andi.l #1,%d0 + rts + .endif qz_is_synth: lea -16(%sp),%sp movem.l %d1/%d3/%a0/%a1,(%sp) @@ -258,20 +275,34 @@ qz_is_synth: mvz.b (%a1,%d2.l),%d0 | the track's machine subq.l #1,%d0 | FLEX jbne qz_is_no - move.l %a0,%a1 - move.l %d2,%d0 - mulu.w #30,%d0 + move.l %a0,%a1 | the FM SYNTH machine chosen in the machine list + move.l %d2,%d0 | (the synth module's machine.s): "FM", 1 in the + mulu.w #30,%d0 | track's NEIGHBOR column, Part + 0x8edbc + 30*track adda.l %d0,%a1 adda.l #0x8edbc,%a1 + .if HAVE_SYDRUM + mvz.b 2(%a1),%d0 | (2.11) or SY DRUM (sy-drum/): "SY", 1 -- its PTCH is semitones + subq.l #1,%d0 | too, its keys and locks as FM SYNTH's + jbne qz_is_unsigned + mvz.w (%a1),%d0 + cmpi.l #0x464d,%d0 | "FM": 1 + jbeq qz_is_fmsig + cmpi.l #0x5359,%d0 | "SY": 2 + jbne qz_is_unsigned + moveq #2,%d0 + jbra qz_is_out +qz_is_fmsig: + .else mvz.w (%a1),%d0 - cmpi.w #0x464d,%d0 - jbne 1f + cmpi.l #0x464d,%d0 + jbne qz_is_unsigned mvz.b 2(%a1),%d0 - cmpi.b #1,%d0 - jbne 1f + subq.l #1,%d0 + jbne qz_is_unsigned + .endif moveq #1,%d0 jbra qz_is_out -1: +qz_is_unsigned: move.l %d2,%d0 lsl.l #2,%d0 add.l %d2,%d0 | track * 5 @@ -909,6 +940,18 @@ qz_g2_mono: jbsr qz_chain_of | legato: the new step continues the held note's chain lea HELD,%a0 move.b %d3,(%a0,%d2.l) | legato: the new key is the held key + .if HAVE_SYDRUM + jbsr qz_is_synth | (2.11) a synth track: the engine's po_legkey (-28) hears the key -- + jbeq qz_g2_mono_mark | SY DRUM recharges its envelopes at the key's trigless frame (FM and + movea.l qz_clock,%a0 | samples: nothing); the engine there (its clock published)? + move.l %a0,%d0 + jbeq qz_g2_mono_mark + movea.l KIND_FLEX,%a0 + movea.l -28(%a0),%a0 + move.l %d3,%d0 + jsr (%a0) +qz_g2_mono_mark: + .endif lea qz_legato(%pc),%a0 move.b %d3,(%a0) | ... and the live recorder records it as trigless (qz_leg3) qz_g2_trigless: diff --git a/sdk/octabam/modules/synth/upstream/synth/README.md b/sdk/octabam/modules/synth/upstream/synth/README.md index df115d416..287a76c48 100644 --- a/sdk/octabam/modules/synth/upstream/synth/README.md +++ b/sdk/octabam/modules/synth/upstream/synth/README.md @@ -1,5 +1,119 @@ # Synth machine (phase 5: paraphonic chords, the engine in DRAM; phase 4: glide; phase 3: the page; phase 2: the FM voice; phase 1: the hollow voice) +**Octatrick 2.12 (tag `v2.12`; 10 - 11 Oct 2026): the metronome no longer +releases a held note.** With the metronome on and the sequencer playing, a held +CHROMATIC key or chord on a synth track was released at the next click as if it +had been let go (at REL INF: cut within 5 ms), and every sequencer note was +released at the next click too. The engine had hooked the metronome's click +word, taking it for the sequencer's STOP word (2.9's `po_stop`); the hook is +gone, and STOP still ends the voices at the stock voice kill. Details: "The +metronome no longer releases a held note (2.12)" below. Grid steps and played +notes were measured at the same level (a grid trig, a held key and a +live-recorded trig of the same note at the same VOIC); what makes steps sound +softer -- HOLD, VOIC, the same pitch repeated at VOIC 2..4 -- is in "Grid steps +and live keys: the same level (2.12)" under "6 dB of headroom"; no level +changed. **And a machine change resets the track's machine parameters to +init:** a machine change to or from FM SYNTH / SY DRUM (either list, or a +marker file) resets the track's PLAYBACK, PLAYBACK SETUP, AMP, LFO and their +SETUP pages to the new machine's init; step and scene locks are kept, as in +the stock firmware; FX slots are kept. Details: "A machine change resets the +track's machine parameters (2.12)" below. + +**Octatrick 2.10 (tag `v2.10`; 5 - 8 Oct 2026).** **FM SYNTH is in the +machine list** (8 Oct): the sixth row of SRC SETUP (FUNC + SRC) and of SELECT +MACHINE TYPE on every track, playing with no sample and no marker file -- see +"Selecting FM SYNTH from the machine list (2.10)" below (adapted from +Modwerk's FM Synth module, MIT, Modwerk contributors). Four changes on the +synth: DEC puts HOLD at 127 (5 Oct); the note-start click is gone (attacks of +at least one carrier period, S-shaped); the LFO destination list speaks FM; +a held CHROMATIC key is the gate (HOLD is for sequencer trigs). And three +fixes after the author's test of the first 2.10 build on his MKI (7 Oct): a +note restarted while it still sounds no longer clicks at a short DEC; a fast +run at VOIC 2..4 no longer loses notes; a live-recorded legato phrase no +longer goes silent on playback. And the engine leaves 6 dB of headroom (8 +Oct): VOL 0 is the reference, VOL up to about +6 stays clean. Each below. + +**2.10: DEC puts HOLD at 127 (5 Oct 2026).** The +PLAYBACK page's DEC knob read `HOLD` at raw 0, then the shortest decay at 1 +up to `2.0s` at 127; HOLD (the index envelope holds: the maximum sustain of +the decay) is now the LAST position on the right. **127 = `HOLD`** (the +index holds for the whole note; the paraphonic k `T_DK` is 0), **0 = the +shortest** (k = `K_MAX`, the instant decay; the page prints `0`), and **1..126 +are unchanged** -- the same k (`K_NUM / raw²`, capped at `K_MAX`) and the same +milliseconds as before, so a saved DEC other than 0 or 127 sounds the same; a +saved 0 (was HOLD) now decays at once and a saved 127 (was `2.0s`) now holds. +The icon follows: a flat top at 127, the shortest fall (L = 2 columns) at 0. +Sites: `poly.s` `sy_env_k` (the mono voice) and the paraphonic frame's RTIM +-> `T_DK` (`po_fr_hold`); `page.s` `pg_fmt_decay` and `pg_ic_decay`. The page +cave stays **1,812 B** (two short branches pay for the compare; `PINNED_PAGE` +re-ratified); the DRAM unit grows 18,452 -> 18,472 B. FUNC + detent from 0 +now steps 0 16 32 .. 112 127 = HOLD at the end. The measured records below +predate this and keep the old law (DEC 0 = HOLD). + +**2.10 also (6 Oct 2026): the note-start click is gone.** Tim heard a click +at the start of FM notes ("noticeable if you turn ratio all the way down, and +play lower octaves"). The cause (measured on the emulator): the 16-frame linear attack +(5.8 ms) is a fraction of one carrier period below about C3 and ends at an +arbitrary phase, so the onset and the corner at its end put 17..35 dB more +energy at 100 Hz..1 kHz than a dark low note (RATO 0.25) carries. Every attack +now lasts at least one carrier period and is S-shaped; a warm START's index +ramp lasts at least a period; a START at another pitch on a sounding mono note +crossfades; a stolen voice fades over a period, and a chord-memory or stolen +voice that must jump UP in pitch fades out first and starts its new note cold. +**What you hear differently:** attacks below C3 are slower -- C1 reaches -3 dB +in 20 ms and full level in 31 ms, C2 in 10 / 16 ms, C3 (F2..F3) in 26 frames += 9.4 ms, from F3 up as before (ATK 0: 6.2 ms, was 5.8) -- and every attack is +S-shaped (the same total time, within 6 %, for every ATK); at VOIC 2..4 a +chord whose voices must move UP while all of them sound starts each such note +one period of the note it replaces late (from a C1: up to 31 ms; from a C3: +7.6 ms). Details and numbers: "The note-start click (2.10)" below. + +**2.10 also (6 Oct 2026): the LFO destination list speaks FM.** On a synth +track LFO SETUP's PMTR lists the FM SYNTH page's own names -- `PTCH RATO INDX +FINE FDBK DEC`, then AMP, then `SPD1 SPD2 DEP1 DEP2`, then FX1 and FX2 -- where +it printed the Flex names (`STRT LEN RATE RTRG RTIM`) and offered `SPD3` / +`DEP3`, which are VOIC and CHRD on a synth track: the knob steps over those +two. Every other track's list is unchanged. Details: "The LFO destination list +(2.10)" below. + +**2.10 also (6 Oct 2026): a held key is the gate.** A CHROMATIC key held on a +synth track sustains for as long as it is held and starts the release (REL) +when it goes up; HOLD now gates sequencer trigs only (and MIDI notes, as +before). Until now a live key's note ended at HOLD even with the key still +down (HOLD 32 at 120 BPM: 282 ms). LEG MONO / POLY behave as before, STOP +still ends everything, and the live recorder still records the played length. +Details: "A held key is the gate (2.10)" below. + +**2.10 also (7 Oct 2026): three fixes from the author's test of the first +2.10 build.** Four things were heard on the MKI; the emulator showed three +causes, and all three are fixed: +- **A click at the start of a note restarted while it still sounds** + (a quick re-press of a CHROMATIC key, the same note again in a fast run, a + sequencer trig on its own tail, a recorded legato phrase played back) at + DEC 0, louder the higher INDX, gone with DEC turned up a little. Such a + START ramped the index back to full and DEC 0 then dropped it to its floor + within one 16-sample frame. Now the index envelope restarts as a fresh + note's and the index itself ramps over at least one carrier period: at DEC + 0 a restarted note sounds like a fresh one. +- **Notes that did not sound** in a fast run at VOIC 2..4 over a long REL: a + second key pressed while a voice faded for the first key took that same + voice and dropped the first key's note. A voice holding a waiting note is + now taken last. +- **A live-recorded legato phrase went silent on playback** at its first + legato step (LEG MONO at VOIC 1, LEG POLY at VOIC 2..4): the recorded + trigless step made the stock renderer walk the synth's marker sample to its + end, which ended the voice. The step now keeps the voice. +Not reproduced on the emulator: notes not sounding at REL 0 with the +sequencer running, and notes jumping up an octave. Details: "The author's +test of the first 2.10 build (2.10)" below. + +**2.10 also (8 Oct 2026): 6 dB of headroom.** The engine's output is 6.02 dB +lower than before, on the mono voice and on every paraphonic voice alike (one +arithmetic shift right before the sum): VOL 0 is the reference, VOL up to +about +6 stays clean, and a VOIC 4 chord's coincident peaks reach full scale +instead of twice it. The relations between VOIC 1..4, the envelopes and the +attacks are unchanged. Details: "6 dB of headroom (2.10)" below. + **OCTATRICK2.9 (28 Sep 2026, later): FINE defaults to 0c when a track becomes a synth track.** A track that is made a synth track -- a FLEX track given an `FMSYNTH*` / `SYNTH*` slot in the machine window or the sample-list @@ -87,7 +201,9 @@ power** -- every shape but `----` plays 2 / 3 / 4 notes at VOIC 2 / 3 / 4 (its own notes first, then octave doublings), a start with a shape is chord memory (the previous chord goes), a voice is `1 / sqrt(VOIC)` of the mono voice (-3 / -4.8 / -6 dB) and a soft limiter takes the in-phase excess -- -**"VOIC, CHRD, and the mono voice"** below. +**"VOIC, CHRD, and the mono voice"** below. (2.10, 8 Oct 2026: every level +the engine puts out is 6.02 dB lower, the relations unchanged -- "6 dB of +headroom (2.10)".) Phase 5 (24 Sep 2026) moves the FM voice engine into a DRAM unit (`poly.s`, the ROM cave `synth.s` kept for the record) and makes the synth paraphonic: the LFO page's VOIC slot (1..4) gives a synth track that many voices playing @@ -105,11 +221,511 @@ FM voice"** below has the design, the parameter map and the numbers. Phase 1's text follows it as written (its record-layout guess is corrected in the phase-2 section and marked in place). +The measurement sections below name the scripts, logs, renders and shots +they came from; those files are in the author's workspace, not in this +repository. Each section states its method (the build, the emulator mode, +the card and the settings) and its numbers. + +--- + +## The metronome no longer releases a held note (2.12, 10 Oct 2026) + +**What the author heard** (his MKI, the 2.11 engine as Modwerk's FM Synth +0.1.4): with the metronome playing, a held note or chord was cut as if it had +been released while the keys were still down. + +**The cause.** BUILD 32 (2.9) added `po_stop`, a detour at the frame +builder's `clrl 0x46c80350` (0x4000b2c8), on the reading that the global +mailbox word 0x46c80350 was the sequencer's STOP / restart word and the byte +the builder posts from it (0x46104d14) the DSP's all-off. Both are the +METRONOME's. While the sequencer plays, the beat code writes the word -- 3 on +the first beat of each bar (0x4009c3a0 at PLAY, 0x400a4d8c), 1 on the other +beats (0x400a4d96); 0x4009bbb8 / 0x4009c3aa are the transport-start forms -- +and every writer sits behind `tst.b 0x80000060`, the project's +METRONOME_ENABLED (parsed at 0x40087c20, edited in PROJECT > CONTROL > +METRONOME). The frame builder turns a nonzero word into the DSP-wide command +byte 0x46104d14 = 0x10 | [0x80001810] (0x30 | .. on the accent) -- the click -- +and clears the word at 0x4000b2c8. So `po_stop` ran at every click and at +nothing else (BUILD 33 measured that it never ran at a STOP: the metronome was +off on that rig). At every click it set S_ON bit 1 (released) and bit 2 +(STOPPED: REL INF takes the 1 ms floor) on every synth track, moved every +sounding paraphonic voice to its release and dropped the notes waiting for a +fading voice -- with the keys still held. A held key or chord was released at +the next beat through the lane's REL (at REL INF cut within 5 ms), and every +sequencer note was released at the first beat after it. SY DRUM tracks (the +same engine record) were released the same way. + +**The fix.** `po_stop` and its detour are removed: 0x4000b2c8 is stock again +and the engine no longer touches the metronome's word (`GLOBAL_WORD` in +`poly.s` is documented as the click word). STOP, a pattern change and the +loaders end the engine's voices where they have since BUILD 33, at the stock +VOICE KILL (`po_kill`, which also drops the waiting notes). Nothing sets S_ON +bit 2 any more, so its two readers (`po_mono_env`'s and `po_frame`'s REL INF +floor) never fire; they are left in place. The DRAM unit shrinks 20,980 -> +20,860 B; every other instruction is unchanged (the two disassemblies differ +only in addresses). + +**Measured** (the pinned ot_emu, DSP lockstep, the stream in TRACKS mode so +track 1's own stem is measured without the click; the v2.11 FM-only octabam +image against the same build of this change; T1 = FM SYNTH, C4, RATO 2.00 +INDX 60 DEC HOLD; 120 BPM, the sequencer playing; the CHROMATIC key C4, or C3 +E3 G3 C4, held 9.1 s = 18 beats; the metronome on (MAIN 64, CUE 64) or off): + +| held, AMP | 2.11, metronome on | 2.12, metronome on | +|---|---|---| +| key, VOIC 1, ATK 0 HOLD 14 REL 98 | released at the first click (S_ON 1 -> 7, `po_stop` 24 times in the session): -30.0 dBFS in the first 0.5 s, -47.0 at 2 s, silent from 4 s with the key down | S_ON 1 and S_GAIN full throughout, -29.45 dBFS to the key-up; the stem identical sample for sample to the metronome-off render | +| key, VOIC 1, HOLD INF REL INF | cut to silence at the first click | identical to metronome off | +| chord, VOIC 4, HOLD 14 REL 98 | all four voices released at the first click, silent from 4 s | four voices sounding throughout; identical to metronome off | + +The click still sounds: the main mix minus the metronome-off render holds one +click per beat (18 in the 9.1 s, -12.0 dBFS peak); the frame builder posted +the click command at each of the session's 24 beats (0x10 | n at 0x4000b2ac; +on the 6 accents 0x30 | n at 0x4000b2c2) and its stock clear at 0x4000b2c8 ran +24 times. +Grid notes with the metronome on (HOLD INF REL INF): 2.11 released each at the +next click (-2.6 dB at +500 ms, silent at +1 s); 2.12 holds them at 0 dB for +the whole 2 s loop, as with the metronome off. STOP, with the metronome on and +off: a held REL INF key, or a HOLD INF grid note, sounds until the stock voice +kill 0.74 s after the press (the rig's stop timing, 0.75 s on 2.11); at the +kill S_ON, S_GAIN and S_GPREV are 0, the stem stays below -69 dBFS after it, +and the next key starts cold (S_GPREV 0 before its START). + +Unchanged: Modwerk's synth harnesses (`gate.cpp`, `lfo_regression.cpp`) pass +with the PCM and state identical to 2.11's and the same cost; the note-start +click cards of 2.10 (the onset splatter per note, nine cards) read the same on +both images within 0.1 dB, and the 2.10 author's-test cards (taps, runs, +sequencer and live-recorded phrases at VOIC 1 and 4) find no onset at +10 dB +or more on either; five SY DRUM renders (the DEC, LFO, PTCH and lock cards) +are identical sample for sample; the machine-list walks (FM SYNTH alone, FM +SYNTH and SY DRUM, the chooser) and a Part save / reload walk are +pixel-identical with every peeked byte equal. + +## Selecting FM SYNTH from the machine list (2.10, 8 Oct 2026) + +FM SYNTH is a machine of its own now: the sixth row of the track's machine +lists, under STATIC FLEX THRU NEIGHBOR PICKUP. **No marker file and no sample +are needed.** The `FMSYNTH*.wav` / `SYNTH*.wav` marker files still work exactly +as before; the machine list is simply the easier way in. + +**To select it** (any track, T1..T8): + +- **SRC SETUP:** hold FUNC and press SRC (the PLAYBACK page key). The machine + list is on the left; go DOWN to `FM SYNTH` (the row after PICKUP) and press YES. The + right-hand side then shows the FLEX setup values the track keeps (LOOP, SLIC, + LEN, RATE, TSTR, TSNS -- all OFF on a fresh FM SYNTH track). +- **SELECT MACHINE TYPE:** press the track key twice quickly (the QUICK ASSIGN + slot list), press LEFT on `<< MACHINE`, go DOWN to `FM SYNTH`, press YES. + +The PLAYBACK page then reads `PTCH RATO INDX FINE FDBK DEC`, the footer +`FM SYNTH>FM SYNTH`, and both lists open on the `FM SYNTH` row for that +track. A track that was not playing the FM voice before starts from PTCH 0, +RATO 1, INDX 32, FINE 0c, FDBK 0, DEC 40 (199 ms). Choosing FM SYNTH again +keeps your patch, and so does choosing it over a track that already plays FM +through a marker file (its patch and its setup values are kept; only the +machine-list mark is added). + +**To go back** choose FLEX or STATIC (or any other machine) in either list. +The mark is removed and the track is the plain machine again, at that +machine's init (since 2.12: "A machine change resets the track's machine +parameters", below; the locks stay). If +the FLEX slot holds a marker file, the track still plays FM through it. + +**What is stored.** The machine byte is FLEX (1), so every stock FLEX path -- +the PLAYBACK lanes, locks, LFOs, scenes, the DSP voice -- stays valid; the +mark is the three bytes `F`, `M`, 1 at the start of the track's NEIGHBOR +PLAYBACK column (the Part + 0x3c + 30 * track; the bank blob + part * 6322 + +0x8edbc + 30 * track), written into the Part and its battery-RAM shadow. A +FLEX track never uses those bytes and the stock Part validator's NEIGHBOR +range is 0..127, so the mark survives SAVE, RELOAD, Part COPY / PASTE and a +project load like any other Part byte. Part CLEAR clears it with the rest. +The quantizer (`quantizer/`, the same tag) knows the mark too: CHROMATIC keys, +the tuning system, LEG and the live recorder treat a chosen track as a synth +track. Build both modules from this tag together; an older quantizer treats a +chosen track as a sample track. + +**On a stock OS, or a build without this change,** such a track is a plain +FLEX track: with an empty slot it is silent; with a sample in its slot it +plays that sample with the FM values as STRT / LEN / RATE / RTRG / RTIM (RATE +64 is half speed); the mark is ignored. **Before downgrading, choose FLEX (or +STATIC) on every FM SYNTH track and SAVE**, or keep a marker file in the slot +if you want those tracks to stay FM on an older Octatrick build. + +### How it works + +The chooser is adapted from Modwerk's FM Synth module ([repeat98/modwerk](https://github.com/repeat98/modwerk), +`sdk/octabam/modules/synth`, commit 1c1d938; MIT, Modwerk contributors), whose +chooser is derived from Modwerk's Analog BD chooser hooks. That module's +LICENSE states its own terms, separate from the repository root's licence; its +attribution lines, carried in this repository's [LICENSE](../LICENSE): + +> Dedicated chooser, Part validation, sample-free transport and original presentation: +> +> MIT License +> +> Copyright (c) 2026 Modwerk contributors +> +> Portions of the firmware analysis tooling originate from the octamax project +> (https://github.com/mxldyn/octamax), Copyright (c) 2025-2026 Maxolydian, +> also under the MIT License. + +and, for the Analog BD chooser it derives from, `Copyright (c) 2026 Sam Banks` +(MIT, the same octamax sentence). + +`synth/machine.s` (a DRAM unit, 956 B) holds the chooser: Modwerk's +`machine.s` and its `registration.c` helpers written out in assembly. The +lists grow from five rows to six (six pokes: `pea 5 -> 6` at `0x40079248` and +`0x400585fa`, `moveq #4 -> #5` at `0x4003c950`, `0x40078678`, `0x400786ce`, +`0x40079904`); row 5 prints `FM SYNTH` (`0x400334d8`; SRC SETUP's name table, +`0x4003c928`, copied from the stock one at run time); a chosen track's row is +highlighted and the lists open on it (`0x4003c980`, `0x400786c8`, +`0x400585dc`, `0x40078886`); SRC SETUP's editor and drawer address row 5 as +FLEX (`0x4003a52e`, `0x4003cd98`); a track's machine name reads FM SYNTH +(`0x4003d718`); the UI tick (`0x4005221e`) puts the FM SYNTH page into the +PLAYBACK descriptor table's spare slot 5 for SRC SETUP. The commits: the +machine window's machine-byte write shares the FINE-0c detour at +`0x40079816` (`fm_main_commit`, then `po_machwin`); SRC SETUP's two writes +`0x4005a616` / `0x4005a850` (the second follows `po_machlist`'s detour, which +returns to it). Row 5 is stored as FLEX plus the mark; any other row clears +the mark. The first choice's seed is written where stock's reload of a track +from its Part (`0x40001f18`) writes: the Part and its shadow, the live lane's +six PLAYBACK bytes and its SETUP bytes (lane + 32), the six PLAYBACK value +words the frame builder hands the DSP and the engine (`0x80000a50 + 64 * +track`, byte << 8), and the slew counters of those bytes cleared +(`0x80000db4 + 32 * track`). The frame builder refreshes the words from the +Part only at a trig from another bank or Part (`0x4000c0b4`), so without them +a track that had already played in this Part kept its sample's words: FM +SYNTH chosen after a sample at RATE 127, STRT 0 played C4 as 67.8 Hz (FINE ++63c, RATO 0.25) under a page reading FINE 0c, RATO 1 (fixed 8 Oct 2026, see +Measured). The Part validator (`0x40002318`) clamps FLEX's PTCH into 4..124, +where FM SYNTH's PTCH is 0..127 (-64..+63 semitones): for a chosen track the +twelve FLEX bytes sit the validation out (the stock defaults in their place, +read from the descriptor, the FM bytes back afterwards). A marker track keeps +the stock clamp, as before. + +Playing without a sample (in `poly.s`): the kind table's FLEX START callback +(`0x400d6458`, a SymbolRef) is `po_fmstart`, which starts a chosen track's +voice without consulting a sample (the CF voice marked active, so the stock +STOP and kill end it; 0x100, a cold start, for the DSP) and calls the stock +callback for every other FLEX track; the frame builder's second supplier call +(`0x4000d514`, `po_fmsource`) gives a chosen track `sy_render` where an empty +FLEX voice would get stock's silent supplier; and `sy_call` writes the source +header stock would have written (count, ring position, rate 1.0, read +position) with silent pairs for the engine to fill, instead of calling the +stock renderer. `po_fmstart` answers as the stock callback does: 0x100 (a cold +start) for a voice that was not running, 0 for one that runs on. Modwerk's +transport answered 0x100 to every START; on this engine that restarted the +DSP's stream under every warm note (a retrigger or a new key on a sounding +note: +40..57 dB of onset splatter in the note-start click table, where a +marker track measures -2..+28 dB), so the port answers 0 there, as stock does +for a START that continues its voice. `po_is_synth`, the page's `pg_resolve`, the VOIC/CHRD page +(`po_lfopage`) and the voice start test the mark ahead of the marker scan. +The page cave gained `fm_descriptor` (16 B, first; `pg_resolve` moved to +16) +and the mark test: 1,868 B (was 1,812), `PINNED_PAGE` re-ratified, inside the +same zero run (`0x400d24d0..0x400d2ce0`). + +Changed from Modwerk's module: one detour at `0x40079816` does both the FINE +rule and the commit (Modwerk dropped the FINE-0c detour); `pg_resolve` is the +only PLAYBACK route (their second resolver detour at `0x40031e74` is not +needed); their name hook at `0x4004c36a` is left out (`a0` is not a machine +byte there, so it never changed a name); no refusal path; the voice start +tests the mark only and keeps this engine's own scan of the starting voice's +sample (a sample lock to a real file on a marker track stays a sample); a +marker track keeps its patch when FM SYNTH is chosen over it; the START +callback answers 0 for a voice that runs on (above); the six-row name table is +copied from the stock one at run time instead of carrying its five addresses. + +### Measured (8 Oct 2026, ot_emu, the quantizer + synth image built from this change through the panel; T1 on the 2.10 click and gate cards) + +- **Selection.** SRC SETUP and SELECT MACHINE TYPE list FM SYNTH as the sixth + row on T1..T8. Chosen on T1 with an empty FLEX slot: the Part's machine byte + 01, the mark `46 4d 01` in the Part, the FLEX PLAYBACK bytes `40 0c 20 40 00 + 28`, the SETUP bytes 0; the page reads PTCH RATO INDX FINE FDBK DEC and the + footer FM SYNTH>FM SYNTH; the pattern's trigs sound at C4 (spectral peak + 262.5 Hz, 2.5 Hz bins) with no sample anywhere in the slot. Both lists then + open on FM SYNTH; FLEX chosen again clears the mark and the page reads FLEX. +- **A sample track** (fixed 8 Oct 2026). T1 = FLEX with a sine in its slot, + PB 64 0 127 127 0 79 and the stock SETUP (LOOP ON, TSTR AUTO): the sample + plays, then FM SYNTH is chosen in SRC SETUP: every note is C4, 261.7 Hz + (autocorrelation; 261.9 Hz spectral peak), sample for sample the render of + FM SYNTH chosen on a track that never played. The click table's note-start + measure on its C4 notes (cold and warm) equals a marker track with the same + patch to 0.0 dB. Before the fix every note was 67.8 Hz: the seed reached + the Part and the page but not the value words the voice reads, so the + sample's STRT 0 (RATO 0.25) and RATE 127 (FINE +63c) played on (the first + build's own render of this route peaked at 271.2 Hz, not C4; its sample had + LEN 0, INDX 0). FLEX chosen again: the sample plays with what the Part and + the page then hold (STRT 12, LEN 32, RATE 64, SETUP 0), sample for sample + (to 1 LSB) a project loaded with that Part -- before the fix it played the + stale values until the next reload. The FLEX + STATIC render before FM + SYNTH is sample for sample the build without this change. +- **A marker assigned after a sample** (the FINE rule below): T1 as above, + the sample plays, then QUICK ASSIGN slot 1 (`SYNTH.WAV`): C4 at RATO 0.25, + FINE 0c = 65.4 Hz (before the fix 67.8 Hz: FINE +63c under a page reading + 0c). +- **A marker track** (FLEX slot 1 = `SYNTH.WAV`, PB 64 20 50 64 30 80): FM + SYNTH chosen over it keeps the PB and SETUP bytes and adds the mark; the + render before and after is the same to 0.00 dB (10 ms RMS envelopes). +- **SAVE and reload.** FM SYNTH chosen, INDX 32 -> 42, PROJ > SAVE > YES > + YES; a cold boot of that card: machine FLEX, the mark, INDX 42, the page + FM SYNTH, both lists on FM SYNTH, the voice at C4. +- **Part COPY / PASTE / CLEAR.** Part ONE with FM SYNTH on T1, copied + (FUNC + REC in the PART chooser) and pasted into THREE (FUNC + STOP): THREE's + T1 is FM SYNTH (mark, patch, sound); CLEAR (FUNC + PLAY) of THREE: T1 + STATIC, no mark, the stock defaults. +- **The note-start click table** (the 2.10 referee: onset splatter per note + over 16-step patterns, cold and warm, retriggers and voice steals, + RATO 0.25 / 1, INDX 0 / 40 / 100, FDBK 0 / 60, ATK 16) on a marker track: + every row within 0.1 dB of the 2.10 build before this change. **The same + Parts on a machine-list track with an empty slot: every row within 0.2 dB of + the marker track** (before the START answer was fixed: +40..+57 dB on every + warm row, cold rows equal). Re-run on the image with the 8 Oct seed fix: + chooser rows within 0.1 dB of the build before it and within 0.2 dB of the + marker track; 23 of the 31 click and gate renders sample-identical to the + build before it, the rest equal to 1 LSB or up to the panel-timed key + events of the held-key renders. +- **The key gate** (a held CHROMATIC key, a sequencer trig, LEG MONO, a VOIC 4 + chord) and the attack table, marker vs machine-list track: the held key + sample-identical up to its release (the release lands up to 2 ms apart, the + panel's timing), the sequencer trig and the C4 pattern sample-identical, LEG + MONO within 0.4 dB (100 ms envelopes), the VOIC 4 chord within 0.16 dB (100 ms; + the voices' phases beat up to 3.8 dB in 10 ms windows, as between any two + runs), ATK 0 / 16 / 20 / 32 = 11.07 / 14.01 / 24.24 / 51.04 ms on both. +- **Unchanged elsewhere.** The LFO destination list and the rest of the 2.10 menu + walk: 171 of 171 screenshots pixel-identical to the build before. The engine harnesses (the + gate and LFO regression suites): pass, PCM and state identical to the build + before; cost per frame mono 1,165.9 -> 1,234.4 instructions (mean; peak 2,556 + -> 2,658), VOIC 4 4,061.9 -> 4,130.4 (peak 6,390 -> 6,492): the mark tests. + poly.s 20,560 -> 20,976 B (32 of them the FINE word, below), machine.s + 956 B; every source assembles at -mcpu=5475 and 54455. + `tools/stock_scan.py`: no stock bytes beyond the displaced instructions and + the idioms the tool allows -- 35 runs against 32 before; the three new + ones are the Part-address idiom `movea.l 0x46c82456; mvz.b 0x100b14cf; + move.l #6322; muls.l` twice in machine.s (16 B each, the second with other + registers; the same idiom as poly.s's own run) and an 18-byte save/restore + idiom in poly.s (the same stock list as five of the runs before). + +Not tested: hardware; MIDI notes, the live recorder and scenes on a +machine-list track (they read the same `po_is_synth` / `qz_is_synth`); +eight machine-list tracks playing at once. + +--- + +## The machine list for more than one machine, and SY DRUM's hooks (2.11, 8 Oct 2026) + +2.10 added one row, FM SYNTH, by poking the stock list code's constants from five rows to six. 2.11 makes the +rows after PICKUP a table so that another machine module running on this engine -- SY DRUM (`sy-drum/`) -- gets +its own row without a site of its own, and the two modules never write the same stock bytes. + +- **The row table** (`machine.s`, `ml_rows`): one row descriptor a machine -- its signature (two letters and a + version: `F`,`M`,1 / `S`,`Y`,1, in the first three bytes of the track's NEIGHBOR PLAYBACK column, as 2.10), the + engine kind the engine's `po_signed` answers for it (1 FM, 2 SY DRUM), its name, its twelve seed bytes, the routine + that returns its page (SRC SETUP's right half; written into the PLAYBACK descriptor table's spare slots 5 and 6 + by the UI tick) and a flag for machines whose SETUP bytes are their own (leaving the row restored FLEX's; since + 2.12 every change from a row resets the FLEX bytes, and the flag is not read). The + table is assembled per remix: FM SYNTH always, SY DRUM's descriptor (`sd_row`, its own unit) when the remix + carries SY DRUM. Choosing, highlighting, naming, opening on, editing and validating a row all read the table. +- **The row count is the remix's.** The six constants 2.10 poked (the two lists' row counts, SRC SETUP's name + lookup, the machine window's drawer, highlight and cursor bounds) are six detours now, assembled with + `5 + ML_COUNT` and `4 + ML_COUNT`: 6 rows with FM SYNTH alone, 7 with SY DRUM. +- **SELECT MACHINE TYPE scrolls.** Its window shows six rows; its row drawer drew `name(row)` for the visible rows + with no scroll offset, so a seventh row was never drawn, and five copies of a row clamp (`0..5`) after each list + move put the cursor back on the sixth row (a study of the list code found the drawer, the development line's machine-list build the clamps, 8 Oct 2026). + The drawer draws `name(row + top)` now (one 12-byte detour at `0x40078678`), the highlight compares `row + top`, + and the five clamps keep `0 .. the last row` (five 8-byte detours, `ml_clamp1..5`). With six rows the top stays + 0 and the clamps keep 0..5: stock's behaviour. +- **One include, two flags.** `poly.s`, `machine.s` and the quantizer include a `remix.inc` the build writes per + remix (octabam's `Linked.include`; `manifest.py` `remix_include`): `HAVE_SYDRUM` (1 when SY DRUM is in the remix) + and `engine_abi.inc`, the equates SYNTH MACHINE shares with SY DRUM (one copy). With `HAVE_SYDRUM` 0 the engine + (`poly.s`) assembles byte for byte as 2.10's and the quantizer as 2.10's. +- **The engine's SY DRUM call-outs** (`.if HAVE_SYDRUM`, read from the development line's engine and re-placed in this + one): a START stores the engine in `sy1_kind[t]` (0 FM, 1 SY DRUM; a change starts cold, its voices freed) and a + SY DRUM START runs that engine's trigger and takes no crossfade; its frame, its render, its LEG MONO recharge and + its SETUP / LFO tick are SY DRUM's routines; FM's onset law, FINE, VOIC / chords, fingered-chord recording, the + paraphonic LEG modes and LFO 3's muting stay FM's (`po_is_fm`). `po_is_synth` is true for both machines (the keys, + MIDI IN, the tuning system). +- **ANALOG BD** (octabam's `modules/analog-bassdrum`) owns the same chooser sites; the manifest declares the + conflict so octabam names the reason. + +## A machine change resets the track's machine parameters (2.12, 10 Oct 2026) + +The author's request: "if i change that track to be another machine, like from FM synth to SY DRUM, all the +parameters, including any LFO settings, should be set to init. They should not inherit any parameters from the +last machine that was on that track." And (11 Oct 2026): "yes i want it to work the same way as stock, i just want +the machine parameters to init." + +**A machine change resets the track's machine parameters (PLAYBACK, PLAYBACK SETUP, AMP, LFO and their SETUP pages) +to the new machine's init; step and scene locks are kept, as in the stock firmware; FX slots are kept.** Whenever the machine a +track plays changes and the old or the new machine is FM SYNTH or SY DRUM -- in SELECT MACHINE TYPE, in SRC SETUP, +or through a marker file (a FLEX track given an `FMSYNTH*` / `SYNTH*` slot in QUICK ASSIGN or the sample list, a +marker track given a sample, a marker file loaded into or replaced in the track's FLEX slot) -- the track gets: + +- **PLAYBACK and PLAYBACK SETUP**: the new machine's defaults -- FM SYNTH: PTCH 0, RATO 1, INDX 32, FINE 0c, FDBK 0, + DEC 40, the setup OFF; SY DRUM: PTCH 0, MODE A, WDTH 64, SWEP 0, SPED 64, DEC 64, LSPD 64, LDEP 0, WAVE OFF, S&H + OFF; a stock machine: the stock defaults of its page (FLEX / STATIC: PTCH 0, STRT 0, LEN 0, RATE 127, RTRG 0, + RTIM 79; LOOP AUTO, SLIC OFF, LEN OFF, RATE PTCH, TSTR AUTO, TSNS 64), and the FLEX page too when the track leaves FM + SYNTH or SY DRUM (they keep their values in the FLEX bytes); +- **AMP and AMP SETUP**: ATK 0, HOLD 127, REL 127, VOL and BAL centred, XVOL 127; AMP SETUP as a new project has it + (RTRG, SYNC ON, ATCK LIN, FX1 / FX2 ANLG, LEG OFF); +- **the three LFOs and LFO SETUP**: SPD 32 and DEP 0 for each; PMTR the first PLAYBACK knob, WAVE TRI, MULT 1X, + TRIG FREE; on FM SYNTH, VOIC 1 and CHRD `----` (LFO 3's bytes). + +These are the values a new project gives a track (the stock Part initialiser's, read from the OS's own page +records at run time; nothing is copied into this repository). FX1 / FX2 -- the effects, their pages and setup -- +stay: they are effect slots, not the machine's parameters. The same machine chosen again keeps its patch (FM SYNTH +over a track that plays FM through a marker file too). A change between two stock machines (FLEX, STATIC, THRU, +NEIGHBOR, PICKUP) is untouched: stock keeps every page there, each machine with its own PLAYBACK page. Nothing is +reset by loading a project, reloading or pasting a Part, or changing patterns: only a machine change does. The +reset is written into the Part and its battery-RAM copy (SAVE keeps it) and into the track's live state, so the +next note plays it. Locks are not machine parameters and stay, as at a stock machine change: the step locks and +trigs are the pattern's, the scene locks the Part's, and both apply by position, so a lock recorded on one machine +plays on the same knob position of the new one; SY DRUM's own lock rows (LSPD LDEP WAVE S&H) stay too and are heard +again when the track returns to SY DRUM. The +LFO designer's shapes are the Part's eight designs, which any track's WAVE can pick, so they stay; the track's +WAVE is reset, so no design plays on it. + +### How it works + +`machine.s`: `fm_choose` compares the machine the track plays before the choice (`ml_ident`: 0 a stock machine, 1 +FM SYNTH -- chosen, or FLEX over a marker slot --, 2 SY DRUM) with the one it plays after it (the row's; a stock +row: 0, or 1 for FLEX over a marker slot) and calls `ml_reset` when they differ and either is 1 or 2. `ml_reset` +writes the PLAYBACK / SETUP column (the row's seeds, or the stock descriptor's defaults through the machine -> +descriptor table `0x400d5f38`), AMP and LFO pages 1 and 2 from the stock AMP and LFO descriptors' defaults (`P + +0x5e`: page 1, `+ 0x64`: page 2 -- the tables the stock Part initialiser `0x40005638` reads; the six PMTR / WAVE and +six MULT / TRIG bytes alternately from slots 6 / 7 and 8 / 9, as it writes them), into the Part, the battery-RAM +shadow and the live lane (its bytes, the page-1 value words, their slew counters): the stock machine change +(`0x400972fc`) reloads a track's lane from its Part only for PICKUP. It writes no pattern data and no scene: the +locks are untouched. The marker route: the slot assigner's hook (`po_assign`) asks `ml_became` +(the machine before and after the slot write, `ml_kindof`); the file browser's hook (`po_loadsel`) resets every +FLEX track on the slot whose file went from a sample to a marker or back (a track chosen in the list keeps its +machine whatever its slot holds) and marks the Part edited as stock's machine write does. The browser unloads +the slot before it writes the new path (`0x40025288` clears the slot's whole settings record; the load callback +`0x400226f4` calls it, then the select calls it again), so the file the slot had is read at the callback's call: +one more detour, `0x40022732` (`jsr`) -> `po_unload` (until 2.12 the hook read the cleared record, so every load +counted as "not a marker before"). The engine (`poly.s`'s +voice code) is unchanged: the FM gate is 512 / 512 with the state of 2.11 (28b11d77). + +## 6 dB of headroom (2.10, 8 Oct 2026) + +**The engine leaves 6 dB of headroom: VOL 0 is the reference, VOL up to about ++6 stays clean.** The author's decision (8 Oct 2026). Two places in `poly.s` +shift right by one bit (`HEADROOM`, 1 = -6.02 dB): the mono voice's sample +after its gain (`sy_loop`, the high word is now c / 2 at full gain) and each +paraphonic voice's contribution before it is added to the sum (`po_fi_loop`). +Every level in the engine moves down by the same 6.02 dB; nothing else +changes: + +- the relations stay: a paraphonic voice is `1 / sqrt(VOIC)` of the mono + voice (-3.0 / -4.8 / -6.0 dB at VOIC 2 / 3 / 4), the AMP envelope, the + crossfades and fades, the attack shapes and the index ramp are untouched; +- the final pass still doubles the sum into the mono format with the + saturating clamp. A VOIC 4 chord's coincident peaks, 2.0 FS before, now + reach 1.0 FS, so the clamp is a guard that a chord at full scale only + touches, where it used to clip every coincidence above 1.0 FS; +- a fading paraphonic voice under the mono voice (VOIC 2..4 -> 1 across a + note) is summed in the same format as before (`po_fill_add` halves the + mono samples into the sum, which now holds the shifted values on both + sides). + +The factor 0.5 is the per-voice law of the author's host-side renderer (in +development), so both engines meet the DSP chain at the same level. The +amount is deliberate: 6.02 dB, not the 18.06 dB (`OUTPUT_SHIFT` 3) of +Modwerk's FM Synth 0.1.2. Cost: one instruction a sample on the mono path +and one a sample per sounding paraphonic voice; the DRAM unit grows 20,976 +-> 20,980 B (two 2-byte `asr.l #1`; every other instruction, branch and +PC-relative form assembles to the same encoding). + +### Measured (8 Oct 2026, ot_emu lockstep `--dsp`, the quantizer + synth image built from this change against the same build of the commit before it; T1 on the 2.10 click and gate cards, the chooser cards for two of them) + +- **Level.** Eighteen renders, each against the same card on the build + before: -6.02 dB on every one, whole-render RMS and peak alike, the + 100 ms windows' median -6.02 dB (C4 at INDX 0 and the INDX 0 / 40 / 100, + FDBK 60, RATO 1 and ATK 16 click cards; the VOIC 2 and VOIC 4 steal cards; + the VOIC 4 chord card, its peaks -15.25 -> -21.27 dBFS; the gate cards; + INDX 0 and the VOIC 4 chord on a machine-list track too). +- **The waveform is the same, halved.** Aligned to the build before (the + captures start up to 47 samples apart), every sample of 16 of the 18 + renders is the old sample / 2 within 1 LSB of the 16-bit capture -- the + attacks, crossfades, steals and releases are unchanged, sample for sample. + The other two (one key held, four keys held) differ only where the panel's + key-down and key-up land (100 ms envelopes within 0.24 dB of the build + before, +6.02 dB applied; the release ends in the same 100 ms). +- **The click referee** (the onset splatter of "The note-start click (2.10)": + HF in the first 10 ms against the tone's own HF, both re the tone) reads up + to 4.8 dB LOWER on the quiet cards: the tone's own HF sits at -77 dB re the + tone, below the 16-bit capture's floor, and at 6 dB less level that floor + rises to -71 dB re the tone (the loud onsets read the same, e.g. the VOIC 4 + chord's C1 -41.9 dB on both). With the samples equal within 1 LSB, that is + the capture's resolution, not the engine. +- **The menu walk** (the LFO destination list, the hidden pages, the master + page): 171 of 171 screens pixel-identical to the build before. +- **Modwerk's harnesses** (`gate.cpp`, `lfo_regression.cpp`): both pass; the + gate harness's PCM is the shifted PCM (its state hash changes, the high + words halve); mean cost per render +17 instructions at VOIC 1 (1,234 -> + 1,251, peak 2,658 -> 2,690) and +64 at VOIC 4 (4,130 -> 4,194, peak 6,492 -> + 6,556): one instruction a sample per voice. `poly.s` assembles at + `-mcpu=5475` and `-mcpu=54455`, 20,976 -> 20,980 B. + +### Grid steps and live keys: the same level (2.12, 10 Oct 2026) + +**What the author heard** (the same test of 2.11): played live the synth was +loud enough, but steps entered in GRID RECORDING played back "much softer". +Later he could not hear the drop on either test image. + +**The engine plays a step and a played note at the same level.** Measured on +the pinned ot_emu (DSP lockstep, track 1's own stem; this image; T1 = FM +SYNTH C4, RATO 2.00 INDX 60 DEC HOLD; the same Part for every case): RMS and +sample peak over +20..300 ms after the note starts, dBFS -- + +| the note | VOIC 1 | VOIC 4 | +|---|---|---| +| a CHROMATIC key held | -29.45 / -26.82 | -35.47 / -32.83 | +| a grid trig (step 1, no lock) | -29.45 / -26.82 | -35.47 / -32.83 | +| a live-recorded trig played back (the take: the key held 0.5 s in REC + PLAY) | -29.44 / -26.82 | -35.47 / -32.83 | + +(AMP HOLD INF, REL INF or REL 60; the recorded rows at REL 60, so no release +tail of the previous loop sounds beside them.) The 10 ms RMS at +10 / 50 / +100 / 200 / 300 / 500 / 1000 / 1500 ms agrees within 0.13 dB between the key, +the trig and the recorded trig -- the spread of 10 ms windows on the steady +tone itself. Nothing on the trig path +carries a level: a trig has no velocity, `po_retrig` changes only the START +byte's form, and the AMP envelope, the index, VOIC and the 6 dB headroom are +read from the same live lane for both. So no level changes in 2.12. + +**What makes steps sound softer than played notes** -- each by design, and +each a setting: + +1. **The metronome (2.11 only)**: every sequencer note was released at the + next click -- fixed in 2.12 ("The metronome no longer releases a held note + (2.12)"). +2. **HOLD**: a sequencer trig is gated by AMP HOLD, a held key is not (2.10's + rule, "A held key is the gate"). At HOLD 14 (one step) REL 98 a grid note is + at 0 dB to +100 ms, then -0.7 (+200 ms), -1.7 (+300), -3.6 (+500), -8.4 dB + (+1000), while a held key stays at 0 dB. A live-recorded trig carries the + played length as its HOLD lock and plays back as the key did. For steps that + last like held keys: HOLD 127 (INF), a longer HOLD, or HOLD locks. (The + first trig of a session still ignores HOLD, an older finding.) +3. **VOIC**: each paraphonic voice is 1 / sqrt(VOIC) of the mono voice, so a + single step at VOIC 4 (-35.47 / -32.83) is 6.0 dB RMS and 11.85 dB peak below + four keys held as a chord on the same track (-29.45 / -20.98). For single + notes on a chord track: VOIC 1, or CHRD locks on the steps. +4. **The same pitch again at VOIC 2..4**: a new note starts cold on a free voice + beside the previous note's release tail (keys and trigs alike), so the two + sum at whatever phase they meet. At 120 BPM, C4, AMP HOLD 14 REL 98, VOIC 4: + a grid trig on every beat summed to -30.5 .. -31.5 dBFS RMS (the regular + grid met the tail near in phase), live taps of about the same length every + ~0.52 s to -35.3 .. -38.4 (a lone tap of that length: -35.9), and a + live-recorded note played back over its previous loop's tail read +0.4 and + +1.3 dB against its take; another tempo or pitch can make the grid's sum + the quieter one. At VOIC 1 the same repeats continue the sounding voice + (phase-continuous): -29.94 for trigs and taps alike. Taking a releasing + voice of the same pitch warm instead would make repeats even; it changes + every VOIC 2..4 retrigger and is left as a design decision. + --- ## The engine owns the envelope (28 Sep 2026, OCTATRICK2.9 BUILD 31: plan B) -Tim's MKI (and the emulator, `root29m/ana_pop.txt`): the CF voice keeps +Tim's MKI (and the emulator, `ana_pop.txt`): the CF voice keeps rendering at full after a note-off and only the DSP's AMP envelope silences it, so a cold START (BUILD 26..28's rule) cut the still-sounding old tone dead in one sample (a step up to 0.95 of the old peak, x25 the tone's slope: @@ -120,7 +736,7 @@ Tim's decision: the engine owns the AMP envelope for synth tracks. **The DSP-facing override** (`sy_render`, the `sy_ison` synth path): every synth call writes the DSP voice record's halfwords 0/1/2 -- `0x80000110 + (ping << 9) + 64 * track`, AMP ATK / HOLD / REL as value << 8 -- `:= 0x0000 / -0x7f00 / 0x7f00` (ATK 0, HOLD INF, REL INF). Stage 1 (`root29o/ana_env.txt`) +0x7f00 / 0x7f00` (ATK 0, HOLD INF, REL INF). Stage 1 (`ana_env.txt`) proved on a stock sample track that those three words keep the DSP's envelope fully open across a note-off (no fade 2 s after the key-up, the level unchanged) and across re-triggers (within 0.3 dB of full, per-frame maxima all @@ -135,7 +751,7 @@ ATK / HOLD / REL is a no-op on the DSP now; the engine reads the lane every frame (locks, scenes and LFOs already applied by the frame builder). **The laws** (the DSP's, as measured in stage 1 on a stock FLEX sine; the -tables from `root29p/gen_env_tables.py`, `po_atk` / `po_relk` / `po_hold128`): +tables from `gen_env_tables.py`, `po_atk` / `po_relk` / `po_hold128`): * ATK: a LINEAR ramp to full in t = 3.85 ms x 2^(v / 8.53) (8: 7.5 ms, 16: 15, 32: 50, 64: 700, 96: 9.45 s); the engine's step a frame is 32768 / max(16, @@ -148,6 +764,8 @@ tables from `root29p/gen_env_tables.py`, `po_atk` / `po_relk` / `po_hold128`): anywhere. The engine arms the same timer at every START, a live key's and a sequencer trig's alike (`S_HTIM` for the mono voice, `V_HOLD` for a paraphonic one); a key-up inside the hold releases at once (as the DSP). + 2.10: a held CHROMATIC key's START arms no timer -- the key is the gate + ("A held key is the gate (2.10)"). * REL: EXPONENTIAL, tau = 0.295 ms x 2^(v / 8.53) (40: 7.6 ms, 60: 38.5, 80: 196, 100: 994, 126: 9 s; 127 = INF), floored at 1 ms: -20 dB in 2.3 ms and -40 dB in 4.6 ms at REL 0..15 -- the ~2 ms minimum fade Tim asked for, where @@ -177,10 +795,12 @@ sequencer trig as before; a finite HOLD ends by the engine's own timer; and STOP, a pattern change, a slot or project change and the sample preview stop all end a stock voice HARD through the one VOICE KILL `0x40006820(t)`, whose CF voice-byte clear carries `po_kill` (BUILD 33, below): the killed track's -engine voices end at that instant. `po_stop` at 0x4000b2c8 (the frame -builder's `clrl 0x46c80350`, after it turns the sequencer's STOP word into the -DSP-wide all-off command) stays as belt-and-braces for a STOP that posts the -word without a kill. Mute stays the DSP's output mute. +engine voices end at that instant. (Until 2.11 `po_stop` at 0x4000b2c8, the +frame builder's `clrl 0x46c80350`, was kept as belt-and-braces for "a STOP that +posts the word without a kill"; that word is the metronome's click, so +`po_stop` released every voice at every click -- removed in 2.12, see "The +metronome no longer releases a held note (2.12)".) Mute stays the DSP's output +mute. **Voice stealing / chord memory**: state 3, a fade of T_GMAX / 8 a frame (8 frames, 2.9 ms; `po_fr_cut`), then freed -- it was one frame. A voice the new @@ -218,7 +838,9 @@ voices (`po_free`: state 0, `V_GAIN` / `V_GPREV` 0) and clears their `V_KEY` / `V_HOLD`. The DSP voice is dead at that instant, so the immediate zero is inaudible, and the next START is cold (`sy_cold`: `S_GPREV == 0` -> phase 0 from 0). A sample track's record is zeroed the same way (nothing of ours was -on). `po_stop` (0x4000b2c8) is kept as belt-and-braces: S_ON bit 2 (STOPPED) +on). [2.12: the next paragraph's `po_stop` is removed -- the global word is the +metronome's click word and `[0x80000060]` is METRONOME_ENABLED, see "The +metronome no longer releases a held note (2.12)".] `po_stop` (0x4000b2c8) is kept as belt-and-braces: S_ON bit 2 (STOPPED) with the released flag on every synth track, so the mono release (`po_mono_env`) and the paraphonic `T_RK` take `po_relk[0]` (tau 1 ms) where REL is INF -- on a killed track it finds S_ON 0 and does nothing; it only @@ -246,7 +868,7 @@ the attack step (`T_AK`) and per voice the same envelope arithmetic as before (the peak scan) and -4 while limiting, -1 divu.l a frame. The DSP override is 12 instructions a call. -### Measured on BUILD 31 (28 Sep 2026, round 1: the octatrick-tuner BUILD 31 bus on ot_emu `--dsp-rt` through the poke panel, a copy of the pop card: T2 = FM SYNTH, C4 sine, ATK 0 HOLD INF REL INF unless said; `root29m/m40.py` pair / rel / stats / atk and `root29p-verify/m42v.py`, the verifier's log `root29p-verify/run_v31.log`) +### Measured on BUILD 31 (28 Sep 2026, round 1: the octatrick-tuner BUILD 31 bus on ot_emu `--dsp-rt` through the poke panel, a copy of the pop card: T2 = FM SYNTH, C4 sine, ATK 0 HOLD INF REL INF unless said; `m40.py` pair / rel / stats / atk and `m42v.py`, the verifier's log `run_v31.log`) - REL INF re-press of the same key at the same VOIC: max |step| x1.12 of the tone's slope, 0 cuts (the START rule: warm, phase-continuous). @@ -267,7 +889,7 @@ the attack step (`T_AK`) and per voice the same envelope arithmetic as before - NOT RUN in round 1: HOLD 20 self-release, the staccato sequencer HOLD 6 / INF, the stuck-note set, the regression set. -### Measured on BUILD 32 (28 Sep 2026, round 2: the octatrick-tuner BUILD 32 bus `root29q/bus_32.bin` on ot_emu `--dsp-rt` through the poke panel; the verifier's logs `root29q-verify/run_pop.log` (m40v: pair / voic / chord / rel / atk / stats), `run_stop.log` (m42q: stopinf / stopkey / stopseq / mute / patchg / hold20 / atk20), `run_seq.log` (m37k: hold6 / seq / lock / seqinf), `run_regr.log` / `run_warm.log` (m32v), `run_leg.log` (m36), `run_midi.log` (m38v); `chain.log` for the order) +### Measured on BUILD 32 (28 Sep 2026, round 2: the octatrick-tuner BUILD 32 bus `bus_32.bin` on ot_emu `--dsp-rt` through the poke panel; the verifier's logs `run_pop.log` (m40v: pair / voic / chord / rel / atk / stats), `run_stop.log` (m42q: stopinf / stopkey / stopseq / mute / patchg / hold20 / atk20), `run_seq.log` (m37k: hold6 / seq / lock / seqinf), `run_regr.log` / `run_warm.log` (m32v), `run_leg.log` (m36), `run_midi.log` (m38v); `chain.log` for the order) The cut measure is `max |step|` across the START against the tone's own slope (184 a sample for the C4 sine at -16.5 dBFS; a bare cut at that level is x14 @@ -312,7 +934,7 @@ The cut measure is `max |step|` across the START against the tone's own slope cleared would have started warm at full into a fresh DSP voice. BUILD 33's `po_kill` is the fix (above). -### Measured on BUILD 33 (28 Sep 2026, round 3: the octatrick-tuner BUILD 33 bus `root29r/bus_33.bin` on ot_emu `--dsp-rt` through the poke panel, a copy of the pop card; the builder's `root29r/run_stop.log` / `run_misc.log` and the verifier's `root29r-verify/run_stop.log`, `run_misc.log`, `run_q42.log`, `run_pop.log`, `run_seq.log`, `run_regr.log`) +### Measured on BUILD 33 (28 Sep 2026, round 3: the octatrick-tuner BUILD 33 bus `bus_33.bin` on ot_emu `--dsp-rt` through the poke panel, a copy of the pop card; the builder's `run_stop.log` / `run_misc.log` and the verifier's `run_stop.log`, `run_misc.log`, `run_q42.log`, `run_pop.log`, `run_seq.log`, `run_regr.log`) - **The voice kill ends the engine's voice** (`po_kill` at `0x4000685c`): wherever the stock kill runs, the engine goes idle at that instant @@ -361,14 +983,14 @@ record's byte +62 is stored into the per-track DSP command byte `0x46104d15[t]` (`0x4000b906`), the OR-0x10 sites (`0x4000b9aa`, `0x4000bd74`, `0x4000bdc0`) add the CF START bit, and the builder copies the byte into the packer's nibble byte `0x46104d0c[t]` at `0x4000c642`. Measured -at that copy (po_rtlog, `root29w/run_f37a.log`): the byte is **`0x10 | n`**, +at that copy (po_rtlog, `run_f37a.log`): the byte is **`0x10 | n`**, the START bit with the trig's sub-frame position n, cycling 4, 0xc, 5, 0xd, 6, 0xe ... at 120 BPM (a 16th is 344.5 frames, so n advances half a frame a step); no bit 5. The packer splits the frame's render calls at n, and the DSP crossfades its old voice under the new one over ~26 samples. Both voices are the engine's ONE continuous stream (the warm START rule: the same oscillator carries on from its level), so old + new is the measured +5.6 dB / 1.8 ms -bump at every trig (BUILD 33, `root29v/run_a.log`: max |step| / the tone's +bump at every trig (BUILD 33, `run_a.log`: max |step| / the tone's slope x24.76, |d2| 4534, per-frame amplitude 1.85 1.58 1.32 1.13 after the trig). A panel key's START on the same sounding note is clean (x1.00, the DIAG round's kwh take): its raw mailbox word `0x1d` reaches the DSP as @@ -393,15 +1015,15 @@ stock's byte, so a cold sequencer START is unchanged. `po_rtlog` (po_clock + START bytes {CK_FRAMES, track | byte posted << 8 | byte now << 16 | S_ON << 24}, the rig's proof that the fix fired. -**Measured on BUILD 37** (the octatrick-tuner BUILD 37 bus `root29w/bus_37.bin` +**Measured on BUILD 37** (the octatrick-tuner BUILD 37 bus `bus_37.bin` on ot_emu `--dsp-rt` through the poke panel, a copy of the pop card, T2 = FM -SYNTH VOIC 1, C4 sine, 120 BPM; `root29w/m46.py` = m44 + the po_rtlog counters +SYNTH VOIC 1, C4 sine, 120 BPM; `m46.py` = m44 + the po_rtlog counters + a per-frame sine-fit amplitude; logs `run_f37a.log`, `run_f37.log`, `run_regr37.log`): | take | BUILD 33 | BUILD 37 | |---|---|---| -| **a** ATK 0 HOLD INF REL 60 INDX 0, a trig on every step (the decisive take) | x24.76, \|d2\| 4534, per-frame 1.85 1.58 1.32 1.13 (root29v) | **max \|step\| / slope x1.01, max \|d2\| 9 (the whole take's 99.9th pct 9), per-frame amplitude 1.00 on all 13 frames of all 8 trigs, level -16.5 dBFS before and after**; po_rtlog: 39 rewrites, every T2 START byte `0x14 0x1c 0x15 0x1d ... -> 0x30` | +| **a** ATK 0 HOLD INF REL 60 INDX 0, a trig on every step (the decisive take) | x24.76, \|d2\| 4534, per-frame 1.85 1.58 1.32 1.13 (the verifier's run of BUILD 33, 28 Sep 2026) | **max \|step\| / slope x1.01, max \|d2\| 9 (the whole take's 99.9th pct 9), per-frame amplitude 1.00 on all 13 frames of all 8 trigs, level -16.5 dBFS before and after**; po_rtlog: 39 rewrites, every T2 START byte `0x14 0x1c 0x15 0x1d ... -> 0x30` | | **b** HOLD INF REL 60 INDX 40 DEC 40 | x6.66, \|d2\| 4632, 1 ms env jump 12.3 dB (the verifier's run_b33x; the round's own run_b33 read x1.10 / \|d2\| 105 -- its trigs were frame-aligned) | x7.45, \|d2\| 4961, 1 ms env jump 11.3 dB: a hard step at each trig (2008 -> 4786 in one sample); the spectral centroid 322 Hz before, 498 Hz 1-11 ms after, 298 Hz at 50-60 ms = the index envelope now restarts at the trig (on BUILD 33 it did not: 515 -> 431 -> 308). See open issues. | | **c** HOLD 64 REL 20 INDX 40 | as b | as b (x7.45, \|d2\| 4961) | | **d** HOLD 6 REL 20 INDX 40 (cold each) | before -34..-24 dBFS, \|d2\| 3 (the grid missed the onsets: 0-5 ms after -37.5) | before -21.8..-15.2 dBFS; the onsets are normal 16-frame ramps (1 ms env -65 -35 -26 -21 -19 -17 -15.5 dBFS, rise 3 ms, \|d2\| 61 at trig 2); the SUMMARY's x2778 / \|d2\| 3047 is trig 1 under the same index step as b | @@ -428,6 +1050,11 @@ project and STOP mid-note were not re-run this pass. ### The index ramp (29 Sep 2026, OCTATRICK2.9 BUILD 38) +(2.10, 7 Oct 2026: the ramp now moves the index, not the envelope -- a warm +START restarts the envelope at 1.0 and the decay runs from the START; see +"The author's test of the first 2.10 build (2.10)". The record below is +BUILD 38's.) + **The fix.** A warm START no longer restarts the index envelope. `sy_cold` (the mono voice) and `po_st_note` (a paraphonic voice) arm a 16-frame ramp instead (`S_ERAMP` at +81, `V_ERAMP` at +62, the frames left): every frame @@ -450,9 +1077,9 @@ S_GAIN +24, S_ENV +8, S_ON +36, V_STATE +36) are unchanged. Cost: four instructions a sample a voice (a load, an add, a store, the multiply on a register instead of memory). -**Measured on BUILD 38** (the octatrick-tuner BUILD 38 bus `root29x/bus_38.bin` -on the same rig, `root29w-verify/m47v.py`, logs `root29x/run_v38b.log` -(take b alone, first) and `run_v38.log`; `root29x/ana38.py` re-reads the +**Measured on BUILD 38** (the octatrick-tuner BUILD 38 bus `bus_38.bin` +on the same rig, `m47v.py`, logs `run_v38b.log` +(take b alone, first) and `run_v38.log`; `ana38.py` re-reads the takes against the PRE-trig waveform's own max slope and 99.9th-percentile |d2|, 40..1 ms before each trig, and traces the centroid in 10 ms windows hopped 2 ms): @@ -468,12 +1095,12 @@ hopped 2 ms): | **held** VOIC 3 chord 1.5 s (INDX 40 this time) | 0 frame-edge steps | **0 samples above the \|d2\| bound 31 (max 19), fold on 16 x1.01**: the per-sample index step adds no frame-edge step | | **mono** | -16.49 dBFS | -16.49 dBFS (0.3..0.6 s -16.44, 0.9..1.2 s -16.50 on the leg take), the cold onset 0 1 4 8 13 20 29 ..., rise to -1 dB 6 ms | | **v14r** REL INF VOIC 1 -> 4 D4 (po_carry) | no cut | no cut: onsets [], x1.78 against the quieter D4's slope as before (the C4 at -16.5 fading under it), \|d2\| 45 | -| regressions (m32v setup regr on card_ex, `root29x/run_regr.log`) | | ROOT A MINOR keys 8/8 OK; the fingered chord recorded as one step (ptch 64 chrd 8 voic 3); the run was stopped at the harness deadline before the legato / tuner / CHAIN AFTER lines | +| regressions (m32v setup regr on card_ex, `run_regr.log`) | | ROOT A MINOR keys 8/8 OK; the fingered chord recorded as one step (ptch 64 chrd 8 voic 3); the run was stopped at the harness deadline before the legato / tuner / CHAIN AFTER lines | **Not run on BUILD 38** (the harness deadline): the VOIC 3 CHRD sequence WITH INDX 40 (the run's chrd take ran after part a, i.e. at INDX 0: x1.02, \|d2\| 35 as BUILD 37), STOP mid-note then the next key cold (m39), FINE 0c on a new -project (m33v A). `root29x/chain38b.sh` holds the recipe for all three. +project (m33v A). `chain38b.sh` holds the recipe for all three. (History: the peak limiter and the warm/cold START rules described below are replaced by plan B, "The engine owns the envelope" above.) @@ -483,9 +1110,9 @@ limiter), unchanged by track LEVEL / AMP VOL, other tracks clean; (b) a pop at the start of every note, on OCTATRIK11 too. **The causes, measured** (the 2.9 BUILD 23 bus of 05c1c8c on ot_emu -`--dsp-rt` through the poke panel, a copy of level2's flat-FX card, T2 = FM -SYNTH, AMP HOLD INF REL 20, SCALE / GLIDE OFF, CHROMATIC key 13 = C4; the -session's `root29f/m34.py`, report `root29f/out_base23/report.txt`; a +`--dsp-rt` through the poke panel, a copy of the level study's flat-FX card, T2 = FM +SYNTH, AMP HOLD INF REL 20, SCALE / GLIDE OFF, CHROMATIC key 13 = C4; a +measuring script over the panel's renders and its report; a discontinuity = a sample whose second difference `x[n] - 2x[n-1] + x[n-2]` exceeds 1.5 x the 99.9th percentile of a 150..4000 Hz low-passed copy's, i.e. what the signal's own slope allows, then folded on the 16-sample frame): @@ -538,7 +1165,7 @@ i.e. what the signal's own slope allows, then folded on the 16-sample frame): hard `po_free` (the safety nets, a mono start) also clears `V_GPREV`. **Measured on the fixed bus** (BUILD 23 of this commit, the same rig and -card; `root29f/out_fix23/report.txt`): +card; the fix's report): - held MAJ at VOIC 3, INDX 40, 1.7 s: **0 discontinuities / s, max |d2| 79 against the bound 92** (was 22 / s, 366); the fold on 16 flat (x1.02, was x1.44); T_LIM 0.58 .. 0.69, mean 0.606 (was 0.604): the limiter does the @@ -601,9 +1228,8 @@ block, sy_mono / sy_loop): the same treatment as the paraphonic voices. (was 39, +3: +7.7 %), plus 12 a call for the step and the frame stamp; poly.s grew 96 bytes. Not measured with the port's counter. -**Measured** (BUILD 24 of that commit, the same rig and card; the session's -`root29g/m37.py`, reports `root29g/out_fix24/report.txt` and -`out_base24/report.txt`; "before" = the a0c0bc1 bus of BUILD 23): +**Measured** (BUILD 24 of that commit, the same rig and card; the +same measuring script, reports of the fix and of the base; "before" = the a0c0bc1 bus of BUILD 23): - a C4 sine at VOIC 1: the first samples 13, 20, 29, 38, 49, 62, 75, 90, 105, 122, 140, 159, 179, 200, 221, 244, 267, 290, .. -- a smooth raised ramp from phase 0, no step at sample 16 (was 22, 44, .. 318, **671**); **max |step| in @@ -637,7 +1263,7 @@ block, sy_mono / sy_loop): the same treatment as the paraphonic voices. onset on every press -- and gives up the retrigger-while-sounding continuity: a new key with LEG OFF while a note sounds now dips for the 16-frame attack (the normal retrigger character) instead of continuing - the phase. Measured (BUILD 26, `root29i/out_fix/report.txt`): + the phase. Measured (BUILD 26, `report.txt`): - fresh onset (VOIC 1 C4 sine): the first samples 13, 20, 29, 38, 49, .. from phase 0, **max |step| in the first 20 ms 183 against the steady tone's 184 (x0.99), 0 discontinuities**; the second press, 0.4 s after @@ -692,7 +1318,7 @@ block, sy_mono / sy_loop): the same treatment as the paraphonic voices. whole 2048 over its few samples. The second commit of this round ramps at the FRAME's slope (/ 16) and stores the gain the ramp reached as S_GPREV (the next call carries on; a short call lengthens the attack, - at most 2x). Measured (`out_fix2/report.txt`): **HOLD 6 max |d2| 24 + at most 2x). Measured (`report.txt`): **HOLD 6 max |d2| 24 (the steady tone's own is ~90), fold x2.25, 35/s above a bound of 15.9 (the release tails' own, near-silent slope); HOLD INF 8/s above 43, max |d2| 197, fold x1.06** -- one per note, the fading old note (at -25 @@ -702,8 +1328,8 @@ block, sy_mono / sy_loop): the same treatment as the paraphonic voices. 130 (x1.00), 0 discontinuities, the envelope 624 / 630 / 1716 / 2842 / 3471 -- identical to BUILD 23 (the paraphonic path untouched). - the LEG MONO + GLIDE 64 legato press and the ROOT A MINOR keys / chord - record / tuner / direct-jump regressions: `root29g/run_leg24.log`, - `run_regr24.log` (run after this commit's measurement; see the session's + record / tuner / direct-jump regressions: `run_leg24.log`, + `run_regr24.log` (run after this commit's measurement; see the report). **The final rule (BUILD 27 / 28, `sy_cold` / `po_rel` / the quantizer's @@ -754,7 +1380,7 @@ changes, no ramp) only when all four hold, else **cold** as BUILD 26: a START from the SEQUENCER is always cold; only a live key or a MIDI note may be warm. A phase reset at gain 0 stays inaudible; LEG MONO hand-overs never START. -**Measured on BUILD 27** (`root29j/out_fix/report.txt`, `out_fix2/`, +**Measured on BUILD 27** (the fix's report and a second pass, `run_midi.log`, `run_leg.log`, `run_regr.log`; the same rig and card, VOIC 1 C4 sine, REL 20): - a retrigger while sounding (C4 held, D4 pressed, LEG OFF, HOLD INF): the @@ -805,8 +1431,8 @@ C4 sine, REL 20): jump CHAIN AFTER 0 -> 1; FINE 0c on a new project's first load; warm boot with the real sram_out.bin ok. -**Measured on BUILD 28** (`root29k/out_fix/report.txt`, `run_midi.log`, -`run_leg.log`, `run_regr.log`, `out_stop28/`, `out_tree28/`; the same rig and +**Measured on BUILD 28** (the fix's report, `run_midi.log`, +`run_leg.log`, `run_regr.log`, the STOP and tree takes; the same rig and card, VOIC 1 C4 sine, REL 20; `S_KEYED` peeked at +127): - **staccato 16ths at HOLD INF: one onset per trig** (55 detected in 9 s at 120 BPM, the rest under the detector's 3 dB rise; S_KEYED 0 mid-play = a @@ -870,6 +1496,367 @@ for it); the port's instruction count; the VOIC 1 -> 2..4 switch mid-note still cuts the mono voice at once. (The re-press click is closed: BUILD 26; the sequencer's warm START: BUILD 28.) +### The note-start click (2.10, 6 Oct 2026) + +**Cause** (proven on the emulator; the on-board 3.0 build is the same): +the attack's 16-frame floor (5.8 ms, linear) is a fraction of one cycle below +about C3 and stops at an arbitrary carrier phase; the onset and the corner at +frame 16 put 17..35 dB of excess energy at 100 Hz..1 kHz above what a RATO 0.25 +low note carries. A warm START's 16-frame index ramp, a pitch-changing warm +START (the phase-continuous frequency jump) and the 8-frame steal fade are the +same kind of splatter. + +**The four changes** (`poly.s`): +1. `po_alaw` -- the attack's step each frame is min(the ATK law's step, one + carrier period's step = ((inc >> 13) x full >> 15) + 1), then S-shaped: + step x 1.28 x (1/4 + 4 r (1 - r)), r = level / full, the same total time. + Called from `po_me_atk` (mono, full 32768) and `po_fr_amp` (per voice, full + T_GMAX); a mono voice at full skips it (`cmpi.l #32768; bge`: two + instructions), a paraphonic one already did. A step under 32 (ATK past + ~370 ms) stays linear: the S-curve's integer truncations there stretched + ATK 64 by 11 % and ATK 87 by 2.1x (the law as first proposed); with the + floor every ATK 0..127 keeps its total time within 6.2 % (a frame-count model of the law, in the author's workspace, not in the repo: + ATK 0/16/20/32 = 16/39/54/144 frames linear -> 17/40/55/146). The period's + step is floored at 128 (at most 256 frames: a carrier under ~11 Hz, or none). +2. `po_erlen` -- a warm START's index ramp (BUILD 38) lasts max(16, one period) + frames, at most 254 (`S_ERAMP` / `V_ERAMP` = 255 marks a fresh ramp). +3. `po_xfq` + `po_carry` in `sy_cold` -- a mono START while the voice sounds, + at a word half a semitone or more from the sounding one (`S_CUR`), moves the + old tone into a fading voice (state 3, its own pitch and phase) and starts + the new note cold from phase 0 with its attack. The same pitch stays + phase-continuous (2.); a paraphonic START keeps its own allocation. +4. `po_fr_cut` (a stolen or chord-memory voice) and `po_fade_frame` (voices + fading under the mono voice: VOIC 2..4 -> 1, and 3.'s crossfade) step + through `po_alaw` too: no faster than one period -- of the voice itself in + po_fr_cut, of the lower of the voice and the mono voice in po_fade_frame, so + a crossfade down lasts the new note's period -- and S-shaped. +5. (second pass) `po_alaw`'s floor for a period of 16..32 frames (~F2..F3) is + max(p, min(2p - 16, 32)) frames: C3 26 frames instead of 21. Nothing + changes from F3 up (p <= 16: the 16-frame floor) or below F2 (p >= 32). +6. (second pass) A paraphonic voice handed a note at a HIGHER pitch (half a + semitone or more) while it still sounds -- chord memory reusing the + previous chord's voices, a VOIC-cap steal -- fades first (state 3, + `po_fr_cut`: one period of its own note, S-shaped) and the note starts cold + (phase 0, its attack): in a free voice of the track when one exists + (`po_st_xf`; the old one fades in its own), else in the same voice once + the fade has ended: the note waits in `po_pend` (8 tracks x 4 voices x 16 + bytes: state, key, root, gate; `po_pfix` writes it, `po_pstart` starts it + from `po_frame` where the voice is freed; a key or MIDI note gone in the + meantime releases at once; `po_free`, `po_fade`, a steal, + `po_carry` and the legato hand-over drop it; `po_start` folds PTCH moves + into it). Before any of that, `po_pmatch` gives a new note the fading voice + already at its pitch (a chord's common tones keep their voices, warm, as one + key's retrigger). A LOWER or the same pitch is taken warm, phase-continuous, + as before (measured: fading a brighter old note scores worse, below). + +**Measured** (both passes, the Modwerk exporter's quantizer+synth image on +the pinned ot_emu `--dsp`, the same rig and measure throughout: the onset splatter = +the energy above max(250 Hz, 8 f0 x ratio) in the first 10 ms over the tone's +own 100 ms later, 23 ms FIR high-pass; RATO 0.25 INDX 40 unless said; dB, mean +of two pattern loops; c = cold, w = warm. c51e304 = the renders of +that image; 1.-4. = 18dd40d; 1.-6. = this image, 161d781d..., +every case rendered again (the results file is in the author's workspace, not +in the repo); targets in +brackets (the click fix's targets, 6 Oct 2026): + +| case | c51e304 | 1.-4. | 1.-6. | +|---|---|---|---| +| cold C1 / C2 / C3 / C5, INDX 40 [-3..+7; C5 no worse] | +29.4 / +25.6 / +16.9 / +3.0 | -0.2 / +2.4 / +8.2 / +1.8 | -0.2 / +2.4 / +5.7 / +1.8 | +| cold C1 / C2 / C3 / C5, INDX 0 | +28.5 / +29.1 / +21.9 / +1.3 | -2.5 / +3.3 / +6.5 / +1.7 | -2.5 / +3.3 / +6.8 / +1.7 | +| cold C1 / C2 / C3 / C5, INDX 100 | +33.2 / +21.5 / +17.4 / +6.5 | +7.0 / +1.2 / +5.9 / +0.6 | +7.0 / +1.2 / +4.6 / +0.6 | +| cold C1 / C2 / C3 / C5, FDBK 60 | +35.3 / +32.1 / +12.9 / -4.8 | +10.6 / +12.3 / +1.4 / -11.1 | +10.6 / +12.3 / -0.7 / -11.1 | +| cold C1 / C2 / C3 / C5, RATO 1 | +13.6 / +13.2 / +17.6 / +8.8 | -10.1 / -3.2 / +6.5 / +3.5 | -10.1 / -3.2 / +4.6 / +3.5 | +| cold C1 / C2 / C3 / C5, ATK 16 | +17.2 / +12.0 / +6.1 / +0.1 | -0.2 / +2.4 / +1.4 / -0.8 | -0.2 / +2.4 / +1.4 / -0.8 | +| warm C1 -> C2 under the tail (s_ case) | +36.3 | +4.5 | +4.5 | +| retrigger every step, same pitch C1>C1 / C2>C2 [<= +8] | +23.5 / +19.4 | +1.2 / -0.5 | +1.2 / -0.5 | +| retrigger every step, pitch change C1>C2 / C2>C1 [10 dB better] | +25.6 / +31.6 | +6.4 / +28.2 | +6.4 / +28.2 | +| VOIC 4 MAJ chord memory every step, C1>C1 / C1>C2 / C2>C1 / C2>C2 [pitch changes 10 dB better] | +13.2 / +31.0 / +21.8 / +17.8 | +3.7 / +30.6 / +21.2 / +14.1 | -2.2 / +7.6 / +14.5 / +13.8 | +| VOIC 2 steals, C1>C1 / C1>C2 / C2>C1 / C2>C2 | +37.1 / +34.5 / +26.3 / +34.0 | +19.6 / +5.2 / +14.7 / +4.8 | +19.6 / +5.2 / +14.7 / +4.8 | +| VOIC 4 MAJ chord, cold C1 / C2 / C3 / C5; C1 chord -> C2 chord (w) | +33.1 / +32.4 / +23.4 / +8.3; +35.4 | +16.6 / +15.8 / +13.9 / +1.8; +32.7 | +16.6 / +14.6 / +13.0 / +1.8; +16.2 | + +ATK 0 / 16 / 20 / 32 at C4 (time to 99 % of full level), 1.-6. over +c51e304: 0.965 / 0.978 / 0.983 / 0.991 (11.07 / 14.01 / 24.24 / 51.04 ms), the +render sample-identical to 1.-4.'s. C4 defaults (PTCH 64, RATO 1, INDX 40): +against c51e304 they differ only in the first ~28 ms of each cold onset (as +1.-4.); against 1.-4. the render is sample-identical. + +**The two pitch changes downward miss the target "10 dB better" (6 Oct 2026), and why** (each part +rendered on its own, and an integer model of the mono and chord cases; the +scripts are in the author's workspace, not in the repo). Rendered apart, the mono +C2>C1 crossfade's OLD TAIL ALONE (the new note muted) scores +29.4 -- the +whole +28.1 -- and its new onset is the cold-start path (a cold C1: -0.2); for +C1>C2 the parts are +2.0 and +3.5 (+6.2 together). The excess is the old C2 +tone itself: its own HF above 262 Hz sits ~30 dB over the C1 tone's own (the +measure's reference), so any old tone left in the 10 ms window counts, and +removing it faster is a click. In an integer model of the engine a C2 tone that simply +continues scores +29.9 and every fade scores more (S over 85 frames +31.1, 43 ++34.0, 16 +42.8, 8 +45.9); an index fade, a glide or the phase-continuous jump +do not go under +29.7. That model first predicted +9..+17 because its crossfade +switched at 500 ms, where the old note's index has decayed; the card switches +after one 16th (125 ms: +31.1 in the same model). The same holds for chords: +fading a VOIC 4 C2 chord before a C1 chord scored +40.2 on the emulator (model ++51), so a voice moving DOWN is taken warm (6.) and `po_pmatch` keeps the +common tone (C2) in its voice: +21.8 -> +14.5, 7.3 dB. + +**DEVIATIONS** (from the targets of 6 Oct 2026 / the first proposal): +- A step under 32 stays linear (above); the first law stretched slow attacks. +- 5. adds frames to F2..F3's attacks (C3 21 -> 26) to bring C3 at INDX 40 into + the window; the first law was one period. +- 6. fades only voices that move UP; a lower pitch stays warm (above). The + in-place fade delays such a note by one period of the note it replaces. +- 3. (the crossfade) stays mono; a paraphonic START is 6.'s. + +**Cost** (Modwerk's `gate.cpp` harness, 512 cases, instructions a render, +both passes): mono mean 1,068 -> 1,161 (1.-4.) -> 1,165 (1.-6.), peak +1,312 -> 2,515 -> 2,556 (a crossfade: the old tone renders as a voice for one +period; cap 2,600); VOIC 4 mean 3,910 -> 4,028 -> 4,060, peak 5,348 -> 5,568 +-> 6,324 (1.136 x 1.-4.'s; cap 1.15 x). The DRAM unit grows 18,472 -> 18,896 +(1.-4.'s click part) -> 20,480 B (1.-6. with the LFO list and the key gate). + +## The LFO destination list (2.10, 6 Oct 2026) + +Tim (6 Oct): "get rid of the parameters that don't exist for granular, and put +the parameters for granular in as destinations. Do that for FM and [SY DRUM] +machine too." On this line: FM. + +**Cause** (found on the emulator): LFO SETUP's PMTR names a destination as page x 6 + slot +(0 PLAYBACK, 1 LFO, 2 AMP, 3 FX1, 4 FX2) and prints it with the formatter +`0x4003bf64` (the LFO descriptor's slot-6 formatter). It takes the PLAYBACK +names from the machine table `0x400d5f38` itself (at `0x4003bff2`) and the LFO +names from the stock descriptor (`0x4003bffa`) -- not through the page +resolver that `page.s` and `po_lfopage` hook -- so a synth track listed the +Flex names. The audio edit `0x400392cc` walks the 30 entries in display order +(PLAYBACK AMP LFO FX1 FX2) with no gaps. + +**The change** (`poly.s` "THE LFO DESTINATION LIST", three 8-byte detours): +- `po_lfdname` at `0x4003bff2`: replays the table pick; a FLEX pick becomes the + page resolver's PLAYBACK descriptor for the track (the FM SYNTH clone on a + synth track, the stock FLEX one on a sample track) unless the resolver + answers the master track's descriptor or 0. +- `po_lfdlfo` at `0x4003bffa`: the LFO page's names from the resolver when it + answers our VOIC / CHRD clone (`po_lfodesc`), else stock. So a destination + stored before 2.10 (or by a scene or a lock) at SPD3 / DEP3 prints `VOIC` / + `CHRD`, what it addresses on a synth track. +- `po_lfdedit` at `0x400392cc`: stock's walk (the position tables `0x400a72a8` + / `0x400a7280`, the encoder `0x4003249c`, the clamp 0..29) with one SHOWN + entry a detent in the turn's direction, stepping over the track's hidden + mask: `0x900` (values 8 = SPD3 / VOIC and 11 = DEP3 / CHRD) when the + resolver gives the track's LFO page as our clone, else none. At an end with + nothing shown beyond, it stays; a stored hidden value is left on the first + turn for the next shown entry that way; a stored value outside 0..29 starts + from the end. PMTR stays the stock Part byte, written by stock's store. +- `po_lfopage` (the resolver's LFO descriptor, a hook since phase 5): its FLEX + test compares the low byte of d5 only (the resolver loads the machine with + `moveb`, so a caller whose d5 was negative failed the long compare and got + the stock page -- the edit's backward walk then showed SPD3). + +**Measured** (the Modwerk exporter's quantizer+synth image with this +change on the pinned ot_emu: LFO SETUP screenshots per detent, the +Part byte read from the bank blob): +- FM track, LFO 1, forward from PTCH: `PTCH RATO INDX FINE FDBK DEC`, `ATK HOLD + REL VOL BAL `, `SPD1 SPD2 DEP1 DEP2`, six FILTER, six DELAY, then it stays + on the last; backward the same list reversed, staying on PTCH. +- A stored 8 prints `LFO VOIC` (Part byte 8); forward -> `DEP1` (9), back from + there `SPD2` (7) `SPD1` (6) `AMP ` (17). A stored 11 prints `LFO CHRD`; + forward -> FX1's first (18), back `DEP2` (10) `DEP1` (9) `SPD2` (7). A + stored hidden value drives what it always drove (LFO 3's speed / depth, + whose depth `po_lfo3` holds at 0 on a synth track): a render with LFO 1 on + VOIC at DEP 127 equals the DEP 0 control in level step for step. +- An LFO on a renamed entry works: LFO 1 on INDX (DEP 127, a C4 note, RATO 1, + DEC HOLD) moves the share of energy above 1.5 f0 between 0.04 and 1.0 (mean + 0.38, its main variation 0.35 Hz); the DEP 0 control stays 0.50..0.51 + (a spectral split of the render at 1.5 f0). +- A trig held (TRACKS mode, no GRID RECORDING): the turn moves the Part byte + (1 -> 4), as on c51e304. With GRID RECORDING on and a trig held, + the turn stores nothing: neither the Part byte (2 throughout) nor a lock (the + step's lock bytes stay 0xff), as on c51e304 -- PMTR takes no lock there. +- The plain FLEX track (T2), a MIDI track (T1 in MIDI mode: its own NOTE / + ARP list) and the master track (T8, MASTER TRACK on): every screenshot of + the same walk is pixel-identical to c51e304's image (63, 13 and 17 shots). +- The stack inside the formatter, measured once (a scratch build painting 1,024 + B below the hooks' entry SP; 14 formatter calls across the FM walk): at most + 40 B below the hook's entry SP (the resolver call). + +The DRAM unit grows 18,896 -> 19,200 B (the three hooks and the step-over); the +page cave (`page.s`) is unchanged. + +## A held key is the gate (2.10, 6 Oct 2026) + +**The rule.** On a synth track a note started by a CHROMATIC key that is still +held when its START reaches the engine has no HOLD timer: it sounds while the +key is held and its release (the lane's REL) starts when the key goes up -- +for the voice or voices that key started, unless LEG handed the note to +another held key (LEG MONO: the newest key is the held one, so letting go of +an earlier key changes nothing and letting go of the newest releases the +note, as since 2 Oct; a paraphonic chord's voices release with their keys). +A sequencer trig is gated by HOLD as before (the timer from the START; 127 = +INF), and so is a MIDI note (its note-off releases it earlier, as before). A +key let go before its START reached the engine is treated as a trig (HOLD). +STOP ends every voice as before. (Until 2.12 every metronome click released a +held key too, by mistake: "The metronome no longer releases a held note +(2.12)".) The live recorder is untouched: the played +length still becomes the recorded step's HOLD (the quantizer's hold / +release rule), so the recorded note plays back for the length it was held. + +**Where** (`poly.s`): `po_livekey` -- the START's identity (`qz_pkey[t]`, +1..25 a panel key) is still held when its bit is in the quantizer's held +mask (`qz_pmask[t]`, kept for a paraphonic track) or it is stock's one held +key (`0x460d171d + t`, key + 1: a mono track's, which never enters the +mask). `sy_cold` (the mono voice) skips arming `S_HTIM` for such a START; +`po_st_env` (a paraphonic voice) arms `V_HOLD` only for a sequencer or MIDI +note. The release itself is the existing path: stock's voice note-off at the +key-up (`po_rel`) for the mono voice, the held mask (`po_frame`) for a +paraphonic voice. The DRAM unit grows 19,200 -> 19,296 B. + +**Measured** (the Modwerk exporter's quantizer+synth image on the +pinned ot_emu `--dsp`, T1 = FM SYNTH, AMP ATK 0 HOLD 32 REL 40, 120 BPM, +the CHROMATIC trig mode; the level every 10 ms, this image's renders against +c51e304's image's): + +| case | c51e304 | 2.10 | +|---|---|---| +| key held 2.0 s (VOIC 1) | sounds 0.3 s (HOLD 32), silent while held | sounds 2.0 s, released at the key-up | +| sequencer trig every bar, HOLD 32 | -- | 0.3 s a note (HOLD), as before | +| VOIC 4, four keys held 2.0 s then let go one by one | the chord ends at 0.5 s | the chord sounds until the keys go; the last key-up releases it | +| LEG MONO: key A, key B (legato), B up, A up | ends at HOLD | ends at B's key-up (the 303 rule, unchanged) | +| LEG MONO: key A, key B (legato), A up, B up | -- | sounds through A's key-up, released at B's | +| key held across STOP (and HOLD INF on c51e304) | ends 0.7 s after STOP | ends 0.7 s after STOP (the rig's stop timing), key still held | +| live recording (REC + PLAY), a key held 1.0 s, then two loops of playback | live 0.3 s; played back 1.0 s, 1.0 s | live 1.0 s; played back 1.0 s, 1.0 s (the same recorded length) | + +The first note of a session ignoring HOLD (an earlier finding) is untouched: +in the sequencer case above the first note lasts until the next trig. + +## The author's test of the first 2.10 build (2.10, 7 Oct 2026) + +The first 2.10 test build (this line at 2db6657) on the author's MKI, with +PLAYBACK PTCH 0, RATO 2, INDX 127, FINE 0c, FDBK 49, DEC 0; AMP ATK 0, HOLD +1.000, REL 98; the CHROMATIC trig mode; LEG OFF unless said. Four findings: +1. a quick press and release: a glitchy pop at the start of notes; +2. playing fast with the sequencer running, REL 0: many notes do not sound; +3. fast runs across the keyboard, the sequencer stopped: glitchy pops, and + now and then a note an octave up; +4. a legato phrase recorded live and played back (HOLD 26, REL 112): clicks at + the starts of notes -- at DEC 0 with INDX above 0, the more the higher + INDX, and none once DEC is turned up a little, even at INDX 127. + +Every gesture was played on the emulator with those settings (VOIC 1, 2 and 4, +REL 0 / 98 / 112, the sequencer running and stopped), with a 1 ms trace of the +engine's records. + +**Cause 1: a warm START at a short DEC** ((1), the pops of (3), (4)). A START +on a voice that still sounds is warm: the oscillator continues from its level +(BUILD 38) -- the same pitch again inside its release, a sequencer trig on its +own tail (a pattern loop restarting on its last note), a lower note onto a +sounding paraphonic voice, a paraphonic legato note. Its index envelope ramped +from its level back to 1.0 over max(16 frames, one carrier period) (`po_erlen`) +and the decay waited for the ramp. At DEC 0 (k = `K_MAX` since 2.10; DEC 1 +reads the same cap) the decay then took the envelope from 1.0 to its floor +(1/16) within one 16-sample frame: 94 % of the index gone in 0.36 ms, about +6 ms after the START (0.69 at DEC 2, 0.17 at DEC 4, the ramp's own 0.06 from +DEC 8 up). A cold START never did this: its envelope decays before its first +sample. **The change** (`sy_warm_env` / `po_st_warm`, `sy_il_*` / +`po_fr_il_*`): a warm START restarts the index envelope at 1.0 as a cold one +does, and the decay runs from the START; the ramp moves to the index itself -- +each of its frames moves I x E by the remaining distance over the remaining +frames (BUILD 38's formula, `po_erlen` frames in all), so the index never +moves faster than over one carrier period. At DEC 0 the index stays at its +floor and a restarted note sounds like a fresh one; a short DEC's pluck is +spread over the ramp; DEC 127 (HOLD) ramps exactly as before. (A first +version limited the index to I / L a frame instead; it let a small move +finish within a few frames, and the click table's same-pitch retriggers at +C1 / C2 came out 14..21 dB worse: not kept.) + +**Cause 2: a second key inside one fade** (the missing notes of (3), VOIC +2..4). A note moving UP onto a sounding paraphonic voice waits in that +voice's `po_pend` entry while the voice fades for one period (2.10, second +pass). `po_alloc` ranked the fading voice (state 3) as good as a free one, so +a second key pressed inside the wait took the same voice and `po_pclear` +dropped the first key's note: D4, then D#4 2 ms later with every voice +releasing -- D4 never sounded. **The change**: `po_alloc` and `po_steal` rank +a voice whose `po_pend` entry holds a waiting note last (3). + +**Cause 3: a trigless step's sub-frame position** (found while measuring (4): +at VOIC 1 the played-back phrase went silent after its first note). A +LIVE REC'd LEG MONO phrase is one trig and trigless steps carrying PTCH (and +the chain's HOLD) locks. A trigless step posts the DSP command byte with no +START bit and its sub-frame position n in the low nibble; the packer keeps n +and splits every following frame's render at n until the next event. The +frame's first call [0, n) runs the stock renderer on the voice without the +marker pinned (`sy_render` pins it on the frame's second call), so the +marker's play position walked n samples a frame; 2..20 ms later it reached +the marker's end, the stock voice ended (the CF voice byte 255 -> 0; not a +kill) and `sy_check` cut the note to silence until the next loop. The same at +VOIC 2..4 with LEG POLY (the voices freed). **The change**: `po_retrig` +clears n in a byte with no START bit when the track's engine voice is on +(`po_retrig_nost`); sample tracks and silent tracks keep stock's byte. + +**Not reproduced.** (2) as silence: at REL 0 with the sequencer running, VOIC +1 / 2 / 4, with and without sequencer trigs on the track, taps and overlaps +20..150 ms apart, and taps over a playing live-recorded legato pattern, every +key got its voice at its pitch within 1..7 ms and sounded for its length. +Causes 2 and 3 can each silence notes (both fixed); neither showed at REL 0. +The octave jumps of (3): no voice in any trace sounded off the pressed +pitches. + +**Measured** (the Modwerk exporter's quantizer+synth image on the pinned +ot_emu `--dsp`, the panel driven as on the unit, the settings above unless +said; first build = 2db6657's image, 161d781d...; now = this line's, +b0c440fc...). xs is the click table's onset-splatter measure below (the HF +above max(250 Hz, 8 f0 x ratio) in the first 10 ms over the note's own 40 ms +later); hf6 the > 6 kHz band's largest 1 ms window in the first 15 ms over its +median 30..60 ms after; both in dB, the worst restarted note of the take: + +| gesture | first build: xs / hf6 | now: xs / hf6 | +|---|---|---| +| C4 re-tapped inside its release, VOIC 1, DEC 0 (the fresh first note: -3.4 / +1.4) | +37.4 / +56.9 | -0.5 / +2.3 | +| the same, DEC 1 | = DEC 0 | = DEC 0 | +| the same, DEC 4 (the fresh note: +11.9 / +23.0) | +38.7 / +57.4 | +16.3 / +25.0 | +| the same, DEC 127 = HOLD | -0.5 / +2.3 | -0.4 / +1.5 | +| the same, INDX 40 FDBK 49 | +32.7 / +42.2 | +0.1 / +10.2 | +| the same, INDX 40 FDBK 0 (the fresh note: +1.9 / +3.3) | +35.5 / +41.6 | +8.6 / +14.0 | +| the same, INDX 40 FDBK 0 DEC 4 | +23.1 / +29.2 | +10.4 / +15.9 | +| the same, INDX 127 FDBK 0 (the fresh note: +2.0 / +3.3) | +58.0 / +56.1 | +9.0 / +14.1 | +| fast runs C3 G3 C4 (taps and overlaps 20..150 ms), REL 98: notes with xs >= +10, VOIC 1 / VOIC 4 (of 33) | 2 / 13 | 0 / 0 | +| fast runs at VOIC 4, every voice releasing, 1..2 ms overlaps: notes dropped / xs >= +10 (of 26) | 2 / 11 | 0 / 0 | +| a trig every bar on its own tail, HOLD 26 REL 112: DEC 0; DEC 4 | +37.9 / +57.5; +38.6 / +57.4 | +0.3 / +2.3; +16.5 / +25.2 | +| the live-recorded LEG MONO phrase played back, VOIC 1: the loop's first note | +53.1 / +49.4 | +1.9 / +5.0 | +| ... its legato steps | silent after the first step (the cut: +66.0 / +88.8) | all sound (-0.2..+12.0: a step DOWN has the old, brighter tone in its window, as live) | +| the same phrase at VOIC 4, LEG MONO (each note a paraphonic START) | +35.4 / +54.3 | +2.3 / +14.0 | +| the same phrase at VOIC 4, LEG POLY | silent after the first step | all sound | +| REL 0, the sequencer running, fast runs, VOIC 1 / 4: notes silent (of 33) | 0 / 0 | 0 / 0 | + +**What is left.** +- At DEC 2..8 a restarted note still has a pluck (DEC 4: +16.3, a fresh + note's own +11.9): the decay's shape, not a collapse. +- At FDBK 0, a darker tone, a re-tapped note at DEC 0 still measures + +8..+9 / +14 (INDX 127 or 40; a fresh note +2 / +3). The index does not + move there, and the same settings at DEC 127 (a bright tone) measure + -0.7 / +2.2: most likely the level's re-attack from the release tail heard + against a tone with little HF of its own -- the attack law's, not DEC's. +- A key tapped and released within the one period it waits for a fading + voice (at most 7.6 ms at C3) starts released and is not heard. +- A recorded legato chain's trigless steps carry the chain's HOLD lock; the + engine arms its HOLD timer at the START only, so the played-back phrase + starts its release at the first step's HOLD (a stock voice re-lengthens its + hold at each step). With REL 112 the later steps sound in the slow release. + +**The click table and the gate, again** (the renders of "The note-start +click (2.10)" and "A held key is the gate (2.10)" on this image): every cold +row, INDX 0 / 100, FDBK 60, RATO 1, ATK 16, the VOIC 2 steals and the s_ warm +row equal the first build's to 0.1 dB; the same-pitch retriggers C1>C1 / +C2>C2 +1.2 / -0.5 -> +1.1 / -1.1, the pitch changes C1>C2 / C2>C1 +6.4 / +28.2 +-> +6.2 / +27.7; VOIC 4 chord memory C1>C1 / C1>C2 / C2>C1 / C2>C2 -2.2 / +7.6 +/ +14.5 / +13.8 -> -1.9 / +6.2 / +15.2 / +11.9 (two rows 0.3 and 0.7 dB worse, +two better); the VOIC 4 MAJ chord's C2w +16.1 -> +15.6. ATK 0 / 16 / 20 / 32 +sample-identical (11.07 / 14.01 / 24.24 / 51.04 ms). A held key and a +sequencer trig: sample-identical; LEG MONO: the same end, within 0.6 dB at +100 ms; the VOIC 4 chord: the same end, within 0.2 dB at 100 ms. + +**Cost** (Modwerk's `gate.cpp` harness): mono mean 1,165.0 -> 1,165.9, peak +2,556 (the same); VOIC 4 mean 4,060.2 -> 4,061.9, peak 6,324 -> 6,390 +(1.010 x). The DRAM unit grows 20,480 -> 20,560 B. No hook, poke or page +change; `page.s` and the manifest are untouched. + ## FINE defaults to 0c when a track becomes a synth track (28 Sep 2026) Tim's report: "when I first load a synth track, its FINE parameter is maxed @@ -899,6 +1886,19 @@ FINE is the user's), or on any sample track (a non-marker slot is no synth track, so the byte is never written). RATE p-locks live in the pattern and are not read. +**8 Oct 2026: the reset reaches the voice at once.** The lane byte is what +the page shows; the DSP and the engine read the lane's value WORD +(`0x80000a50 + 64 * track + 6`, byte << 8), which the frame builder takes +from the lane only through its slew counters (`0x80000db4 + 32 * track`, +the converter at `0x4000d63c`) or rewrites from the Part at a trig from another bank or +Part. So a marker assigned to a track that had played a sample at RATE 127 +in this Part read FINE 0c and played +63c until the next Part change or +reload (measured: C4 at RATO 0.25 as 67.8 Hz instead of 65.4). +`po_fine_reset` now also writes the word (0x4000) and clears the slew counter +of the lane's bytes 0..3, as stock's reload of a track from its Part does +(`0x40001f18`); the FM SYNTH chooser's seed does the same for all six bytes +and the SETUP bytes ("Selecting FM SYNTH from the machine list"). + **The sites** (three `jmp` detours of 8 bytes each into the DRAM unit, `po_assign` / `po_machwin` / `po_machlist`; `manifest.py` ASSIGN_HOOK, MACHWIN_HOOK, LISTWIN_HOOK). Stock has ONE routine that assigns a slot to @@ -933,14 +1933,14 @@ reset. `po_is_synth` now shares the marker scan (`po_slot_marker`). The unit grew by 262 B (the ROM footprint and the cave are unchanged: 3,236 B of cave left in the octatrick-tuner remix, BUILD 21). -### Measured (28 Sep 2026, the octatrick-tuner BUILD 21 bus on ot_emu `--dsp-rt` through the panel, a copy of the 2.9 card: T1 = FLEX slot 3 `third-0.wav` (a 438.645 Hz sine) with RATE 127, T2 = FM SYNTH slot 5 `SYNTH.wav`; the session's `root29c/m31.py`, shots `root29c/out21/`; the Part byte read from the bank blob, the shadow from `0x1001614e + 0x8edb3`, the lane from `0x80000813`; T1's slot-byte shadow `0x100a5198..+5` watched with ot_emu's `OT_WATCHMEM` for the writers' PCs) +### Measured (28 Sep 2026, the octatrick-tuner BUILD 21 bus on ot_emu `--dsp-rt` through the panel, a copy of the 2.9 card: T1 = FLEX slot 3 `third-0.wav` (a 438.645 Hz sine) with RATE 127, T2 = FM SYNTH slot 5 `SYNTH.wav`; `m31.py` and its shots; the Part byte read from the bank blob, the shadow from `0x1001614e + 0x8edb3`, the lane from `0x80000813`; T1's slot-byte shadow `0x100a5198..+5` watched with ot_emu's `OT_WATCHMEM` for the writers' PCs) - The assignment (the machine window, slot 5 = the FMSYNTH marker, YES) on T1 = FLEX slot 3 with RATE 127: the FLEX PLAYBACK bytes go `[64,0,127,127,0,79]` -> `[64,0,127,64,0,79]`; the Part byte, the battery-RAM shadow (`0x1001614e + 0x8edb3`) and the live lane (`0x80000813`) all read 64; the page redraws as FM SYNTH > FLEX with - FINE 0c at once (`root29c/out21/assign_page.png`). + FINE 0c at once (`assign_page.png`). - FINE +20c (raw 84), SCALE MAJOR / ROOT A / GLIDE 12, PROJECT > SAVE, eject, a cold boot loading the project: RATE 84 in the Part, the shadow and the lane -- a tuned synth track keeps its FINE. @@ -963,8 +1963,8 @@ of cave left in the octatrick-tuner remix, BUILD 21). through `0x40021d94`) never ran and is not detoured -- an assignment completing that way would not reset. `po_machlist` (the sample-list window's machine-only write) is placed and byte-checked, not exercised. - Measured twice: the builder's pass (`root29c/`) and an independent - verifier's on its own panel (`root29c-verify/`, the SAVE / reload, + Measured twice: the builder's pass and an independent + verifier's on its own panel (the SAVE / reload, regression and warm-boot takes). ### The new-project case (OCTATRICK2.9 BUILD 22, 27 Sep 2026): the fourth site @@ -1012,10 +2012,10 @@ so an existing project behaves as measured above. The unit grew by 102 B (the cave is unchanged: 3,236 B left in the octatrick-tuner remix, BUILD 22). -### Measured (27 Sep 2026, the port through the panel; the session's `root29e/m33.py`, logs and shots `root29e/`) +### Measured (27 Sep 2026, the port through the panel; `m33.py`, its logs and shots) The new project: a project directory holding only `project.work` / -`project.strd` (`root29b/PROJECT_ref` with its four `[SAMPLE]` blocks +`project.strd` (`PROJECT_ref` with its four `[SAMPLE]` blocks removed and SCALE / ROOT / GLIDE at their defaults; no bank files, so every Part is stock's default -- the way the tuner rig's card builder stages a project, `panel_server_poke.py --project`), an AUDIO folder @@ -1038,8 +2038,8 @@ every slot empty (`+8` = 1, path ''). after the load were the machine window's machine byte (`0x40079828` -> `0x100a4ef0` = 1, twice) -- no assigner slot write, no site reached. - **BUILD 22 (the fourth site), the same new project from cold boots** - (the builder's chain `root29e/run_e.sh`, then an independent verifier's - own new project and panel, `root29e-verify/run_v*.log`): flow A (T1 + (the builder's chain `run_e.sh`, then an independent verifier's + own new project and panel, `run_v*.log`): flow A (T1 machine window, SYNTH.wav into T1's own slot): RATE Part / shadow / lane 64 / 64 / 64 on the PLAYBACK page, after leaving and re-entering it, and after one CHROMATIC note -- FINE 0c; flow B (T3, FUNC + PLAYBACK machine @@ -1107,7 +2107,7 @@ without a start, and slide. Nothing is restored after the call: the record holds what stock leaves in it, as on a stock unit (until 2.8 the wrapper did nothing at all on a sample track). -### Measured (5 Oct 2026, the octatrick-tuner BUILD 18 bus = OCTATRICK2.8 on ot_emu `--dsp-rt` through the panel; a copy of the OTLIVE card whose `third-0.wav` is a 2 s sine at 300.13 Hz (65 whole cycles in the slot's 9,551-frame marker window, LOOPMODE 1, so the tone holds while a key is held -- the slot's markers come from the project and keep the old length), T7 = FLEX slot 2 and T3 = STATIC slot 2 = that sine, 120 BPM, CHROMATIC keys with key 1 = C4 on T7 (key 1 = C3 on T3: 150 Hz); the session's `v28/slide2.py`, `v28/slide3.py`; pitch by zero crossings per 10 ms, "restart" from the stock voice's play position `0x800049d8 + 0xa8 * t + 68` (its wraps at 9,551 are the loop)) +### Measured (5 Oct 2026, the octatrick-tuner BUILD 18 bus = OCTATRICK2.8 on ot_emu `--dsp-rt` through the panel; a copy of the OTLIVE card whose `third-0.wav` is a 2 s sine at 300.13 Hz (65 whole cycles in the slot's 9,551-frame marker window, LOOPMODE 1, so the tone holds while a key is held -- the slot's markers come from the project and keep the old length), T7 = FLEX slot 2 and T3 = STATIC slot 2 = that sine, 120 BPM, CHROMATIC keys with key 1 = C4 on T7 (key 1 = C3 on T3: 150 Hz); `slide2.py`, `slide3.py`; pitch by zero crossings per 10 ms, "restart" from the stock voice's play position `0x800049d8 + 0xa8 * t + 68` (its wraps at 9,551 are the loop)) - **T7 FLEX, LEG MONO, GLIDE 64**, C4 held and E4 130 ms later: 300.2 -> 377.9 Hz, ONE attack, the position runs on; the pitch leaves 300 within @@ -1131,7 +2131,7 @@ did nothing at all on a sample track). 3 / 4 = `0x40004424` / `0x4000466c` / `0x40004008`, stock's (THRU / NEIGHBOR / PICKUP not exercised). - **The synth path** (T2 = FM SYNTH slot 5 on a copy of the OTLIVE card, - `v28/rec28.py`): a lone C4 261.7 Hz; LEG MONO, GLIDE 64, C4 held + E4: + `rec28.py`): a lone C4 261.7 Hz; LEG MONO, GLIDE 64, C4 held + E4: 261.7 -> 330 Hz, t63 90 ms, t95 290 ms -- as 2.7. - **The recogniser on 2.8's table** (T2, VOIC 3, CHRD `----`, LEG OFF, GLIDE 0, chords rolled 30 ms apart during live recording, the CHRD lock @@ -1139,7 +2139,7 @@ did nothing at all on a sample track). Eb G `MIN`, D F A C `MI7`, C E A `MA6`, D# F G `AD2`, C F# `DIM`, C E `3MA`, E G C5 `MAJ1`, C E D5 `AD9`, C F G `SU4`, and the two changed ties C A `MI6` (was MA6), C A# D5 `MI9` (was DO9) -- 16 of 16 as the - Python model of `po_match` (`v28/chords.py`, which also ran every 2-, + Python model of `po_match` (`chords.py`, which also ran every 2-, 3- and 4-key set within two octaves against 2.7's) predicts. ## Transposing a step: FUNC + UP / DOWN with a trig held (4 Oct 2026) @@ -1218,7 +2218,7 @@ hardware). trig held (the trig keys are notes there); the MIDI-mode pages are not touched (their FUNC + UP / DOWN is another table). -### Measured (4 Oct 2026, the octatrick-tuner BUILD 18 bus = OCTATRICK2.8 on ot_emu `--dsp-rt` through the panel, a copy of the OTLIVE card, T2 = FM SYNTH slot 5, PTCH 0 / FINE 0 / INDX 0 / FDBK 0 (a sine), AMP HOLD INF REL 20, SCALE / GLIDE OFF, GRID RECORDING on, TRACKS mode; the session's `oct/rig_oct.py`, shots `oct/out_new/`; the lock read back from the bank's RAM record `[0x46c82456] + T * 2330 + pattern * 36568 + 0x59 + step * 32`, the selector's state from its handle `0x400bebae` while FUNC was still down) +### Measured (4 Oct 2026, the octatrick-tuner BUILD 18 bus = OCTATRICK2.8 on ot_emu `--dsp-rt` through the panel, a copy of the OTLIVE card, T2 = FM SYNTH slot 5, PTCH 0 / FINE 0 / INDX 0 / FDBK 0 (a sine), AMP HOLD INF REL 20, SCALE / GLIDE OFF, GRID RECORDING on, TRACKS mode; `rig_oct.py` and its shots; the lock read back from the bank's RAM record `[0x46c82456] + T * 2330 + pattern * 36568 + 0x59 + step * 32`, the selector's state from its handle `0x400bebae` while FUNC was still down) - **The octave**: a trig on step 1 with PTCH lock +3 (held trig + knob A). Trig 1 held + FUNC + DOWN -> **-9**; again -> **-21**; FUNC + UP three @@ -1243,7 +2243,7 @@ hardware). up), the locks untouched; the screenshot is **pixel-identical** (0 of 8,192 pixels differ, the text dump equal) to the 2.7 bus (modules b44790b) taken in the same state on a second unit (`07_selector_new.png` - vs `out_ref/selector_ref.png`; a first comparison from a different page + vs `selector_ref.png`; a first comparison from a different page state differed by 30 pixels behind the window -- FDBK 0 vs 127 and the page arrow -- none of them the selector). - **CHROMATIC mode**, trig 1 held + FUNC + DOWN: the selector opened, the @@ -1368,11 +2368,11 @@ of `sy_render`) so the ROM change there is a jsr and a compare for a MIDI note-on and takes the trigless path itself (the lock byte set, `mailbox |= 0x119`, no START, `T_MLEG` for the recorder). -### Measured (2 Oct 2026, the octatrick-tuner BUILD 17 bus = OCTATRICK2.7 on ot_emu `--dsp-rt` through the panel, a copy of the OTLIVE card, T2 = FM SYNTH slot 5 on MIDI channel 2, INDX 0 / FDBK 0, AMP HOLD INF REL 40, AMP = RTRG with ATK 40 so a fresh START shows as a new attack, 130 BPM, GLIDE 64 unless said, the CHROMATIC octave +1: key 1 = C4, 5 = E4, 8 = G4; the session's `leg/live.py`, `leg/chordpress.py`, `leg/audio.py`, `leg/storage.py`, `leg/warm.py`; pitches as spectral peaks per 10 ms, single voices by zero crossings, "retrigger" read from the engine's voice records -- a voice keeps its allocation stamp (`V_AGE`) across a legato press and gets a new one at a START) +### Measured (2 Oct 2026, the octatrick-tuner BUILD 17 bus = OCTATRICK2.7 on ot_emu `--dsp-rt` through the panel, a copy of the OTLIVE card, T2 = FM SYNTH slot 5 on MIDI channel 2, INDX 0 / FDBK 0, AMP HOLD INF REL 40, AMP = RTRG with ATK 40 so a fresh START shows as a new attack, 130 BPM, GLIDE 64 unless said, the CHROMATIC octave +1: key 1 = C4, 5 = E4, 8 = G4; `live.py`, `chordpress.py`, `audio.py`, `storage.py`, `warm.py`; pitches as spectral peaks per 10 ms, single voices by zero crossings, "retrigger" read from the engine's voice records -- a voice keeps its allocation stamp (`V_AGE`) across a legato press and gets a new one at a START) - **The page.** T2's AMP SETUP shows LEG in the sixth box: OFF / MONO / POLY - on the three-position dial as the F knob is turned (`leg_montage.png` - in the session's `leg/out/`), the Part byte 0 / 1 / 2, the knob clamped + on the three-position dial as the F knob is turned (a + `leg_montage.png` of the shots), the Part byte 0 / 1 / 2, the knob clamped at both ends (+7 detents from OFF read 2, -6 from POLY read 0). T7, a sample track: the page is **pixel-identical** to the OCTATRICK2.6 bus (0 differing pixels of 128 x 64), and its F knob leaves the Part byte. @@ -1443,7 +2443,7 @@ for a MIDI note-on and takes the trigless path itself (the lock byte set, T2 LEG Part byte 2, shadow 2, the page reads POLY -- it came back from battery RAM, not the card (GLIDE 64 came back with it). -### Measured (3 Oct 2026, LEG on every audio track: the octatrick-tuner BUILD 17 bus = OCTATRICK2.7 third pass, modules f53032c, on ot_emu `--dsp-rt` through the panel, a copy of the OTLIVE card, 130 BPM; T7 = FLEX slot 2 `third-0.wav`, a sample track, in the CHROMATIC trig mode with the project's SCALE (PHRYGN, so the E4 key sounds Eb4: rate 1.19 = +3 semitones); the session's `leg3/ui.py`, `leg3/t7.py`, `leg3/e.py`, `leg3/storage.py`; "retrigger" on T7 read from the stock voice's play position, `0x800049d8 + 0xa8 * 6 + 68`) +### Measured (3 Oct 2026, LEG on every audio track: the octatrick-tuner BUILD 17 bus = OCTATRICK2.7 third pass, modules f53032c, on ot_emu `--dsp-rt` through the panel, a copy of the OTLIVE card, 130 BPM; T7 = FLEX slot 2 `third-0.wav`, a sample track, in the CHROMATIC trig mode with the project's SCALE (PHRYGN, so the E4 key sounds Eb4: rate 1.19 = +3 semitones); `ui.py`, `t7.py`, `e.py`, `storage.py`; "retrigger" on T7 read from the stock voice's play position, `0x800049d8 + 0xa8 * 6 + 68`) - **The page.** T7's AMP SETUP shows LEG in the sixth box: **OFF / MONO** (`leg3_montage.png`), the Part byte 0 / 1 and its shadow with it; the @@ -1722,7 +2722,7 @@ off here as it does on stock pages. synth track's PTCH from the detent count by scale degree, for any handler -- FUNC and the push then count degrees, not semitones. SCALE OFF: as the table. -### Measured (30 Sep 2026, the OCTATRICK2.4 BUILD 14 bus on ot_emu, a copy of the OTLIVE card, T2 = FM SYNTH slot 5, SCALE OFF; the BUILD 12 bus beside it as the baseline; `knobs_measure.py`, shots `knobs-shots/`) +### Measured (30 Sep 2026, the OCTATRICK2.4 BUILD 14 bus on ot_emu, a copy of the OTLIVE card, T2 = FM SYNTH slot 5, SCALE OFF; the BUILD 12 bus beside it as the baseline; `knobs_measure.py`, its shots) The clone's six handler slots read `po_knob` (`40a96582` x6, `po_pg_built` 1); the pointer block before `sy_render` reads `po_knob po_keyrec po_hold128 @@ -1753,7 +2753,7 @@ byte read back after the turn; the page's text is the shot. - **INDX, FDBK, DEC** from 0, each: plain 1; FUNC + detent 16, again 32; pressed 7; FUNC x9 up: 16 32 48 64 80 96 112 127 127; FUNC down from 127: 111; FUNC down from 0: 0. (DEC 16 prints `32`, its milliseconds; 127 - `2.0s`.) + `2.0s` -- `HOLD` since 2.10, where 0 prints `0`.) - **The baseline (BUILD 12, handlers "0")**: pressed + detent on T2's PTCH already gave 71 (`+7`: the stock default stepper `0x4003240c` reads the push switch), FUNC + detent gave 65 (`+1`, no FUNC layer), FUNC + detent @@ -1824,8 +2824,8 @@ byte and the stock "+7.0" display, byte for byte. What changes, and where: Measured (27 Sep 2026, the panel on 8901, OCTATRIK12 = octatrick-tuner BUILD 12 with this tree, a copy of the OTLIVE card, T2 = FLEX slot 5 SYNTH.wav, SCALE OFF, GLIDE OFF, INDX 0 / FDBK 0, AMP HOLD INF / REL 20; -notes = spectral peaks over 0.3-0.5 s windows; `tuning/measure.py` in the -session scratchpad, `report_*.txt`): +notes = spectral peaks over 0.3-0.5 s windows; `measure.py` in the +author's workspace (not in the repo), `report_*.txt`): - **Keyboard octaves** (VOIC 1, key 13): octave -2 = 65.4 Hz, -1 = 130.8, 0 = 261.6, +1 = 523.2, +3 = 2093.0; +4 = 4186.0 (key 16 = 4978.0, D#8); @@ -1891,7 +2891,7 @@ envelopes, release and glide -- summed into the track's one source stream. LFO 3 is muted on a synth track (its slots are VOIC and CHRD there).** Normal FLEX tracks keep the stock page and their three LFOs. Emulation only (`ot_emu` through the virtual panel and the pipe), flashed as OCTATRICK9 on an MKI, 26 Sep 2026 (emulator-verified since); scripts, -captures and screens in the session scratchpad `poly/`. +captures and screens in the author's workspace (not in the repo). ### The DRAM chain (step 1) @@ -1937,6 +2937,10 @@ tables at `0x400d7480..0x400d7594`. read the Part's byte. **VOIC 1 is the exact mono synth of OCTATRICK4**: this file's mono path, GLIDE legato through the quantizer's hooks, the stock voice lifecycle, the same level. +- **2.10 (8 Oct 2026): 6 dB of headroom.** The levels this section states and + measures (the mono voice's peaks, FS, the coincident peaks of a chord) are + from before it: since 2.10 the mono voice and each paraphonic voice are + 6.02 dB lower, the relations unchanged; see "6 dB of headroom (2.10)". - **The level: equal power, a voice is `1 / sqrt(VOIC)` of the mono voice, and a peak limiter** (29 Sep 2026, Tim: "the volume difference between mono and 2 voices and 3 voices is too drastic" -- the 1/VOIC level of 28 @@ -2056,8 +3060,8 @@ tables at `0x400d7480..0x400d7594`. : 1.498 (3 notes). Level, as measured then: two voices in phase reached full scale, so the sum's peaks clipped and third-order products sat at about -26 dB (the phase 5 level design; **changed 28 Sep 2026, "The - level" below**). Scripts and captures: the session scratchpad - `voiccap/` (`measure.py`, `t1keys.py`, `ab.py`). + level" below**). Scripts and captures: the author's workspace (not in the repo: + `measure.py`, `t1keys.py`, `ab.py`). **The voicings and the level, measured (29 Sep 2026)** (the panel on 8906, `--sound on`, bus = octatrick-tuner BUILD 13 = OCTATRIK13 with @@ -2066,8 +3070,8 @@ tables at `0x400d7480..0x400d7594`. OFF, CHROMATIC keys, key 13 = C4; the emulator's clock reads C4 as 271.3 Hz, ratio 1.037, every pitch below is scaled by it; peaks and lines over the last 0.5 s of a 0.9 s hold, the engine's `V_STATE` / `V_GAIN` / - `T_LIM` read beside them; scripts and captures in the session scratchpad - `level2/` -- `measure4.py`, `diag.py`, `diag2.py`). The mono voice (VOIC + `T_LIM` read beside them; scripts and captures in the author's workspace (not in the repo + -- `measure4.py`, `diag.py`, `diag2.py`). The mono voice (VOIC 1) peaks at **-16.49 dBFS** at C4 and within 0.02 dB of it at E4 F4 G4 B4 C5 F5 C6 C7: the reference. - **The shapes**: `4TH` at VOIC 2 = C4 F4 (271.3, 362.1 Hz; C5 and F5 @@ -2119,8 +3123,7 @@ tables at `0x400d7480..0x400d7594`. DELAY flattened on the copy first -- the project's resonant filter lifts C5 15 dB over C4 and the delay adds echoes, which had made the first pass unreadable; the emulator's clock reads C4 as 271.3 Hz, ratio 1.037; - peaks and lines over the last 0.5 s of a 0.9 s hold; the session - scratchpad `level/measure3.py`). The mono voice (VOIC 1, key 13) peaks + peaks and lines over the last 0.5 s of a 0.9 s hold; the author's workspace (not in the repo) `measure3.py`). The mono voice (VOIC 1, key 13) peaks at **-16.49 dBFS** at C4, and the same at C5, C6, C7, G4 and G5 through the keyboard octave (the flat chain): that is the reference. - single notes: VOIC 2 **-22.52** dBFS = **-6.02** dB re the mono voice, @@ -2248,7 +3251,7 @@ tables at `0x400d7480..0x400d7594`. `NAME2` `NAME3` = the first, second, third inversion (byte + 1, 2, 3): the bass (= PTCH) is the shape's second, third, fourth distinct note and the notes below it go up an octave. The knob's detents, measured on - the LFO page (`v26/out/chrd_knob_grid.png`, 2.7's table): raw 0 and 1 + the LFO page (`chrd_knob_grid.png`, 2.7's table): raw 0 and 1 `----`, 4 `4TH`, 36 `MAJ`, 37 `MAJ1`, 38 `MAJ2`, 39 `MAJ3`, 40 `MIN`, 92 `MA7`, 93 `MA71`, 95 `MA73`, 96 `MI7`, 124 `QUA`, 127 `QUA3` -- on 2.8 the same detents read `----`, `MIN`, `DI7`, `DI71`, `DI72`, `DI73`, @@ -2286,7 +3289,7 @@ other), the last key's release posts the AMP release as stock. Level: a paraphonic note is 6 dB below the mono voice (each voice at 1/2), four in phase reach full scale. -### Measurements (24 Sep 2026, the panel on 8593, a copy of the OTLIVE card, T2 = SYNTH slot 5, INDX 0 / FDBK 0 for clean lines unless said; `poly/voicF pB3 pC pD2 pF chrom cost`) +### Measurements (24 Sep 2026, the panel on 8593, a copy of the OTLIVE card, T2 = SYNTH slot 5, INDX 0 / FDBK 0 for clean lines unless said; the rigs `voicF pB3 pC pD2 pF chrom cost`) - **VOIC 1 == OCTATRICK4**: the lockstep rig on `synth8q.img` (T8 = SYNTH, its SPD3 byte the stock 32 = VOIC 1): the sequencer fixture (trigs on @@ -2344,7 +3347,7 @@ renderer. Hardware cost unmeasured. A verification run reported a constant 6,201.6 Hz tone at full level (with harmonics on the 689 Hz = 44,100/64 grid) that appeared after a legato step and never stopped. Reproduced on a fresh boot with the same sequence -(`poly/stuck_loop.py`, `stuck/it1_key13_1.json`) and peeked while it sounded: +(`stuck_loop.py`, `it1_key13_1.json`) and peeked while it sounded: T2's stock voice struct **inactive** (`0x800049d8 + 0xa8` = `00`), the engine idle for it (`T_POLY` 0, the voices free), no key held, no pending key; **T5's voice struct active (`0xff`) on flex slot 1**; muting T5 (FUNC + @@ -2369,7 +3372,7 @@ owner went). The mono path's samples are unchanged by them: on this build the lockstep VOIC 1 A/B against OCTATRICK4 is again **0 samples differ** at a one-sample capture alignment (the sequencer fixture 120,090 frames, the CHROMATIC key sequence 233,954 frames), and the reproduction loop -(`poly/stuck_loop.py`: fresh boot, PLAY/STOP, INDX 0, SCALE and GLIDE OFF, +(`stuck_loop.py`: fresh boot, PLAY/STOP, INDX 0, SCALE and GLIDE OFF, CHRD MAJ, CHROMATIC verified by peek, key 13 x4, GLIDE 64 legato 13 + 16, VOIC 3 MAJ under SCALE OFF and PHRYGN, back to VOIC 1, rapid overlapping presses with GLIDE 64 and off) ran **10 fresh boots clean** (every key press 261.4 Hz, every MAJ 261.6/329.6/392.0, every PHRYGN MAJ 261.6/311.1/391.9) -- and in @@ -2407,14 +3410,14 @@ still its held key (`HELD` = the key): a re-press inside one key scan -- -- or, on hardware, a key bounce. On a paraphonic track `qz_leg1` skips the note-off (the other keys must keep sounding) and `qz_leg2` posts a fresh trig, so `po_start` allocated a second voice: **two sounding voices of key -13, both at full gain, on every such re-press** (`chord/part1c.py`: 2 +13, both at full gain, on every such re-press** (`part1c.py`: 2 allocations, voices `(1, 13) (1, 13)`). At VOIC 1 the same press goes down the stock note-off + retrig path and restarts the one mono voice, which is why the doubling only exists at VOIC 2..4. (2) During live recording, the pattern's own trigs replaying under the finger: the recorder quantizes a key to the nearest step and a recorded trig is a sequencer trig to the engine (`qz_pkey` 0), which released the key's voice and allocated its own -- the -same note twice, tens of ms apart, on the next pass (`chord/part1.py`: 2-3 +same note twice, tens of ms apart, on the next pass (`part1.py`: 2-3 allocations a press once the pattern had wrapped). The race the report guessed at (the key post and the START in the other order) cannot happen: `qz_leg2` writes `qz_pkey`/`qz_pmask` before it posts the mailbox, and a @@ -2436,8 +3439,8 @@ the note sustains while the key is held and releases with it. A shape voice still restarts on every trig word, as before. **Measured (26 Sep 2026, ot_emu on the OCTATRICK2.4 bus, T2 = FMSYNTH, -SCALE/GLIDE OFF, INDX 0 / FDBK 0, AMP HOLD INF, `chord/part1_final.py`, -`chord/part1_audio.py`; the engine's allocation stamp `po_seq` and the +SCALE/GLIDE OFF, INDX 0 / FDBK 0, AMP HOLD INF, `part1_final.py`, +`part1_audio.py`; the engine's allocation stamp `po_seq` and the voice states read from RAM, key 13 = C4):** | case | allocations per press | sounding voices 150-250 ms after the press | @@ -2589,8 +3592,8 @@ card, T2 = FM SYNTH, VOIC 3 unless said, CHRD "----", SCALE OFF, GLIDE OFF, AMP HOLD INF / REL 40, INDX 0 / FDBK 0, 130 BPM (a step = 115 ms), CHROMATIC octave +1 (key 1 = C4); keys held 0.5 s, the locks read back from the pattern record, the live lines from the engine's voices and the take, -playback = the lines 0.03-0.4 s after the first onset; `v26/cases.py`, -logs `v26/out_*.log`):** +playback = the lines 0.03-0.4 s after the first onset; `cases.py`, +logs `out_*.log`):** | keys | recorded (one step unless said) | live | playback | |---|---|---|---| @@ -2719,7 +3722,7 @@ index-1 form), this cave at 0x400d6d00, the page cave at 0x400d24d0 byte at 0x400d2cdc (the second run: 112 B left between the page cave and it; the byte moved to battery RAM 0x100b14ed on 26 Sep 2026). -### Measurements (24 Sep 2026, the panel on 8593, `--sound on`, T2 = SYNTH slot 5 of a copy of the OTLIVE card; the session scratchpad's `glide_audio2/3.py`, `glide_seq2.py`) +### Measurements (24 Sep 2026, the panel on 8593, `--sound on`, T2 = SYNTH slot 5 of a copy of the OTLIVE card; the author's audio and sequencer scripts, not in the repo) The legato and glide numbers are in `modules/quantizer/README.md` ("GLIDE and legato", measurements 2-3): CHROMATIC [TRIG 13] held, [TRIG 16] @@ -2798,8 +3801,9 @@ names read **PTCH RATO INDX RATE FDBK DEC** (four characters: the boxes are 19 px wide and a five-character name fills them edge to edge, measured below); the four synth slots show their value all the time, Digitone style, formatted as the voice understands them -- RATO the ratio table's entry -(`0.25 … 1 1.41 … 3.5 … 16`), INDX and FDBK `0..127`, DEC `HOLD` / `32ms` / -`286ms` / `2.0s`; and each draws an icon where the sample dial was: the two +(`0.25 … 1 1.41 … 3.5 … 16`), INDX and FDBK `0..127`, DEC `32ms` / +`286ms` / `1.9s` / `HOLD` (since 2.10 HOLD is raw 127 and raw 0 prints `0`, +the shortest; before, raw 0 was `HOLD` and 127 `2.0s`); and each draws an icon where the sample dial was: the two operators **M→C** (RATO), a **sideband spectrum** whose bars grow with the index (INDX), the modulator **with its feedback loop** when FDBK > 0, and the **index envelope**, a falling curve whose length follows the value (DEC; a @@ -2808,7 +3812,7 @@ every other page draw byte for byte as stock. One pinned cave (1,672 bytes, `page.s`; the descriptor itself is a runtime clone built in the DRAM unit, below) and one 6-byte poke; **flashed as OCTATRICK9 on an MKI, 26 Sep 2026 (emulator-verified since)**, everything below measured under `ot_emu` through the virtual panel and the oracle, 22 Sep 2026; logs, -screens, takes: `out/_agents/synth3/`. +screens, takes: the author's workspace (not in the repo). ![the page](page_montage.png) @@ -2817,7 +3821,7 @@ STRT 0 LEN 0 RATE 127 RTRG 0 RTIM 79): the defaults (RATO 0.25, INDX 0, FDBK 0, DEC 774ms); RATO raw 36 = `2`; raw 127 = `16`; INDX 127 (the full spectrum); FDBK 64 (the loop); DEC 16 = `32ms` (a short curve); DEC 127 = `2.0s`; DEC 0 = `HOLD` (flat); and T7, a FLEX track with a sample, unchanged -(`shots/remix/`, `shots/stock/`). +(shots of the remix and of the stock image). ### Where the page comes from, and the hook @@ -2966,13 +3970,15 @@ DEC: the baseline, an instant rise at column 1 (rows 1–11), then I/16) stretched over `L = 2 + 13·v/127` columns 2–15 (`i = 16·(c−1)/L`, clipped to 16), drawn as the vertical runs between consecutive heights (an outline); -v = 0 keeps `i = 0`, a flat top at row 11 — the index holds. +v = 127 keeps `i = 0`, a flat top at row 11 — the index holds (v = 0 until +2.10; v = 0 is now the shortest fall, L = 2). **Formatters** (`fmt(buf, value)`, C convention, `sprintf` `0x40013a08`): `pg_fmt_plain` `"%d"` (INDX, FDBK); `pg_fmt_ratio` looks the raw value up in a copy of `sy_ratio` (`raw >> 2`, Q8) and prints `"%d"` when the fraction is 0, `"%d.5"` when it is .50, else `"%d.%02d"` (`0.25 0.5 0.75 1 1.01 1.25 -1.41 1.5 1.75 2 2.01 … 16`); `pg_fmt_decay` prints `HOLD` for 0, else +1.41 1.5 1.75 2 2.01 … 16`); `pg_fmt_decay` prints `HOLD` for 127 (for 0 +until 2.10; 0 now takes the formula and prints `0`), else `ms = (2000·raw² + 8064) / 16129` (τ = 2 s·(raw/127)², rounded) as the milliseconds alone (`"%d"`) below 1000 and `"%d.%ds"` above (raw 8 → `8`, 16 → `32`, 48 → `286`, 79 → `774`, 96 → `1.1s`, 127 → `2.0s`) -- the value @@ -2992,7 +3998,7 @@ is `poly.s`'s RAM now), **pinned at `0x400d24d0`** — the second zero run 60 B before this change) — because the override list holds absolute pointers to the formatters and widgets; `PINNED_PAGE` in `manifest.py` is the ratified form (linked with `m68k-elf-as -mcpu=5475`, -`ld -Ttext=0x400d24d0`, `objcopy -j .text`; `out/_agents/synth3/asm/`), the +`ld -Ttext=0x400d24d0`, `objcopy -j .text`), the build re-links the source there and refuses on a difference, `emit()` returns `b""` plus the one poke. The phase-2 voice cave is untouched and still floats. `REMIX=synth make cf`: **2,345 bytes changed** (was 1,153), @@ -3003,13 +4009,13 @@ of the third run left** as before (`build_tim_v3.log`); `out/mainos_cf.bin` ends as the `tim` build. Both keep the DSP payloads, dispatch and the FX2 chooser byte-identical to stock (the CFONLY check). -### Measurements (all `out/_agents/synth3/`; the panel on 8593/8594, +### Measurements (the author's workspace, not in the repo; the panel on 8593/8594, `--image mainos_synth_v2.bin` (the first build; the shipped form differs only by the marker gate, §6) / the stock section, `--card` a fresh copy of -phase 1's `synth8q.img`; `session3.py`, `remix.log`, `stock.log`, `shots/`) +phase 1's `synth8q.img`; `session3.py`, `remix.log`, `stock.log`, shots) **1. The page.** Booted, [T8], [PLAYBACK]: the frames in the montage and -`shots/remix/01..16`; the Part bytes after each turn confirm the encoders +`01..16`; the Part bytes after each turn confirm the encoders map 1:1 (`remix.log`: RATO +12 +24 +16 +75 → STRT `0c 24 34 7f`, INDX +32 +32 +63 → LEN `20 40 7f`, FDBK +1 +63 → RTRG `01 40`, DEC −63 +32 +79 −127 → RTIM `10 30 7f 00`; `/knob/reset` restores the defaults). The value @@ -3020,11 +4026,11 @@ frame); the icons change as designed (INDX 0 → one bar, 32 → three, 64 a 4-column drop, 48 → 7, 127 → the full width, 0 → flat). Names: the first build carried `RATIO INDEX DECAY` — five characters are 19 px at the 3×5 font's 4-px pitch, exactly the box interior, so `RATIO` and `INDEX` touched -across the dotted separator (`shots/remix_v1/`, `remix_v1.log`); the +across the dotted separator (the first build's screens, `remix_v1.log`); the four-character forms have the stock's 2-px margins. **2. Everything else is stock.** The same key script on the stock image -(port 8594, `shots/stock/`): `00_main`, T7's PLAYBACK page and its STRT +(port 8594, the stock shots): `00_main`, T7's PLAYBACK page and its STRT turn, T7's AMP / LFO / FX1 / FX2 / MIXER, T8's AMP / LFO / FX1 / FX2 / MIXER, the FLEX slot list (double-tap [T8]: `« MACHINE:FLEX`, `5▸SYNTH.wav 0.33`) and the file browser (`LOAD FILE TO FLEX 6`) — **16 frames @@ -3034,9 +4040,9 @@ are T8's PLAYBACK page, by design. Locks: REC and [TRIG 9] held on T8 carries no PLAYBACK lock — so the highlight path (the stock invert plus the icon's own inversion) is by construction, not measured. -**3. Boot A/B** (`ab/`; `tools/emu/ot_emu/oracle/drive.py --emu +**3. Boot A/B** (`tools/emu/ot_emu/oracle/drive.py --emu out/emu/ot_emu --image `, the `inter` battery -on the OTLIVE card; `ab/remix` is the first build, `ab/remix_v3` the +on the OTLIVE card; `remix` is the first build, `remix_v3` the shipped one): `peeks.txt` and `stderr.txt` byte-identical; `tx.bin` **18,297 vs 18,289 bytes** — with every LED-level pair (`0x3n `) removed the streams are identical (**16,873 bytes: every LCD block and every @@ -3050,13 +4056,13 @@ lookup (the first build: 18,293 bytes, `3d 24`/`3d 25` alone; `ready` (`modules/quantizer/README.md` §5). No text, no state peek differs. **4. The sound is phase 2's.** PLAYBACK page, RATO +36 (ratio 2), INDX +64, -PLAY 4.2 s, STOP → `takes/remix_fm_r2_i64.wav` (`fm.py --ratio 2`, 0.15–0.95 +PLAY 4.2 s, STOP → `remix_fm_r2_i64.wav` (`fm.py --ratio 2`, 0.15–0.95 s): **261.626 Hz (+0.0 cents)**, the lines at 262 ± k·523 Hz: 262 0, 1308 −2.2, 785 −12.4, 1831 −16.0, 2355 −21.1, 2878 −37.3 dB, worst spur off the lines −51.3 dB (a Hann sidelobe); the sidebands sit lower than phase 2's held-index table because DEC stayed at its default 774 ms and the index -decays through the window. The `tim` image (`tim_check.py`, `shots/tim/`, -`takes/tim_fm_r2_i64.wav`) draws the identical page (`/screen.txt` equal to +decays through the window. The `tim` image (`tim_check.py`, its shots, +`tim_fm_r2_i64.wav`) draws the identical page (`/screen.txt` equal to the `synth` remix's, T7 equal to stock) and measures the identical lines. **5. Gates** (`gates.log`, `gates.sh`, `REMIX=synth`): `make bus`, @@ -3070,8 +4076,8 @@ mode_views" (the Makefile's SKIP); `verify_replaces` fails only on the eight MIDI SCENES remixes without the submodule (pre-existing). **6. A sample loaded through the file browser** (`browser_load.py`, -`repro_v2.log` / `repro_v3.log`, `shots/repro_v2/`, `shots/repro_v3/`, -`takes/repro_*`): the fixture booted, T8's PLAYBACK page and a take on the +`repro_v2.log` / `repro_v3.log`, the shots and takes of both, +`repro_*`): the fixture booted, T8's PLAYBACK page and a take on the project-file slot 5; then double-tap [T8] → the FLEX slot list, DOWN ×2 → slot 7, RIGHT → `LOAD FILE TO FLEX 7`, DOWN ×36 → `SYNTH.wav`, YES (load), YES (assign: T8's slot byte `04 → 06`), NO NO, [PLAYBACK], a take. The new @@ -3110,7 +4116,7 @@ pages read the synth and both takes carry the voice (§6 numbers in scan, ≤ 255 bytes) — UI-thread only. - Flashed as OCTATRICK9 on an MKI, 26 Sep 2026 (emulator-verified since). -Tooling for the record (`out/_agents/synth3/`): `gen_page.py` (the clone +Tooling for the record (the author's workspace, not in the repo): `gen_page.py` (the clone and the column bitmaps from ASCII art), `dis.py` (listing slices by address), `montage.py`, `session3.py`, `slotlist_check.py`, `tim_check.py`, `gates.sh`; the disassembly slices read for this phase are `dis_*.txt`. @@ -3134,7 +4140,7 @@ FDBK / DEC, with icons -- "Phase 3: the page" below): | LEN (C) | **INDEX** | `fp[2]` = `+4` | I = 8 rad · raw/127, linear (the word's low byte counts: LFO and scene fractions morph smoothly); INDEX 0 = a clean sine | | RATE (D) | playback rate | `fp[3]` = `+6` | the stock rate arithmetic (RATE scales the pitch increment; 127 = ×1) | | RTRG (E) | **FEEDBACK** | `fp[4]` = `+8` | 0 .. 0.25 cycle of modulator phase per full-scale modulator sample, linear in raw; the stock retrig it used to be is switched off for synth voices | -| RTIM (F) | **DECAY** | `fp[5]` = `+10` | the index envelope: E := 1 at the trig, then `E -= (E − 1/16)·k` per frame, k = 3,068,384 / raw² (Q20; ≥ 1 = instant), i.e. an exponential toward I/16 with time constant τ = 2 s · (raw/127)²: raw 8 = 8 ms, 16 = 32 ms, 32 = 127 ms, 48 = 286 ms, 64 = 0.5 s, 96 = 1.14 s, 127 = 2.0 s; **0 = no decay** (the index holds) | +| RTIM (F) | **DECAY** | `fp[5]` = `+10` | the index envelope: E := 1 at the trig, then `E -= (E − 1/16)·k` per frame, k = 3,068,384 / raw² (Q20; ≥ 1 = instant), i.e. an exponential toward I/16 with time constant τ = 2 s · (raw/127)²: raw 8 = 8 ms, 16 = 32 ms, 32 = 127 ms, 48 = 286 ms, 64 = 0.5 s, 96 = 1.14 s, 126 = 1.97 s; **127 = no decay** (the index holds), **0 = the shortest** (k = K_MAX, instant) -- since 2.10; until then 0 held and 127 was 2.0 s | Locks, scenes and LFOs on those slots reach the voice every frame because the cave reads the per-frame record the frame builder already fills for @@ -3142,7 +4148,7 @@ the DSP (the packer's per-track pointer `0x800062a8`; the record for track t of ping p is `0x80000510 + 384·p + 48·t`). The layout was measured by poking the Part bytes `0x40171062..67` (T8: PTCH STRT LEN RATE RTRG RTIM) to `64 11 22 100 33 44` and reading both ping records: `4000 0b00 1600 -6400 2100 2c00` (`runs/rec_poked.log`; defaults `4000 0000 0000 7f00 0000 +6400 2100 2c00` (`rec_poked.log`; defaults `4000 0000 0000 7f00 0000 4f00`). **This corrects phase 1's guess**: the stock renderer's `fp@(0)` is PTCH (0x4000 = 0 semitones), `fp@(6)` is RATE (0x7f00 = ×1, applied when the mode byte `fp@(27)` is 0), and `fp@(10)` — which phase 1 read as PTCH — @@ -3195,7 +4201,7 @@ is ≤ 1.0, so the sample never exceeds it — nothing to saturate; measured **Space.** The cave is **1,684 bytes** (code 0x000–0x30d, `sy_ratio` 0x30e, `sy_tab` 0x350, per-track state 8 × 40 B at 0x554), position independent — linked at `0x400d6b80`, `0x400d6e00`, `0x400d7000` and -`0x400d7300` the bytes are identical (`out/_agents/synth2/asm/`) — and +`0x400d7300` the bytes are identical — and pinned as phase 1 (`PINNED`, `reference=lambda addr: PINNED`, `emit()` returning `b""` plus the kind-table poke). `REMIX=synth make cf`: **1,153 bytes changed**, cave at `0x400d6b80`, **2,600 B of cave left** @@ -3207,7 +4213,7 @@ the second zero run (`0x400d24d0`, 2,064 B) or a DRAM unit. Both DSP payloads, dispatch and the FX2 chooser byte-identical to stock (the CFONLY check). -### Measurements (all `out/_agents/synth2/`; lockstep `--dsp` unless said) +### Measurements (the author's workspace, not in the repo; lockstep `--dsp` unless said) Rig: `rig.py` (render.py with timed pokes and pc watches), `fm.py` (FFT of a window: the fundamental with parabolic interpolation, the FM lines @@ -3221,7 +4227,7 @@ Part bytes before PLAY (`0x40171063` STRT, `..64` LEN, `..66` RTRG, `..67` RTIM, `..62` PTCH); windows are 0.15–0.95 s (the first note, past the voice-start burst described under "What does not work"). -**1. INDEX 0 = a clean carrier** (`runs/base`, defaults: STRT 0, LEN 0, +**1. INDEX 0 = a clean carrier** (`base`, defaults: STRT 0, LEN 0, RTIM 79): **261.626 Hz (+0.0 cents)** by FFT, 261.636 Hz by zero crossings, −24.0 dBFS L/R. Spur: −54.0 dB with the Hann window (that is the window's own sidelobe at ±7 Hz, as phase 1's −51/−52 were); with a @@ -3230,7 +4236,7 @@ and the harmonics h2–h5 are −104 to −119 dB. The second note (1.15–1.95 reads the same pitch, spur −77 dB. Under the panel's rt JIT mode (take 12, defaults) 261.626 Hz, spur −54 (Hann). -**2. Sidebands** (`runs/r{1,2,35}i{32,64,127}`, RTIM 0 so the index +**2. Sidebands** (`r{1,2,35}i{32,64,127}`, RTIM 0 so the index holds; STRT raw 12 / 36 / 52 = ratio 1 / 2 / 3.5, LEN raw 32 / 64 / 127 = 2.0 / 4.0 / 8.0 rad). Every line sits at f0 ± k·ratio·f0 to the Hz, the fundamental stays at 261.62 Hz (±0.1 cents) in all nine, and the @@ -3246,11 +4252,11 @@ strongest peaks (dB relative to the strongest line) are: fundamental's J0 is small, at 8 rad the energy sits at k = 5–7). Off the lines the worst spur is −54 dB (a window sidelobe) at INDEX 32; the "spurs" at higher index (−33 to −1 dB) are the k = 7.. lines beyond fm.py's -six. WAVs to hear: `wav/fm_ratio1_index64.wav`, -`wav/fm_ratio2_index64.wav`, `wav/fm_ratio3.5_index64.wav`, plus +six. WAVs to hear: `fm_ratio1_index64.wav`, +`fm_ratio2_index64.wav`, `fm_ratio3.5_index64.wav`, plus `fm_ratio2_index127.wav`, `carrier_index0.wav`. -**3. DECAY** (`runs/dec16`, `runs/dec48`: ratio 2, INDEX 64, RTIM 16 = τ +**3. DECAY** (`dec16`, `dec48`: ratio 2, INDEX 64, RTIM 16 = τ 32 ms and RTIM 48 = τ 286 ms). Sideband/fundamental energy per 25 ms bin from the trig (dB; the first bin holds the voice-start burst): @@ -3263,11 +4269,11 @@ from the trig (dB; the first bin holds the voice-start burst): index passes 2.4 rad, and fitting the small-index tail (0.2–0.5 s, where the ratio ≈ I²/2: I = 1.15 → 0.58 rad) gives **τ = 0.295 s** against the designed 0.286 s; −10 dB relative to the 100 ms value at ~170 ms. - `wav/fm_ratio2_index64_decay48.wav`. + `fm_ratio2_index64_decay48.wav`. **4. A lock on one step; a scene on the crossfader** (the panel on 8593, `--image mainos_synth.bin --card cards/panel.img --sound on`, rt 1.002; -`session.log`, `shots/`, `takes/`). [T8], PLAYBACK page, STRT +36 (Part +`session.log`, shots and takes). [T8], PLAYBACK page, STRT +36 (Part `40 24 00 7f 00 4f`: ratio 2), grid recording, [TRIG 9] held, LEN +64 → **the lock byte `0x400e62f1` = 0x40** (T8's track record `0x400e6196` + 0x59 + 8·32 + 2; PTCH/STRT stay `ff`) and the Part's LEN untouched (a @@ -3283,10 +4289,10 @@ held, STRT +64 → scene B's RATIO = raw 100 (10.0), the Part's STRT still The crossfader morphs the raw STRT value, so RATIO steps through the table (2 → 5 → 10 here); INDEX under a scene would morph smoothly. WAVs: -`wav/panel_sceneA_ratio2_step1idx127_step9lock64.wav`, -`wav/panel_xfader64_ratio5.wav`, `wav/panel_sceneB_ratio10.wav`. +`panel_sceneA_ratio2_step1idx127_step9lock64.wav`, +`panel_xfader64_ratio5.wav`, `panel_sceneB_ratio10.wav`. -**5. PTCH ±12** (`runs/p12`, `runs/m12`: Part PTCH 124 / 4, ratio 2, INDEX +**5. PTCH ±12** (`p12`, `m12`: Part PTCH 124 / 4, ratio 2, INDEX 64): **523.250 Hz (−0.0 cents)** and **130.813 Hz (+0.0 cents)**; the lines at 523 ± k·1046 and 131 ± k·262 with the same relative levels as at PTCH 0 (−4.4/−5.2/0/−14.7/−5.5/−19.4/−21.6 dB for k = −1..6, i.e. the @@ -3295,7 +4301,7 @@ timbre is pitch-invariant); **worst non-harmonic spur −92.3 dB at +12, first-order k = 7.. lines above the six fm.py lists reach 7,849 Hz at −37 dB at +12, which is the FM spectrum itself, not aliasing. -**6. Mute, AMP, FX1** (`runs/amp`, `runs/fx1`, `takes/mute2.wav`). AMP +**6. Mute, AMP, FX1** (`amp`, `fx1`, `mute2.wav`). AMP ATK 60 / HOLD 30 / REL 40 poked (`0x40171128..2a`): the 100 ms envelope of each note reads `−31 −34 −29 −26 −24 −31 −94 −90 …`, rising and released as phase 1's sine did. FX1 FILTER BASE 100 (`0x4017112e`): −38.9 dBFS @@ -3304,9 +4310,9 @@ phase 1's −69.7 for a bare 261 Hz). FUNC + [T8] at 2.0 s of a panel take (mute mask `0x8000000a` → 0x80): `… −23 −24 −25 −30 −90 −90 −90 −90 −90 −90 −18 −20 −36 −85 −104 −90 …` — digital silence after the mute, except the DSP's voice-start burst at the 3.0 s trig (below), which the mute does not -stop either. `wav/panel_mute_at_2s.wav`. +stop either. `panel_mute_at_2s.wav`. -**7. Cost** (`runs/base/hits.txt`, `runs/p12/hits.txt`; `cost2.py hits +**7. Cost** (the PTCH 0 and PTCH +12 runs' `hits.txt`; `cost2.py hits 400d6b80 400d6e86` — the cave's entry and its `rts` at +0x306 — plus the stock pair `0x40004008/0x40004266`; T8 = SYNTH, the frame's event nibble is 4 on this card so the calls are [0,4) and [4,16)): @@ -3326,7 +4332,7 @@ stock renderer's part is what a playing FLEX track costs anyway. **7b. The settings-record bounds check (24 Sep 2026).** A start frame's name scan follows the voice struct's `+8` pointer; it is now scanned only when it lies inside the settings table (`0x100b14f0` + 136 x `0x448`), because on the unit RAM after power-on is garbage and a refused start leaves the previous value -- the emulator's zeroed RAM never showed it. Code +14 B, the tables moved to `+0x360` / `+0x564`, cave 1,700 B. -**8. Stock behaviour untouched** (`ab/`): `tools/emu/ot_emu/oracle/drive.py +**8. Stock behaviour untouched** (A/B boots): `tools/emu/ot_emu/oracle/drive.py --emu out/emu/ot_emu --image `: `ready.txt`, `steps.txt`, `stamps.txt`, `peeks.txt`, `txlen.txt`, **`tx.bin`** and `stderr.txt` byte-identical; `boot.log` differs only in the image path @@ -3335,10 +4341,10 @@ same results as phase 1 — every `verify_*` and `make bus`/`make cf` exit 0, `verify_midiscenes`/`verify_labels`/`verify_burn` their usual SKIPs, `verify_modenames` its "no module declares mode_views", `verify_replaces` failing only on the eight MIDI SCENES remixes without the submodule -(pre-existing). `REMIX=tim make cf` boots and plays the voice (`runs/tim`: +(pre-existing). `REMIX=tim make cf` boots and plays the voice (`tim`: 261.626 Hz, the ratio-2 lines). -**FEEDBACK** (`runs/fb`, `wav/fm_ratio1_index64_feedback127.wav`: ratio +**FEEDBACK** (`fb`, `fm_ratio1_index64_feedback127.wav`: ratio 1, INDEX 64, RTRG 127): the ratio-1 lines stay (523 0, 262 −0.4, 785 −2.7, 1046 −13.4 …) and the spectrum fills up to Nyquist (a cluster at 21.3–21.9 kHz at −3 to −13 dB): full feedback on a 262 Hz modulator is a bright, @@ -3349,11 +4355,11 @@ subtle; the useful range is the lower half. - **A ~30 ms burst at every voice start, up to full scale**, in the emulator: the stock image playing the silent `SYNTH.wav` on the same card - produces the identical sample sequence at each trig (`runs/stock_silent`: + produces the identical sample sequence at each trig (`stock_silent`: `103 −310 −916 −587 957 2037 …` from sample 81, peaks 19,281 at 0.0 s and - 32,767 at 1.0 s; phase 1's image the same, `runs/len0`), it passes the + 32,767 at 1.0 s; phase 1's image the same, `len0`), it passes the track mute, and at 12 ms the T8 record already holds the cave's own sine - (`runs/hdr_watch.log`) — so it is the DSP's voice-start path (or its + (`hdr_watch.log`) — so it is the DSP's voice-start path (or its emulation) working on stale data, not the cave. Phase 1 saw only its RMS trace ("a 1 dB dip in one 10 ms bin"; the 100 ms envelopes here read −17 /−19 at 1.0–1.2 s). It hides the retrigger click the start ramp was for, @@ -3399,7 +4405,7 @@ One ColdFire cave (1,068 bytes, floating, position independent) and one hook in a stock routine. **flashed as OCTATRICK9 on an MKI, 26 Sep 2026 (emulator-verified since)**; everything below is measured under `ot_emu` — the lockstep interpreter through the pipe, and the panel's real-time JIT mode through keys and takes — 22 Sep 2026. Logs, WAVs, -screens and numbers: `out/_agents/synth/`. +screens and numbers: the author's workspace (not in the repo). To try it: build (`PATH=.venv/bin:$PATH REMIX=synth make cf` or `REMIX=tim make cf` → `out/mainos_cf.bin`), put any WAV named `SYNTH.wav` (or @@ -3440,7 +4446,7 @@ shipped. The DSP resamples that source by the voice's rate, which is where PTCH, the locks and the LFO are already folded in — no pitch arithmetic in the cave at all. -## What was found (stock 1.40C main OS at `0x40000400`; listing `out/_agents/synth/mainos.dis`) +## What was found (stock 1.40C main OS at `0x40000400`; a disassembly listing of it) ### The per-frame record and its renderers @@ -3449,11 +4455,11 @@ the cave at all. | the record packer | `0x4000d3fc..0x4000d55e`: for each of 8 tracks, cursor `0x80001c80 := 0x80001c90 + ping·0xa80 + 336·track` (T1–T4's records go to core 1, T5–T8's to core 0 via eDMA ch 0), then TWO renderer calls, `renderer(track, ping, 0, n)` from the current table `0x400d61d0[track]` and `renderer(track, ping, n, 16)` from the next-frame table `0x400d61f0[track]`, where **n = the low nibble of the per-track event byte `0x46104d0c + track`** — the sub-frame position of this frame's event — and **bit 4 of that byte = a voice starts this frame**: the packer then calls the start handler (`0x400d6454[kind]`, `0x4000f450` for STATIC/FLEX), installs the new renderer from the kind table, resets the render state (`0x80004898 + 40·track`) and clears bits 4–7 AFTER the second call | | the kind table | `0x400d6434`, 8 longs, index = the machine type (byte `0x80000eb4 + ping·8 + track`): 0 STATIC / 1 FLEX / 4 PICKUP → `0x40004008` (the sample renderer), 2 THRU → `0x40004424`, 3 NEIGHBOR → `0x4000466c`, 5–7 → `0x400047f0` (silent). The renderer for track t is installed at `0x4000c004` (`0x400d61f0[t] := table[kind & 7]`) | | the sample renderer | `0x40004008(track, ping, start, end)`, C convention, d0/d1/a0/a1 scratch: writes a 16-byte header at the cursor, recomputes the rate on the frame's second call (`btst #4` on the `end` argument's low byte — 16 = the full frame), ships the source samples through `0x40007960` in sub-segments (retrigs), advances the cursor. The rate: **[corrected in phase 2: `fp@(0)` is PTCH, `fp@(6)` RATE, `fp@(10)` RTIM — the retrig interval; the text below is kept as written]** the pitch word `fp@(10)` of the DSP parameter record (`fp = 0x80000510 + ping·384 + 48·track`) interpolated through the table `0x400aae0c` (a 2^(x/12) curve) into `state+24`, times the RATE word `fp@(0)` through `0x400aa294`, into `state+36`, Q26 (`0x04000000` = 1.0) | -| the record a call writes (measured, `runs/stock_explore2.log`) | header `+0` = source count (bits 0–7) \| out count << 8 [\| out2 << 16 \| out3 << 24], `+4` fractional phase, `+8` rate Q26, `+12` tag; then `src` samples of 8 bytes: L long, R long; **the DSP takes the top 24 bits of each long** (a 16-bit sample sits at bits 31..16). Silent T8 at n = 0: `[0,0,0x04000000,0]` then `[0x1010, 0x20, 0x04000000, 0x8000]` + 16 zero pairs; a sounding track at n = 4: `[0x404, 0x3c, 1.0, 0xf0000000]` + 4 pairs, `[0xc0c, 0, 1.0, 0]` + 12 pairs (`ffbe0084 ffb000a0 …`) | +| the record a call writes (measured, `stock_explore2.log`) | header `+0` = source count (bits 0–7) \| out count << 8 [\| out2 << 16 \| out3 << 24], `+4` fractional phase, `+8` rate Q26, `+12` tag; then `src` samples of 8 bytes: L long, R long; **the DSP takes the top 24 bits of each long** (a 16-bit sample sits at bits 31..16). Silent T8 at n = 0: `[0,0,0x04000000,0]` then `[0x1010, 0x20, 0x04000000, 0x8000]` + 16 zero pairs; a sounding track at n = 4: `[0x404, 0x3c, 1.0, 0xf0000000]` + 4 pairs, `[0xc0c, 0, 1.0, 0]` + 12 pairs (`ffbe0084 ffb000a0 …`) | | the voice struct | `0x800049d8 + 0xa8·track`: `+0` active byte (`0xff` while the CF voice runs, 0 when it ended — after which the renderer ships zeros), `+4` the slot's state record (`0x46c922c4 + 44·slot` FLEX, `0x46c90a78 + 44·slot` STATIC), `+8` its settings record (`0x100b14f0 + 0x448·slot` FLEX, `0x100d5b30 + …` STATIC), both written by the start handler `0x4000f450` at `+0x4dc/+0x4e0` unconditionally, i.e. before the start frame's second call. **The settings record's path string is at `+0`** (`"../AUDIO/SYNTH.wav"` for a slot the unit's own browser loaded; slot numbers are 0-based: FLEX slot 5 = index 4 = `0x100b2610`) | | the trig → voice path | `0x40005030(track, cmd, flags, slot)`: reads the machine byte, the Part's slot byte (`+0x8f04a + track·5 + type`, or the argument), the settings record (`≤ 128` STATIC, `≤ 135` FLEX/PICKUP), refuses an unloaded slot (`+0x129 == −1`), posts `0x8000186e/0x8000188e/0x800018ae[track]` (`slot \| type << 10`); the frame builder (`0x4000b2ee..`) checks the type against the Part's machine byte and posts the mailbox `0x46c80354[track]` and the slot byte `0x46c80282[track]`; `0x400068e4(track, ping, start, end)` is the per-track voice state machine the packer runs before rendering (`0x4000d322`), not a renderer | | parameter locks (found on the way, corrects a 35-byte guess) | a pattern's track record (`blob + pattern·0x8ed8 + track·0x91a`; bank A blob `0x400e21e0`) holds **32 bytes per step from `+0x59`**: byte 0 = PTCH … byte 31 = the sample slot lock; the p-lock editor `0x40050e60` writes the blob byte and an SRAM mirror (`0x100161a6 + pattern·stride + track·2330 + 1 + step·32 + param`). T8 step 9's PTCH lock: blob `+0x159`, mirror `+0x101` | -| the FLEX cost, stock (`runs/stock_explore.log`, `hits` on `0x40004008/0x40004266`) | first call 16 instructions; second call 261 idle, 264–694 streaming, **897–1,152 for the playing SYNTH slot** (mean 936) | +| the FLEX cost, stock (`stock_explore.log`, `hits` on `0x40004008/0x40004266`) | first call 16 instructions; second call 261 idle, 264–694 streaming, **897–1,152 for the playing SYNTH slot** (mean 936) | ### The cave (`synth.s`, 1,068 bytes; `.org` layout in `manifest.py`) @@ -3480,8 +4486,8 @@ its return value, then: interpolation would be −48). Position independent (pc-relative data, OS absolutes): the bytes are -identical linked at `0x400d7000`, `0x400d7300` and `0x400d6b80` -(`out/_agents/synth/asm/`), and `PINNED` in the manifest is the ratified +identical linked at `0x400d7000`, `0x400d7300` and `0x400d6b80`, +and `PINNED` in the manifest is the ratified form the build re-links at the address the cave lands on — `0x400d6b80` in `synth`, `0x400d6e00` in `tim`, both accepted. State lives in the cave (`sy_on[8]`, `sy_phase[8]`; the main OS runs from DRAM). @@ -3494,19 +4500,19 @@ changed, the synth cave at `0x400d6e00`, the quantizer's tables at DSP payloads, dispatch and FX2 chooser byte-identical to stock (the CFONLY check). -## Measurements (all `out/_agents/synth/`) +## Measurements (files in the author's workspace, not in the repo) -**Rig.** `trees/synth8q` = the clean tree2 fixture (`out/_agents/audio/tree2`) +**Rig.** `synth8q` = the clean tree2 fixture (`tree2`) with `SYNTH.wav` (4 s of silence, 16-bit mono 44.1 kHz) as FLEX slot 5 (`LOOPMODE=1`), T8 on it in parts 1 and 5, T3/T4/T7 moved to empty slots so only T8 sounds, the fixture's step-9 sample-slot lock on T8 cleared -(`mktree.py`, `cards/synth8q.img`); T8 trigs at steps 1 and 9, 120 BPM. +(`mktree.py`, `synth8q.img`); T8 trigs at steps 1 and 9, 120 BPM. `render.py` boots `out/emu/ot_emu --interactive --dsp` (lockstep) on the image + card, PLAYs, captures main L/R; `measure.py` gives the FFT peak (parabolic interpolation, 0.8 s Hann window), zero-crossing frequency, RMS and a 100 ms envelope. `explore.py` is the peek/watch driver. -**1. PTCH 0** (`runs/ptch0`, `REMIX=synth`): the first note, 0.1–0.9 s: +**1. PTCH 0** (`ptch0`, `REMIX=synth`): the first note, 0.1–0.9 s: **FFT 261.634 Hz (+0.1 cents), zero crossings 261.684 Hz (+0.4 cents)**, −24.0 dBFS L and R, worst spur −51.3 dB. The note holds until the next trig (the fixture's AMP HOLD/REL are 127; a looped FLEX voice holds): @@ -3514,21 +4520,21 @@ envelope −23/−24 dBFS throughout, silence after STOP. The step-9 note in the first run played the fixture's own slot lock (fourth-0.wav, 5 kHz tonal) — a sample-locked step correctly stays a sample. -**2. PTCH +12** (`runs/ptch12`): T8's Part PTCH byte `0x40171062` poked +**2. PTCH +12** (`ptch12`): T8's Part PTCH byte `0x40171062` poked to 124 (+12.0) before PLAY: **523.258 Hz (+1200.0 cents)**, both measures, −24.0 dBFS, spur −49.9 dB. The cave did nothing different: the DSP consumed 32 source samples a frame at rate 2.0. -**3. A p-locked PTCH on one step** (panel, `takes/take-plock.wav`, take 7; +**3. A p-locked PTCH on one step** (panel, `take-plock.wav`, take 7; GRID RECORDING, [TRIG 9] held, PTCH encoder +120 detents, the lock clamps at 124): notes at 0.1–0.9 / 1.1–1.9 / 2.1–2.9 / 3.1–3.9 s = **261.634 / 523.258 / 261.634 / 523.256 Hz** — step 9 alone at +12 (the earlier take 6 measures the same). The lock was written to the SRAM mirror and the blob (`+0x159`); a poke of the blob byte alone before PLAY did not -change the pitch (`runs/plock`), so the sequencer reads the mirror or a +change the pitch (`plock`), so the sequencer reads the mirror or a later copy — the unit's own editor is the path to use. -**4. The AMP page shapes it** (panel, `takes/amp.wav`, take 8): AMP page, +**4. The AMP page shapes it** (panel, `amp.wav`, take 8): AMP page, ATK 0 → 60, HOLD 127 → 30, REL 127 → 40 (Part bytes `0x40171128..2a` = `3c 1e 28`, read back). Each note now rises from −35 to −24 dBFS over ~340 ms and is released to silence by ~0.5 s (20 ms bins: `-35 -32 -31 … @@ -3536,20 +4542,20 @@ ATK 0 → 60, HOLD 127 → 30, REL 127 → 40 (Part bytes `0x40171128..2a` = the unshaped take holds a flat −24. Knobs reset afterwards with `/knob/reset` (`0 127 127`). -**5. FX1 = FILTER audibly changes it** (panel, `takes/fx1.wav`, take 9): +**5. FX1 = FILTER audibly changes it** (panel, `fx1.wav`, take 9): FX1 page, BASE 0 → 100 (`0x4017112e` = `64`; a high-pass rising above the tone). Level −24.0 dBFS → **−69.7 dBFS on the 261 Hz steps and −57 dBFS on the +12 (523 Hz) steps** — the lower note is 13 dB deeper into the slope, as a filter should. Reset to 0 afterwards. -**6. Mute silences it** (panel, `takes/mute.wav`, take 10): FUNC + [T8] at +**6. Mute silences it** (panel, `mute.wav`, take 10): FUNC + [T8] at 2.0 s of play (mute mask `0x8000000a` `7f` → `ff`): −24.0 dBFS until the 2.0 s bin, **digital silence (−999 dBFS) after it**. **7. Selecting it on the unit with keys** (panel `--port 8593 --image -out/_agents/synth/mainos_synth.bin --project out/_projects/otlive/OTLIVE/PROJECT ---set OTLIVE --name PROJECT --audio out/_agents/synth/audio --sound on`, -rt 0.999; `panelctl.py`, `shots/`): double-tap [T8] → the FLEX slot list +mainos_synth.bin --project out/_projects/otlive/OTLIVE/PROJECT +--set OTLIVE --name PROJECT --audio audio --sound on`, +rt 0.999; `panelctl.py`, shots): double-tap [T8] → the FLEX slot list `« MACHINE:FLEX` (`01_slotlist`), DOWN to slot 5, RIGHT → `LOAD FILE TO FLEX 5` (`03_browser`), DOWN ×36 to `SYNTH.wav` in the name-ordered list (`04_browser_synth`), YES loads it — the list reads **`5>SYNTH.wav 0.34`** @@ -3563,7 +4569,7 @@ of the tone at **261.634 Hz (+0.1 cents), spur −52.6 dB** under the panel's as lockstep. A retrigger (each note restarts at phase 0) shows as a 1 dB dip in one 10 ms bin. -**8. Cost** (`runs/cost`, `runs/cost12`; `cost.py` over `hits` on the +**8. Cost** (`cost`, `cost12`; `cost.py` over `hits` on the cave's entry `0x400d6b80` and return `0x400d6cb8` and the stock pair): | per frame, T8 = SYNTH | first call [0,0) | second call [0,16) | @@ -3583,7 +4589,7 @@ resamples the file whose audio the cave then discards); phase 2 may skip it once the voice lifecycle it carries has been re-read, which would halve the total. -**9. Stock behaviour untouched** (`ab/`): `tools/emu/ot_emu/oracle/drive.py +**9. Stock behaviour untouched** (A/B boots): `tools/emu/ot_emu/oracle/drive.py --emu out/emu/ot_emu --image ` (boot on the OTLIVE card, YES, MIXER, NO, T1 double tap, DOWN, RIGHT, NO, NO, PLAY 20 × 100 ms, STOP 5 × 100 ms): `ready.txt`, `steps.txt`, `stamps.txt`, `peeks.txt`, @@ -3601,7 +4607,7 @@ differs because nothing new is drawn. mode_views" (the Makefile's SKIP); `verify_replaces` fails only on the eight MIDI SCENES remixes whose submodule is not checked out (pre-existing; `make check` stops there). `REMIX=tim make cf` boots and -plays the tone (`runs/tim`: 261.634 Hz). +plays the tone (`tim`: 261.634 Hz). ## What does not work, and what is left for phase 2 diff --git a/sdk/octabam/modules/synth/upstream/synth/engine_abi.inc b/sdk/octabam/modules/synth/upstream/synth/engine_abi.inc new file mode 100644 index 000000000..1157edde3 --- /dev/null +++ b/sdk/octabam/modules/synth/upstream/synth/engine_abi.inc @@ -0,0 +1,55 @@ +| SYNTH MACHINE -- the equates shared by its units (poly.s, machine.s) and by the +| machine modules that run on its engine (sy-drum/sydrum.s). GNU as. Not assembled +| on its own: each unit's manifest reads this file into the unit's `remix.inc` +| (octabam's Linked.include, written per remix beside the unit), after the remix's +| flags (HAVE_SYDRUM: 1 when SY DRUM is in the remix). One copy, so the units cannot +| disagree about an address or a record layout. Values only: no code, no data. + +| ---- OS 1.40C addresses (all read by the units; nothing here is stock code) ---- + .set NIBBLE, 0x46104d0c | the packer's per-track DSP command byte (bit 4: a voice START this frame) + .set PITCH_TAB, 0x400aa294 | the stock 2^(x/12) curve, longs Q26 (index = PTCH word >> 5) + .set CURVALS, 0x80000810 | the frame builder's per-track live lane, 72 B a track, locks applied + .set CV_STRIDE, 72 + .set CV_WORDS, 0x80000a50 | + 64 * track: the lane's value words (byte << 8) the frame builder copies on + .set CV_SLEW, 0x80000db4 | + 32 * track: their slew counters, a long per four lane bytes (0 = take the lane's) + .set PART_PTR, 0x46c82456 | the bank blob; the Part = blob + part index * 6322 + .set PART_IDX, 0x100b14cf | the current Part's index + .set PART_SHADOW, 0x1001614e | the bank's mirror in battery RAM: + part * 6322 + the Part offset = that byte's shadow + .set MACH_OFF, 0x8eda2 | Part + track: the machine byte (0 STATIC, 1 FLEX, 2 THRU, 3 NEIGHBOR, 4 PICKUP) + .set SIG_OFF, 0x8edbc | Part + 30 * track: the NEIGHBOR PLAYBACK column's first three bytes, the + | machine list's signature of a FLEX track ("FM", 1 / "SY", 1; machine.s) + .set FLEX_P, 0x400d31ae | the stock FLEX PLAYBACK descriptor (P form) ... + .set FLEX_DESC_LEN, 0x192 | ... 402 bytes, the source of every runtime page clone + .set PING_AT, 0x800000e0 | the frame builder's ping (the record set being built this frame) + .set SPRINTF, 0x40013a08 | stock's sprintf (buf, format, ...) + .set FUNC_HELD, 0x46c7dd26 | nonzero while [FUNCTION] is down (the stock knob handlers' tst.l) + .set FUNC_ENC_OFF, 0x800000a0 | PERSONALIZE: DISABLE FUNCTION + ENCODER (the stock handlers' gate) + .set KEY_HELD, 0x46c7d8de+0x10 | + code * 0x18: nonzero while that key is held + .set PUSH_CODE, 0x38 | the encoder push switches: key codes 0x38..0x3d = A..F + .set KN_SLOTS, 0x12a | a page descriptor's six knob handler pointers + .set FM_DESC, 0x400d24d0 | page.s's fm_descriptor (the pinned page cave's first routine): d0 := the FM + | SYNTH page clone, built on first use (d0/d1/a0/a1 clobbered) + +| ---- the engine's numbers ---- + .set C4_INC, 25480119 | C4: 261.6256 / 44100 * 2^32 + .set ENV_ONE, 0x01000000 | the index envelope's 1.0 (Q24) + .set INC_MAX, 0x73000000 | a carrier above ~19.8 kHz (increment > 0.45 cycle a sample) is no note + | of this synth: the safety net resets such a voice (24 Sep 2026; raised + | from 8 kHz on 27 Sep 2026 for PTCH +63 and the octave shapes) + +| ---- po_clock (poly.s): the sequencer clock, its fields ---- + .set CK_TICKS, 0 | ticks seen (monotonic, no pattern wrap) + .set CK_TPS, 4 | ticks a step (the largest tick value seen + 1; 0 = not yet) + .set CK_FPS, 8 | frames a step (measured between step changes; 345 until then) + .set CK_FRAMES, 12 | frames seen + +| ---- sy_state (poly.s): the per-track record, the fields another engine shares ---- + .set ST_STRIDE, 128 + .set S_GAIN, 24 | the mono voice's level, Q15 (po_mono_env: the AMP envelope) + .set S_ON, 36 | bit 0: the playing voice is an engine's; bit 1: released; bit 2: STOPPED (unused since 2.12) + .set S_GPREV, 38 | word: the level the last rendered call ended at (0 = silent: cold) + +| ---- po_voices (poly.s): a voice record, the fields another engine shares ---- + .set V_STRIDE, 64 + .set V_GAIN, 24 | Q15 (the mono voice's S_GAIN format) + .set V_GPREV, 60 | word: the level the last rendered frame ended at diff --git a/sdk/octabam/modules/synth/upstream/synth/machine.s b/sdk/octabam/modules/synth/upstream/synth/machine.s new file mode 100644 index 000000000..c49d10dbb --- /dev/null +++ b/sdk/octabam/modules/synth/upstream/synth/machine.s @@ -0,0 +1,952 @@ +| SYNTH MACHINE -- the machine list (2.10, 8 Oct 2026; table-driven since 2.11): FM SYNTH +| is the sixth row of the machine window (the track key twice, LEFT: SELECT MACHINE TYPE) +| and of SRC SETUP (FUNC + SRC); a machine module that runs on this engine adds the next +| row (2.11: SY DRUM, sy-drum/). GNU as, -mcpu=5475; a DRAM unit linked beside poly.s +| (modules/synth/manifest.py, Linked "fmmachine"). +| +| THE ROWS (2.11). ml_rows lists the rows after the stock five, one row descriptor each, +| assembled per remix (remix.inc: FM SYNTH always, SY DRUM when HAVE_SYDRUM); ML_COUNT is +| their number. A row descriptor (20 bytes): +0 its signature (two letters and a version, +| "FM", 1), +3 the engine kind po_signed answers for it (1 FM, 2 SY), +4 its name, +8 its +| twelve seed bytes (6 PLAYBACK, 6 SETUP), +12 a routine returning its PLAYBACK descriptor +| in d0 (SRC SETUP's page for that row; clobbers d0/d1/a0/a1), +16 flags (bit 0: its SETUP +| bytes are not FLEX's; 2.12: not read -- leaving a row resets the FLEX bytes anyway). The +| stock code's row-count and row-bound constants (five machines, rows 0..4) are not poked: +| six small detours take 5 + ML_COUNT and 4 + ML_COUNT, so the same sites serve any number +| of rows and a second machine module needs no site of its own. With FM SYNTH alone every +| list behaves as 2.10's (six rows). +| +| Adapted from Modwerk's FM Synth module (repeat98/modwerk, sdk/octabam/modules/synth: +| machine.s and registration.c, commit 1c1d938; MIT, Copyright (c) 2026 Modwerk +| contributors -- the dedicated chooser, the Part validation and the sample-free +| transport; derived there from Modwerk's Analog BD chooser hooks). Their C helpers are +| written out here by hand; the stubs keep their hook sites and register contracts. +| Changed for this repository: the machine window's commit shares its site with +| po_machwin (poly.s: one detour at 0x40079816 does both jobs); the PLAYBACK page of a +| chosen track is resolved by page.s's pg_resolve (no second resolver detour); no +| refusal path (every track 1..8 may be FM SYNTH); the six-row name table is filled at +| run time from the stock one; a track that already plays the FM voice through a +| marker file keeps its patch when FM SYNTH is chosen over it. +| +| THE STORAGE. A chosen track is stored as FLEX (the machine byte 1, so every stock +| FLEX path -- the PLAYBACK lanes, locks, LFOs, scenes, the DSP voice -- stays valid) +| with the signature "F", "M", 1 in the first three bytes of the track's NEIGHBOR +| PLAYBACK column: the bank blob + part * 6322 + 0x8edbc + 30 * track, and the same +| offset in the battery-RAM shadow. NEIGHBOR has no sample and its six PLAYBACK bytes +| are never written for a FLEX track; the stock validator's NEIGHBOR range is 0..127, +| so "F" (70), "M" (77) and 1 survive a load. A Part is saved, copied, pasted and +| reloaded with its signature like any other Part byte. Choosing FM SYNTH on a track +| that does not play the FM voice yet seeds the FLEX PLAYBACK bytes with PTCH 0, RATO +| 1, INDX 32, FINE 0c, FDBK 0, DEC 40 and the FLEX SETUP bytes with 0 (LOOP, SLIC, +| LEN, RATE, TSTR, TSNS: OFF); choosing it again keeps the patch; choosing any other +| row clears the signature. +| +| THE MACHINE CHANGE (2.12). A change of the machine a track plays, where the old or the +| new machine is a row's (FM SYNTH, SY DRUM), sets the track to the new machine's init: +| PLAYBACK and its SETUP, AMP and AMP SETUP, the three LFOs and LFO SETUP; step and scene +| locks (SY DRUM's lock rows too) and the FX1 / FX2 slots stay (ml_reset). Through either list (fm_choose) and through +| a marker file (poly.s: the slot assigner and the file browser, ml_became / ml_reset). A +| change between two stock machines is stock's (nothing written); a project, a Part +| reload, a paste or a pattern change is no change (the bytes saved are the bytes played). +| +| The engine's side is poly.s: po_is_synth (and the quantizer's qz_is_synth) answer +| yes for a signed track, po_fmstart starts its voice without a sample, po_fmsource +| hands its source to sy_render, sy_call writes the source header stock would have; +| po_signed answers the row's engine kind (2.11). + + .text + .global fm_machine_name, fm_name_a, fm_src_names + .global fm_setup_row, fm_chooser_row, fm_setup_open, fm_chooser_open + .global fm_setup_edit6, fm_setup_draw6 + .global fm_main_commit, fm_src_commit, fm_src_commit2 + .global fm_tick, fm_validate + .global ml_win_count, ml_src_count, ml_name_bound, ml_draw_bound, ml_hi_bound, ml_cur_bound + .global ml_clamp1, ml_clamp2, ml_clamp3, ml_clamp4, ml_clamp5 + .global ml_rows, fm_row + .global ml_reset, ml_kindof, ml_became | 2.12: the machine change's init (poly.s: the marker route) + .include "remix.inc" | 2.11: HAVE_SYDRUM and the shared equates (engine_abi.inc; manifest.py) + + .set FM_ROW, 5 | the sixth row, the first after the stock five: FM SYNTH + .set ML_COUNT, 1 + HAVE_SYDRUM | the rows after the stock five (2.11) + .set ML_LAST, 4 + ML_COUNT | the last row's index + .set RD_KIND, 3 | a row descriptor: +0 signature (3 B), +3 kind, +4 name, + .set RD_NAME, 4 | +8 seeds (12 B), +12 its PLAYBACK descriptor routine, +16 flags + .set RD_SEEDS, 8 + .set RD_DESC, 12 + .set RD_FLAGS, 16 + .set FLEX, 1 + .set PART_OFF, 0x8ed80 | the Part's own +0 (what the validator is handed) + .set FLEX_PB, 0x8edb0 | + 30 * track: the FLEX PLAYBACK bytes (PTCH STRT LEN RATE RTRG RTIM) + .set SETUP_GAP, 0x1b0 | ... their FLEX SETUP bytes this far on (blob + 0x8ef60 + 30 * track) + .set CV_SETUP, 32 | + lane: the PLAYBACK page's SETUP bytes (LOOP SLIC LEN RATE TSTR TSNS) + .set SRC_CURSOR, 0x460d5c30 | SRC SETUP's machine row (the sample-list window's) + .set NAMES, 0x400a78c8 | the stock machine-name table, five pointers (STATIC .. PICKUP) + .set PB_TABLE, 0x400d5f38 | the machine -> PLAYBACK descriptor table (slot 5: a spare) + .set FLEX_DEF, 0x400d320c | the FLEX descriptor's defaults: 6 PLAYBACK, then 6 SETUP bytes + .set NAME_RET, 0x400334de | fm_machine_name's way back + .set NAME_A_RET, 0x4003d722 + .set SETUP_ROW_RET, 0x4003c986 + .set CHOOSER_HIT, 0x400786ce | the chooser's row-equals-machine branch ... + .set CHOOSER_MISS, 0x400786fc | ... and its other one + .set SETUP_OPEN_RET, 0x400585e6 + .set CHOOSER_OPEN_RET, 0x40078890 + .set EDIT6_RET, 0x4003a536 + .set DRAW6_RET, 0x4003cda0 + .set SRC_COMMIT_RET, 0x4005a61c + .set SRC_COMMIT2_RET, 0x4005a856 + .set TICK_A, 0x4005213c | the UI tick's two calls fm_tick replays (the first is PC-relative + .set TICK_B, 0x4007e940 | in stock, so it is replayed by its absolute address) + .set TICK_RET, 0x40052228 + .set WIN_COUNT_RET, 0x40079250 | the machine window's list init after its `pea 5; pea 6` (ml_win_count) + .set SRC_COUNT_RET, 0x40058602 | SRC SETUP's list init after its `pea 5; pea 5` (ml_src_count) + .set NAME_BOUND_IN, 0x4003c956 | SRC SETUP's name lookup: the row has a name ... + .set NAME_BOUND_OUT, 0x4003c95a | ... or not (the default string) + .set DRAW_BOUND_IN, 0x40078684 | the machine window's drawer: the row's name drawn (after the formatter call) ... + .set WIN_LIST, 0x460e7386 | SELECT MACHINE TYPE's list: +0 top, +4 cursor in the window, +8 row, +12 visible, +16 count + .set DRAW_BOUND_OUT, 0x400786a2 | ... or not + .set HI_BOUND_IN, 0x400786d4 | the machine window's highlight: the row is highlighted ... + .set CUR_BOUND_IN, 0x400797cc | the machine window's cursor: the row is applied ... + .set CUR_BOUND_OUT, 0x4007990c | ... or not + .set VALIDATE_BODY, 0x40002320 | the Part validator after its prologue + +| ---- fm_choose: d1 = the chosen row, d2 = the track (the current Part) -> d1 = the +| machine byte to store. A row of ml_rows -> FLEX, with that row's signature written into +| the Part and its shadow; a stock row: unchanged, any row's signature cleared. Preserves +| every other register. +| THE MACHINE CHANGE (2.12, Tim, 10 Oct 2026: "all the parameters, including any LFO +| settings, should be set to init. They should not inherit any parameters from the last +| machine that was on that track."). The machine the track plays before the choice (ml_ident: +| 0 a stock machine, 1 FM -- chosen, or FLEX with a marker slot --, 2 SY DRUM) is compared +| with the one it plays after it (the row's engine kind; a stock row: 0, or 1 for FLEX +| whose FLEX slot holds a marker file). When they differ and either is one of the rows' +| machines, the track gets the new machine's init (ml_reset): its PLAYBACK and PLAYBACK +| SETUP bytes, the AMP and AMP SETUP pages, the three LFOs and LFO SETUP; the step and scene +| locks (SY DRUM's lock rows too) and the FX1 / FX2 slots are kept. The same machine again keeps the patch (a track that +| plays the FM voice through a marker file keeps it when FM SYNTH is chosen over it). A +| change between stock machines writes nothing here (stock's own behaviour: each machine +| keeps its PLAYBACK column, every other page stays). +fm_choose: + lea -36(%sp),%sp + movem.l %d0/%d3/%a0-%a6,(%sp) + movea.l PART_PTR,%a0 + mvz.b PART_IDX,%d0 + move.l #6322,%d3 + muls.l %d3,%d0 | part * 6322 + adda.l %d0,%a0 | the Part + lea PART_SHADOW,%a1 + adda.l %d0,%a1 | its shadow + move.l %d2,%d0 + mulu.w #30,%d0 | track * 30 + adda.l %d0,%a0 + adda.l %d0,%a1 + movea.l %a0,%a2 + adda.l #SIG_OFF,%a2 | a2 = the signature in the Part + movea.l %a1,%a3 + adda.l #SIG_OFF,%a3 | a3 = ... in the shadow + bsr ml_ident | (d2) the machine the track plays now: 0 / 1 FM / 2 SY DRUM + move.l %d0,%d3 + move.l %d1,%d0 + subq.l #FM_ROW,%d0 | the row's index in ml_rows + cmpi.l #ML_COUNT,%d0 + bcc fm_ch_stock | (unsigned) a stock row + lea ml_rows(%pc),%a6 + movea.l (%a6,%d0.l*4),%a6 | a6 = the row's descriptor + mvz.b RD_KIND(%a6),%d0 | the machine it plays + cmp.l %d3,%d0 + beq fm_ch_sign | that machine already: its patch is the user's + bsr ml_reset | its init (d0 = its kind) +fm_ch_sign: + move.b (%a6),(%a2) | the row's signature: the Part ... + move.b 1(%a6),1(%a2) + move.b 2(%a6),2(%a2) + move.b (%a6),(%a3) | ... and its shadow + move.b 1(%a6),1(%a3) + move.b 2(%a6),2(%a3) + moveq #FLEX,%d1 | stored as FLEX + bra fm_ch_out +fm_ch_stock: + moveq #0,%d0 | a stock machine ... + cmpi.l #FLEX,%d1 + bne fm_ch_new + bsr ml_flexmark | ... but FLEX over a marker slot plays the FM voice +fm_ch_new: + cmp.l %d3,%d0 + beq fm_ch_unsign | the same machine (or stock to stock): nothing written + bsr ml_reset | d0 = 0 / 1, d1 = the stock machine +fm_ch_unsign: + movea.l %a2,%a0 + bsr fm_unsign + movea.l %a3,%a0 + bsr fm_unsign +fm_ch_out: + movem.l (%sp),%d0/%d3/%a0-%a6 + lea 36(%sp),%sp + rts +| ml_ident: d2 = track of the current Part -> d0 = the machine it plays: 1 FM (FM SYNTH +| chosen, or FLEX with an FMSYNTH* / SYNTH* slot), 2 SY DRUM (chosen), 0 a stock machine; +| tst.l done. Preserves every other register. +ml_ident: + .if HAVE_SYDRUM + jmp po_ident + .else + jmp po_is_synth + .endif +| ml_flexmark: d2 = track -> d0 = 1 when the current Part's FLEX slot of the track holds a +| marker file (po_slot_marker), else 0; tst.l done. Preserves every other register. +ml_flexmark: + move.l %a0,-(%sp) + bsr ml_part + move.l %d2,%d0 + mulu.w #5,%d0 + adda.l %d0,%a0 + adda.l #SLOT_FLEX,%a0 + mvz.b (%a0),%d0 | its FLEX slot, 0-based + movea.l (%sp)+,%a0 + jmp po_slot_marker +| ml_part: a0 := the current Part (the bank blob + part * 6322). Preserves every other register. +ml_part: + move.l %d0,-(%sp) + mvz.b PART_IDX,%d0 + mulu.w #6322,%d0 | part * 6322 (part 0..3) + movea.l PART_PTR,%a0 + adda.l %d0,%a0 + move.l (%sp)+,%d0 + rts +| ml_kindof: d0 = a machine byte, d1 = a FLEX slot, d2 = track of the current Part -> d0 = +| the machine the track plays with them: 0 a stock machine, the row's kind for FLEX with a +| row's signature (the Part's), 1 for FLEX over a marker slot; tst.l done. Preserves every +| other register (the assigners' test, before or after their write: poly.s). +ml_kindof: + cmpi.l #FLEX,%d0 + bne ml_ko_stock + move.l %a0,-(%sp) + bsr ml_part + move.l %d2,%d0 + mulu.w #30,%d0 + adda.l %d0,%a0 + adda.l #SIG_OFF,%a0 | the track's signature place + bsr ml_sigrow + bmi ml_ko_marker + lea ml_rows(%pc),%a0 + movea.l (%a0,%d0.l*4),%a0 + mvz.b RD_KIND(%a0),%d0 | the row's machine + movea.l (%sp)+,%a0 + rts +ml_ko_marker: + movea.l (%sp)+,%a0 + move.l %d1,%d0 + jmp po_slot_marker +ml_ko_stock: + moveq #0,%d0 + rts +| ml_became: the slot assigner's test (poly.s po_assign, after its slot-byte write): d0 = +| the old machine, d1 = the new one, d2 = the track, d3 = the old slot of the new machine's +| column, d4 = its new slot. The machine the track played before the assignment and the one +| it plays after it (ml_kindof, the FLEX slot being d3 / d4 when the new machine is FLEX, +| else the Part's): different, and one of them a row's -> ml_reset to the new one. The +| marker route: a FLEX track given an FMSYNTH* / SYNTH* slot, or a marker track given a +| sample, a STATIC slot... Preserves every register. +ml_became: + lea -24(%sp),%sp + movem.l %d0-%d5,(%sp) + move.l %d1,%d5 | the new machine + cmpi.l #FLEX,%d1 + beq ml_bc_slots | the FLEX column's slots are d3 (before) and d4 (after) + bsr ml_flexslot | another column: the FLEX slot is the Part's, unchanged + move.l %d1,%d3 + move.l %d1,%d4 +ml_bc_slots: + move.l %d3,%d1 + bsr ml_kindof | d0 (the old machine), d1 (its FLEX slot): before + move.l %d0,%d3 + move.l %d5,%d0 + move.l %d4,%d1 + bsr ml_kindof | after + cmp.l %d3,%d0 + beq ml_bc_out + move.l %d5,%d1 | the new machine (the column of a stock init) + bsr ml_reset +ml_bc_out: + movem.l (%sp),%d0-%d5 + lea 24(%sp),%sp + rts +| ml_flexslot: d2 = track -> d1 = the current Part's FLEX slot of the track (0-based). +| Preserves every other register. +ml_flexslot: + move.l %a0,-(%sp) + bsr ml_part + move.l %d2,%d1 + mulu.w #5,%d1 + adda.l %d1,%a0 + adda.l #SLOT_FLEX,%a0 + mvz.b (%a0),%d1 + movea.l (%sp)+,%a0 + rts + +| ---- ml_reset (2.12): d0 = the machine the track gets (0 a stock machine, 1 FM SYNTH, 2 SY +| DRUM), d1 = the stock machine (d0 = 0), d2 = the track of the current Part. Writes the +| machine's INIT into the Part, its battery-RAM shadow and the track's live lane, so the +| page shows it at once, the next frame plays it and SAVE keeps it: +| * PLAYBACK and PLAYBACK SETUP: a row's machine -- its twelve seeds in the FLEX column (FM +| SYNTH: PTCH 0, RATO 1, INDX 32, FINE 0c, FDBK 0, DEC 40, SETUP 0; SY DRUM: PTCH 0, MODE +| A, WDTH 64, SWEP 0, SPED 64, DEC 64, LSPD 64, LDEP 0, WAVE OFF, S&H OFF); a stock +| machine -- its column at the defaults of its stock PLAYBACK descriptor, and the FLEX +| column at FLEX's (the FLEX bytes a row's machine had used: they are no sample settings); +| * AMP (ATK HOLD REL VOL BAL XVOL), AMP SETUP (AMP SYNC ATCK FX1 FX2, and LEG / TRIG), the +| LFO page (SPD1..3 DEP1..3) and LFO SETUP (PMTR WAVE MULT TRIG of each LFO): the stock +| new-track values -- what the stock Part initialiser (0x40005638, run for every Part of +| a new project and by Part CLEAR) writes, read here from the same stock descriptors at +| run time (their defaults: page 1 at P + 0x5e, page 2 at P + 0x64; the six PMTR / WAVE +| bytes from the LFO page's slot 6 / 7 defaults and the six MULT / TRIG bytes from its +| slot 8 / 9 defaults, alternately, as the initialiser writes them); +| FM SYNTH: VOIC 1 and CHRD off in LFO 3's SPD3 / DEP3 bytes; +| FX1 / FX2 (their effect, page and setup bytes) are kept: effect slots, not the machine's. +| Step locks, scene locks and SY DRUM's own lock rows are kept, as at a stock machine change. +| The live lane gets the bytes written (its PLAYBACK, SETUP, page-1 and page-2 bytes, the +| page-1 value words byte << 8, their slew counters cleared): the stock machine change +| reloads a track from its Part only for PICKUP (0x400972fc -> 0x40001f18), and the frame +| builder refreshes the words from the Part only at a trig from another bank or Part (8 Oct +| 2026: a stale RATE word played 67.8 Hz). Preserves every register. + .set LFO_P, 0x400d37f6 | the stock LFO page descriptor (P form) + .set AMP_P, 0x400d3988 | the stock AMP page descriptor (P form) + .set DEF1, 0x5e | a descriptor's twelve defaults: page 1 (6), then page 2 (6) at + 6 + .set PB_OFF, 0x8edaa | + 30 * track + 6 * machine: a PLAYBACK column (SETUP_GAP on: its SETUP column) + .set PAGE1_OFF, 0x8ee9a | + 24 * track: LFO (SPD1..3 DEP1..3), AMP, FX1, FX2 page 1 + .set PAGE2_OFF, 0x8f072 | + 30 * track: PMTR1..3 WAVE1..3, AMP SETUP, FX1 SETUP, FX2 SETUP, the MULT / TRIG bytes + .set SLOT_FLEX, 0x8f04b | + 5 * track: the FLEX column's slot byte + .set CV_LFO, 6 | the lane: page 1 at + 6 (LFO, then AMP at + 12) + .set CV_P2, 38 | ... page 2's PMTR / WAVE at + 38, AMP SETUP at + 44 + .set CV_MT, 62 | ... MULT / TRIG at + 62 +ml_reset: + lea -48(%sp),%sp + movem.l %d0-%d5/%a0-%a5,(%sp) + bsr ml_part | a0 = the Part + move.l %a0,%d3 + sub.l PART_PTR,%d3 + lea PART_SHADOW,%a1 + adda.l %d3,%a1 | a1 = its shadow + move.l %d2,%d3 + mulu.w #CV_STRIDE,%d3 + addi.l #CURVALS,%d3 + movea.l %d3,%a2 | a2 = the track's live lane + move.l %d2,%d3 + lsl.l #6,%d3 + addi.l #CV_WORDS,%d3 + movea.l %d3,%a3 | a3 = its value words + move.l %d0,%d4 | the machine + beq ml_rs_stock + bsr ml_kindrow | a5 = the row's descriptor + movea.l RD_SEEDS(%a5),%a5 | its seeds: 6 PLAYBACK, then 6 SETUP + moveq #FLEX,%d1 + moveq #1,%d3 + bsr ml_column | the FLEX column, the lane's too + bra ml_rs_pages +ml_rs_stock: + move.l %d1,%d5 | the stock machine + moveq #FLEX,%d1 + movea.l PB_TABLE+4*FLEX,%a5 + lea DEF1(%a5),%a5 | FLEX's defaults + moveq #0,%d3 + bsr ml_column | the FLEX column (Part and shadow) + move.l %d5,%d1 + lea PB_TABLE,%a5 | the machine -> PLAYBACK descriptor table + movea.l (%a5,%d1.l*4),%a5 + lea DEF1(%a5),%a5 | the machine's defaults + moveq #1,%d3 + bsr ml_column | its column, the lane's too +ml_rs_pages: + move.l %d2,%d3 + lsl.l #3,%d3 + move.l %d3,%d0 + add.l %d3,%d3 + add.l %d0,%d3 | track * 24 + addi.l #PAGE1_OFF,%d3 + lea LFO_P+DEF1,%a4 | the LFO page: SPD1..3 DEP1..3 + moveq #0,%d1 +ml_rs_lfo: + mvz.b (%a4)+,%d0 + cmpi.l #1,%d4 + bne ml_rs_lfo1 | FM SYNTH: SPD3 = VOIC 1, DEP3 = CHRD off + cmpi.l #2,%d1 + bne ml_rs_chrd + moveq #1,%d0 +ml_rs_chrd: + cmpi.l #5,%d1 + bne ml_rs_lfo1 + moveq #0,%d0 +ml_rs_lfo1: + bsr ml_p1byte + addq.l #1,%d1 + cmpi.l #6,%d1 + bne ml_rs_lfo + lea AMP_P+DEF1,%a4 | the AMP page: ATK HOLD REL VOL BAL XVOL +ml_rs_amp: + mvz.b (%a4)+,%d0 + bsr ml_p1byte + addq.l #1,%d1 + cmpi.l #12,%d1 + bne ml_rs_amp + move.l %d2,%d3 + lsl.l #5,%d3 + sub.l %d2,%d3 + sub.l %d2,%d3 | track * 30 + addi.l #PAGE2_OFF,%d3 + moveq #0,%d1 | page 2's bytes 0..5: PMTR / WAVE of LFO 1..3 + lea LFO_P+DEF1+6,%a4 | (the LFO page's slot 6 / 7 defaults: PMTR, WAVE) + bsr ml_p2pairs + lea AMP_P+DEF1+6,%a4 | 6..11: AMP SETUP +ml_rs_amps: + mvz.b (%a4)+,%d0 + bsr ml_p2byte + addq.l #1,%d1 + cmpi.l #12,%d1 + bne ml_rs_amps + moveq #24,%d1 | 24..29: MULT / TRIG of LFO 1..3 + lea LFO_P+DEF1+8,%a4 | (slot 8 / 9 defaults: MULT, TRIG) + bsr ml_p2pairs + move.l %d2,%d0 + lsl.l #5,%d0 + addi.l #CV_SLEW,%d0 + movea.l %d0,%a4 | the slew counters of lane bytes 0..19 (a long per four) + clr.l (%a4)+ + clr.l (%a4)+ + clr.l (%a4)+ + clr.l (%a4)+ + clr.l (%a4) + movem.l (%sp),%d0-%d5/%a0-%a5 + lea 48(%sp),%sp + rts +| ml_column: d1 = a machine's column, d2 = track, a5 = 12 bytes (6 PLAYBACK, 6 SETUP), d3 = 1 +| to write the live lane (a2) and the value words (a3) too, a0 / a1 = the Part / shadow -> +| the column's PLAYBACK and SETUP bytes written. Clobbers d0 only. +ml_column: + lea -12(%sp),%sp + movem.l %d1/%d4-%d5,(%sp) + move.l %d2,%d0 + mulu.w #30,%d0 + move.l %d1,%d4 + mulu.w #6,%d4 + add.l %d4,%d0 + addi.l #PB_OFF,%d0 | the column's PLAYBACK bytes + move.l %d0,%d5 + addi.l #SETUP_GAP,%d5 | its SETUP bytes + moveq #0,%d1 +ml_cl_byte: + mvz.b (%a5,%d1.l),%d4 + move.b %d4,(%a0,%d0.l) | PLAYBACK k: the Part, + move.b %d4,(%a1,%d0.l) | its shadow + tst.l %d3 + beq ml_cl_setup + move.b %d4,(%a2,%d1.l) | the lane + lsl.l #8,%d4 + move.w %d4,(%a3,%d1.l*2) | its value word +ml_cl_setup: + mvz.b 6(%a5,%d1.l),%d4 + move.b %d4,(%a0,%d5.l) | SETUP k: the Part, + move.b %d4,(%a1,%d5.l) | its shadow + tst.l %d3 + beq ml_cl_next + move.b %d4,CV_SETUP(%a2,%d1.l) | the lane +ml_cl_next: + addq.l #1,%d0 + addq.l #1,%d5 + addq.l #1,%d1 + cmpi.l #6,%d1 + bne ml_cl_byte + movem.l (%sp),%d1/%d4-%d5 + lea 12(%sp),%sp + rts +| ml_p1byte: d0 = a value, d1 = page 1's slot 0..11 (LFO, AMP), d3 = the track's page 1 in +| the Part -> the Part, the shadow, the lane (+ 6 + slot) and the value word written. +ml_p1byte: + move.l %d3,-(%sp) + add.l %d1,%d3 + move.b %d0,(%a0,%d3.l) + move.b %d0,(%a1,%d3.l) + move.b %d0,CV_LFO(%a2,%d1.l) + lsl.l #8,%d0 + move.w %d0,2*CV_LFO(%a3,%d1.l*2) + move.l (%sp)+,%d3 + rts +| ml_p2byte: d0 = a value, d1 = page 2's byte 0..29, d3 = the track's page 2 in the Part -> +| the Part, the shadow and the lane (+ 38 + byte; MULT / TRIG at + 62) written. +ml_p2byte: + move.l %d3,-(%sp) + add.l %d1,%d3 + move.b %d0,(%a0,%d3.l) + move.b %d0,(%a1,%d3.l) + move.l (%sp)+,%d3 + cmpi.l #24,%d1 + bcc ml_p2_mt + move.b %d0,CV_P2(%a2,%d1.l) + rts +ml_p2_mt: + move.b %d0,CV_MT-24(%a2,%d1.l) + rts +| ml_p2pairs: a4 = two defaults (a, b), d1 = page 2's first byte -> bytes d1 .. d1 + 5 = +| a b a b a b, as the stock initialiser writes them (`slot & 1` picks the default); d1 += 6. +ml_p2pairs: + moveq #3,%d5 +ml_pp_lfo: + mvz.b (%a4),%d0 + bsr ml_p2byte + addq.l #1,%d1 + mvz.b 1(%a4),%d0 + bsr ml_p2byte + addq.l #1,%d1 + subq.l #1,%d5 + bne ml_pp_lfo + rts +| ml_kindrow: d0 = a row's engine kind -> a5 = its descriptor in ml_rows (the first row +| when none matches). Preserves every other register. +ml_kindrow: + move.l %d1,-(%sp) + moveq #0,%d1 +ml_kr_row: + lea ml_rows(%pc),%a5 + movea.l (%a5,%d1.l*4),%a5 + cmp.b RD_KIND(%a5),%d0 + beq ml_kr_out + addq.l #1,%d1 + cmpi.l #ML_COUNT,%d1 + bne ml_kr_row + lea ml_rows(%pc),%a5 + movea.l (%a5),%a5 +ml_kr_out: + move.l (%sp)+,%d1 + rts +| ml_sigrow: a0 = a signature's place -> d0 = the index in ml_rows of the row whose +| signature it holds, else -1; tst.l done (bmi: none). Preserves every other register. +ml_sigrow: + lea -8(%sp),%sp + movem.l %d1/%a1,(%sp) + moveq #0,%d0 +ml_sr_row: + lea ml_rows(%pc),%a1 + movea.l (%a1,%d0.l*4),%a1 | the row's descriptor: its signature at +0 + mvz.b (%a0),%d1 + cmp.b (%a1),%d1 + bne ml_sr_next + mvz.b 1(%a0),%d1 + cmp.b 1(%a1),%d1 + bne ml_sr_next + mvz.b 2(%a0),%d1 + cmp.b 2(%a1),%d1 + beq ml_sr_out +ml_sr_next: + addq.l #1,%d0 + cmpi.l #ML_COUNT,%d0 + bne ml_sr_row + moveq #-1,%d0 +ml_sr_out: + movem.l (%sp),%d1/%a1 + lea 8(%sp),%sp + tst.l %d0 + rts +| fm_unsign: a0 = a signature's place -> cleared when it holds a row's signature (d0 clobbered) +fm_unsign: + bsr ml_sigrow + bmi fm_us_out + clr.b (%a0) + clr.b 1(%a0) + clr.b 2(%a0) +fm_us_out: + rts + +| ---- 0x40079816 (jmp, 8 B): the machine window's machine-byte write (FUNC + SRC, YES). +| a1 = the bank blob, d0 = part * 6322, d1 = the track, d2 = the part index, d4 = the +| chosen row, a0 = blob + part * 6322 + track. The row becomes the machine byte to store +| (fm_choose), then po_machwin (poly.s) replays the displaced `addal #0x8eda2,%a0; mvsb +| %a0@,%d3` with its FINE rule and stock stores d4 into the Part and its shadow. +fm_main_commit: + move.l %d2,-(%sp) + move.l %d1,-(%sp) + move.l %d1,%d2 | the track + move.l %d4,%d1 | the row + bsr fm_choose + move.l %d1,%d4 | the machine to store + move.l (%sp)+,%d1 + move.l (%sp)+,%d2 + jmp po_machwin + +| ---- 0x4005a616 / 0x4005a850 (jmp, 6 B each): SRC SETUP's two machine-byte writes: +| displaced `movel 0x460d5c30,%d1` (the chosen row), then stock stores d1 at a0 (the +| machine byte) and into the shadow. d2 = the track. The second site follows +| po_machlist's detour (0x4005a848, poly.s), which returns to it. +fm_src_commit: + move.l SRC_CURSOR,%d1 | displaced + bsr fm_choose + jmp SRC_COMMIT_RET +fm_src_commit2: + move.l SRC_CURSOR,%d1 | displaced + bsr fm_choose + jmp SRC_COMMIT2_RET + +| ---- fm_type: d0 = a machine read through a0 = a pointer to it -> d0 = 5 + the row's +| index when a0 is the current Part's machine byte of a track whose row of ml_rows is +| chosen (FLEX, signed), else d0 unchanged. Preserves every other register +| (registration.c's fm_type). +fm_type: + cmpi.l #FLEX,%d0 + bne fm_ty_rts + lea -16(%sp),%sp + movem.l %d1-%d2/%a0-%a1,(%sp) + movea.l PART_PTR,%a1 + mvz.b PART_IDX,%d1 + move.l #6322,%d2 + muls.l %d2,%d1 + adda.l %d1,%a1 | the Part + move.l %a0,%d2 + sub.l %a1,%d2 + subi.l #MACH_OFF,%d2 | the track, if a0 is one of its machine bytes + moveq #8,%d1 + cmp.l %d1,%d2 + bhs fm_ty_out | (unsigned) another byte: unchanged + mulu.w #30,%d2 + movea.l %a1,%a0 + adda.l %d2,%a0 + adda.l #SIG_OFF,%a0 | the track's signature place (FLEX: the caller's byte) + bsr ml_sigrow + bmi fm_ty_flex | unsigned: FLEX + addq.l #FM_ROW,%d0 | its row + bra fm_ty_out +fm_ty_flex: + moveq #FLEX,%d0 +fm_ty_out: + movem.l (%sp),%d1-%d2/%a0-%a1 + lea 16(%sp),%sp +fm_ty_rts: + rts + +| ---- 0x400334d8 (jmp, 6 B): the machine-name formatter name(row): rows 5.. -> their +| names; any other row replays the displaced `movel %d2,%sp@-; movel %sp@(8),%d1`. +fm_machine_name: + move.l 4(%sp),%d0 + subq.l #FM_ROW,%d0 + cmpi.l #ML_COUNT,%d0 + bcs fm_mn_row | (unsigned) one of ours + move.l %d2,-(%sp) | displaced + move.l 8(%sp),%d1 | displaced + jmp NAME_RET +fm_mn_row: + lea ml_rows(%pc),%a0 + movea.l (%a0,%d0.l*4),%a0 + move.l RD_NAME(%a0),%d0 + rts + +| ---- 0x4003d718 (jmp, 6 B): a track's machine name, d0 = its machine (0..4 checked), +| a0 = its machine byte; displaced `lea 0x400a78c8,%a0`, then d1 := the name (the +| instruction after, `movel %a0@(0,%d0:l:4),%d1`, is done here: rejoins after it). +fm_name_a: + bsr fm_type | FLEX + signed -> its row + lea NAMES,%a0 | displaced + cmpi.l #FM_ROW,%d0 + bge fm_na_row + move.l (%a0,%d0.l*4),%d1 + jmp NAME_A_RET +fm_na_row: + lea ml_rows-4*FM_ROW(%pc),%a0 + movea.l (%a0,%d0.l*4),%a0 + move.l RD_NAME(%a0),%d1 + lea NAMES,%a0 + jmp NAME_A_RET + +| ---- 0x4003c980 (jmp, 6 B): SRC SETUP's row highlight: displaced `mvsb %a0@,%d0; lea +| %sp@(24),%sp` (a0 = the track's machine byte); a signed track's row is its row. +fm_setup_row: + mvs.b (%a0),%d0 + lea 24(%sp),%sp + bsr fm_type + jmp SETUP_ROW_RET + +| ---- 0x400786c8 (jmp, 6 B): the machine window's row highlight: displaced `mvsb +| %a0@,%d0; cmpl %d0,%d2; bnes 0x400786fc` (d2 = the visible row drawn; 2.11: the +| window scrolls, so the list row is d2 + its top, ml_draw_name). +fm_chooser_row: + mvs.b (%a0),%d0 + bsr fm_type + move.l WIN_LIST,%d1 | the list's top row (d1 is reloaded at 0x400786fc) + add.l %d2,%d1 + cmp.l %d0,%d1 + bne fm_cr_miss + jmp CHOOSER_HIT +fm_cr_miss: + jmp CHOOSER_MISS + +| ---- 0x400585dc (jmp, 10 B): SRC SETUP opens on the track's row: displaced `moveb +| %a0@,%d3; mvsb %d3,%d4; pea 0x400bb704`; d4 indexes the PLAYBACK descriptor table +| (slots 5..: the rows' pages, fm_tick), d3 is the row the window starts on. +fm_setup_open: + move.b (%a0),%d3 + move.l %d0,-(%sp) + mvs.b %d3,%d0 + bsr fm_type + move.l %d0,%d3 + move.l (%sp)+,%d0 + mvs.b %d3,%d4 + pea (0x400bb704).l | displaced + jmp SETUP_OPEN_RET + +| ---- 0x40078886 (jmp, 10 B): the machine window opens on the track's row: displaced +| `mvsb %a0@,%d0; movel %d0,%sp@-; pea 0x460e7386`. +fm_chooser_open: + mvs.b (%a0),%d0 + bsr fm_type + move.l %d0,-(%sp) + pea (0x460e7386).l | displaced + jmp CHOOSER_OPEN_RET + +| ---- 0x4003a52e / 0x4003cd98 (jmp, 8 B each): SRC SETUP's editor and drawer address the +| row's Part bytes at row * 6: displaced `movel %dN,%d0; lsll #3,%d0; addl %dN,%dN; subl +| %dN,%d0` (d2 / d6, the row; d0 / d7 := row * 6). Rows 5.. address FLEX's. +fm_setup_edit6: + cmpi.l #FM_ROW,%d2 + blt fm_e6 + moveq #FLEX,%d2 +fm_e6: + move.l %d2,%d0 + lsl.l #3,%d0 + add.l %d2,%d2 + sub.l %d2,%d0 + jmp EDIT6_RET +fm_setup_draw6: + cmpi.l #FM_ROW,%d6 + blt fm_d6 + moveq #FLEX,%d6 +fm_d6: + move.l %d6,%d7 + lsl.l #3,%d7 + add.l %d6,%d6 + sub.l %d6,%d7 + jmp DRAW6_RET + +| ---- 0x4005221e (jmp, 10 B): the UI tick: displaced `jsr 0x4005213c; jsr 0x4007e940`, +| replayed; then SRC SETUP's name table is filled from the stock one and the rows' names, +| and the PLAYBACK descriptor table's spare slots 5.. get the rows' pages (FM SYNTH: +| page.s, built on first use; SY DRUM: its unit's), so the SRC SETUP window has a page +| for each row. (Slots 5 and 6 are spares holding NEIGHBOR's descriptor in stock; slot 7 +| is the table's end: two rows at most write here.) +fm_tick: + jsr TICK_A | displaced + jsr TICK_B | displaced + lea NAMES,%a0 + lea fm_src_names(%pc),%a1 + moveq #5,%d0 +fm_tk_copy: + move.l (%a0)+,(%a1)+ + subq.l #1,%d0 + bne fm_tk_copy + move.l %a2,-(%sp) + move.l %d2,-(%sp) + moveq #0,%d2 +fm_tk_row: + lea ml_rows(%pc),%a2 + movea.l (%a2,%d2.l*4),%a2 | the row's descriptor + move.l RD_NAME(%a2),%d0 | its name + lea fm_src_names+4*FM_ROW(%pc),%a0 + move.l %d0,(%a0,%d2.l*4) + movea.l RD_DESC(%a2),%a0 + jsr (%a0) | d0 = its page (d0/d1/a0/a1 clobbered) + lea PB_TABLE+4*FM_ROW,%a0 + move.l %d0,(%a0,%d2.l*4) + addq.l #1,%d2 + cmpi.l #ML_COUNT,%d2 + bne fm_tk_row + move.l (%sp)+,%d2 + movea.l (%sp)+,%a2 + jmp TICK_RET + +| ---- the six row-count and row-bound sites (2.11): the stock list code was written for +| five machines; 2.10 poked its constants to six rows. Each detour does what the stock +| instruction did with 5 + ML_COUNT rows (the last row ML_LAST), and rejoins. +| 0x40079248 (jmp, 8 B): the machine window's list init, `pea 5; pea 6` (rows, visible). +ml_win_count: + pea (5+ML_COUNT).w | the rows + pea 6.w | the rows visible (stock's) + jmp WIN_COUNT_RET +| 0x400585fa (jmp, 8 B): SRC SETUP's list init, `pea 5; pea 5` (rows, visible). +ml_src_count: + pea (5+ML_COUNT).w + pea 5.w + jmp SRC_COUNT_RET +| 0x4003c950 (jmp, 6 B): SRC SETUP's name lookup, `moveq #4,%d1; cmpl %d2,%d1; bcss +| 0x4003c95a` (d2 = the row: past the last row, the default string). +ml_name_bound: + moveq #ML_LAST,%d1 + cmp.l %d2,%d1 + bcs ml_nb_out + jmp NAME_BOUND_IN +ml_nb_out: + jmp NAME_BOUND_OUT +| 0x40078678 (jmp, 12 B): the machine window's row drawer, `moveq #4,%d0; cmpl %d2,%d0; +| blts 0x400786a2; movel %d2,%sp@-; moveal %d3,%a0; jsr %a0@` (d2 = the visible row, d3 = +| the name formatter). It drew name(d2) for d2 = 0 .. 5 with no scroll offset: a seventh +| row was never drawn and the cursor box left its name (found by a study of the list code, 8 Oct 2026). 2.11: +| name(d2 + the list's top), up to the last row -- the window scrolls as SRC SETUP's does, +| six rows visible. With six rows or fewer the top stays 0: stock's drawing. +ml_draw_bound: + move.l WIN_LIST,%d0 | the top row + add.l %d2,%d0 + cmpi.l #ML_LAST,%d0 + bgt ml_db_out + move.l %d0,-(%sp) | (replaces the pushed d2: the same stack) + movea.l %d3,%a0 + jsr (%a0) + jmp DRAW_BOUND_IN +ml_db_out: + jmp DRAW_BOUND_OUT +| 0x400786ce (jmp, 6 B): the machine window's highlight, `moveq #4,%d0; cmpl %d2,%d0; +| blts 0x400786fc`. +ml_hi_bound: + moveq #ML_LAST,%d0 + cmp.l %d2,%d0 + blt ml_hb_out + jmp HI_BOUND_IN +ml_hb_out: + jmp CHOOSER_MISS +| The machine window's row clamp after a list move (five sites, 8 B each: `moveq #5,%d1; +| cmpl %d0,%d1; bges +2; moveq #5,%d0` -- the absolute row 0x460e738e kept to 0..5, the +| six rows the window shows, then handed to the list's row setter 0x4007edb0): a row past +| the sixth was put back on the sixth, so it could not be selected or committed (located by the +| development line's machine-list build, 8 Oct 2026). 2.11: kept to 0 .. the last row. + .macro ML_CLAMP n, back +ml_clamp\n: + moveq #ML_LAST,%d1 + cmp.l %d0,%d1 + bge 1f + moveq #ML_LAST,%d0 +1: jmp \back + .endm + ML_CLAMP 1, 0x40078c2a + ML_CLAMP 2, 0x40078cb8 + ML_CLAMP 3, 0x40078d36 + ML_CLAMP 4, 0x40078df6 + ML_CLAMP 5, 0x40078e84 +| 0x40079904 (jmp, 8 B): the machine window's cursor, `moveq #4,%d3; cmpl %d4,%d3; +| bgew 0x400797cc` (d4 = the cursor's row). +ml_cur_bound: + moveq #ML_LAST,%d3 + cmp.l %d4,%d3 + blt ml_cb_out + jmp CUR_BOUND_IN +ml_cb_out: + jmp CUR_BOUND_OUT + +| ---- 0x40002318 (jmp, 8 B): the Part validator (4(sp) = the Part's +0), entered before +| its prologue. The stock validator clamps every column's PLAYBACK bytes into its +| descriptor's ranges -- FLEX's PTCH into 4..124, where FM SYNTH's PTCH is 0..127 +| (-64..+63 semitones) -- and the SETUP bytes into theirs (SY DRUM's LSPD is 0..127 in +| FLEX's LOOP byte). For each track signed with a row of ml_rows the twelve FLEX bytes +| are swapped for the stock FLEX defaults (read from the descriptor), the stock +| validator runs, and the row's bytes are put back. d0 = the stock result. +fm_validate: + lea -116(%sp),%sp | 96 B: 8 tracks x 12 saved bytes; 20 B: d2-d4/a2-a3 + movem.l %d2-%d4/%a2-%a3,96(%sp) + movea.l 120(%sp),%a2 + suba.l #PART_OFF,%a2 | blob-relative: + MACH_OFF / SIG_OFF / FLEX_PB + moveq #0,%d3 | the signed tracks + moveq #0,%d2 +fm_vd_save: + movea.l %a2,%a0 + adda.l #MACH_OFF,%a0 + mvz.b (%a0,%d2.l),%d0 | the track's machine + subq.l #FLEX,%d0 + bne fm_vd_snext | not FLEX + move.l %d2,%d0 + mulu.w #30,%d0 + movea.l %a2,%a0 + adda.l %d0,%a0 + adda.l #SIG_OFF,%a0 + bsr ml_sigrow | signed with one of the rows? + bmi fm_vd_snext + bset %d2,%d3 + bsr fm_vd_ptrs | a0 = its FLEX bytes, a1 = its save area, a3 = the defaults + moveq #6,%d4 +fm_vd_s1: + move.b (%a0),(%a1) + move.b SETUP_GAP(%a0),6(%a1) + move.b (%a3),(%a0) + move.b 6(%a3),SETUP_GAP(%a0) + addq.l #1,%a0 + addq.l #1,%a1 + addq.l #1,%a3 + subq.l #1,%d4 + bne fm_vd_s1 +fm_vd_snext: + addq.l #1,%d2 + moveq #8,%d0 + cmp.l %d0,%d2 + bne fm_vd_save + move.l 120(%sp),-(%sp) + bsr fm_stock_validate + addq.l #4,%sp + moveq #0,%d2 +fm_vd_rest: + btst %d2,%d3 + beq fm_vd_rnext + move.l %d0,-(%sp) + bsr fm_vd_ptrs4 | (the stack is 4 deeper here) + move.l (%sp)+,%d0 + moveq #6,%d4 +fm_vd_r1: + move.b (%a1),(%a0) + move.b 6(%a1),SETUP_GAP(%a0) + addq.l #1,%a0 + addq.l #1,%a1 + subq.l #1,%d4 + bne fm_vd_r1 +fm_vd_rnext: + addq.l #1,%d2 + moveq #8,%d4 + cmp.l %d4,%d2 + bne fm_vd_rest + movem.l 96(%sp),%d2-%d4/%a2-%a3 + lea 116(%sp),%sp + rts +| fm_vd_ptrs: d2 = the track -> a0 = blob-relative Part a2 + FLEX_PB + 30 * track, a1 = +| the caller's save area + 12 * track, a3 = the stock FLEX defaults (d0 clobbered). +fm_vd_ptrs: + lea 4(%sp),%a1 | the caller's frame: its save area at its 0(sp) + bra fm_vd_p +fm_vd_ptrs4: + lea 8(%sp),%a1 +fm_vd_p: + move.l %d2,%d0 + mulu.w #12,%d0 + adda.l %d0,%a1 + move.l %d2,%d0 + mulu.w #30,%d0 + movea.l %a2,%a0 + adda.l %d0,%a0 + adda.l #FLEX_PB,%a0 + lea FLEX_DEF,%a3 + rts +fm_stock_validate: + lea -96(%sp),%sp | displaced + movem.l %d2-%d7/%a2-%fp,(%sp) | displaced + jmp VALIDATE_BODY + +fm_defaults: + .byte 64, 12, 32, 64, 0, 40 | PLAYBACK: PTCH 0, RATO 1, INDX 32, FINE 0c, FDBK 0, DEC 40 (Modwerk's) + .byte 0, 0, 0, 0, 0, 0 | SETUP: LOOP SLIC LEN RATE TSTR TSNS all 0 (OFF) +fm_name: + .asciz "FM SYNTH" + .balign 4 +| FM SYNTH's row descriptor (the layout above ml_rows) +fm_row: + .byte 'F', 'M', 1, 1 | the signature "FM", 1; engine kind 1 + .long fm_name + .long fm_defaults + .long FM_DESC | page.s's fm_descriptor: the FM SYNTH page (built on first use) + .byte 0, 0, 0, 0 | flags: its SETUP bytes are FLEX's (kept when it is left, as 2.10) +| THE ROWS after the stock five, in list order (2.11): FM SYNTH, then each machine module +| on this engine the remix carries -- SY DRUM's descriptor is its own unit's sd_row. +ml_rows: + .long fm_row + .if HAVE_SYDRUM + .long sd_row + .endif +| SRC SETUP's name table (the lea at 0x4003c928 points here): the stock five, copied +| by fm_tick, and the rows' names. +fm_src_names: + .fill 5+ML_COUNT, 4, 0 diff --git a/sdk/octabam/modules/synth/upstream/synth/manifest.py b/sdk/octabam/modules/synth/upstream/synth/manifest.py index c6c83e614..10875cfd3 100644 --- a/sdk/octabam/modules/synth/upstream/synth/manifest.py +++ b/sdk/octabam/modules/synth/upstream/synth/manifest.py @@ -12,7 +12,8 @@ parameters, read per frame from the DSP parameter record (halfwords, raw << 8): STRT = RATIO (32-step table 0.25..16), LEN = INDEX (0..8 rad), RTRG = FEEDBACK (0..0.25 cycle), RTIM = DECAY of the index toward 1/16 (time -constant 2 s * (raw/127)^2; 0 = hold). Phase 3 relabels the slots. +constant 2 s * (raw/127)^2; 127 = hold, 0 = the shortest). Phase 3 +relabels the slots. WHERE IT HOOKS. The per-frame record packer (0x4000d3fc) renders each track's audio through a per-track renderer pointer copied from the kind table @@ -42,7 +43,7 @@ slot's sample is named SYNTH* -- by the settings record's path, loaded into flex RAM or not -- it returns a clone of the FLEX descriptor whose slots read PTCH RATO INDX FINE FDBK DEC, whose title makes the footer read FM SYNTH>FLEX, -whose formatters print the ratio table's value, 0..127 and HOLD/ms/s, and +whose formatters print the ratio table's value, 0..127 and ms/s/HOLD, and whose widgets draw the M->C operator diagram, a sideband spectrum, the modulator with its feedback loop and the index envelope over the stock dial. THE TUNING SYSTEM (27 Sep 2026): on a synth track PTCH is semitones, raw 64 @@ -131,8 +132,9 @@ SAME oscillator phase-continuously from its current level (the attack re-runs from there); a silent one starts at phase 0 from 0. Note ends: every note-off path (po_rel), the HOLD timer, MIDI note-off, the key mask, the sequencer trig, -and STOP (po_stop, a new detour at the frame builder's consumption of the -sequencer's STOP word 0x46c80350, after it posts the DSP all-off). Voice +and STOP (BUILD 31's po_stop, a detour at the frame builder's consumption of +0x46c80350 taken for the sequencer's STOP word; that word is the metronome's +click, and 2.12 removes po_stop -- see BUILD 33 and 2.12 below). Voice stealing and chord-memory cuts fade over 8 frames (2.9 ms, state 3). The per-frame peak limiter is REMOVED: the sum is clamped (saturated) only; the level law 1/sqrt(VOIC) is unchanged. README "The engine owns the envelope". @@ -151,7 +153,7 @@ 1.13). The panel key's START reaches the DSP as 0x30 (bits 4 and 5, nibble 0) and is clean. po_retrig, a detour at the copy (0x4000c634), rewrites every START byte on a synth track whose engine voice is on (S_ON) to that clean form; the -trig lands a frame boundary early (at most 0.34 ms). Measured (root29w/run_f37a.log, +trig lands a frame boundary early (at most 0.34 ms). Measured (BUILD 37's log, a trig on every step at 120 BPM, VOIC 1 C4, ATK 0 HOLD INF REL 60 INDX 0): max |step| / the tone's slope x1.01, max |d2| 9, per-frame amplitude 1.00 on every frame of every trig, the fix fired on every trig (po_rtlog: 39 rewrites, the @@ -171,12 +173,12 @@ are the running value, sy_loop / po_fi_loop step them by S_ISTEP (+126, the word S_HOLD / S_KEYED held) / V_ISTEP (+38), (target - running) / 16, once a frame -- no frame-edge step for the ramp, the decay or a knob. V_GPREV is a word -at +60. Measured (root29x/run_v38b.log, take b: HOLD INF REL 60 INDX 40 DEC 40, +at +60. Measured (BUILD 38's log, take b: HOLD INF REL 60 INDX 40 DEC 40, a trig every step): max |step| / the tone's slope x1.07, max |d2| 88 against the take's own 99.9th pct 108, the centroid moving over ~8 ms. BUILD 33 (round 3): STOP ENDS THE ENGINE'S VOICES AT THE STOCK VOICE KILL. Round 2 -measured (root29q-verify/run_stop.log) that po_stop's site 0x4000b2c8 never runs +measured (an independent rerun's STOP log) that po_stop's site 0x4000b2c8 never runs on the rig for any STOP form -- the global word's writers (0x4009bbb8 / 0x4009c3a0 / 0x400a4d8c) sit behind [0x80000060] and the pattern-state bytes 0x80006511/12, and the frame builder's consumer behind two more gates -- so a REL INF tone kept @@ -190,8 +192,18 @@ 0x4000685c (interrupts masked, d1 = the track): the killed track's engine record ends at that instant -- S_GAIN / S_GPREV / S_HTIM / S_ON := 0, the four paraphonic voices freed with V_KEY / V_HOLD 0 -- so nothing of ours sounds -after the kill and the next START is cold. po_stop stays as belt-and-braces (a -killed track has S_ON 0: it does nothing there). +after the kill and the next START is cold. po_stop stayed as belt-and-braces (a +killed track has S_ON 0: it does nothing there) -- until 2.12. +2.12 (10 Oct 2026): A HELD NOTE OR CHORD IS NO LONGER RELEASED AT EVERY METRONOME +CLICK. The global word 0x46c80350 is the METRONOME's: while the sequencer plays, +the beat code writes 3 (the bar's first beat) or 1 (the other beats), every writer +behind METRONOME_ENABLED (0x80000060 -- the gate BUILD 33 saw closed on the rig), +and the frame builder turns it into the DSP-wide click command 0x46104d14 and +clears it at 0x4000b2c8. po_stop, hooked at that clear, therefore ran at every +click and nowhere else: every synth track's mono voice took the released flag and +STOPPED (REL INF: cut within 5 ms), every sounding paraphonic voice released, while +the keys were still held. The detour and po_stop are removed; 0x4000b2c8 is +stock; STOP stays po_kill's (above). History, BUILD 27: WARM ONLY WHEN THE DSP IS PROVABLY SUSTAINING AT FULL -- BUILD 26's cold start made a new key while the old note still sounds (LEG OFF, HOLD INF) a hard cut at the frame edge (a -12 dB 5 ms click). sy_cold keeps the phases and @@ -283,6 +295,42 @@ track whose FLEX slot is that slot. A marker over a marker, a sample over a marker, a STATIC slot, a recorder buffer: untouched. +A MACHINE CHANGE RESETS THE TRACK TO THE NEW MACHINE'S INIT (2.12, machine.s +ml_reset). To or from FM SYNTH / SY DRUM -- through the lists (fm_choose), the +slot assigner (po_assign -> ml_became) or the file browser (po_loadsel) -- the +track's PLAYBACK and SETUP, AMP and AMP SETUP, LFO page and LFO SETUP take the +new machine's init (a stock machine's: the stock page descriptors' defaults, as +the stock Part initialiser writes them); step and scene locks are kept, as in +the stock firmware (SY DRUM's lock rows too); FX1 / FX2 stay. The browser's select unloads the slot (0x40025288 clears its +settings record) before the path write -- twice: the browser's load callback +0x400226f4 unloads it, then calls the select, which unloads it again -- so the +file the slot had is read at the callback's call: one more jsr detour, +0x40022732 -> po_unload. + +FM SYNTH IN THE MACHINE LIST (2.10, 8 Oct 2026, machine.s; adapted from +Modwerk's FM Synth module, MIT, Modwerk contributors). FM SYNTH is the sixth +row of SELECT MACHINE TYPE (the track key twice, then LEFT) and of SRC SETUP +(FUNC + SRC). Choosing it stores the track as FLEX with "F", "M", 1 in the +first three bytes of its NEIGHBOR PLAYBACK column (the bank blob + part * +6322 + 0x8edbc + 30 * track, and the battery-RAM shadow): no marker file, no +sample. Fourteen detours (the machine window's commit shares MACHWIN_HOOK), +six pokes (the lists' bound 5 -> 6) and one SymbolRef (the kind table's FLEX +START callback -> po_fmstart: the voice starts without a sample; the frame +builder's second supplier call -> po_fmsource: its source is sy_render). The +page cave gained fm_descriptor (pg_resolve moved to +16) and the signature +test; the quantizer's qz_is_synth tests the signature too. The marker files +still select the engine. + +THE MACHINE LIST FOR MORE THAN ONE MACHINE (2.11, machine.s): the rows after +PICKUP are a table (ml_rows) assembled per remix -- FM SYNTH always, SY DRUM +(sy-drum/) when the remix carries it -- and the six pokes became six detours +that take the remix's row count (6 with FM SYNTH alone, 7 with SY DRUM), so +a second machine module adds a row without a site of its own. The units +include a per-remix remix.inc (remix_include below): HAVE_SYDRUM and the +equates shared with SY DRUM (engine_abi.inc); with HAVE_SYDRUM 0 the engine +(poly.s) is byte for byte 2.10's. ANALOG BD is a declared conflict (it owns +the same chooser sites). + Verified in ot_emu through the virtual panel and the pipe (README); flashed as OCTATRICK9 on an MKI, 26 Sep 2026 (emulator-verified since). """ @@ -290,7 +338,7 @@ import os from remix.stock_guard import stock_guard -from remix.schema import CavePatch, Detour, Kind, Linked, Module, SymbolRef +from remix.schema import Detour, Kind, Linked, Module, SymbolRef # This module's own directory, relative to the build's cwd (octabam's repo # root): "modules/synth" when the module is checked out directly, and @@ -299,6 +347,22 @@ # so one manifest serves both layouts. _HERE = os.path.relpath(os.path.dirname(os.path.realpath(__file__))) + +def engine_abi(): + """synth/engine_abi.inc: the equates SYNTH MACHINE's units share with the machine + modules that run on its engine (sy-drum). Read from disk, so one file is the truth.""" + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "engine_abi.inc"), + encoding="utf-8") as source: + return source.read() + + +def remix_include(modules): + """The `remix.inc` each unit of this module includes (octabam's Linked.include, written + per remix): HAVE_SYDRUM -- 1 when SY DRUM (sy-drum/) is in the remix: its machine-list row + and the engine's calls into its unit are assembled; 0 assembles nothing of it, byte for + byte the 2.10 engine (poly.s) -- then the shared equates.""" + return f".set HAVE_SYDRUM, {int('SY DRUM' in modules)}\n" + engine_abi() + # The kind table: kind -> renderer, 8 longs at 0x400d6434 (0 STATIC, 1 FLEX, # 2 THRU, 3 NEIGHBOR, 4 PICKUP). STATIC and FLEX share the stock sample # renderer 0x40004008; both entries point at sy_render since OCTATRICK2.8 @@ -327,8 +391,6 @@ MIDI_GATE_STOCK = stock_guard(0x4000e452, 8, "1e3f89d63562977cf6928c72536e014118c40df97d7f5f3d80c78b10ed4c0448") RETRIG_HOOK = 0x4000c634 # the frame builder's per-track copy of the DSP command byte 0x46104d15[t] into the packer's nibble byte 0x46104d0c[t]: `moveal 114(sp),a1; moveb (a0,a1.l),d0` (a0 = 0x46104d15; BUILD 37: po_retrig turns a sequencer START on a sounding synth voice, 0x10 | n, into the key's clean 0x30) RETRIG_STOCK = stock_guard(0x4000c634, 8, "59609fac3f03905ee63fde6f557553d44e997fc6e02eb6c8aa4c475715ff2662") -STOP_HOOK = 0x4000b2c8 # `clrl 0x46c80350` (the frame builder consumes the sequencer's STOP / restart word after posting the DSP all-off; plan B: po_stop ends every engine voice) -STOP_STOCK = stock_guard(0x4000b2c8, 6, "172262e5ddbeacf59e683eccacdcbe33bfaa4609e2ff79f932b5e1b70a9a93bb") KILL_HOOK = 0x4000685c # the stock VOICE KILL 0x40006820(t): `clrb %d0; moveb %d0,%a1@(0,%a0:l)`, the CF voice byte 0x800049d8 + 168 t := 0, interrupts masked; d1 = t (BUILD 33: po_kill ends the engine's voices of the track at that instant) KILL_STOCK = stock_guard(0x4000685c, 6, "98e9488a15bc1e186e25540bc49839ccd37ffcb4a835ab2c26ec17362380b922") REL_HOOK = 0x4000b51a # `moveal %sp@(114),%a3; movel %a1@(0,%a3:l:4),%d0` (the frame builder's AMP-release consumer, mailbox bit 6: every note-off path) @@ -378,6 +440,67 @@ # the sample-list window's LOAD FILE never touches the Part). LOADSEL_HOOK = 0x40022686 # `jsr 0x40013a08` (sprintf: record, "%s..", path) in the browser's select 0x40022610 LOADSEL_STOCK = stock_guard(0x40022686, 6, "ffcc36e1a4e00547dfdf889bb0fa8d8eccd4ca4166d097f5f79689712cd0cb07") +UNLOAD_HOOK = 0x40022732 # `jsr 0x40025288` (the slot unload: machine, slot) in the browser's load callback 0x400226f4, before its call of the select +UNLOAD_STOCK = stock_guard(0x40022732, 6, "1828d38aca11808e502a8f12cd4843db42612d2a1317d5ed1225d617cfc5b5e4") + +# 2.10 (8 Oct 2026): FM SYNTH IN THE MACHINE LIST (machine.s; after Modwerk's FM Synth +# module, MIT, Modwerk contributors -- its dedicated chooser, Part validation and +# sample-free transport). FM SYNTH is the sixth row of the machine window (FUNC + SRC) +# and of SRC SETUP; a chosen track is stored as FLEX with "FM", 1 in its NEIGHBOR +# PLAYBACK bytes (the Part + 0x8edbc + 30 * track, and the battery-RAM shadow), so it +# needs no marker file and no sample. The marker files still select the engine. +# The machine window's commit shares MACHWIN_HOOK (fm_main_commit, then po_machwin). +FM_NAME_HOOK = 0x400334d8 # `movel %d2,%sp@-; movel %sp@(8),%d1` (the machine-name formatter name(row)) +FM_NAME_STOCK = stock_guard(0x400334d8, 6, "d6c9f1f60272197a17511f496fc19dd21ddd7df5f1f1ec0cab7a16cad60b0609") +FM_NAMES_HOOK = 0x4003c928 # `lea 0x400a78c8,%a5` (SRC SETUP's name table: five pointers) +FM_NAMES_STOCK = stock_guard(0x4003c928, 6, "237cd1acd4da7acb61dc7fda7c88e648a5186b36732f433b4dd91966c5a669af") +FM_NAME_A_HOOK = 0x4003d718 # `lea 0x400a78c8,%a0` (a track's machine name, a0 = its machine byte) +FM_NAME_A_STOCK = stock_guard(0x4003d718, 6, "53b8d23acf2c3fe0f794c0a5f37475b17c3433bc6a466e0e1861515989cfecda") +FM_SETUP_ROW_HOOK = 0x4003c980 # `mvsb %a0@,%d0; lea %sp@(24),%sp` (SRC SETUP's row highlight) +FM_SETUP_ROW_STOCK = stock_guard(0x4003c980, 6, "237e017ccd95444195ca390a3e6ca6076fbfa1f0931f2e7dfbdbee8c5ec72ca3") +FM_CHOOSER_ROW_HOOK = 0x400786c8 # `mvsb %a0@,%d0; cmpl %d0,%d2; bnes 0x400786fc` (the machine window's row highlight) +FM_CHOOSER_ROW_STOCK = stock_guard(0x400786c8, 6, "c611970befafba97f882ab297bd19f0efe0c7d927bfd6110c53f03ad4b7da1c7") +FM_SETUP_OPEN_HOOK = 0x400585dc # `moveb %a0@,%d3; mvsb %d3,%d4; pea 0x400bb704` (SRC SETUP opens on the track's row) +FM_SETUP_OPEN_STOCK = stock_guard(0x400585dc, 10, "22fb6e2bea4520003d2fae7035c0cd57bcd6fe6f2cfdd5b70580a3f6f2a68034") +FM_CHOOSER_OPEN_HOOK = 0x40078886 # `mvsb %a0@,%d0; movel %d0,%sp@-; pea 0x460e7386` (the machine window opens on it) +FM_CHOOSER_OPEN_STOCK = stock_guard(0x40078886, 10, "fe6cb52458c7d968dc467c1cd7721d832baf6bc2fac1826c8c10ed07fee2e7f8") +FM_EDIT6_HOOK = 0x4003a52e # `movel %d2,%d0; lsll #3,%d0; addl %d2,%d2; subl %d2,%d0` (SRC SETUP's editor: row * 6) +FM_EDIT6_STOCK = stock_guard(0x4003a52e, 8, "cae34bf50383462ed4f92d8044e3021247edb8bf69c17a9c7ead62f6edf79510") +FM_DRAW6_HOOK = 0x4003cd98 # `movel %d6,%d7; lsll #3,%d7; addl %d6,%d6; subl %d6,%d7` (its drawer: the same) +FM_DRAW6_STOCK = stock_guard(0x4003cd98, 8, "ed71ef874312890edc87cef7e19cd8c0f8ac4ac9b90182c2364f78d3a4567f45") +FM_TICK_HOOK = 0x4005221e # `jsr %pc@(0x4005213c); jsr 0x4007e940` (the UI tick) +FM_TICK_STOCK = stock_guard(0x4005221e, 10, "8f0fa4f7d6db857f695996b185b38b788fa6af0eacd86d7dd01e2ff61201ffed") +FM_SRC_COMMIT_HOOK = 0x4005a616 # `movel 0x460d5c30,%d1` (SRC SETUP's machine-byte write: the chosen row) +FM_SRC_COMMIT_STOCK = stock_guard(0x4005a616, 6, "7a7350a69423ec41ec468a189ba167059626cb7f499d9862394d2388a23c8b59") +FM_SRC_COMMIT2_HOOK = 0x4005a850 # the same instruction on its second path (after LISTWIN_HOOK, which returns here) +FM_SRC_COMMIT2_STOCK = stock_guard(0x4005a850, 6, "7a7350a69423ec41ec468a189ba167059626cb7f499d9862394d2388a23c8b59") +FM_VALIDATE_HOOK = 0x40002318 # `lea %sp@(-96),%sp; moveml %d2-%d7/%a2-%fp,%sp@` (the Part validator's prologue) +FM_VALIDATE_STOCK = stock_guard(0x40002318, 8, "3d25d06bf11f3a7bccef894d0016b52adbb4ae468a0f475dd497d7b5966e2694") +FM_SOURCE_HOOK = 0x4000d514 # `moveal %a4@+,%a0; movel %a0,%a3@+; pea 0x10` (the frame builder's second supplier call) +FM_SOURCE_STOCK = stock_guard(0x4000d514, 8, "39173055fb04ac0a6c355f89561b48834bc5b31ad67609e3fe53efbff7de1866") +# 2.11: the machine list's six row-count / row-bound sites (machine.s ml_*; pokes 5 -> 6 until 2.10) +ML_WIN_COUNT_HOOK = 0x40079248 # `pea 5; pea 6` (the machine window's list init: rows, visible) +ML_WIN_COUNT_STOCK = stock_guard(ML_WIN_COUNT_HOOK, 8, "3ec3e33dc9eae9cfbe075a2a5e2697563293203896e3649ae7e7a718a6d7f34c") +ML_SRC_COUNT_HOOK = 0x400585fa # `pea 5; pea 5` (SRC SETUP's list init: rows, visible) +ML_SRC_COUNT_STOCK = stock_guard(ML_SRC_COUNT_HOOK, 8, "28992093b4deda97ffa595ce8acd14c8a2a391c67871c1e8dd3421d97467a29d") +ML_NAME_BOUND_HOOK = 0x4003c950 # `moveq #4,%d1; cmpl %d2,%d1; bcss 0x4003c95a` (SRC SETUP's name lookup) +ML_NAME_BOUND_STOCK = stock_guard(ML_NAME_BOUND_HOOK, 6, "6fa1f2f2fc3d83f1e1917364e374491dd04fc060d1beea8b77f03bdd332e2b75") +ML_DRAW_BOUND_HOOK = 0x40078678 # `moveq #4,%d0; cmpl %d2,%d0; blts 0x400786a2` (the machine window's drawer) +ML_DRAW_BOUND_STOCK = stock_guard(ML_DRAW_BOUND_HOOK, 12, "d0853f882affa9f487c3a82a5e08c3b3bc4c3082692c8af90dd2949eb191a733") # ... movel %d2,%sp@-; moveal %d3,%a0; jsr %a0@ (12 B) +ML_HI_BOUND_HOOK = 0x400786ce # `moveq #4,%d0; cmpl %d2,%d0; blts 0x400786fc` (its highlight) +ML_HI_BOUND_STOCK = stock_guard(ML_HI_BOUND_HOOK, 6, "2c62e6999a40a15016ea1fe57b7ca93f9d4fe1558127921ae736814a30b5455b") +ML_CUR_BOUND_HOOK = 0x40079904 # `moveq #4,%d3; cmpl %d4,%d3; bgew 0x400797cc` (its cursor) +ML_CUR_BOUND_STOCK = stock_guard(ML_CUR_BOUND_HOOK, 8, "845ca10fcd33861c3a74cd9bb5f9da1fd78bb0cf44e30f2fc0ae43db045a633e") +ML_CLAMP_HOOKS = (0x40078c22, 0x40078cb0, 0x40078d2e, 0x40078dee, 0x40078e7c) # the machine window's row clamp after a list move +ML_CLAMP_SHA256 = "d749208f2ebf1e144a87c325281e533ef582759f54549234fcc92c90dc89a595" # `moveq #5,%d1; cmpl %d0,%d1; bges +2; moveq #5,%d0` +ML_CLAMP1_STOCK = stock_guard(0x40078c22, 8, ML_CLAMP_SHA256) +ML_CLAMP2_STOCK = stock_guard(0x40078cb0, 8, ML_CLAMP_SHA256) +ML_CLAMP3_STOCK = stock_guard(0x40078d2e, 8, ML_CLAMP_SHA256) +ML_CLAMP4_STOCK = stock_guard(0x40078dee, 8, ML_CLAMP_SHA256) +ML_CLAMP5_STOCK = stock_guard(0x40078e7c, 8, ML_CLAMP_SHA256) +ML_CLAMP_GUARDS = (ML_CLAMP1_STOCK, ML_CLAMP2_STOCK, ML_CLAMP3_STOCK, ML_CLAMP4_STOCK, ML_CLAMP5_STOCK) # the same 8 stock bytes at each site +KIND_TABLE_FLEX_START = 0x400d6458 # the kind table's FLEX START callback (stock 0x4000f450) +FLEX_START = 0x4000f450 # The FM voice engine is a DRAM unit since 24 Sep 2026 (poly.s, the # paraphonic engine): linked into the platform runtime at the base of the @@ -389,7 +512,7 @@ # resolver (0x40031da4, the kind-0 `tbl[machine]` load at 0x40031ece) returns a # runtime clone of the FLEX descriptor -- names PTCH RATO INDX FINE FDBK DEC, the title # "FM SYNTH" (the footer reads FM SYNTH>FLEX), formatters (the ratio table's -# value, 0..127, HOLD/ms/s), widgets that draw the operator diagram, the +# value, 0..127, ms/s/HOLD), widgets that draw the operator diagram, the # sideband spectrum, the feedback loop and the index envelope over the stock # dial -- when the current track's assigned FLEX sample is named FMSYNTH* (or SYNTH*). # The clone itself is built at runtime by poly.s (po_pgdesc, first use) from @@ -398,31 +521,48 @@ # holds absolute pointers into the cave. One 6-byte poke (`movel # %a0@(0,%d0:l:4),%d0; bras` -> `jmp pg_resolve`). PAGE_AT = 0x400d24d0 -PAGE_LEN = 1812 +PAGE_LEN = 1868 +PG_RESOLVE_AT = 16 # pg_resolve's offset in the cave (fm_descriptor, 16 B, is first) RESOLVER_HOOK = 0x40031ece RESOLVER_STOCK = stock_guard(0x40031ece, 6, "5aed68f7498659f87e414631493741e91f3442c5319f46e6893b567c423ce24f") +# 2.10 (6 Oct 2026, poly.s "THE LFO DESTINATION LIST"): LFO SETUP's PMTR. The +# formatter 0x4003bf64 (the LFO descriptor's slot-6 formatter) picks the PLAYBACK +# names from the machine table and the LFO names from the stock descriptor +# itself, not through the page resolver; the audio edit 0x400392cc walks all 30 +# destinations. On a synth track the names become the FM SYNTH page's and the +# VOIC / CHRD clone's, and the edit steps over SPD3 / DEP3 (VOIC / CHRD). +LFD_NAME_HOOK = 0x4003bff2 # `lea 0x400d5f38,%a0; bras 0x4003c054` (page 0: the machine table) +LFD_NAME_STOCK = stock_guard(0x4003bff2, 8, "646fea5c7ac7852156feed7591534de986df92af53074c25ecf54e4295d072ae") +LFD_LFO_HOOK = 0x4003bffa # `lea 0x400d37f6,%a2; bras 0x4003c058` (page 1: the LFO descriptor) +LFD_LFO_STOCK = stock_guard(0x4003bffa, 8, "c317505c7277fb592c805f2fcf7b5c789d8d1b43735f0677697b101a75473e71") +LFD_EDIT_HOOK = 0x400392cc # `movel %d3,%d0; moveq #6,%d2; remsl %d2,%d7,%d0` (the audio PMTR edit after its branches) +LFD_EDIT_STOCK = stock_guard(0x400392cc, 8, "a57855f641d419b81f08ce2f502a1bf18363df142106b0b0ab926fc65b1c0948") # Ratified bytes: page.s with m68k-elf-as -mcpu=5475, linked at PAGE_AT (26 Sep 2026: the # runtime clone -- 1,672 B, down from 1,948: the 402-byte copy of the stock descriptor is gone, # and the "%d" formatter is the stock's own; 27 Sep 2026: 1,800 B with the tuning system's # PTCH and FINE formatters and the range / handler overrides; 30 Sep 2026: 1812 B, the -# FMSYNTH* marker name -- a leading "FM" is skipped before the SYNTH compare). -# Page bytes are assembled from authored source; no embedded stock replay bytes. - - - - +# FMSYNTH* marker name -- a leading "FM" is skipped before the SYNTH compare; 5 Oct 2026: +# still 1812 B, DEC's HOLD moved from raw 0 to raw 127 -- short branches pay for the compare; +# 8 Oct 2026: 1868 B, the FM SYNTH machine -- fm_descriptor (16 B) first, so pg_resolve is at +# +16, and pg_resolve's test of the Part's "FM", 1 ahead of the marker scan; still inside the +# zero run 0x400d24d0..0x400d2ce0, 2064 B). MODULE = Module( name="synth", key="SYNTH MACHINE", kind=Kind.CF_PATCH, - doc="A FLEX track whose sample is named SYNTH* plays a two-operator FM " + # 2.11: ANALOG BD (octabam modules/analog-bassdrum) owns the same machine-list sites + # (the sixth row and its hooks); the ledger refuses the pair on bytes already, this + # names the reason. + conflicts=(("ANALOG BD", "both own the machine list's rows after PICKUP (the same chooser sites)"),), + doc="FM SYNTH in the machine list (FUNC + SRC, or SELECT MACHINE TYPE), or a FLEX track whose sample is named SYNTH*, plays a two-operator FM " "voice (STRT/LEN/RTRG/RTIM = ratio/index/feedback/decay); the DSP " "shapes and effects it as a sample. Its PLAYBACK page reads RATO/INDX/" "FDBK/DEC with icons and the title FM SYNTH; PTCH is semitones (-64..+63) " "and RATE is FINE (cents) on a synth track, 0c the moment a track becomes one. " "A FLEX or STATIC sample track with LEG MONO and GLIDE slides its pitch (2.8).", linked=( - Linked("poly", os.path.join(_HERE, "poly.s"), cpu="5475", dram=True), + Linked("poly", os.path.join(_HERE, "poly.s"), cpu="5475", dram=True, include=remix_include), + Linked("fmmachine", os.path.join(_HERE, "machine.s"), cpu="5475", dram=True, include=remix_include), ), # The kind table's FLEX entry is a 4-byte data pointer, not an # instruction: upstream octabam's SymbolRef (a stock u32 rewritten to a @@ -435,6 +575,9 @@ SymbolRef(KIND_TABLE_STATIC, STOCK_RENDERER, "poly", "sy_render", "kind table STATIC renderer -> sy_render too (2.8: a STATIC sample's PTCH slides " "with LEG MONO + GLIDE; no marker scan, the stock call otherwise)"), + SymbolRef(KIND_TABLE_FLEX_START, FLEX_START, "poly", "po_fmstart", + "kind table FLEX START callback -> po_fmstart (2.10): a track whose FM SYNTH is chosen in the " + "machine list starts its voice without a sample; every other FLEX track the stock callback"), ), detours=( Detour(LFO_DEPTH_HOOK, LFO_DEPTH_STOCK, "poly", "po_lfo3", @@ -446,6 +589,15 @@ Detour(LFO_PAGE_HOOK, LFO_PAGE_STOCK, "poly", "po_lfopage", "page resolver LFO descriptor: a synth track gets the VOIC/CHRD clone", kind="jmp", pad_to=8), + Detour(LFD_NAME_HOOK, LFD_NAME_STOCK, "poly", "po_lfdname", + "LFO SETUP PMTR formatter, PLAYBACK names: the page resolver's descriptor (the FM SYNTH page on a synth track) instead of the machine table's; the master track and every other machine stock", + kind="jmp", pad_to=8), + Detour(LFD_LFO_HOOK, LFD_LFO_STOCK, "poly", "po_lfdlfo", + "LFO SETUP PMTR formatter, LFO names: the VOIC / CHRD clone on a synth track (a stored SPD3 / DEP3 prints VOIC / CHRD); every other track stock", + kind="jmp", pad_to=8), + Detour(LFD_EDIT_HOOK, LFD_EDIT_STOCK, "poly", "po_lfdedit", + "LFO SETUP PMTR edit (audio tracks): stock's display-order walk, one shown destination a detent, stepping over SPD3 / DEP3 (VOIC / CHRD) on a synth track; the Part byte or the lock as stock stores it", + kind="jmp", pad_to=8), Detour(MIDI_MAP_HOOK, MIDI_MAP_STOCK, "poly", "po_mon", "MIDI IN note-on (the STANDARD map's chromatic block): a synth track's PTCH raw is note - 20 (semitones, 84 = 0), stored before the START, and the note is posted to the engine as a key of its own", kind="jmp"), @@ -455,9 +607,6 @@ Detour(REL_HOOK, REL_STOCK, "poly", "po_rel", "the frame builder's AMP-release consumer (mailbox 0x40, every note-off path): a synth voice takes the released flag -- plan B: the engine's own release starts (po_mono_env)", kind="jmp", pad_to=8), - Detour(STOP_HOOK, STOP_STOCK, "poly", "po_stop", - "the frame builder's STOP / restart word consumer (after the DSP all-off): every engine voice releases (plan B: the DSP's envelope ends nothing, so no note may stick)", - kind="jmp"), Detour(RETRIG_HOOK, RETRIG_STOCK, "poly", "po_retrig", "the frame builder's copy of the DSP command byte into the packer's nibble byte (every START form's funnel): a START posted on a synth track whose engine voice is on (S_ON) becomes the key's clean form 0x30 -- the CF START bit with sub-frame position 0 -- so the DSP does not crossfade its old voice under the engine's one continuous stream (BUILD 37: the +5.6 dB bump at a sequencer trig on a still-sounding note)", kind="jmp", pad_to=8), @@ -475,7 +624,7 @@ kind="jmp", pad_to=10), Detour(MIDI_TRIG_HOOK, MIDI_TRIG_STOCK, "poly", "po_mtrig", "MIDI IN with a trig held: a synth track's held trig gets its PTCH lock in semitones", - kind="jmp", pad_to=8), + kind="jmp", pad_to=10), # the whole 10-byte displaced span (the lea's extension word included) Detour(OCT_HOOK, OCT_STOCK, "poly", "po_octave", "FUNC + UP/DOWN (the trig-mode selector): with a trig held on a synth track in GRID RECORDING, any trig mode, " "the held steps' PTCH lock moves an octave instead (no trig held, a sample track, GRID RECORDING off: the selector)", @@ -502,8 +651,10 @@ "slot assigner (both windows): a track that becomes a synth track by this slot write -- FLEX with an FMSYNTH*/SYNTH* slot, " "not one before -- gets FINE 0c (RATE := 64 in the Part, its shadow and the live lane); a synth track already, or a sample track: untouched", kind="jmp", pad_to=8), - Detour(MACHWIN_HOOK, MACHWIN_STOCK, "poly", "po_machwin", - "machine window, the machine-only write (the slot equal): a track that becomes a synth track by the machine change gets FINE 0c", + Detour(MACHWIN_HOOK, MACHWIN_STOCK, "fmmachine", "fm_main_commit", + "machine window, the machine-only write (the slot equal): row 5 (FM SYNTH) is stored as FLEX with the Part's \"FM\", 1 (seeded " + "unless the track plays the FM voice already), any other row clears it (2.10, machine.s); then po_machwin: a track that " + "becomes a synth track by the machine change gets FINE 0c", kind="jmp", pad_to=8), Detour(LISTWIN_HOOK, LISTWIN_STOCK, "poly", "po_machlist", "sample-list window, the machine-only write: the same", @@ -513,6 +664,83 @@ "non-marker -> FMSYNTH*/SYNTH* makes every FLEX track holding that slot a synth track: FINE 0c for each (the new-project case: " "T1 = FLEX slot 1, the first marker loaded into slot 1, no machine or slot byte changes)", kind="jsr"), + Detour(UNLOAD_HOOK, UNLOAD_STOCK, "poly", "po_unload", + "file browser load callback: the slot unload before the select (2.12) -- the FLEX slot's marker state is kept for " + "po_loadsel (the unload clears the slot's record), then the stock unload", + kind="jsr"), + # ---- FM SYNTH in the machine list (2.10, machine.s; MACHWIN_HOOK above) ---- + Detour(FM_NAME_HOOK, FM_NAME_STOCK, "fmmachine", "fm_machine_name", + "the machine-name formatter: row 5 reads FM SYNTH (the machine window's sixth row)", + kind="jmp"), + Detour(FM_NAMES_HOOK, FM_NAMES_STOCK, "fmmachine", "fm_src_names", + "SRC SETUP's machine-name table: six rows, the stock five (copied at run time) and FM SYNTH", + kind="lea"), + Detour(FM_NAME_A_HOOK, FM_NAME_A_STOCK, "fmmachine", "fm_name_a", + "a track's machine name: FM SYNTH for a chosen track (FLEX with the Part's \"FM\", 1)", + kind="jmp"), + Detour(FM_SETUP_ROW_HOOK, FM_SETUP_ROW_STOCK, "fmmachine", "fm_setup_row", + "SRC SETUP's row highlight: a chosen track's row is FM SYNTH", + kind="jmp"), + Detour(FM_CHOOSER_ROW_HOOK, FM_CHOOSER_ROW_STOCK, "fmmachine", "fm_chooser_row", + "the machine window's row highlight: the same", + kind="jmp"), + Detour(FM_SETUP_OPEN_HOOK, FM_SETUP_OPEN_STOCK, "fmmachine", "fm_setup_open", + "SRC SETUP opens on FM SYNTH for a chosen track (its page: the descriptor table's slot 5, fm_tick)", + kind="jmp", pad_to=10), + Detour(FM_CHOOSER_OPEN_HOOK, FM_CHOOSER_OPEN_STOCK, "fmmachine", "fm_chooser_open", + "the machine window opens on FM SYNTH for a chosen track", + kind="jmp", pad_to=10), + Detour(FM_EDIT6_HOOK, FM_EDIT6_STOCK, "fmmachine", "fm_setup_edit6", + "SRC SETUP's editor addresses row 5's Part bytes as FLEX's", + kind="jmp", pad_to=8), + Detour(FM_DRAW6_HOOK, FM_DRAW6_STOCK, "fmmachine", "fm_setup_draw6", + "SRC SETUP's drawer: the same", + kind="jmp", pad_to=8), + Detour(FM_TICK_HOOK, FM_TICK_STOCK, "fmmachine", "fm_tick", + "the UI tick (its two calls replayed): SRC SETUP's six names refreshed and the PLAYBACK descriptor table's " + "spare slot 5 (0x400d5f4c) := the FM SYNTH page (page.s fm_descriptor)", + kind="jmp", pad_to=10), + Detour(FM_SRC_COMMIT_HOOK, FM_SRC_COMMIT_STOCK, "fmmachine", "fm_src_commit", + "SRC SETUP's machine-byte write: row 5 is stored as FLEX with the signature, any other row clears it", + kind="jmp"), + Detour(FM_SRC_COMMIT2_HOOK, FM_SRC_COMMIT2_STOCK, "fmmachine", "fm_src_commit2", + "the same on SRC SETUP's second path (po_machlist's detour returns to this site)", + kind="jmp"), + Detour(FM_VALIDATE_HOOK, FM_VALIDATE_STOCK, "fmmachine", "fm_validate", + "the Part validator: a chosen track's twelve FLEX bytes sit out the stock clamps (FLEX's PTCH 4..124; FM SYNTH's is " + "0..127) -- the stock defaults in their place while it runs, the FM bytes back after", + kind="jmp", pad_to=8), + Detour(FM_SOURCE_HOOK, FM_SOURCE_STOCK, "poly", "po_fmsource", + "the frame builder's second supplier call: a chosen track's supplier is sy_render (an empty FLEX voice gets stock's " + "silent one)", + kind="jmp", pad_to=8), + # ---- the machine list's row count and bounds (2.11, machine.s; 2.10 poked them to 6 rows) ---- + Detour(ML_WIN_COUNT_HOOK, ML_WIN_COUNT_STOCK, "fmmachine", "ml_win_count", + "the machine window's list init: 5 + the remix's machine-list rows (6 with FM SYNTH, 7 with SY DRUM), six visible", + kind="jmp", pad_to=8), + Detour(ML_SRC_COUNT_HOOK, ML_SRC_COUNT_STOCK, "fmmachine", "ml_src_count", + "SRC SETUP's machine selector: 5 + the machine-list rows, five visible", + kind="jmp", pad_to=8), + Detour(ML_NAME_BOUND_HOOK, ML_NAME_BOUND_STOCK, "fmmachine", "ml_name_bound", + "SRC SETUP's name lookup admits the machine-list rows", + kind="jmp"), + Detour(ML_DRAW_BOUND_HOOK, ML_DRAW_BOUND_STOCK, "fmmachine", "ml_draw_bound", + "the machine window's row drawer draws name(row + top) up to the last row: SELECT MACHINE TYPE scrolls like SRC SETUP", + kind="jmp", pad_to=12), + Detour(ML_HI_BOUND_HOOK, ML_HI_BOUND_STOCK, "fmmachine", "ml_hi_bound", + "the machine window highlights the machine-list rows", + kind="jmp"), + Detour(ML_CUR_BOUND_HOOK, ML_CUR_BOUND_STOCK, "fmmachine", "ml_cur_bound", + "the machine window applies a machine-list row under its cursor", + kind="jmp", pad_to=8), + ) + tuple( + Detour(a, g, "fmmachine", f"ml_clamp{i + 1}", + "the machine window's row clamp after a list move: 0 .. the last row (stock 0..5)", + kind="jmp", pad_to=8) + for i, (a, g) in enumerate(zip(ML_CLAMP_HOOKS, ML_CLAMP_GUARDS))) + ( ), + # 2.11: the six row-count / row-bound constants were pokes (5 -> 6) until 2.10; a poke is + # static, the number of rows is the remix's (FM SYNTH, + SY DRUM): six detours read it. + pokes=(), cf_patches=(), ) diff --git a/sdk/octabam/modules/synth/upstream/synth/page.s b/sdk/octabam/modules/synth/upstream/synth/page.s index 66fe71882..ef93a8045 100644 --- a/sdk/octabam/modules/synth/upstream/synth/page.s +++ b/sdk/octabam/modules/synth/upstream/synth/page.s @@ -3,11 +3,12 @@ | below (pg_over) carries absolute pointers to the formatters and widgets, so | the bytes depend on the address. | -| WHAT: when the current track's FLEX slot holds an FMSYNTH*- (or SYNTH*-) named sample, the -| PLAYBACK page presents the FM voice instead of a sample player -- the slot +| WHAT: when FM SYNTH is chosen in the track's machine list (2.10: FLEX with the Part's +| "FM", 1, machine.s) or the current track's FLEX slot holds an FMSYNTH*- (or SYNTH*-) +| named sample, the PLAYBACK page presents the FM voice instead of a sample player -- the slot | names read PTCH RATO INDX FINE FDBK DEC (four characters: the boxes are 19 px), the values format as the voice | understands them (RATIO from the ratio table "0.25".."16", INDEX and FDBK -| 0..127, DECAY as "HOLD" / the milliseconds alone / "1.1s" -- four characters +| 0..127, DECAY as the milliseconds alone / "1.1s" / "HOLD" at 127 -- four characters | at most, the width of the value field), the four synth slots show their | value all the time (Digitone style) and draw an icon where the sample dial | was: two operator boxes, the modulator feeding the carrier (RATIO), a @@ -69,16 +70,11 @@ | Formatters have the stock signature fmt(buf, value) -> sprintf. .text - .global fm_descriptor, pg_resolve -fm_descriptor: - moveal KIND_FLEX,%a0 - moveal %a0@(-4),%a0 - lea pg_over(%pc),%a1 - jmp %a0@ .set RESOLVER_RET, 0x40031ed6 .set FLEX_P, 0x400d31ae | the stock FLEX PLAYBACK descriptor (P form) .set KIND_FLEX, 0x400d6438 | the kind table's FLEX entry: sy_render's address (the manifest's SymbolRef) .set SLOT_OFF, 0x8f04b | Part: 0x8f04a + track*5 + machine (1 = FLEX) + .set SIG_OFF, 0x8edbc | Part: 0x8ed80 + 0x3c + track*30: the NEIGHBOR column, "FM", 1 (machine.s) .set SETTINGS_BASE, 0x100b14f0 .set SETTINGS_STRIDE, 0x448 .set STOCK_WIDGET, 0x400479b4 @@ -87,8 +83,20 @@ fm_descriptor: .set FMT_D, 0x400b465d | "%d" .set FMT_PLAIN, 0x4003c178 | the stock formatter fmt(buf, value) -> sprintf(buf, "%d", value) +| ---- the FM SYNTH machine's descriptor (machine.s, at the cave's first byte) --- +| fm_descriptor() -> d0 = the FM SYNTH PLAYBACK descriptor (po_pgdesc with this cave's +| list; built on first use). machine.s calls it at this fixed address: the UI tick +| publishes it as the machine table's sixth entry for SRC SETUP's FM SYNTH row (after +| Modwerk's dedicated chooser, MIT). d0/d1/a0/a1 clobbered (C scratch). +fm_descriptor: + moveal KIND_FLEX,%a0 | sy_render + moveal %a0@(-4),%a0 | po_pgdesc + lea pg_over(%pc),%a1 + jmp %a0@ + | ---- the resolver detour ------------------------------------------------------ -| 0x40031ece: `movel %a0@(0,%d0:l:4),%d0; bras 0x40031ed6` -> `jmp pg_resolve`. +| 0x40031ece: `movel %a0@(0,%d0:l:4),%d0; bras 0x40031ed6` -> `jmp pg_resolve` (the +| cave's +16 since 2.10: fm_descriptor is first; the manifest's PG_RESOLVE_AT). | d0 = machine type, a0 = the table 0x400d5f38, d3 = track, d1 = part index, | a1 = the bank blob; d2-d5 are restored by the resolver's epilogue, d1/a0/a1 | are C scratch, d6/d7/a2-a6 are left alone. @@ -99,18 +107,18 @@ pg_resolve: movel #6322,%d2 mulsl %d1,%d2 | part * 6322 addl %a1,%d2 | + the bank blob - moveal %d2,%a0 - movel %d3,%d4 - muluw #30,%d4 + moveal %d2,%a0 | FM SYNTH chosen in the machine list (machine.s): FLEX + movel %d3,%d4 | (the descriptor said so) with "FM", 1 in the track's + muluw #30,%d4 | NEIGHBOR column addal %d4,%a0 - addal #0x8edbc,%a0 + addal #SIG_OFF,%a0 mvzw %a0@,%d5 - cmpiw #0x464d,%d5 - bne 1f + cmpil #0x464d,%d5 + bne pg_unsigned mvzb %a0@(2),%d5 - cmpib #1,%d5 - beq fm_pg_clone -1: + cmpil #1,%d5 + beq pg_synth +pg_unsigned: movel %d3,%d4 lsll #2,%d4 addl %d3,%d4 | track * 5 @@ -155,7 +163,7 @@ pg_cmp: bne pg_r_done subql #1,%d4 bne pg_cmp -fm_pg_clone: +pg_synth: moveal KIND_FLEX,%a0 | the kind table's FLEX entry: poly.s's sy_render moveal %a0@(-4),%a0 | the long before it: po_pgdesc lea pg_over(%pc),%a1 | this cave's fields @@ -288,10 +296,12 @@ pg_fr_out: movel %sp@+,%d2 rts -pg_fmt_decay: | tau = 2 s * (raw/127)^2: "HOLD", "32", "286", "1.1s", "2.0s" +pg_fmt_decay: | tau = 2 s * (raw/127)^2: "0", "32", "286", "1.1s", "1.9s", 127 "HOLD" movel %d2,%sp@- movel %sp@(12),%d0 | raw - beq pg_fd_hold + moveq #127,%d1 | 127 (the last position): HOLD; 0 = the shortest, prints "0" + cmpl %d1,%d0 | (5 Oct 2026: HOLD moved from 0 to 127; the short + bccs pg_fd_hold | branches keep the cave at 1,812 B) movel %d0,%d1 mulul %d0,%d1 | raw^2 movel #2000,%d0 @@ -300,7 +310,7 @@ pg_fmt_decay: | tau = 2 s * (raw/127)^2: "HOLD", "32" movel #16129,%d1 divul %d1,%d0 | / 127^2, rounded: ms cmpil #1000,%d0 - bcc pg_fd_sec + bccs pg_fd_sec movel %d0,%sp@- pea FMT_D | "%d": the milliseconds alone -- the value field movel %sp@(16),%sp@- | fits four characters and "598ms" ran into the divider @@ -449,7 +459,8 @@ pg_ic_fdbk: | DECAY: the index envelope -- an instant rise at column 1, then pg_env's | exponential fall stretched over L = 2 + value * 13 / 127 columns to the -| floor (row 1 = I/16), columns 2..15; value 0 holds, a flat top +| floor (row 1 = I/16), columns 2..15; value 127 holds, a flat top (0 = the +| shortest fall, L = 2) pg_ic_decay: lea pg_img_base(%pc),%a0 bsr pg_copy @@ -457,9 +468,10 @@ pg_ic_decay: moveq #1,%d1 moveq #11,%d2 bsr pg_vline | the attack edge - movel #0x7fff,%d5 | value 0: i stays 0, h stays 11 - tstl %d6 - beq pg_dc_go + movel #0x7fff,%d5 | value 127 (HOLD): i stays 0, h stays 11 + moveq #127,%d0 + cmpl %d0,%d6 + bccs pg_dc_go movel %d6,%d5 moveq #13,%d0 mulul %d0,%d5 diff --git a/sdk/octabam/modules/synth/upstream/synth/poly.s b/sdk/octabam/modules/synth/upstream/synth/poly.s index ced9a7b69..c9e27f14a 100644 --- a/sdk/octabam/modules/synth/upstream/synth/poly.s +++ b/sdk/octabam/modules/synth/upstream/synth/poly.s @@ -1,8 +1,3 @@ -| Modwerk pending 0.1.2 (#273): OUTPUT_SHIFT applies 18.06 dB headroom to -| mono samples and EACH paraphonic product before summing. Envelope gains, -| equal-power voice ratios and crossfade state stay unchanged. Old comments -| below describe the author's unattenuated format; output now scales by 1/8. -| This prevents high-level source peaks reaching AMP; effects/mix may still clip. | SYNTH MACHINE -- the FM voice engine as a DRAM unit, phase 5: paraphonic | chords (24 Sep 2026). GNU as, -mcpu=5475. Linked by the build into the | platform runtime at the base of the arena reserve (docs/remixer/ @@ -64,12 +59,13 @@ | / HOLD INF / REL INF on a synth track (sy_ison) and each voice, mono or | paraphonic, runs the live lane's ATK / HOLD / REL with the DSP's measured | laws (po_mono_env, po_frame): ATK a linear ramp to full in 3.85 ms x -| 2^(v/8.53) (16 frames the floor, po_atk); HOLD a timer from the START, +| 2^(v/8.53) (16 frames the floor, po_atk; 2.10: at least one carrier +| period, S-shaped, po_alaw); HOLD a timer from the START, | po_hold128 x frames a step (127 = INF), the note ends by itself; REL | exponential, tau = 0.295 ms x 2^(v/8.53) floored at 1 ms (po_relk), 127 -| = INF: the voice sustains until stolen -- except after STOP (po_stop: -| S_ON bit 2), when INF takes the 1 ms floor so every voice reaches -| silence and the next START is cold. A start while the voice still +| = INF: the voice sustains until stolen or until STOP / a pattern +| change ends it at the stock voice kill (po_kill: silent, the next +| START is cold). A start while the voice still | sounds continues it phase-continuously from its level (the START rule); | * PITCH per voice: target = V_ROOT + (PTCH word - the word at the last | start), so a lock or slide or LFO on PTCH moves every voice by the @@ -86,8 +82,12 @@ | (c * g << 1: the high word is the sample) with a saturating clamp only | (plan B: the peak limiter is gone). A single note is 3.0 / 4.8 / 6.0 dB | below the mono voice at VOIC 2 / 3 / 4; a VOIC 4 chord's coincident -| peaks reach 2.0 FS and clamp. A stolen or chord-memory voice fades over -| 8 frames (state 3, T_GMAX / 8 a frame); a voice above the cap (a tail +| peaks reach 2.0 FS and clamp (2.10, 8 Oct 2026: both paths leave 6.02 dB +| of headroom, HEADROOM -- the mono voice peaks at half its old level and +| each voice is halved before the sum, so those peaks reach 1.0 FS, the +| relations above unchanged). A stolen or chord-memory voice fades over +| 8 frames (state 3, T_GMAX / 8 a frame; 2.10: at least one period of the +| voice, S-shaped); a voice above the cap (a tail | from a lower VOIC, the mono voice carried in) converges on it at that | rate; a voice is freed below gain 64 (-54 dB re the mono voice's full | scale) once po_fill has ramped it there; @@ -97,6 +97,15 @@ | it; VOIC 2..4 -> 1: sy_warm's po_fade fades the voices over 8 frames | under the mono voice (po_fade_frame steps them, po_fill_add sums them | onto its samples) and the mono voice starts cold; +| * A PITCH CHANGE ON A SOUNDING MONO NOTE (2.10): a START at another pitch +| (half a semitone or more from the sounding word) crossfades -- the old +| tone becomes a fading voice (po_carry; one period of the lower note, +| S-shaped) and the new note starts cold from phase 0 with its attack; the +| same pitch stays phase-continuous (the index ramp, po_erlen). A warm +| START's index envelope restarts at 1.0 and decays at DEC's rate as a +| cold one's; the ramp moves the index itself (the remaining distance +| over the remaining frames, L = po_erlen in all): DEC 0 never collapses +| the index within a frame; | The stock voice lifecycle is untouched: a new key restarts the DSP voice | (the AMP and filter envelopes run over the whole mix as for a sample), a | released last key posts the AMP release as stock. Musically: AMP ATK 0, @@ -138,105 +147,109 @@ .global sy_render, po_lfo3, po_lfo3b, po_lfopage .global po_mon, po_moff, po_mgate, po_mogate, po_mrec, po_mtrig .global po_ampstage, po_ampdraw1, po_ampdraw2, po_ampdraw3, po_ampedit, po_amplane - .global po_assign, po_machwin, po_machlist, po_loadsel, po_stop, po_kill + .global po_assign, po_machwin, po_machlist, po_loadsel, po_kill + .global po_unload | 2.12: the browser's first slot unload (the file a FLEX slot had) .global po_retrig + .global po_fmsource, po_fmstart | the FM SYNTH machine (machine.s): the source supplier and the START callback + .global po_is_synth, po_sig | ... and its tests (machine.s: the chooser's commits, the row lookups, the Part validator) + .global po_slot_marker | 2.12: ... and the marker test (machine.s: the machine change, ml_kindof) + .global po_lfdname, po_lfdedit, po_lfdlfo | 2.10: the LFO destination list (LFO SETUP's PMTR) + .include "remix.inc" | 2.11: the remix's flags (HAVE_SYDRUM: SY DRUM in the remix) and the shared + | equates (engine_abi.inc), written beside this unit by the build (manifest.py); + | with HAVE_SYDRUM 0 the unit assembles byte for byte as 2.10's + .if HAVE_SYDRUM + .global po_ident, po_is_fm, po_signed | 2.11: SY DRUM (sy-drum/sydrum.s) on this engine: the tests, + .global po_clock, po_voices, sy_state | and the state its engine reads + .endif .set VOICE_BASE, 0x800049d8 .set VOICE_STRIDE, 0xa8 .set CURSOR, 0x80001c80 - .set NIBBLE, 0x46104d0c .set STOCK_RENDER, 0x40004008 + .set FMSRC_RET, 0x4000d51c | po_fmsource's way back + .set FLEX_START, 0x4000f450 | the stock FLEX START callback (the kind table's second half, 0x400d6458) .set FP_PTR, 0x800062a8 | the packer's per-track DSP parameter record .set RS_PTR, 0x800062a4 | the packer's per-track render state + .set HEADROOM, 1 | 2.10: 6.02 dB of headroom (one arithmetic shift right) at the mono output and + | at each paraphonic voice's contribution -- the sidecar host's 0.5-per-voice + | law; VOL 0 is the reference, VOL up to about +6 stays clean (8 Oct 2026) .set SETTINGS_BASE, 0x100b14f0 | the sample settings records, 0x448 each, slots 0..135 .set SETTINGS_SPAN, 0x24640 | 136 * 0x448 .set SETTINGS_STRIDE, 0x448 - .set PITCH_TAB, 0x400aa294 | the stock 2^(x/12) curve, longs Q26 (index = PTCH word >> 5) - .set OUTPUT_SHIFT, 3 | 18.06 dB source headroom before stock AMP VOL, including chord/fade peaks - .set C4_INC, 25480119 | C4: 261.6256 / 44100 * 2^32 - .set ENV_ONE, 0x01000000 | the index envelope's 1.0 (Q24) .set ENV_FLOOR, 0x00100000 | it decays toward 1/16 - .set K_NUM, 3068384 | k = K_NUM / RTIM^2, Q20 per frame: tau = 2 s at 127 + .set K_NUM, 3068384 | k = K_NUM / RTIM^2, Q20 per frame: tau = 2 s * (RTIM/127)^2; 127 holds .set K_MAX, 0xfffff .set INDEX_SCALE, 2628 | 8 rad / 127 in cycles * 2^18 (offset = m_Q14 * I) .set FB_SCALE, 516 | 0.25 cycle / 127 * 2^16 - .set INC_MAX, 0x73000000 | a carrier above ~19.8 kHz (increment > 0.45 cycle a sample) is no note - | of this synth: the safety net resets such a voice (24 Sep 2026; raised - | from 8 kHz on 27 Sep 2026 for PTCH +63 and the octave shapes) .set GLIDE_AT, 0x100b14ed | the GLIDE byte in battery RAM (modules/quantizer/manifest.py GLIDE_AT; 0 = off) + .set LK_HELD, 0x460d171d | stock's chromatic key handler: the held key per track (key + 1; 0 = none) .set KEYS_AT, 0x400d2cb0 | modules/quantizer/keys.s: qz_pkey[8] bytes, qz_pmask[8] longs at +8, .set CLOCK_AT, KEYS_AT+40 | qz_clock at +40: where this unit publishes po_clock's address .set SEQ_STEP, 0x800065b2 | the sequencer's step (word) and tick (byte, counting down) ... .set SEQ_TICK, 0x800065b6 | ... the clock below counts their changes .set CV_HOLD, 13 | flat slot 13 = AMP page slot 1 (HOLD): a sequencer note's gate - .set CK_TICKS, 0 | po_clock: ticks seen (monotonic, no pattern wrap) - .set CK_TPS, 4 | ticks a step (the largest tick value seen + 1; 0 = not yet) - .set CK_FPS, 8 | frames a step (measured between step changes; 345 until then) - .set CK_FRAMES, 12 | frames seen .set CK_LAST, 16 | the last (step << 8 | tick) .set CK_PING, 20 | (unused) .set CK_STEP, 24 | the last step .set CK_FSTEP, 28 | frames at the last step change .set FPS_DEFAULT, 345 | 120 BPM, 1x: 60 / 120 / 4 s * 44100 / 16 .set SCALE_AT, 0x400d2ca8 | modules/quantizer/scale.s: `jmp qz_scale_mask` -- d0 := the scale's pitch-class mask, 0 = OFF - .set CURVALS, 0x80000810 | the frame builder's per-track current values, 72 B a track, locks applied - .set CV_STRIDE, 72 .set CV_CHRD, 11 | flat slot 11 = LFO page slot 5 (DEP3): the chord shape .set CV_VOIC, 8 | flat slot 8 = LFO page slot 2 (SPD3): the voice count, 1..4 .set CV_REL, 14 | flat slot 14 = AMP page slot 2 (REL) .set LFO_STATE, 0x80004858 | the LFO engine's per-track state, 8 B a track (a4 in its loop) .set LFO_P, 0x400d37f6 | the stock LFO page descriptor (P form), 0x192 bytes .set LFO_DESC_LEN, 0x192 - .set FLEX_P, 0x400d31ae | the stock FLEX PLAYBACK descriptor (P form), 0x192 bytes: - .set FLEX_DESC_LEN, 0x192 | the source of the page clone po_pgdesc builds for page.s .set RESOLVER_RET, 0x40031ed6 .set SLOT_OFF, 0x8f04b | Part: 0x8f04a + track*5 + machine (1 = FLEX) .set SLOT_COL, 0x8f04a | Part: the five slot bytes of a track, one a machine (the column the assigners index by machine) - .set PART_SHADOW, 0x1001614e | the bank's mirror in battery RAM: + part * 6322 + the Part offset = the shadow of a Part byte (0x100a5198 = the slot bytes', 0x100a4ef0 = the machine's, 0x100a51c6 = the AMP page-2's) .set CV_RATE, 3 | flat slot 3 = PLAYBACK slot D (RATE; FINE on a synth track) .set LISTWIN_MACH, 0x460d5c30 | the sample-list window's chosen machine (its apply path 0x4005a826) .set ASSIGN_RET, 0x400795c2 | the slot assigner 0x40079424: after its slot-byte write (po_assign) .set MACHWIN_RET, 0x4007981e | the machine window's apply path 0x400797cc: its machine-byte write (po_machwin) .set LISTWIN_RET, 0x4005a850 | the sample-list window's apply path: its machine read (po_machlist) .set LOADSEL_SPRINTF, 0x40013a08 | stock's sprintf: the browser's select 0x40022610 writes the chosen path into the slot's settings record with it (po_loadsel) - .set SPRINTF, 0x40013a08 .set SEMI, 0x500 | one semitone of the PTCH word (5 raw units << 8) .set SHAPE_END, -128 | ends a shape's offsets ("----" only: every other shape has four notes) - .set PING_AT, 0x800000e0 | the frame builder's ping (the copier 0x4000caf4 reads it: the record set being built this frame) .set DSP_REC, 0x80000110 | the DSP voice records: + (ping << 9) + 64 * track, halfwords 0/1/2 = AMP ATK / HOLD / REL (value << 8) .set CV_ATK, 12 | flat slot 12 = AMP page slot 0 (ATK) - .set CUT_SHIFT, 3 | a stolen voice fades over 8 frames (2.9 ms): T_GMAX / 8 a frame (plan B) - .set GLOBAL_WORD, 0x46c80350 | the sequencer's STOP / restart word the frame builder turns into the DSP all-off (po_stop) + .set CUT_SHIFT, 3 | a stolen voice fades over 8 frames (2.9 ms): T_GMAX / 8 a frame (plan B) -- 2.10: at least one + | period of the voice, S-shaped (po_alaw from po_fr_cut / po_fade_frame) + .set GLOBAL_WORD, 0x46c80350 | the METRONOME's click word, not a STOP word (2.12): the beat code writes 3 (the bar's first + | beat, the accent) or 1 (the other beats) while the sequencer plays, every writer behind + | METRONOME_ENABLED (0x80000060); the frame builder turns it into the DSP-wide click command + | 0x46104d14 (0x10 | .., 0x30 | .. the accent) at 0x4000b292..0x4000b2c2 and clears it at + | 0x4000b2c8. 2.9..2.11 hooked that clear (po_stop) as a STOP and released every engine voice + | at every click; nothing of ours reads or hooks it since 2.12 (STOP is po_kill's) .set KILL_RET, 0x40006862 | the stock VOICE KILL 0x40006820: after its CF voice-byte clear (po_kill) | ---- the per-track record, 128 bytes: the mono voice (0..43, synth.s's layout) and the poly frame - .set ST_STRIDE, 128 +| (ST_STRIDE, S_GAIN +24, S_ON +36, S_GPREV +38 are in engine_abi.inc: SY DRUM shares them. S_ON +| bit 0: the playing voice is a synth; bit 1: a release (the AMP release, mailbox 0x40) reached the +| frame builder since that START (po_rel; BUILD 27) -- tst.b still means "a synth plays": bits 1 / 2 +| are set only while bit 0 is; bit 2: STOPPED (BUILD 32's po_stop, removed in 2.12: nothing sets it +| now, so its two readers -- po_mono_env's and po_frame's REL INF floor -- never fire). S_GPREV, a word: the gain the last rendered call ended at -- sy_loop ramps S_GPREV -> +| S_GAIN across the call; every start clears it with the phases, BUILD 26.) .set S_PHC, 0 | carrier phase, Q32 cycles .set S_PHM, 4 | modulator phase .set S_ENV, 8 | index envelope, Q24 .set S_INC, 12 | carrier increment per sample (the true pitch) .set S_INCM, 16 | modulator increment .set S_IEFF, 20 | index * envelope, the per-sample multiplier -- the RUNNING value: sy_loop steps it by S_ISTEP a sample (BUILD 38) - .set S_GAIN, 24 | start ramp, Q15 .set S_FB, 28 | feedback multiplier .set S_LASTM, 32 | the modulator's last sample, Q14 - .set S_ON, 36 | bit 0: the playing voice is a synth; bit 1: a release (the AMP release, mailbox 0x40) - | reached the frame builder since that START (po_rel; BUILD 27) -- tst.b still - | means "a synth plays": bits 1 / 2 are set only while bit 0 is; - | bit 2: STOPPED (po_stop, BUILD 32): a release under REL INF takes the 1 ms floor - .set S_GPREV, 38 | word: the gain the last rendered call ended at (sy_loop ramps S_GPREV -> S_GAIN across the call; every start clears it with the phases -- BUILD 26) .set S_CUR, 40 | the slewed PTCH word, Q12 (GLIDE) .set T_REF, 44 | paraphonic: the PTCH word at the last voice start .set T_LAST, 48 | paraphonic: the PTCH word seen last frame .set T_MASK, 52 | paraphonic: the held-key mask seen last frame - .set T_DK, 56 | paraphonic: the index decay k this frame (0 = hold) + .set T_DK, 56 | paraphonic: the index decay k this frame (0 = hold, RTIM 127) .set T_RK, 60 | paraphonic: the release k this frame (Q16) .set T_RATIO, 64 | paraphonic: the ratio this frame (Q8) .set T_I, 68 | paraphonic: the index I this frame .set T_W, 72 | paraphonic: this frame's PTCH word .set T_SCALE, 76 | paraphonic: the scale mask at the last start (0 = OFF) .set T_POLY, 80 | byte: the note that started last is paraphonic (VOIC 2..4 then) - .set S_ERAMP, 81 | byte: frames left of the mono voice's index-envelope ramp after a warm START (BUILD 38: sy_env_ramp; 0 = none) + .set S_ERAMP, 81 | byte: frames left of the mono voice's index ramp after a warm START (BUILD 38; 2.10: the index ramps, sy_il_*; 0 = none) .set RT_CALLS, 0 | po_rtlog (po_clock + 32, peekable): passes of the frame builder's DSP command-byte copy (po_retrig) - .set RT_FIXED, 4 | ... START bytes rewritten to the clean form 0x30 + .set RT_FIXED, 4 | ... START bytes rewritten to the clean form 0x30 (2.10: and trigless bytes' n cleared) .set RT_HEAD, 8 | ... the ring's head .set RT_RING, 16 | ... 32 entries x 8 B: {CK_FRAMES, track | byte posted << 8 | byte now << 16 | S_ON << 24} .set RT_SIZE, 16 + 32 * 8 @@ -249,19 +262,17 @@ .set T_LASTK, 120 | paraphonic: the frame of the last live panel-key start (a key within KR_WIN of it is a chord press) .set T_POS, 92 | the marker's sample positions (voice +64..+83) before the stock call, restored after it: a synth voice never reaches the marker's end .set V_POS, 64 | the voice struct's sample position fields: +64 window base, +68 position, +72/+76 the stream's, +80 frames left in the fetched chunk - .set PART_PTR, 0x46c82456 | the bank blob; the Part = blob + part index * 6322 - .set PART_IDX, 0x100b14cf .set UI_TRACK, 0x100b14cc .set LFO_PAGE_OFF, 0x8ee9a | the Part's LFO page bytes, 24 a track: +2 = VOIC, +5 = CHRD | ---- a voice record, 64 bytes (the first 36 match the mono voice's fields) - .set V_STRIDE, 64 +| (V_STRIDE, V_GAIN +24 -- Q15, at most T_GMAX -- and V_GPREV +60 -- a word, the gain the last frame +| ended at, po_fill ramps V_GPREV -> V_GAIN; 0 = silent -- are in engine_abi.inc) .set V_PHC, 0 .set V_PHM, 4 .set V_ENV, 8 .set V_INC, 12 .set V_INCM, 16 .set V_IEFF, 20 | the running I * E (po_fi_loop steps it by V_ISTEP a sample; BUILD 38) - .set V_GAIN, 24 | Q15 (the mono voice's S_GAIN format), at most T_GMAX .set V_FB, 28 .set V_LASTM, 32 .set V_STATE, 36 | 0 free, 1 sounding, 2 releasing, 3 fading (stolen / chord-memory cut: T_GMAX / 8 a frame, plan B) @@ -272,8 +283,7 @@ .set V_AGE, 48 | the allocation stamp .set V_HOLD, 52 | a sequencer note: frames left before it releases (0 = no gate) .set V_FRAME, 56 | the frame (po_clock CK_FRAMES) the voice was allocated at (26 Sep 2026) - .set V_GPREV, 60 | word: the gain the last rendered frame ended at (po_fill ramps V_GPREV -> V_GAIN across a frame; 0 = the voice has been silent: a start resets the phases; a long until BUILD 38) - .set V_ERAMP, 62 | word: frames left of the voice's index-envelope ramp to ENV_ONE after a warm START (po_frame; BUILD 38; 0 = none) + .set V_ERAMP, 62 | word: frames left of the voice's index ramp after a warm START (po_frame; BUILD 38; 2.10: the index ramps, po_fr_il_*; 0 = none) | ---- the chord record (po_keyrec, 26 Sep 2026; 32 bytes a track since the rolling window) -- .set CR_STRIDE, 32 .set CR_STEP, 0 | the step the chord's first key was recorded on @@ -316,11 +326,6 @@ | ramps the index across the frame; +126 / +127 were S_HOLD / S_KEYED, BUILD 27/28's record, dead since plan B) .set T_MLEG, 37 | MIDI IN: the last note-on took the legato path (po_mrec records it trigless; a byte -- the byte after S_ON, free: S_ON is a byte, S_GPREV the word at +38; it was +126 until BUILD 25, aliasing the then S_LASTF word) | ---- the FM SYNTH page's knob handler (po_knob, 30 Sep 2026) ------------------- - .set FUNC_HELD, 0x46c7dd26 | nonzero while [FUNCTION] is down (the stock handlers' tst.l) - .set FUNC_ENC_OFF, 0x800000a0 | PERSONALIZE: DISABLE FUNCTION + ENCODER (the stock handlers' gate) - .set KEY_HELD, 0x46c7d8de+0x10 | + code * 0x18: nonzero while that key is held (PANEL.md) - .set PUSH_CODE, 0x38 | the encoder push switches: key codes 0x38..0x3d = A..F - .set KN_SLOTS, 0x12a | the descriptor's six knob handler pointers .set KN_RATIO, 1 | slot B = RATO .set KN_RATIO_N, 31 | the ratio table's last position .set KN_SUBUNITY, 3 | positions below it: 0.25 0.5 0.75 @@ -358,11 +363,19 @@ sy_render: lea (%a0,%d2.l),%a0 btst #4,(%a0) | a voice starts this frame: resolve the marker beq sy_ison + .if HAVE_SYDRUM + lea sy1_leg_pending:l,%a0 | (SY DRUM) a real START supersedes any queued mono key recharge + clr.b (%a0,%d2.l) + .endif bsr sy_machine | d0 := the track's machine (the Part's byte; clobbers d0, d1, a0) subq.l #1,%d0 bne sy_no | not FLEX (a STATIC track, kind 0, comes here since 2.8): a sample, no marker scan - bsr po_is_synth + bsr po_signed | FM SYNTH chosen in the machine list (machine.s): a synth, no marker scan + .if HAVE_SYDRUM + bne sy_listed | (2.11: or SY DRUM -- d0 = the row's engine kind) + .else bne sy_cold + .endif move.l #VOICE_STRIDE,%d3 muls.l %d2,%d3 lea VOICE_BASE,%a0 @@ -398,19 +411,44 @@ sy_cmp: bne sy_no subq.l #1,%d3 bne sy_cmp + .if HAVE_SYDRUM +| ---- (2.11) the engine of this START: sy1_kind[track] (sy-drum/sydrum.s) 0 FM, 1 SY DRUM -- +| the development line's sy_kind_found. A track whose engine changes starts cold (the other engine's +| level, its voices freed); a SY DRUM START runs that engine's trigger (sd_trigger: its LFO's +| outputs brought up to this frame, both envelopes recharged, warm keeps phase and filter). + moveq #1,%d0 | an FM marker: FM +sy_listed: | d0 = the engine kind of the machine-list row (1 FM, 2 SY DRUM) + subq.l #1,%d0 + move.l %d0,%d5 | the engine: 0 FM, 1 SY DRUM + lea sy1_kind:l,%a0 + cmp.b (%a0,%d2.l),%d5 + beq sy_kind_same + clr.l S_GAIN(%a3) | a different engine starts cold + clr.w S_GPREV(%a3) + bsr po_free + lea sy1_kind:l,%a0 + move.b %d5,(%a0,%d2.l) +sy_kind_same: + tst.l %d5 + beq sy_cold + jsr sd_trigger:l | SY DRUM's START (a3 = the track record; every register kept) + .endif sy_cold: | a synth starts -- THE START RULE (plan B, 28 Sep 2026): the engine owns move.l #CV_STRIDE,%d1 | the AMP envelope (the DSP sees ATK 0 / HOLD INF / REL INF: sy_ison), so muls.l %d2,%d1 | the only question is whether THIS voice still sounds. S_GPREV != 0 (the lea CURVALS,%a0 | gain the last call ended at): WARM -- the SAME oscillator continues mvz.b CV_HOLD(%a0,%d1.l),%d1 | phase-continuously at its current level, the pitch changes, the attack | re-runs from that level (an analog mono synth's retrigger), and the index - | envelope RAMPS from its level to ENV_ONE over 16 frames (BUILD 38). S_GPREV + | envelope restarts at 1.0 and the INDEX ramps to I * E over the ramp's + | frames (BUILD 38's ramp; 2.10: sy_il_*, never a one-frame collapse). S_GPREV | == 0: COLD -- both operators at phase 0, the gain ramps from 0 (the ATK law, | the 16-frame ramp its floor), the index envelope restarts at once (from | silence: inaudible). BUILD 27/28's four-condition rule (S_HOLD, S_KEYED, | the released flag) is gone: the DSP no longer restarts or cmpi.l #127,%d1 | releases anything. The HOLD the live lane holds now (the lock, else the beq sy_cold_nohold | Part's) arms the mono HOLD timer: 127 = INF (none), else po_hold128 steps + bsr po_livekey | (2.10) a CHROMATIC key still held: no timer -- the key is the gate + bne sy_cold_nohold | (its release posts the AMP release: po_rel) lea po_hold128(%pc),%a0 | in 1/128 x frames a step (the DSP's own timer runs from the START at that mvz.w (%a0,%d1.l*2),%d1 | length, stage 1: HOLD 32 = 282 ms at 120 BPM, a key held or not), and its lea po_clock(%pc),%a0 | end starts the release (po_mono_env). (S_HOLD / S_KEYED went with BUILD 38: S_ISTEP has +126.) @@ -424,7 +462,16 @@ sy_cold_nohold: sy_cold_hold: move.l %d1,S_HTIM(%a3) tst.w S_GPREV(%a3) - bne sy_warm_env | sounding: continuous + beq sy_cold1 | silent: cold + .if HAVE_SYDRUM + lea sy1_kind:l,%a0 | (2.11) SY DRUM: no crossfade -- its START recharged the envelopes + tst.b (%a0,%d2.l) | of the same voice (sd_trigger), sounding or not, as on the development line + bne sy_warm_env + .endif + bsr po_xfq | (2.10) sounding: a mono note at ANOTHER pitch crossfades -- the old tone + beq sy_warm_env | fades as a voice (po_carry, state 3: one period, S-shaped, po_fade_frame) + bsr po_carry | and the new note starts cold from phase 0 with its attack; the same + | pitch (within half a semitone) or a paraphonic note: continuous sy_cold1: clr.l S_PHC(%a3) | silent: phase 0, the gain from 0 clr.l S_PHM(%a3) @@ -437,10 +484,20 @@ sy_cold1: move.l %d1,S_ENV(%a3) bra sy_warm sy_warm_env: - move.b #16,S_ERAMP(%a3) | warm: the index envelope ramps from its level to ENV_ONE over 16 frames + move.b #255,S_ERAMP(%a3) | warm: the index (I * E) ramps from its level over max(16, one period) frames (255 = fresh: po_erlen) + move.l #ENV_ONE,%d1 | (2.10) and the envelope restarts as a cold note's: it decays from the + move.l %d1,S_ENV(%a3) | START at DEC's rate, the ramp only limits how fast the index follows it sy_warm: | (sy_env_ramp, 5.8 ms), the carrier and the level continuous (BUILD 38) move.l RS_PTR,%a0 clr.l 4(%a0) | the retrig count the packer just latched: no stock retrigs + .if HAVE_SYDRUM + lea sy1_kind:l,%a0 | (2.11) SY DRUM is one voice a track: VOIC is not its + tst.b (%a0,%d2.l) + beq sy_warm_voic + clr.b T_POLY(%a3) + bra sy_warm_mono +sy_warm_voic: + .endif move.l #CV_STRIDE,%d1 | VOIC (the current value, locks applied) 2..4: this note muls.l %d2,%d1 | is paraphonic; 1, or anything outside 1..4: the mono voice lea CURVALS,%a0 @@ -450,6 +507,9 @@ sy_warm: | (sy_env_ramp, 5.8 ms), the carrier an sls %d1 move.b %d1,T_POLY(%a3) bne sy_topoly + .if HAVE_SYDRUM +sy_warm_mono: + .endif bsr po_fade | a mono note: whatever paraphonic voices the track still had (VOIC 2..4 -> 1 bsr po_mring_reset | across a sounding note) FADE over 8 frames under the mono voice (state 3: lea KEYS_AT,%a0 | po_fade_frame steps them, po_fill_add sums them; BUILD 32 -- po_free cut them) @@ -469,6 +529,10 @@ sy_synth: bra sy_set sy_no: bsr po_free | not a synth: nothing of ours may sound on this track + .if HAVE_SYDRUM + lea sy1_kind:l,%a0 | (2.11) a sample: the engine kind is FM's 0 again + clr.b (%a0,%d2.l) + .endif clr.w S_GPREV(%a3) | (the mono voice is silent too) lea KEYS_AT,%a0 | the identity the quantizer posted for this START (a CHROMATIC key on clr.b (%a0,%d2.l) | any track since BUILD 28) is consumed: nothing stale for a later synth START @@ -542,37 +606,39 @@ sy_sample: bsr sy_slew | d6 := the word slewed toward it (clobbers d0, d1, d3, d7, a0) move.w %d6,(%a4) | the stock rate arithmetic reads the slewed word sy_call: - bsr fm_is_native - beq fm_stock_call - move.l 64(%sp),%d0 - sub.l 60(%sp),%d0 - move.l %d0,(%a2) | source count - move.l 60(%sp),4(%a2) | source ring write position + bsr po_signed | FM SYNTH chosen in the machine list: no sample, so no stock call -- + beq sy_stock | the source header stock would write (count, ring position, rate 1.0, + move.l 64(%sp),%d0 | read position) and silent pairs for the engine to overwrite + sub.l 60(%sp),%d0 | (Modwerk's sample-free transport, MIT) + move.l %d0,(%a2) | source count + move.l 60(%sp),4(%a2) | source ring write position move.l #0x04000000,%d1 - move.l %d1,8(%a2) | source rate 1.0, Q26 + move.l %d1,8(%a2) | source rate 1.0, Q26 move.l 60(%sp),%d1 - moveq #26,%d3 - lsl.l %d3,%d1 - move.l %d1,12(%a2) | source read position, Q26 + lsl.l #8,%d1 + lsl.l #8,%d1 + lsl.l #8,%d1 + lsl.l #2,%d1 | (d3 kept for the code after the call) + move.l %d1,12(%a2) | source read position, Q26 lea 16(%a2),%a0 add.l %d0,%d0 - beq fm_empty_source -fm_clear_source: + beq sy_nat_end +sy_nat_clr: clr.l (%a0)+ subq.l #1,%d0 - bne fm_clear_source -fm_empty_source: + bne sy_nat_clr +sy_nat_end: move.l %a0,CURSOR moveq #0,%d0 - bra fm_call_done -fm_stock_call: + bra sy_called +sy_stock: move.l 64(%sp),-(%sp) | end move.l 64(%sp),-(%sp) | start move.l 64(%sp),-(%sp) | ping move.l 64(%sp),-(%sp) | track jsr STOCK_RENDER lea 16(%sp),%sp -fm_call_done: +sy_called: move.l %d0,44(%sp) | the stock return value, handed back tst.l %d4 beq sy_check @@ -614,6 +680,17 @@ sy_machine: | ---- the mono voice's frame: the rate, as the stock renderer computes it ----- sy_mono_frame: bsr po_fade_frame | paraphonic voices fading under the mono voice: their envelope (BUILD 32) + .if HAVE_SYDRUM + lea sy1_kind:l,%a0 | (2.11) SY DRUM's frame: a posted LEG MONO key's recharge, the + tst.b (%a0,%d2.l) | engine's controls from the record (d6 = the slewed word), then + beq sy_mono_fm_frame | the AMP envelope the engine owns, as FM's + jsr sy1_mono_key_frame:l + jsr sy1_mono_frame:l + move.l 52(%sp),%d2 | track + bsr po_mono_env + bra sy_check +sy_mono_fm_frame: + .endif bsr po_rate_fold | d0 = the increment for the word d6, any octave (clobbers d0/d1/d3/d4/d7/a0) cmpi.l #INC_MAX,%d0 | safety net: an impossible pitch (a word off the curve's bls sy_mono_inc | table, a stale record) silences the voice instead of @@ -637,19 +714,16 @@ sy_mono_inc: mulu.l %d0,%d1 lsr.l #8,%d1 move.l %d1,S_FB(%a3) - mvz.b S_ERAMP(%a3),%d1 | a warm START's ramp (BUILD 38): the remaining distance to ENV_ONE over - beq sy_env_k | the remaining frames -- linear, ENV_ONE exactly at the last; the decay - move.l #ENV_ONE,%d0 | waits for it - sub.l S_ENV(%a3),%d0 - divs.l %d1,%d0 - add.l %d0,S_ENV(%a3) - subq.l #1,%d1 - move.b %d1,S_ERAMP(%a3) - bra sy_envdone -sy_env_k: +sy_env_k: | (2.10: a warm START's envelope decays from ENV_ONE as a cold one's; sy_il_* ramps the index) mvz.w 10(%a4),%d1 | RTIM -> the index envelope lsr.l #8,%d1 - beq sy_nodecay + moveq #127,%d0 | RTIM 127 (the last position): the index holds; + cmp.l %d0,%d1 | 0 = the shortest -- read as 1, whose k is the cap + bcc sy_nodecay | K_MAX (5 Oct 2026: HOLD moved from 0 to 127; + tst.l %d1 | 1..126 unchanged) + bne.s sy_env0 + moveq #1,%d1 +sy_env0: move.l %d1,%d2 mulu.l %d2,%d1 | raw^2 move.l #K_NUM,%d0 @@ -668,7 +742,7 @@ sy_env1: sub.l %d1,S_ENV(%a3) bra sy_envdone sy_nodecay: - move.l #ENV_ONE,%d1 | RTIM 0: the index holds + move.l #ENV_ONE,%d1 | RTIM 127: the index holds move.l %d1,S_ENV(%a3) sy_envdone: mvz.w 4(%a4),%d1 | LEN -> index @@ -682,6 +756,21 @@ sy_envdone: lsr.l #8,%d1 lsr.l #4,%d1 | I * E: this call's target sub.l S_IEFF(%a3),%d1 | ... minus the running value: sy_loop ramps S_IEFF there across the + mvz.b S_ERAMP(%a3),%d0 | (2.10) a warm START's ramp: the index moves by the remaining distance + beq sy_il_done | over the remaining frames, L = max(16, one carrier period) frames in + move.l %d1,%d2 | all (po_erlen) -- linear toward a target that decays from the START, + cmpi.l #255,%d0 | so the climb to a fresh note's index and, at a short DEC, the fall + bne sy_il_left | that follows take the whole ramp: never a one-frame collapse (DEC 0: + move.l %d0,%d1 | the envelope is at the floor from the START, the index stays where + move.l S_INC(%a3),%d0 | it was) + bsr po_erlen | d1 = L (clobbers d0) + move.l %d1,%d0 | fresh: L frames left +sy_il_left: + move.l %d2,%d1 + divs.l %d0,%d1 | this frame's move: (target - running) / frames left + subq.l #1,%d0 + move.b %d0,S_ERAMP(%a3) +sy_il_done: moveq #16,%d2 | frame, (target - running) / 16 a sample (BUILD 38: no step at a frame divs.l %d2,%d1 | edge when the index moves -- the ramp, the decay, a knob) move.w %d1,S_ISTEP(%a3) @@ -695,8 +784,8 @@ sy_envdone: | bytes (CURVALS + 72 t + 12 / 13 / 14: locks, scenes and LFOs applied by the frame | builder before the render loop) with the DSP's own laws as measured (stage 1): | RELEASING (S_ON bit 1: po_rel for every note-off path -- the panel key's -| release, MIDI note-off, the sequencer's -- or the HOLD timer below, or -| po_stop): gain -= gain * k, k = po_relk[REL] (Q16 a frame: tau = 0.295 ms x +| release, MIDI note-off, the sequencer's -- or the HOLD timer below): +| gain -= gain * k, k = po_relk[REL] (Q16 a frame: tau = 0.295 ms x | 2^(REL / 8.53), at least 1 ms -- Tim's ~2 ms minimum fade even at REL 0; | 127 = INF, holds); a step below 1 still makes progress; at or under 64 | (-54 dB) the voice is silent (0; its next START is cold). @@ -706,7 +795,9 @@ sy_envdone: | SOUNDING: gain += po_atk[ATK] (Q15 a frame: a LINEAR ramp to full in 3.85 ms x | 2^(ATK / 8.53), the 16-frame ramp its floor, the step at least 1), capped | at full -- from wherever the level is (a warm START re-attacks from its -| current level; a cold one from 0). +| current level; a cold one from 0). 2.10: the step goes through po_alaw -- +| no faster than one carrier period, S-shaped at the same total time (linear +| for a step under 32) -- and a voice at full skips it (two instructions). | Clobbers d0, d1, a0. po_mono_env: move.l S_GAIN(%a3),%d1 @@ -724,9 +815,24 @@ po_mono_env: bset #1,S_ON(%a3) | the hold ran out: release bra po_me_rel po_me_atk: + cmpi.l #32768,%d1 | at full: the attack is over, nothing to pay (po_alaw is for attacks only) + bge po_me_store mvz.b CV_ATK(%a0),%d0 lea po_atk(%pc),%a0 mvz.w (%a0,%d0.l*2),%d0 + .if HAVE_SYDRUM + lea sy1_kind:l,%a0 | (2.11) the onset law is the FM voice's: a SY DRUM track keeps the + tst.b (%a0,%d2.l) | linear law (S_INC is not its pitch), as on the development line + bne po_me_add + .endif + movea.l S_INC(%a3),%a0 | the onset law (po_alaw): no faster than one carrier period, S-shaped + move.l %a1,-(%sp) + movea.l #32768,%a1 + bsr po_alaw + movea.l (%sp)+,%a1 + .if HAVE_SYDRUM +po_me_add: + .endif add.l %d0,%d1 cmpi.l #32768,%d1 ble po_me_store @@ -737,8 +843,8 @@ po_me_rel: lea po_relk(%pc),%a0 mvz.w (%a0,%d0.l*2),%d0 | k, Q16 (0 = INF) bne po_me_rel0 - btst #2,S_ON(%a3) | INF holds -- unless STOPPED (po_stop): the floor, tau 1 ms, so the - beq po_me_store | voice reaches silence and the next START is cold (BUILD 32) + btst #2,S_ON(%a3) | INF holds -- unless STOPPED (BUILD 32): the floor, tau 1 ms. Since 2.12 + beq po_me_store | nothing sets bit 2 (po_stop removed): INF always holds here mvz.w (%a0),%d0 | po_relk[0] po_me_rel0: move.l %d1,%a0 @@ -758,6 +864,165 @@ po_me_store: move.l %d1,S_GAIN(%a3) rts +| ---- po_alaw: the attack's step this frame (2.10, 6 Oct 2026: the low-note onset click) ---- +| d0 = the ATK law's linear step (Q15 of full, a frame), d1 = the level now, a0 = the +| voice's carrier increment, a1 = full (32768 mono, T_GMAX paraphonic) -> d0 = the step. +| (1) no faster than ONE CARRIER PERIOD: the 16-frame floor (5.8 ms) is a fraction of a +| cycle below ~C3, and the ramp's splatter (100 Hz .. 1 kHz) is louder than a dark low +| tone's own content (RATO 0.25: Tim's click); one period's step is inc >> 13 (Q15 a +| frame for 32768), scaled to full. (7 Oct 2026) A period of 16..32 frames (~F2..F3) gets +| 2p - 16 frames (at most 32): C3 26 instead of 21; nothing changes from F3 up or below F2. +| (2) S-SHAPED: step' * (1/4 + 4 r (1 - r)), r = level / full, step' = 1.28 step (the same total time) -- the linear ramp's corner at full (a +| slope step at an arbitrary phase) shrinks to a quarter. Preserves all but d0. +po_alaw: + lea -12(%sp),%sp + movem.l %d1-%d3,(%sp) + move.l %a0,%d2 + moveq #13,%d3 + lsr.l %d3,%d2 | one carrier period's step for full 32768 + cmpi.l #128,%d2 + bcc po_al_p + move.l #128,%d2 | (2.10) at most 256 frames (93 ms; a carrier under ~11 Hz, or none) +po_al_p: + cmpi.l #4096,%d2 + bcc po_al_s | the 8-frame fade / 16-frame attack floor is the slower: no change + cmpi.l #2048,%d2 + bcc po_al_sc | (2.10, 7 Oct) a period of 16 frames or less: one period + move.l #32768,%d3 + divu.l %d2,%d3 | p, frames + cmpi.l #32,%d3 + bcc po_al_sc | p >= 32 (below ~F2): one period + add.l %d3,%d3 | 16 < p < 32 (~F2..F3): max(p, min(2p - 16, 32)) frames -- C3's + subi.l #16,%d3 | 21-frame attack ended on a corner that splattered (+8.2 dB at INDX 40); 26 frames + cmpi.l #32,%d3 + bls po_al_f + moveq #32,%d3 +po_al_f: + move.l #32768,%d2 + divu.l %d3,%d2 | the longer floor's step +po_al_sc: + move.l %a1,%d3 + mulu.l %d3,%d2 + moveq #15,%d3 + lsr.l %d3,%d2 + addq.l #1,%d2 | ... scaled to full + cmp.l %d0,%d2 + bcc po_al_s + move.l %d2,%d0 +po_al_s: + moveq #32,%d2 + cmp.l %d2,%d0 + bcs po_al_out | (2.10) a step under 32 (a slow ATK, past ~370 ms): linear -- the S-curve's + move.l %d0,%d2 | truncations there stretched ATK 64 by 11 %, 87 by 2.1x; its corner is inaudible + lsr.l #2,%d2 + add.l %d2,%d0 | step * 1.25 + lsr.l #3,%d2 + add.l %d2,%d0 | + step / 32: 1.28 + moveq #15,%d3 + lsl.l %d3,%d1 + move.l %a1,%d3 + divu.l %d3,%d1 | r, Q15 + move.l #32768,%d3 + sub.l %d1,%d3 + bpl po_al_q + moveq #0,%d3 +po_al_q: + mulu.l %d1,%d3 + moveq #15,%d1 + lsr.l %d1,%d3 | r (1 - r), Q15: at most 8192 + mulu.l %d0,%d3 + lsr.l #8,%d3 + lsr.l #5,%d3 | step' * 4 r (1 - r) + lsr.l #2,%d0 + add.l %d3,%d0 | + step' / 4 + bne po_al_out + moveq #1,%d0 +po_al_out: + movem.l (%sp),%d1-%d3 + lea 12(%sp),%sp + rts + +| ---- po_livekey (2.10): Z clear when this START (track d2) is a CHROMATIC key's that is still +| held: the identity the quantizer posted (qz_pkey[t], 1..25 = a panel key's index + 1; 0 = the +| sequencer, 0x80 | note = MIDI) and either that key's bit in the held mask (qz_pmask[t]: the +| quantizer keeps it for a paraphonic track) or stock's one held key (HELD[t], key + 1: a mono +| track's, which never enters the mask). A key let go before its START reached the engine, a +| sequencer trig, a MIDI note: Z set (the HOLD timer as before). Clobbers d0, a0. +po_livekey: + move.l %d1,-(%sp) + lea KEYS_AT,%a0 + mvz.b (%a0,%d2.l),%d0 + beq po_lk_out + cmpi.l #0x80,%d0 + bcc po_lk_no + lea LK_HELD,%a0 + cmp.b (%a0,%d2.l),%d0 | stock's held key is this one (mono) + beq po_lk_yes + lea KEYS_AT,%a0 + subq.l #1,%d0 + move.l 8(%a0,%d2.l*4),%d1 | the held keys (paraphonic) + btst %d0,%d1 + beq po_lk_no | let go already +po_lk_yes: + moveq #1,%d0 + bra po_lk_out +po_lk_no: + moveq #0,%d0 +po_lk_out: + move.l (%sp)+,%d1 + tst.l %d0 + rts + +| ---- po_xfq (2.10): Z clear when a warm START is a MONO note (VOIC 1, as sy_warm reads it) at +| another pitch than the sounding one: the word now (sy_word) at least half a semitone from +| S_CUR's. a3 = the track record, a4 = fp, d2 = track. Clobbers d0, d1, d6, a0. +po_xfq: + move.l #CV_STRIDE,%d1 + muls.l %d2,%d1 + lea CURVALS,%a0 + mvz.b CV_VOIC(%a0,%d1.l),%d1 + subq.l #2,%d1 + cmpi.l #2,%d1 + bls po_xq_no | VOIC 2..4: paraphonic (po_start's own rule) + bsr sy_word | d6 = the new note's word + move.l S_CUR(%a3),%d0 + asr.l #8,%d0 + asr.l #4,%d0 | the sounding word + sub.l %d6,%d0 + bpl po_xq_abs + neg.l %d0 +po_xq_abs: + cmpi.l #SEMI/2,%d0 + bcc po_xq_yes +po_xq_no: + moveq #0,%d0 + rts +po_xq_yes: + moveq #1,%d0 + rts + +| ---- po_erlen (2.10, the low-note onset click): d1 = a warm START's index-ramp frames left; 255 = fresh (sy_cold / po_st_note) -> +| max(16, one carrier period of the increment d0: 2^28 / inc frames), at most 254. A 16-frame index jump on a +| low note is the same onset splatter as the 16-frame attack (RATO 0.25, C1: +37 dB over the tone's own HF in +| the model). Clobbers d0. +po_erlen: + cmpi.l #255,%d1 + bne po_erl_out + move.l #0x10000000,%d1 + tst.l %d0 + beq po_erl_16 + divu.l %d0,%d1 + cmpi.l #16,%d1 + bcs po_erl_16 + cmpi.l #254,%d1 + bls po_erl_out + move.l #254,%d1 + rts +po_erl_16: + moveq #16,%d1 +po_erl_out: + rts + | ---- the samples -------------------------------------------------------------- sy_check: tst.b S_ON(%a3) @@ -765,7 +1030,7 @@ sy_check: move.l 52(%sp),%d2 | track (the rate block used d2) move.l #VOICE_STRIDE,%d3 muls.l %d2,%d3 - bsr fm_is_native + bsr po_signed | FM SYNTH chosen: no sample voice to end (po_fmstart / the stock kill own it) bne sy_alive lea VOICE_BASE,%a0 tst.b (%a0,%d3.l) | the CF voice ended: leave stock's silence ... @@ -784,6 +1049,14 @@ sy_alive: clr.w S_GPREV(%a3) | paraphonic: the mono voice is silent (a VOIC change mid-note carried it into a voice: po_carry) ... bra po_fill | ... sum the voices (ends at sy_done) sy_mono: + .if HAVE_SYDRUM + lea sy1_kind:l,%a0 | (2.11) SY DRUM renders its own samples (a1 = the first L long, d7 = + tst.b (%a0,%d2.l) | the count; S_GPREV updated), then the fading voices' tails as FM's + beq sy_mono_fm + jsr sy1_mono_render:l + bra sy_mono_tails +sy_mono_fm: + .endif move.l %a2,-(%sp) | the header: sy_gend's pass over the fading voices reads it (popped there) move.l S_PHC(%a3),%d0 move.l S_PHM(%a3),%d6 @@ -845,8 +1118,8 @@ sy_loop: move.l %a5,%d2 swap %d2 | gain / 2, Q14 muls.w %d2,%d1 | c * gain / 2 (16 x 16) - lsl.l #2,%d1 | = (c * g) << 1: mono source format - asr.l #OUTPUT_SHIFT,%d1 | headroom before AMP, matched by each paraphonic contribution + lsl.l #2,%d1 | = (c * g) << 1: the high word is (c * g) >> 15 -- at full gain c itself + asr.l #HEADROOM,%d1 | 2.10: -6.02 dB (the high word c / 2 at full gain), as each paraphonic voice below clr.w %d1 | sample << 16: the DSP's 24-bit word is the top 24 bits move.l %d1,(%a1)+ | L move.l %d1,(%a1)+ | R @@ -865,6 +1138,7 @@ sy_loop: sy_gend: move.w %d1,S_GPREV(%a3) move.l (%sp)+,%a2 | the header +sy_mono_tails: move.l 52(%sp),%d2 | track bsr po_any | paraphonic voices still fading under the mono voice (VOIC 2..4 -> 1 across beq sy_done | a note, sy_warm's po_fade): summed onto the mono voice's samples (BUILD 32) @@ -890,6 +1164,10 @@ po_tick: lea po_clock(%pc),%a0 move.l %a0,CLOCK_AT | published for the quantizer addq.l #1,CK_FRAMES(%a0) + .if HAVE_SYDRUM + jsr sd_tick:l | (2.11) SY DRUM, once a frame: its SETUP controls to the engine, the CF + | records' SETUP bytes neutral on its tracks, its LFOs (every register kept) + .endif mvz.w SEQ_STEP,%d1 lsl.l #8,%d1 mvz.b SEQ_TICK,%d3 @@ -983,6 +1261,13 @@ sy_word: move.l %d6,%d0 lsl.l #2,%d6 add.l %d0,%d6 | (ptch - 0x4000) * 5 + .if HAVE_SYDRUM + move.l %a0,-(%sp) + lea sy1_kind:l,%a0 | (2.11) SY DRUM: its RATE byte is SWEP, not FINE (the development line's + tst.b (%a0,%d2.l) | sy_word_base) + movea.l (%sp)+,%a0 + bne sy_word_base + .endif mvz.w 6(%a4),%d0 subi.l #0x4000,%d0 | (fine - 0x4000): cents << 8 ... move.l #3277,%d1 @@ -990,6 +1275,9 @@ sy_word: asr.l #8,%d0 asr.l #8,%d0 | ... / 20 = cents * 12.8 (3277 / 65536 = 1/20) add.l %d0,%d6 + .if HAVE_SYDRUM +sy_word_base: + .endif addi.l #0x4000,%d6 rts @@ -1227,9 +1515,15 @@ po_fr_params: mulu.l %d0,%d1 lsr.l #8,%d1 move.l %d1,S_FB(%a5) - mvz.w 10(%a4),%d1 | RTIM -> the index decay k (0 = hold) - lsr.l #8,%d1 - beq po_fr_hold + mvz.w 10(%a4),%d1 | RTIM -> the index decay k (127 = hold: k 0; + lsr.l #8,%d1 | 0 = the shortest: read as 1, k the cap K_MAX) + moveq #127,%d0 + cmp.l %d0,%d1 + bcc po_fr_hold + tst.l %d1 + bne.s po_fr_k + moveq #1,%d1 +po_fr_k: move.l %d1,%d0 mulu.l %d0,%d1 | raw^2 move.l #K_NUM,%d0 @@ -1255,8 +1549,8 @@ po_fr_dk: lea po_relk(%pc),%a1 mvz.w (%a1,%d0.l*2),%d0 bne po_fr_rk - btst #2,S_ON(%a5) | REL INF, STOPPED (po_stop): the floor, tau 1 ms -- every releasing - beq po_fr_rk | voice reaches silence under the DSP's all-off (BUILD 32) + btst #2,S_ON(%a5) | REL INF, STOPPED (BUILD 32): the floor, tau 1 ms. Since 2.12 nothing + beq po_fr_rk | sets bit 2 (po_stop removed): INF always holds here mvz.w (%a1),%d0 po_fr_rk: move.l %d0,T_RK(%a5) @@ -1291,16 +1585,7 @@ po_fr_inc1: asr.l #8,%d0 muls.l T_RATIO(%a5),%d0 move.l %d0,V_INCM(%a6) | modulator increment = ratio * pitch - mvz.w V_ERAMP(%a6),%d1 | a warm START's ramp (BUILD 38): the remaining distance to ENV_ONE over - beq po_fr_envk | the remaining frames -- linear; the decay waits for it - move.l #ENV_ONE,%d0 - sub.l V_ENV(%a6),%d0 - divs.l %d1,%d0 - add.l %d0,V_ENV(%a6) - subq.l #1,%d1 - move.w %d1,V_ERAMP(%a6) - bra po_fr_env -po_fr_envk: +po_fr_envk: | (2.10: a warm START's envelope decays from ENV_ONE as a cold one's; po_fr_il_* ramps the index) move.l T_DK(%a5),%d0 | the index envelope beq po_fr_envhold move.l V_ENV(%a6),%d1 @@ -1324,6 +1609,21 @@ po_fr_env: lsr.l #8,%d1 lsr.l #4,%d1 | I * E: this frame's target ... sub.l V_IEFF(%a6),%d1 | ... minus the running value: po_fill ramps V_IEFF across the frame, + mvz.w V_ERAMP(%a6),%d0 | (2.10) a warm START's ramp: the remaining distance over the remaining + beq po_fr_il_done | frames (as sy_il_*) + move.l %d1,-(%sp) + cmpi.l #255,%d0 + bne po_fr_il_left + move.l %d0,%d1 + move.l V_INC(%a6),%d0 + bsr po_erlen | d1 = L (clobbers d0) + move.l %d1,%d0 | fresh: L frames left +po_fr_il_left: + move.l (%sp)+,%d1 + divs.l %d0,%d1 | this frame's move + subq.l #1,%d0 + move.w %d0,V_ERAMP(%a6) +po_fr_il_done: moveq #16,%d0 | (target - running) / 16 a sample (BUILD 38) divs.l %d0,%d1 move.w %d1,V_ISTEP(%a6) @@ -1346,14 +1646,31 @@ po_fr_amp: cmp.l %d0,%d1 bge po_fr_cap | at full (or above it: a voice po_carry brought in, taken warm): no attack mvz.w T_AK(%a5),%d3 + lea -12(%sp),%sp + movem.l %d0/%a0-%a1,(%sp) | the onset law (po_alaw) per voice: full = T_GMAX, the voice's carrier + movea.l %d0,%a1 + movea.l V_INC(%a6),%a0 + move.l %d3,%d0 + bsr po_alaw + move.l %d0,%d3 + movem.l (%sp),%d0/%a0-%a1 + lea 12(%sp),%sp add.l %d3,%d1 cmp.l %d0,%d1 ble po_fr_gain move.l %d0,%d1 bra po_fr_gain po_fr_cut: - move.l T_GMAX(%a5),%d3 | stolen / chord-memory cut: T_GMAX / 8 a frame (8 frames = 2.9 ms), then freed - lsr.l #CUT_SHIFT,%d3 + move.l T_GMAX(%a5),%d0 | stolen / chord-memory cut: T_GMAX / 8 a frame (8 frames = 2.9 ms), then freed -- + lsr.l #CUT_SHIFT,%d0 | (2.10) no faster than one carrier period, S-shaped (po_alaw, as the attack) + lea -8(%sp),%sp + movem.l %a0-%a1,(%sp) + movea.l T_GMAX(%a5),%a1 + movea.l V_INC(%a6),%a0 + bsr po_alaw + movem.l (%sp),%a0-%a1 + lea 8(%sp),%sp + move.l %d0,%d3 bra po_fr_rel1 po_fr_rel: move.l T_RK(%a5),%d0 | releasing: gain -= gain * k @@ -1388,6 +1705,7 @@ po_fr_rel2: cmpi.l #64,%d0 | ... and free once po_fill has ramped it down there (a cut bgt po_fr_gain | voice -- po_fade / po_st_cut -- fades over its last frame first) clr.b V_STATE(%a6) + bsr po_pstart | (2.10) a note waiting for this voice starts now, cold po_fr_gain: move.l %d1,V_GAIN(%a6) po_fr_next: @@ -1404,6 +1722,14 @@ po_fr_next: | reset: the safety net when their owner -- the stock voice, or a paraphonic note -- | is gone, and the chord-memory cut at a start with a shape. Clobbers d0, a0. po_free: + lea po_pend(%pc),%a0 | (2.10) no note waits for them any more + move.l %d2,%d0 + lsl.l #6,%d0 + add.l %d0,%a0 + clr.b (%a0) + clr.b 16(%a0) + clr.b 32(%a0) + clr.b 48(%a0) lea po_voices(%pc),%a0 move.l %d2,%d0 lsl.l #8,%d0 @@ -1425,6 +1751,14 @@ po_free1: | the new chord takes in this same start restarts phase-continuous from that | gain (po_st_note). T_LIM is kept: the limiter's ramp runs on. Clobbers d0, a0. po_fade: + lea po_pend(%pc),%a0 | (2.10) the previous chord's waiting notes go with it + move.l %d2,%d0 + lsl.l #6,%d0 + add.l %d0,%a0 + clr.b (%a0) + clr.b 16(%a0) + clr.b 32(%a0) + clr.b 48(%a0) lea po_voices(%pc),%a0 move.l %d2,%d0 lsl.l #8,%d0 @@ -1433,7 +1767,7 @@ po_fade: po_fade1: tst.b V_STATE(%a0) beq po_fade2 - move.b #3,V_STATE(%a0) | fading (po_fr_cut: 8 frames), unless the new chord takes it warm first + move.b #3,V_STATE(%a0) | fading (po_fr_cut: 8 frames, 2.10: at least a period), unless the new chord takes it warm first po_fade2: lea V_STRIDE(%a0),%a0 subq.l #1,%d0 @@ -1479,6 +1813,8 @@ po_ca_next: po_ca_free: move.l %a0,%a1 po_ca_take: + move.l %a1,%a0 + bsr po_pclear | (2.10) a note that waited for it is dropped move.l %a3,%a0 moveq #9,%d0 po_ca_copy: @@ -1521,13 +1857,15 @@ po_ca_g: | ---- po_fade_frame: the fading voices' frame under the MONO voice (BUILD 32) ---- | The mono path's frame (sy_mono_frame, the frame's second call): every active -| voice of track d2 (state 3 after sy_warm's po_fade; any state is treated so) -| steps down by T_GMAX / 8 a frame (at least 2048: the T_GMAX of the last -| paraphonic start, 0 before any) and is freed once po_fill has ramped it to -| silence, as po_frame's po_fr_rel2 does. a3 = the track record. Clobbers nothing. +| voice of track d2 (state 3 after sy_warm's po_fade or a crossfade's po_carry; +| any state is treated so) steps down by T_GMAX / 8 a frame (at least 2048: the +| T_GMAX of the last paraphonic start, 0 before any) -- 2.10: through po_alaw, no +| faster than one period of the lower of its pitch and the mono voice's, S-shaped +| -- and is freed once po_fill has ramped it to silence, as po_frame's po_fr_rel2 +| does. a3 = the track record. Clobbers nothing. po_fade_frame: - lea -20(%sp),%sp - movem.l %d0/%d1/%d3/%d4/%a0,(%sp) + lea -24(%sp),%sp + movem.l %d0/%d1/%d3/%d4/%a0/%a1,(%sp) lea po_voices(%pc),%a0 move.l %d2,%d0 lsl.l #8,%d0 @@ -1542,8 +1880,25 @@ po_ff_step: po_ff_loop: tst.b V_STATE(%a0) beq po_ff_next - move.l V_GAIN(%a0),%d1 - sub.l %d3,%d1 + move.l %d0,-(%sp) | (the count) + move.l %a0,%d4 | (2.10) the step no faster than one period of the LOWER of the voice and + move.l V_INC(%a0),%d0 | the mono voice now (a crossfade down lasts the new note's period, up + move.l S_INC(%a3),%d1 | the old one's), S-shaped (po_alaw; full = the mono voice's 32768) + beq po_ff_i + cmp.l %d1,%d0 + bls po_ff_i + move.l %d1,%d0 +po_ff_i: + movea.l %d0,%a0 + movea.l %d4,%a1 + move.l V_GAIN(%a1),%d1 + move.l %d3,%d0 + movea.l #32768,%a1 + bsr po_alaw + movea.l %d4,%a0 + move.l %d0,%d4 | the step + move.l (%sp)+,%d0 + sub.l %d4,%d1 bpl po_ff_1 moveq #0,%d1 po_ff_1: @@ -1560,8 +1915,169 @@ po_ff_next: lea V_STRIDE(%a0),%a0 subq.l #1,%d0 bne po_ff_loop - movem.l (%sp),%d0/%d1/%d3/%d4/%a0 - lea 20(%sp),%sp + movem.l (%sp),%d0/%d1/%d3/%d4/%a0/%a1 + lea 24(%sp),%sp + rts + +| ---- the waiting notes (2.10): a paraphonic voice taken for a note at another pitch while it +| still sounds fades first (po_fr_cut: one period of its note, S-shaped) and the note starts +| cold after it. po_pend: 8 tracks x 4 voices x 16 bytes, the voice's entry -- +0 the state +| the note takes (1 sounding, 2 releasing; 0 none, 255 being written), +1 its V_KEY, +4 its +| V_ROOT, +8 its V_HOLD. +| po_pendof: a1 := voice a0's entry. Clobbers d0. +po_pendof: + move.l %a0,%d0 + lea po_voices(%pc),%a1 + sub.l %a1,%d0 + lsr.l #2,%d0 + lea po_pend(%pc),%a1 + add.l %d0,%a1 + rts +| po_pclear: voice a0's waiting note is dropped. Clobbers d0. +po_pclear: + move.l %a1,-(%sp) + bsr po_pendof + clr.b (%a1) + movea.l (%sp)+,%a1 + rts +| po_st_xf: a0 = the sounding voice po_alloc took for a note at another pitch (po_st_note). It +| fades (state 3, no gate, stamped newest). A free voice of the track takes the note: a0 := it, +| NE (the cold start). None: the note waits in a0's entry (255 + the fading root and word, +| swapped in by po_pfix), EQ. Clobbers d0, a2, a6. +po_st_xf: + move.b #3,V_STATE(%a0) + clr.l V_HOLD(%a0) + lea po_seq(%pc),%a6 + addq.l #1,(%a6) + move.l (%a6),V_AGE(%a0) + bsr po_voices_of +po_xf_scan: + tst.b V_STATE(%a6) + bne po_xf_next + movea.l %a6,%a0 | a free voice: the note starts there, cold + clr.w V_GPREV(%a0) + moveq #1,%d0 + rts +po_xf_next: + lea V_STRIDE(%a6),%a6 + cmp.l %a2,%a6 + bne po_xf_scan + move.l %a1,-(%sp) | none free: in place + bsr po_pendof + move.l V_ROOT(%a0),4(%a1) + move.l V_CUR(%a0),8(%a1) + moveq #-1,%d0 + move.b %d0,(%a1) + movea.l (%sp)+,%a1 + moveq #0,%d0 + rts +| po_pmatch (po_st_note): a0 := a fading voice of the track (state 3, still sounding, no note +| waiting for it) at the pitch of the note d3 semitones over T_W (within half a semitone), NE; +| none: EQ. Chord memory then keeps a common tone's voice (the same pitch: warm, as one key's +| retrigger) instead of handing the voices out by age. Clobbers d0, d1, d4, a0, a2, a6. +po_pmatch: + move.l #SEMI,%d0 + muls.l %d3,%d0 + add.l T_W(%a5),%d0 + bsr po_snap + move.l %d0,%d4 + bsr po_voices_of +po_pm_loop: + mvz.b V_STATE(%a6),%d0 + subq.l #3,%d0 + bne po_pm_next | fading voices only + move.l V_CUR(%a6),%d0 + asr.l #8,%d0 + asr.l #4,%d0 + sub.l %d4,%d0 + bpl po_pm_a + neg.l %d0 +po_pm_a: + cmpi.l #SEMI/2,%d0 + bcc po_pm_next | another pitch + tst.w V_GPREV(%a6) + beq po_pm_next | silent already + movea.l %a6,%a0 + move.l %a1,-(%sp) + bsr po_pendof + tst.b (%a1) + movea.l (%sp)+,%a1 + beq po_pm_yes | no note waits for it: take it +po_pm_next: + lea V_STRIDE(%a6),%a6 + cmp.l %a2,%a6 + bne po_pm_loop + moveq #0,%d0 + rts +po_pm_yes: + moveq #1,%d0 + rts +| po_pfix (po_st_state): voice a0's entry is being written (255): the note po_st_note wrote into +| the voice moves into the entry and the voice fades on at its old pitch. Clobbers d0. +po_pfix: + move.l %a1,-(%sp) + bsr po_pendof + mvz.b (%a1),%d0 + cmpi.l #255,%d0 + bne po_pf_out + move.l V_ROOT(%a0),%d0 + move.l 4(%a1),V_ROOT(%a0) + move.l %d0,4(%a1) + move.l 8(%a1),V_CUR(%a0) + move.l V_HOLD(%a0),8(%a1) + clr.l V_HOLD(%a0) + move.b V_KEY(%a0),1(%a1) + clr.b V_KEY(%a0) + move.b V_STATE(%a0),(%a1) + move.b #3,V_STATE(%a0) +po_pf_out: + movea.l (%sp)+,%a1 + rts +| po_pstart (po_frame, the voice a6 just freed): its waiting note starts, cold -- phase 0, the +| gain from 0 (the attack from the next frame), the index envelope fresh, at its own pitch; +| a key or MIDI note released while it waited releases at once. a5 = the track record, d2 = +| track. Preserves every register. +po_pstart: + lea -16(%sp),%sp + movem.l %d0/%d1/%a0/%a1,(%sp) + movea.l %a6,%a0 + bsr po_pendof + mvz.b (%a1),%d0 + beq po_ps_out + cmpi.l #255,%d0 + beq po_ps_drop + move.b %d0,V_STATE(%a6) + move.b 1(%a1),V_KEY(%a6) + move.l 4(%a1),V_ROOT(%a6) + move.l 8(%a1),V_HOLD(%a6) + move.l T_W(%a5),%d0 + sub.l T_REF(%a5),%d0 + add.l 4(%a1),%d0 + lsl.l #8,%d0 + lsl.l #4,%d0 + move.l %d0,V_CUR(%a6) + clr.l V_PHC(%a6) + clr.l V_PHM(%a6) + clr.l V_LASTM(%a6) + clr.l V_GAIN(%a6) + clr.w V_GPREV(%a6) + clr.l V_IEFF(%a6) + clr.w V_ERAMP(%a6) + move.l #ENV_ONE,%d0 + move.l %d0,V_ENV(%a6) + lea po_clock(%pc),%a0 + move.l CK_FRAMES(%a0),%d0 + move.l %d0,V_FRAME(%a6) + mvz.b V_KEY(%a6),%d0 + beq po_ps_drop + bsr po_held + bne po_ps_drop + move.b #2,V_STATE(%a6) +po_ps_drop: + clr.b (%a1) +po_ps_out: + movem.l (%sp),%d0/%d1/%a0/%a1 + lea 16(%sp),%sp rts | ---- po_any: Z clear when any voice of track d2 is active. Clobbers d0, d1, a0. - @@ -1596,12 +2112,20 @@ po_start: bsr po_voices_of | (clobbers d0) move.l T_LAST(%a5),%d1 sub.l T_REF(%a5),%d1 | what PTCH moved since the last start: + lea po_pend(%pc),%a0 | (2.10) a0 walks the voices' waiting notes beside a6 + move.l %d2,%d0 + lsl.l #6,%d0 + add.l %d0,%a0 po_st_fold: | fold it into the sounding voices' roots tst.b V_STATE(%a6) beq po_st_fold1 add.l %d1,V_ROOT(%a6) + tst.b (%a0) | (2.10) and into a note waiting for the voice + beq po_st_fold1 + add.l %d1,4(%a0) po_st_fold1: lea V_STRIDE(%a6),%a6 + lea 16(%a0),%a0 cmp.l %a2,%a6 bne po_st_fold move.l T_W(%a5),%d1 @@ -1865,7 +2389,8 @@ po_st_cut: tst.l %d3 | the excess: that many of the oldest active voices go ble po_st_note bsr po_steal | a0 = the oldest active voice (clobbers d0, d1, d4, a6) - move.b #3,V_STATE(%a0) | it fades over 8 frames (po_fr_cut: T_GMAX / 8 a frame), not a cut + bsr po_pclear | (2.10) a note that waited for it goes too + move.b #3,V_STATE(%a0) | it fades over 8 frames (po_fr_cut: T_GMAX / 8 a frame; 2.10: at least a period, S-shaped), not a cut lea po_seq(%pc),%a6 addq.l #1,(%a6) move.l (%a6),V_AGE(%a0) | stamped newest: po_steal does not pick it again @@ -1875,9 +2400,27 @@ po_st_note: | the voicing's notes (a shape: its n n mvs.b (%a1)+,%d3 | semitones above the note; SHAPE_END ends "----" after its one note cmpi.l #SHAPE_END,%d3 beq po_st_done + bsr po_pmatch | (2.10) a fading voice at this note's pitch (chord memory, a steal): + bne po_st_warm | taken warm, at its own pitch (a0) bsr po_alloc | a0 = the voice to use + bsr po_pclear | (2.10) a note that waited for it is dropped: it is taken now tst.w V_GPREV(%a0) | it sounded last frame (a retrigger, a tail, a chord-memory fade): - bne po_st_warm | phase-continuous, its gain ramps on from where po_fill left it + beq po_st_cold | at the same pitch it is taken warm -- phase-continuous, its gain ramps on + move.l #SEMI,%d0 | from where po_fill left it; (2.10) at ANOTHER pitch it fades first + muls.l %d3,%d0 | (po_st_xf) and the note starts cold + add.l T_W(%a5),%d0 + bsr po_snap | the note's word (uses d1, d4) + move.l V_CUR(%a0),%d1 + asr.l #8,%d1 + asr.l #4,%d1 | the word the voice sounds at + sub.l %d0,%d1 + bpl po_st_warm | the same pitch or a LOWER note: warm (a fade of the brighter old note + neg.l %d1 | measured worse than the phase-continuous jump) + cmpi.l #SEMI/2,%d1 + bcs po_st_warm | the same pitch: warm + bsr po_st_xf | a0 := a free voice (NE: cold there), or the same voice (EQ: the note waits) + beq po_st_env +po_st_cold: clr.l V_PHC(%a0) | a silent voice: both operators at phase 0 ... clr.l V_PHM(%a0) clr.l V_LASTM(%a0) @@ -1888,7 +2431,9 @@ po_st_note: | the voicing's notes (a shape: its n n move.l %d0,V_ENV(%a0) bra po_st_env po_st_warm: - move.w #16,V_ERAMP(%a0) | warm: the index envelope ramps from its level to ENV_ONE over 16 frames (po_frame; BUILD 38) + move.w #255,V_ERAMP(%a0) | warm: the index (I * E) ramps from its level over max(16, one period) frames (po_frame) + move.l #ENV_ONE,%d0 | (2.10) and the envelope restarts as a cold note's (decays from the START) + move.l %d0,V_ENV(%a0) po_st_env: move.l #SEMI,%d0 muls.l %d3,%d0 @@ -1903,10 +2448,15 @@ po_st_env: lea po_clock(%pc),%a6 move.l CK_FRAMES(%a6),%d0 move.l %d0,V_FRAME(%a0) | when (the dedupe above) - move.l #CV_STRIDE,%d1 | EVERY note is gated for the lane's HOLD (the lock, else the Part's - muls.l %d2,%d1 | byte): frames = hold * frames a step -- a sequencer note and a live - lea CURVALS,%a4 | key alike (plan B: the DSP's own timer ran from the START whoever - mvz.b CV_HOLD(%a4,%d1.l),%d1 | started it, stage 1; 127 = INF: until the note-off / the next trig) + tst.l %d7 | (2.10) a CHROMATIC key's note (1..25) is gated by the key: no + beq po_st_hseq | HOLD timer, it releases when the key goes (po_frame's mask; a + cmpi.l #0x80,%d7 | key already gone releases at once below) + bcs po_st_hinf +po_st_hseq: + move.l #CV_STRIDE,%d1 | a sequencer note (and a MIDI note) is gated for the lane's HOLD (the lock, + muls.l %d2,%d1 | else the Part's byte): frames = hold * frames a step (plan B: the DSP's + lea CURVALS,%a4 | own timer ran from the START, stage 1; 127 = INF: until the note-off / + mvz.b CV_HOLD(%a4,%d1.l),%d1 | the next trig) cmpi.l #127,%d1 beq po_st_hinf lea po_hold128(%pc),%a4 @@ -1944,6 +2494,7 @@ po_st_state: lea po_seq(%pc),%a6 addq.l #1,(%a6) move.l (%a6),V_AGE(%a0) + bsr po_pfix | (2.10) a note waiting for this voice: the voice fades on at its old pitch subq.l #1,%d6 bne po_st_note po_st_done: @@ -2033,6 +2584,36 @@ po_cp_out: | release posts the stock AMP release (the quantizer's qz_leg0, po_moff). | GLIDE OFF: sy_slew snaps, so the chord changes at once without a retrigger. po_legkey: + .if HAVE_SYDRUM +| (2.11) the quantizer also calls this for a legato key on a SY DRUM track (qz_g2_mono): a mono +| legato key there recharges the drum's envelopes once its trigless pitch lock lands (the development +| line's po_legkey: sy1_leg_pending, consumed by sy1_mono_key_frame); paraphonic tracks as before. + lea -12(%sp),%sp + movem.l %d0-%d1/%a0,(%sp) + bsr po_polytrack + bne po_lgk_poly + bsr po_is_synth + beq po_lgk_out | a sample's mono legato stays stock + lea sy1_kind:l,%a0 + tst.b (%a0,%d2.l) + beq po_lgk_out | FM mono legato never recharges its envelopes + lea sy1_leg_pending:l,%a0 + moveq #1,%d1 + move.b %d1,(%a0,%d2.l) | SY DRUM mono: deferred to the key's posted trigless frame + bra po_lgk_out +po_lgk_poly: + move.l (%sp),%d0 | the key's identity + lea sy_state(%pc),%a0 + move.l %d2,%d1 + lsl.l #7,%d1 + adda.l %d1,%a0 + move.b %d0,T_LEGKEY(%a0) + clr.b T_LEGN(%a0) +po_lgk_out: + movem.l (%sp),%d0-%d1/%a0 + lea 12(%sp),%sp + rts + .else move.l %a0,-(%sp) move.l %d1,-(%sp) lea sy_state(%pc),%a0 @@ -2044,6 +2625,7 @@ po_legkey: move.l (%sp)+,%d1 movea.l (%sp)+,%a0 rts + .endif | ---- po_handover: a5 = the track record, d2 = track, d7 = the identity, T_W = the | new word. Clobbers d0, d1, d3, d4, d6, d7, a0, a1, a2, a6. @@ -2134,6 +2716,7 @@ po_ho_as: po_ho_fresh: move.l %d0,-(%sp) bsr po_alloc | a0 = a free voice (k < n: one exists; clobbers d0, d1, d4, a6) + bsr po_pclear | (2.10) a note that waited for it is dropped move.l (%sp)+,%d0 clr.l V_PHM(%a0) | as po_st_note starts one clr.l V_LASTM(%a0) @@ -2219,7 +2802,11 @@ po_ho_new1: | track any nonzero byte is MONO). A stock Part reads OFF. tst.l d0 done; | preserves every other register. po_legmode: + .if HAVE_SYDRUM + bsr po_is_fm | (2.11) SY DRUM, like a sample track, offers OFF / MONO only + .else bsr po_is_synth + .endif beq po_lm_sample | not a synth track: its LEG byte, 0 -> 0 (stock), else 1 (mono legato) lea -16(%sp),%sp movem.l %d1/%d3/%d4/%a0,(%sp) @@ -2349,32 +2936,41 @@ po_sn_ret: | ---- po_alloc: a0 := the voice of track d2 to take for a new note --------------- | A free voice (state 0) first, else the oldest releasing one, else the oldest -| sounding one: the least (rank << 28 | age) with rank 0 / 1 / 2. Clobbers d0, -| d1, d4, a6; saves d3, d6 and a1 for po_start. +| sounding one: the least (rank << 28 | age) with rank 0 / 1 / 2; a voice whose +| po_pend entry holds a waiting note ranks 3 in both searches (2.10: a second key +| inside one wait took it again and dropped the first key's note). Clobbers d0, +| d1, d4, a6; saves d3, d6, a1 and a2 for po_start. | po_steal (the VOIC cap): the same search with a free voice ranked last (3), | so a0 := the oldest releasing voice, else the oldest sounding one -- the | one to cut when the track is over its cap (po_start calls it only while an | active voice exists). po_steal: - lea -12(%sp),%sp - movem.l %d3/%d6/%a1,(%sp) + lea -16(%sp),%sp + movem.l %d3/%d6/%a1/%a2,(%sp) lea po_rank_act(%pc),%a1 bra po_al_go po_alloc: - lea -12(%sp),%sp - movem.l %d3/%d6/%a1,(%sp) + lea -16(%sp),%sp + movem.l %d3/%d6/%a1/%a2,(%sp) lea po_rank(%pc),%a1 po_al_go: lea po_voices(%pc),%a6 move.l %d2,%d0 lsl.l #8,%d0 add.l %d0,%a6 + lea po_pend(%pc),%a2 | (2.10) the voices' waiting notes beside a6 + lsr.l #2,%d0 + add.l %d0,%a2 move.l %a6,%a0 moveq #-1,%d4 | the best key so far (unsigned) moveq #4,%d6 po_al_loop: mvz.b V_STATE(%a6),%d0 mvz.b (%a1,%d0.l),%d0 + tst.b (%a2) | (2.10) a voice a waiting note holds is spoken for: last (a + beq po_al_rk | second key inside one wait took it again and po_pclear dropped + moveq #3,%d0 | the first key's note) +po_al_rk: lsl.l #8,%d0 lsl.l #8,%d0 lsl.l #8,%d0 @@ -2388,10 +2984,11 @@ po_al_loop: move.l %a6,%a0 po_al_next: lea V_STRIDE(%a6),%a6 + lea 16(%a2),%a2 subq.l #1,%d6 bne po_al_loop - movem.l (%sp),%d3/%d6/%a1 - lea 12(%sp),%sp + movem.l (%sp),%d3/%d6/%a1/%a2 + lea 16(%sp),%sp rts po_rank: .byte 0, 2, 1, 0 @@ -2409,7 +3006,8 @@ po_rank_act: | po_steal: free voices last | CLAMP on the sum only -- the peak limiter that follows in this note is HISTORY | (removed: its gain movement was itself a level modulation under chords; the | level law 1/sqrt(VOIC) alone bounds a single note at 0.5 FS and a VOIC 4 chord's -| coincident peaks at 2.0 FS, clamped). History (29 Sep 2026): a PEAK LIMITER, a gain and not a curve: the frame's +| coincident peaks at 2.0 FS, clamped; 2.10: each contribution is shifted right by +| HEADROOM first, as the mono voice's samples are, so they reach 1.0 FS). History (29 Sep 2026): a PEAK LIMITER, a gain and not a curve: the frame's | peak |x| is scanned; the track's gain g (T_LIM, Q16) releases toward 1.0 | by 1/2048 of the deficit a frame (tau = 0.74 s) and is pulled down to FS / | peak at once when this frame's peak would pass FS, so no sample ever @@ -2428,8 +3026,8 @@ po_rank_act: | po_steal: free voices last | divu.l a frame while limiting. The saturation after the doubling is only a | guard: |x * g| <= FS always. po_fill_add: | BUILD 32: the mono voice's samples are in the record (sy_gend): the - moveq #1,%d4 | fading voices are ADDED to them -- each L long, (c * g) << 1 with its low - bra po_fill1 | word cleared, is halved into the sum's format (c * g) first + moveq #1,%d4 | fading voices are ADDED to them -- each L long, ((c * g) << 1) >> HEADROOM with its + bra po_fill1 | low word cleared, is halved into the sum's format ((c * g) >> HEADROOM) first po_fill: moveq #0,%d4 | the record is cleared first po_fill1: @@ -2523,7 +3121,8 @@ po_fi_loop: move.l %a5,%d2 swap %d2 | its integer part, Q15 muls.w %d2,%d1 | c * gain (16 x 16: c is +-0x4000, the gain at most 23170) - asr.l #OUTPUT_SHIFT,%d1 | headroom BEFORE summing: coincident voices cannot overflow the mix + asr.l #HEADROOM,%d1 | 2.10: -6.02 dB BEFORE summing, as the mono voice (a VOIC 4 chord's coincident + | peaks reach 1.0 FS, not 2.0; the clamp below stays as the guard) add.l %d1,(%a1) | into the sum addq.l #8,%a1 add.l %d5,%d0 @@ -2581,7 +3180,9 @@ po_lfo3b: bne po_l3b_depth cmpi.l #2,%d2 bne po_l3b_depth + move.l %a0,-(%sp) | preserve the staging base across the frame clock bsr po_tick | track 0, LFO 3: the clock, once a frame + movea.l (%sp)+,%a0 | the displaced lea below still needs stock a0 po_l3b_depth: bsr po_lfo3_depth jmp 0x4000d046 @@ -2593,6 +3194,11 @@ po_lfo3_depth: move.l %a4,%d1 | (whatever VOIC: slot 5 is the chord byte on a synth track, subi.l #LFO_STATE,%d1 lsr.l #3,%d1 | the track + .if HAVE_SYDRUM + lea sy1_kind:l,%a0 | (2.11) SY DRUM keeps the stock LFO page: LFO 3 is an LFO there + tst.b (%a0,%d1.l) + bne po_l3_back + .endif lsl.l #7,%d1 lea sy_state(%pc),%a0 | and stock's depth on it would be a pitch LFO, 24 Sep 2026) tst.b S_ON(%a0,%d1.l) | its playing voice is a synth? @@ -2609,23 +3215,19 @@ po_l3_back: | built from the stock descriptor on first use -- is returned. po_lfopage: move.l #LFO_P,%d0 | displaced: the stock descriptor - cmpi.l #1,%d5 | FLEX? + mvz.b %d5,%d2 | 2.10: the resolver loads only d5's LOW byte (`moveb %a0@,%d5`); + cmpi.l #1,%d2 | FLEX? (the long compare failed for a caller whose d5 was negative) bne po_lp_done move.l #6322,%d2 muls.l %d1,%d2 | part * 6322 add.l %a1,%d2 | + the bank blob - move.l %d2,%a0 - move.l %d3,%d4 - mulu.w #30,%d4 - adda.l %d4,%a0 - adda.l #0x8edbc,%a0 - mvz.w (%a0),%d5 - cmpi.w #0x464d,%d5 - bne 1f - mvz.b 2(%a0),%d5 - cmpi.b #1,%d5 - beq fm_lfo_clone -1: + move.l %d0,-(%sp) + movea.l %d2,%a0 + move.l %d3,%d0 + bsr po_sig | FM SYNTH chosen in the machine list? + movem.l (%sp),%d0 | (flags kept) + lea 4(%sp),%sp + bne po_lp_clone move.l %d3,%d4 lsl.l #2,%d4 add.l %d3,%d4 | track * 5 @@ -2665,7 +3267,7 @@ po_lp_cmp: bne po_lp_done subq.l #1,%d4 bne po_lp_cmp -fm_lfo_clone: +po_lp_clone: lea po_lfodesc(%pc),%a1 tst.b po_lfo_built bne po_lp_have @@ -2704,6 +3306,158 @@ po_lp_have: po_lp_done: jmp RESOLVER_RET +| ==== THE LFO DESTINATION LIST (2.10, 6 Oct 2026; three hooks) ====================== +| LFO SETUP's PMTR names a destination as page * 6 + slot (0 PLAYBACK, 1 LFO, 2 AMP, 3 FX1, +| 4 FX2: the resolver's page kinds) and prints it through the stock formatter 0x4003bf64 +| (the LFO descriptor's slot-6 formatter), which takes the PLAYBACK names from the machine +| table 0x400d5f38 itself and the LFO page's from the stock descriptor -- not through the +| page resolver page.s and po_lfopage hook -- so a synth track listed STRT LEN RATE RTRG +| RTIM and SPD3 / DEP3. The list now reads PTCH RATO INDX FINE FDBK DEC | ATK HOLD REL VOL +| BAL | SPD1 SPD2 DEP1 DEP2 | FX1 | FX2 on a synth track: VOIC / CHRD (the bytes of SPD3 / +| DEP3 there: a voice count and a chord shape, no destinations) are stepped over. The stock edit 0x400392cc walks the 30 entries in display +| order (PLAYBACK AMP LFO FX1 FX2: stock's tables 0x400a72a8 value -> position, 0x400a7280 +| back) with no gaps. Nothing is stored differently: PMTR stays the stock Part byte. + .set LFD_RESOLVER, 0x40031da4 | the page resolver (track, page kind) -> descriptor + .set LFD_MACHTAB, 0x400d5f38 | the PLAYBACK descriptors by machine + .set LFD_MASTER, 0x400d2e8a | the resolver's descriptor for the master track + .set LFD_FMT_RET, 0x4003c058 | the formatter after its descriptor pick (a2) + .set LFD_STORE, 0x4003932c | the PMTR edit's store (d3 = the new value) + .set LFD_ENC, 0x4003249c | the stock encoder delta (0, arg) -> steps + .set LFD_V2P, 0x400a72a8 | page -> display page + .set LFD_P2V, 0x400a7280 | display page -> page + .set LFD_LAST, 29 | the last stock destination (FX2 slot 6) + .set LFD_HIDE_FM, 0x900 | bits 8 and 11, FM SYNTH: SPD3 (VOIC) and DEP3 (CHRD) + +| po_lfdname: 0x4003bff2 (jmp, 8 B), `lea 0x400d5f38,%a0; bras 0x4003c054` (then +| a2 = tbl[machine]). d0 = the machine byte, d1 = the track, d2 = the slot (kept), a4 = +| buf. A FLEX pick is replaced by the page resolver's answer -- FM SYNTH's clone on a +| synth track -- so LFO SETUP prints the PLAYBACK page's own names; the master track +| and every other machine keep the stock pick. +po_lfdname: + lea LFD_MACHTAB,%a0 + movea.l (%a0,%d0.l*4),%a2 | displaced: the stock pick + cmpa.l #FLEX_P,%a2 + bne po_ln_out + clr.l -(%sp) | page kind 0: PLAYBACK + move.l %d1,-(%sp) | the track + jsr LFD_RESOLVER | d0 = its descriptor (d0/d1/a0/a1 clobbered, d2-d5 kept) + addq.l #8,%sp + cmpi.l #LFD_MASTER,%d0 + beq po_ln_out + tst.l %d0 + beq po_ln_out + movea.l %d0,%a2 +po_ln_out: + jmp LFD_FMT_RET + +| po_lfdlfo: 0x4003bffa (jmp, 8 B), `lea 0x400d37f6,%a2; bras 0x4003c058`: the LFO page's +| names (page kind 1). d1 = the track, d2 = the slot (kept), a4 = buf. On a synth track +| the page resolver hands its LFO page our VOIC / CHRD clone (po_lfopage): a value the +| edit no longer offers (SPD3 / DEP3, stored before 2.10 or by a lock) prints as VOIC / +| CHRD, what it drives there. Every other track: the stock descriptor. +po_lfdlfo: + lea LFO_P,%a2 | displaced: the stock pick + moveq #1,%d0 + move.l %d0,-(%sp) | page kind 1: LFO + move.l %d1,-(%sp) | the track + jsr LFD_RESOLVER | d0 = its descriptor (d0/d1/a0/a1 clobbered, d2-d5 kept) + addq.l #8,%sp + lea po_lfodesc(%pc),%a0 + cmpa.l %d0,%a0 + bne po_lo_out | not our clone: stock + movea.l %a0,%a2 +po_lo_out: + jmp LFD_FMT_RET + +| po_lfdedit: 0x400392cc (jmp, 8 B), the audio PMTR edit after its two branches (d3 = the +| stored value, d5 = the encoder argument; d0-d2, d7, a0, a1 scratch as in the stock code +| it replaces). Stock's walk -- position, encoder steps, clamp to 0..29, back to a value -- +| with the destinations hidden on this track stepped over in both directions. A hidden +| value already stored keeps working and prints as VOIC / CHRD (po_lfdlfo); the first turn +| leaves it for the next shown entry that way. At an end with nothing shown beyond, it stays. +po_lfdedit: + lea -8(%sp),%sp + movem.l %d4/%d6,(%sp) + move.l %d3,%d2 | the stored value (a signed byte) + cmpi.l #LFD_LAST,%d2 + bls po_le_ok | unsigned: 0..29 + moveq #LFD_LAST,%d2 | anything else: from the end of the list +po_le_ok: + lea LFD_V2P,%a0 + bsr po_le_map + move.l %d0,%d7 | the position + move.l %d5,-(%sp) + clr.l -(%sp) + jsr LFD_ENC | the steps, as stock asks for them + addq.l #8,%sp + move.l %d0,%d3 + bsr po_ld_hidden + move.l %d0,%d4 | the hidden mask + moveq #1,%d1 + tst.l %d3 + bpl po_le_cnt + moveq #-1,%d1 + neg.l %d3 +po_le_cnt: + tst.l %d3 + beq po_le_end + move.l %d7,%d2 +po_le_nx: + add.l %d1,%d2 + bmi po_le_end | before the first entry: stay + moveq #LFD_LAST,%d0 + cmp.l %d0,%d2 + bgt po_le_end | past the last: stay + lea LFD_P2V,%a0 + bsr po_le_map | d0 = the value shown at position d2 + btst %d0,%d4 + bne po_le_nx | hidden: over it + move.l %d2,%d7 + subq.l #1,%d3 + bra po_le_cnt +po_le_end: + move.l %d7,%d2 + lea LFD_P2V,%a0 + bsr po_le_map + move.l %d0,%d3 | the new value + movem.l (%sp),%d4/%d6 + lea 8(%sp),%sp + jmp LFD_STORE + +| d2 = a value or a position 0..29, a0 = one of stock's page-order tables -> d0 = +| d2 + 6 * (table[d2 / 6] - d2 / 6). Clobbers d6. +po_le_map: + move.l %d2,%d0 + moveq #6,%d6 + divs.l %d6,%d0 + move.l (%a0,%d0.l*4),%d6 + sub.l %d0,%d6 + move.l %d6,%d0 + add.l %d0,%d0 + add.l %d6,%d0 + add.l %d0,%d0 + add.l %d2,%d0 + rts + +| po_ld_hidden -> d0 = the destinations hidden on the UI track (bit v = PMTR value v): +| an FM SYNTH track -- the resolver hands its LFO page our VOIC / CHRD clone -- hides +| SPD3 and DEP3; every other track hides nothing. Clobbers d1, a0, a1. +po_ld_hidden: + moveq #1,%d0 + move.l %d0,-(%sp) | page kind 1: LFO + mvz.b UI_TRACK,%d0 + move.l %d0,-(%sp) + jsr LFD_RESOLVER + addq.l #8,%sp + lea po_lfodesc(%pc),%a0 + cmpa.l %d0,%a0 + bne po_lh_none + move.l #LFD_HIDE_FM,%d0 + rts +po_lh_none: + moveq #0,%d0 + rts + | ---- po_fmt_voic: fmt(buf, value) -> "1".."4" (a byte outside 1..4 reads 1, as the engine reads it) -- po_fmt_voic: move.l 8(%sp),%d0 @@ -3370,6 +4124,10 @@ po_keyrec: move.l %d0,%d5 | the step, or -1: the join query move.l %a2,%d6 | the raw move.l %d3,%d4 | the identity (index + 1, or 0x80 | note) + .if HAVE_SYDRUM + bsr po_is_fm | (2.11) SY DRUM never records VOIC / CHRD locks from held keys + beq po_kr_out + .endif movea.l PART_PTR,%a0 | the Part's LFO page bytes: VOIC 2..4 and CHRD "----" only mvz.b PART_IDX,%d0 move.l #6322,%d1 @@ -3556,6 +4314,10 @@ po_keyrel: lea -52(%sp),%sp movem.l %d0-%d7/%a0-%a4,(%sp) move.l %d0,%d4 + .if HAVE_SYDRUM + bsr po_is_fm | (2.11) no FM chord record can rewrite a SY DRUM key's release + beq po_rl_out + .endif cmpi.l #0x80,%d4 bcc po_rl_id addq.l #1,%d4 | a panel key's identity @@ -3740,12 +4502,21 @@ po_lockw: | FOLLOW TM with the CHROMATIC trig mode is a different path (0x400500e8 -> | the key handler 0x4004fb94 with key = note - 72: the quantizer's 25-key | paraphony, 72..96) and is not changed. - .set MACH_OFF, 0x8eda2 | Part: 0x8eda2 + track = the machine (1 = FLEX) -| ---- po_is_synth: d2 = track -> d0 = 1 when a FLEX track whose Part slot holds a -| SYNTH* / FMSYNTH* sample (the quantizer's qz_is_synth, read here from the Part -| and the settings table), else 0; tst.l d0 done. Preserves every other register. +| ---- po_is_synth: d2 = track -> d0 = 1 when a FLEX track whose FM SYNTH machine is +| chosen in the machine list ("FM", 1 in the Part: po_signed, 2.10) or whose Part slot +| holds a SYNTH* / FMSYNTH* sample (the quantizer's qz_is_synth, read here from the +| Part and the settings table), else 0; tst.l d0 done. Preserves every other register. po_is_synth: + bsr po_signed | FM SYNTH chosen in the machine list (machine.s) + .if HAVE_SYDRUM + beq po_is_unsigned | (2.11) or SY DRUM: any machine of the list answers 1 + moveq #1,%d0 + rts +po_is_unsigned: + .else + bne po_is_rts + .endif lea -16(%sp),%sp movem.l %d1/%d3/%a0/%a1,(%sp) movea.l PART_PTR,%a0 @@ -3758,20 +4529,6 @@ po_is_synth: mvz.b (%a1,%d2.l),%d0 | the track's machine subq.l #1,%d0 | FLEX? bne po_is_no - move.l %a0,%a1 - move.l %d2,%d0 - mulu.w #30,%d0 - adda.l %d0,%a1 - adda.l #0x8edbc,%a1 - mvz.w (%a1),%d0 - cmpi.w #0x464d,%d0 - bne 1f - mvz.b 2(%a1),%d0 - cmpi.b #1,%d0 - bne 1f - moveq #1,%d0 - bra po_is_out -1: move.l %d2,%d0 lsl.l #2,%d0 add.l %d2,%d0 | track * 5 @@ -3786,8 +4543,169 @@ po_is_out: movem.l (%sp),%d1/%d3/%a0/%a1 lea 16(%sp),%sp tst.l %d0 +po_is_rts: rts +| ---- THE FM SYNTH MACHINE (the machine list's sixth row, machine.s; after Modwerk's +| dedicated chooser, MIT, Modwerk contributors). A chosen track is stored as FLEX with +| "FM", 1 in the first three bytes of its NEIGHBOR PLAYBACK column (Part + 0x8ed80 + +| 0x3c + 30 * track, unused while the track is FLEX; the battery-RAM shadow the same). +| po_is_synth answers 1 for it as for a marker track; the marker still works. +| po_sig: a0 = the Part (the bank blob + part * 6322), d0 = track -> d0 = 1 when it is +| FLEX and signed, else 0; tst.l done. Preserves every other register. +po_sig: + lea -8(%sp),%sp + movem.l %d1/%a1,(%sp) + movea.l %a0,%a1 + adda.l #MACH_OFF,%a1 + mvz.b (%a1,%d0.l),%d1 | the track's machine + subq.l #1,%d1 + bne po_sig_no | not FLEX + mulu.w #30,%d0 + movea.l %a0,%a1 + adda.l %d0,%a1 + adda.l #SIG_OFF,%a1 + mvz.w (%a1),%d1 + cmpi.l #0x464d,%d1 | "FM" + bne po_sig_no + mvz.b 2(%a1),%d1 + subq.l #1,%d1 | version 1 + bne po_sig_no + moveq #1,%d0 + bra po_sig_out +po_sig_no: + moveq #0,%d0 +po_sig_out: + movem.l (%sp),%d1/%a1 + lea 8(%sp),%sp + tst.l %d0 + rts +| po_signed: d2 = track of the current Part -> d0 = 1 when FM SYNTH is chosen (2.11: 2 +| when SY DRUM is: the engine kind of the track's machine-list row); tst.l done. +| Preserves every other register. +po_signed: + lea -8(%sp),%sp + movem.l %d1/%a0,(%sp) + movea.l PART_PTR,%a0 + mvz.b PART_IDX,%d0 + move.l #6322,%d1 + muls.l %d1,%d0 + adda.l %d0,%a0 | the Part + move.l %d2,%d0 + .if HAVE_SYDRUM + bsr po_sigkind + .else + bsr po_sig + .endif + movem.l (%sp),%d1/%a0 + lea 8(%sp),%sp + tst.l %d0 + rts + .if HAVE_SYDRUM +| po_sigkind (2.11): a0 = the Part, d0 = track -> d0 = 1 for FLEX with "FM", 1, 2 for FLEX +| with "SY", 1 (SY DRUM, sy-drum/), else 0; tst.l done. Preserves every other register. +po_sigkind: + lea -8(%sp),%sp + movem.l %d1/%a1,(%sp) + movea.l %a0,%a1 + adda.l #MACH_OFF,%a1 + mvz.b (%a1,%d0.l),%d1 | the track's machine + subq.l #1,%d1 + bne po_sk_no | not FLEX + mulu.w #30,%d0 + movea.l %a0,%a1 + adda.l %d0,%a1 + adda.l #SIG_OFF,%a1 + mvz.b 2(%a1),%d1 + subq.l #1,%d1 | version 1 + bne po_sk_no + mvz.w (%a1),%d1 + moveq #1,%d0 + cmpi.l #0x464d,%d1 | "FM" + beq po_sk_out + moveq #2,%d0 + cmpi.l #0x5359,%d1 | "SY" + beq po_sk_out +po_sk_no: + moveq #0,%d0 +po_sk_out: + movem.l (%sp),%d1/%a1 + lea 8(%sp),%sp + tst.l %d0 + rts +| po_ident (2.11): d2 = track of the current Part -> d0 = the machine it plays: 1 FM (FM +| SYNTH chosen, or FLEX with an FMSYNTH* / SYNTH* slot), 2 SY DRUM (chosen), 0 a sample +| track; tst.l done. Preserves every other register. +po_ident: + bsr po_signed + bne po_id_rts + bsr po_is_synth | (not chosen) the FM marker: 1 +po_id_rts: + rts +| po_is_fm (2.11): d2 -> d0 = 1 when the track plays FM (po_ident 1), else 0; tst.l done -- +| what owns VOIC / CHRD, chords and the paraphonic LEG modes: SY DRUM is a mono drum voice +| (the development line's po_is_fm). Preserves every other register. +po_is_fm: + bsr po_ident + subq.l #1,%d0 + seq %d0 + andi.l #1,%d0 + rts + .endif + +| ---- 0x4000d514 (jmp, 8 B): the frame builder's second supplier call takes the per-track +| supplier from its list (`moveal %a4@+,%a0`), caches it (`movel %a0,%a3@+`) and pushes +| the end argument 16 (`pea 16`). An EMPTY FLEX voice gets stock's silent supplier there; +| a chosen FM SYNTH track gets sy_render, which needs no sample. d3 = track. +po_fmsource: + movea.l (%a4)+,%a0 | displaced: the supplier + lea -8(%sp),%sp + movem.l %d0/%d2,(%sp) + move.l %d3,%d2 + bsr po_signed + beq po_fs_keep + lea sy_render,%a0 +po_fs_keep: + movem.l (%sp),%d0/%d2 + lea 8(%sp),%sp + move.l %a0,(%a3)+ | displaced: cached for the next first call + pea 16.w | displaced: the second call's end argument + jmp FMSRC_RET + +| ---- the kind table's FLEX START callback 0x400d6458 (stock 0x4000f450, a SymbolRef): +| po_fmstart(track). A chosen FM SYNTH track starts without consulting a sample: the CF +| voice is marked active (0xff: the stock transport and kill own it, po_kill sees it end) +| and its source kind FLEX. The answer is the stock callback's: 0x100 (the DSP's cold +| start) for a voice that was not running, 0 for one that runs on (stock answers 0 for a +| START that continues its voice). With 0x100 on every START the DSP restarted its stream +| under the engine's warm path: +40..57 dB at every retrigger on a sounding note (the +| note-start click table on a sample-free track; Modwerk's transport returned 0x100 +| always). Any other FLEX track: the stock callback, its ABI untouched. +po_fmstart: + lea -8(%sp),%sp + movem.l %d2/%a0,(%sp) + move.l 12(%sp),%d2 | track + bsr po_signed + beq po_fst_stock + move.l #VOICE_STRIDE,%d0 + muls.l %d2,%d0 + lea VOICE_BASE,%a0 + add.l %d0,%a0 + move.l #0x100,%d0 | a voice that was not running: the DSP's cold start + tst.b (%a0) + beq po_fst_cold + moveq #0,%d0 | the voice runs on: 0, as stock answers a START that continues its +po_fst_cold: | voice -- the DSP keeps its stream, the engine's warm path stays seamless + move.b #0xff,(%a0) | active + move.b #1,20(%a0) | source kind FLEX + movem.l (%sp),%d2/%a0 + lea 8(%sp),%sp + rts +po_fst_stock: + movem.l (%sp),%d2/%a0 + lea 8(%sp),%sp + jmp FLEX_START + | ---- po_slot_marker: d0 = a FLEX slot (0-based) -> d0 = 1 when the slot's sample | is named FMSYNTH* / SYNTH* (the settings record's path, loaded into flex RAM or | not), else 0 (a slot above 127 -- none, or a recorder buffer -- is never one); @@ -3842,7 +4760,11 @@ po_sm_out: | is 2..4 (the panel keys' qz_polytrack rule: the release of one note among | several ends nothing), else 0; tst.l done. Preserves every other register. po_polytrack: + .if HAVE_SYDRUM + bsr po_is_fm | (2.11) VOIC is FM's: a SY DRUM track is never paraphonic + .else bsr po_is_synth + .endif beq po_pt_ret move.l %d1,-(%sp) move.l %a0,-(%sp) @@ -4176,7 +5098,7 @@ po_rel_out: | a0 = 0x46104d15) -- the frame builder's per-track copy of the DSP command byte | 0x46104d15[t] into the packer's nibble byte 0x46104d0c[t] (0x4000c642), the one | funnel every START form passes on its way to the DSP and the packer. THE CAUSE -| (BUILD 37, 29 Sep 2026; po_rtlog's ring, root29w/run_f37a.log): a sequencer trig +| (BUILD 37, 29 Sep 2026; read back from po_rtlog's ring): a sequencer trig | on a track whose voice still sounds is posted by the builder's own sequencer | path (0x4000b906: the trig record's byte +62, then the OR-0x10 of 0x4000b9aa / | 0x4000bd74 / 0x4000bdc0) as 0x10 | n -- the CF START bit 4 with the trig's @@ -4195,6 +5117,11 @@ po_rel_out: | warm: phase-continuous from the level reached). A silent synth track (S_ON 0) | and every sample track keep stock's byte. po_rtlog counts the passes and the | rewrites and rings the START bytes (the rig peeks it at po_clock + 32). +| 2.10: a byte with NO START bit on a synth track with a voice on (a trigless +| trig -- a recorded LEG MONO step, a lock trig -- or a release) keeps its flags +| and loses its sub-frame position n (po_retrig_nost): the packer split every +| following frame at n and the stock renderer's unpinned first call walked the +| marker to its end, which ended the stock voice and cut the note. po_retrig: movea.l 114(%sp),%a1 | displaced: a1 = the track lea -24(%sp),%sp @@ -4205,19 +5132,29 @@ po_retrig: mvz.b (%a0,%a1.l),%d1 | the DSP command byte posted this frame lea po_rtlog(%pc),%a2 addq.l #1,RT_CALLS(%a2) - move.l %d1,%d2 - andi.l #0x30,%d2 - beq po_retrig_out | no START of any form (a release, nothing): stock's byte stands move.l %d4,%d2 lsl.l #7,%d2 lea sy_state(%pc),%a3 add.l %d2,%a3 | the track's engine record move.l %d1,%d3 | the byte as posted + move.l %d1,%d2 + andi.l #0x30,%d2 + beq po_retrig_nost | no START of any form tst.b S_ON(%a3) beq po_retrig_log | not a synth track with a voice on: stock's byte stands moveq #0x30,%d1 move.b %d1,(%a0,%a1.l) | the clean form: a CF START at the frame's first sample addq.l #1,RT_FIXED(%a2) + bra po_retrig_log +po_retrig_nost: | (2.10) no START: a release, or a TRIGLESS trig (a recorded legato step, + move.l %d1,%d2 | a lock trig) with its sub-frame position n in the low nibble. The + andi.l #0x0f,%d2 | packer keeps n and splits EVERY following frame at it until the + beq po_retrig_out | next event; the first call [0,n) runs the stock renderer on the + tst.b S_ON(%a3) | voice unpinned (sy_render pins the marker on the frame's second + beq po_retrig_out | call only), the marker's position walks n samples a frame and the + andi.l #0xf0,%d1 | stock voice ends at its end: the note cut to silence 2..20 ms + move.b %d1,(%a0,%a1.l) | later. On a synth track with a voice on, n := 0 (nothing starts: + addq.l #1,RT_FIXED(%a2) | there is nothing to place); sample tracks keep stock's byte po_retrig_log: move.l RT_HEAD(%a2),%d2 addq.l #1,RT_HEAD(%a2) @@ -4240,44 +5177,13 @@ po_retrig_out: move.b (%a0,%a1.l),%d0 | displaced: the byte (rewritten or not) for the packer's nibble byte jmp 0x4000c63c -| ---- po_stop: 0x4000b2c8, the frame builder's `clrl 0x46c80350` (6 bytes) -- the -| sequencer's STOP / restart word (3, or 1) consumed: the builder has just turned -| it into the DSP-wide all-off command byte (0x10 / 0x30 into 0x46104d14), with no -| per-track mailbox and no CF voice kill (stage 1). The DSP's envelope no longer -| ends anything (plan B), so the engine ends everything here: every synth track's -| mono voice takes the released flag (S_ON bit 1 -> po_mono_env's release) and -| every sounding paraphonic voice releases (state 2 -> po_frame's release). The -| tail fades under the DSP's all-off (inaudible) and the levels reach 0, so the -| next START is cold. d0 is dead here (reloaded at 0x4000b2ce); a0 / d1 saved. -po_stop: - clr.l GLOBAL_WORD | displaced - lea -16(%sp),%sp - movem.l %d1/%d2/%a0/%a1,(%sp) - lea sy_state(%pc),%a0 - lea po_voices(%pc),%a1 - moveq #8,%d1 -po_stop_t: - btst #0,S_ON(%a0) - beq po_stop_p - bset #1,S_ON(%a0) - bset #2,S_ON(%a0) | STOPPED: REL INF releases at the 1 ms floor (po_mono_env, po_frame's T_RK) -po_stop_p: - moveq #4,%d2 -po_stop_v: - mvz.b V_STATE(%a1),%d0 - cmpi.l #1,%d0 - bne po_stop_v1 - move.b #2,V_STATE(%a1) -po_stop_v1: - lea V_STRIDE(%a1),%a1 - subq.l #1,%d2 - bne po_stop_v - lea ST_STRIDE(%a0),%a0 - subq.l #1,%d1 - bne po_stop_t - movem.l (%sp),%d1/%d2/%a0/%a1 - lea 16(%sp),%sp - jmp 0x4000b2ce +| ---- (2.12) po_stop is gone. 2.9..2.11 hooked the frame builder's `clrl 0x46c80350` at +| 0x4000b2c8 as "the sequencer's STOP / restart word, after the DSP all-off" and released every +| engine voice there (S_ON bits 1 and 2, the paraphonic voices to state 2, po_pend cleared). That +| word is the METRONOME's click (GLOBAL_WORD above) and the byte the builder posts from it is the +| click, not an all-off: with the metronome on, every held key and chord was released at the next +| beat (REL INF: cut within 5 ms) while the key was still down. STOP ends the voices at the stock +| VOICE KILL (po_kill, below; BUILD 33), which never needed the global word. 0x4000b2c8 is stock. | ---- po_kill: 0x4000685c, the stock VOICE KILL 0x40006820(t)'s CF voice-byte | clear `clrb %d0; moveb %d0,%a1@(0,%a0:l)` (6 bytes; BUILD 33). The kill's @@ -4294,9 +5200,9 @@ po_stop_v1: | synth playing), the four paraphonic voices freed (po_free: state 0, V_GAIN / | V_GPREV 0) with V_KEY / V_HOLD := 0. The next START is cold (sy_cold: | S_GPREV == 0 -> phase 0 from 0). A sample track's record is zeroed the same -| way (S_ON was 0 already). po_stop (0x4000b2c8) stays as belt-and-braces for a -| STOP that posts the global word without a kill: on a killed track it finds -| S_ON 0 and does nothing. d0 / a0 are dead here (reloaded at 0x40006862 / +| way (S_ON was 0 already). (2.9..2.11 also hooked 0x4000b2c8 as a STOP, po_stop: +| that site is the metronome's click, removed in 2.12; this kill is the STOP +| path.) d0 / a0 are dead here (reloaded at 0x40006862 / | 0x40006866), a2 is set at 0x40006868; d1 / d2 / a1 preserved. po_kill: clr.b %d0 | displaced @@ -4751,7 +5657,11 @@ po_amp_kind: tst.b MASTER_ON | track 8 as the MASTER track: not ours bne po_ak_out po_ak_audio: + .if HAVE_SYDRUM + bsr po_is_fm | (2.11) 2 sample / SY DRUM (OFF / MONO), 3 FM (OFF / MONO / POLY) + .else bsr po_is_synth + .endif addq.l #2,%d0 | 2 sample, 3 synth po_ak_out: tst.l %d0 @@ -4973,7 +5883,7 @@ po_assign: move.l 24(%sp),%d0 | the old machine move.l 20(%sp),%d2 | the track move.l %a3,%d1 | the new machine - bsr po_became + jsr ml_became | 2.12 (machine.s): a machine change -> the new machine's init movem.l (%sp),%d1-%d4 lea 16(%sp),%sp jmp ASSIGN_RET @@ -5056,7 +5966,12 @@ po_flex_slot: | ---- po_fine_reset: RATE := 64 (FINE 0c) for track d2 -- the Part's FLEX PLAYBACK | byte (PLAY_SLOTS + 6 + 3), its battery-RAM shadow and the live lane's flat slot -| 3, as the page's knob writes them. Preserves every register. +| 3, as the page's knob writes them, and the lane's RATE value word (CV_WORDS + 64 * +| track + 6, byte << 8) with the slew counter of lane bytes 0..3 cleared, as stock's +| reload of a track from its Part writes them (0x40001f18). The frame builder hands +| the DSP and sy_render that word, not the lane byte, and refreshes it from the Part +| only at a trig from another bank or Part: a track that had played a sample at RATE +| 127 kept FINE +63c under a page reading 0c (8 Oct 2026). Preserves every register. po_fine_reset: lea -16(%sp),%sp movem.l %d0-%d1/%a0-%a1,(%sp) @@ -5078,6 +5993,15 @@ po_fine_reset: muls.l %d2,%d1 lea CURVALS,%a0 move.b %d0,CV_RATE(%a0,%d1.l) | the live lane + move.l %d2,%d1 + lsl.l #6,%d1 + lea CV_WORDS,%a0 + adda.l %d1,%a0 + move.w #0x4000,2*CV_RATE(%a0) | its value word + move.l %d2,%d1 + lsl.l #5,%d1 + lea CV_SLEW,%a0 + clr.l (%a0,%d1.l) | lane bytes 0..3: the next frame takes the lane's values movem.l (%sp),%d0-%d1/%a0-%a1 lea 16(%sp),%sp rts @@ -5095,35 +6019,41 @@ po_fine_reset: | touches the Part: the machine and the slot byte do not change, none of the | three assigner sites runs, and the track has become a synth track by its | slot's FILE changing. So the change of a FLEX slot's file is the fourth site: -| the slot's marker state is read before the path write and after it, and when -| it went non-marker (or empty) -> marker, every FLEX track whose FLEX slot is -| this slot became a synth track: RATE := 64 for each (po_fine_reset). A marker -| replaced by a marker changes nothing (its synth tracks keep their FINE), a -| marker replaced by a sample, a STATIC slot, a recorder buffer: nothing. +| the slot's marker state before the load and after the path write. 2.12: "before" +| is read at the browser's first unload call (po_unload, 0x40022732): the unload +| 0x40025288 clears the slot's whole settings record (memset 0x448), and the load +| callback 0x400226f4 calls it before the select (which calls it again before +| this path write), so the file the slot had is gone here (until 2.12 every load +| read "not a marker" before it). The machine change (machine.s ml_reset): when the +| marker state changed -- a sample (or nothing) +| replaced by a marker, or a marker by a sample --, every FLEX track whose FLEX +| slot is this slot and which is not chosen in the list (a row's signature: that +| machine plays, whatever the slot holds) changed machine, FLEX <-> FM SYNTH: it +| gets the new machine's init, and the Part is marked edited as stock's machine +| write marks it (po_dirty). A marker replaced by a marker, a sample by a sample, +| a STATIC slot, a recorder buffer: nothing. (Until 2.12 only FINE was reset, and +| only on the way to a marker.) po_loadsel: - lea -12(%sp),%sp - movem.l %d2/%d6-%d7,(%sp) | 12 B: the return address at 12(sp), the arguments at 16 / 20 / 24 - moveq #0,%d7 - moveq #1,%d0 - cmp.l %d4,%d0 - bne po_ls_call | not the FLEX list - move.l %d3,%d0 - bsr po_slot_marker - move.l %d0,%d7 | d7 := the slot's file WAS a marker + lea -16(%sp),%sp + movem.l %d2/%d5-%d7,(%sp) | 16 B: the return address at 16(sp), the arguments at 20 / 24 / 28 + mvz.b po_ls_was(%pc),%d7 | d7 := the slot's file WAS a marker (po_unload, just before) + lea po_ls_was(%pc),%a0 + clr.b (%a0) po_ls_call: - move.l 24(%sp),-(%sp) | the path - move.l 24(%sp),-(%sp) | the format - move.l 24(%sp),-(%sp) | the record + move.l 28(%sp),-(%sp) | the path + move.l 28(%sp),-(%sp) | the format + move.l 28(%sp),-(%sp) | the record jsr LOADSEL_SPRINTF | stock: the path into the record lea 12(%sp),%sp moveq #1,%d0 cmp.l %d4,%d0 bne po_ls_out - tst.l %d7 - bne po_ls_out | a marker already: its synth tracks keep their FINE move.l %d3,%d0 bsr po_slot_marker - beq po_ls_out | not a marker now: no synth track + cmp.l %d7,%d0 + beq po_ls_out | its marker state unchanged: its tracks play what they played + move.l %d0,%d5 | the machine its FLEX tracks play now: 1 FM SYNTH, 0 FLEX + moveq #0,%d7 | (no track reset yet) move.l %d3,%d6 | the slot moveq #0,%d2 | the track po_ls_track: @@ -5144,17 +6074,77 @@ po_ls_track: mvz.b (%a0,%d0.l),%d0 | its FLEX slot cmp.l %d6,%d0 bne po_ls_next - bsr po_fine_reset | d2 = the track: FINE 0c (preserves every register) + moveq #1,%d0 + move.l #255,%d1 + jsr ml_kindof | FLEX with no slot: a row's machine when it is chosen in the list + bne po_ls_next | chosen: that machine plays, unchanged + move.l %d5,%d0 + moveq #1,%d1 + jsr ml_reset | d2 = the track: the init of the machine it plays now + moveq #1,%d7 po_ls_next: addq.l #1,%d2 moveq #8,%d0 cmp.l %d2,%d0 bne po_ls_track + tst.l %d7 + beq po_ls_out + bsr po_dirty | a track was reset: the Part is edited po_ls_out: - movem.l (%sp),%d2/%d6-%d7 - lea 12(%sp),%sp + movem.l (%sp),%d2/%d5-%d7 + lea 16(%sp),%sp rts +| ---- 0x40022732 (jsr, 6 B): the browser's load callback 0x400226f4 calls the slot unload +| 0x40025288(machine, slot) before the select 0x40022610 (which unloads it again and then +| writes the new path); replaced by `jsr po_unload` (2.12): for the FLEX list the slot's +| marker state is kept in po_ls_was for po_loadsel, then the stock unload runs with the +| caller's arguments (a tail jump: it returns to the callback). Clobbers d0, a0. + .set UNLOAD, 0x40025288 | the stock slot unload (machine, slot): its settings record cleared +po_unload: + move.l 4(%sp),%d0 | the machine + subq.l #1,%d0 + beq po_ul_flex | the FLEX list + moveq #0,%d0 + bra po_ul_store +po_ul_flex: + move.l 8(%sp),%d0 | the slot + bsr po_slot_marker | d0 := 1 when its file is a marker +po_ul_store: + lea po_ls_was(%pc),%a0 + move.b %d0,(%a0) + jmp UNLOAD + +| ---- po_dirty (2.12): the current Part is marked edited as stock's machine write marks it +| (the machine window's apply path, 0x4007982c..0x40079866): its unsaved bit in the bank +| (+ 0x95048) and in battery RAM (0x100b145e), the bank's write flag (+ 0x9b332) and its +| battery-RAM copy (0x100f8598), then the stock clipboard / undo reset 0x40027e00 the same +| path calls. Clobbers d0, d1, a0, a1. + .set PART_UNSAVED, 0x95048 | the bank blob + this: bit p = Part p edited since its SAVE + .set PART_UNSAVED_BR, 0x100b145e | ... its battery-RAM copy + .set BANK_WRITE, 0x9b332 | the bank blob + this: the bank is written by the next bank write (a long) + .set BANK_WRITE_BR, 0x100f8598 | ... its battery-RAM copy + .set UNDO_RESET, 0x40027e00 | the stock clipboard / undo state reset after a Part edit +po_dirty: + mvz.b PART_IDX,%d1 + moveq #0,%d0 + bset %d1,%d0 | the Part's bit + movea.l PART_PTR,%a0 + adda.l #PART_UNSAVED,%a0 + mvz.b (%a0),%d1 + or.l %d0,%d1 + move.b %d1,(%a0) | the bank's unsaved bits + lea PART_UNSAVED_BR,%a1 + mvz.b (%a1),%d1 + or.l %d0,%d1 + move.b %d1,(%a1) | ... their battery-RAM copy + movea.l PART_PTR,%a0 + adda.l #BANK_WRITE,%a0 + moveq #1,%d0 + move.l %d0,(%a0) | the bank is written by the next bank write + move.l %d0,BANK_WRITE_BR + jmp UNDO_RESET + | ---- data ---------------------------------------------------------------------- sy_name: .ascii "SYNTH" @@ -5256,11 +6246,11 @@ po_gmax: .long 32768, 32768, 23170, 18919, 16384 | ---- po_atk: AMP ATK raw -> the attack step a frame, Q15 (full = 32768) ---------- -| The DSP's attack as measured (stage 1, root29o/ana_env.txt): a LINEAR ramp to +| The DSP's attack as measured (stage 1, stock renders): a LINEAR ramp to | full in t = 3.85 ms x 2^(ATK / 8.53) (8: 7.5 ms, 16: 15, 32: 50, 64: 700, 96: | 9.45 s); step = 32768 / max(16, t / 0.3628 ms) a frame -- the 16-frame ramp | (5.8 ms, ATK 0..8) is the floor, and the Q15 step's floor of 1 makes ATK >= 91 -| an 11.9 s ramp (the DSP's 96 is 9.5 s, its 127 anomalous). root29p/gen_env_tables.py. +| an 11.9 s ramp (the DSP's 96 is 9.5 s, its 127 anomalous). Table generated from that law. po_atk: .short 2048, 2048, 2048, 2048, 2048, 2048, 1896, 1748 .short 1612, 1486, 1370, 1263, 1165, 1074, 990, 913 @@ -5285,7 +6275,7 @@ po_atk: | 126: 9 s), 127 = INF (k = 0); tau is floored at 1 ms (REL 0..15) -- the DSP's | REL 0 is a one-sample dead cut (Tim's click), the engine's is -20 dB in 2.3 ms, | -40 dB in 4.6 ms. (Until plan B: tau = 5 ms x 1000^(rel / 126), 134 ms at REL -| 60 where the DSP takes 38.) root29p/gen_env_tables.py. +| 60 where the DSP takes 38.) Table generated from that law. po_relk: .short 19941, 19941, 19941, 19941, 19941, 19941, 19941, 19941 .short 19941, 19941, 19941, 19941, 19941, 19941, 19941, 19941 @@ -5367,6 +6357,8 @@ sy_state: | 8 tracks x 128 bytes .fill 8 * ST_STRIDE, 1, 0 po_voices: | 8 tracks x 4 voices x 64 bytes .fill 8 * 4 * V_STRIDE, 1, 0 +po_pend: | (2.10) 8 tracks x 4 voices x 16 bytes: the notes waiting for a fading voice (po_st_xf) + .fill 8 * 4 * 16, 1, 0 po_seq: | the allocation stamp .long 0 po_chord: | 8 tracks x 32 bytes: the fingered chord being recorded (po_keyrec / po_keyrel) @@ -5397,83 +6389,10 @@ po_pgdesc_buf: | the FM SYNTH PLAYBACK descriptor clon .fill FLEX_DESC_LEN, 1, 0 po_amp_built: .byte 0 +po_ls_was: | (2.12) the browser's slot held a marker before its load (po_unload -> po_loadsel) + .byte 0 .align 4 po_ampdesc_buf: | the AMP descriptor clone with LEG in slot 11 (built on first use, po_amp_desc) .fill AMP_DESC_LEN, 1, 0 .align 4 po_end: - -| d0 := FM/1 signed FLEX track d2. Preserve all other registers and test d0. -fm_is_native: - lea -12(%sp),%sp - movem.l %d1/%a0-%a1,(%sp) - movea.l PART_PTR,%a0 - mvz.b PART_IDX,%d0 - move.l #6322,%d1 - muls.l %d1,%d0 - adda.l %d0,%a0 - adda.l #MACH_OFF,%a0 - mvz.b (%a0,%d2.l),%d0 - cmpi.b #1,%d0 - suba.l #MACH_OFF,%a0 - bne 1f - move.l %d2,%d0 - mulu.w #30,%d0 - adda.l %d0,%a0 - adda.l #0x8edbc,%a0 - mvz.w (%a0),%d0 - cmpi.w #0x464d,%d0 - bne 1f - mvz.b 2(%a0),%d0 - cmpi.b #1,%d0 - bne 1f - moveq #1,%d0 - bra 2f -1: moveq #0,%d0 -2: movem.l (%sp),%d1/%a0-%a1 - lea 12(%sp),%sp - tst.l %d0 - rts - - -| Source supplier dispatch, frame builder's second call (track in d3). -| An empty FLEX voice keeps stock's silent supplier in its per-track cache. -| A signed FM track supplies its own audio without a sample initialization. - .global fm_source_dispatch -fm_source_dispatch: - movea.l (%a4)+,%a0 | displaced supplier load - lea -8(%sp),%sp - movem.l %d0/%d2,(%sp) - move.l %d3,%d2 - bsr fm_is_native - beq 1f - lea sy_render,%a0 -1: movem.l (%sp),%d0/%d2 - lea 8(%sp),%sp - move.l %a0,(%a3)+ | displaced: cache for the next first call - pea 16.w | displaced second-call end argument - jmp 0x4000d51c - -| Dedicated FM START callback. No resource or sample settings are consulted. -| The native supplier owns its samples; 0x100 requests the DSP's cold start. -| Ordinary FLEX retains the complete stock callback and ABI. - .global fm_start -fm_start: - lea -8(%sp),%sp - movem.l %d2/%a0,(%sp) - move.l 12(%sp),%d2 - bsr fm_is_native - beq 1f - move.l #VOICE_STRIDE,%d0 - muls.l %d2,%d0 - lea VOICE_BASE,%a0 - add.l %d0,%a0 - move.b #0xff,(%a0) | stock transport/kill owns this active voice - move.b #1,20(%a0) | FLEX source kind - move.l #0x100,%d0 - movem.l (%sp),%d2/%a0 - lea 8(%sp),%sp - rts -1: movem.l (%sp),%d2/%a0 - lea 8(%sp),%sp - jmp 0x4000f450 diff --git a/sdk/tests/test_synth_release.py b/sdk/tests/test_synth_release.py index 9c00a3fc3..1915f6acf 100644 --- a/sdk/tests/test_synth_release.py +++ b/sdk/tests/test_synth_release.py @@ -7,28 +7,32 @@ APP = Path(__file__).resolve().parents[2] ORIGINAL = json.loads((APP/'sdk/imports/synth-949f3be.json').read_text()) -RECORD = json.loads((APP/'sdk/imports/synth-fixes-273-264.json').read_text()) +PREVIOUS = json.loads((APP/'sdk/imports/synth-fixes-273-264.json').read_text()) +RECORD = json.loads((APP/'sdk/imports/synth-ca13ed8.json').read_text()) FOLDER = APP/RECORD['folder'] sha = lambda path: hashlib.sha256(path.read_bytes()).hexdigest() class SynthRelease(unittest.TestCase): def test_source_identities_and_complete_notices(self): - files = ORIGINAL['files'] + ORIGINAL['localFiles'] - updates = {item['path']: item for item in RECORD['files']} self.assertEqual(RECORD['originalImport'], 'synth-949f3be.json') - self.assertEqual(RECORD['status'], 'released') - self.assertEqual(len(files), len({item['path'] for item in files})) - for item in files: - update = updates.get(item['path']) - if update: - self.assertEqual(update['previousSha256'], item['vendoredSha256']) - self.assertEqual(sha(FOLDER/item['path']), update['draftSha256'] if update else item['vendoredSha256'], item['path']) - if 'revision' in item: - self.assertRegex(item['revision'], r'^[a-f0-9]{40}$') + self.assertEqual(RECORD['previousUpdate'], 'synth-fixes-273-264.json') + self.assertEqual(PREVIOUS['version'], '0.1.2-experimental') + self.assertRegex(RECORD['authorPin']['revision'], r'^[a-f0-9]{40}$') + files = {item['path']: item for item in RECORD['files']} + on_disk = sorted(str(p.relative_to(FOLDER)) for p in FOLDER.rglob('*') if p.is_file() and '__pycache__' not in p.parts and p.name != '.DS_Store') + self.assertEqual(sorted(files), on_disk) + for path, item in files.items(): + self.assertEqual(sha(FOLDER/path), item['sha256'], path) + if 'sourceSha256' in item: self.assertRegex(item['sourceSha256'], r'^[a-f0-9]{64}$') if not item.get('changes'): - self.assertEqual(item['sourceSha256'], item['vendoredSha256']) + self.assertEqual(item['sourceSha256'], item['sha256'], path) + for path in ['upstream/synth/poly.s', 'upstream/synth/page.s', 'upstream/synth/machine.s', 'upstream/synth/engine_abi.inc', 'upstream/synth/README.md', 'upstream/LICENSE']: + self.assertEqual(files[path]['revision'], RECORD['authorPin']['revision']) + self.assertEqual(files[path]['sourceSha256'], files[path]['sha256'], path + ' must be the author file byte for byte') + for path in RECORD['removed']: + self.assertFalse((FOLDER/path).exists(), path) license_text = (FOLDER/'LICENSE').read_text() for name in ['upstream/LICENSE', 'OCTABAM-LICENSE']: self.assertIn((FOLDER/name).read_text().strip(), license_text) @@ -43,51 +47,41 @@ def test_stock_expectations_are_lazy_and_no_precompiled_page_is_carried(self): if name.endswith('_STOCK'): self.assertIsInstance(value, ast.Call, name) self.assertEqual(value.func.id, 'stock_guard', name) + top = (FOLDER/'manifest.py').read_text() + self.assertNotIn('fromhex("', top) guards = json.loads((FOLDER/'stock-guards.json').read_text())['guards'] - self.assertGreater(len(guards), 30) + self.assertGreater(len(guards), 60) self.assertTrue(any(item['address'] == 0x4000d514 and item['length'] == 8 for item in guards)) for guard in guards: self.assertEqual(set(guard), {'path', 'address', 'length', 'sha256'}) self.assertRegex(guard['sha256'], r'^[a-f0-9]{64}$') - def test_experimental_release_does_not_claim_hardware_or_timing(self): + def test_measured_release_keeps_hardware_attribution_separate(self): doc = json.loads((FOLDER/'octamod.module.json').read_text()) self.assertEqual(doc['version'], RECORD['version']) self.assertNotIn('build', doc) - self.assertEqual(doc['tests']['hardwareStatus'], 'untested') - self.assertNotIn('qualification', doc['tests']) - self.assertTrue((APP/'sdk/octabam/modules/synth').exists()) - for name in ['sdk/catalog.json','src/catalog/module-documents.json']: + self.assertNotIn('releaseWaiver', doc['tests']) + q = doc['tests']['qualification'] + self.assertEqual(q['moduleVersion'], doc['version']) + self.assertEqual(q['hardware']['imageSha256'], q['imageSha256']) + self.assertEqual(q['hardware']['sourceRevision'], doc['tests']['evidenceRevision']) + for name in ['sdk/catalog.json', 'src/catalog/module-documents.json']: self.assertIn('synth', {item['id'] for item in json.loads((APP/name).read_text())['modules']}) - self.assertNotIn('synth', {item['id'] for item in json.loads((APP/'sdk/module-qualification-baseline.json').read_text())['modules']}) approval = json.loads((APP/'sdk/synth-build-approval.json').read_text()) - self.assertEqual(approval['kind'], 'owner-approved-update') - self.assertEqual(approval['version'], doc['version']) - self.assertEqual(set(approval['waived']), {'current-build-hardware', 'chip-worst-case-cycles', 'complete-memory-bounds'}) - self.assertIsNone(doc['resources']['processing']['value']) - report = json.loads((FOLDER/'evidence/regressions.json').read_text()) - self.assertEqual(report['moduleVersion'], doc['version']) - for regression in report['releaseRegressions'].values(): - self.assertEqual(regression['status'], 'passed') - self.assertEqual(regression['imageSha256'], report['patchedBrowser']['mainSha256']) - self.assertRegex(regression['emulatorSha256'], r'^[a-f0-9]{64}$') - for file, fingerprint in report['compiledCode'].items(): - self.assertEqual(sha(FOLDER/file), fingerprint) - self.assertEqual(report['audio']['mono']['patched']['finalHalfSecondPeak'], 0) - self.assertLessEqual(report['audio']['fourVoices']['patched']['finalHalfSecondPeak'], 2) + self.assertEqual(approval['version'], '0.1.2-experimental') + self.assertEqual((FOLDER/'evidence/release-0.1.2/build-approval.json').read_text(), (APP/'sdk/synth-build-approval.json').read_text()) + self.assertTrue((FOLDER/'evidence/release-0.1.1/owner-hardware.md').exists()) capture = json.loads((FOLDER/'media/capture.json').read_text()) image = capture['imageSha256'] - screenshots = [media for media in doc['media'] if 'otUi' in media] - self.assertEqual(len(screenshots), 7) - for media in screenshots: + self.assertEqual(image, q['imageSha256']) + self.assertEqual(capture['moduleVersion'], doc['version']) + self.assertEqual(capture['sourceSha256'], q['sourceSha256']) + for media in [media for media in doc['media'] if 'otUi' in media]: self.assertEqual(media['otUi']['imageSha256'], image) self.assertEqual(sha(FOLDER/media['path']), capture['screenshots'][Path(media['path']).name]) - self.assertEqual(image, report['patchedNativeMainSha256']) - self.assertEqual(capture['moduleVersion'], doc['version']) - self.assertEqual(capture['sourceSha256'], approval['sourceSha256']) composition = json.loads((APP/'sdk/native-comparisons/synth.json').read_text()) self.assertEqual(composition['moduleVersion'], doc['version']) - self.assertEqual(composition['moduleSourceSha256'], approval['sourceSha256']) + self.assertEqual(composition['moduleSourceSha256'], q['sourceSha256']) self.assertEqual(composition['summary']['mismatches'], 0) for path in FOLDER.rglob('*'): self.assertFalse(path.is_symlink()) diff --git a/src/catalog/module-qualification.test.ts b/src/catalog/module-qualification.test.ts index bc62fec6d..1797de55a 100644 --- a/src/catalog/module-qualification.test.ts +++ b/src/catalog/module-qualification.test.ts @@ -89,7 +89,7 @@ describe('module qualification hard gates',()=>{ // Generated web documents include display-only baseline resource estimates. // Qualify the source manifest, as publication validation does. const source = parseModuleDocument(JSON.parse(readFileSync(resolve(folder, 'octamod.module.json'), 'utf8'))) - expect(await requireFolderQualification(folder, source, frozen)).toBe(unchanged ? 'retained' : source.tests.retainedEvidence ? 'retained-evidence' : ['midi-scenes', 'miniverb', 'synth', 'usb-audio-out-tracks-main-cue', 'euclid', 'mute-modes', 'recorder-loop-fix', 'analog-bassdrum', 'airwindows-chorus', 'poly8', 'quantizer', 'everb'].includes(module.id) ? 'owner-approved-update' : 'qualified') + expect(await requireFolderQualification(folder, source, frozen)).toBe(unchanged ? 'retained' : source.tests.retainedEvidence ? 'retained-evidence' : ['midi-scenes', 'miniverb', 'usb-audio-out-tracks-main-cue', 'euclid', 'mute-modes', 'recorder-loop-fix', 'analog-bassdrum', 'airwindows-chorus', 'poly8', 'quantizer', 'everb'].includes(module.id) ? 'owner-approved-update' : 'qualified') }) } it('binds an owner-approved update to its exact version and native source, never to verified hardware',async()=>{ diff --git a/src/catalog/synth-release.test.ts b/src/catalog/synth-release.test.ts index ff006a6b1..9b4db9043 100644 --- a/src/catalog/synth-release.test.ts +++ b/src/catalog/synth-release.test.ts @@ -1,15 +1,14 @@ import { describe, expect, it } from 'vitest' import { resolve } from 'node:path' import { readFile } from 'node:fs/promises' -import { cpSync, mkdtempSync, readFileSync, writeFileSync, rmSync } from 'node:fs' +import { cpSync, mkdtempSync, writeFileSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { createHash } from 'node:crypto' import source from '../../sdk/octabam/modules/synth/octamod.module.json' -import template from '../../sdk/octabam/modules/synth/qualification.example.json' import capture from '../../sdk/octabam/modules/synth/media/capture.json' import baseline from '../../sdk/module-qualification-baseline.json' import approval from '../../sdk/synth-build-approval.json' -import regressions from '../../sdk/octabam/modules/synth/evidence/regressions.json' +import comparison from '../../sdk/native-comparisons/synth.json' import { parseModuleDocument, requireModuleUiForPublication, requireModuleQualificationForPublication } from './module-contract' import { moduleNativeSourceSha256, parseQualificationBaseline, requireFolderQualification } from '../../scripts/module-qualification.mjs' import { requireCompleteReadme, requireMonochromePng } from '../../scripts/module-documentation.mjs' @@ -17,43 +16,41 @@ import { MODULES, resolveSelection } from './modules' const folder = resolve('sdk/octabam/modules/synth') -describe('FM Synth exact experimental release', () => { - it('publishes only the exact owner-approved experimental release and keeps unknown measurements visible', async () => { +describe('FM Synth 0.1.4 (octatrick 2.12)', () => { + it('publishes through measured cycles, exact memory and the attributed hardware report of this exact source and image', async () => { const document = parseModuleDocument(source) - expect(document.tests.hardwareStatus).toBe('untested') - expect(document.resources.processing.value).toBeNull() + const q = document.tests.qualification! + expect(q).toBeDefined() + expect(document.tests.releaseWaiver).toBeUndefined() expect(() => requireModuleUiForPublication(document)).not.toThrow() - expect(() => requireModuleQualificationForPublication(document)).toThrow('worst-case cycles, exact memory and hardware') - expect(await requireFolderQualification(folder, document, parseQualificationBaseline(baseline))).toBe('owner-approved-update') - expect(approval.version).toBe(document.version) - expect(approval.sourceSha256).toBe(await moduleNativeSourceSha256(folder, document)) - expect(regressions.moduleVersion).toBe(document.version) - for (const regression of Object.values(regressions.releaseRegressions)) { - expect(regression.status).toBe('passed') - expect(regression.imageSha256).toBe(regressions.patchedBrowser.mainSha256) - } + expect(() => requireModuleQualificationForPublication(document)).not.toThrow() + expect(q.moduleVersion).toBe(document.version) + expect(q.sourceSha256).toBe(await moduleNativeSourceSha256(folder, document)) + expect(q.imageSha256).toBe(capture.imageSha256) + expect(q.cycles.every(c => c.maxConfiguration <= c.budget)).toBe(true) + expect(await requireFolderQualification(folder, document, parseQualificationBaseline(baseline))).toBe('qualified') + // The owner's update approval stays bound to the version it named; it never covers this one. + expect(approval.version).not.toBe(document.version) + expect(comparison.moduleVersion).toBe(document.version) + expect(comparison.moduleSourceSha256).toBe(q.sourceSha256) + expect(comparison.summary.mismatches).toBe(0) expect(resolveSelection([document.id]).map(module => module.id)).toEqual(['synth']) expect(MODULES.some(module => module.id === document.id)).toBe(true) for (const mutate of [ - (d: typeof document) => { d.version = '0.1.3-experimental' }, - (d: typeof document) => { d.tests.hardwareStatus = 'verified' }, + (d: typeof document) => { d.version = '0.1.5-experimental' }, + (d: typeof document) => { d.tests.qualification!.sourceSha256 = 'f'.repeat(64) }, ]) { const changed = structuredClone(document); mutate(changed) await expect(requireFolderQualification(folder, changed, parseQualificationBaseline(baseline))).rejects.toThrow() } }) - it('refuses changed native source or regression evidence under the exact source approval', async () => { + it('refuses changed native source under the recorded qualification', async () => { const copy = mkdtempSync(resolve(tmpdir(), 'modwerk-synth-release-test.')) try { cpSync(folder, copy, { recursive: true }) - const path = resolve(copy, 'evidence/regressions.json'), report = JSON.parse(readFileSync(path, 'utf8')) - report.releaseRegressions.midi.status = 'failed' - writeFileSync(path, JSON.stringify(report)) - await expect(requireFolderQualification(copy, parseModuleDocument(source), parseQualificationBaseline(baseline))).rejects.toThrow('exact native source') - cpSync(resolve(folder, 'evidence/regressions.json'), path) writeFileSync(resolve(copy, 'manifest.py'), 'raise AssertionError("changed submitted source must never execute")') - await expect(requireFolderQualification(copy, parseModuleDocument(source), parseQualificationBaseline(baseline))).rejects.toThrow('exact native source') + await expect(requireFolderQualification(copy, parseModuleDocument(source), parseQualificationBaseline(baseline))).rejects.toThrow('source SHA-256') } finally { rmSync(copy, { recursive: true, force: true }) } }) @@ -61,9 +58,7 @@ describe('FM Synth exact experimental release', () => { const document = parseModuleDocument(source) expect(capture.moduleVersion).toBe(document.version) expect(capture.sourceSha256).toBe(await moduleNativeSourceSha256(folder, document)) - expect(template.documentation.screenshots).toEqual(document.access!.screenshots) - // Validate only the template's documentation; never assert its pending hardware/cycles qualify. - requireCompleteReadme({ id: document.id, tests: { qualification: { documentation: { ...template.documentation, screenshotStyle: 'black-and-white' as const } } } }, await readFile(resolve(folder, 'README.md'), 'utf8')) + requireCompleteReadme(document, await readFile(resolve(folder, 'README.md'), 'utf8')) for (const media of document.media.filter(item => item.otUi)) { const bytes = await readFile(resolve(folder, media.path)) requireMonochromePng(bytes) diff --git a/src/community/downloaded-module-guide.ts b/src/community/downloaded-module-guide.ts index d6fd2f8d2..42423e1ab 100644 --- a/src/community/downloaded-module-guide.ts +++ b/src/community/downloaded-module-guide.ts @@ -69,7 +69,7 @@ const QUICK_TESTS: Record = { { title: 'Shape the clang.', text: 'Add trigs and press PLAY. Adjust TUNE, then raise METAL and turn COLOR. Add FBK gradually.' }, { title: 'Finish the test.', text: 'Press STOP and let the AMP release finish. Remove example locks and return SWEEP and FOLD to 0.' }, ] }, - synth: { version: '0.1.2-experimental', steps: [ + synth: { version: '0.1.4-experimental', steps: [ { title: 'Choose FM SYNTH.', text: 'In a fresh project, select an audio track. Hold FUNC + SRC, choose FM SYNTH with UP/DOWN and press YES. No sample is needed.' }, { title: 'Add harmonics.', text: 'Press SRC, set RATO to 1 and raise INDX from 0. The sine tone gains FM harmonics; PTCH changes pitch.' }, { title: 'Play a pattern.', text: 'Place a trig and press PLAY. Use AMP ATK, HOLD and REL to shape note length. The full instructions cover voices, chords and glide.' }, diff --git a/src/community/module-changelogs.json b/src/community/module-changelogs.json index 028dc0d2b..617b38f0b 100644 --- a/src/community/module-changelogs.json +++ b/src/community/module-changelogs.json @@ -374,6 +374,21 @@ } ], "synth": [ + { + "version": "0.1.4-experimental", + "date": "2026-10-11", + "changes": [ + "move to the author’s octatrick 2.12 line (timhastie/octatrick-modules tag v2.12, ca13ed8): the FM SYNTH chooser is the author’s machine.s, adapted from Modwerk’s, with the same FLEX + FM/1 Part signature, so 0.1.1/0.1.2 projects open unchanged.", + "the metronome click no longer cuts a held note: the engine had hooked the frame builder’s clear of the metronome’s click word (read as a STOP word), so with CLICK ON every beat released held notes and chords. 2.12 removes that hook; STOP still ends the engine’s voices through the stock voice kill.", + "a machine change to or from FM SYNTH, through SELECT MACHINE TYPE, SRC SETUP or the file browser, resets the track’s machine parameters (PLAYBACK, PLAYBACK SETUP, AMP, AMP SETUP, LFO and LFO SETUP) to init: FM SYNTH’s own seeds, or a stock machine’s defaults as the stock Part initialiser writes them. Step and scene locks are kept, as in the stock firmware; FX1 and FX2 stay.", + "the machine list is shared with the author’s SY DRUM machine: the rows after PICKUP are a row table, the six row-count and row-bound constants became detours that read it, and SELECT MACHINE TYPE scrolls like SRC SETUP. With FM Synth alone the list keeps its six rows.", + "DEC puts HOLD at 127 and 0 is the shortest decay; note starts no longer click (attacks of at least one carrier period, S-shaped; low notes start slightly slower).", + "LFO SETUP lists the FM page’s names (PTCH RATO INDX FINE FDBK DEC) as destinations on an FM track and skips SPD3/DEP3; a held CHROMATIC key is the gate, HOLD applies to sequencer trigs.", + "fixes from the author’s MKI test: no click when a sounding note restarts at a short DEC, no lost notes in fast VOIC 2–4 runs, recorded legato phrases keep sounding, and FM SYNTH chosen after a sample plays C4 at FINE 0c.", + "6 dB of headroom in the engine (author’s decision; replaces the 18.06 dB OUTPUT_SHIFT of 0.1.2): the mono voice and each paraphonic voice are 6.02 dB lower than the author’s earlier 2.10 builds, so AMP VOL 0 is the reference and VOL up to about +6 stays clean; output is about 12 dB louder than 0.1.2 at the same settings. The 0.1.2 MIDI chromatic drawer fix is kept.", + "Emulator worst-case ColdFire cost, exact memory and two-track Part/project save and reload are recorded for this build (TESTING.md). The author’s MKI report of this exact image is pending; physical reboot survival is not yet verified." + ] + }, { "version": "0.1.2-experimental", "date": "2026-10-08", From 2797e5fec6f8d894b50b7e7f9fa786c3613b84e0 Mon Sep 17 00:00:00 2001 From: timhastie Date: Sun, 11 Oct 2026 20:40:29 -0700 Subject: [PATCH 2/7] FM Synth 0.1.4: evidence revision 651dc0f (the measured source); import inventory refreshed on main 3151975 --- sdk/imports/synth-ca13ed8.json | 2 +- sdk/octabam/modules/synth/octamod.module.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/imports/synth-ca13ed8.json b/sdk/imports/synth-ca13ed8.json index e4f9144eb..81e520cf1 100644 --- a/sdk/imports/synth-ca13ed8.json +++ b/sdk/imports/synth-ca13ed8.json @@ -271,7 +271,7 @@ }, { "path": "octamod.module.json", - "sha256": "e84133735551ac8a6c7cbc98a91d61489691accd7607e6b8e1708edeab517755", + "sha256": "2650be7edc1198c4864f9018925965cd1ff6b1d2ca2fbf31abae13a5d173a5a6", "previousSha256": "3a09d67d94ffaee529f261153cb3101e8aabcb1e4bca31b029e0bf7e66200e27" }, { diff --git a/sdk/octabam/modules/synth/octamod.module.json b/sdk/octabam/modules/synth/octamod.module.json index 9bf82085f..ee5183da0 100644 --- a/sdk/octabam/modules/synth/octamod.module.json +++ b/sdk/octabam/modules/synth/octamod.module.json @@ -253,7 +253,7 @@ "report": "TESTING.md", "summary": "Emulator qualification of the native FM Synth 0.1.4 image (MAIN OS 00fa6caa..., built from the author's ca13ed8, octatrick 2.12 tag v2.12, with the bundled quantizer): frame-interrupt instruction counts for 1 to 8 FM tracks at VOIC 1, 2 and 4, converted at the hardware CF METER calibration (9.44 ns/instruction, 264 MHz); the bounding cards measured five times. One FM track at VOIC 4 keeps every block, including the first START, inside the 16-sample block (81,422 of 95,779 cycles); eight VOIC 4 tracks do not. Exact memory from the link map: 43,812 module-owned bytes plus the shared 10,461,204-byte sample-RAM platform reserve. Software verification: the module's own native Part smoke (verify.py) passes on this image; two FM tracks pass Part save/reload, project save/reload and a fresh project load in the emulator (the battery-RAM-only restart is the hardware test); the 2.12 metronome case and the machine-change init checked in the emulator; eight LCD captures; the author's odd-address check. The native/browser coverage record is not refreshed: POLY8's chooser bridge stops on this chooser (TESTING.md). A hardware report for this exact image is pending; Modwerk's 0.1.1/0.1.2 owner approvals stay with those versions.", "hardwareStatus": "historical", - "evidenceRevision": "5abb183cad93bc735c42ef1f8f9ce7925285b6db", + "evidenceRevision": "651dc0f2d233eb9dce69fbe7f5f0d064ee33ddf5", "gates": [ "verify.py: native Part smoke on image 00fa6caa (eight-track selection, Part and shadow signature, sample slots unchanged, reselection keeps controls, return to FLEX): passed (evidence/native-smoke.json)", "Two FM instances (T1, T5) with distinct settings on image 00fa6caa in the emulator: isolation, Part save/reload, project save/reload and a fresh project load restore both; audio after each transition (TESTING.md)", @@ -402,7 +402,7 @@ "model": "MKI", "testedOn": "TIM: YYYY-MM-DD", "tester": "TIM: credited handle", - "sourceRevision": "5abb183cad93bc735c42ef1f8f9ce7925285b6db", + "sourceRevision": "651dc0f2d233eb9dce69fbe7f5f0d064ee33ddf5", "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", "summary": "TIM: what was played on the unit and what happened (MAIN OS 00fa6caa..., the hardware test sheet)", "limitations": [ From bcf3b7fde7b15dbd7ee744fc2b5b6ea862f4db9f Mon Sep 17 00:00:00 2001 From: timhastie Date: Mon, 12 Oct 2026 12:39:28 -0700 Subject: [PATCH 3/7] FM Synth 0.1.5: the author's 2.13 quantizer (scale and root on MIDI tracks) The bundled quantizer moves to timhastie/octatrick-modules 48b89e7 (tag v2.13; its synth/ files are the same bytes as v2.12 ca13ed8, so the engine, chooser and page are 0.1.4's). With a scale on, a MIDI track's NOTE knob and NOTE locks step through the scale from ROOT and its CHROMATIC keys play the scale from ROOT (three new detours: 0x40055382 qz_mknob, 0x40050b98 qz_mplock, 0x400436a8 qz_mkey); NOT2..4, TRAN, the arpeggiator, MIDI IN and SCALE OFF are unchanged. 2.13 takes Modwerk's #264 form of the CHROMATIC drawer hook (0x400449be), so quantizer.s, core.s, keys.s, scale.s and README.md are the author's files byte for byte; the quantizer manifest is his 2.13 manifest in stock_guard form (23 guards). The module LICENSE carries his 2.13 LICENSE (one sentence crediting Modwerk's form). Inventory sdk/imports/synth-48b89e7.json, following 0.1.4's synth-ca13ed8.json. Emulator evidence on the image built from this source (MAIN OS 37661aee): the FM objects are byte-identical to 0.1.4's at the same addresses; NOTE knob / locks 409 of 409 and CHROMATIC keys 245 of 245 against a model of the rules, SCALE OFF as stock, the sequencer unchanged, the quantizer's added cost per call; Part smoke, two-instance persistence, machine-change init; exact memory (44,404 module-owned bytes), stack bound 352 B; 0.1.4's cycle table with its bounding cards rerun here; seven captures (a MIDI track's CHROMATIC page new; two 0.1.4 captures dropped for the eight-media limit). Hardware fields are placeholders for the author's report. --- docs/VERIFICATION.md | 58 +++ scripts/module-source.test.mjs | 2 +- sdk/catalog.json | 2 +- sdk/imports/synth-48b89e7.json | 415 ++++++++++++++++++ sdk/octabam/modules/synth/LICENSE | 6 +- sdk/octabam/modules/synth/OCTABAM.md | 19 +- sdk/octabam/modules/synth/README.md | 31 +- sdk/octabam/modules/synth/TESTING.md | 177 ++++---- .../modules/synth/evidence/native-smoke.json | 2 +- sdk/octabam/modules/synth/manifest.py | 8 +- sdk/octabam/modules/synth/media/LICENSE.md | 10 +- sdk/octabam/modules/synth/media/capture.json | 122 ++++- sdk/octabam/modules/synth/media/ot-glide.png | Bin 3669 -> 3664 bytes .../modules/synth/media/ot-machine-list.png | Bin 3798 -> 0 bytes .../modules/synth/media/ot-midi-chromatic.png | Bin 0 -> 5237 bytes sdk/octabam/modules/synth/media/ot-scale.png | Bin 3945 -> 0 bytes sdk/octabam/modules/synth/octamod.module.json | 181 ++++---- sdk/octabam/modules/synth/release-notes.json | 18 +- sdk/octabam/modules/synth/stock-guards.json | 18 + sdk/octabam/modules/synth/upstream/LICENSE | 6 +- .../synth/upstream/quantizer/README.md | 157 ++++++- .../synth/upstream/quantizer/manifest.py | 39 +- .../synth/upstream/quantizer/quantizer.s | 271 ++++++++++-- sdk/tests/test_synth_release.py | 16 +- src/catalog/synth-release.test.ts | 4 +- src/community/downloaded-module-guide.ts | 2 +- src/community/module-changelogs.json | 11 + 27 files changed, 1299 insertions(+), 276 deletions(-) create mode 100644 sdk/imports/synth-48b89e7.json delete mode 100644 sdk/octabam/modules/synth/media/ot-machine-list.png create mode 100644 sdk/octabam/modules/synth/media/ot-midi-chromatic.png delete mode 100644 sdk/octabam/modules/synth/media/ot-scale.png diff --git a/docs/VERIFICATION.md b/docs/VERIFICATION.md index 07938a822..2bf9863bf 100644 --- a/docs/VERIFICATION.md +++ b/docs/VERIFICATION.md @@ -1444,3 +1444,61 @@ retained Analog BD record was re-verified against the new packages (162 native OS and GNU bootloader comparisons, 50 matching refusals, 6 firmware round trips) before its source-inventory binding was updated. No firmware is committed. + +## FM Synth 0.1.5 (octatrick 2.13 quantizer) — 12 October 2026 + +Local draft by the module author's port; not published, no owner approval; +it follows the unpublished 0.1.4 above. FM Synth's bundled quantizer moves to +the author's 2.13 line (`timhastie/octatrick-modules` `48b89e7`, tag `v2.13`, +which leaves `synth/` byte-identical to `v2.12`): with a scale on, a MIDI +track's NOTE knob and NOTE locks step through the scale from ROOT and its +CHROMATIC keys play the scale from ROOT (three new detours: `0x40055382`, +`0x40050b98`, `0x400436a8`); chord offsets, TRAN, the arpeggiator, MIDI IN +and SCALE OFF are unchanged. The CHROMATIC drawer hook at `0x400449be` is +now the author's own form of Modwerk's #264 fix, so `quantizer.s` is his +file byte for byte; its manifest is his 2.13 manifest in stock-guard form +(23 guards, each checked against the developer's MAIN OS). Inventory: +`sdk/imports/synth-48b89e7.json`; native source SHA-256 `401d3b61…`. + +The native exporter built MAIN OS `37661aee…` (update file 462,628 B, +`b42048d5…`; Modwerk's TS packer decodes it to that MAIN OS and re-encodes +the same bytes). The FM engine and chooser objects are byte-identical to +0.1.4's and link to the same addresses; the page links to the author's +ratified bytes; the author's 2.13 `tools/align_check.py` found every +instruction and every detour target at an even address. In the pinned +emulator on that image: the NOTE knob and NOTE locks matched a model of the +rules in 409 of 409 cases over eight SCALE / ROOT settings (0.1.4's image +matches the stock clamp in all 409), the MIDI CHROMATIC keys in 245 of 245 +(note-offs balanced, the arpeggiator fed snapped notes), CHROMATIC on a MIDI +track draws and plays, the sequencer, MIDI IN and the arpeggiator send the same MIDI +events as on 0.1.4's image under SCALE OFF and three scales. The quantizer's added cost is 49–77 +instructions per CHROMATIC key and up to 278 / 302 per NOTE knob / lock +turn of three clicks, all in the UI task. The module's `verify.py` Part +smoke, two-track Part / project save and reload, a fresh project load and +the 2.12 machine-change init passed on this image. Exact memory: 44,404 +module-owned bytes (+592: the quantizer's DRAM unit +360, three hook sites, +the larger loader); stack bound 352 B, unchanged. Cycles: 0.1.4's table +(same FM objects); the bounding cards rerun twice on this image stay inside +the rig's spread, and the record's bounds over all seven runs give one VOIC +4 track 81,459 of 95,779 cycles/block. Seven LCD captures (five +byte-identical to 0.1.4's; a MIDI track's CHROMATIC page is new). The +hardware fields are `TIM:` placeholders pending the author's MKI report of +this exact image. + +POLY8's bridge still stops on the author's chooser, as for 0.1.4, so +`sdk/native-comparisons/synth.json` is not refreshed. Without POLY8 in the pool (a local +run on main `263aa1b` in a scratch copy, not committed) the comparison gave +122 selections: 35 builds match outside the platform writes, 87 refusals +match, 0 mismatches, the native side on the host toolchain instead of Docker. + +Packages were rebuilt from source with `--include-requested` and imported +with `--development` on main `263aa1b`; only the Synth objects and the Synth +version change against main's 0.1.2 packages (Modwerk's `fmregistration` +object is gone, the author's `poly` and `fmmachine` objects come in, the +bundled quantizer objects change); 0.1.4 has no package commit of its own on +this branch, so these packages serve both. +A fresh import of the same build reproduces the committed assets +(`--check-only --verify-existing`). The retained Analog BD record was +re-verified against the new packages (162 native OS and GNU bootloader +comparisons, 50 matching refusals, 6 firmware round trips) before its +source-inventory binding was updated. No firmware is committed. diff --git a/scripts/module-source.test.mjs b/scripts/module-source.test.mjs index d85c3a80c..1b1ac2629 100644 --- a/scripts/module-source.test.mjs +++ b/scripts/module-source.test.mjs @@ -27,7 +27,7 @@ describe('release package scope and reviewed source inventory', () => { expect(versions['midi-scenes']).toBe('0.2.5-experimental') expect(versions.miniverb).toBe('0.2.1-experimental') for (const id of verifiedRequested) { - expect(versions[id]).toBe(id === 'analog-bassdrum' ? '0.1.5-experimental' : id === 'vector' ? '0.2.4-experimental' : id === 'quantizer' ? '0.1.3-experimental' : id === 'synth' ? '0.1.4-experimental' : id === 'usb-audio-out-tracks-main-cue' ? '0.2.0-experimental' : '0.1.2-experimental') + expect(versions[id]).toBe(id === 'analog-bassdrum' ? '0.1.5-experimental' : id === 'vector' ? '0.2.4-experimental' : id === 'quantizer' ? '0.1.3-experimental' : id === 'synth' ? '0.1.5-experimental' : id === 'usb-audio-out-tracks-main-cue' ? '0.2.0-experimental' : '0.1.2-experimental') expect(paths).toContain('modules/' + id + '/manifest.py') } expect(paths).toContain('modules/midi-scenes/recipe.json') diff --git a/sdk/catalog.json b/sdk/catalog.json index f423c1628..31160429e 100644 --- a/sdk/catalog.json +++ b/sdk/catalog.json @@ -74,7 +74,7 @@ }, { "id": "synth", - "version": "0.1.4-experimental", + "version": "0.1.5-experimental", "addedAt": "2026-10-06T08:00:28Z" }, { diff --git a/sdk/imports/synth-48b89e7.json b/sdk/imports/synth-48b89e7.json new file mode 100644 index 000000000..3f402c176 --- /dev/null +++ b/sdk/imports/synth-48b89e7.json @@ -0,0 +1,415 @@ +{ + "schemaVersion": 1, + "id": "synth", + "version": "0.1.5-experimental", + "status": "local-draft-unpublished", + "basedOn": "263aa1b9a8db278b1da96a8a33e07f6f65a94a74", + "originalImport": "synth-949f3be.json", + "previousUpdate": "synth-ca13ed8.json", + "folder": "sdk/octabam/modules/synth", + "authorPin": { + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "path": "synth", + "version": "2.13 (commit 48b89e7, tag v2.13; the engine, page and chooser files are the same bytes as v2.12 ca13ed8)" + }, + "hooks": { + "added2_13": [ + "0x40055382 qz_mknob (jsr, 8 B): the knob handler's MIDI-track clamp just before its store 0x4005538a (which stays stock) -- a MIDI track's NOTE knob steps by scale degree from ROOT", + "0x40050b98 qz_mplock (jsr, 10 B): the MIDI p-lock editor's lock store -- a NOTE lock steps by scale degree from ROOT", + "0x400436a8 qz_mkey (jmp, 10 B): the MIDI CHROMATIC key press -- the key's note + ROOT snapped to the nearest scale note (ties down); the note-on, the arpeggiator's add, the held note (so the note-off) and the live recorder take it" + ], + "unchangedSite": [ + "0x400449be qz_octnum (jmp, 8 B): the CHROMATIC drawer's common argument push -- Modwerk 0.1.2's #264 site, now the author's own form (2.13); a MIDI track keeps stock's number, with ROOT's name before it when a scale is on" + ], + "engine": "The FM engine (poly.s), the chooser (machine.s), the page (page.s), engine_abi.inc and upstream/synth/manifest.py are unchanged from 0.1.4 (same bytes); their hooks are synth-ca13ed8.json's." + }, + "files": [ + { + "path": "LICENSE", + "sha256": "669826ea22d68ec500f50eb955bf0faf98b4dba20cb713644a1ca864f951175f", + "previousSha256": "4d8c71b951cb102a1e6cb761d8e1101748346de633d695a85c48461f49a2cfa2" + }, + { + "path": "OCTABAM-LICENSE", + "sha256": "f91a43799d9e31ccc3540f385f68c4d6ff5a096f93ae933be2ce98f6b3f7f113", + "previousSha256": "f91a43799d9e31ccc3540f385f68c4d6ff5a096f93ae933be2ce98f6b3f7f113" + }, + { + "path": "OCTABAM.md", + "sha256": "ae18a9a1dfc7512fd199141e8ff2c1d7932fcda9e08c5e90f4ec862246cb3e79", + "previousSha256": "be0806b7c13cca799018bce3e1f431bf7c29362ff16ca04ad06e27a80b95eedf" + }, + { + "path": "README.md", + "sha256": "581d224a2fae36253ca1ae919aaad5d92897b51a8e22505655ea871fac580829", + "previousSha256": "827248dce8d0577732c416ff2921d8ef34d7383740e341b1b752b4b8b63b4fb5" + }, + { + "path": "TESTING.md", + "sha256": "78c25654ea7ff2802946bd093ddc5d3963fb126c3f2911d0ff0b224d7981c005", + "previousSha256": "08b83d4b002489695b300efae2b58c4d023085dbc7668671e0e96c3ae688e0ad" + }, + { + "path": "evidence/native-smoke.json", + "sha256": "67edca9902c9fd839d81ca0ff4ee406c5c7931f4550d4faa0bbecb139824ddf0", + "previousSha256": "66ebc4af8515b6af8a83730e04769fc8a85a806c3274fa3e652cba78adefbcdc" + }, + { + "path": "evidence/release-0.1.1/TESTING.md", + "sha256": "682641b3cbfec00bb58b70ea5cd172d8b07e63430c2af851ccea3f3ee9e58173", + "previousSha256": "682641b3cbfec00bb58b70ea5cd172d8b07e63430c2af851ccea3f3ee9e58173" + }, + { + "path": "evidence/release-0.1.1/audio.json", + "sha256": "aea4e6b640318b3db237cb0bfe0af184f303c0a70caf6870cc8a630225e49668", + "previousSha256": "aea4e6b640318b3db237cb0bfe0af184f303c0a70caf6870cc8a630225e49668" + }, + { + "path": "evidence/release-0.1.1/browser-audio.json", + "sha256": "cb35c8c26264ca44cb98ccc3f66a053f1ff93c1ac2d414dbc397736d43dab028", + "previousSha256": "cb35c8c26264ca44cb98ccc3f66a053f1ff93c1ac2d414dbc397736d43dab028" + }, + { + "path": "evidence/release-0.1.1/build-approval.json", + "sha256": "bb73f69b436d48c6e1a6c38b8ff2187d6c6512130d770634e279725b2ebebbda", + "previousSha256": "bb73f69b436d48c6e1a6c38b8ff2187d6c6512130d770634e279725b2ebebbda" + }, + { + "path": "evidence/release-0.1.1/native-build.json", + "sha256": "618c96e7c6eb1f412bd1ef1cc82c0887814f6ddb99dd6a69f2e431f0a2fb1f8f", + "previousSha256": "618c96e7c6eb1f412bd1ef1cc82c0887814f6ddb99dd6a69f2e431f0a2fb1f8f" + }, + { + "path": "evidence/release-0.1.1/octemu-walk.jsonl", + "sha256": "7625d9d9047393924668b8fee9d6c032507c6087b14b48fcec01d81e5ba9bc3b", + "previousSha256": "7625d9d9047393924668b8fee9d6c032507c6087b14b48fcec01d81e5ba9bc3b" + }, + { + "path": "evidence/release-0.1.1/owner-hardware.md", + "sha256": "f48c3e5b54463bf91b695189e6c5e9a51b9876bfea5561e775b550c1e25d7e6f", + "previousSha256": "f48c3e5b54463bf91b695189e6c5e9a51b9876bfea5561e775b550c1e25d7e6f" + }, + { + "path": "evidence/release-0.1.1/software.json", + "sha256": "f74a7cf16898043c03abe3e3c1b591abfe5d908eecc76367aceddb6d2e8b85a0", + "previousSha256": "f74a7cf16898043c03abe3e3c1b591abfe5d908eecc76367aceddb6d2e8b85a0" + }, + { + "path": "evidence/release-0.1.1/storage.json", + "sha256": "87b0368ad8e955f178531cea0faeb3b7124e2c211c58899b9ea3015318ce6ed6", + "previousSha256": "87b0368ad8e955f178531cea0faeb3b7124e2c211c58899b9ea3015318ce6ed6" + }, + { + "path": "evidence/release-0.1.2/TESTING.md", + "sha256": "d096affbb1985a3fd495a55d546cd67759973d84754aa68eaff22048a04be60c", + "previousSha256": "d096affbb1985a3fd495a55d546cd67759973d84754aa68eaff22048a04be60c" + }, + { + "path": "evidence/release-0.1.2/build-approval.json", + "sha256": "11984dd38136e2947c6e05920b71cd65ecfb4b2d85074c4d781f720502f0238f", + "previousSha256": "11984dd38136e2947c6e05920b71cd65ecfb4b2d85074c4d781f720502f0238f" + }, + { + "path": "evidence/release-0.1.2/composition.json", + "sha256": "e47b77ee626dd014836e6561eb3e68034b68b404c77bc19d203347695dc1f466", + "previousSha256": "e47b77ee626dd014836e6561eb3e68034b68b404c77bc19d203347695dc1f466" + }, + { + "path": "evidence/release-0.1.2/development-regressions.json", + "sha256": "10772b3699adebb982818e09620c4febbd2e45ef7b67ed6ca49ad4d82eee8b69", + "previousSha256": "10772b3699adebb982818e09620c4febbd2e45ef7b67ed6ca49ad4d82eee8b69" + }, + { + "path": "evidence/release-0.1.2/media/amp-max-fixed.png", + "sha256": "d776d287d403deffa07330350a33d4b2313db2c6d0710610ec46b4ba98b0fc1f", + "previousSha256": "d776d287d403deffa07330350a33d4b2313db2c6d0710610ec46b4ba98b0fc1f" + }, + { + "path": "evidence/release-0.1.2/media/capture.json", + "sha256": "3e90641f7a8a8b0f872864f308836308d4fd3190e2095f507c4f724fd5f2a34b", + "previousSha256": "3e90641f7a8a8b0f872864f308836308d4fd3190e2095f507c4f724fd5f2a34b" + }, + { + "path": "evidence/release-0.1.2/media/midi-synth-chromatic-fixed.png", + "sha256": "0b4a8a395c4e17f4fd4c675857e692716c7d0f9345b45680887fb25edd4f2468", + "previousSha256": "0b4a8a395c4e17f4fd4c675857e692716c7d0f9345b45680887fb25edd4f2468" + }, + { + "path": "evidence/release-0.1.2/media/poly-controls.png", + "sha256": "376fcdc83ed4df158bf6cb747f6deb92e2518172f02f5f23ca9a946b6000911a", + "previousSha256": "376fcdc83ed4df158bf6cb747f6deb92e2518172f02f5f23ca9a946b6000911a" + }, + { + "path": "evidence/release-0.1.2/media/regression-capture.json", + "sha256": "a42ea9a3bfd9ee50445face5a551235fbae5a10579820cc2bc44e7bbdfc634f6", + "previousSha256": "a42ea9a3bfd9ee50445face5a551235fbae5a10579820cc2bc44e7bbdfc634f6" + }, + { + "path": "evidence/release-0.1.2/midi-walk.jsonl", + "sha256": "a755ea8eaf205ab4c3a0f5639dcaee4019d5602aeb6dec5bc026d0bb4272b4ba", + "previousSha256": "a755ea8eaf205ab4c3a0f5639dcaee4019d5602aeb6dec5bc026d0bb4272b4ba" + }, + { + "path": "evidence/release-0.1.2/mono-max-walk.jsonl", + "sha256": "a5bafa1d26e8d09771ae4b7e4aee046fddc10dac88cd9cee482179bac6be2272", + "previousSha256": "a5bafa1d26e8d09771ae4b7e4aee046fddc10dac88cd9cee482179bac6be2272" + }, + { + "path": "evidence/release-0.1.2/poly-max-walk.jsonl", + "sha256": "4d407f2777a1b166ef11b096b80bbeaf3c4f546f647d776461cd821e852ed615", + "previousSha256": "4d407f2777a1b166ef11b096b80bbeaf3c4f546f647d776461cd821e852ed615" + }, + { + "path": "evidence/release-0.1.2/regressions.json", + "sha256": "cf0043058c17eb4655b1d22c441063f73dbf6d42984506a7041a95b18a4d8e15", + "previousSha256": "cf0043058c17eb4655b1d22c441063f73dbf6d42984506a7041a95b18a4d8e15" + }, + { + "path": "evidence/release-0.1.2/software-checks.json", + "sha256": "61f410796ea1453cd657614629c8e71a537ef9bdc89f42f27085b7b76dac0fb4", + "previousSha256": "61f410796ea1453cd657614629c8e71a537ef9bdc89f42f27085b7b76dac0fb4" + }, + { + "path": "manifest.py", + "sha256": "f364cd97b5a2a416b3029752fe2ce0691fc977d59203419d9d5a52bbeb7913b7", + "previousSha256": "e1f89e0e9557e9e615af51e07e87140fc1e8b3a300956339a2f25748a641ef92" + }, + { + "path": "media/LICENSE.md", + "sha256": "d80830f21f4a9da2508f723078beedce2920ff3655e5dfdc8bb54bec82113f47", + "previousSha256": "9157e7a9de0a397617e28af2326c2e83d7ecb98eee209ece7495f9b5850622fe" + }, + { + "path": "media/audio-preview.json", + "sha256": "55cb73aec46766a7c285f8c2258edb225bcec17a74435ade8fd410cd93d360ca" + }, + { + "path": "media/audio-preview.mp3", + "sha256": "b44fa27659fd947eacf5d0dfe186cddfeaf0ecef44b0cfe7d59b48e3ac185f7f" + }, + { + "path": "media/capture.json", + "sha256": "fe025c685875be38e19155769cd6bd0d867e2637339decf3dcfb6cc959144727", + "previousSha256": "aba4e896a00418964b801da68fbccd8869523d2ddeb08cb979c22fa617c672aa" + }, + { + "path": "media/ot-controls.png", + "sha256": "2eb52e9b732308891e289aa23d92556a87dd8820b12a37cbe73f66e720a162f0", + "previousSha256": "2eb52e9b732308891e289aa23d92556a87dd8820b12a37cbe73f66e720a162f0" + }, + { + "path": "media/ot-glide.png", + "sha256": "f67a2c2d5fff741e4a1cca6bf897af7a56d857cb18705d5d64c1e404dfa3ec7c", + "previousSha256": "77b4cf73fd16e96d1c78f5a9cdd0590b20a7958c614d1c6cdb93b77791dc505b" + }, + { + "path": "media/ot-legato.png", + "sha256": "e5cd86583126a834f8a9351a8f2b51bca9f76c101023e37db4c933be957d5bc8", + "previousSha256": "e5cd86583126a834f8a9351a8f2b51bca9f76c101023e37db4c933be957d5bc8" + }, + { + "path": "media/ot-lfo-setup.png", + "sha256": "ab3071680706882acdf5f9ccdefb1adcc24c50690afd90560ca42e25535e3e94", + "previousSha256": "ab3071680706882acdf5f9ccdefb1adcc24c50690afd90560ca42e25535e3e94" + }, + { + "path": "media/ot-location.png", + "sha256": "4e1d84898790433813720c437d2e767cb3225c7770ea15be9115f76e70b82270", + "previousSha256": "4e1d84898790433813720c437d2e767cb3225c7770ea15be9115f76e70b82270" + }, + { + "path": "media/ot-midi-chromatic.png", + "sha256": "29ee87e6fef88d3ab7c8a52a90d7c5bc69852f9d175720e89b76c723985a45d0" + }, + { + "path": "media/ot-voices.png", + "sha256": "603b3e5d31446bf31570b02dc56b6efccb958b371a06b87c3dc79a6a6090d1e5", + "previousSha256": "603b3e5d31446bf31570b02dc56b6efccb958b371a06b87c3dc79a6a6090d1e5" + }, + { + "path": "octamod.module.json", + "sha256": "c116a69c3ea6c3ff568fff823b4a3b329274b0cd1e1feadd7fa6d64ae910bcee", + "previousSha256": "2650be7edc1198c4864f9018925965cd1ff6b1d2ca2fbf31abae13a5d173a5a6" + }, + { + "path": "presentation/LICENSE", + "sha256": "dc99139facc8371c0830c830eb381263467de65e6fd834ff7f1642996dd9bc8a", + "previousSha256": "dc99139facc8371c0830c830eb381263467de65e6fd834ff7f1642996dd9bc8a" + }, + { + "path": "presentation/thumbnail.svg", + "sha256": "451e1fcf768d8c13ea8f5be9c77c9ad82c49bc28c373fb07c8809954dfd3e21f", + "previousSha256": "451e1fcf768d8c13ea8f5be9c77c9ad82c49bc28c373fb07c8809954dfd3e21f" + }, + { + "path": "release-notes.json", + "sha256": "50e2dd52ea63e6ea351f9899eba440ffcab069326e83e9097c96ec9eda360f74", + "previousSha256": "f84f858de9b6817dde325e197c51c8f91fb7601ceeafdf97d1da5b4e54d360af" + }, + { + "path": "stock-guards.json", + "sha256": "da4ed4a80b845a759e0a5fbbf95c00b477fd01a2bc82e15653e5378a5302f02a", + "previousSha256": "09c5cc87e78b0a0498629137233f6fd9eba6bb49772f10291f371a942f4d6a95" + }, + { + "path": "upstream/LICENSE", + "sha256": "9895a0c9da98720fec21e348456c9716ca81ca6438b7f046eca1fb2f456b24a7", + "previousSha256": "1f72dbab3547d1d94861cf9f129519301295395129fe3f56760ed767d07f04fe", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "LICENSE", + "sourceBytes": 5068, + "sourceSha256": "9895a0c9da98720fec21e348456c9716ca81ca6438b7f046eca1fb2f456b24a7" + }, + { + "path": "upstream/quantizer/README.md", + "sha256": "4b12e1f27539cf91aabe9f9282c2f96f168c22803d5986442ed30b192c17f1fe", + "previousSha256": "9b7e0323af6133d0260b2e1a69c4321c8af4dad776bbce64fd8b4b9a8c04bfb2", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "quantizer/README.md", + "sourceBytes": 87033, + "sourceSha256": "4b12e1f27539cf91aabe9f9282c2f96f168c22803d5986442ed30b192c17f1fe" + }, + { + "path": "upstream/quantizer/core.s", + "sha256": "2992b6c85cff440de03ae84eb22dee8b8a83a84f23e94b699404d28940b6f958", + "previousSha256": "2992b6c85cff440de03ae84eb22dee8b8a83a84f23e94b699404d28940b6f958", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "quantizer/core.s", + "sourceBytes": 5883, + "sourceSha256": "2992b6c85cff440de03ae84eb22dee8b8a83a84f23e94b699404d28940b6f958" + }, + { + "path": "upstream/quantizer/keys.s", + "sha256": "e2ffdbfae34b0828794a4eb4cc229e3efcaed25dc953bf7c05c13f6e37f65587", + "previousSha256": "e2ffdbfae34b0828794a4eb4cc229e3efcaed25dc953bf7c05c13f6e37f65587", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "quantizer/keys.s", + "sourceBytes": 1765, + "sourceSha256": "e2ffdbfae34b0828794a4eb4cc229e3efcaed25dc953bf7c05c13f6e37f65587" + }, + { + "path": "upstream/quantizer/manifest.py", + "sha256": "e114460f9e0107bbda9c6663ba232f6a52c8dd477ae5eb10efe92e918b5b5a63", + "previousSha256": "f6a4af8e934500c57eacec9fb5a994ecd2c0821f60d05696249a3ea79bf6215b", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "quantizer/manifest.py", + "sourceBytes": 23039, + "sourceSha256": "5640dea05225ddf95e964ab6f257f86eae316421796c294b117222c200758caa", + "changes": [ + "Modwerk's stock-guard form of the author's 2.13 manifest: each of its 23 embedded stock expectations (22 detour sites and the poke's expect) replaced by stock_guard(address, length, SHA-256), each literal checked against the developer's MAIN OS 164f3122..., and the stock_guard import added; nothing else changed (descriptions, the per-build include and the poke's write are the author's)." + ] + }, + { + "path": "upstream/quantizer/quantizer.s", + "sha256": "bdf7fe4815d0a742ceb77a4688db0eeb79f6c2d472fcc4f9bfb869a77db9ea8c", + "previousSha256": "0e6c0a0238a563a1092eb9ab8c1b7962038a6ca74d29f8978cee3bb8e5eccba7", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "quantizer/quantizer.s", + "sourceBytes": 81971, + "sourceSha256": "bdf7fe4815d0a742ceb77a4688db0eeb79f6c2d472fcc4f9bfb869a77db9ea8c" + }, + { + "path": "upstream/quantizer/scale.s", + "sha256": "82242c407847ac61a6f60fe15bdc5b0b4fc852bac82c39745b5c838ec9389c64", + "previousSha256": "82242c407847ac61a6f60fe15bdc5b0b4fc852bac82c39745b5c838ec9389c64", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "quantizer/scale.s", + "sourceBytes": 695, + "sourceSha256": "82242c407847ac61a6f60fe15bdc5b0b4fc852bac82c39745b5c838ec9389c64" + }, + { + "path": "upstream/synth/README.md", + "sha256": "8c3f499e4d327c78115a42c971e3f39f27d6dd7a4dffde222d3e3d8fb8f368ad", + "previousSha256": "8c3f499e4d327c78115a42c971e3f39f27d6dd7a4dffde222d3e3d8fb8f368ad", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "synth/README.md", + "sourceBytes": 324402, + "sourceSha256": "8c3f499e4d327c78115a42c971e3f39f27d6dd7a4dffde222d3e3d8fb8f368ad" + }, + { + "path": "upstream/synth/engine_abi.inc", + "sha256": "50a855a7c0e148332bae159159584965baa709230b7e59d0bf8644c7ff51adf6", + "previousSha256": "50a855a7c0e148332bae159159584965baa709230b7e59d0bf8644c7ff51adf6", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "synth/engine_abi.inc", + "sourceBytes": 4596, + "sourceSha256": "50a855a7c0e148332bae159159584965baa709230b7e59d0bf8644c7ff51adf6" + }, + { + "path": "upstream/synth/machine.s", + "sha256": "63ff52340e82f55535a81a678921f3be1c7a281dcd05f25aaea072221c01cd50", + "previousSha256": "63ff52340e82f55535a81a678921f3be1c7a281dcd05f25aaea072221c01cd50", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "synth/machine.s", + "sourceBytes": 42214, + "sourceSha256": "63ff52340e82f55535a81a678921f3be1c7a281dcd05f25aaea072221c01cd50" + }, + { + "path": "upstream/synth/manifest.py", + "sha256": "197a85ad6a7abd5934ce423f6b227da26451bb2a933af890255fe3fb1c368383", + "previousSha256": "197a85ad6a7abd5934ce423f6b227da26451bb2a933af890255fe3fb1c368383", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "synth/manifest.py", + "sourceBytes": 60294, + "sourceSha256": "0337158f4cfe0500a88d42baaddcb679d7111aedc5f79250e18822c46c2cee6a", + "changes": [ + "Unchanged from 0.1.4 (the author's file has the same bytes at 48b89e7 as at ca13ed8): every embedded stock expectation replaced by stock_guard(address, length, SHA-256) (53 guards); the precompiled PINNED_PAGE, its asserts, emit_page and the CavePatch removed (synth-ca13ed8.json)." + ] + }, + { + "path": "upstream/synth/page.s", + "sha256": "8d85620ee336174eea12767745023897ff99b8f63589bff9dfd817642564965d", + "previousSha256": "8d85620ee336174eea12767745023897ff99b8f63589bff9dfd817642564965d", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "synth/page.s", + "sourceBytes": 27806, + "sourceSha256": "8d85620ee336174eea12767745023897ff99b8f63589bff9dfd817642564965d" + }, + { + "path": "upstream/synth/poly.s", + "sha256": "9d2f68963a735b98e909d7537ce8896eba370aa651f984dbbb5085354071e6d8", + "previousSha256": "9d2f68963a735b98e909d7537ce8896eba370aa651f984dbbb5085354071e6d8", + "repository": "https://github.com/timhastie/octatrick-modules", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", + "sourcePath": "synth/poly.s", + "sourceBytes": 298601, + "sourceSha256": "9d2f68963a735b98e909d7537ce8896eba370aa651f984dbbb5085354071e6d8" + }, + { + "path": "verify-audio.py", + "sha256": "d02b7b916d9b198b9e10e34d21c7eb0242267ef8bdac5762b5958721c05b0283", + "previousSha256": "d02b7b916d9b198b9e10e34d21c7eb0242267ef8bdac5762b5958721c05b0283" + }, + { + "path": "verify-regressions.py", + "sha256": "86f6eb9e2a0c738212703bfcec2ad830910702e4064052b1b7e2bc10fbba444d", + "previousSha256": "86f6eb9e2a0c738212703bfcec2ad830910702e4064052b1b7e2bc10fbba444d" + }, + { + "path": "verify.py", + "sha256": "685d4aa146ed206caf8c9d78acf0f635e727f8eccc359167bb46205d647f3a75", + "previousSha256": "685d4aa146ed206caf8c9d78acf0f635e727f8eccc359167bb46205d647f3a75" + } + ], + "removed": [ + "media/ot-machine-list.png", + "media/ot-scale.png" + ], + "notes": [ + "The author's 48b89e7 is the head of his quant-2.13 line (local tag v2.13; to be pushed to timhastie/octatrick-modules after his hardware test). From v2.12 (ca13ed8) it changes quantizer/quantizer.s, quantizer/manifest.py, quantizer/README.md, LICENSE (a sentence crediting Modwerk's #264 form of the drawer hook), README.md and tools/align_check.py; synth/ is unchanged. The bundled quantizer's quantizer.s, core.s, keys.s, scale.s and README.md are his 48b89e7 files byte for byte.", + "0.1.4-experimental (synth-ca13ed8.json) is still unpublished; this record follows it. The published version before both is 0.1.2-experimental." + ] +} diff --git a/sdk/octabam/modules/synth/LICENSE b/sdk/octabam/modules/synth/LICENSE index 603d2c5f8..a72183b3f 100644 --- a/sdk/octabam/modules/synth/LICENSE +++ b/sdk/octabam/modules/synth/LICENSE @@ -85,8 +85,10 @@ The FM SYNTH machine-list chooser -- synth/machine.s and the chooser's parts of synth/poly.s, synth/page.s and quantizer/quantizer.s -- is adapted from Modwerk's FM Synth module (https://github.com/repeat98/modwerk, sdk/octabam/modules/synth, commit 1c1d938), whose chooser is derived from -Modwerk's Analog BD chooser hooks. Their notices, as that module's LICENSE -carries them: +Modwerk's Analog BD chooser hooks. Since 2.13 the quantizer's CHROMATIC drawer +hook at the drawer's common push (quantizer/quantizer.s qz_octnum at 0x400449be, +Modwerk #264) takes the form of the quantizer that module bundles. Their +notices, as that module's LICENSE carries them: Dedicated chooser, Part validation, sample-free transport and original presentation: diff --git a/sdk/octabam/modules/synth/OCTABAM.md b/sdk/octabam/modules/synth/OCTABAM.md index 3a9259642..f0b1f657c 100644 --- a/sdk/octabam/modules/synth/OCTABAM.md +++ b/sdk/octabam/modules/synth/OCTABAM.md @@ -1,11 +1,13 @@ -# `synth` — SYNTH MACHINE (octabam build notes, 2.12) +# `synth` — SYNTH MACHINE (octabam build notes, 2.12 engine, 2.13 quantizer) Adapted from Sam Banks's octabam `modules/synth/README.md` (pinned at -`949f3be`, MIT) for the octatrick 2.12 line the module now carries. +`949f3be`, MIT) for the octatrick line the module now carries: the 2.12 engine, +page and chooser, and the 2.13 quantizer. A two-operator FM synth machine built from [timhastie/octatrick-modules](https://github.com/timhastie/octatrick-modules) -at `ca13ed8` (tag `v2.12`), vendored under `upstream/`. +at `48b89e7` (tag `v2.13`; its `synth/` files are the same bytes as `v2.12` +`ca13ed8`), vendored under `upstream/`. `Kind.CF_PATCH`: the voice engine and the FM SYNTH chooser as DRAM units (`poly.s`, `machine.s`), the PLAYBACK page as a pinned ROM cave (`page.s`; the page descriptor itself is cloned from the unit's own ROM at first use, @@ -51,6 +53,14 @@ again: the earlier `po_stop` hook there released held notes at every click. `upstream/synth/README.md` is the author's full description, with every measurement. +Since 2.13 the bundled quantizer also acts on MIDI tracks: with a scale on, the +NOTE knob (`qz_mknob` at `0x40055382`, the knob handler's MIDI clamp before +its store `0x4005538a`, which stays stock), NOTE locks (`qz_mplock` at +`0x40050b98`) and the MIDI CHROMATIC keys (`qz_mkey` at `0x400436a8`) follow +SCALE and ROOT; the CHROMATIC drawer hook `qz_octnum` sits at the common +argument push `0x400449be` (Modwerk #264, now the author's own form). +`upstream/quantizer/README.md`, "MIDI tracks (2.13)", is his description. + ## How it is built here `manifest.py` executes `upstream/synth/manifest.py` (the author's manifest, @@ -62,7 +72,8 @@ Quantizer's objects). It adds the page as a `Linked` unit pinned at fails if `page.s` or the toolchain drifts from them), and one guarded poke at the page resolver (`0x40031ece`) that jumps to `pg_resolve` at the cave's +16, the same bytes the author's `emit_page` writes. `poly.s`, `machine.s`, -`page.s` and `engine_abi.inc` are the author's files byte for byte. The +`page.s` and `engine_abi.inc` are the author's files byte for byte, and so are +the quantizer's `quantizer.s`, `core.s`, `keys.s` and `scale.s`. The author's manifest declares Analog BD as a native `conflicts` entry; the wrapper clears it, because Modwerk declares conflicts in `octamod.module.json`. The DRAM units are linked diff --git a/sdk/octabam/modules/synth/README.md b/sdk/octabam/modules/synth/README.md index ff4e4ff32..86b543187 100644 --- a/sdk/octabam/modules/synth/README.md +++ b/sdk/octabam/modules/synth/README.md @@ -1,10 +1,12 @@ -# FM Synth — 0.1.4 experimental +# FM Synth — 0.1.5 experimental ## Overview FM SYNTH is a dedicated track machine beside FLEX, STATIC, THRU, NEIGHBOR and PICKUP. It uses Tim Hastie’s two-operator FM engine from octatrick 2.12: a sine carrier with a sine modulator, feedback and an index-decay envelope. One to four paraphonic voices feed the track’s existing AMP, filter and effects. The machine plays without a sample. -Version 0.1.4 moves the module to the author’s 2.12 line (`timhastie/octatrick-modules` tag `v2.12`, `ca13ed8`). The FM SYNTH chooser is now the author’s own: his `machine.s` is adapted from Modwerk’s 0.1.1 chooser and keeps the same Part signature (FLEX plus `FM`, 1 in unused NEIGHBOR bytes). It replaces Modwerk’s `machine.s` / `registration.s`. The update brings the four 2.10 synth changes, the fixes from the author’s hardware test of the first 2.10 build, the 2.11 machine list and the two 2.12 changes: +Version 0.1.5 brings the author’s 2.13 quantizer (`timhastie/octatrick-modules` `48b89e7`, tag `v2.13`): **SCALE and ROOT act on MIDI tracks too.** With a scale on, a MIDI track’s NOTE knob and NOTE locks step through the scale’s notes from ROOT, and the MIDI CHROMATIC keys play the scale from ROOT: key 1 sounds the root, and the number beside the keyboard reads the root and the octave (“A 3” with ROOT A). Chord offsets (NOT2–NOT4), TRAN, the arpeggiator and MIDI IN are unchanged, and SCALE OFF is stock. The CHROMATIC drawer fix for #264 is now the author’s own: his 2.13 quantizer takes the form of Modwerk’s 0.1.2 fix (credit: the Modwerk contributors). The FM engine, the chooser and the page are unchanged from 0.1.4. + +Version 0.1.4 moved the module to the author’s 2.12 line (`timhastie/octatrick-modules` tag `v2.12`, `ca13ed8`). The FM SYNTH chooser is now the author’s own: his `machine.s` is adapted from Modwerk’s 0.1.1 chooser and keeps the same Part signature (FLEX plus `FM`, 1 in unused NEIGHBOR bytes). It replaces Modwerk’s `machine.s` / `registration.s`. The update brings the four 2.10 synth changes, the fixes from the author’s hardware test of the first 2.10 build, the 2.11 machine list and the two 2.12 changes: - **DEC puts HOLD at 127.** 127 holds the modulation index for the whole note; 0 is the shortest decay. Values 1–126 are unchanged. - **No click at note start.** Every attack lasts at least one carrier period and is S-shaped, so low notes (RATO down, C1–C3) start without a tick. Attacks below C3 are slightly slower (C1 reaches full level in about 31 ms). @@ -15,7 +17,7 @@ Version 0.1.4 moves the module to the author’s 2.12 line (`timhastie/octatrick - **A machine change resets the track to init (2.12).** Changing a track to or from FM SYNTH, through SRC SETUP, SELECT MACHINE TYPE or the file browser, sets its PLAYBACK, PLAYBACK SETUP, AMP, AMP SETUP, LFO and LFO SETUP to the new machine’s init: FM SYNTH’s own seeds, or a stock machine’s defaults as the stock Part initialiser writes them. Step and scene locks are kept and apply by position, as at a stock machine change; FX1 and FX2 stay. - **8 October fixes.** A note restarted while it still sounds no longer clicks at a short DEC; a fast run of keys at VOIC 2–4 no longer loses notes; a live-recorded legato phrase no longer goes silent at its first trigless step. FM SYNTH chosen on a track that played a sample now plays C4 at FINE 0c, as its page reads. -0.1.4 keeps 0.1.2’s MIDI chromatic drawer fix in the bundled quantizer. **The engine leaves 6 dB of headroom** (the author’s decision): the mono voice and each paraphonic voice are 6.02 dB below his earlier 2.10 builds, so AMP VOL 0 is the reference and VOL up to about +6 stays clean. This replaces 0.1.2’s 18.06 dB `OUTPUT_SHIFT`: at the same mixer settings 0.1.4 plays about 12 dB louder than 0.1.2. Lower track LEVEL or AMP VOL if a mix clips. +0.1.4 kept 0.1.2’s MIDI chromatic drawer fix in the bundled quantizer; since 0.1.5 the quantizer carries the author’s own form of it. **The engine leaves 6 dB of headroom** (the author’s decision): the mono voice and each paraphonic voice are 6.02 dB below his earlier 2.10 builds, so AMP VOL 0 is the reference and VOL up to about +6 stays clean. This replaces 0.1.2’s 18.06 dB `OUTPUT_SHIFT`: at the same mixer settings 0.1.4 plays about 12 dB louder than 0.1.2. Lower track LEVEL or AMP VOL if a mix clips. Worst-case ColdFire cost, exact memory and two-track Part/project save and reload are measured in the emulator for this build; see TESTING.md. The hardware report for this exact build is pending. @@ -33,8 +35,8 @@ Worst-case ColdFire cost, exact memory and two-track Part/project save and reloa | LFO F | CHRD | ----; 32 shapes × 4 inversions | Chord shape and inversion; raw `shape<<2 | inversion`. | | FUNC+AMP F | LEG | OFF / MONO / POLY | Retrigger or overlapping mono/paraphonic legato. | | FUNC+LFO PMTR | LFO destination | PTCH … DEC, AMP, SPD1/2, DEP1/2, FX1, FX2 | The FM page’s names on an FM track; SPD3/DEP3 are skipped. | -| PROJECT > CONTROL > SEQUENCER | SCALE | OFF; 24 scales | Scale quantization of pitch edits and chromatic playing. | -| Same menu | ROOT | C; 12 roots | Key of the active scale. | +| PROJECT > CONTROL > SEQUENCER | SCALE | OFF; 24 scales | Scale quantization of pitch edits and chromatic playing; on MIDI tracks of the NOTE knob, NOTE locks and CHROMATIC keys. | +| Same menu | ROOT | C; 12 roots | Key of the active scale; key 1 of the CHROMATIC keyboards (audio and MIDI tracks). | | Same menu | GLIDE | OFF; raw 0…127 | Pitch slide time, interpreted with LEG. | AMP ATK, HOLD and REL are Part values and shape the engine’s own gain envelope; HOLD ends sequencer notes, a held key ends its own note. Choosing FM SYNTH on a track that had another machine sets the track’s PLAYBACK, PLAYBACK SETUP, AMP, AMP SETUP, LFO and LFO SETUP to init (the values in the table, the stock new-track AMP and LFO values); reselecting FM SYNTH on an FM track keeps its controls. Step and scene locks and FX1 / FX2 are kept. LFO3’s speed and depth become VOIC/CHRD; its depth contribution is muted. Four voices share the track’s filtering and envelope path. The [author documentation](upstream/synth/README.md) describes chord shapes, MIDI, keyboard recording, envelope details and every 2.10 change with its measurements. @@ -49,10 +51,11 @@ Start with one audio track and a simple trig pattern; the tutorial below selects 2. Press SRC. Set RATO to 1 and turn INDX from 0 upwards to hear a sine tone acquire FM harmonics. PTCH transposes it; FINE tunes it in cents; FDBK adds feedback; DEC shortens the modulation envelope. 3. Place a trig and press PLAY. Press LFO to set VOIC to 2–4 and CHRD to a chord if wanted. AMP ATK/HOLD/REL shape the gain envelope; FUNC+AMP exposes LEG. 4. Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; PLAYBACK, AMP and LFO return to the init values of that machine (step and scene locks stay, as in stock). Assign the desired sample in the normal way. +5. For scales and glide, open PROJECT > CONTROL > SEQUENCER. Select SCALE, ROOT or GLIDE with UP/DOWN and turn LEVEL. SCALE OFF restores unquantized pitch. With a scale on, a MIDI track's NOTE knob and NOTE locks step through the scale from ROOT, and its CHROMATIC keys (FUNC + DOWN on the NOTE page) play the scale from ROOT. FM SYNTH is also the sixth row of SELECT MACHINE TYPE: press the track key twice quickly, press LEFT on `<< MACHINE`, go DOWN to FM SYNTH and press YES. Turn DEC fully right for HOLD (the index does not decay). A FLEX slot holding a file named `FMSYNTH*.wav` or `SYNTH*.wav` still plays FM as in earlier versions. -For scales and glide, open PROJECT > CONTROL > SEQUENCER. Select SCALE, ROOT or GLIDE with UP/DOWN and turn LEVEL. SCALE OFF restores unquantized pitch. +**MIDI tracks (0.1.5).** On a MIDI track with a scale on, one NOTE click steps to the next note of the scale in the turn direction (from C3 up in MINOR with ROOT A: D3, E3, F3 …); a note between scale notes, set while the scale was off, goes to the nearest scale note in the turn direction. A NOTE lock (a TRIG key held, or a turn during LIVE RECORDING) does the same. In CHROMATIC mode every key plays a note of the scale: the key’s note plus ROOT, then the nearest scale note (the lower one on a tie); the note-on, the note-off at the key’s release, the arpeggiator fed by the keys and LIVE RECORDING all carry that note. The chord a trig plays is still NOTE plus NOT2–NOT4 in semitones, TRAN transposes in semitones and the arpeggiator’s own notes are stock; use ARP SETUP > KEY to force everything a MIDI track sends into a key. The author’s [quantizer documentation](upstream/quantizer/README.md) (“MIDI tracks (2.13)”) has the details and his measurements. Trigs, note starts and stops come from the instrument’s existing sequencer and panel/MIDI paths. The audio oscillator advances at the audio sample rate, including live playing while transport is stopped; it is not a second sequencer clock. Tempo changes, non-default track speed/length, swing and loop/pattern transitions are not tested in this version. @@ -60,7 +63,7 @@ Trigs, note starts and stops come from the instrument’s existing sequencer and OS 1.40C only. Parts store internal FLEX kind 1 plus `FM`, 1 in unused NEIGHBOR bytes, in both the live Part and its battery-RAM shadow. This is the same signature as 0.1.1 and 0.1.2, so their projects open as FM tracks. The chooser keeps the ordinary sample slots unchanged. A machine change to or from FM SYNTH resets the track’s machine parameters to init and keeps its step and scene locks, as a stock machine change does; a lock made for one machine applies by position to the next. A stock build reads the track as FLEX and cannot synthesize FM; select a stock machine explicitly before downgrading. -The matching quantizer is bundled privately because FM shares its mailbox and scale accessor. Do not combine with the public Scale Quantizer or Analog BD: their hooks/caves overlap. POLY8’s shared chooser is reviewed against 0.1.2’s chooser hooks, which this version replaces; until that bridge is updated, a build with POLY8 and FM Synth 0.1.4 stops at POLY8’s seam check (TESTING.md). +The matching quantizer is bundled privately because FM shares its mailbox and scale accessor. Do not combine with the public Scale Quantizer or Analog BD: their hooks/caves overlap. POLY8’s shared chooser is reviewed against 0.1.2’s chooser hooks, which this version replaces; until that bridge is updated, a build with POLY8 and FM Synth 0.1.4 or 0.1.5 stops at POLY8’s seam check (TESTING.md). On MIDI tracks the quantizer acts on the NOTE knob, NOTE locks and the CHROMATIC keys only: NOT2–NOT4, TRAN, the arpeggiator’s notes, MIDI IN, LFOs on NOTE and scene locks are not quantized, and a NOTE set before the scale was on plays as stored until it is turned. Emulator measurements price one FM track at VOIC 4, or three at VOIC 1, inside every audio block including the first START after boot. Larger loads (two or more tracks at VOIC 4, eight tracks) exceed the conservative per-block budget on their START blocks. They are not shown to fail on the unit, only not qualified. See TESTING.md for the table. @@ -68,11 +71,11 @@ External MIDI note/CC traffic, Part/project reloads and a power cycle on hardwar ## Tests and measurements -See [TESTING.md](TESTING.md). The native image used for the captures and measurements is built by Modwerk’s own native exporter from this source. The author’s page links byte for byte to the bytes he ratified. Worst-case ColdFire instruction counts and exact memory come from the pinned emulator and the image’s link map. The hardware report fields stay pending until the author’s MKI test of this exact image (MAIN OS `00fa6caa…`). Release notes are in [release-notes.json](release-notes.json). +See [TESTING.md](TESTING.md). The native image used for the captures and measurements is built by Modwerk’s own native exporter from this source. The author’s page links byte for byte to the bytes he ratified. Worst-case ColdFire instruction counts and exact memory come from the pinned emulator and the image’s link map. The hardware report fields stay pending until the author’s MKI test of this exact image (MAIN OS `37661aee…`). Release notes are in [release-notes.json](release-notes.json). ## Authorship and licences -Tim Hastie: FM engine, voices/chords, MIDI/legato, the FM SYNTH chooser (2.10, adapted from Modwerk’s), its 2.11 machine-list table and 2.12 machine-change init, and the matching quantizer, pinned to `ca13ed85547790f788ceaaada5df3cdd31508441` (tag `v2.12`) of [timhastie/octatrick-modules](https://github.com/timhastie/octatrick-modules). Sam Banks: octabam wrapper/platform, pinned to `949f3be15eae5d3d16a7682b9e3218d42f6c1284`. Modwerk contributors: the original dedicated chooser and Part validation (0.1.1), the quantizer drawer fix (0.1.2), verification, docs and original SVG. Full MIT terms are in [LICENSE](LICENSE), with the author originals retained. The import inventories are [synth-949f3be.json](../../../imports/synth-949f3be.json), [synth-fixes-273-264.json](../../../imports/synth-fixes-273-264.json) and, for this version, [synth-ca13ed8.json](../../../imports/synth-ca13ed8.json). +Tim Hastie: FM engine, voices/chords, MIDI/legato, the FM SYNTH chooser (2.10, adapted from Modwerk’s), its 2.11 machine-list table and 2.12 machine-change init, and the matching quantizer (2.13: MIDI tracks), pinned to `48b89e7e5315971bf5c29a3fd86183abe8df48f3` (tag `v2.13`; the engine, page and chooser files are the same bytes as `v2.12` `ca13ed8`) of [timhastie/octatrick-modules](https://github.com/timhastie/octatrick-modules). Sam Banks: octabam wrapper/platform, pinned to `949f3be15eae5d3d16a7682b9e3218d42f6c1284`. Modwerk contributors: the original dedicated chooser and Part validation (0.1.1), the quantizer drawer fix (0.1.2, #264; the author’s 2.13 quantizer takes its form), verification, docs and original SVG. Full MIT terms are in [LICENSE](LICENSE), with the author originals retained. The import inventories are [synth-949f3be.json](../../../imports/synth-949f3be.json), [synth-fixes-273-264.json](../../../imports/synth-fixes-273-264.json) [synth-ca13ed8.json](../../../imports/synth-ca13ed8.json) (0.1.4) and, for this version, [synth-48b89e7.json](../../../imports/synth-48b89e7.json). No firmware, extracted stock byte blobs/tables, cards, RAM dumps or compiled images are distributed. Stock expectations resolve lazily from the verified developer-owned OS by address, length and SHA-256. @@ -80,12 +83,10 @@ No firmware, extracted stock byte blobs/tables, cards, RAM dumps or compiled ima **Audio preview.** [Listen](media/audio-preview.mp3). A Vector-generated minor synth phrase at 128 BPM, entered as FM note locks. INDX sweeps 44→92 and FDBK 0→28, with Mini Verb in FX2 (DECAY 96, DAMP 58, MIX 36). One common gain, no added gaps or external samples. Captured from FM Synth 0.1.2 and Mini Verb 0.2.1 running in Octemu on a computer; no physical-hardware recording. VECTOR’s generator supplies the notes offline; the instrument sequencer plays the FM locks. Exact sequence, settings, image identity, panel walk and render details are in [audio-preview.json](media/audio-preview.json); [media rights](media/LICENSE.md) covers the generated audio. -These eight captures are actual monochrome emulator LCD exports of the native 0.1.4 image, visually reviewed, bound to it in [media/capture.json](media/capture.json). They are emulator evidence; no hardware capture is claimed. See [media/LICENSE.md](media/LICENSE.md) for the interface rights declaration and [presentation/thumbnail.svg](presentation/thumbnail.svg) for original art. +These seven captures are actual monochrome emulator LCD exports of the native 0.1.5 image, visually reviewed, bound to it in [media/capture.json](media/capture.json). They are emulator evidence; no hardware capture is claimed. See [media/LICENSE.md](media/LICENSE.md) for the interface rights declaration and [presentation/thumbnail.svg](presentation/thumbnail.svg) for original art. ![FM SYNTH is the dedicated sixth machine row in SRC SETUP.](media/ot-location.png) -![FM SYNTH is also the sixth row of SELECT MACHINE TYPE.](media/ot-machine-list.png) - ![PTCH, RATO, INDX, FINE, FDBK and DEC replace the FLEX source controls.](media/ot-controls.png) ![VOIC and CHRD occupy the third LFO speed and depth controls.](media/ot-voices.png) @@ -94,8 +95,8 @@ These eight captures are actual monochrome emulator LCD exports of the native 0. ![LEG selects OFF, MONO or POLY on an FM track.](media/ot-legato.png) -![The privately bundled quantizer adds SCALE; this example selects MAJOR.](media/ot-scale.png) +![The privately bundled quantizer adds SCALE, ROOT and GLIDE; this example is MINOR with ROOT A and GLIDE OFF.](media/ot-glide.png) -![GLIDE sets pitch slide time; this example shows OFF.](media/ot-glide.png) +![On a MIDI track with a scale on, the CHROMATIC keyboard plays the scale from ROOT; the number beside it reads A 3 (ROOT A, octave 3).](media/ot-midi-chromatic.png) -No audio files are retained in this module. +Only sanitized audio measurements are retained. The temporary Octemu WAV, card, battery file and firmware remain local and are excluded from the submission. diff --git a/sdk/octabam/modules/synth/TESTING.md b/sdk/octabam/modules/synth/TESTING.md index 3bda830a4..d07c2f257 100644 --- a/sdk/octabam/modules/synth/TESTING.md +++ b/sdk/octabam/modules/synth/TESTING.md @@ -1,61 +1,66 @@ -# FM Synth 0.1.4 testing +# FM Synth 0.1.5 testing -0.1.4 is an update of Modwerk's published FM Synth (0.1.1 of 6 October, 0.1.2 of 8 October) to the author's octatrick 2.12 line, `timhastie/octatrick-modules` at `ca13ed85547790f788ceaaada5df3cdd31508441` (tag `v2.12`). Two earlier local drafts were never submitted: 0.1.3 on the 2.10 line (`9bd7169`, MAIN OS `d5e1a7b0…`) and 0.1.4 on the 2.11 line (`5f3d745`, MAIN OS `248d987e…`). The 2.11 build is compared below by its MAIN OS hash. This file is the report that `tests.qualification` names. Earlier reports are kept unchanged: `evidence/release-0.1.1/TESTING.md` and `evidence/release-0.1.2/TESTING.md`. +0.1.5 updates FM Synth 0.1.4 (the author's octatrick 2.12 line, still unpublished) to the author's 2.13 quantizer, `timhastie/octatrick-modules` at `48b89e7e5315971bf5c29a3fd86183abe8df48f3` (tag `v2.13`). At that commit the engine, page and chooser files (`synth/`) are the same bytes as at `v2.12` (`ca13ed8`); the quantizer (`quantizer/`), the repository's `LICENSE` and README change. The published version before 0.1.4 is 0.1.2. This file is the report that `tests.qualification` names. Earlier reports are kept unchanged: `evidence/release-0.1.1/TESTING.md` and `evidence/release-0.1.2/TESTING.md`. 0.1.4's measurements are carried below where its objects are byte-identical in this image, and named as such. -**Status.** Emulator worst-case cycles, exact memory, the 8 October persistence and two-instance checks and the two 2.12 behaviour changes are measured on the exact 0.1.4 image (MAIN OS `00fa6caa…`). The module's own native Part smoke passes on that image. The hardware fields are `TIM:` placeholders until the author reports his MKI test of this exact image (`hardwareStatus` stays `historical` until then). Modwerk's owner approvals are kept as they are: 0.1.1's in `evidence/release-0.1.1/` (`owner-hardware.md`, `build-approval.json`), and 0.1.2's in `sdk/synth-build-approval.json`, copied to `evidence/release-0.1.2/build-approval.json`. They cover only those versions and are not extended to 0.1.4. +**Status.** On the exact 0.1.5 image (MAIN OS `37661aee…`), in the pinned emulator: the quantizer's new MIDI-track behaviour against a model of its rules, SCALE OFF against stock, the quantizer's added cost per call, the module's own native Part smoke, the 8 October persistence and two-instance checks, the 2.12 machine-change init, exact memory, the stack bound, the LCD captures and the author's odd-address check. The frame-interrupt cycle table is 0.1.4's (the FM engine and chooser are the same compiled objects at the same addresses), with its bounding cards rerun on this image. The hardware fields are `TIM:` placeholders until the author reports his MKI test of this exact image (`hardwareStatus` stays `historical` until then). Modwerk's owner approvals are kept as they are: 0.1.1's in `evidence/release-0.1.1/` (`owner-hardware.md`, `build-approval.json`), and 0.1.2's in `sdk/synth-build-approval.json`, copied to `evidence/release-0.1.2/build-approval.json`. They cover only those versions and are not extended to 0.1.4 or 0.1.5. -## What changed from 0.1.2 +## What changed from 0.1.4 -**The 2.12 changes.** Both are the author's, in `upstream/synth/poly.s`, `machine.s` and `manifest.py` (his README describes them with his measurements): +**SCALE and ROOT on MIDI tracks (the author's 2.13 quantizer).** With a scale on (PROJECT > CONTROL > SEQUENCER, SCALE and ROOT), a MIDI track's notes follow the scale where they are edited or played from the panel: -- **The metronome no longer cuts a held note.** Since the 2.9 line the engine hooked the frame builder's `clrl 0x46c80350` at `0x4000b2c8` (`po_stop`), taking that word for the sequencer's STOP word. It is the metronome's click word: while the sequencer plays with CLICK ON, the beat code writes 3 or 1 into it, and the frame builder turns it into the DSP click command and clears it. So `po_stop` ran at every click and released every held note and chord. 2.12 removes the detour; `0x4000b2c8` is stock again. STOP still ends the engine's voices through `po_kill` at the stock voice kill (`0x4000685c`). -- **A machine change resets the track to init.** Changing a track to or from FM SYNTH, through SRC SETUP or SELECT MACHINE TYPE (`fm_choose`), the slot assigner (`po_assign`) or the file browser (`po_loadsel`), runs `ml_reset` in `machine.s`. It writes the new machine's init into the track's PLAYBACK and PLAYBACK SETUP (FM SYNTH's own seeds, or a stock machine's PLAYBACK descriptor defaults), AMP, AMP SETUP, the LFO page and LFO SETUP (the stock new-track values, read at run time from the same stock descriptors the stock Part initialiser uses; FM SYNTH puts VOIC 1 and CHRD off in LFO 3's bytes), in the Part, its battery-RAM shadow and the live lane. Step locks and scene locks are kept and apply by position, as at a stock machine change; FX1 and FX2 stay. One new detour supports it: `po_unload` (`jsr`, 6 B) at `0x40022732`, in the file browser's load callback, keeps the FLEX slot's marker state across the browser's slot unload so `po_loadsel` sees the change. A change between two stock machines is untouched. +- **The NOTE knob** (the NOTE page's first knob, on the Part's value): one click steps to the next note of the ROOT-rotated scale in the turn direction instead of one semitone; a value between scale notes goes to the nearest scale note in the turn direction; at 0 and 127 it stays. Hook `qz_mknob` (`jsr`, 8 B) at `0x40055382`, the knob handler's MIDI-track clamp just before its store `0x4005538a`, which stays stock. +- **NOTE locks**: the same, with a TRIG key held (from the step's lock, or from the Part's NOTE when the step has none), and the lock a NOTE turn writes during LIVE RECORDING. Hook `qz_mplock` (`jsr`, 10 B) at `0x40050b98`, the MIDI p-lock editor's lock store. +- **The MIDI CHROMATIC keys**: each key plays the key's note plus ROOT, snapped to the nearest scale note (the lower one on a tie), so key 1 sounds the root; the number beside the keyboard reads the root's name with stock's octave ("A 3" where stock reads "3"). The note-on, the arpeggiator fed by the keys, the note-off at the key's release (stock stores the note at the press) and LIVE RECORDING carry the snapped note. Hook `qz_mkey` (`jmp`, 10 B) at `0x400436a8`, the press path. -**The 2.11 machine list.** The author's 2.11 line makes the rows after PICKUP a table (`ml_rows` in `machine.s`), assembled per build through each unit's `remix.inc`: `HAVE_SYDRUM` (1 only when his SY DRUM module is in the build) and the equates shared with SY DRUM (`engine_abi.inc`). FM SYNTH is always the first table row. The six row-count and row-bound constants that 2.10 poked (`pea 5` → 6 and five `moveq #4` → 5) became six detours that read the table's count (`0x40079248`, `0x400585fa`, `0x4003c950`, `0x40078678`, `0x400786ce`, `0x40079904`). Five more detours widen the machine window's row clamp after a list move (`0x40078c22`, `0x40078cb0`, `0x40078d2e`, `0x40078dee`, `0x40078e7c`). The drawer detour at `0x40078678` draws `name(row + the list's top)`, so SELECT MACHINE TYPE scrolls like SRC SETUP once a build has more than six rows. This package has FM SYNTH alone: one table row, the same six rows as 0.1.2, and `HAVE_SYDRUM` 0. The bundled quantizer (`fm_qz`) assembles to exactly the 2.10 and 2.11 builds' code; the engine (`poly`, 20,996 B) differs from the 2.11 build's only by 2.12's two changes, and the chooser unit `fmmachine` grows from 1,440 to 2,076 B with the machine-change init. The author's manifest also declares Analog BD as a native `conflicts` entry; the wrapper clears it, because Modwerk carries conflicts in `octamod.module.json` (it lists `analog-bassdrum` already) and a native entry would switch on the browser's span-contract check for every module. +A note's pitch class is its MIDI number mod 12, tested against the same root-rotated scale mask the audio tracks use. NOT2–NOT4 stay semitone offsets from NOTE, TRAN transposes in semitones, and the arpeggiator's own notes, MIDI IN, VEL, LEN, CCs, LFOs on NOTE and scene locks are stock: the author's choice (editing only), because a drum machine's note numbers on a MIDI track are sounds, not pitches; stock's ARP SETUP > KEY forces every note a track sends into a key. **SCALE OFF is stock**: every new detour replays what it displaced. Nothing new is stored: SCALE and ROOT are the existing battery-RAM bytes, and no Part or project format changes. -**Source.** These files are the author's ca13ed8 (v2.12) files byte for byte: `upstream/synth/poly.s`, `page.s`, `machine.s`, `engine_abi.inc`, `README.md` and `upstream/LICENSE`. Every measurement below was made on the image built from them, MAIN OS `00fa6caa…`. `upstream/synth/manifest.py` is the author's manifest with every stock expectation replaced by `stock_guard(address, length, SHA-256)`. There are 53 guards, one per hook site (2.12 drops the `po_stop` site's guard and adds `po_unload`'s; the five clamp sites share one hash), each checked against the developer's MAIN OS `164f3122…`; `stock-guards.json` lists all 73 with the quantizer's 20. The manifest's precompiled `PINNED_PAGE`, `emit_page` and `CavePatch` are removed. `upstream/quantizer/` is unchanged from v2.11 in v2.12: `quantizer.s` is the author's file plus Modwerk 0.1.2's octave-drawer fix (#264) at the common argument push `0x400449be`, the same site and behaviour as the public Scale Quantizer's 0.1.3 MIDI fix; the author's file recognizes the FM/1 signature in `qz_is_synth`, and SY/1 only when `HAVE_SYDRUM` is 1. The quantizer manifest is Modwerk 0.1.2's with the author's 2.11 per-build include on its DRAM unit. The import inventory is `sdk/imports/synth-ca13ed8.json`. +**The #264 drawer fix is the author's own.** Modwerk's 0.1.2 moved the bundled quantizer's CHROMATIC drawer hook from `0x400449b8` to the drawer's common argument push `0x400449be`, because stock's MIDI branch jumps to `0x400449be` and the old hook skipped the octave push there (CHROMATIC on a MIDI track froze the unit). The author's public line carried the old site from 2.9 to 2.12; his 2.13 takes Modwerk's form at the same site and credits it (his `quantizer/README.md`, "THE FREEZE FIXED (Modwerk #264)", and his `LICENSE`). So the bundled `quantizer.s` no longer carries a Modwerk patch: it is the author's file byte for byte, and its `qz_octnum` keeps stock's number on a MIDI track (with ROOT's name before it when a scale is on). FM Synth images were never affected: 0.1.2 and 0.1.4 carried Modwerk's form. -**The chooser is now the author's.** His `machine.s` is adapted from Modwerk's 0.1.1 chooser and keeps the same Part signature, FLEX plus `FM`, 1 in the track's NEIGHBOR PLAYBACK bytes. Projects made with 0.1.1 or 0.1.2 therefore open as FM tracks. Hooks dropped from 0.1.2: +**Source.** `upstream/quantizer/quantizer.s`, `core.s`, `keys.s`, `scale.s` and `README.md`, and `upstream/LICENSE`, are the author's `48b89e7` files byte for byte; `upstream/synth/*` is unchanged from 0.1.4 (the same bytes at `48b89e7` as at `ca13ed8`). `upstream/quantizer/manifest.py` is the author's 2.13 manifest in Modwerk's stock-guard form: each of its 23 embedded stock expectations (22 detour sites and the SEQUENCER poke's `expect`) became `stock_guard(address, length, SHA-256)`, each literal checked against the developer's MAIN OS `164f3122…` before it was replaced, and the `stock_guard` import is added; descriptions, the per-build include and the poke's write are the author's. `stock-guards.json` lists 76 guards: the synth manifest's 53 (unchanged) and the quantizer's 23 (three new: `0x40055382` 8 B, `0x40050b98` 10 B, `0x400436a8` 10 B). The module `LICENSE` carries the author's 2.13 `LICENSE` in full (one added sentence on the drawer hook's origin). The wrapper `manifest.py` changes only its docstring and proof note. The import inventory is `sdk/imports/synth-48b89e7.json` (it follows 0.1.4's `synth-ca13ed8.json`). -- `machine.s`, `registration.c` and `registration.s` (units `fmmachine` / `fmregistration`). -- `fm_resolve_pb` at `0x40031e74`: the author routes PLAYBACK through `pg_resolve` only. -- `fm_name_b` at `0x4004c36a`. -- `fm_main_commit` at `0x4007991c`: the author commits at the machine window's MACHWIN site `0x40079816`. -- The `fm_start` SymbolRef at `0x400d6458` and the `fm_source_dispatch` detour at `0x4000d514`. The author's own `po_fmstart` and `po_fmsource` replace them at the same sites. -- The `pg_resolve` detour at `0x40031ece`. It becomes a guarded poke writing the author's `emit_page` jump `jmp 0x400d24e0`, so `page.s` stays byte-identical. +**Compatibility.** The public Scale Quantizer and Analog BD stay declared conflicts. POLY8's chooser bridge is still bound to 0.1.2's chooser (see "Composition parity"). Modwerk's MIDI Scenes builds only on its own (`src/engine/compose-os.ts` refuses any other module beside it), so it never shares an image with FM Synth; its MIDISC2.0 writes cover the knob store `0x4005538a` (8 B) and none of the three new sites, which end before it. No conflict entry is added. -The other chooser sites (`0x400334d8`, `0x4003c928`, `0x4003d718`, `0x4003c980`, `0x400786c8` now 6 B, `0x400585dc`, `0x40078886`, `0x4005221e`, `0x4003a52e`, `0x4003cd98`, `0x4005a616`, `0x4005a850`, `0x40002318`) are the same sites, now running the author's code; the six chooser pokes are the 2.11 detours above. Two author detours that 0.1.1 removed come back: MACHWIN (`0x40079816`, into `fm_main_commit` and then `po_machwin`) and LISTWIN (`0x4005a848`, `po_machlist`). The three 2.10 LFO-list detours (`0x4003bff2`, `0x4003bffa`, `0x400392cc`) are new. The Part validator wrap is the author's `fm_validate` at the same site; Modwerk's wrap is no longer needed. The private quantizer bundling and its `fm_*` relabelling are kept as Modwerk wrote them. - -**Behaviour.** The 2.10 changes and the 7–8 October fixes are described, with the author's measurements, in `upstream/synth/README.md`: - -- DEC 127 = HOLD, and 0 = the shortest decay. -- Note starts no longer click. -- The LFO destination list shows the FM names. -- A held key is the gate. -- A note restarted at a short DEC no longer clicks. -- Fast VOIC 2–4 runs no longer lose notes. -- Recorded legato phrases keep sounding. -- FM SYNTH chosen after a sample plays C4 at FINE 0c. - -**Headroom: 6 dB, the author's decision.** 0.1.2's `OUTPUT_SHIFT` (18.06 dB source attenuation for #273) was Modwerk's change to `poly.s`. The author's 2.10 engine, unchanged in 2.11 and 2.12, makes its own: one arithmetic shift right (`HEADROOM`, 6.02 dB) at the mono voice's output and at each paraphonic voice's contribution before the sum, the same places 0.1.2 shifted by 3. Envelopes, voice ratios (`1 / sqrt(VOIC)`), crossfades and the final saturating clamp are unchanged; a VOIC 4 chord's coincident peaks now reach full scale instead of twice it. AMP VOL 0 is the reference and VOL up to about +6 stays clean. At identical settings 0.1.4 is about 12 dB louder than 0.1.2. The author measured it on his own build: -6.02 dB on every render, each sample the previous one halved within 1 LSB (`upstream/synth/README.md`, "6 dB of headroom (2.10)"). +The 0.1.4 changes (the 2.12 metronome fix and machine-change init, the 2.11 machine list, the author's chooser, the 2.10 engine changes and the 6 dB headroom) are unchanged; they are described in the module README and `upstream/synth/README.md`, and the sections below that carry 0.1.4's results say so. ## Build and identities | item | value | | --- | --- | -| Module | `synth` 0.1.4-experimental; native source SHA-256 `4fc06072b4039db83cba1600e949d00770c9753d974bbf76a5809f8e1be9e851` | -| Image | MAIN OS SHA-256 `00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e` (1,126,653 B): FM Synth with its bundled quantizer and all stock effects. It is built from this source by `scripts/export-composition-proofs.py --vendored-sdk --static-stock --modules synth --select synth:true`. The LCD captures (`media/capture.json`), the measurements below, the Part smoke, the persistence checks and the 2.12 checks all used this image. | -| Update file for the hardware test | 462,396 B, SHA-256 `2267af13293b302a4bd88bf3c0c753965e36bb7861d2609875060303f1072909` (OCTAMOD79). Modwerk's own TS packer decodes it to the MAIN OS above. Re-encoding the stock update with that MAIN OS gives the same bytes. | +| Module | `synth` 0.1.5-experimental; native source SHA-256 `401d3b61412aff964617581f9f0bfcfe0ac83d6a6ee970bb1440f2393a6ac60a` | +| Image | MAIN OS SHA-256 `37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236` (1,126,857 B): FM Synth with its bundled quantizer and all stock effects. It is built from this source by `scripts/export-composition-proofs.py --vendored-sdk --static-stock --modules synth --select synth:true`. The LCD captures (`media/capture.json`), the measurements below, the Part smoke, the persistence checks and the quantizer gates all used this image. | +| Update file for the hardware test | 462,628 B, SHA-256 `b42048d57e487869fc9662657ffaf36bc8c54498fa9dda431097bc32e216b68f` (OCTAMOD79). Modwerk's own TS packer decodes it to the MAIN OS above. Re-encoding the stock update with that MAIN OS gives the same bytes. | +| Against 0.1.4's image | MAIN OS `00fa6caa…` (1,126,653 B). The platform runtime's first 23,072 bytes, the engine unit `poly` (20,996 B) and the chooser unit `fmmachine` (2,076 B), are byte-identical and at the same addresses (`0x40a955e0..0x40a9b000`); their objects `00_poly.o` and `01_fmmachine.o` are byte-identical. The page `fmpage` and the quantizer's ROM units (`fm_qzc` 192 B, `fm_qzk` 45 B, `fm_qzs` 6 B) are unchanged. The other differences are the quantizer's: its DRAM unit (3,532 → 3,892 B), the three new hook sites, the operands of its existing detours and SEQUENCER table entries that point into the relinked unit, and the appended loader that packs the runtime (14,093 → 14,297 B). No FM hook site changes. | | Page | `fmpage` 1,868 B linked at `0x400d24d0`; the build log reports "matches the author's build" against reference `b43d76c9c13c1441f16e32b85826ac2e7664352947fa4dc393401ff332710f92` | -| Address check | the author's `tools/align_check.py` (v2.12) on this build: 5,723 instruction rows in the five linked objects, all at even addresses, and 75 detour / symbol-ref / poke targets even, every jump into them on an instruction (ColdFire faults on an odd jump target and the emulator does not model it) | -| Stock bytes | no stock bytes are committed. In the compiled FM units the code's stock-equal runs are the displaced-instruction replays at the hook sites, at most 22 bytes (as in the 2.10 and 2.11 builds); the 2.12 code adds none. The longest stock-equal run of any kind, leaving out fills, is 32 bytes, mostly zero, in the page's icon tables (`pg_img_ratio` / `pg_img_fdbk`), unchanged since 2.10. | +| Address check | the author's `tools/align_check.py` (2.13, `48b89e7`) on this build: 5,843 instruction rows in the five linked objects, all at even addresses, and 78 detour / symbol-ref / poke targets even, every jump into them on an instruction (ColdFire faults on an odd jump target and the emulator does not model it) | +| Stock bytes | no stock bytes are committed. The author's `tools/stock_scan.py` (2.13) over every file of the module folder finds no run of 16 or more stock bytes in any data (manifests, JSON, text). Over the assembled code it finds the displaced-instruction replays and shared assembler idioms only: the bundled quantizer's six hits are exactly those of the author's reviewed 2.13 scan (at most 22 bytes), and the FM units' hits are 0.1.4's (at most 22 bytes; the page's 32-byte icon-table run is mostly zero and is filtered as low-entropy). | | Emulator | pinned octabam `ot_emu`, SHA-256 `9dc31d1249793fcf857fa6b1c1ac72ab7966646fd59f3e09e90683c8eb41e711`, not rebuilt | The image, the emulator, the cards and the rig scripts stay local. Only their hashes and the results enter this repository. +## The quantizer on MIDI tracks (this image) + +The author's own gate scripts for 2.13, unchanged, on this image: the pinned emulator with the DSP running, the MKII panel and real keys and encoders, MIDI track 1 on channel 1, SCALE and ROOT written into their battery-RAM bytes between cases, and MIDI OUT captured as the bytes the unit's MIDI port transmits. Expected values come from a model of the rules (the author's `qmodel.py`). The SCALE / ROOT settings: OFF, OFF with ROOT A, MAJOR C, MINOR A, PENT.MN F#, WHOLE B, HUNG.MIN D#, MAJOR F#. + +- **NOTE knob and NOTE locks: 409 of 409 as the model.** 384 knob turns (the eight settings × eight start values on and off the scale, 0, 1, 126 and 127 × ±1, 2 and 3 clicks) and 25 NOTE-lock checks (a TRIG key held, from no lock, four turns, in five settings, the Part's NOTE untouched). The same script on 0.1.4's image matches the stock clamp 409 of 409: the two SCALE OFF settings give the same values on both images. +- **CHROMATIC keys: 245 of 245.** Keys 1–16 at stock's octave words 0, 3, 4, 5 and 10 in the eight settings (240 keys): every note-on as the model and each with its note-off; a key held across a ROOT change and two keys held across SCALE OFF release their notes; two keys that snap to the same note send one note-on and one note-off; keys feeding the arpeggiator (ARP MODE UP), stopped and playing, give the snapped notes (A3, D4 under MINOR A). +- **CHROMATIC on a MIDI track draws and plays** (the freeze of the author's 2.9–2.12 public tags, #264): every key case above entered CHROMATIC on MIDI track 1, and the capture `media/ot-midi-chromatic.png` shows the keyboard with the readout "A 3". +- **LIVE RECORDING**: two NOTE clicks up from C#3 under MINOR A write the lock E3 on the playing step; every peeked lock and Part byte is equal to the author's run of the same script on his 2.13 build. +- **The sequencer is unchanged.** The author's sequencer gate (trigs with NOT2–NOT4, NOTE locks at 0 and 127, TRAN, ARP SETUP KEY and the arpeggiator; MIDI IN thru and the auto channel, direct and into the arpeggiator), each under SCALE OFF, MAJOR C, MINOR A and PENT.MN F#: every capture gives the same MIDI events on this image as on 0.1.4's, and within this image the same events under every SCALE setting (editing only). The one intended difference: a NOTE set by two clicks up from C#3 under MAJOR C is E3 here and plays E3 F3 G3 with its chord (0.1.4: D#3, D#3 E3 F#3). + +**Cost** (emulator instruction counts per call, not a hardware share; SCALE OFF, MAJOR C, MINOR A, PENT.MN F#, WHOLE B, HUNG.MIN D#; each counted from the hook site to its continuation, so it includes the displaced stock instructions): + +| call | SCALE OFF | scale on | 0.1.4's image (stock code) | +| --- | ---: | ---: | ---: | +| MIDI CHROMATIC key press (`0x400436a8` → `0x400436b2`) | 6 | 49–77 | 2 (the author's stock measurement) | +| NOTE knob turn, 1–3 clicks (`0x40055382` → `0x40055392`, the clamp, the hook and the store) | 10 | 104–278 | 5 | +| NOTE lock turn, 1–3 clicks (`0x40050b98` → `0x40050ba2`) | 8 | 117–302 (see below) | 2 (the author's stock measurement) | +| sequencer tick (`0x400a1dfe` call → return, not hooked) | mean 1,137.9, max 2,510 | mean 1,137.9, max 2,524 | — | + +Each click walks the notes up to the next scale note, so a scale with fewer notes costs more; the counts equal the author's measurement on his 2.13 build, window for window, but one: in both full runs on this image the first NOTE-lock window under PENT.MN F# counted 1,165. A run that also armed `qz_mplock`'s entry and the frame interrupt's entry and exit counts the same window at 173 (the author's figure), with `qz_mplock` entered one instruction after the site and no frame interrupt inside; the 1,165 therefore includes work outside the hook's own path, not identified. All three hooks run in the UI task when a knob is turned or a key pressed; none runs in the frame interrupt. + ## Native Part smoke -The module's own `verify.py` (Modwerk's 0.1.1 gate) ran unchanged on this image. It checks: +The module's own `verify.py` (Modwerk's 0.1.1 gate) ran unchanged on this image (MAIN OS `37661aee…`). It checks: - selecting FM SYNTH through FUNC + SRC on all eight tracks; - the machine byte FLEX, and `FM`, 1 in the Part and in the battery-RAM shadow; @@ -64,51 +69,56 @@ The module's own `verify.py` (Modwerk's 0.1.1 gate) ran unchanged on this image. - reselection, which keeps an edited control; - the return to FLEX, which clears the signature. -All five checks passed (`evidence/native-smoke.json`). The author's chooser keeps Modwerk's signature and layout bit for bit; reselecting FM SYNTH on an FM track is not a machine change, so 2.12's init does not touch it. `verify-audio.py` and `verify-regressions.py` need Octemu, which is not available here, and were not rerun for 0.1.4; the 0.1.2 results remain in `evidence/release-0.1.2/`. - -## The 2.12 changes on this image +All five checks passed (`evidence/native-smoke.json`). The author's chooser keeps Modwerk's signature and layout bit for bit; reselecting FM SYNTH on an FM track is not a machine change, so 2.12's init does not touch it. `verify-audio.py` and `verify-regressions.py` need Octemu, which is not available here, and were not rerun for 0.1.4 or 0.1.5; the 0.1.2 results remain in `evidence/release-0.1.2/`. +## The 2.12 changes -Both were checked in the pinned emulator on this image (DSP running, MKII panel, real keys and encoders; private scripts and cards kept local). +Both were checked in the pinned emulator (DSP running, MKII panel, real keys and encoders; private scripts and cards kept local). The machine-change walk ran again on this image; the metronome case is 0.1.4's, on its image `00fa6caa…`, whose frame-interrupt code is this image's (the engine is the same object at the same address, and the quantizer adds no frame-interrupt code). **The metronome case.** The author's own gate, unchanged: a sequencer playing at 120 BPM, a key (VOIC 1) or a VOIC 4 chord held for 9.1 s (18 beats), the metronome ON against the same run with it OFF, track 1's stem compared sample by sample from the key-down. -| case | this image, metronome ON vs OFF | the 2.11 build (`248d987e…`), metronome ON | +| case | 0.1.4's image `00fa6caa…`, metronome ON vs OFF | the 2.11 build (`248d987e…`), metronome ON | | --- | --- | --- | | a key held, VOIC 1 | sample-identical over the 9.14 s hold (0 samples differ); the engine keeps the note (S_ON never set released) | released at the first clicks: `po_stop` cleared the click word 24 times, S_ON went to released + STOPPED, the stem fell to silence (-90 dBFS) within 4 s | | a chord held, VOIC 4 | the four voices stay in state 1 (sounding) for the whole hold; 500 ms levels within 0.02 dB of OFF (not sample-identical: the four panel keys land about 16 samples apart between runs) | every voice released (state 2) at the first clicks; the stem fell to silence within 4 s | -The click itself still sounds on both images: in each ON run the frame builder posted the DSP click command 30 times. On this image `po_stop` no longer exists and nothing writes the click word except stock's own code. +The click itself still sounds on both images: in each ON run the frame builder posted the DSP click command 30 times. On 0.1.4's image (and this one) `po_stop` no longer exists and nothing writes the click word except stock's own code. -**The machine-change init.** T1 of an empty project, through SRC SETUP: FLEX → FM SYNTH, then PLAYBACK, LFO, AMP and FX1 edited, → FLEX, → FM SYNTH again. The Part bytes after each step: +**The machine-change init** (this image). T1 of an empty project, through SRC SETUP: FLEX → FM SYNTH, then PLAYBACK, LFO, AMP and FX1 edited, → FLEX, → FM SYNTH again. The Part bytes after each step: - FM SYNTH chosen: FLEX + `FM`, 1; PLAYBACK at FM SYNTH's seeds (PTCH 0, RATO 1, INDX 32, FINE 0c, FDBK 0, DEC 40); the LFO page at the stock new-track values with VOIC 1 and CHRD off; AMP at the stock new-track values. - FLEX chosen after the edits: the signature cleared; the FLEX PLAYBACK column back at FLEX's defaults (the project's original bytes); the LFO page and AMP at the stock new-track values; FX1 as edited. - FM SYNTH chosen again: PLAYBACK, LFO, AMP and the setup pages byte-identical to the first choice; FX1 still as edited; FX2 untouched throughout. -All 10 checks passed. LFO SETUP and AMP SETUP were not edited in this walk, and step and scene locks were not part of it; the verification of the author's 2.12 line covered every route, the locks kept, SAVE and a cold boot on his builds of the same `machine.s`. - +All 10 checks passed, as on 0.1.4's image. LFO SETUP and AMP SETUP were not edited in this walk, and step and scene locks were not part of it; the verification of the author's 2.12 line covered every route, the locks kept, SAVE and a cold boot on his builds of the same `machine.s`. ## Persistence and two instances (8 October rules) -Two FM tracks with different settings, on this image in the pinned emulator (DSP running, MKII panel, real keys and encoders; private walk scripts, the card kept local): +Two FM tracks with different settings, on this image (MAIN OS `37661aee…`) in the pinned emulator (DSP running, MKII panel, real keys and encoders; private walk scripts, the card kept local): - **Two instances.** T1 and T5 were chosen as FM SYNTH through FUNC + SRC and edited differently (four SRC controls each; on the LFO page T1 VOIC 1 with a chord shape, T5 VOIC 4 with another), with trigs on both. Editing T1 left T5 unchanged (passed); editing T5 left T1 unchanged (passed); both carry FLEX + `FM`, 1 and their own PLAYBACK and LFO bytes (passed); the five sample-slot bytes of both tracks never changed (passed). - **Part save / reload.** FUNC + PART → SAVE, later edits on both tracks, FUNC + PART → RELOAD: both tracks back to the saved bytes (passed). - **Project save / reload.** PROJECT → SAVE (no card-write error), unsaved edits on both tracks, PROJECT → RELOAD: both back to the saved bytes (passed). - **Fresh project load.** A new emulator process mounted the saved card and loaded the project: both tracks as saved (passed). -- **Audio after the transitions.** Main-output renders of the two tracks' trigs: after the edits RMS 1267.1, after the project reload 1265.9, after the fresh load 1265.7 (16-bit, peak 3638): sound in every case (passed). The level differs from the 2.11 build's run of the same walk because choosing FM SYNTH now resets AMP to the stock new-track values. +- **Audio after the transitions.** Main-output renders of the two tracks' trigs: after the edits RMS 1267.2, after the project reload 1265.9, after the fresh load 1265.6 (16-bit, peak 3638): sound in every case (passed); 0.1.4's image gave 1267.1, 1265.9 and 1265.7 on the same walk. - **Not tested here: the battery-RAM-only restart.** The pinned emulator keeps no battery RAM between processes, and a fresh process followed by an explicit project load is not a power cycle. The power cycle with the card in (both tracks back without loading the project) is on the author's hardware sheet for this image. - ## Composition parity -**Open: POLY8.** Main carries POLY8 0.2.6, whose shared chooser bridge (`sdk/octabam/modules/poly8/composition.json`, applied by `tools/remix/machine_composition.py` and `src/engine/requested-modules.ts`) replaces FM Synth's chooser hooks with its own and checks every hook it replaces against the exact claims of FM Synth 0.1.2 (Modwerk's chooser). The author's chooser, in 0.1.3 already and in 0.1.4, makes different claims at two of those sites (`0x4005221e` is `fm_tick`, `0x400786c8` takes 6 bytes), drops four of them, and turns the six count pokes into detours. Both sides therefore stop on any selection with POLY8 and FM Synth ("seam drift"), as the bridge is designed to. On main `3151975`, `npm run module:verify -- synth` stops at the first such selection (`POLY8 composition seam drift: synth detours 0x400786c8`), and two POLY8 tests in `src/engine/requested-modules.test.ts` fail. Making the pair build again needs POLY8's bridge to be reviewed for the new chooser (its shared machine also calls 0.1.2's registration functions `fm_type`, `fm_admit_track`, `fm_track_page`, `fm_ui_tick` and `fm_defaults`). That is POLY8's code; this update does not change it. +**Open: POLY8.** Main carries POLY8 0.2.6, whose shared chooser bridge (`sdk/octabam/modules/poly8/composition.json`, applied by `tools/remix/machine_composition.py` and `src/engine/requested-modules.ts`) replaces FM Synth's chooser hooks with its own and checks every hook it replaces against the exact claims of FM Synth 0.1.2 (Modwerk's chooser). The author's chooser, in 0.1.3 already and in 0.1.4 and 0.1.5, makes different claims at two of those sites (`0x4005221e` is `fm_tick`, `0x400786c8` takes 6 bytes), drops four of them, and turns the six count pokes into detours. Both sides therefore stop on any selection with POLY8 and FM Synth ("seam drift"), as the bridge is designed to. On main `263aa1b`, as on `3151975`, `npm run module:verify -- synth` stops at the first such selection (`POLY8 composition seam drift: synth detours 0x400786c8`), and two POLY8 tests in `src/engine/requested-modules.test.ts` fail. Making the pair build again needs POLY8's bridge to be reviewed for the new chooser (its shared machine also calls 0.1.2's registration functions `fm_type`, `fm_admit_track`, `fm_track_page`, `fm_ui_tick` and `fm_defaults`). That is POLY8's code; this update does not change it. Main's POLY8 is now 0.2.7; its `composition.json` is unchanged from 0.2.6. -Without POLY8 in the pool (a local run on main `3151975` only; the record is not committed), `npm run module:verify -- synth` compared Modwerk's browser composer with native octabam on 122 selections: 35 builds match native outside the platform writes, 87 refusals match, 0 mismatches. The native side ran the same exporter on the host toolchain instead of the pinned Docker image, which is not available here. `sdk/native-comparisons/synth.json` is still 0.1.3's record. +Without POLY8 in the pool (a local run on main `263aa1b` with POLY8 left out of the coverage pool in a scratch copy; the record is not committed), `npm run module:verify -- synth` compared Modwerk's browser composer with native octabam on 122 selections of this version (native source `401d3b61…`), the pool now including E-Verb and Output Matrix: 35 builds match native outside the platform writes, 87 refusals match, 0 mismatches. The native side ran the same exporter on the host toolchain instead of the pinned Docker image, which is not available here. With POLY8 in the pool the same command stops at `POLY8 composition seam drift: synth detours 0x400786c8`, as for 0.1.4. `sdk/native-comparisons/synth.json` is still 0.1.2's record. ## LCD captures -Modwerk's `scripts/capture-module-ui.py` ran unchanged on this image with the pinned `ot_emu`, an empty card, the MKII panel and 50 ms keys, in one slot of the shared emulator rig. The plan is 0.1.2's plan with waits, plus LFO SETUP and SELECT MACHINE TYPE (`media/capture.json`). Eight PNGs are committed (a ninth, the root page, was captured and is not committed: eight media per module). Seven are byte-identical to the 2.11 build's captures of the same plan. `ot-voices.png` differs by design: choosing FM SYNTH now writes its init, VOIC 1 into LFO 3's SPD3 byte, so the VOIC dial is drawn at its first position; the 2.11 build left the stock byte there, which also reads VOIC 1. SRC SETUP and SELECT MACHINE TYPE show FM SYNTH as the sixth row and nothing after it. All were reviewed visually. +Modwerk's `scripts/capture-module-ui.py` ran unchanged on this image with the pinned `ot_emu`, an empty card, the MKII panel and 50 ms keys, in one slot of the shared emulator rig. The plan (`media/capture.json`) is 0.1.4's up to AMP SETUP, then SCALE MINOR, ROOT A and GLIDE in PROJECT > CONTROL > SEQUENCER, SELECT MACHINE TYPE, and MIDI track 1: the NOTE page (C3, then one NOTE click up, which reads D 3 under MINOR A) and CHROMATIC (FUNC held, DOWN). Twelve PNGs were captured and reviewed visually; seven are committed, because a module carries at most eight media and the audio preview is one of them: -## Qualification: cycles and memory — 11 October 2026 +- `ot-location.png`, `ot-controls.png`, `ot-voices.png`, `ot-lfo-setup.png`, `ot-legato.png`: byte-identical to 0.1.4's captures of the same plan on image `00fa6caa…`; +- `ot-glide.png`: the SEQUENCER window with SCALE MINOR, ROOT A and GLIDE OFF (0.1.4's showed MAJOR, C, OFF); +- `ot-midi-chromatic.png` (new): a MIDI track in CHROMATIC with SCALE MINOR and ROOT A; the number beside the keyboard reads "A 3". + +Kept local: SELECT MACHINE TYPE (byte-identical to 0.1.4's committed capture: FM SYNTH the sixth and last row), the SCALE and ROOT rows, the NOTE page before and after the click, and CHROMATIC after a key press. 0.1.4's `ot-machine-list.png` and `ot-scale.png` are removed for the media limit; the location page and the SEQUENCER window with all three quantizer rows remain. + +## Qualification: cycles and memory + +The frame-interrupt measurements are 0.1.4's, made on 11 October on its image `00fa6caa…` (the method, the cards and the per-configuration table below). This image carries the same engine and chooser objects at the same addresses and no new frame-interrupt code, and the four cards the bounds come from were run twice more on this image on 12 October; the record's bounds are taken over all seven runs. The memory and stack sections are this image's own. Every count marked **instr** is an emulated ColdFire instruction count from the pinned emulator. These counts are not chip wall-clock cycles and not a CPU percentage. **cycles** are those counts converted with the calibration below. @@ -137,7 +147,7 @@ Emulated instructions are converted with the toolkit's hardware calibration (CF The criterion is strict: the whole frame-interrupt body, stock reserve plus the module, must fit one block on every block, including the first START after boot, with the measured run-to-run spread to spare. -### Results (instr per block, frame-interrupt body; the first run of every card) +### Results on 0.1.4's image (instr per block, frame-interrupt body; the first run of every card) | configuration | sustained mean (playing) | first START after boot | later START blocks | `sy_render` per block, mean / max | | --- | ---: | ---: | ---: | ---: | @@ -155,7 +165,9 @@ The criterion is strict: the whole frame-interrupt body, stock reserve plus the | 8 tracks VOIC 4, DEC 127, chosen as FM SYNTH (Part signature) | 55,958 | 88,317 | 81,385 | 34,168 / 48,624 | | reference: 8 stock sample tracks, same locks/LFOs/scenes | 26,996 | 51,809 | 46,836 | 4,965 / 11,867 | -Five runs of the bounding cards: one VOIC 4 track peaks at 32,214..32,299 (spread 85); the no-FM block floor is 22,619..22,709 (spread 90); eight VOIC 4 tracks peak at 97,681..97,729 and run at 63,362..63,405 sustained. +Five runs of the bounding cards on 0.1.4's image: one VOIC 4 track peaks at 32,214..32,299 (spread 85); the no-FM block floor is 22,619..22,709 (spread 90); eight VOIC 4 tracks peak at 97,681..97,729 and run at 63,362..63,405 sustained. + +**This image (two runs of the same four cards, 12 October):** one VOIC 4 track peaks at 32,314 and 32,172 (sustained means 27,851 and 27,768); the no-FM floor is 22,617 and 22,709; eight VOIC 4 tracks peak at 97,689..97,745 and run at 63,366..63,395 sustained. Every figure is inside the rig's run-to-run spread of 0.1.4's (about ±100 instructions). Over the seven runs: one VOIC 4 track 32,172..32,314 (spread 142), the floor 22,617..22,709 (spread 92), eight VOIC 4 tracks 97,681..97,745. FM's own code per `sy_render` call (two calls per track per block), excluding the nested stock renderer: @@ -176,12 +188,12 @@ The cost per track (first run): **Against the 2.11 build** (MAIN OS `248d987e…`, the same cards and rig, one run per card): the frame-interrupt code differs only by the removed `po_stop` hook, and 2.12's machine-change code runs in the UI task. The counts agree within the rig's run-to-run spread: START peaks differ by at most 78 instructions (0.19 %), sustained means by at most 87. The record below uses this image's own numbers. -**Worst case found:** 97,729 instr in one block, at the first PLAY after boot with eight VOIC 4 marker tracks starting chords together (DEC 0; DEC 127 is within the spread). That is 243,557 cycles, 2.54× the block. Sustained, the same load runs at 63,362..63,405 instr, 1.65× the block. The eight-track VOIC 4 configuration cannot meet the budget. +**Worst case found:** 97,745 instr in one block (this image's runs; 97,729 on 0.1.4's), at the first PLAY after boot with eight VOIC 4 marker tracks starting chords together (DEC 0 and DEC 127 within the spread). That is 243,597 cycles, 2.54× the block. Sustained, the same load runs at 63,362..63,405 instr, 1.65× the block. The eight-track VOIC 4 configuration cannot meet the budget. ### Pricing for the record -- `worstCase`, one instance: the START-block increment of one VOIC 4 track over the no-FM block minimum, the largest one-track peak of the five runs over the smallest floor. That is 32,299 − 22,619 = 9,680 instr → **24,125 cycles/block** (rounded up). -- `maxConfiguration`: the largest configuration whose every block, including the first START, fits under the module's own worst case (VOIC 4 chords) is **one FM track**. Its largest measured peak is 32,299 instr + 372 wrapper allowance = 32,671 instr → **81,422 cycles/block** (rounded up). That is inside the budget of 95,779 and leaves 14,357 cycles (5,761 instruction-times), far more than the measured spread (90). +- `worstCase`, one instance: the START-block increment of one VOIC 4 track over the no-FM block minimum, the largest one-track peak of the seven runs over the smallest floor. That is 32,314 − 22,617 = 9,697 instr → **24,167 cycles/block** (rounded up; 0.1.4 recorded 24,125 from its five runs). +- `maxConfiguration`: the largest configuration whose every block, including the first START, fits under the module's own worst case (VOIC 4 chords) is **one FM track**. Its largest measured peak is 32,314 instr + 372 wrapper allowance = 32,686 instr → **81,459 cycles/block** (rounded up; 0.1.4: 81,422). That is inside the budget of 95,779 and leaves 14,320 cycles (5,746 instruction-times), far more than the measured spread (142). - `maxInstances` = 1 for cycles: one FM track at VOIC 4. Memory is preallocated for eight tracks, so `memory.maxInstances` = 8. Three tracks at VOIC 1 also keep every block in budget: 37,368 + 372 = 37,740. These configurations fit only at the 8.38 ns end of the calibration (≤ 43,294): @@ -192,68 +204,67 @@ Three tracks at VOIC 1 also keep every block in budget: 37,368 + 372 = 37,740. T Everything larger does not fit. Eight ordinary sample tracks starting together on the same fixture peak at 51,809 instr, which is also over the block: a simultaneous START is a single-block transient that stock firmware itself produces. A configuration outside this record is not shown to fail on the unit; it is only not qualified. -### Exact memory, from the link map +### Exact memory, from the link map (this image) | region | bytes | scope | | --- | ---: | --- | -| engine DRAM unit `poly`, code and tables, `0x40a955e0..0x40a9921c` | 15,420 | shared | +| engine DRAM unit `poly`, code and tables, `0x40a955e0..0x40a9921c` (the same object and addresses as 0.1.4) | 15,420 | shared | | per-track state ×8: `sy_state` 128 + 4 voices × 64 + `po_pend` 4 × 16 + `po_chord` 32 + `po_mheld` 8 + `po_mring` 16 + `po_mhead`/`po_mtail` 2 (interleaved arrays `0x40a9921c..0x40a9a1f4`) | 506 per track = 4,048 | instance | | shared engine state: `po_seq`, `po_vbuf`, `po_clock`, `po_rtlog`, LFO/PLAYBACK/AMP descriptor clones (`..0x40a9a7e4`) | 1,528 | shared | -| chooser DRAM unit `fmmachine` (`machine.s`: code, the six-name table, the 2.11 row table, the eleven list detours and 2.12's machine-change init) | 2,076 | shared | +| chooser DRAM unit `fmmachine` (`machine.s`, the same object and addresses as 0.1.4) | 2,076 | shared | | pinned FM PLAYBACK page `fmpage`, `0x400d24d0..0x400d2c1c`, in the free ROM run `0x400d24d0..0x400d2ce0` | 1,868 | shared | -| FM hook sites rewritten in place: 52 detours (416 B; eleven the 2.11 machine-list sites, `po_unload` the 2.12 file-browser site in place of the removed `po_stop`), the resolver jump (6 B) | 422 | shared | +| FM hook sites rewritten in place: 52 detours (416 B) and the resolver jump (6 B), unchanged | 422 | shared | | kind-table entries: FLEX and STATIC renderer, FLEX START | 12 | shared | -| bundled quantizer DRAM unit `fm_qz` | 3,532 | shared | -| bundled quantizer ROM units (`fm_qzk` 45, `fm_qzc` 192, `fm_qzs` 6), SEQUENCER tables 72, 19 detour sites 142, one poke 4 | 461 | shared | -| platform loader and packed DRAM units appended to the image at `0x4010fdf0` | 14,093 | shared | -| platform reserve: 1,707 pages × 6,144 = 10,487,808 B of Flex sample RAM, less the three DRAM units (26,604) | 10,461,204 | shared | +| bundled quantizer DRAM unit `fm_qz`, `0x40a9b000..0x40a9bf34` (3,532 B in 0.1.4; the 2.13 MIDI-track code) | 3,892 | shared | +| bundled quantizer ROM units (`fm_qzk` 45, `fm_qzc` 192, `fm_qzs` 6, unchanged), SEQUENCER tables 72, 22 detour sites 170 (three new: 8 + 10 + 10), one poke 4 | 489 | shared | +| platform loader and packed DRAM units appended to the image at `0x4010fdf0` | 14,297 | shared | +| platform reserve: 1,707 pages × 6,144 = 10,487,808 B of Flex sample RAM, less the three DRAM units (26,964) | 10,460,844 | shared | | ColdFire stack increment, upper bound (see Stack) | 352 | shared | -Totals: 506 × 8 + 10,500,968 = **10,505,016 bytes**. The module's own bytes are **43,812** (the 2.11 build: 42,722; the difference is the chooser unit's machine-change init, +636, the engine, +16, and the appended loader that packs the larger units, +438). The rest is the sample-RAM platform reserve, which any DRAM module creates (sample pages 14,602 → 12,895) and which is counted once. +Totals: 506 × 8 + 10,501,200 = **10,505,248 bytes**. The module's own bytes are **44,404** (0.1.4: 43,812; the difference is the quantizer's DRAM unit, +360, its three new hook sites, +28, and the appended loader that packs the larger unit, +204). The rest is the sample-RAM platform reserve, which any DRAM module creates (sample pages 14,602 → 12,895) and which is counted once. Reconciliation with the native allocation map: -- `runtime.elf` `.text` is 26,604 B at `0x40a955e0`: `poly` 20,996 (`00_poly.o`), `fmmachine` 2,076 (`01_fmmachine.o`) and `fm_qz` 3,532 (`02_fm_qz.o`). The `.text` of `02_fm_qz.o` is byte-identical to the 2.11 build's. +- `runtime.elf` `.text` is 26,964 B at `0x40a955e0`: `poly` 20,996 (`00_poly.o`), `fmmachine` 2,076 (`01_fmmachine.o`) and `fm_qz` 3,892 (`02_fm_qz.o`). `00_poly.o` and `01_fmmachine.o` are byte-identical to 0.1.4's, and so are the runtime's first 23,072 bytes. - poly's 20,996 B = 15,420 + 1,528 + 8 × 506. -- In `layout.json`, `runtime_end` is `0x40a9bdcc`, and the boot stage holds 13,861 B. The append is 14,093 B, as the build log says. +- In `layout.json`, `runtime_end` is `0x40a9bf34`, and the boot stage holds 14,065 B. The append is 14,297 B, as the build log says. - The build log reports 12,895 pages left for samples and recorders, against stock's 14,602. -There are no other allocations: no heap, no dynamic allocation and no DSP or SRAM allocation. The module has no battery-RAM bytes of its own. GLIDE is the bundled quantizer's byte. LEG, the LFO destination and the FM SYNTH signature use stock Part bytes and their battery-RAM shadow, written by the author's code or by stock's own store; the machine-change init writes stock Part bytes, their shadow and the live lane. +There are no other allocations: no heap, no dynamic allocation and no DSP or SRAM allocation. The module has no battery-RAM bytes of its own. GLIDE is the bundled quantizer's byte; the 2.13 hooks read the existing SCALE and ROOT bytes and write only the stock byte, lock or note register each one replaces. LEG, the LFO destination and the FM SYNTH signature use stock Part bytes and their battery-RAM shadow, written by the author's code or by stock's own store; the machine-change init writes stock Part bytes, their shadow and the live lane. -### Stack +### Stack (this image) -A static upper bound computed from the assembled runtime of this image (`runtime.elf`), following its control flow: SP displacements of every instruction, both ways at each conditional branch, the callee's own bound at each call (+4 for the return address), a jump out of the runtime ending a path at its depth. Exclusive branches are not summed. Every detour, symbol-ref and table entry point in the runtime was analysed (71; the bundled quantizer's two ROM-unit entries are outside the runtime). +A static upper bound computed from the assembled runtime of this image (`runtime.elf`), following its control flow: SP displacements of every instruction, both ways at each conditional branch, the callee's own bound at each call (+4 for the return address), a jump out of the runtime ending a path at its depth. Exclusive branches are not summed. Every detour, symbol-ref and table entry point in the runtime was analysed (74: 0.1.4's 71 and the three 2.13 hooks; the bundled quantizer's two ROM-unit entries are outside the runtime). Every bound of 0.1.4's 71 entry points is unchanged. | path | entry points | bound | | --- | --- | ---: | | frame interrupt | `sy_render` 132, `po_fmsource` 32, `po_fmstart` 32, `po_retrig` 24, `po_kill` 12, `po_lfo3b` 8, `po_lfo3` 4, `po_rel` 0 | 132 B | | MIDI task | `po_mrec` 196, `po_moff` 188, `po_mogate` 52, `po_mon` 44, `po_mtrig` 44, `po_mgate` 44 | 196 B | -| UI task, chooser (`machine.s`) | `fm_validate` 220 (its 116-byte frame + the replayed stock 96-byte validator prologue), `fm_main_commit` 116 (88 on the 2.11 build: it now calls the machine-change init), `fm_src_commit`/`2` 108 (80), `fm_setup_open` 36, `fm_name_a`/`fm_chooser_row`/`fm_chooser_open` 32, `fm_tick` 12, `fm_setup_row` 8, `ml_win_count`/`ml_src_count`/`ml_draw_bound` 8 (each replays the stock pushes it replaces), `fm_machine_name` 4, the other list detours 0 | 220 B | -| UI task, engine pages | `po_assign` 112 (80 on the 2.11 build), `po_machwin`/`po_machlist`/`po_octave` 88, `po_loadsel` 84 (32), `po_unload` 20 (new), `po_ampstage`/`po_ampdraw1`/`po_ampdraw2`/`po_ampdraw3` 52, `po_ampedit`/`po_amplane` 48, `po_lfdedit` 24, `po_lfopage` 16, `po_lfdname`/`po_lfdlfo` 12 | 112 B | -| UI task, bundled quantizer | `qz_plock` 76, `qz_knob` 68, `qz_leg0` 68, `qz_leg1` 48, `qz_wr` 36, the others 28 or less | 76 B | +| UI task, chooser (`machine.s`) | `fm_validate` 220 (its 116-byte frame + the replayed stock 96-byte validator prologue), `fm_main_commit` 116, `fm_src_commit`/`2` 108, `fm_setup_open` 36, `fm_name_a`/`fm_chooser_row`/`fm_chooser_open` 32, `fm_tick` 12, `fm_setup_row` 8, `ml_win_count`/`ml_src_count`/`ml_draw_bound` 8 (each replays the stock pushes it replaces), `fm_machine_name` 4, the other list detours 0 | 220 B | +| UI task, engine pages | `po_assign` 112, `po_machwin`/`po_machlist`/`po_octave` 88, `po_loadsel` 84, `po_unload` 20, `po_ampstage`/`po_ampdraw1`/`po_ampdraw2`/`po_ampdraw3` 52, `po_ampedit`/`po_amplane` 48, `po_lfdedit` 24, `po_lfopage` 16, `po_lfdname`/`po_lfdlfo` 12 | 112 B | +| UI task, bundled quantizer | `qz_plock` 76, `qz_mknob` 72 (new), `qz_mplock` 72 (new), `qz_knob` 68, `qz_leg0` 68, `qz_leg1` 48, `qz_wr` 36, `qz_mkey` 20 (new), the others 28 or less | 76 B | -Calls into stock add stock's own frames on top (`ml_draw_bound` calls stock's name formatter exactly where stock does). The frame interrupt runs on whichever task stack it interrupts, so the worst single-stack increment is the deepest task path plus the frame path: 220 + 132 = **352 B**, as in the 2.10 and 2.11 builds. +Calls into stock add stock's own frames on top (`ml_draw_bound` calls stock's name formatter exactly where stock does). The frame interrupt runs on whichever task stack it interrupts, so the worst single-stack increment is the deepest task path plus the frame path: 220 + 132 = **352 B**, as in 0.1.4. ### What was not measured - **Chip cycles.** The counts are converted by a calibration from one hardware session. They are not chip wall-clock cycles. -- **UI-task costs.** Machine choice (with the machine-change init), the Part validator, page and setup-window changes, Part changes and project load run in the UI task, outside the frame-interrupt deadline. They were not timed; their stack is bounded statically above. +- **UI-task costs.** Machine choice (with the machine-change init), the Part validator, page and setup-window changes, Part changes and project load run in the UI task, outside the frame-interrupt deadline, and were not timed; their stack is bounded statically above. The 2.13 quantizer hooks are the exception: their instruction counts per call are in "The quantizer on MIDI tracks". - **USB and MIDI traffic, live keys, the metronome.** These were not driven in the cost windows. The key gate's test runs at every START, so the trig STARTs above include it. - **Hardware stack canary.** Not measured. - **Other fixtures.** One fixture family was used (120 BPM, CF DELAY on all eight tracks). ## Hardware -The author tests this image (the update file above, MAIN OS `00fa6caa…`) with a short test sheet that covers: +The author tests this image (the update file above, MAIN OS `37661aee…`) with a short test sheet that covers: -- the metronome case: CLICK ON, the sequencer playing, a held chord and a held key that keep sounding through the clicks; -- a machine change FM SYNTH → FLEX → FM SYNTH that shows FM SYNTH's init on PLAYBACK and LFO, with an FX1 setting kept; -- the FM SYNTH row in SRC SETUP and SELECT MACHINE TYPE; -- a power cycle with the card in (the battery-RAM case the emulator cannot test) with two FM tracks. +- a MIDI track with a scale and ROOT on: the NOTE knob steps through the scale's notes from ROOT; the CHROMATIC keys follow ROOT (key 1 sounds the root, the readout shows it); a chord (NOT2–NOT4) and the arpeggiator behave as before; CHROMATIC on a MIDI track no longer freezes; SCALE OFF as before; +- FM SYNTH still plays and chooses as on 0.1.4; +- a power cycle with the card in (the battery-RAM case the emulator cannot test). -His report fills `tests.qualification.hardware`, whose `TIM:` fields are placeholders, and sets `hardwareStatus` to `reported`. Until then this version is not publishable under the qualification gate. The earlier local builds of 0.1.3 and 0.1.4 (MAIN OS `d5e1a7b0…`, `248d987e…`) are superseded. +His report fills `tests.qualification.hardware`, whose `TIM:` fields are placeholders, and sets `hardwareStatus` to `reported`. Until then this version is not publishable under the qualification gate. 0.1.4's image (`00fa6caa…`) has its own pending hardware sheet; it does not qualify this image. -Historical hardware: the 2.3–2.9 builds and the first 2.10 build ran on the author's MKI, and his 7–8 October report on that build produced the fixes above. On 10 October he reported the metronome cut-out and the machine-change request on the 2.11 build (`248d987e…`); 2.12 is his answer to both. Those reports do not qualify this exact image. +Historical hardware: the 2.3–2.9 builds and the first 2.10 build ran on the author's MKI, and his 7–8 October report on that build produced the 2.10 fixes. On 10 October he reported the metronome cut-out and the machine-change request on the 2.11 build (`248d987e…`); 2.12 is his answer to both. Those reports do not qualify this exact image. ## Repository validation diff --git a/sdk/octabam/modules/synth/evidence/native-smoke.json b/sdk/octabam/modules/synth/evidence/native-smoke.json index 413134edf..5a9819a81 100644 --- a/sdk/octabam/modules/synth/evidence/native-smoke.json +++ b/sdk/octabam/modules/synth/evidence/native-smoke.json @@ -1,5 +1,5 @@ { - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "checks": { "eightTrackSelection": "passed", "sampleSlotsUnchanged": "passed", diff --git a/sdk/octabam/modules/synth/manifest.py b/sdk/octabam/modules/synth/manifest.py index a8f9745d9..0f1e5c033 100644 --- a/sdk/octabam/modules/synth/manifest.py +++ b/sdk/octabam/modules/synth/manifest.py @@ -1,4 +1,4 @@ -"""FM SYNTH: Tim Hastie's FM engine and machine-list chooser (octatrick 2.12). +"""FM SYNTH: Tim Hastie's FM engine and machine-list chooser (octatrick 2.12 engine, 2.13 quantizer). The engine, the PLAYBACK page and, since 2.10, the FM SYNTH chooser itself (upstream/synth/machine.s: FLEX + "FM", 1 in unused NEIGHBOR PLAYBACK bytes, @@ -13,7 +13,9 @@ units' remix.inc sets HAVE_SYDRUM 0, so the engine assembles as 2.10's. This wrapper adds only what the Modwerk build needs: the page as a Linked unit at its pinned cave, checked against the author's ratified page bytes; the resolver's jump into it; and the matching -quantizer bundled privately. Combining with the public quantizer or Analog BD +quantizer bundled privately (the author's 2.13 quantizer: on a MIDI track the NOTE +knob, NOTE locks and the CHROMATIC keys follow SCALE and ROOT, and the CHROMATIC +drawer hook sits at the common push 0x400449be, Modwerk #264). Combining with the public quantizer or Analog BD is refused for overlapping registrations. The author's manifest also names the Analog BD conflict as a native `conflicts` entry; Modwerk declares conflicts in octamod.module.json (compatibility.conflicts already lists analog-bassdrum), and @@ -47,7 +49,7 @@ MODULE=dataclasses.replace(_SY, category=Category.MACHINES, author='timhastie', author_url='https://github.com/timhastie', doc='Dedicated FM SYNTH machine: two-operator FM, one to four voices, chords, MIDI and glide.', - proof=Proof.UNTESTED, proof_note='Octatrick 2.12 engine and machine list; emulator-qualified, hardware report for this build pending', + proof=Proof.UNTESTED, proof_note='Octatrick 2.12 engine and machine list, 2.13 quantizer; emulator-qualified, hardware report for this build pending', requires=(), conflicts=(), cf_patches=(), linked=_SY.linked+_QZ.linked+( Linked('fmpage',_REL('upstream/synth/page.s'),cpu='5475',cave_addr=_UP['PAGE_AT'], diff --git a/sdk/octabam/modules/synth/media/LICENSE.md b/sdk/octabam/modules/synth/media/LICENSE.md index 7fb059378..6195c0d31 100644 --- a/sdk/octabam/modules/synth/media/LICENSE.md +++ b/sdk/octabam/modules/synth/media/LICENSE.md @@ -1,9 +1,11 @@ # FM Synth media declaration -The eight `ot-*.png` files are original headless-emulator LCD exports of the -FM Synth 0.1.3 native image, made on 8 October 2026 with Modwerk's capture -tool and capture plan (six are byte-identical to the 0.1.2 captures of 6-8 -October, made for Modwerk at the owner's request). Credit: Modwerk contributors. +The seven `ot-*.png` files are original headless-emulator LCD exports of the +FM Synth 0.1.5 native image (MAIN OS `37661aee…`), made on 12 October 2026 with +Modwerk's capture tool and its capture plan, extended by a MIDI track's +CHROMATIC page (five are byte-identical to the 0.1.4 captures of 11 October, +whose plan descends from the captures made for Modwerk at the owner's request). +Credit: Modwerk contributors. Their provenance, capture plan and hashes are in [capture.json](capture.json). Only the rendered LCD images and sanitized metadata are submitted; firmware, stock-containing runtime binaries, raw LCD/RAM dumps, cards, source project diff --git a/sdk/octabam/modules/synth/media/capture.json b/sdk/octabam/modules/synth/media/capture.json index 91d6ce9ed..da137c14f 100644 --- a/sdk/octabam/modules/synth/media/capture.json +++ b/sdk/octabam/modules/synth/media/capture.json @@ -1,6 +1,6 @@ { "firmware": "1.40C", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "emulatorSha256": "9dc31d1249793fcf857fa6b1c1ac72ab7966646fd59f3e09e90683c8eb41e711", "setup": "Headless ot_emu; MKII panel; stopped transport; 128×64 LCD at integer scale 6.", "palette": { @@ -229,7 +229,7 @@ { "encoder": { "name": "LEVEL", - "delta": 1 + "delta": 6 } }, { @@ -246,6 +246,15 @@ { "wait": 300 }, + { + "encoder": { + "name": "LEVEL", + "delta": 9 + } + }, + { + "wait": 300 + }, { "capture": "ot-root.png" }, @@ -315,6 +324,102 @@ }, { "capture": "ot-machine-list.png" + }, + { + "press": [ + "NO" + ] + }, + { + "wait": 300 + }, + { + "press": [ + "NO" + ] + }, + { + "wait": 300 + }, + { + "press": [ + "MIDI" + ] + }, + { + "wait": 500 + }, + { + "press": [ + "SRC" + ] + }, + { + "wait": 300 + }, + { + "capture": "ot-midi-note-c3.png" + }, + { + "encoder": { + "name": "A", + "delta": 1 + } + }, + { + "wait": 300 + }, + { + "capture": "ot-midi-note.png" + }, + { + "hold": [ + "FUNC" + ] + }, + { + "wait": 200 + }, + { + "hold": [ + "DOWN" + ] + }, + { + "wait": 250 + }, + { + "release": [ + "DOWN" + ] + }, + { + "wait": 200 + }, + { + "wait": 300 + }, + { + "release": [ + "FUNC" + ] + }, + { + "wait": 500 + }, + { + "capture": "ot-midi-chromatic.png" + }, + { + "press": [ + "TRIG1" + ] + }, + { + "wait": 300 + }, + { + "capture": "ot-midi-chromatic-key1.png" } ], "screenshots": { @@ -323,14 +428,13 @@ "ot-voices.png": "603b3e5d31446bf31570b02dc56b6efccb958b371a06b87c3dc79a6a6090d1e5", "ot-lfo-setup.png": "ab3071680706882acdf5f9ccdefb1adcc24c50690afd90560ca42e25535e3e94", "ot-legato.png": "e5cd86583126a834f8a9351a8f2b51bca9f76c101023e37db4c933be957d5bc8", - "ot-scale.png": "71ffe8fe7413a1e2eb0605ad7119159bba95db7f7734dc47ae13468f68a74d13", - "ot-glide.png": "77b4cf73fd16e96d1c78f5a9cdd0590b20a7958c614d1c6cdb93b77791dc505b", - "ot-machine-list.png": "47305d8bdb86bc29f838f4e3ba712af7b0a6e6d3c063e987adc9a59f82daa8ba" + "ot-glide.png": "f67a2c2d5fff741e4a1cca6bf897af7a56d857cb18705d5d64c1e404dfa3ec7c", + "ot-midi-chromatic.png": "29ee87e6fef88d3ab7c8a52a90d7c5bc69852f9d175720e89b76c723985a45d0" }, "schemaVersion": 1, "moduleId": "synth", - "moduleVersion": "0.1.4-experimental", - "captured": "2026-10-11", + "moduleVersion": "0.1.5-experimental", + "captured": "2026-10-12", "imageProfile": { "modules": [ "SYNTH MACHINE" @@ -338,6 +442,6 @@ "keepStockFx2": true, "staticStock": true }, - "releaseProvenance": "Fresh 11 October captures from the native Synth 0.1.4 image on the author's octatrick 2.12 (MAIN OS 00fa6caa..., Modwerk's export-composition-proofs.py, author source ca13ed8, tag v2.12). Seven screenshots are byte-identical to the capture of the same plan on the 0.1.4 build of the 2.11 line (MAIN OS 248d987e..., author source 5f3d745). ot-voices.png differs by design: choosing FM SYNTH now writes its init, VOIC 1 into LFO 3's SPD3 byte, so the VOIC dial is drawn at its first position (the 2.11 build left the stock byte, which also reads VOIC 1). The plan is 0.1.2's with waits, plus LFO SETUP and SELECT MACHINE TYPE; ot-root.png was captured but is not committed (eight media per module). No hardware verification is conferred.", - "sourceSha256": "4fc06072b4039db83cba1600e949d00770c9753d974bbf76a5809f8e1be9e851" + "releaseProvenance": "Fresh 12 October captures from the native Synth 0.1.5 image (MAIN OS 37661aee..., Modwerk's export-composition-proofs.py, author source 48b89e7: the 2.12 engine, page and chooser and the 2.13 quantizer). The plan is 0.1.4's up to AMP SETUP, then SCALE MINOR, ROOT A and GLIDE in CONTROL > SEQUENCER, SELECT MACHINE TYPE, and a MIDI track: the NOTE page (C3, one NOTE click up to D3) and CHROMATIC (FUNC held, DOWN) with the readout A 3. Seven of the twelve captures are committed (a module carries at most eight media, one of them the audio preview); SRC SETUP, the FM page, the LFO page, LFO SETUP and AMP SETUP are byte-identical to 0.1.4's captures of the same plan on image 00fa6caa; SELECT MACHINE TYPE, also byte-identical to 0.1.4's, the SCALE and ROOT rows, the NOTE page and a key press are kept local.", + "sourceSha256": "401d3b61412aff964617581f9f0bfcfe0ac83d6a6ee970bb1440f2393a6ac60a" } diff --git a/sdk/octabam/modules/synth/media/ot-glide.png b/sdk/octabam/modules/synth/media/ot-glide.png index ba334807d7ecbc15aeb04598324eaa444f92a7a2..b985ffc9bc68d3ce0e3c7c4b1be32e6d2f848fdb 100644 GIT binary patch literal 3664 zcmb_f4OEm>8h*cz5BT_tfk2$akwL-6vO-c={+3Vt36>6y0nx4qWV0K`m9{}aXbnD; zU@ZgIpCkCkU}b<7WzwS3fjYWOMnEnK2$~^+;W~*ZY#_Mry)!VwpL@=p<(#>5=id81 z@AJO*{kiN=Mm%q4{VQt#unP-a@gjg3P8pGB@oR7WE(O42E@3N{MWtQ7eI+dUdZ?|i z#%=jva7o^^kA0(;eQV{BGk9r9_`!mB*;kiZjurYIA3WkW=+#`jG2X&*_Tz=G%0=79 zycT%X2_TpMD{$>!uNY}YN`enDfo`z4Ti@^Vg6c#`Eg3Cq9mdxm3qhFkD4uuL|v zcpwWxAS-P6A$YTbPQD|-M}LA1)$hRF+U@XlAjfoNSu)w_JgYUcXZe1~o94=Hvp@6f zQBL{eyIH_;61lk3M*xP^CYrrqh5VAlg$yr$-_FYu7}ZGz5BlvOr|2)1mk6s|6MhR5 zj&J1U!9%=Ors>`z5kv$n&;#qmJW!P5<>Vtk(8Isx$Y!e+fMlJ^zWD9FqwqLLgnzh^ zC1eupr3ETQhfVKCL%Q8uiBQ{is_%E}h&+P^GK2;Bh~Q|d?r^@(b$if0>_m;gOgHSw zoDH=N@Xq|14%`bA+Wl zQbe|r1Fkuu1$0{i=$p;A95YfsD9SvS^Or@Nf+O>fjW!0x1h^LM{w`Aa%V`@TY>>qn zN{8qH4LJHWL?qxd{fOo@d9TjXU8+8?lTBO>-?bhcz0lexJw9Nb(tA-I`*&86hoq2; zhnT(XIt6nlsvNEJ!U=3P?y{kZUq3?>NT{lkYgLqiL5LhkY`pW~@GhVEVsCC#W!c_S z#T(k|3tNj?g8Ji}AI)!O=5)ts{;0E{c94s`ixTQ>GQr_Ab>#?+~AtT;ef zPU{A}O=D`3W2p&a-kNM927>N4*)o@pB3+jDSG2y7|A&l}A_rkhWB<-GH=0lQ-e1_- zzxn@iFIJkoZ})EE#fnrUZwwUl|Ow*Orzf{*_?hzwNQ5F ze{yK3g%Rhd8K=Q?PJ($gb>KC2OxMyhkPj{WcU7`vnn=g!udN1uXJ6FGHO0rp2S>Z$ zlw{uIq0mLxl4Pqwb z-(*B2MczK1T73)fzW((*xn5FHRKfHPNp=l8kmw@Sp)H^K36*Ly&`pkl@B{cS9qqMj zx{?$z5?z-$qD$AMIX$PlQ$GSSGsWvH4PW9>dVCxbRh@lre6fSjrNOfivz~F13F`36 zWd-G#Pela;7g=3UztYgLx-!Z8HMYR7&In`-TTU{jhg_ra1lH}bw8bj>g}JihX3ZI| z7#q!*o{uu1?P!DZRwmaVs&EkSGHLP9`WQjlR$e@ zoei7nV87bO`{%10o?}Yg1oapR;x>I=AcL{RaYXck!E(BVCzShuJ$fRMp1}S@;!BP` z@xae`p|?VJkpnWbDd9q4NK}#_Mk$O=Y9CNF9Eo_Wk<vs!;~9rnPS7MNJiy*Lb!3KrXY~?)6=E{0c-?cd568^BTWd2OX%o#i1ZF>5IvTd5 z8WQ5*oP%kB`JZmy+f18ZGBDJ)CV0Jd$NpSi^EuPia?QMeyfnsrO^vdU%0=o@sY=v0 zN>u?q8PFFIZ*^~IKYM6#LjSzic2)TrT;Ltn;Ytw%&^J7MQyBUy7`h&La-jU_SX+kM z_;OdZg|WtbNoBBfgw50YdV1mJr^MdkvsEK$c5^?~7J2C2^MCmLk4}gQ2Ku%0p3t*q zX8G+tYeen5s!s$;fKyiP8`=4^fvEQv27dgfS}VEUnw1S6%K&p3^U#PQ(b+N0i$#-s znIY3s*qe3`JLFb|m;C5FRCbqT-i=ZFuCM&ueaS10AulJDAJA80qPm}!Xf$r3|Bxa& z>ee&FQQl&0h(e6r!eHWde(kBd6nH6N0;UEwi;=0jy2q~me*K3E1J}96J%f$UqmH- z#4wLQFNbD4cyflfit(jxEH5Sj56lwsSfBb3BFzgM zOD|UtYN{Q{PLOU6(o5riH`ti~uqO#<*@HWFRkDE3wjHUt0r(fTGGfIiAqm_510D^1 A$p8QV literal 3669 zcmb7H4OA4@6@D|j!1A+kgr1qv`dng-?Fek(k`^?*joP=SzGF|C;aNl@yy) z@dgU~nYvo-C8GAe%A<=5+_SS^b#J)&{w0^~#VcZWQhTddp5c@|j~E@knGif`bHLr+b9XaHYfhVp$f@m?r z@`r;sL|J{pYuByfJ!#GHa9ZJ@sd1$x7|Rv@X>p~n3@=5LFR`VXDBSwV6E1;nsPWA2 zO7|28&muR(aF=B6d;Jhth*+mVWInmTi4msC=AxPq4cnm?IpoNKTeQUX@`%dT-~KWFkUHnE zaiJN)yW#7R$|V$FL}M)Q#udW>4cIY^4?~O&;b6Yv!!CB8d3XHuh2(N&UFwMPPT|%4 zy1wC>^l-b+x_9I0hcE&LgM}ah(+C4YDDaM$uzYPbhxYg3{VK}ZPfex=I;lMASGv|u z?)pz>cal!lxa(xa$bqMMJ>Upd{4cDXw^BdQPOyoULq#LR4Ig69>CDfuL&im9g2AIB z_SUr=vAiJq)U$ulj{n*Fe9B9?;S<{Tp8j4JAMboL5)5Jn9{q^dKgFRt=}GsTRb^e- zvsDpW6hU{r#ypW7Li@%pX^yNQGCb~fzKyJ_YHGqh-WdOGi44il?S=72|SrC5`L^-e4T z(#Pa>41%|Lji8;^PvO|e-B#I^Cbb(}RN8>un;y1`P?(->G)yez5Ej^XFD#!aVOu{I zp)XrM?mbp)S`J1n!N0ZZX}SOiEMG7_HPzJ4*x(Rsbq6}|?G-u2ZxRjbvx*(C@0Kva zW6u&%qv7@!nPF#(uiAq`pbBwGJ3g+gd#aG4E`KQIhWu7YcT>tWgTC{Z4w#)#paidS zWQUfL3WDd5w;)K;7&Lic4tF3|PP-Coj=pQ()AZoDjO$*pUw_(fcBH$1sOlpZ(6hmmd zZhh?rYPcrFxEczT6or5)F@XcH*gOOLGs^?|+TZNteZz%0DRIuK)UV?UP>A1noPge0`S${-&M^*cD=z2cqX1Vsu6G3kL3~DCq z7+Evc9-8-M5pNsB%-LhlI+Gdm%^jlpPCjA6JMK1;AuQ9x5`)$5%Om2w7)xDfypRB# zKTtcd0s4cx#gYX&w9-zIj$RaQSInmMIs|nlITt8#a^4VGd69Jb|`3KNwawjl~9Shy^j-Ny++@51gW z**J1#TYlM^(d(g6s8-6}vJW6M8+qe|jUrHkgk4F)ij13Ou?s#*!S04(yG^v#%E`9lQ3H#?3yvsrMVxrACu9JGZRY;%Uw zNaNZU^>5#8I;sCKLfu(D`c+%L}8vzY1H6P6BsVB z7;1z{$i-W|2P9_fCMgYj=3f#rAg|}k*2AKNjbo@>L!LAdd8}pmsiBg|mArO#ghXb+ z_V>V&j)P;|5U^3?pRp$PL(h^Q+&sV3iKVW~IORw6z%&k}?gKo4f-0AzJOeR87$Pdf zQ^e-;Or#STPt%Q=lMNY@kS7If_3h$C+bM1st(RfXL)Ej(XHsD@gl@bD+W-H$uu6+` z4_J~JXF>2YWEsc3zXHai5I!LJ)YM+oo+0t%7W XuLg>K{b|ez1b;DG(|Jv=?YrrNBTq*2?haitkAV)TR~Iv3s*((rJ0`y zmW(J6PZ|S6z|_=HH`8g*AwV3!3Sq4fMM_o|5Gk_Gz3(l%EGsF0+`aGJd(Ziu-}#+$ zAG^Xr1AF%x)C&N52h9nH0FdL9;T1CcNh$f<2cSHqu*~76XGBmgI*<^T`1KAvK zQ!yiWppT(`46EXC>JN;9yiSh*{O#|>HP7{X)l`t?Uzu7Q+*EYYvhw#7uh}NUYMn<# z*{7zDcT9RT3eUOE6s^@%xe)7C;|`Aq?b^{{#0_pypqGVe>rN&24YN)+Q~%TU8yDVc z*_YdqTb!TMW~iTat7E{0O-n3SGQYEKWs}Fh;&9f%Ec&6*eTsf)H0aSE+*Ioqxf+L7 zUAQ0WE?i7a^|)HG#COH(SqaDd=JpfuP-J+59Hb_sgXoS`;)|hn*lkV@udP^j%s7yn zSX9ziFqz}4&5g#7jGLDX<4^uu(-i@AAQ~d}Xa-2ahy%h3@qG@#kR`*@iSn4I`7ATx zw-4&_GNW}KEl=Cq%5)E#8k;&^DHz-X%FRP;h@H)S9(&x6V1PL=ILMh;@Eu#8##CN7 z4&NJL_g_<=v1!A2e&uVU;bHvNKtKF|&psO|>HO!Y1 zYXc_EBw*AAHE5gR2CL4fLj0kM4Lj;}D_ZXnIV5|{o(vCBbxY4aJ!l@I5DragjkcY( zJF27^5tlmE=G0kCFf59XjICbHhLxVJYc6pU^4ccTgZqZ_><*9w{lZM;jOMe(rX->s z5Q;QV01nD1FM`8o%8NX8dwp>zN%zG~1I35E=v;P9TA`4nZ3rwG{Gj_>A&yzss?Vu7vpMPVo)N%A&QraJZ_{?u zx#JUMKFi|O@}95mt3EzZk) z%=*mc=}9|TY6}fh!mQl|JrNS^)?##ET>x*x;FFsYnysv|L zay=k2Laf-c>`apmK-Q|gIJ0-)%bgjpZ-KPy*<}aUTGqgzo zB(LJoLnBq;*`D=X{CC9Y#nT$~IiwbmETyCu)(KE?x#1M-u1?K%39H;6H#`WKRj>1w zUZ{Pm^sMxBv$ts|uYPrtjs$C*;R+QfAT0(Va`tRK%Z+fXW9N`<`ZZ3;P8;h z?K9KX;? zokEK&{M63(GB3uKO^N^NZmceYVZ;6jeX!!87&tjiwL~BBNt6}|P^6DGfI<}EyLy^hdvYmxH>PNk zF3S}{q9Gg|?zMI_orL6?C&NmZeI1-^OxhDTGW_F^JEi_z;_B`VMTCslLc?H%VCxlu z)}Q+eg4l7)2eyjf+9!UM?Yb;&Qm<#~oASfjma(_z+)inmr%7U*)H-R%QUYYQ<=Y_% z2?rv}@+{d|p*M@qJ@TB&Lt||&{UUZOw9YRQyUfy3Ilt!J(b3@py_>IMt5F-27*hB0 zw3A6EolP!b^?^0#j)(WXUq5)%vJkT#zW<0ha5l}I(jwg`k^t~Jabz8sbsE0lJ8Z$J z-dvsbGj(?=QJ|C|EcWtWp>a_|jxTUUTW*|ul&S9d%MU*)YUi9@dg@wSP6Q`+2A@_0 zx#qg#YAId+?P_`28x_T0eCx+=x};&)6DaS;dcqB@6^VqN?FvTbj_;GW%hhSw4pPPA z_LV1Dvrm!mSMS*OU`2ZF)n6GA>Hi)ftP@f1?k&76iwTX<#=oQmX1SX%)zQ9`8tFeU zqOC40M4{Gji?zdw7Z8ZLpb-wkOLd7=OS diff --git a/sdk/octabam/modules/synth/media/ot-midi-chromatic.png b/sdk/octabam/modules/synth/media/ot-midi-chromatic.png new file mode 100644 index 0000000000000000000000000000000000000000..e4a9c27055cf395b315d9c5926d1c3f0c973b495 GIT binary patch literal 5237 zcmbVQ2~<m1JR<%8~o;h)tJM%B!|NZ~` z_hyEN&T|+sW&}dWK@|M{VuaW*rD!`A{FkGW`y%vefav{qmuzY7(!9HIEYCh;k>_PY z&{w-<3H~CUBQ=Ox&o_p%1JPS!~x0gtnu-wt)_(X6x9`O(@YdR zZa&__Qd~%j4a(bFQ-5Vpvif*IcSQf(W`4d@S697UZ!hgc$Cq_iE~o5h?W|D<)Zrm> z*R#ApDIPB9;ArzYQeHuJG&5|3KDI(I6-^%|T=jsLwl6vFctS${;lA##QVfSe?;P2D zXHn0=gjam?BCYs-ExI~14P%2f zn}%5kvDfGX_QN96jy7#Z`-UqjoWk_!pM5$eztv;9nDX7ej;&aEduwx^RCVL9sK0uc z$zcYLzwvuxB)U59q9Zz1H40UgojpdiHE2@W5102Rhi=mic=&Zis}I*XqMOC`7B9^1 zyximwi>~)Cso<9PN97N_io6pOTxa@;HaACZ?=*$e0yE|o(w?^QLck|Rk7WXq__rxV zMNoQA!0hpZHIDI1eb%aV-`>&@E6)YFP7^1+#xP`oD)0JAMu5Mc)q2(fUA&r!N6DUA zW>0==prkd8fOy8m}Q(h>1Id4r?KXWRQI`i0-<&4TA&&}y= zcwC;M5ezkSrcSCqz%sho{hEbqEHCLus}Rd7hFK_SO&Rmt1uZc+SF;rnKLsDrH@^8! zmz}6z<=V49Q?R%>V#+JRIL?4Z(q8a+Di8e)vOIT0^Fgg@gR zuxql{jbNOiss$GF-=%vTEhKWjTfu%#o+Ts5qirF((7x^3R!vFI8_^xvo7!^=i%ZvJ z2u@BwP3x@zw(th9vle2N!d97h5Vfmz6z{xO+3S>krmnLnb%%~2&%+fg8T;~%PcSj$ zH7>OKXh(ZYBw4_Iq%GPMeV`PO2E|-XDQ_f`+Z(6kmZI1ZG(V`+5K`S8m-}x)Q2IZK zaV>a@3kD#0GT22Z4G&`8+-tJk-_NuNl@zrJPWsG1 z=0>aBXcyE__7AqK&|RNzNNskKdW^0Os`D2o6$td|Oj3Fw%tj+Y3U7+L3Q7Z5g*8Rb zGWr4`(_Y+Qv>9sxmcX`t)KL2>TI@sfTLZf$Y=~ZPf$yr%+x(6yxmCh3QBj8dBqpf`#T+p_m3jGjo&$-hfTlJN}{W}p3eb_NB^7F&I zo#;);_F!)#Cg5KO2MDm4FJuB&oj7QBCT~iN8sbA7;bf>F{P^N42>C1Xio-kzpxZ%Y zC)Lf)>62=%3Fxva9wyChZy*i(@};+7&J0dQy0M&N-q>?+Mg{~hUNS_$muWx_14cV1 zT>r!U`zTzuCfQI>H{|)fF7Yhnw7)(x>7*cnAt(sJ2K*Tnh1mo4c#nfhJfGD3RPsbb$wx3WQ|=0yE3}mVyPi8p=>?_gan+9$>*Z6r2nmlO`6* zfh_=r-+Len2UXtX1a-=EosVfV32kO&-rIka{!oHr?P@26b$7H|wVf%4;vNxS%>KR2Vc`HNQ;gopzBZBf&w z-lQjp`6h={S6T5grnbtNKe^;7_t!}@UrJXTt}%o}%>&0sA;X-snkb=cg7+eSKlHGQ za6z7BPp3|XQsvE#ed9uR^>U5R&A#M)_Fkt%8A~+TOAOslY#1IA4g^xd=6@+YflC=$;=wF?W+EWecgw=1+-K-zy`j4!MO6sUnR-lscl}J&Z zLkhC<-5HD;me*J}Ut$y%r<|*s0TwXEmh4G(V6uTcS_i+KWBbu2L-6gVZ7f+<=L{t2 z_OD*9p52&yG&#*cecg?(CKRl=n__BZ<6{)a=+Lv8NHcCqXAEXZVkbLX+=tpfD(ilr zZoQJQ`6r57Tm4|DKlNh<7Gvv&aWGaN2$^{?=^gy~K?1@Bf_S6gdcoL(M>CJ}zzb8r zjrT8?lHov!m`lus+-e}GR*)OhiXfTdV-V)09nBrba-rehH4T6dEcata+Th~jfrAEmjJ1fF8 zha_)*pAw*`-Gn6TJL2jl5H|dgiB{{pyErRM;|0-y*U|#q^N>7aQj;7v((okow?HxG zG5kKig9N(^h3oZAQ{UqRFf@e|TAD*5hszP87q?XeFNU(uES?!Iln3NYR^6bSusWE7 zh?$<4%LO55s}?UMNDoNk82=U5d{xIq!#I!|?DMNl$AvC7C7pmSh#k-{}0IQ88%ad?op|)Jf>N z-8SgK@n5B{|M$T1p~7Fq_Zv?;BF$FZlar>@cp73Svj}2&J!74<*$QX-oh#3Q!(p!?<;fP~sG#C0rumq0q?27M| zv*9~}Pm8-Gs0`Xi+2Ko(j5}nv5j=ue48RRc#N;zgv4tjt7;zBAklQ}o7^WM;2qClhqWm3>t_H29eD&=|6Sljcg?Hkld0)m2onSEn!93Hjvd1Ki zC^Bz=3E*rRcVa#1&5zWfx#vz5equDwBno!EgtQw)*Lq7O?dQ%7eDPgU|NPt&Ho`c8 z_DbOEUB)+TQ^Wq(-F`HNcK<-m-7yQ-6mpy^!s)@qFM*a1e`y_~!hTsY|I<|gzajW1 N`XKcElXId!|3A7D+F1Yq literal 0 HcmV?d00001 diff --git a/sdk/octabam/modules/synth/media/ot-scale.png b/sdk/octabam/modules/synth/media/ot-scale.png deleted file mode 100644 index d0560fd434236c7bbafde315d349ca316d13d7cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3945 zcmb7H4OEQj9{;~@Z#AWlQFe3nVY+1shq@MRlj<#{*d$xHZf6s*MQgcf$l~^j%UWNp zyUU*BG<(^teB>ge2$!QdT^}tvY*ef??WD<;Vz$)%KhI3f(1&|Fr{{g&=l}ij{Ql2u zi;4`hwj5yz0M^sPLp}yD!B<8QvH1A*`W{aJTkq*1K{07nP1QlG-F&Pz#-_2R*r0OpV|8BcFs?u6N06Q+ZtXn$o=W@w0`>oX^s&Y`mr3Wjy@BDLx{8*G8D| zArA}tdCFuTT1Ce_NHF^$$X0p`Cd?4?B8m4>Q?W?GiN7mkMVy!wMf`=~SP{b^1Iug% z_8;xFCQ1>U+vJ{6%~4K~M90A@9bGtay{kyFB2CPHw^bsXe;fo0UH*L()a$P}W(cDh z_s)e90T@msIcP2B1(;9;(X5!C<`_Q%SY?t)gavxNoG^NP4J;>?2s6}Ir4y32!w1o|v(<`wz-&$=Svz-!x7PK;jnyHw58>EoNISdtd5K?B=jgcm2?vg!YAbEISnu1kG&OQ^b4H!qfv~v} zNmN4gVexz3P@!12M^zhb8@MTDhDfn3;h&!;*UX&8yaoLe?@eEhBBq0hUH4%R_Yu<~^q$L%TDgFl)(Hg>h8H)VFZuoJGj2l5 z)G^DK$x9=vCizKMtBTiX=1>tLOe1)Qw{NrQ3=-*v95!z}%SGK}u9ZSo+Dz3ft(&_l zzUG8XbHt;Hy3(%&o#VZ>JKnI6VjKuYuefE>pxM9jm&d8gX8Wao<4LzudR=fJm>J!&iu z0*2V@vvjZxF%Lx&?9*=5lg3dsArjXjF*iF$R{ zjlgM;Gy|RnhA?;SdHJoii=ZiO#DNl@~Xi7NJaEOcs2qaxe;vtBd2`mvpWTiGJ3^PU|{lK>uSJ6CA zw!oviR2rZqi%g9$m6E<-J-daA9kSfGwZF7Z zFOjBuMyx;Vg$Bka7PNc{l}pa*@;85p9*)Bf!pU+QU!6haPoC(+)JXW*kw%R~J06LP zR~V9P<2yEK#d$IBR}(MaJ!zb9JK%oynYcA3o=t%c4Nl9oN1kQc^M_0;@_UhMvmTR4 zd?`u`2|o1gyEPk5`V9Wrj+)FT{$`C(ttrUgeR)>;VeRN=U)u4XSu2Wbqp&nI|8vdB zgDO_KCdv(6>y$FLtY+}f`wqNzWYMwM9nG5L#>VX8dJO`r!|EaatrcpKLJfx>$Z0mv z0=ez<80tw{Pa|etFh{`auat|~xp_NQUq^L7>eZcK$KQ8k8xE{I*D+glY|V*DSmB%1a|%kXm7OwfY~(Y_ z`lm)`qYhimh@|ggnsGvEzn07twpma9O|EgNWRrf6-k=@9iS14DcA-idzVV#|+ul$> z-#X5?p88J=feQN89sN62yWT9Us;tBB>_=VZduF}9Tb*0nmY_P&`0bhUSle^aQ(}8M zMQ5tF>v7xAkX+A=Mc*!>w}o2FOaS+caLM?y&`tOoBkb+Y2*ElI4pO)GdUwm!HN%7L z&$w*PEy&;RXsUh&CLd1KD0d1##Y$SiKhzH!$Ny?5zW zdXb4|=GTZQu-uRuYq`H1N38E>e{;MhPzNe};CcsPn4R%flL zjhEj`{XFE+Q%KnfGDkj8H=W?|A4#rTq{C2WPeqB&0y?BEaN6UX(g%d2snAP^^j8!@ z`K_YP+%bE2sLo2rwMk>XapL6{zb8SGYk?jG3@70o9=DrDsUGBiAOQbLY93XM7umt8 zhd#&x$JA)yCrB}L=6 zKW6J)V=nFSlNSSA;lz9*UP0=YlUewBj%1)t#`d9&J&-3PX1wANa^K}Cml@6i^|Qy> tHxA51f7Fgs8h@Cf7XaugkNP@qEI7SkN%8d`yRHEKO%II>DF}|w_%B+>@Dcz3 diff --git a/sdk/octabam/modules/synth/octamod.module.json b/sdk/octabam/modules/synth/octamod.module.json index ee5183da0..d7ddc1223 100644 --- a/sdk/octabam/modules/synth/octamod.module.json +++ b/sdk/octabam/modules/synth/octamod.module.json @@ -3,11 +3,11 @@ "id": "synth", "key": "SYNTH MACHINE", "name": "FM Synth", - "version": "0.1.4-experimental", + "version": "0.1.5-experimental", "category": "machines", "source": { "repository": "https://github.com/timhastie/octatrick-modules", - "revision": "ca13ed85547790f788ceaaada5df3cdd31508441", + "revision": "48b89e7e5315971bf5c29a3fd86183abe8df48f3", "path": "synth" }, "author": { @@ -23,19 +23,19 @@ "presentation": { "label": "Two-operator FM", "family": "Synth machines", - "summary": "Two-operator FM with a dedicated track machine, one to four voices, chords and glide; octatrick 2.12.", - "overview": "FM SYNTH provides one to four paraphonic FM voices through the stock AMP and effects. Version 0.1.4 carries Tim Hastie's octatrick 2.12 line: his own FM SYNTH chooser (adapted from Modwerk's, same Part signature) with the machine list he shares with his SY DRUM machine (a row table; SELECT MACHINE TYPE scrolls), a machine change that resets the track's machine parameters to init (locks kept, as in stock), held notes that no longer stop at the metronome click, DEC HOLD at 127, click-free note starts, FM names in the LFO destination list, the held key as gate and the fixes from his MKI test of the first 2.10 build. With FM Synth alone the list keeps its six rows. The 0.1.2 MIDI drawer fix is kept; the engine leaves 6 dB of headroom, the author's choice in place of 0.1.2's 18 dB attenuation. Worst-case ColdFire cost and exact memory are emulator-measured; the hardware report for this exact build is pending.", + "summary": "Two-operator FM with a dedicated track machine, one to four voices, chords and glide; scale and root on MIDI tracks too; octatrick 2.12 engine, 2.13 quantizer.", + "overview": "FM SYNTH provides one to four paraphonic FM voices through the stock AMP and effects. Version 0.1.5 carries Tim Hastie's 2.13 quantizer: with a scale on, a MIDI track's NOTE knob, NOTE locks and CHROMATIC keys follow SCALE and ROOT as the audio tracks do (chord offsets, TRAN, the arpeggiator and SCALE OFF unchanged), and the CHROMATIC drawer fix for #264 is now the author's own. The engine, chooser and page are 0.1.4's, his octatrick 2.12 line: his own FM SYNTH chooser (adapted from Modwerk's, same Part signature) with the machine list he shares with his SY DRUM machine (a row table; SELECT MACHINE TYPE scrolls), a machine change that resets the track's machine parameters to init (locks kept, as in stock), held notes that no longer stop at the metronome click, DEC HOLD at 127, click-free note starts, FM names in the LFO destination list, the held key as gate and the fixes from his MKI test of the first 2.10 build. With FM Synth alone the list keeps its six rows. The engine leaves 6 dB of headroom, the author's choice in place of 0.1.2's 18 dB attenuation. Worst-case ColdFire cost and exact memory are emulator-measured; the hardware report for this exact build is pending.", "highlights": [ "Dedicated machine chooser entry in SRC SETUP and SELECT MACHINE TYPE; no sample required.", "Pitch, ratio, index, fine tuning, feedback and index decay (HOLD at 127) on SRC.", "One to four paraphonic voices, chord shapes and inversions; click-free note starts.", - "Chromatic playing with held-key gate, MIDI, legato, FM LFO destinations and privately bundled scale/glide controls." + "Chromatic playing with held-key gate, MIDI, legato, FM LFO destinations and privately bundled scale/root/glide controls; on MIDI tracks the NOTE knob, locks and CHROMATIC keys follow the scale." ], "usage": [ "Open a disposable project with FM Synth firmware. Select an audio track, hold FUNC and press SRC, select FM SYNTH with UP/DOWN and press YES. No sample is required.", "Press SRC. Set RATO to 1 and turn INDX from 0 upwards to hear a sine tone acquire FM harmonics. PTCH transposes it; FINE tunes it in cents; FDBK adds feedback; DEC shortens the modulation envelope.", "Place a trig and press PLAY. Press LFO to set VOIC to 2–4 and CHRD to a chord if wanted. AMP ATK/HOLD/REL shape the gain envelope; FUNC+AMP exposes LEG.", - "Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; PLAYBACK, AMP and LFO return to the init values of that machine (step and scene locks stay, as in stock). Assign the desired sample in the normal way." + "For scales and glide, open PROJECT > CONTROL > SEQUENCER. Select SCALE, ROOT or GLIDE with UP/DOWN and turn LEVEL. SCALE OFF restores unquantized pitch. With a scale on, a MIDI track's NOTE knob and NOTE locks step through the scale from ROOT, and its CHROMATIC keys (FUNC + DOWN on the NOTE page) play the scale from ROOT." ] }, "access": { @@ -45,17 +45,16 @@ "Press SRC. Set RATO to 1 and turn INDX from 0 upwards to hear a sine tone acquire FM harmonics. PTCH transposes it; FINE tunes it in cents; FDBK adds feedback; DEC shortens the modulation envelope.", "Place a trig and press PLAY. Press LFO to set VOIC to 2–4 and CHRD to a chord if wanted. AMP ATK/HOLD/REL shape the gain envelope; FUNC+AMP exposes LEG.", "Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; assign the desired sample in the normal way.", - "For scales and glide, open PROJECT > CONTROL > SEQUENCER. Select SCALE, ROOT or GLIDE with UP/DOWN and turn LEVEL. SCALE OFF restores unquantized pitch." + "For scales and glide, open PROJECT > CONTROL > SEQUENCER. Select SCALE, ROOT or GLIDE with UP/DOWN and turn LEVEL. SCALE OFF restores unquantized pitch. With a scale on, a MIDI track's NOTE knob and NOTE locks step through the scale from ROOT, and its CHROMATIC keys (FUNC + DOWN on the NOTE page) play the scale from ROOT." ], "screenshots": [ "media/ot-location.png", - "media/ot-machine-list.png", "media/ot-controls.png", "media/ot-voices.png", "media/ot-lfo-setup.png", "media/ot-legato.png", - "media/ot-scale.png", - "media/ot-glide.png" + "media/ot-glide.png", + "media/ot-midi-chromatic.png" ] }, "controls": [ @@ -130,7 +129,7 @@ "name": "SCALE", "default": 0, "count": 25, - "doc": "OFF restores stock pitch behavior; select a scale to constrain PTCH edits, locks and chromatic keys.", + "doc": "OFF restores stock pitch behavior; select a scale to constrain PTCH edits, locks and chromatic keys, and on MIDI tracks the NOTE knob, NOTE locks and CHROMATIC keys.", "labels": [ "OFF", "MAJOR", @@ -163,7 +162,7 @@ "name": "ROOT", "default": 0, "count": 12, - "doc": "Root of the scale and chromatic keyboard. Ignored with SCALE OFF.", + "doc": "Root of the scale and of the chromatic keyboards (audio and MIDI tracks). Ignored with SCALE OFF.", "labels": [ "C", "C#", @@ -204,27 +203,28 @@ "Emulator measurements qualify one FM track at VOIC 4 (or three at VOIC 1) inside every audio block including the first START; larger loads exceed the conservative per-block budget on START blocks and are not qualified. External MIDI note/CC traffic and Part/project reloads and a power cycle on hardware remain unqualified.", "Marker-file activation (FMSYNTH*.wav / SYNTH*.wav in a FLEX slot) remains for compatibility; the machine list is the recommended route.", "The engine leaves 6 dB of headroom (the author's 6.02 dB in place of 0.1.2's 18.06 dB source attenuation), so 0.1.4 is about 12 dB louder than 0.1.2 at identical mixer settings: AMP VOL 0 is the reference and VOL up to about +6 stays clean. Effects, resonant filtering, higher VOL and summed tracks can clip.", - "POLY8's shared chooser bridge (sdk/octabam/modules/poly8/composition.json) is reviewed against 0.1.2's chooser hooks, which this version replaces with the author's; until that bridge is updated, a build that selects POLY8 together with FM Synth 0.1.4 stops at POLY8's seam check (native and browser alike) instead of building." + "On MIDI tracks the quantizer acts where a note is edited or played from the panel: the NOTE knob, NOTE locks (grid and live recording) and the CHROMATIC keys. NOT2–NOT4 stay semitone offsets, and TRAN, the arpeggiator's own notes, MIDI IN, LFOs on NOTE and scene locks are not quantized; ARP SETUP KEY forces a track's notes into a key. A NOTE set before the scale was on plays as stored until it is turned.", + "POLY8's shared chooser bridge (sdk/octabam/modules/poly8/composition.json) is reviewed against 0.1.2's chooser hooks, which 0.1.4 and this version replace with the author's; until that bridge is updated, a build that selects POLY8 together with FM Synth stops at POLY8's seam check (native and browser alike) instead of building." ] }, "resources": { - "recorded": "2026-10-11; emulator cycles and exact memory, v2.12 image 00fa6caa; hardware report pending", + "recorded": "2026-10-12; emulator cycles and exact memory, image 37661aee; hardware report pending", "storage": { "label": "FM code, state and image bytes (bundled quantizer included)", - "display": "43,460 bytes plus shared platform reserve", - "value": 43460, + "display": "44,052 bytes plus shared platform reserve", + "value": 44052, "unit": "bytes", "method": "static", - "conditions": "Exact from the link map: 20,996-byte engine DRAM unit (code, tables and state for all eight tracks), 2,076-byte chooser unit (machine.s with the 2.11 machine-list row table and 2.12's machine-change init), 3,532-byte bundled quantizer unit, 1,868-byte pinned page, 461 bytes of quantizer ROM units, tables and hook sites, 422 bytes of FM hook sites and 12 bytes of kind-table entries in the OS image, and the 14,093-byte appended platform loader. Excludes the 352-byte stack bound and the shared 10,461,204-byte Flex sample-RAM platform reserve (sample pages 14,602 -> 12,895). See TESTING.md.", + "conditions": "Exact from the link map: 20,996-byte engine DRAM unit (code, tables and state for all eight tracks), 2,076-byte chooser unit (machine.s with the 2.11 machine-list row table and 2.12's machine-change init), 3,892-byte bundled quantizer unit (2.13), 1,868-byte pinned page, 489 bytes of quantizer ROM units, tables and hook sites, 422 bytes of FM hook sites and 12 bytes of kind-table entries in the OS image, and the 14,297-byte appended platform loader. Excludes the 352-byte stack bound and the shared 10,460,844-byte Flex sample-RAM platform reserve (sample pages 14,602 -> 12,895). See TESTING.md.", "source": "TESTING.md" }, "processing": { "label": "ColdFire processing", - "display": "24,125 cycles/block per VOIC 4 track (emulated)", - "value": 24125, + "display": "24,167 cycles/block per VOIC 4 track (emulated)", + "value": 24167, "unit": "cycles/block", "method": "emulator", - "conditions": "Emulated ColdFire instructions per 16-sample frame interrupt (362.8 us at 44.1 kHz), converted at 9.44 ns per instruction (the hardware CF METER calibration, 2.49216 cycles at 264 MHz); not chip wall-clock cycles. One VOIC 4 track adds up to 9,680 instructions on its START block (24,125 cycles; the largest one-track peak of five runs over the smallest no-FM block) and about 5,018 to 5,087 sustained; one VOIC 1 track adds up to 4,981 on START and about 2,184 to 2,215 sustained. Worst case with one VOIC 4 track: 32,299 instructions + 372 wrapper allowance = 81,422 of 95,779 cycles/block, stock reserve included. Eight VOIC 4 tracks peak at 97,729 instructions (243,557 cycles, 2.54x the block) and are over budget. See TESTING.md.", + "conditions": "Emulated ColdFire instructions per 16-sample frame interrupt (362.8 us at 44.1 kHz), converted at 9.44 ns per instruction (the hardware CF METER calibration, 2.49216 cycles at 264 MHz); not chip wall-clock cycles. One VOIC 4 track adds up to 9,697 instructions on its START block (24,167 cycles; the largest one-track peak of seven runs, five on 0.1.4's image and two on this one, over the smallest no-FM block) and about 5,018 to 5,087 sustained; one VOIC 1 track adds up to 4,981 on START and about 2,184 to 2,215 sustained. Worst case with one VOIC 4 track: 32,314 instructions + 372 wrapper allowance = 81,459 of 95,779 cycles/block, stock reserve included. Eight VOIC 4 tracks peak at 97,745 instructions (243,597 cycles, 2.54x the block) and are over budget. The 2.13 quantizer adds no frame-interrupt code. See TESTING.md.", "source": "TESTING.md" }, "impact": { @@ -232,7 +232,7 @@ "cpu": { "level": "high", "basis": "source-estimate", - "rationale": "ColdFire generates two sine operators per voice per sample, plus envelopes, glide, crossfades and fades at note changes and UI hooks. Measured: about 625 own instructions per render call at VOIC 1 and 2,066 at VOIC 4; one VOIC 4 track adds up to 9,680 instructions on its START block.", + "rationale": "ColdFire generates two sine operators per voice per sample, plus envelopes, glide, crossfades and fades at note changes and UI hooks. Measured: about 625 own instructions per render call at VOIC 1 and 2,066 at VOIC 4; one VOIC 4 track adds up to 9,697 instructions on its START block. The 2.13 quantizer's MIDI-track hooks run in the UI task: up to 77 instructions per CHROMATIC key, 302 per NOTE lock turn of three clicks.", "source": "TESTING.md" }, "dsp": { @@ -244,48 +244,50 @@ "memory": { "level": "moderate", "basis": "source-estimate", - "rationale": "20,996-byte engine unit with state for all eight tracks, 2,076-byte chooser, 3,532-byte bundled quantizer and a 1,868-byte fixed UI page; the 14,093-byte loader and the 10,461,204-byte sample-RAM platform reserve are the shared DRAM platform's.", + "rationale": "20,996-byte engine unit with state for all eight tracks, 2,076-byte chooser, 3,892-byte bundled quantizer and a 1,868-byte fixed UI page; the 14,297-byte loader and the 10,460,844-byte sample-RAM platform reserve are the shared DRAM platform's.", "source": "TESTING.md" } } }, "tests": { "report": "TESTING.md", - "summary": "Emulator qualification of the native FM Synth 0.1.4 image (MAIN OS 00fa6caa..., built from the author's ca13ed8, octatrick 2.12 tag v2.12, with the bundled quantizer): frame-interrupt instruction counts for 1 to 8 FM tracks at VOIC 1, 2 and 4, converted at the hardware CF METER calibration (9.44 ns/instruction, 264 MHz); the bounding cards measured five times. One FM track at VOIC 4 keeps every block, including the first START, inside the 16-sample block (81,422 of 95,779 cycles); eight VOIC 4 tracks do not. Exact memory from the link map: 43,812 module-owned bytes plus the shared 10,461,204-byte sample-RAM platform reserve. Software verification: the module's own native Part smoke (verify.py) passes on this image; two FM tracks pass Part save/reload, project save/reload and a fresh project load in the emulator (the battery-RAM-only restart is the hardware test); the 2.12 metronome case and the machine-change init checked in the emulator; eight LCD captures; the author's odd-address check. The native/browser coverage record is not refreshed: POLY8's chooser bridge stops on this chooser (TESTING.md). A hardware report for this exact image is pending; Modwerk's 0.1.1/0.1.2 owner approvals stay with those versions.", + "summary": "Emulator qualification of the native FM Synth 0.1.5 image (MAIN OS 37661aee..., built from the author's 48b89e7: the octatrick 2.12 engine, page and chooser, byte-identical in the image to 0.1.4's, and his 2.13 quantizer): the quantizer's MIDI-track gates against a model of the rules (NOTE knob and NOTE locks 409 of 409 over eight SCALE / ROOT settings; CHROMATIC keys 245 of 245 with balanced note-offs and the arpeggiator fed snapped notes; a LIVE RECORDING lock as the author's build), SCALE OFF equal to stock, CHROMATIC on a MIDI track draws and plays, and its added cost per call. Frame-interrupt instruction counts for 1 to 8 FM tracks at VOIC 1, 2 and 4 are 0.1.4's (image 00fa6caa, the same FM objects), with the bounding cards rerun twice on this image, converted at the hardware CF METER calibration (9.44 ns/instruction, 264 MHz). One FM track at VOIC 4 keeps every block, including the first START, inside the 16-sample block (81,459 of 95,779 cycles); eight VOIC 4 tracks do not. Exact memory from the link map: 44,404 module-owned bytes plus the shared 10,460,844-byte sample-RAM platform reserve. Software verification on this image: the module's own native Part smoke (verify.py); two FM tracks pass Part save/reload, project save/reload and a fresh project load in the emulator (the battery-RAM-only restart is the hardware test); the machine-change init; seven LCD captures; the author's odd-address check. The native/browser coverage record is not refreshed: POLY8's chooser bridge stops on this chooser (TESTING.md). A hardware report for this exact image is pending; Modwerk's 0.1.1/0.1.2 owner approvals stay with those versions.", "hardwareStatus": "historical", "evidenceRevision": "651dc0f2d233eb9dce69fbe7f5f0d064ee33ddf5", "gates": [ - "verify.py: native Part smoke on image 00fa6caa (eight-track selection, Part and shadow signature, sample slots unchanged, reselection keeps controls, return to FLEX): passed (evidence/native-smoke.json)", - "Two FM instances (T1, T5) with distinct settings on image 00fa6caa in the emulator: isolation, Part save/reload, project save/reload and a fresh project load restore both; audio after each transition (TESTING.md)", + "verify.py: native Part smoke on image 37661aee (eight-track selection, Part and shadow signature, sample slots unchanged, reselection keeps controls, return to FLEX): passed (evidence/native-smoke.json)", + "2.13 quantizer on MIDI track 1 of image 37661aee in the emulator: NOTE knob and NOTE locks 409 of 409 against the model of the rules (eight SCALE / ROOT settings, SCALE OFF included); CHROMATIC keys 1..16 at five octave words 245 of 245, note-offs balanced, keys held across SCALE / ROOT changes released, the arpeggiator fed snapped notes; LIVE RECORDING lock as the author's build (TESTING.md)", + "0.1.4's image 00fa6caa under the same knob script: 409 of 409 equal to the stock clamp (the stock behaviour SCALE OFF must keep)", + "CHROMATIC on a MIDI track (the #264 freeze of the author's 2.9..2.12 public tags): the drawer draws with the readout and the keys play on image 37661aee", + "Two FM instances (T1, T5) with distinct settings on image 37661aee in the emulator: isolation, Part save/reload, project save/reload and a fresh project load restore both; audio after each transition (TESTING.md)", "Battery-RAM-only restart: not testable on the pinned emulator; on the author's hardware sheet", - "2.12 metronome case on image 00fa6caa: a held key sample-identical with the metronome on and off, a held VOIC 4 chord's voices never released; the 2.11 build cut both (TESTING.md)", - "2.12 machine-change init on image 00fa6caa: FLEX -> FM SYNTH -> FLEX -> FM SYNTH gives FM SYNTH's init both times, FLEX's defaults between, FX1 kept (TESTING.md)", - "Native/browser composition: the coverage set with POLY8 stops at POLY8's seam check (open, TESTING.md); without POLY8, 122 selections: 35 builds match outside the platform writes, 87 refusals match, 0 mismatches (local run on main 3151975; sdk/native-comparisons/synth.json not refreshed)", - "Emulator worst-case frame-interrupt cycles with stock reserve, the bounding cards measured five times, converted at the CF METER calibration", + "2.12 machine-change init on image 37661aee: FLEX -> FM SYNTH -> FLEX -> FM SYNTH gives FM SYNTH's init both times, FLEX's defaults between, FX1 kept (TESTING.md)", + "Native/browser composition: the coverage set with POLY8 stops at POLY8's seam check (open, TESTING.md); without POLY8 in the pool, a local run (TESTING.md; sdk/native-comparisons/synth.json not refreshed)", + "Emulator worst-case frame-interrupt cycles with stock reserve: 0.1.4's cards (the same FM objects), the bounding cards measured five times on image 00fa6caa and twice on image 37661aee, converted at the CF METER calibration", "Exact memory inventory reconciled with the native link map; static stack bound from the assembled runtime", - "The author's tools/align_check.py: no instruction and no detour target at an odd address", - "verify-audio.py and verify-regressions.py (Octemu audio and the #264 MIDI selector): not rerun for 0.1.4; 0.1.2 results in evidence/release-0.1.2", + "The author's tools/align_check.py (2.13): no instruction and no detour target at an odd address", + "verify-audio.py and verify-regressions.py (Octemu audio and the #264 MIDI selector): not rerun; 0.1.2 results in evidence/release-0.1.2", "Hardware report for this source and image: pending (TIM placeholders)" ], "qualification": { - "moduleVersion": "0.1.4-experimental", - "sourceSha256": "4fc06072b4039db83cba1600e949d00770c9753d974bbf76a5809f8e1be9e851", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "moduleVersion": "0.1.5-experimental", + "sourceSha256": "401d3b61412aff964617581f9f0bfcfe0ac83d6a6ee970bb1440f2393a6ac60a", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "cycles": [ { "processor": "coldfire", - "worstCase": 24125, - "maxConfiguration": 81422, + "worstCase": 24167, + "maxConfiguration": 81459, "maxInstances": 1, "budget": 95779, "unit": "cycles/block", "method": "emulator", "conditions": { - "parameterExtremes": "Emulator windows with PTCH/RATO/INDX/FINE/FDBK p-locked to their endpoints (alternating 0 and 127 every step, PTCH 52..75), Part RATO/INDX/FDBK at 127, DEC at both extremes (Part and lock 0 and 127: 63,369 vs 63,398 instr/block sustained at eight tracks, peaks 97,705 vs 97,697 in the first run and 97,681..97,729 over five runs, so decay does not bound the render), VOIC 1, 2 and 4, CHRD stepping through 22 shapes with all four inversions as step locks, GLIDE 20 in battery RAM. Every step changes the pitch, so the 2.10 crossfades, voice fades and waiting notes run every step. Measured in emulated ColdFire instructions on the pinned ot_emu and converted at the CF METER calibration; see TESTING.md.", + "parameterExtremes": "Emulator windows with PTCH/RATO/INDX/FINE/FDBK p-locked to their endpoints (alternating 0 and 127 every step, PTCH 52..75), Part RATO/INDX/FDBK at 127, DEC at both extremes (Part and lock 0 and 127: 63,369 vs 63,398 instr/block sustained at eight tracks, peaks 97,705 vs 97,697 in the first run of image 7 and 97,681..97,745 over its five runs and image 8's two, so decay does not bound the render), VOIC 1, 2 and 4, CHRD stepping through 22 shapes with all four inversions as step locks, GLIDE 20 in battery RAM. Every step changes the pitch, so the 2.10 crossfades, voice fades and waiting notes run every step. Measured in emulated ColdFire instructions on the pinned ot_emu and converted at the CF METER calibration; see TESTING.md.", "parameterModulation": "Every trig carries locks on all six PLAYBACK controls plus VOIC and CHRD; LFO1 and LFO2 of every track at full speed and depth on INDX and FDBK; scene locks on all six PLAYBACK controls in all sixteen scenes with the crossfader at mid-travel, so scene interpolation and LFO evaluation run every block. MIDI CC and live knob turns use the same stock lock/parameter path inside the measured frame ISR and were not driven separately.", - "modeSwitching": "The first PLAY after boot (the stock full Part publication on every track's first trig) is the measured peak; every later step is a warm retrigger of all voices (VOIC 4 chords steal and restart voices every step). STOP/PLAY repeated once per run. Machine choice (SRC SETUP, SELECT MACHINE TYPE, the file browser) with 2.12's machine-change init, the Part validator, page and setup-window changes run in the UI task, outside the frame ISR deadline, and were not timed; their stack is bounded statically.", - "maxLoad": "Eight FLEX tracks on the SYNTH marker, all starting on the same step every step at 120 BPM, stock DELAY in FX2 on all eight tracks, FX1 stock; the same load with the tracks chosen as FM SYNTH by the Part signature (no sample renderer nested) peaks lower, 88,317. Measured scaling: 1, 2, 3, 4 and 8 tracks at VOIC 1, 2 and 4. One track at VOIC 4 (32,299 instr peak, the maximum of five runs, spread 85) and three at VOIC 1 (37,368) keep every block, including the first START, inside the 38,432-instruction-time block; two at VOIC 2 (39,415), two at VOIC 4 (41,613) and four at VOIC 1 (42,140) fit only at the 8.38 ns end; eight tracks at VOIC 4 peak at 97,729 and run at 63,362..63,405 sustained. maxInstances is therefore 1 under the module's own worst case. No USB modules are in the measured image; USB/MIDI traffic and held chromatic keys were not driven.", - "inputConditions": "The FM engine generates its own signal; it has no audio input. Render cost depends on voice count, glide, note changes (crossfades and fades) and lock/LFO/scene activity, all driven at their worst; DEC extremes do not change cost. Silence (n0: no FM track playing) was measured as the stock reserve plus the per-track source hook: 22,619..22,893 instr/block over five runs." + "modeSwitching": "The first PLAY after boot (the stock full Part publication on every track's first trig) is the measured peak; every later step is a warm retrigger of all voices (VOIC 4 chords steal and restart voices every step). STOP/PLAY repeated once per run. The bundled quantizer's MIDI-track hooks (NOTE knob, NOTE lock, CHROMATIC key) run in the UI task when a knob is turned or a key pressed: emulator instruction counts per call in TESTING.md. Machine choice (SRC SETUP, SELECT MACHINE TYPE, the file browser) with 2.12's machine-change init, the Part validator, page and setup-window changes run in the UI task, outside the frame ISR deadline, and were not timed; their stack is bounded statically.", + "maxLoad": "Eight FLEX tracks on the SYNTH marker, all starting on the same step every step at 120 BPM, stock DELAY in FX2 on all eight tracks, FX1 stock; the same load with the tracks chosen as FM SYNTH by the Part signature (no sample renderer nested) peaks lower, 88,317. Measured scaling: 1, 2, 3, 4 and 8 tracks at VOIC 1, 2 and 4. One track at VOIC 4 (32,314 instr peak, the maximum of seven runs over image 7 and image 8, spread 142) and three at VOIC 1 (37,368) keep every block, including the first START, inside the 38,432-instruction-time block; two at VOIC 2 (39,415), two at VOIC 4 (41,613) and four at VOIC 1 (42,140) fit only at the 8.38 ns end; eight tracks at VOIC 4 peak at 97,745 and run at 63,362..63,405 sustained. maxInstances is therefore 1 under the module's own worst case. No USB modules are in the measured image; USB/MIDI traffic and held chromatic keys were not driven.", + "inputConditions": "The FM engine generates its own signal; it has no audio input. Render cost depends on voice count, glide, note changes (crossfades and fades) and lock/LFO/scene activity, all driven at their worst; DEC extremes do not change cost. Silence (n0: no FM track playing) was measured as the stock reserve plus the per-track source hook: 22,617..22,893 instr/block over seven runs." }, "report": "TESTING.md" } @@ -349,35 +351,35 @@ "scope": "shared" }, { - "name": "Bundled quantizer DRAM unit", + "name": "Bundled quantizer DRAM unit (2.13: the MIDI-track NOTE knob, NOTE lock and CHROMATIC key code)", "space": "sdram", - "words": 3532, + "words": 3892, "wordBits": 8, - "bytes": 3532, + "bytes": 3892, "scope": "shared" }, { - "name": "Bundled quantizer ROM units (243 B), SEQUENCER tables (72 B) and hook sites (19 detours 142 B, one poke 4 B)", + "name": "Bundled quantizer ROM units (243 B), SEQUENCER tables (72 B) and hook sites (22 detours 170 B, one poke 4 B)", "space": "cpu-flash", - "words": 461, + "words": 489, "wordBits": 8, - "bytes": 461, + "bytes": 489, "scope": "shared" }, { "name": "Platform loader and packed DRAM units appended to the image", "space": "cpu-flash", - "words": 14093, + "words": 14297, "wordBits": 8, - "bytes": 14093, + "bytes": 14297, "scope": "shared" }, { "name": "Platform reserve: Flex sample RAM given up to the DRAM platform, less the three DRAM units", "space": "sdram", - "words": 10461204, + "words": 10460844, "wordBits": 8, - "bytes": 10461204, + "bytes": 10460844, "scope": "shared" }, { @@ -390,10 +392,10 @@ } ], "perInstanceBytes": 506, - "sharedBytes": 10500968, + "sharedBytes": 10501200, "maxInstances": 8, - "totalBytes": 10505016, - "conditions": "Exact link map of the measured native build (runtime.elf, layout.json, the native build log). The module's own bytes are 43,812: the 20,996-byte engine DRAM unit (15,420 code/tables + 1,528 shared state + 8 x 506 per-track state, preallocated for all eight tracks), the 2,076-byte chooser unit (machine.s: the 2.11 row table, the eleven machine-list detours' code and 2.12's machine-change init), the 3,532-byte bundled quantizer unit, the 1,868-byte pinned page, 461 bytes of quantizer ROM units/tables and hook sites, FM hook sites and kind-table entries, the 14,093-byte appended platform loader and the 352-byte stack bound. The 10,461,204-byte platform reserve is the Flex sample RAM given up to the DRAM platform (sample pages 14,602 -> 12,895, 1,707 pages of 6,144 bytes, less the three DRAM units): any DRAM module creates it, and it is counted once here, not per track. No heap or dynamic allocation; no SRAM, battery-RAM or DSP allocation of its own (GLIDE is the bundled quantizer's battery-RAM byte; LEG, the LFO destination and the FM SYNTH signature use stock Part bytes and their battery-RAM shadow; the machine-change init writes stock Part bytes, their shadow and the live lane). Stack: static upper bound from the assembled runtime of this image, following its control flow (the deepest UI-task path is still the Part validator wrap fm_validate, 220 B including the replayed stock 96-byte prologue; the machine-change init's callers reach 116 B; the frame path sy_render 132 B). Hardware canaries not measured.", + "totalBytes": 10505248, + "conditions": "Exact link map of the measured native build (runtime.elf, layout.json, the native build log). The module's own bytes are 44,404: the 20,996-byte engine DRAM unit (15,420 code/tables + 1,528 shared state + 8 x 506 per-track state, preallocated for all eight tracks) and the 2,076-byte chooser unit (machine.s), both byte-identical to 0.1.4's and linked at the same addresses; the 3,892-byte bundled quantizer unit (3,532 in 0.1.4: the 2.13 MIDI-track code); the 1,868-byte pinned page; 489 bytes of quantizer ROM units/tables and hook sites (three new 2.13 sites, 28 B); FM hook sites and kind-table entries; the 14,297-byte appended platform loader and the 352-byte stack bound. The 10,460,844-byte platform reserve is the Flex sample RAM given up to the DRAM platform (sample pages 14,602 -> 12,895, 1,707 pages of 6,144 bytes, less the three DRAM units): any DRAM module creates it, and it is counted once here, not per track. No heap or dynamic allocation; no SRAM, battery-RAM or DSP allocation of its own (GLIDE is the bundled quantizer's battery-RAM byte; the MIDI-track hooks read the existing SCALE and ROOT bytes and write only the stock NOTE Part byte, lock byte or note register they replace; LEG, the LFO destination and the FM SYNTH signature use stock Part bytes and their battery-RAM shadow; the machine-change init writes stock Part bytes, their shadow and the live lane). Stack: static upper bound from the assembled runtime of this image, following its control flow (the deepest UI-task path is still the Part validator wrap fm_validate, 220 B including the replayed stock 96-byte prologue; the 2.13 quantizer hooks reach 72 B; the frame path sy_render 132 B). Hardware canaries not measured.", "report": "TESTING.md" }, "hardware": { @@ -403,8 +405,8 @@ "testedOn": "TIM: YYYY-MM-DD", "tester": "TIM: credited handle", "sourceRevision": "651dc0f2d233eb9dce69fbe7f5f0d064ee33ddf5", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", - "summary": "TIM: what was played on the unit and what happened (MAIN OS 00fa6caa..., the hardware test sheet)", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", + "summary": "TIM: what was played on the unit and what happened (MAIN OS 37661aee..., the hardware test sheet)", "limitations": [ "TIM: duration, track and voice counts, untested cases" ], @@ -417,18 +419,18 @@ "Open a disposable project with FM Synth firmware. Select an audio track, hold FUNC and press SRC, select FM SYNTH with UP/DOWN and press YES. No sample is required.", "Press SRC. Set RATO to 1 and turn INDX from 0 upwards to hear a sine tone acquire FM harmonics. PTCH transposes it; FINE tunes it in cents; FDBK adds feedback; DEC shortens the modulation envelope.", "Place a trig and press PLAY. Press LFO to set VOIC to 2–4 and CHRD to a chord if wanted. AMP ATK/HOLD/REL shape the gain envelope; FUNC+AMP exposes LEG.", - "Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; PLAYBACK, AMP and LFO return to the init values of that machine (step and scene locks stay, as in stock). Assign the desired sample in the normal way." + "Press STOP twice to silence the voice. To return to a sample machine, hold FUNC and press SRC, select FLEX or STATIC and press YES; PLAYBACK, AMP and LFO return to the init values of that machine (step and scene locks stay, as in stock). Assign the desired sample in the normal way.", + "For scales and glide, open PROJECT > CONTROL > SEQUENCER. Select SCALE, ROOT or GLIDE with UP/DOWN and turn LEVEL. SCALE OFF restores unquantized pitch. With a scale on, a MIDI track's NOTE knob and NOTE locks step through the scale from ROOT, and its CHROMATIC keys (FUNC + DOWN on the NOTE page) play the scale from ROOT." ] }, "screenshots": [ "media/ot-location.png", - "media/ot-machine-list.png", "media/ot-controls.png", "media/ot-voices.png", "media/ot-lfo-setup.png", "media/ot-legato.png", - "media/ot-scale.png", - "media/ot-glide.png" + "media/ot-glide.png", + "media/ot-midi-chromatic.png" ], "screenshotStyle": "black-and-white" } @@ -452,25 +454,8 @@ "page": "SRC SETUP", "shows": "location", "firmware": "1.40C", - "moduleVersion": "0.1.4-experimental", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", - "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." - } - }, - { - "path": "media/ot-machine-list.png", - "captureType": "emulator", - "caption": "FM SYNTH is also the sixth row of SELECT MACHINE TYPE.", - "alt": "Actual Octatrack LCD: FM SYNTH is also the sixth row of SELECT MACHINE TYPE.", - "credit": "Modwerk contributors", - "license": "LicenseRef-OT-UI-Documentation", - "source": "original", - "otUi": { - "page": "SELECT MACHINE TYPE", - "shows": "location", - "firmware": "1.40C", - "moduleVersion": "0.1.4-experimental", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "moduleVersion": "0.1.5-experimental", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, @@ -486,8 +471,8 @@ "page": "FM SYNTH SRC", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.4-experimental", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "moduleVersion": "0.1.5-experimental", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, @@ -503,8 +488,8 @@ "page": "FM SYNTH LFO", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.4-experimental", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "moduleVersion": "0.1.5-experimental", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, @@ -520,8 +505,8 @@ "page": "LFO SETUP", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.4-experimental", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "moduleVersion": "0.1.5-experimental", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, @@ -537,42 +522,42 @@ "page": "AMP SETUP", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.4-experimental", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "moduleVersion": "0.1.5-experimental", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, { - "path": "media/ot-scale.png", + "path": "media/ot-glide.png", "captureType": "emulator", - "caption": "The privately bundled quantizer adds SCALE; this example selects MAJOR.", - "alt": "Actual Octatrack LCD: The privately bundled quantizer adds SCALE; this example selects MAJOR.", + "caption": "The privately bundled quantizer adds SCALE, ROOT and GLIDE; this example is MINOR with ROOT A and GLIDE OFF.", + "alt": "Actual Octatrack LCD: The privately bundled quantizer adds SCALE, ROOT and GLIDE; this example is MINOR with ROOT A and GLIDE OFF.", "credit": "Modwerk contributors", "license": "LicenseRef-OT-UI-Documentation", "source": "original", "otUi": { - "page": "CONTROL SEQUENCER: SCALE", + "page": "CONTROL SEQUENCER: SCALE, ROOT, GLIDE", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.4-experimental", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "moduleVersion": "0.1.5-experimental", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, { - "path": "media/ot-glide.png", + "path": "media/ot-midi-chromatic.png", "captureType": "emulator", - "caption": "GLIDE sets pitch slide time; this example shows OFF.", - "alt": "Actual Octatrack LCD: GLIDE sets pitch slide time; this example shows OFF.", + "caption": "On a MIDI track with a scale on, the CHROMATIC keyboard plays the scale from ROOT; the number beside it reads A 3 (ROOT A, octave 3).", + "alt": "Actual Octatrack LCD: On a MIDI track with a scale on, the CHROMATIC keyboard plays the scale from ROOT; the number beside it reads A 3 (ROOT A, octave 3).", "credit": "Modwerk contributors", "license": "LicenseRef-OT-UI-Documentation", "source": "original", "otUi": { - "page": "CONTROL SEQUENCER: GLIDE", + "page": "MIDI NOTE: CHROMATIC", "shows": "controls", "firmware": "1.40C", - "moduleVersion": "0.1.4-experimental", - "imageSha256": "00fa6caab06dbfa95d5258b2f255fd174f3492a7aebfdbb7c3658ede9157033e", + "moduleVersion": "0.1.5-experimental", + "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "setup": "Headless ot_emu; MKII panel; empty 32 MiB disposable card; stopped transport; actual 128×64 LCD pixels at integer scale 6." } }, diff --git a/sdk/octabam/modules/synth/release-notes.json b/sdk/octabam/modules/synth/release-notes.json index 413ce7c0b..371da975f 100644 --- a/sdk/octabam/modules/synth/release-notes.json +++ b/sdk/octabam/modules/synth/release-notes.json @@ -1,15 +1,11 @@ { - "version": "0.1.4-experimental", - "date": "2026-10-11", + "version": "0.1.5-experimental", + "date": "2026-10-12", "changes": [ - "move to the author’s octatrick 2.12 line (timhastie/octatrick-modules tag v2.12, ca13ed8): the FM SYNTH chooser is the author’s machine.s, adapted from Modwerk’s, with the same FLEX + FM/1 Part signature, so 0.1.1/0.1.2 projects open unchanged.", - "the metronome click no longer cuts a held note: the engine had hooked the frame builder’s clear of the metronome’s click word (read as a STOP word), so with CLICK ON every beat released held notes and chords. 2.12 removes that hook; STOP still ends the engine’s voices through the stock voice kill.", - "a machine change to or from FM SYNTH, through SELECT MACHINE TYPE, SRC SETUP or the file browser, resets the track’s machine parameters (PLAYBACK, PLAYBACK SETUP, AMP, AMP SETUP, LFO and LFO SETUP) to init: FM SYNTH’s own seeds, or a stock machine’s defaults as the stock Part initialiser writes them. Step and scene locks are kept, as in the stock firmware; FX1 and FX2 stay.", - "the machine list is shared with the author’s SY DRUM machine: the rows after PICKUP are a row table, the six row-count and row-bound constants became detours that read it, and SELECT MACHINE TYPE scrolls like SRC SETUP. With FM Synth alone the list keeps its six rows.", - "DEC puts HOLD at 127 and 0 is the shortest decay; note starts no longer click (attacks of at least one carrier period, S-shaped; low notes start slightly slower).", - "LFO SETUP lists the FM page’s names (PTCH RATO INDX FINE FDBK DEC) as destinations on an FM track and skips SPD3/DEP3; a held CHROMATIC key is the gate, HOLD applies to sequencer trigs.", - "fixes from the author’s MKI test: no click when a sounding note restarts at a short DEC, no lost notes in fast VOIC 2–4 runs, recorded legato phrases keep sounding, and FM SYNTH chosen after a sample plays C4 at FINE 0c.", - "6 dB of headroom in the engine (author’s decision; replaces the 18.06 dB OUTPUT_SHIFT of 0.1.2): the mono voice and each paraphonic voice are 6.02 dB lower than the author’s earlier 2.10 builds, so AMP VOL 0 is the reference and VOL up to about +6 stays clean; output is about 12 dB louder than 0.1.2 at the same settings. The 0.1.2 MIDI chromatic drawer fix is kept.", - "Emulator worst-case ColdFire cost, exact memory and two-track Part/project save and reload are recorded for this build (TESTING.md). The author’s MKI report of this exact image is pending; physical reboot survival is not yet verified." + "SCALE QUANTIZER on MIDI tracks (the author’s octatrick 2.13 quantizer, timhastie/octatrick-modules 48b89e7): with a scale on, a MIDI track’s NOTE knob and NOTE locks step through the scale’s notes from ROOT, and the MIDI CHROMATIC keys play the scale from ROOT (key 1 sounds the root; the number beside the keyboard reads the root and the octave, for example “A 3”).", + "chord offsets (NOT2–NOT4), the arpeggiator, TRAN, MIDI IN and the sequencer are unchanged on MIDI tracks: the quantizer acts where a note is edited or played from the panel (ARP SETUP KEY still forces every note a track sends into a key). SCALE OFF is unchanged.", + "the MIDI chromatic freeze is fixed in the author’s own line: since 2.13 his quantizer hooks the CHROMATIC drawer at its common argument push (0x400449be), the form of Modwerk’s 0.1.2 fix for #264, credited to the Modwerk contributors. The bundled quantizer’s sources are now the author’s, byte for byte (its manifest in Modwerk’s stock_guard form); FM Synth images carried the fix already.", + "the FM engine, the FM SYNTH chooser and the page are unchanged from 0.1.4: the same compiled objects at the same addresses in the image.", + "Emulator checks on this exact image (TESTING.md): the NOTE knob and locks against a model of the rules (409 of 409), the CHROMATIC keys (245 of 245), SCALE OFF equal to stock, the quantizer’s added cost, exact memory, two-track Part/project save and reload. The author’s MKI report of this exact image is pending; physical reboot survival is not yet verified." ] } diff --git a/sdk/octabam/modules/synth/stock-guards.json b/sdk/octabam/modules/synth/stock-guards.json index 8381e6b27..2500b5beb 100644 --- a/sdk/octabam/modules/synth/stock-guards.json +++ b/sdk/octabam/modules/synth/stock-guards.json @@ -332,6 +332,24 @@ "length": 10, "sha256": "ccf689e403ebd88a651c829b4ae191d609c85baa2a99409bd0a2870d20aaa73e" }, + { + "path": "upstream/quantizer/manifest.py", + "address": 1074090882, + "length": 8, + "sha256": "03bcaf390b2fd396821abc005afa9853b59e2fcaf10ddc223c001b3a9d276e08" + }, + { + "path": "upstream/quantizer/manifest.py", + "address": 1074072472, + "length": 10, + "sha256": "9cb4af08e685b7a46a08340cfec555868a0568533c1fb6fc2ec37159dfd980d9" + }, + { + "path": "upstream/quantizer/manifest.py", + "address": 1074017960, + "length": 10, + "sha256": "2ab0a5529e7748a00dea86e66bc4fbb6105ec7f36e73e481bb840f6570da98cc" + }, { "path": "upstream/quantizer/manifest.py", "address": 1074068568, diff --git a/sdk/octabam/modules/synth/upstream/LICENSE b/sdk/octabam/modules/synth/upstream/LICENSE index d350f0f05..abff1c76e 100644 --- a/sdk/octabam/modules/synth/upstream/LICENSE +++ b/sdk/octabam/modules/synth/upstream/LICENSE @@ -41,8 +41,10 @@ The FM SYNTH machine-list chooser -- synth/machine.s and the chooser's parts of synth/poly.s, synth/page.s and quantizer/quantizer.s -- is adapted from Modwerk's FM Synth module (https://github.com/repeat98/modwerk, sdk/octabam/modules/synth, commit 1c1d938), whose chooser is derived from -Modwerk's Analog BD chooser hooks. Their notices, as that module's LICENSE -carries them: +Modwerk's Analog BD chooser hooks. Since 2.13 the quantizer's CHROMATIC drawer +hook at the drawer's common push (quantizer/quantizer.s qz_octnum at 0x400449be, +Modwerk #264) takes the form of the quantizer that module bundles. Their +notices, as that module's LICENSE carries them: Dedicated chooser, Part validation, sample-free transport and original presentation: diff --git a/sdk/octabam/modules/synth/upstream/quantizer/README.md b/sdk/octabam/modules/synth/upstream/quantizer/README.md index 71faca16f..f5d8ad885 100644 --- a/sdk/octabam/modules/synth/upstream/quantizer/README.md +++ b/sdk/octabam/modules/synth/upstream/quantizer/README.md @@ -1,6 +1,8 @@ # Scale quantizer -(28 Sep 2026, Octatrick 2.9: a ROOT row under SCALE, and the unit moved into DRAM -- "ROOT, and the move into DRAM" below. +(12 Oct 2026, Octatrick 2.13: SCALE and ROOT act on the MIDI tracks too -- the NOTE knob, NOTE locks and the +MIDI CHROMATIC keyboard -- and CHROMATIC on a MIDI track no longer freezes the unit -- "MIDI tracks (2.13)" below. +28 Sep 2026, Octatrick 2.9: a ROOT row under SCALE, and the unit moved into DRAM -- "ROOT, and the move into DRAM" below. 24 Sep 2026: polyphonic CHROMATIC keys on a paraphonic synth track -- "Paraphonic keys" below. 27 Sep 2026: the synth tracks' tuning units and the -4..+4 CHROMATIC octave -- "Synth tracks: semitone units" below. 2 Oct 2026: the legato switch is the track's LEG setting (3 Oct 2026: on every audio track -- OFF / MONO on a sample track), GLIDE is the slide time only -- "The LEG gate" below.) @@ -27,10 +29,10 @@ key does nothing, releasing the last one releases the note. Saved as `#SYNTH_GLIDE=n` after the SCALE line. Section "GLIDE and legato" below. Since 2.9 the quantizer is a DRAM unit of octabam's platform runtime -(`quantizer.s`, 3,428 B) plus three small ROM units: `core.s` (192 B, +(`quantizer.s`, 3,428 B at 2.9, 3,952 B at 2.13) plus three small ROM units: `core.s` (192 B, floating: the boot clamps, the defaults, the ROOT-rotated scale mask and -the 24 masks), `keys.s` (45 B, pinned) and `scale.s` (6 B, pinned); 19 -detours, three grown pointer tables (3 + 3 entries), one poke — all in the +the 24 masks), `keys.s` (45 B, pinned) and `scale.s` (6 B, pinned); 22 +detours since 2.13 (19 before), three grown pointer tables (3 + 3 entries), one poke — all in the main-OS section plus the loader's append; the bootstrap and every flash- programming path are untouched. (Until 2.8: one ROM unit of 3,268 B, which left the zero run no room for other modules' caves -- see "ROOT, and the @@ -46,6 +48,150 @@ measured under `ot_emu` through the virtual panel only (24 Sep 2026). The 13 Sep measurements below are the SCALE half's; logs and screens are in the author's workspace, not in this repository. +## MIDI tracks (2.13) + +With a scale on, SCALE and ROOT act on the MIDI tracks the way they act on +the sample tracks: **the pitches you can pick change, and the keyboard +follows the root**. What you play or program from the panel lands on the +scale; what the sequencer and the arpeggiator then make of it is stock. + +**What is snapped.** + +- **The NOTE knob** (the NOTE page's first knob, on the Part's value): one + click steps to the next note of the scale in the turn direction instead + of one semitone -- from C3 up in MINOR, ROOT A: D3, E3, F3, G3, A3 ...; + a note set while the scale was off (or with another scale) goes to the + nearest scale note in the direction you turn; at 0 or 127 it stays. + The NOTE page shows the note's name as before. +- **NOTE locks**: the same, with a [TRIG] key held in GRID RECORDING (from + the step's lock, or from the Part's NOTE when the step has none), and the + lock a turn of the NOTE knob writes during LIVE RECORDING. +- **The MIDI CHROMATIC keyboard** (TRACKS / CHROMATIC on a MIDI track): + key 1 sounds the root, every key a note of the scale -- the key's note + plus ROOT, then the nearest scale note (the lower one on a tie), the + rule of the sample and synth keyboards. The number beside the keyboard + reads the root's name with stock's octave: "A 3" where stock reads "3" + (key 1 = A3). FUNC + LEFT / RIGHT moves the octave as on stock. The + note-on, the arpeggiator (keys feed it the snapped notes), the note-off + at the key's release and LIVE RECORDING all carry the snapped note. + +A note's pitch class is its MIDI number mod 12 (C3 = 48, as the Octatrack +names notes), tested against the same root-rotated mask the audio tracks +use (core.s `qz_scale_mask`); the scales and roots are the 24 + 12 in the +tables above. Nothing is stored: SCALE and ROOT are the existing +battery-RAM bytes, no project or Part format changes, and **SCALE OFF is +stock** (ROOT is ignored; every new detour replays what it displaced). + +**What is not snapped, on purpose.** + +- **NOT2, NOT3, NOT4** stay semitone offsets from NOTE (the chord a trig + plays is NOTE + its offsets, as on stock), **TRAN** transposes by + semitones, and **the arpeggiator's own notes** (RNGE, the octave + steps) are stock. So a chord or a transposition can leave the scale. + To force every note a MIDI track sends into a key, use the track's own + **ARP SETUP > KEY** (stock): it acts on everything the track plays. The + reason is the drum machine on a MIDI track: its note numbers are + sounds, not pitches, and a sequencer-wide snap would remap them. +- **MIDI IN** (thru to a MIDI track's channel, the auto channel's direct + play and the notes it feeds the arpeggiator), VEL, LEN, CCs, program + changes, scene locks and the LFOs on NOTE: stock. +- **A NOTE set before the scale was on** plays as stored until you turn it. + +**Safety.** A key's note-off is the note its press sent (stock stores it at +the press), so changing SCALE or ROOT while keys are held cannot leave a +note hanging (measured: a key held across a ROOT change, two keys across +SCALE OFF). Two held keys that snap to the same note send one note-on and one +note-off between them (stock does not repeat a note that is already on). + +**THE FREEZE FIXED (Modwerk #264).** Since the ROOT round (public tags +2.9 .. 2.12), entering CHROMATIC on a MIDI track froze the unit: the +keyboard drawer's octave number was hooked at 0x400449b8, the audio +branch's load, but stock's MIDI branch jumps to 0x400449be -- the hook's +pad -- so the octave was never pushed and the drawer's call ran one +argument short (measured on the 2.12 image under the emulator: the +screen stops, keys and PLAY send nothing, the CPU stops at an illegal +instruction). Modwerk found it as #264 and its bundled quantizer (FM Synth +0.1.2 and later) moves the hook to the common push at 0x400449be; 2.13 +takes that form (credit: the Modwerk contributors, +[repeat98/modwerk](https://github.com/repeat98/modwerk)). The Modwerk +images were never affected. + +**Hooks (stock 1.40C addresses).** + +| site | stock bytes (displaced) | kind | stub | what | +|---|---|---|---|---| +| `0x40055382` knob handler, the MIDI tracks' clamp to the max, just before the store `0x4005538a` | `2400 b280 6c02 2401` -- `move.l %d0,%d2; cmp.l %d0,%d1; bge.s 0x4005538a; move.l %d1,%d2` | jsr + nop | `qz_mknob` | the four (d2 := the value capped at the max); then NOTE (flat 0, slot named `NOTE`): d2 := the old Part byte stepped by scale degree (`qz_quantm`); d0 / d1 / a0 / a1 as stock leaves them; the store at `0x4005538a` stays stock | +| `0x40050b98` MIDI p-lock editor, the lock store | `1144 4900 73b9 100b14cc` -- `move.b %d4,(18688,%a0); mvz.b 0x100b14cc,%d1` | jsr + 2 nops | `qz_mplock` | NOTE: d4 := the old lock (or the Part's NOTE when 0xff) stepped; then both | +| `0x400436a8` MIDI CHROMATIC key, the press | `4878 0001 73b9 100b14cf` -- `pea 1; mvz.b 0x100b14cf,%d1` | jmp + 2 nops | `qz_mkey` | d3 := `qz_msnap`(min(127, note + ROOT)); both; `jmp 0x400436b2` | +| `0x400449be` CHROMATIC drawer, the common push | `2f00 4879 400b465d` -- `move.l %d0,-(%sp); pea 0x400b465d` | jmp + nop | `qz_octnum` | MIDI: stock's number, `" %d"` with a scale on; audio: as 2.12; `jmp 0x400449c6` | + +No branch in the stock code lands inside any of the three new sites (the +`bge.s` the knob site displaces targets `0x4005538a`, the instruction +after the site), and no other module of the octatrick remix hooks within +16 bytes of them. The knob's hook sits one step before the store rather +than on it because octabam's MIDI SCENES module (scene locks for the MIDI +tracks) detours the store `0x4005538a` itself; with the hook at +`0x40055382` the two share a remix: this module decides the value, MIDI +SCENES' detour stores it and runs its mix. (A MIDI SCENES scene lock on +NOTE is set with a scene key held, a path of its own, and is not snapped.) + +**Measured** (the pinned emulator, the octatrick remix with SY DRUM and +PROCESSOR LOAD on octabam 063a426; MIDI OUT captured as the bytes the +unit's MIDI port transmits; expected values from a model of the rules): + +- NOTE knob: 8 SCALE / ROOT settings (OFF, OFF with ROOT A, MAJOR C, + MINOR A, PENT.MN F#, WHOLE B, HUNG.MIN D#, MAJOR F#) x 8 start values + (on and off the scale, 0, 1, 126, 127) x +-1 / 2 / 3 clicks: 384 of 384 + as the model; NOTE locks from none and from a lock in 5 settings: 20 of + 20, the Part's NOTE untouched; SCALE OFF: every value equal to 2.12's. +- Keys 1..16 at stock's octave words 0, 3, 4, 5 and 10 (readouts -1 .. 9) + in the same 8 settings (240 keys): every note-on as the model, every note-on with its note-off; + SCALE OFF: the same MIDI events as the stock OS. The readout ("A 3", + "B 3", "A -1" ...; "3" with SCALE OFF, pixel-equal to stock). +- The sequencer (trigs with NOT2..4, NOTE locks at 0 and 127, TRAN, ARP + SETUP KEY, the arpeggiator), MIDI IN thru and the auto channel (direct + and into the arpeggiator), each under SCALE OFF, MAJOR C, MINOR A and + PENT.MN F#: the same MIDI events as 2.12 in every case. +- LIVE RECORDING: two NOTE clicks up from C#3 under MINOR A write the + lock E3 on the playing step (2.12: D#3). PROJECT > SAVE, then changes, + then RELOAD: SCALE, ROOT, the NOTE and its lock come back as saved; a + project saved by 2.12 boots and plays the same notes on 2.13. +- The audio tracks: the PTCH knob walk, PTCH locks and the CHROMATIC keys' + pitches on a sample track and on an FM SYNTH track, in 5 or 6 SCALE / + ROOT settings and both keyboard octaves: equal to 2.12 value for value; the + readout beside the sample keyboard pixel-equal; the FM synth harness + 512 / 512 with the same state as 2.12 (the synth is not changed). +- With MIDI SCENES (a scratch remix of this module, the synth, MIDI + SCENES and STORE on current octabam; the ledger accepts it, and refused + it while this module's hook sat on the store itself): the NOTE knob gate above 409 of 409 + and the LIVE RECORDING lock as above; two clicks up from C#3 under MINOR + A store E3 in the Part and MIDI SCENES' mix puts E3 in the track record + (the same remix without this module: D#3), the registers MIDI SCENES' + detour starts from equal but for that value; its scene locks on a MIDI + track (scene A and B held, a CTRL1 knob turned) write the same lock table + and send the same MIDI with and without this module. +- Cost (emulator instruction counts, not a hardware share; SCALE OFF, + MAJOR C, MINOR A, PENT.MN F#, WHOLE B, HUNG.MIN D#): a MIDI CHROMATIC + press 6 instructions with SCALE OFF (stock 2), 49..77 with a scale on; a + NOTE knob turn, counted over the clamp, the hook and the store + (`0x40055382` .. `0x40055392`), 10 with SCALE OFF (stock 5), 104..278 + with a scale on for 1..3 clicks; a NOTE lock turn 8 (stock 2), 117..302 + for 1..3 clicks. Each click walks the notes up to the next scale note, + so a scale with fewer notes costs more: on a seven-note scale up to + about 230 (knob) and 250 (lock) for 3 clicks, on a five-note scale + (PENT.MN) up to about 280 (knob) and 300 (lock). The sequencer tick is + not hooked (mean 1,138, max 2,540 on a chord trig, equal to 2.12). The + DRAM unit grew by 376 B (3,576 -> 3,952 B). The quantizer's ROM units + (core.s, keys.s, scale.s) are unchanged; the new and moved hook sites + and the relinked operands change the image. + +**An option not taken.** One snapping stage where the sequencer produces a +note (after TRAN and stock's KEY, before the note-on, 0x4009fb80) would +force chords, TRAN and the arpeggiator onto the scale too, with the note +stored for its note-off so nothing can hang. It is left out so that a MIDI +track keeps its programmed notes (and a drum machine its sounds); stock's +ARP SETUP KEY already does that job per track. It can be added if wanted. + ## The scales `qz_masks` in `quantizer.s`: bit k = semitone k above the root (the @@ -235,7 +381,8 @@ stock bytes before anything is written (`manifest.py`). | `0x40050254` CHROMATIC key handler's caller | `2039 460d16fc 2200` — `move.l 0x460d16fc,%d0; move.l %d0,%d1` | jmp + nop | `qz_keyidx` | both; a synth track: d0 = d1 = 0; `jmp 0x4005025c` | | `0x40045918` FUNC + LEFT/RIGHT, CHROMATIC | `7401 b5b9 460d16fc` — `moveq #1,%d2; eor.l %d2,0x460d16fc` | jmp + nop | `qz_octkey` | both; a synth track: `qz_oct` +-1 (clamped -4..+4), the word := 0; `jmp 0x40045920` | | `0x40044968` CHROMATIC drawer, picture choice | `7201 b2b9 460d16fc` — `moveq #1,%d1; cmp.l 0x460d16fc,%d1` | jmp + nop | `qz_octdraw` | both (the compare last: the `bne` after reads its flags), after clearing the word on a synth track; `jmp 0x40044970` | -| `0x400449b8` CHROMATIC drawer, the number | `2039 460d16fc 2f00` — `move.l 0x460d16fc,%d0; move.l %d0,-(%sp)` | jmp + nop | `qz_octnum` | both; a synth track pushes `qz_oct`; `jmp 0x400449c0` — with a scale on (2.9) it pushes its own format `" %d"` in place of stock's `pea "%d"` and continues at `0x400449c6` (the same nine arguments) | +| `0x400449be` CHROMATIC drawer, the number (2.13, Modwerk #264; `0x400449b8` until 2.12) | `2f00 4879 400b465d` — `move.l %d0,-(%sp); pea 0x400b465d` | jmp + nop | `qz_octnum` | both on every path (stock's MIDI branch jumps here); a MIDI track pushes stock's number, a synth track `qz_oct`, a sample track its position; with a scale on (2.9) its own format `" %d"` in place of stock's `"%d"`; `jmp 0x400449c6` (the same nine arguments) | +| `0x40055382`, `0x40050b98`, `0x400436a8` (2.13) | see "MIDI tracks (2.13)" | | `qz_mknob`, `qz_mplock`, `qz_mkey` | | | `0x40010212` boot sanitiser of the battery block | `4a39 100b14ae` — `tst.b 0x100b14ae` | jsr | `qz_boot` (`core.s`, ROM) | the `tst.b` last (the caller's `bge` reads its flags), after clamping SCALE 0..24, GLIDE 0..127, ROOT 0..11 | | `0x40025ac2` project defaults | `42b9 100b14d4` — `clr.l 0x100b14d4` | jsr | `qz_defaults` (`core.s`, ROM) | the `clr.l`, after SCALE := OFF, GLIDE := OFF, ROOT := C | | `0x40065bca` SEQUENCER draw loop | `4282 4fef 0020` — `clr.l %d2; lea 32(%sp),%sp` | jmp | `qz_draw` | the `lea`; d2 := scroll offset × 4; `jmp 0x40065bd0` | diff --git a/sdk/octabam/modules/synth/upstream/quantizer/manifest.py b/sdk/octabam/modules/synth/upstream/quantizer/manifest.py index 11be76aeb..67556f323 100644 --- a/sdk/octabam/modules/synth/upstream/quantizer/manifest.py +++ b/sdk/octabam/modules/synth/upstream/quantizer/manifest.py @@ -8,6 +8,26 @@ lower) before it becomes the pitch the voice, the lock and the screen see. OFF is stock: every detour replays what it displaced. +MIDI TRACKS (12 Oct 2026, Octatrick 2.13). With a scale on, a MIDI track's +NOTE knob (qz_mknob at 0x40055382, the knob handler's MIDI clamp just before +its store 0x4005538a, which stays stock: octabam's MIDI SCENES detours that +store and composes with this hook) and its +NOTE locks (qz_mplock at 0x40050b98, the MIDI p-lock editor's store; a LIVE +REC lock reads the knob's value) step to the next note of the ROOT-rotated +scale in the turn direction (pitch class = note mod 12; qz_quant's MIDI +mode), and a MIDI CHROMATIC key (qz_mkey at 0x400436a8, the press path of +0x40043664) plays note + ROOT snapped to the nearest scale note (qz_msnap, +ties down): the note-on, the arpeggiator's add, the held note its release +turns off and the live recorder all take it. The drawer's number reads +" " (qz_octnum). NOT2..4, TRAN, the arpeggiator's output, MIDI +IN, VEL / LEN and the sequencer are stock (editing only: stock's ARP SETUP +KEY forces a track's every note into a key); SCALE OFF replays every +displaced instruction. qz_octnum moved from 0x400449b8 to the drawer's +common push 0x400449be (Modwerk #264): stock's MIDI branch jumps to +0x400449be, so the 2.9 .. 2.12 detour skipped the octave push there and +the drawer's call ran one argument short -- CHROMATIC on a MIDI track froze +the unit. + ROOT (28 Sep 2026, Octatrick 2.9). One per project, a fifth SEQUENCER row right under SCALE: C C# D D# E F F# G G# A A# B (C = 2.8's behaviour: the pitches, the knob and the locks byte for byte; the keyboard's number reads @@ -173,7 +193,7 @@ by FUNC + LEFT / RIGHT (qz_octkey at 0x40045918) while stock's 0/1 octave word is held at 0 on such a track (qz_keyidx at the handler's caller 0x40050254, qz_octdraw in the drawer 0x40044968) and the number beside the -keyboard prints qz_oct (qz_octnum, 0x400449b8, stock's "%d"). MIDI IN on +keyboard prints qz_oct (qz_octnum, 0x400449be since 2.13, stock's "%d"). MIDI IN on a synth track is modules/synth's (30 Sep 2026: its po_mon at the block's START 0x4000e746 replaced this module's qz_midi at 0x4000e74c). Sample tracks: the displaced instructions, byte for byte. @@ -228,7 +248,8 @@ def _qz_include(modules): "the scale is built on (C..B; key 1 of the CHROMATIC keyboard sounds it); > GLIDE: the " "synth's glide time (OFF, 1..127; the legato switch is the synth track's LEG setting); " "polyphonic chromatic keys on a synth track whose VOIC is 2..4; on a synth track " - "PTCH is semitones (-64..+63) and the CHROMATIC octave runs -4..+4.", + "PTCH is semitones (-64..+63) and the CHROMATIC octave runs -4..+4; on a MIDI track " + "(2.13) the NOTE knob, NOTE locks and the CHROMATIC keys follow the scale and root.", linked=( # ROM link order: qzs names core.s's qz_scale_mask Linked("qzk", os.path.join(_HERE, "keys.s"), cpu="5475", cave_addr=KEYS_AT), Linked("qzc", os.path.join(_HERE, "core.s"), cpu="5475"), @@ -243,6 +264,16 @@ def _qz_include(modules): Detour(0x40050e60, stock_guard(0x40050e60, 10, "ccf689e403ebd88a651c829b4ae191d609c85baa2a99409bd0a2870d20aaa73e"), "qz", "qz_plock", "p-lock editor (TRIG held): the lock store -- PTCH steps by scale degree", kind="jsr", pad_to=10), + Detour(0x40055382, stock_guard(0x40055382, 8, "03bcaf390b2fd396821abc005afa9853b59e2fcaf10ddc223c001b3a9d276e08"), "qz", "qz_mknob", + "knob handler, the MIDI tracks' branch: the clamp to the max just before the store 0x4005538a -- a MIDI " + "track's NOTE steps by scale degree from ROOT (2.13); the store stays stock, so MIDI SCENES' detour there composes", + kind="jsr", pad_to=8), + Detour(0x40050b98, stock_guard(0x40050b98, 10, "9cb4af08e685b7a46a08340cfec555868a0568533c1fb6fc2ec37159dfd980d9"), "qz", "qz_mplock", + "MIDI p-lock editor (TRIG held): the lock store -- a NOTE lock steps by scale degree from ROOT (2.13)", + kind="jsr", pad_to=10), + Detour(0x400436a8, stock_guard(0x400436a8, 10, "2ab0a5529e7748a00dea86e66bc4fbb6105ec7f36e73e481bb840f6570da98cc"), "qz", "qz_mkey", + "MIDI track CHROMATIC key press: the note + ROOT snapped to the nearest scale note (key 1 sounds the root); the note-on, the arp, the held note (so the note-off) and the recorder take it (2.13)", + kind="jmp", pad_to=10), Detour(0x4004fc58, stock_guard(0x4004fc58, 10, "ddfca03eed59b4f4fffffc9061b138ce3e2a33c30712b5e377d94d054fa7b0f0"), "qz", "qz_chrom", "CHROMATIC trig key -> pitch: transpose the key index by ROOT (a sample track: within -12..+12 st, the other position an octave below for ROOT G..B) and snap it to the scale", kind="jsr", pad_to=10), @@ -274,7 +305,9 @@ def _qz_include(modules): "CHROMATIC drawer: a synth track draws the keyboard at stock octave 0 (the word is cleared)", kind="jmp", pad_to=8), Detour(0x400449be, stock_guard(0x400449be, 8, "494435e3f72313a14453f642bdba5b064022c657c63c015752fe23092eabd19f"), "qz", "qz_octnum", - "CHROMATIC drawer common argument push preserves MIDI stack; audio octave is qz_oct on a synth track, the position 0 / 1 / -1 on a sample track, with ROOT's name before it when a scale is on", + "CHROMATIC drawer, the common push of the octave number both branches reach (2.13, Modwerk #264: the MIDI branch " + "jumps here, so the push is never skipped): a MIDI track keeps stock's number, a synth track prints qz_oct, a sample " + "track the position 0 / 1 / -1, with ROOT's name before it when a scale is on", kind="jmp", pad_to=8), Detour(0x40065bca, stock_guard(0x40065bca, 6, "484a241b3cea4a13c5b18550e3ab44eaffc021673bd5677a424bb05cc6486a37"), "qz", "qz_draw", "SEQUENCER window draw loop: index the row tables from the scroll offset", diff --git a/sdk/octabam/modules/synth/upstream/quantizer/quantizer.s b/sdk/octabam/modules/synth/upstream/quantizer/quantizer.s index 3e83a41f0..6c2d4033f 100644 --- a/sdk/octabam/modules/synth/upstream/quantizer/quantizer.s +++ b/sdk/octabam/modules/synth/upstream/quantizer/quantizer.s @@ -1,10 +1,14 @@ -| Modwerk pending 0.1.2 (#264): the octave drawer detour is at the COMMON -| push (0x400449be), reached by audio and MIDI. Stock MIDI branches directly -| there and already carries d0. Keep its value and push the value and format -| once each. Detouring only the audio load at 0x400449b8 removes the push -| reached by MIDI and underflows the drawer's nine-argument stack. | SCALE QUANTIZER -- the DRAM unit (13 Sep 2026; a DRAM unit since 28 Sep 2026). GNU as. | +| OCTATRICK 2.13 (12 Oct 2026): the MIDI tracks. With a scale on, a MIDI +| track's NOTE knob and NOTE locks step by scale degree from ROOT +| (qz_mknob, qz_mplock, qz_quant's MIDI mode) and its CHROMATIC keys play +| note + ROOT snapped to the scale (qz_mkey, qz_msnap); the keyboard's +| number reads "A 3". Editing only: the sequencer, NOT2..4, TRAN, the +| arpeggiator's notes and MIDI IN are stock. qz_octnum sits at the +| drawer's common push 0x400449be (Modwerk #264; until 2.12 CHROMATIC on a +| MIDI track froze the unit). +| | OCTATRICK 2.9 (28 Sep 2026): ROOT, and the move into DRAM. | * ROOT: a fifth SEQUENCER row, right under SCALE (GLIDE is the sixth | now): C C# D D# E F F# G G# A A# B, the root the scale is @@ -151,6 +155,7 @@ .global qz_leg0, qz_leg3, qz_leg4, qz_leg5 .global qz_get_root, qz_set_root, qz_lbl_root .global qz_keyidx, qz_octkey, qz_octdraw, qz_octnum + .global qz_mknob, qz_mplock, qz_mkey .set OCT_WORD, 0x460d16fc | stock's CHROMATIC octave word: 0 or 1 (FUNC + LEFT/RIGHT eor it) .set KEYS_AT, 0x400d2cb0 | keys.s, pinned (manifest KEYS_AT): the paraphonic key mailbox .set qz_pkey, KEYS_AT | the live key (index + 1) per track, 8 bytes @@ -171,6 +176,9 @@ .set MIDI_NOTE, 0x4003f3a8 | (track, note, velocity): the key's MIDI note out .set SPRINTF, 0x40013a08 .set FMT_D, 0x400b465d | "%d" + .set MIDI_MODE, 0x80000012 | (2.13) != 0: the UI shows the MIDI tracks + .set PARTS, 0x46c82456 | the project's Part blob (6322 B a Part; 0x100b14cf = the Part) + .set MPART_NOTE, 0x8f162 | a MIDI track's parameters in its Part: + 32 * track, NOTE at +0 | ---- the settings ---------------------------------------------------------------- | Two bytes of the unit's BATTERY-BACKED RAM (CS1, 0x10000000..: the same @@ -449,14 +457,119 @@ qz_p_done: mvz.b 0x100b14cc,%d1 | displaced rts +| ---- (2.13) THE MIDI TRACKS' NOTE: the knob, jsr planted at 0x40055382 ----------- +| The MIDI branch of the knob handler (0x40055304, reached with the MIDI tracks +| on screen) resolves the Part byte into a5 (Part + 0x8f162 + 32 * track + +| flat), calls the slot's handler (NOTE: the generic 0x4003240c, a semitone a +| detent), clamps it to the descriptor's min into d0, and at 0x40055382 -- the +| hook, 8 bytes: move.l d0,d2; cmp.l d0,d1; bge.s; move.l d1,d2 -- to the max +| d1 into d2, which 0x4005538a stores into the Part; the SRAM mirror, the live +| copy 0x46c76dc0 + 68 * track, a LIVE REC lock (0x40042158) and the CC echo +| then read d2. The hook sits one instruction group before the store so that +| the store itself stays stock: octabam's MIDI SCENES detours 0x4005538a (its +| scn_pwrite), and the two compose -- this unit decides d2, the store +| (stock's or MIDI SCENES') takes it. Registers here: d6 = the flat slot +| (signed byte; 0 = NOTE, slot A of the NOTE page), d5 = slot, a4 = the +| delta (detents, signed), a3 = the page descriptor (slot names at +0x16), +| (a5) still the old value. With a scale on, NOTE steps by scale degree from +| the old value exactly as PTCH does on an audio track (qz_quantm: a value +| between degrees goes to the next one in the turn direction, at 0 or 127 it +| stays). VEL, LEN, NOT2..4 and every other slot: stock. Only d2 changes: +| d0 / d1 / a0 / a1 leave as stock leaves them (d1 is kept on the stack +| while it names the slot), so whatever follows the store sees stock's +| registers but for d2. +qz_mknob: + move.l %d0,%d2 | displaced: d2 := the value, + cmp.l %d0,%d1 | displaced: capped at the max (d1) + jbge qz_mk_capped | displaced + move.l %d1,%d2 | displaced +qz_mk_capped: + tst.b NV_SCALE + jbeq qz_mk_done | OFF: stock + tst.l MIDI_MODE + jbeq qz_mk_done + tst.b %d6 | flat 0: NOTE + jbne qz_mk_done + tst.l %d5 | slot A + jbne qz_mk_done + move.l %d1,-(%sp) + move.l 0x16(%a3),%d1 | the slot's name: "NOTE" + cmpi.l #0x4e4f5445,%d1 + jbne qz_mk_pop + move.l %a4,%d1 | delta: detents, signed + jbeq qz_mk_pop + mvz.b (%a5),%d2 | from the value the knob was turned from + jbsr qz_quantm +qz_mk_pop: + move.l (%sp)+,%d1 +qz_mk_done: + rts + +| ---- (2.13) the MIDI NOTE with a [TRIG] key held: jsr planted at 0x40050b98 ------ +| The MIDI p-lock editor (the loop at 0x400509f6, GRID REC + a held trig) takes +| the step's lock -- or the Part's value + delta when there is none (0xff) -- +| clamps it into d4 and stores it at 0x40050b98 into 18688(a0) (the pattern's +| lock byte: a0 = the step's record + flat) and then into the SRAM mirror at +| (a0', a1). Registers: a1 = the flat slot (0 = NOTE), d5 = slot, fp = delta, +| a3 = the descriptor; the old lock is still in 18688(a0). qz_plock's twin: +| with a scale on, NOTE steps by scale degree from the lock, or from the +| Part's NOTE when the step has none. Only d0 / d1 are free (a0 / a1 / d2 / d3 +| are read after the hook). +qz_mplock: + mvz.b NV_SCALE,%d0 + jbeq qz_mp_store | OFF: stock + tst.l MIDI_MODE + jbeq qz_mp_store + move.l %a1,%d0 | flat 0: NOTE + jbne qz_mp_store + tst.l %d5 + jbne qz_mp_store + move.l 0x16(%a3),%d0 + cmpi.l #0x4e4f5445,%d0 | "NOTE" + jbne qz_mp_store + move.l %fp,%d1 | delta + jbeq qz_mp_store + move.l %d2,-(%sp) + mvz.b (18688,%a0),%d2 | the step's lock before the store + cmpi.l #0xff,%d2 + jbne qz_mp_go | a lock: step from it + move.l %a0,-(%sp) | none: from the Part's NOTE + move.l #6322,%d0 | a Part's size + mvz.b 0x100b14cf,%d2 | the Part + muls.l %d0,%d2 + movea.l PARTS,%a0 + adda.l %d2,%a0 | the Part + mvz.b UI_TRACK,%d2 + lsl.l #5,%d2 + adda.l %d2,%a0 | + 32 * track + adda.l #MPART_NOTE,%a0 + mvz.b (%a0),%d2 | its NOTE + movea.l (%sp)+,%a0 +qz_mp_go: + jbsr qz_quantm + move.l %d2,%d4 | the value the lock and the mirror take + move.l (%sp)+,%d2 +qz_mp_store: + move.b %d4,(18688,%a0) | displaced: the lock byte + mvz.b UI_TRACK,%d1 | displaced + rts + | ---- qz_quant: d2 := d2 stepped |d1| degrees in the direction of d1 ------------- | SCALE is on (the mask comes from SCALE_AT), a3 = the descriptor (min at +0x6a, count at | +0x9a for slot A). Each step goes to the next raw value on a semitone of the | scale (raw = 64 + 5 * n; qz_pcraw maps raw - min to a pitch class); a value | between degrees snaps to the nearest one in the turn direction; at the ends | the value stays, which is the stock clamp. Preserves everything but d2. +| qz_quantm (2.13) is the same walk over a MIDI track's NOTE: raw = the MIDI +| note, every value a semitone, pitch class = note mod 12 (48 = C3, C = 0). qz_quant: move.l %d0,-(%sp) + moveq #0,%d0 | an audio track (qz_ui_synth decides below) + jbra qz_q_go +qz_quantm: + move.l %d0,-(%sp) + moveq #-1,%d0 | (2.13) a MIDI track's NOTE +qz_q_go: move.l %d1,-(%sp) move.l %d3,-(%sp) move.l %d4,-(%sp) @@ -465,14 +578,18 @@ qz_quant: move.l %d7,-(%sp) move.l %a0,-(%sp) move.l %a1,-(%sp) + move.l %d0,%d6 | the mode (d6 is saved; the walk sets it before use) jsr SCALE_AT | d3 = the scale's pitch-class mask, rotated by ROOT (core.s) move.l %d0,%d3 - move.l 0x6a(%a3),%d4 | d4 = the parameter's minimum (4; 0 on the synth page) + move.l 0x6a(%a3),%d4 | d4 = the parameter's minimum (4; 0 on the synth page and MIDI's NOTE) move.l 0x9a(%a3),%d5 add.l %d4,%d5 - subq.l #1,%d5 | d5 = its maximum (124; 127 on the synth page) + subq.l #1,%d5 | d5 = its maximum (124; 127 on the synth page and MIDI's NOTE) lea qz_pcraw(%pc),%a0 + move.l %d6,%d0 + jbne qz_q_mode | MIDI: -1 jbsr qz_ui_synth | a synth track: raw = 64 + semitones (qz_pcof) +qz_q_mode: lea qz_synthq(%pc),%a1 move.b %d0,(%a1) move.l %d1,%d0 | d0 = |delta| degrees to step @@ -532,16 +649,22 @@ qz_k_dn1: | qz_pcof: d7 := the pitch class of raw d6 (a0 = qz_pcraw, d4 = the minimum): a | sample track looks raw - min up in qz_pcraw (0xff between semitones); a synth -| track (qz_synthq, set by qz_quant) computes (raw + 56) mod 12 = (raw - 64) -| mod 12, every raw a semitone. Preserves everything but d7. +| track (qz_synthq > 0, set by qz_quant) computes (raw + 56) mod 12 = (raw - 64) +| mod 12, every raw a semitone; a MIDI track's NOTE (qz_synthq < 0, qz_quantm) +| is the note itself, raw mod 12. Preserves everything but d7. qz_pcof: move.l %d6,%d7 sub.l %d4,%d7 tst.b qz_synthq(%pc) jbeq qz_pc_tab + jbpl qz_pc_syn + move.l %d6,%d7 | (2.13) MIDI: the note's own pitch class + jbra qz_pc_mod +qz_pc_syn: + addi.l #56,%d7 +qz_pc_mod: move.l %d0,-(%sp) move.l %d6,-(%sp) - addi.l #56,%d7 move.l %d7,%d6 moveq #12,%d0 divu.l %d0,%d6 | q @@ -699,7 +822,7 @@ qz_cs_lo_ok: | Stock keeps ONE octave word for the keyboard, OCT_WORD, 0 or 1: FUNC + LEFT | or RIGHT eor it (0x40045918), the key handler's caller adds 12 * word to the | key (0x40050254) and the drawer picks the 16- or 13-key picture by it -| (0x40044968), prints it with "%d" (0x400449b8) and places the held-key marks +| (0x40044968), prints it with "%d" (0x400449be) and places the held-key marks | with it (0x40044abe). On a synth track the octave is qz_oct instead, -4..+4: | the stock word is held at 0 there (the key index stays the physical key, | the marks land on the keys, the 16-key picture shows), qz_chrom adds the 12 @@ -766,22 +889,35 @@ qz_od_cmp: cmp.l OCT_WORD,%d1 | displaced jmp 0x40044970 -| 0x400449b8, the number beside the keyboard: `movel OCT_WORD,%d0; movel -| %d0,%sp@-` (8 bytes) -> jmp qz_octnum; the value is printed by stock's own -| "%d" (0x400b465d) at 0x400449c0 -- the formatter 0x40013904 takes (ctx, -| font, x 0x44, y 9, 1, 0, buf 0x400b527d, fmt, value) and 0x400449ec pops -| the nine -- so a synth track's -4..+4 prints as is. With a scale on (28 Sep -| 2026) the format is this unit's qz_ofmt, ROOT's name + " %d" ("A %d"), pushed -| in place of stock's, the continuation at 0x400449c6: same nine arguments. +| 0x400449be, the number beside the keyboard: `movel %d0,%sp@-; pea +| 0x400b465d` (8 bytes, the octave and stock's "%d") -> jmp qz_octnum. The +| drawer reaches 0x400449be from BOTH branches: the audio one loads OCT_WORD +| into d0 at 0x400449b8 and falls through, the MIDI one (0x400449a6: `tstl +| 0x80000012`) loads stock's MIDI octave word 0x400beba2 - 1 into d0 and +| branches straight here. The value is printed by the formatter 0x40013904 +| (ctx, font, x 0x44, y 9, 1, 0, buf 0x400b527d, fmt, value), and 0x400449ec +| pops the nine -- so a synth track's -4..+4 prints as is. With a scale on +| (28 Sep 2026) the format is this unit's qz_ofmt, ROOT's name + " %d" ("A +| %d"), pushed in place of stock's; the continuation is 0x400449c6 on every +| path: same nine arguments. +| 2.13 (Modwerk #264): until 2.12 this detour sat at 0x400449b8, the audio +| branch's load, and 0x400449be was its pad. Stock's MIDI branch jumps to +| 0x400449be, so on a MIDI track the octave push was skipped, the drawer's +| call ran one argument short and the unit froze when CHROMATIC was entered +| (every public tag since the ROOT round, 2.9 .. 2.12). Modwerk found it and +| its bundled quantizer moved the detour to the common push; this is that +| form: the push is ours on both branches, a MIDI track's d0 is stock's. +| A MIDI track (2.13): stock's number, with ROOT's name before it when a +| scale is on ("A 3": qz_mkey makes key 1 the root at that octave). qz_octnum: - tst.l 0x80000012 | MIDI: stock already loaded its octave into d0 - jbne qz_on_stockfmt | never resolve the corresponding audio track or its scale + tst.l MIDI_MODE | MIDI: stock already loaded its octave into d0 + jbne qz_on_out | (an audio track's octave is not read) jbsr qz_ui_synth jbeq qz_on_stock mvs.b qz_oct,%d0 | the synth track's octave (keys.s, pinned) jbra qz_on_out qz_on_stock: - move.l OCT_WORD,%d0 | displaced: a sample track's position, 0 or 1 + move.l OCT_WORD,%d0 | as 0x400449b8: a sample track's position, 0 or 1 jbeq qz_on_out | the default position: 0 lea qz_scale,%a0 tst.b (%a0) @@ -809,14 +945,97 @@ qz_on_copied: move.b #37,(%a0)+ | '%' move.b #100,(%a0)+ | 'd' clr.b (%a0) - pea qz_ofmt(%pc) | the format, in place of stock's pea "%d" at 0x400449c0 + pea qz_ofmt(%pc) | the format, in place of stock's "%d" jmp 0x400449c6 -qz_on_stockfmt: - move.l %d0,-(%sp) | common MIDI/audio push: never bypassed by stock's MIDI branch qz_on_fmt: - pea 0x400b465d | displaced stock "%d" format + pea FMT_D | displaced: stock's "%d" jmp 0x400449c6 +| ---- (2.13) the CHROMATIC keys of a MIDI track: jmp planted at 0x400436a8 --------- +| 0x40043664(track, note, press) plays a MIDI track's CHROMATIC key: its caller +| (0x40050226) made the note key + 12 * stock's octave word 0x400beba2 (4 at +| boot: key 1 = 48 = C3). On a press stock has checked note <= 127 and taken +| the key's held slot d2 = note & 15; at 0x400436a8 it pushes the event flag +| (`pea 1`) and then calls 0x4009e9a8(track, note, the Part's VEL, 1) -- the +| note-on, or the arpeggiator's add with ARP MODE on -- and stores held[d2] = +| note + 1 (0x460d170c), which the release turns back into the note-off; the +| live recorder records the note it was given (event 0x46). With a scale on +| the note becomes the keyboard's own rule (qz_chrom's, the synth track's): +| note + ROOT (at most 127), then the nearest scale note, ties down -- key 1 +| sounds the root, every key a note of the scale. The note-on, the arp, held[] +| (so the note-off) and the recorder all carry it; the release needs no hook. +| Registers: d3 = the note, d4 = track, d2 = the slot; d0 / d1 / a0 / a1 are +| free (d5 is not: stock's `movew #6322,%d5` keeps its upper half). +qz_mkey: + mvz.b NV_SCALE,%d0 + jbeq qz_mky_replay | OFF: stock + mvz.b NV_ROOT,%d0 + add.l %d3,%d0 | + ROOT: key 1 sounds the root + moveq #127,%d1 + cmp.l %d1,%d0 + jble qz_mky_snap + move.l %d1,%d0 | the MIDI ceiling +qz_mky_snap: + jbsr qz_msnap + move.l %d0,%d3 +qz_mky_replay: + pea 1 | displaced: the event flag + mvz.b 0x100b14cf,%d1 | displaced: the Part + jmp 0x400436b2 + +| qz_msnap: d0 := the MIDI note nearest to note d0 (0..127) whose pitch class +| (note mod 12, C = 0) is in the scale's ROOT-rotated mask (SCALE_AT), the +| lower candidate first at each distance (ties go down: qz_chrom's and the +| synth's po_snap's rule), within 0..127. SCALE is on. Clobbers d1, a0, a1. +qz_msnap: + lea -12(%sp),%sp + movem.l %d2-%d4,(%sp) + move.l %d0,%d2 | n + jsr SCALE_AT | d0 = the mask (clobbers a0, d0) + move.l %d0,%d3 + move.l %d2,%d1 + moveq #12,%d4 + divu.l %d4,%d1 + mulu.l %d4,%d1 + move.l %d2,%d0 + sub.l %d1,%d0 + move.l %d0,%a1 | a1 = n's pitch class + moveq #0,%d4 | distance 0, 1, 2 ... +qz_ms_loop: + move.l %d2,%d1 + sub.l %d4,%d1 | the lower candidate first + jbmi qz_ms_hi | (below note 0: none) + move.l %a1,%d0 + sub.l %d4,%d0 + jbpl qz_ms_lo + addi.l #12,%d0 +qz_ms_lo: + btst %d0,%d3 + jbne qz_ms_found +qz_ms_hi: + move.l %d2,%d1 + add.l %d4,%d1 | then the upper + cmpi.l #127,%d1 + jbgt qz_ms_next | (above note 127: none) + move.l %a1,%d0 + add.l %d4,%d0 + cmpi.l #12,%d0 + jblt qz_ms_up + subi.l #12,%d0 +qz_ms_up: + btst %d0,%d3 + jbne qz_ms_found +qz_ms_next: + addq.l #1,%d4 + cmpi.l #11,%d4 + jble qz_ms_loop + move.l %d2,%d1 | no degree at all: cannot happen (every mask has the root) +qz_ms_found: + move.l %d1,%d0 + movem.l (%sp),%d2-%d4 + lea 12(%sp),%sp + rts + | ---- GLIDE legato: two jmp detours in the same handler -------------------------- | 0x4004fb94 keeps ONE held key per track (HELD + track = key + 1). Stock, a | press while a key is held first ends that key -- 0x4004fbfe..0x4004fc40: the @@ -1513,7 +1732,7 @@ qz_synthname: .ascii "SYNTH" qz_gbuf: .fill 8, 1, 0 | the GLIDE row's number (RAM) qz_ofmt: .fill 8, 1, 0 | the keyboard's octave format with ROOT's name: "A# %d" (RAM) qz_legato: .byte 0 | the press in flight is a legato one (qz_leg2 -> qz_leg3) -qz_synthq: .byte 0 | qz_quant runs for a synth track (qz_pcof reads it) +qz_synthq: .byte 0 | qz_quant runs for a synth track (> 0) or a MIDI note (< 0, 2.13; qz_pcof reads it) .balign 4 qz_chain: .long 0 | ... and continues a held note: that note's start (ticks), 0 = a fresh note qz_step: .byte 0 | the step the key in flight was recorded on (qz_leg4 -> qz_leg5) diff --git a/sdk/tests/test_synth_release.py b/sdk/tests/test_synth_release.py index 1915f6acf..5ac69d5ac 100644 --- a/sdk/tests/test_synth_release.py +++ b/sdk/tests/test_synth_release.py @@ -7,8 +7,9 @@ APP = Path(__file__).resolve().parents[2] ORIGINAL = json.loads((APP/'sdk/imports/synth-949f3be.json').read_text()) -PREVIOUS = json.loads((APP/'sdk/imports/synth-fixes-273-264.json').read_text()) -RECORD = json.loads((APP/'sdk/imports/synth-ca13ed8.json').read_text()) +PUBLISHED = json.loads((APP/'sdk/imports/synth-fixes-273-264.json').read_text()) +PREVIOUS = json.loads((APP/'sdk/imports/synth-ca13ed8.json').read_text()) +RECORD = json.loads((APP/'sdk/imports/synth-48b89e7.json').read_text()) FOLDER = APP/RECORD['folder'] sha = lambda path: hashlib.sha256(path.read_bytes()).hexdigest() @@ -16,8 +17,10 @@ class SynthRelease(unittest.TestCase): def test_source_identities_and_complete_notices(self): self.assertEqual(RECORD['originalImport'], 'synth-949f3be.json') - self.assertEqual(RECORD['previousUpdate'], 'synth-fixes-273-264.json') - self.assertEqual(PREVIOUS['version'], '0.1.2-experimental') + self.assertEqual(RECORD['previousUpdate'], 'synth-ca13ed8.json') + self.assertEqual(PREVIOUS['previousUpdate'], 'synth-fixes-273-264.json') + self.assertEqual(PREVIOUS['version'], '0.1.4-experimental') + self.assertEqual(PUBLISHED['version'], '0.1.2-experimental') self.assertRegex(RECORD['authorPin']['revision'], r'^[a-f0-9]{40}$') files = {item['path']: item for item in RECORD['files']} on_disk = sorted(str(p.relative_to(FOLDER)) for p in FOLDER.rglob('*') if p.is_file() and '__pycache__' not in p.parts and p.name != '.DS_Store') @@ -28,7 +31,8 @@ def test_source_identities_and_complete_notices(self): self.assertRegex(item['sourceSha256'], r'^[a-f0-9]{64}$') if not item.get('changes'): self.assertEqual(item['sourceSha256'], item['sha256'], path) - for path in ['upstream/synth/poly.s', 'upstream/synth/page.s', 'upstream/synth/machine.s', 'upstream/synth/engine_abi.inc', 'upstream/synth/README.md', 'upstream/LICENSE']: + for path in ['upstream/synth/poly.s', 'upstream/synth/page.s', 'upstream/synth/machine.s', 'upstream/synth/engine_abi.inc', 'upstream/synth/README.md', 'upstream/LICENSE', + 'upstream/quantizer/quantizer.s', 'upstream/quantizer/core.s', 'upstream/quantizer/keys.s', 'upstream/quantizer/scale.s', 'upstream/quantizer/README.md']: self.assertEqual(files[path]['revision'], RECORD['authorPin']['revision']) self.assertEqual(files[path]['sourceSha256'], files[path]['sha256'], path + ' must be the author file byte for byte') for path in RECORD['removed']: @@ -47,6 +51,8 @@ def test_stock_expectations_are_lazy_and_no_precompiled_page_is_carried(self): if name.endswith('_STOCK'): self.assertIsInstance(value, ast.Call, name) self.assertEqual(value.func.id, 'stock_guard', name) + quantizer = (FOLDER/'upstream/quantizer/manifest.py').read_text() + self.assertNotRegex(quantizer, r'(Detour\(0x[0-9a-fA-F]+,\s*H\(|expect=H\()') top = (FOLDER/'manifest.py').read_text() self.assertNotIn('fromhex("', top) guards = json.loads((FOLDER/'stock-guards.json').read_text())['guards'] diff --git a/src/catalog/synth-release.test.ts b/src/catalog/synth-release.test.ts index 9b4db9043..10bbb6f51 100644 --- a/src/catalog/synth-release.test.ts +++ b/src/catalog/synth-release.test.ts @@ -16,7 +16,7 @@ import { MODULES, resolveSelection } from './modules' const folder = resolve('sdk/octabam/modules/synth') -describe('FM Synth 0.1.4 (octatrick 2.12)', () => { +describe('FM Synth 0.1.5 (octatrick 2.12 engine, 2.13 quantizer)', () => { it('publishes through measured cycles, exact memory and the attributed hardware report of this exact source and image', async () => { const document = parseModuleDocument(source) const q = document.tests.qualification! @@ -37,7 +37,7 @@ describe('FM Synth 0.1.4 (octatrick 2.12)', () => { expect(resolveSelection([document.id]).map(module => module.id)).toEqual(['synth']) expect(MODULES.some(module => module.id === document.id)).toBe(true) for (const mutate of [ - (d: typeof document) => { d.version = '0.1.5-experimental' }, + (d: typeof document) => { d.version = '0.1.6-experimental' }, (d: typeof document) => { d.tests.qualification!.sourceSha256 = 'f'.repeat(64) }, ]) { const changed = structuredClone(document); mutate(changed) diff --git a/src/community/downloaded-module-guide.ts b/src/community/downloaded-module-guide.ts index 42423e1ab..155e52eab 100644 --- a/src/community/downloaded-module-guide.ts +++ b/src/community/downloaded-module-guide.ts @@ -69,7 +69,7 @@ const QUICK_TESTS: Record = { { title: 'Shape the clang.', text: 'Add trigs and press PLAY. Adjust TUNE, then raise METAL and turn COLOR. Add FBK gradually.' }, { title: 'Finish the test.', text: 'Press STOP and let the AMP release finish. Remove example locks and return SWEEP and FOLD to 0.' }, ] }, - synth: { version: '0.1.4-experimental', steps: [ + synth: { version: '0.1.5-experimental', steps: [ { title: 'Choose FM SYNTH.', text: 'In a fresh project, select an audio track. Hold FUNC + SRC, choose FM SYNTH with UP/DOWN and press YES. No sample is needed.' }, { title: 'Add harmonics.', text: 'Press SRC, set RATO to 1 and raise INDX from 0. The sine tone gains FM harmonics; PTCH changes pitch.' }, { title: 'Play a pattern.', text: 'Place a trig and press PLAY. Use AMP ATK, HOLD and REL to shape note length. The full instructions cover voices, chords and glide.' }, diff --git a/src/community/module-changelogs.json b/src/community/module-changelogs.json index 617b38f0b..674cb2de5 100644 --- a/src/community/module-changelogs.json +++ b/src/community/module-changelogs.json @@ -374,6 +374,17 @@ } ], "synth": [ + { + "version": "0.1.5-experimental", + "date": "2026-10-12", + "changes": [ + "SCALE QUANTIZER on MIDI tracks (the author’s octatrick 2.13 quantizer, timhastie/octatrick-modules 48b89e7): with a scale on, a MIDI track’s NOTE knob and NOTE locks step through the scale’s notes from ROOT, and the MIDI CHROMATIC keys play the scale from ROOT (key 1 sounds the root; the number beside the keyboard reads the root and the octave, for example “A 3”).", + "chord offsets (NOT2–NOT4), the arpeggiator, TRAN, MIDI IN and the sequencer are unchanged on MIDI tracks: the quantizer acts where a note is edited or played from the panel (ARP SETUP KEY still forces every note a track sends into a key). SCALE OFF is unchanged.", + "the MIDI chromatic freeze is fixed in the author’s own line: since 2.13 his quantizer hooks the CHROMATIC drawer at its common argument push (0x400449be), the form of Modwerk’s 0.1.2 fix for #264, credited to the Modwerk contributors. The bundled quantizer’s sources are now the author’s, byte for byte (its manifest in Modwerk’s stock_guard form); FM Synth images carried the fix already.", + "the FM engine, the FM SYNTH chooser and the page are unchanged from 0.1.4: the same compiled objects at the same addresses in the image.", + "Emulator checks on this exact image (TESTING.md): the NOTE knob and locks against a model of the rules (409 of 409), the CHROMATIC keys (245 of 245), SCALE OFF equal to stock, the quantizer’s added cost, exact memory, two-track Part/project save and reload. The author’s MKI report of this exact image is pending; physical reboot survival is not yet verified." + ] + }, { "version": "0.1.4-experimental", "date": "2026-10-11", From 4c531f664db0dbf2f3cc0a70237911d64c060e37 Mon Sep 17 00:00:00 2001 From: timhastie Date: Mon, 12 Oct 2026 12:39:47 -0700 Subject: [PATCH 4/7] FM Synth 0.1.5: packages rebuilt on main 263aa1b (the Synth objects only); Analog BD record re-verified and rebound build-module-packages.py --include-requested on this source, imported with --development: only the Synth objects and the Synth version change against main's 0.1.2 packages (Modwerk's fmregistration object is gone, the author's poly and fmmachine objects come in, the bundled quantizer objects change; 0.1.4 has no package commit of its own on this branch). The Analog BD record's moduleSourceTreeSha256 is rebound after verify-analog-bd-native.mjs passed against the new packages (162 native OS and GNU bootloader comparisons, 50 matching refusals, 6 firmware round trips). --- .../assets/analog-bd-composition-proofs.json | 2 +- src/engine/assets/bootstrap-package.json | 2 +- src/engine/assets/coldfire-packages.json | 2 +- src/engine/assets/descriptor-recipes.json | 2 +- src/engine/assets/dsp-packages.json | 2 +- src/engine/assets/menu-recipes.json | 2 +- src/engine/assets/module-build.json | 28 +- src/engine/assets/platform-writes.json | 2 +- src/engine/assets/requested-packages.json | 1365 +++++++++-------- src/engine/assets/resident-dsp.json | 2 +- src/engine/assets/rom-packages.json | 2 +- src/engine/assets/usb-audio-packages.json | 2 +- src/engine/assets/utility-packages.json | 2 +- 13 files changed, 761 insertions(+), 654 deletions(-) diff --git a/src/engine/assets/analog-bd-composition-proofs.json b/src/engine/assets/analog-bd-composition-proofs.json index ae7ab29e7..ab0b5d5ef 100644 --- a/src/engine/assets/analog-bd-composition-proofs.json +++ b/src/engine/assets/analog-bd-composition-proofs.json @@ -8,7 +8,7 @@ "tools/build/ab_image.py": "c4573d4e0715961f7081dc1d5a0639d42ac63313ccc9e50a0737ac0685e2fc14", "tools/remix/loader.S": "32e09fd813ddc65511390faaebc3b86d963b2700f304b35cdb1ec48f90449287" }, - "moduleSourceTreeSha256": "4af7f602316cfc3055c44f9f46cb835412bebc9990018252577c997bbe1c084a", + "moduleSourceTreeSha256": "eb3f4b468f216bdb8e856d8459309c58232a990cbefcb23418cf6acc6496d5d4", "packing": null, "proofs": [ { diff --git a/src/engine/assets/bootstrap-package.json b/src/engine/assets/bootstrap-package.json index b2799c611..d48a03091 100644 --- a/src/engine/assets/bootstrap-package.json +++ b/src/engine/assets/bootstrap-package.json @@ -32,7 +32,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", diff --git a/src/engine/assets/coldfire-packages.json b/src/engine/assets/coldfire-packages.json index aca196d8d..60c590e77 100644 --- a/src/engine/assets/coldfire-packages.json +++ b/src/engine/assets/coldfire-packages.json @@ -207,7 +207,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", diff --git a/src/engine/assets/descriptor-recipes.json b/src/engine/assets/descriptor-recipes.json index 2e187dbc3..3a891e9b0 100644 --- a/src/engine/assets/descriptor-recipes.json +++ b/src/engine/assets/descriptor-recipes.json @@ -3401,7 +3401,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", diff --git a/src/engine/assets/dsp-packages.json b/src/engine/assets/dsp-packages.json index fbedd2797..0cd2ce70c 100644 --- a/src/engine/assets/dsp-packages.json +++ b/src/engine/assets/dsp-packages.json @@ -18,7 +18,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", diff --git a/src/engine/assets/menu-recipes.json b/src/engine/assets/menu-recipes.json index 229a8541e..3f845b539 100644 --- a/src/engine/assets/menu-recipes.json +++ b/src/engine/assets/menu-recipes.json @@ -1287,7 +1287,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", diff --git a/src/engine/assets/module-build.json b/src/engine/assets/module-build.json index 41c665db1..35addaee9 100644 --- a/src/engine/assets/module-build.json +++ b/src/engine/assets/module-build.json @@ -3,7 +3,7 @@ "kind": "source-packages", "sourceCommit": null, "nativeRevision": "b8deefc88b2c3e5f3c6158e364eb741df1924e1d", - "sourceTreeSha256": "4af7f602316cfc3055c44f9f46cb835412bebc9990018252577c997bbe1c084a", + "sourceTreeSha256": "eb3f4b468f216bdb8e856d8459309c58232a990cbefcb23418cf6acc6496d5d4", "compilerSha256": "1f61ddb22cbcbb6e5495da364a83f9f427a6e2afb4c31fdffe33279bd00768a4", "moduleVersions": { "spectrum": "0.1.1-experimental", @@ -20,7 +20,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", @@ -34,47 +34,47 @@ "files": { "dsp-packages.json": { "bytes": 67452, - "sha256": "d87487e3be190f3b8d0975c73dfc37daacf00ffbecc9c3d47fd2fafaef774e1d" + "sha256": "86a9bb9beca781c0d79d8bfd5048a0eb73b19937841c65407f4526f52849987c" }, "resident-dsp.json": { "bytes": 24894, - "sha256": "f2d80127c6bbc4dd9ee566cccc1fa775c01c69d312d6c9184ed2c14ab447aaf2" + "sha256": "c561eb33c19ddeac1450ee610e1f9391c866fcc2db88a0520d7ef4c88803b74e" }, "coldfire-packages.json": { "bytes": 351489, - "sha256": "bd201bf6196a24bc2676822c2fea645a1ac45a8d2c697b44e203ea22e78e3e43" + "sha256": "ca167d0622ce7e3a23eefab610c32c930a2d47785fec12486a2dc28e1aa6883b" }, "rom-packages.json": { "bytes": 25336, - "sha256": "44ad988b753e8a956339a90163d392ee81d73fa233b93671399cda68d884bb3d" + "sha256": "fcb1aa22c8be0fc3a009a3373e33e5e1c5853ec0531546e3d69b171de035de17" }, "bootstrap-package.json": { "bytes": 3628, - "sha256": "b5240d5f2aba52d5fb6b008673fc61923a0166f6ac63716d1698656d8413a824" + "sha256": "a6c29f6b27fe5a6eadb3655d7cc4bdf1a479b943afcef771391361934d9e7253" }, "menu-recipes.json": { "bytes": 33848, - "sha256": "a635b64ff5babc2762d69ba835fd90303812dfd8eb6f60f3abf48955b1def347" + "sha256": "75caf2e01cac4bf86e9cd130332feaae5e5a1ec02a84a7f1687f32112c33281e" }, "descriptor-recipes.json": { "bytes": 65131, - "sha256": "d391781576ac33b334b982908132628e803c58b960346e9f75ffd74c66a55e2d" + "sha256": "42db2127f2b633cab775be0424f6b3078aed006807bb0e60391b6cbbdb928b0f" }, "platform-writes.json": { "bytes": 16958, - "sha256": "56720770788c112599dcede88923bafeb162b16cae07f69b5716ea3718deabc6" + "sha256": "c92dd48804ac643f2ea8dc8265a9535be8e96b2c266d670876f12be38d7e751f" }, "requested-packages.json": { - "bytes": 1065080, - "sha256": "17a783646f8651aeecd5ebe459aa9fc6a066d9ea305b410ede25090b5e6e87f8" + "bytes": 1097811, + "sha256": "1a953ea6b2918f71e2295a830d76ff10ad202207cc9293f5cf071eac3e75f26d" }, "utility-packages.json": { "bytes": 12756, - "sha256": "e3a5240b4153f433e5419f922ebf1d7be94f07647e25ae6930211b5af33c1329" + "sha256": "734ab0195bf4b745c704841d955da88b8f443013a591ee0c79c1f4750ee488e3" }, "usb-audio-packages.json": { "bytes": 737398, - "sha256": "ab8f212d5d2085cc1b2edc234b17c45e5d495cef88a13f9e2be447dae6a2d82d" + "sha256": "af87c79d5a7484f5cf4e123e4ed2fb6f9f0e98d5043370a192a23cf5dcce44e2" } }, "approval": null, diff --git a/src/engine/assets/platform-writes.json b/src/engine/assets/platform-writes.json index c149816c8..47a2d2cfd 100644 --- a/src/engine/assets/platform-writes.json +++ b/src/engine/assets/platform-writes.json @@ -530,7 +530,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", diff --git a/src/engine/assets/requested-packages.json b/src/engine/assets/requested-packages.json index 069ffcb85..ad623c780 100644 --- a/src/engine/assets/requested-packages.json +++ b/src/engine/assets/requested-packages.json @@ -17,7 +17,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", @@ -239,7 +239,7 @@ { "label": "poly", "moduleId": "synth", - "version": "0.1.2-experimental", + "version": "0.1.5-experimental", "key": "SYNTH MACHINE", "author": "timhastie", "nativeAuthor": "timhastie", @@ -248,12 +248,12 @@ "caveAddress": null, "source": "modules/synth/upstream/synth/poly.s", "sources": { - "modules/synth/upstream/synth/poly.s": "ce21f5f7cddcd7268d27e25c500f1403cc59a9dff28044b1832cf2b45592d34c", - "modules/synth/manifest.py": "7854330aca001d2841de2fb384f0ca8adb7497db49dacc56da2815c1f444c0e6" + "modules/synth/upstream/synth/poly.s": "9d2f68963a735b98e909d7537ce8896eba370aa651f984dbbb5085354071e6d8", + "modules/synth/manifest.py": "f364cd97b5a2a416b3029752fe2ce0691fc977d59203419d9d5a52bbeb7913b7" }, - "bytes": 37696, - "code": "7f454c4601020100000000000000000000010004000000010000000000000000000092000000002600340000000000280008000700000000000000000000000000000000000000000000000000000000000000004fefffd048d77cfc247980001c80242f00342602ef8b47fa34c0d7c32879800062a878007210b2af00406600021241f946104d0c41f028000810000467000176610002ac53806600014a6100214a6600006e263c000000a84c02380041f9800049d8207038082608670001280483100b14f00c8300024640640001182248263c000000ff7398670000140c810000002f66000004224853836600ffea323c464db25166000004548941fa2d9e760573987b99b285660000dc53836600fff272484c02180041f98000081073b0180d0c810000007f6700001e41fa30f873f01a0041fa41ce202800084c001000ee895281600000047200274100584a6b00266600002a429342ab000442ab002042ab0018426b002642ab0014422b0051223c010000002741000860000008177c001000512079800062a442a8000472484c02180041f98000081073b0180855810c810000000253c1174100506600001861000a52610022fc41f9400d2cb0423028006000000e4a6b00266700000661000a5a610005522206e189e98927410028760160000020610009f8426b002641f9400d2cb04230280073d4e189e989274100287600174300244a2b00246700007c41f98000081072484c021800423018312239800000e0028100000001e189e3892002ed88d28041f980000110d1c14250317c7f000002317c7f0000047bec000673d4e08927410070610004cc4a2b0050660000066100058a38bc4000397c7f0000067801610000b443eb005c22d822d822d822d822d86000000a7dd46100056238866100466067000046202f004090af003c2480256f003c0004223c0400000025410008222f003c761ae7a92541000c41ea0010d0806700000a429853806600fffa23c880001c8070006000001c2f2f00402f2f00402f2f00402f2f00404eb9400040084fef00102f40002c4a84670002043886394500066100002043eb005c20d920d920d920d920d94a2b00506700004661000566600001dc223c000000a84c02180041f9800049d8d1c141e800404e75207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008eda271b028004e7561000986610003ea0c80730000006300000c700042ab001842ab00142740000c73ec0002e089e48941fa2b4273f01a00e0804c0108002740001073ec0008203c000002044c001000e0892741001c73ab00516700001e203c0100000090ab00084c410800d1ab00085381174100516000005673ec000ae0896700004224014c021000203c002ed1e04c4100000c80000fffff6f000008203c000fffff222b00080481001000006f00001ee089e8894c001000e08993ab00086000000c223c010000002741000873ec0004203c00000a444c001000e089242b0008e08ae88a4c021000e089e88992ab001474104c4218013741007e242f003461000006600000a0222b001870484c02080041f980000810d1c0082b0001002466000040202b0058670000165380274000586600000c08eb000100246000002471a8000c41fa2b8271f00a00d2800c81000080006f00004a223c000080006000004071a8000e41fa2c6071f00a006600000e082b000200246700002671d020414c001000e089e0896600000472012008908122000c81000000406e0000047200274100184e754a2b002467000130242f0034263c000000a84c023800610043d86600002441f9800049d84a30380066000016426b00264a2b005067000100610006a2600000f87faa0003670000f043ea00104a2b00506700000a426b00266000105c2f0a20132c2b00042a2b000c246b0010286b0020222b001875eb00269282760fe7a9e8812c41e7aa2a4241fa2d9c7618220c4c2b1800001cd2862401e6aad48279702800757028029484e08973c14c012800e082e082d8822844736b007ed2ab0014274100144c014800d8802404e6aad48273702800757028029481e08c79c44c042800e082e082d282dbce240d4842c3c2e589e681424122c122c1d085dc8a53876600ff8c268027460004274c0020220d760fe6a16a000004720037410026245f242f00346100073a6700000e7faa000343ea001060000f7c202f002c4cd77cfc4fef00304e7541fa3cba23c8400d2cd852a8000c73f9800065b2e18977b9800065b68283b2a80010670000402141001052905283b6a800046f00000621430004e089b2a8001867000022214100182228000c260196a8001c2141001c0c83000000106d000006214300084e752006a3430c4040006f00000a048000003c0076002200ea8041f9400aa29441f00c00781be9a92818e289a104a8984901a801080079ac001b6600002a28050c447f006c000020a1c04844e58c6400000ae28c44846000000ae28c068480000000a8000900a1c0e6a0223c0184cbb7a0010800a1c0eb884e757dd40486000040002006e58edc8071ec0006048000004000223c00000ccd4c010800e080e080dc800686000040004e752f052f062a3c00007f007e000c8600007c006f00000e048600003c0052876000ffec0c86000004006c00000e068600003c0053876000ffec0c87000000066f00000870ff6000001a6100ff0e4a87670000106b000008efa8600000064487eea02c1f2a1f4e7571b9100b14ed4e75207946c8245671b9100b14cf263c000018b24c030800d1c02602eb8b96829682d1fc0008f07871b038054e756100ffca22002006e188e9884a816700007c2e006100ffbe260020074a836700006c767f96812e3c00000d804c0730002203484173c177c32e3c000001e04c073000484377c341f9400aa31426303c00e08be48b2e3c000017c74c0730007e189e81eeab222b002890816a0000124480e0884c030000e088928060000012e0884c030000e088d28060000004220027410028e081e8812c014e754feffff848d70c002a4b2b46004841f9400d2cb820302c00222d00342b400034c1ad00744ab9460d172a6600001241fa38762602eb8b42303801423038034680c28067000034610004b271ae00240c80000000016600001871ae00256700001053800101670000081d7c000200244dee0040bdca6600ffd46100048071ae00240c80000000016600001e71ae00250c80000000806500001061001a26660000081d7c000200244dee0040bdca6600ffce7fad007c67000030202d0048b0ad00306600001671ad007d52801b40007d0c800000000865000010422d007c610008942879800062a841f946104d0c41f0280008100004670000066100041073ec0002e089e48941fa254273f01a002b41004073ec0008203c000002044c001000e0892b41001c73ec000ae0896700002620014c001000203c002ed1e04c4100000c80000fffff6f00000e203c000fffff6000000470002b40003873ec0004203c00000a444c001000e0892b41004470484c02080041f980000810d1c071a8000e43fa271c71f10a006600000e082d000200246700000471d12b40003c71a8000c43fa25fc71f10a004c2d00000054e088ee886600000470013b4000526100033e4a2e002467000176264e2c2d00489cad002cdcae002c6100fd906100fcf20c80730000006300000e422e002442ae00186000014a2d40000ce0804c2d080000402d40001073ee003e6700001e203c0100000090ae00084c410800d1ae000853813d41003e60000034202d003867000022222e00080481001000006f00001ee089e8894c001000e08993ae00086000000c223c010000002d410008222d0044202e0008e088e8884c001000e089e88992ae001470104c4018013d410026202d001c2d40001c202e00346700001253802d400034660000081d7c00020024222e001871ae00240c8000000002670000320c80000000036700001e202d0054b2806c00004077ed0052d283b2806f00006c220060000066262d0054e68b6000001c202d003c26014c003000e08be08b6600000a4a8067000004760192836a0000047200b2ad00546f000014202d00542600e68b9283b2806c00000422000c81000000406e000016720071ee003c0c80000000406e000006422e00242d4100184dee0040bdca6600fe7c202d00482b4000304cd70c004fef00084e7541fa2d4a2002e188d1c070044228002442a800184268003c41e8004053806600ffec4e7541fa2d262002e188d1c070044a28002467000008117c0003002441e8004053806600ffea4e752f0341fa2cfe2002e188d1c02248263c7fffffff70044a2800246700001e22280018b283640000062601224841e8004053806600ffe2600000042248204b700922d853806600fffa43e9ffdc137c0003002442290025202b002823400028e080e88090ab0030d0ab002c2340002c42a9003441fa348e52902350003041fa365c2028000c2340003871eb00260c8000007fff63000008203c00007fff3340003c202b00180c8000007fff63000008203c00007fff2340001842ab0018426b0026261f4e754fefffec48d7011b41fa2c342002e188d1c0262b0054e68b0c830000080064000008263c0000080070044a280024670000302228001892836a00000472000c81000000406e000016720079e8003c0c84000000406e000006422800242141001841e8004053806600ffc24cd7011b4fef00144e7541fa2bc82002e188d1c071a8002473a80064808173a800a4808173a800e480814e754dfa2ba62002e188ddc045ee01004e756100ffee222d003092ad002c4a2e002467000006d3ae002c4dee0040bdca6600ffec222d00482b41002c41f9400d2cb07fb02800423028000c8700000080650000362f2d00482f2d0070610016986700001e9097223c000005004c010800d0af00042b400048610000826000ffde588f2b5f00484e752f07610000702e1f4a87670000662f2d0048202d0034222d00744681c0816700004e7e000f006600000852876000fff6220704810000000c7139400d2cdc760c4c030800d28006810000004092ad0070203c000005004c010800d0976100063e2b40004852876100000c6000ffa62b5f00484e754a876700001a0c87000000806400001020075380222d007401c12b41007470484c02080041f98000081071b0080be488660000b86100fed041fa344c2628000c28280008e28c4a876700005271ae00240c80000000016600003671ae0025b087670002724a8066000026202d0048b0ae002c6600001a200390ae0038b0846400000e1d47002542ae0034600002484dee0040bdca6600ffb66000005041f9400d2cb84ab02c006600000a610014126700003a71ae00240c8000000001660000224a2e00256700001a202d0048b0ae002c6600000e200390ae0038b084650001f64dee0040bdca6600ffca4a87660000246100fe1471ae00240c8000000001660000081d7c000200244dee0040bdca6600ffe4700073f9400d2ca80c8100004ef9660000084eb9400d2ca82b40004c70484c02080041f9800008107db0080853860c8600000003630000047c00528671b0080b2600e48b6700001c22000281000000032003260643fa324a61000aaa76016000000643fa1f1e41fa201a20306c002b4000544a836700000a6100fbfa6000004a610001426600013c760196866100fd66204e70044a28002467000004528341e8004053806600ffee4a836f00001c61000506117c000300244dfa30e252962156003053836000ffe277190c83ffffff80670000f0610004f04a68003c66000026429042a8000442a8002042a8001842a800144268003e203c010000002140000860000008317c0010003e203c000005004c030800d0ad0048610003fc2140002ce188e988214000281147002542a800344dfa3242202e000c2140003872484c02180049f98000081073b4180d0c810000007f6700001e49fa213e73f41a0049fa3214282c00084c041000ee895281214100342879800062a870014a87670000300c8700000080650000162f0020076100122a201f6600001870026000001222075381282d00340304660000047002114000244dfa2fe852962156003053866600ff084e7570004a876700008441fa31a62228000c200190ad00782b4100780c800000019d620000660c87000000806400001620075380222d003401814a816700004c6000001241fa309c2002e7884a300801670000386100fbdc720071ae0024670000160c8000000001660000204a2e00256700001852814dee0040bdca6600ffdcb2866500000670014e7570004e752f082f0141fa23462202ef89d1c11140007c4228007d221f205f4e750c87000000806400001020075380222d007401c12b4100746100fb6e222d003092ad002c4a2e002467000006d3ae002c4dee0040bdca6600ffec222d00482b41002c70484c02080041f98000081077b0080b7db0080853860c8600000003630000047c0052862003e488670001282203028100000003260643fa2fba6100081a4fefffec42af00106100fafe71ae00240c80000000016600002e222f00104a816700001c20771cfc2028002cb0ae002c6f00000c2f881c0053816000ffe22f8e1c0052af00104dee0040bdca6600ffbe760043fa2f60b6866400009a7119223c000005004c010800d0ad0048610001bc282f0010b6846400001620773c002140002c1147002542a80034600000582f0061000248201f42a8000442a8002042a8001842a800144268003e223c01000000214100082140002ce188e988214000281147002542a800344dfa2fbe202e000c21400038117c000100244dfa2dd452962156003052830c83000000046500ff686000001c282f0010b6846400001220773c00117c0002002452836000ffe84fef00144e756100f9f293c9780071ae00240c800000000166000010202e0030b084630000062800224e4dee0040bdca6600ffdc20096700fabc202d0048610000dc2340002c1347002542a900344e7561000e0e670000a84feffff048d7011a207946c8245671b9100b14cf223c000018b24c010800d1c02202e7892001d281d280d1fc0008ee9a77b0180279b0180591fc0008ee9a2202eb8992829282d1fc0008f07871b0180555830c83000000026300000e4a806700003a7001600000340c800000000264000008700260000024e48c6600001c41fa20ca2202ef89203a2ea690b018780c800000019d6300ffdc70034cd7011a4fef00104a804e754feffff848d701086100f3324cd701084fef00084a806700000470014e75282d004c670000984feffff82e822f4600042c00048600004000068600025a80243c000005004c4260060686000004382206740c4c4210012401d482d481e58a9c82240672002c029c816a00000806860000000c0d046600002e2c02dc810c860000000c6d00000804860000000c0d046600001652810c81000000066f00ffc872006000001044812c3c000005004c016800d08624172c2f00044fef00084e754feffff448d7024843fa00606000000e4feffff448d7024843fa004c4dfa23c82002e188ddc0204e78ff7c0471ae002471b10800e188e188e188e988262e003002830fffffff8680b684640000062803204e4dee004053866600ffd24cd702484fef000c4e7500020100030201037801600000047800242f00344fefffe82e892f4700042f4b000c204920074a84660000104290508853806600fff8600000102210e2812081508853806600fff46100f78e2f4a00084a2e0024670000cc264e22572e2f000420132c2b00042a2b000c246b0010286b0020736b00262f410014426b0026222b001875eb003c3741003c9282484142414c4718012f410010484242422a4241fa1cdc7618220c4c2b1800001cd2862401e6aad48279702800757028029484e08973c14c012800e082e082d8822844222b0014d2af0014274100144c014800d8802404e6aad48273702800757028029481e08c79c44c042800e082e082d282dbef0010240d4842c3c2e681d3915089d085dc8a53876600ff8e268027460004274c00204dee0040bdef00086600ff2422572e2f00042211d28169000016424122c122c153876600ffee4fef00186000ef406b00000c223c800000006000ffe0223c7fff00006000ffd66100002c4ef940003cac220c048180004858660000100c8200000002660000066100ef12610000084ef94000d04671722a1243f04a000c82000000026600001c220c048180004858e689ef8941fa1dca4a3018246700000470004e75203c400d37f60c850000000166000122243c000018b24c012800d48920422803c8fc001ed1c4d1fc0008edbc7bd00c45464d6600000e7ba800020c050001670000722803e58cd883d4842042d1fc0008f04b75900c820000007f620000d4283c000004484c0248000684100b14f020442248283c000000ff7b98670000140c850000002f66000004224853846600ffea3a3c464dba5166000004548941fa168e78057b987599ba826600008653846600fff243fa2c144a3a2c0c6600007241f9400d37f6283c0000019212d853846600fffa43fa2bf441fa1659235800343350003841fa007c234800de41fa164b235800223350002641fa003e234800d2780123440072134400607804234400a242a901322829018e0284ff0ff0ff0084005005002344018e41fa2b9c10bc000120094ef940031ed6202f000853800c800000000363000004700052802f00487a16102f2f000c4eb940013a084fef000c4e75207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a71b9100b14cce7882200d081d08171b0080255800c80000000026300000a700072006000001c202f00082200028100000003e48802800000001f660000047200e58841fa166041f00800226f000470044a106700000a12d853806600fff44a816700000a06810000003012c142114e754feffff848d70c0045fa2c644a3a2c5e6600004e41f9400d31ae264a223c0000019216d853816600fffa71596d00001673d9205947f2080016d853816600fffa6000ffe841fa0026220847ea012a700626c153806600fffa41fa2c1210bc0001200a4cd70c004fef00084e754feffff448d7001c242f0010222f0014202f00184ab946c7dd26670000284ab9800000a06600001e0c82000000016700003e41fa009077b028004c031800d0816000007826020683000000382043d683d688e78b41f946c7d8ee4ab03800670000082601e7899283d0816000004ee48802800000001f76014a816700003c6e000006448344812400d4830c820000001f620000260c82000000036d00001441fa146879f02a000284000000ff6600ffda200253816600ffd0e5884cd7001c4fef000c4e750c00100a10104fefffd048d70cfc45ef002041fa147041f00c00740018302800158428001582280452820c82000000046600ffea6100015674017a007d322800610001a82e067d3258006100019ebe866600001241f228041c1008c6000710866000000a5285ba826600ffd252820c82000000046600ffc4610001502a01ba84650000089a846000fff67db258087f3268007400b485640000207db2280841f268007d1006860000000cbc876f00fff6108652826000ffde610000d26100010c7daa00087f3268007a0074004a3228046a0000282c05700cbc846500000e9c8406800000000c6000fff07db268087d326800dc8015862800528552820c82000000046600ffc870007a0074007db22804028600000003bc806700001252820c82000000046600ffe66000000e7d3228009c871586580c52855280ba836500ffcc220353816f00002874007d32280c7132280db0866c00000a1580280c1586280d5282b4816600ffe453816600ffdc74001c32280c138628005282b4836600fff220044cd70cfc4fef00304e7578037a007d3258007f325801be866c00001a15875800158658011c3258041e325805158758041586580552850c85000000036600ffd053846600ffc84e75780074004a3228046b00000815824808528452820c82000000046600ffe84e750c860000000c6500000c04860000000c6000ffee4e754fefffc848d71cfc45ef002477ab000141eb000c740010302800158028005282b4836600fff2220353816f00002874007db2280071b22801b0866400000a15802800158628015282b4816600ffe453816600ffdc7d927400780071b228009086158028006100015a01c45282b4836600ffea7bb228ff700928407e007c01610000766100005852860c86000000206600ffeeb9fc00000009660000307c017e01610000546100003652870c87000000046600ffee52860c86000000206600ffe0b9fc0000000967000060202f002c22000281000000ffe088600000dc4a8067000014b08c6400000e28402006e18880872f4000304e752006220743ea00046100fd06be806400001e2200740010322800b03228046600000e5282b4836600ffee20014e7570004e7542af002c203c7fffffff2f4000307c017e002006220743ea00046100fcc2be806400005228407400720071b228046100006801c15282b4836600fff0b2846600002871b2380390856a0000044480e788d08cb0af0030640000102f4000302006e18880872f40002c52870c87000000046600ffa052860c86000000206600ff92202f002c22000281000000ffe0884cd71cfc4fef00384e750c800000000c6500000c04800000000c6000ffee4e754fefffd448d77cfc2a002c0a2803207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2002e7882200d081d08173b0080255810c8100000002620000fc548171b00805e488660000f047fa22782002eb88d7c01741001d49fa243e2e2c000c610000e24a856a0000b471ab0001670000c8220792ab00040c810000019d620000b841eb000822007198610004526600000c53816600fff26000009e71ab00012f0041eb000843eb00084beb000c4deb000c72007198b08467000010610004206700000812c01cd55281528d53976600ffe4588f422b00030c81000000046400002412841c8652811744000327470010203946c7e956274000142014274000181746001c1741000127470004610000544cd77cfc4fef002c70014e75168527470004174400081746000c177c00010001422b0002422b00034cd77cfc4fef002c70004e754a2b000367000016200790ab00100c800000008a65000006422b00034e7577ab000141eb000c787f20037398b28464000004280153806600fff220047200610001d00c8300000002650000306100fc904a8067000026e5888081720b610001b271ab001db0836300000420037208610001a0177c000100024e754a2b00026700001e203c000000ff720b61000184203c000000ff720861000178422b00024e754fefffcc48d71fff28000c840000008064000004528447fa20c82002eb88d7c07bab0003670000dc49fa228a2e2c000c6100ff2e4a2b0003670000c8ba84670000c271ab00015380670000b841eb0008b8186700000c53806600fff6600000a471ab0001538017400001422b00036100ff0e4feffff82e82202b00142f4000044eb940042d1c4fef00084a806b0000742c0061000078168617450008102b001c1740000c177c00010001422b0002202b00102740000471ab001c7200610000b20c85000000806400003a206f002020086700003072034a2800026700000e508853816a00fff2206f002020055380108011460001117c00010002202b0018214000044cd71fff4fef00344e752002223c0000048d4c01080073b980000004263c0000476c4c031800d081d08606800000044d2600d68341f91001614ed1c332100281ffffe07f308173b980000002263c0004d9a04c031800d081d08041f9400e21e0d1c032100281ffffe07f30814e754fefffec2e822f4100042f40000871932f40000c203946c7e9562f4000104eb9400421584fef00144e754feffff048d7030a207946c8245671b9100b14cf223c000018b24c010800d1c02248d3fc0008eda271b1280053806600004622482002c0fc001ed3c0d3fc0008edbc71d10c40464d6600001471a900020c0000016600000870016000001c2002e588d082d1c0d1fc0008f04b7190610000146000000470004cd7030a4fef00104a804e754feffff048d7030a0c800000007f6200005c223c000004484c0100000680100b14f020402248263c000000ff7198670000140c800000002f66000004224853836600ffea303c464db05166000004548941fa0bdc760571987399b0816600000e53836600fff270016000000470004cd7030a4fef00104a804e756100ff006700004c2f012f08207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2202e7892001d281d28071b0180255800c8000000002630000087000600000047001205f221f4a804e7543fa1ef62002e788d3c04a114e754feffff448d702030c800000008064000014538043f9400d2cb822312c0001016000001e6100ffcc20177208b0196700000e53816600fff672006000000472014cd702034fef000c4e754feffff048d703096100ff9e204976087190b081670000204a8067000018528853836600ffec2049760710e8000153836600fff810814cd703094fef00104e754feffff448d703086100ff5e204976087190b081670000164a806700000a528853836600ffec70ff6000002653836700000c10e8000153836600fff84210700076084a196700000a528053836600fff44cd703084fef000c4e754feffff048d7030941fa1ec477b028002003528002800000000743fa1eaab03128006700001c43fa1e1ed683d3c32602e98bd3c31281261713430001118028004cd703094fef00104e7541fa1e7a73b0280043fa1e7a7e00b23128006700002443fa1de42002e988d3c0d3c1d3c17f9171a900015281028100000007118128004a874e7543fa1e4871b1280041fa1e38118028004e756100fd246700009871930480000000146a0000047000224412802040739300810000008043fa10422002ef88d3c0422900256100fe44670000466100eeda080000006700003a6100fe880c80000000036600000820016100ecc443fa100c2002ef88d3c0137c0001002520352c000080000001192b802c004ef94000e7546100fe5020086100fee443f9400d2cb013812800721d2b812c004ef94000e754721d2b812c004ef94000e74c6100fc7a6700003e73930081000000806100fe546b00002e2f002f08200191c86100fac0205f201f22006100fd4e670000144a81660000084ef94000dfdc4ef94000dff873107113b0816600fff24ef94000dfdc266f00722031bc00080000026600001c240bef8a41fa0f54d1c20828000000246700000808e8000100244ef94000b522226f00724fefffe848d70c1e28090c84000000086400006473b0980045fa1d14529224010282000000306700004e2404ef8a47fa0f06d7c226014a2b00246700000c72301181980052aa0004242a000852aa000802820000001fe78a45f22810243a1cbc248275ab0024e18a8481e18a8483e18a8484254200044cd70c1e4fef0018103098004ef94000c63c42b946c803504feffff048d7030641fa0e9e43fa129a72080828000000246700000e08e80001002408e800020024740471a900240c800000000166000008137c0002002443e9004053826600ffe441e8008053816600ffc24cd703064fef00104ef94000b2ce4200138088004feffff848d7020424012001ef8841fa0e32d1c04228002442a800184268002642a800586100e4d041fa12182002e188d1c070044228002542a8003441e8004053806600fff04cd702044fef00084ef94000686271930c80000000486500000c0c80000000606300004c080300086700001675b9800000006100fa86670000364ef94000e6e22f017200740705036700000c6100fa6c6700000405c153826a00ffec4a816700000c2601221f4ef94000e6e2221f200655807405b4804ef94000e45a0c82000000486500000c0c8200000060630000664feffff448d70007080400086700001e75b9800000006100fa12670000404cd700074fef000c4ef94000df9e7200740705046700000c6100f9f26700000405c153826a00ffec4a816700001228014cd700074fef000c4ef94000df9e4cd700074fef000c55807605b6804ef94000de16202a000423c046c7e9564fefffe848d71c1c24016100f9a4670000b277aa000228030484000000146a0000047800008300000080244447fa0cc42002ef88d7c04a2b00256600002870ff6100f5e84a806600006c4fefffec2e82203946c7e9562f4000044eb940042d1c6000001c422b00254fefffec2e82203946c7e9562f4000044eb94004271c4a806b00002e28002f40000c2f4a000842af0004203946c7e9562f4000104eb9400421584fef001420046100f580600000064fef00144cd71c1c4fef00184ef940062d1c4cd71c1c4fef0018202a00044ef9400625ea752a0002204241f02c002f0224016100f8cc241f4a806700001820020480000000146a000004700006800000016420404ef9400626344ab9400bebae6600014c4ab9460d17366700013c4a79460d174a670001324fefffd048d73cff75b9100b14cc6100f8784a806700011220175380660000fc760c0c81000000336700000444837ff9460d174a7c000d07670000862006d0b9460d174ceb882202283c0000091a4c041800d08173b9100b14d0283c00008ed84c041800d081247946c82456d5c045ea005947f9100161a7d7c073920c81000000ff66000006610000b4d2836a00000472000c810000007f6f000004727f14811681207946c82456203c0009b33272012181080023c1100f85984eb940027e0052860c86000000106d00ff6c2f024eb94009da20588f700123c0460d173a41f9460d1a9e4250426800024268000442680006426800084279460d10dc23c0460d17504eb9400418e04878ffff4eb94004d948588f4eb940027de44cd73cff4fef00304ef9400520064cd73cff4fef00304ef940051ff64ef940051fd6207946c8245671b9100b14cf223c000018b24c010800d1c02208d28206810008eda222417191d0802200d080d280d1c12202eb8992829282d1c1d1fc0008edaa73904e7570000c82000000076600000c4a3980000034660000086100f6f054804a804e752f0275b9800000006100ffd6241f4a80670000702f0043fa1c464a3a1c406600005841f9400d3988223c0000019212d853816600fffa43fa1c2641fa036f235800583350005c41fa0066234800f6223c40046d9c23410126422900692229018a0281ffff0fff0081000010002341018a41fa1bea10bc0001201f234000c620094e75203c400d39884e754feffff048d7030675b9800000006100ff4653806a00000470004cd703064fef00104e756100ffda2200202f0008b081630000042001e78841fa02edd1c0226f000412d86600fffc4e756100ffb42200202f000cd0af00086a0000047000b0816f00000420014e756100ff0c2f004ef940059d5c6100ff00204049e8003a47e800e24ef9400368666100feec20402f28018e2f28018a4ef9400368b86100fed820402f28018e2f28018a4ef9400369520680400d398822402069012a0c820000000b660000162f0224046100fe8a241f4a806700000641faff724ef94003ae4c41f98000083cb5fc00000005660000242f022f002404e68a6100f56467000010201f241f423008004ef94003af14201f241f118208004ef94003af14d1fc0008f04a719010814feffff048d7001e26002801202f0018242f0014220b610000684cd7001e4fef00104ef9400795c2d1fc0008eda277104fefffe848d7031720032401220476ff6100003e4cd703174fef001877104ef94007981ed1fc0008eda279104fefffe848d7030f20042239460d5c3076ff610000104cd7030f4fef00184ef94005a8504fefffd848d703ff53816600002c4a836a0000066100002c2e0020046100f51e6700001653876600000c20036100f50e66000006610000384cd703ff4fef00284e75207946c8245677b9100b14cf283c000018b24c043800d1c32602e58bd682d1c3d1fc0008f04b779028034e754feffff048d70303207946c8245671b9100b14cf223c000018b24c0108002202eb8992829282d28006810008edb370401180180043f91001614e1380180072484c02180041f980000810118018034cd703034fef00104e754feffff448d700c47e007001b0846600000a20036100f4602e002f2f00182f2f00182f2f00184eb940013a084fef000c7001b084660000644a876600005e20036100f434670000542c037400207946c8245671b9100b14cf223c000018b24c010800d1c0200206800008eda271b0080053806600001c2002e588d08206800008f04b71b00800b086660000066100ff1a52827008b0826600ffb44cd700c44fef000c4e7553594e5448434852440000564f494300004c45470000004f464600000000004d4f4e4f00000000504f4c5900000000257300256400000040008000c0010001030140016a018001c002000203028003000380040004030480050005800600068007000780080009000a000b000c000d000e000f001000008080800003070c0004070c0002070c0005070c000a0307000a0407000b040700050a07000603090002040700020307000e04070009030700090407000a06030006030c0008040c000a0e03000a0e04000b0e0400090e0400050a0f00050c1100070c13000c182400030c0f00040c10000a0c16000b0c170007100c00070f0c2d2d2d2d4d494e004d414a0053553200535534004d493700444f37004d4137003753340044493700414432004d443200414439004d4936004d4136004d37350044494d00415547004d493900444f39004d4139004d3639005155410034544800355448004f435400334d4900334d4100374d4900374d41004d4153004d495300000080000000800000005a82000049e700004000080008000800080008000800076806d4064c05ce055a04ef048d043203de03910349030802cb029302600230020501dc01b7019501750158013d0125010e00f900e500d300c300b400a60099008d00820078006e0066005e00560050004a0044003e003a00350031002d002a002600230021001e001c001a001800160014001200110010000e000d000c000b000a000a000900080008000700060006000500050005000400040004000300030003000300020002000200020002000200010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100014de54de54de54de54de54de54de54de54de54de54de54de54de54de54de54de548e5440b3f753b21370d33382f9e2c3f29162622235f20cc1e651c291a141824165714ac131e11ae10580f1c0df60ce60beb0b020a2a096308ab0800076306d1064b05ce055c04f20490043503e20395034e030c02d0029802640235020901e001bb01980179015b01400127011000fb00e800d600c500b600a7009a008e0083007900700067005f00570051004a0045003f003a00360032002e002a002700240021001e001c001a001800160014001300110010000f000d000c000b000b000a00090008000800070006000600060005000500040004000400030003000300000001000100020003000400060008000b001000170020002d0040005b008000880090009800a000a800b000b800c000c800d000d800e000e800f000f8010001100120013001400150016001700180019001a001b001c001d001e001f00200022002400260028002a002c002e00300032003400360038003a003c003e004000440048004c005000540058005c006000640068006c007000740078007c008000880090009800a000a800b000b800c000c800d000d800e000e800f000f8010001100120013001400150016001700180019001a001b001c001d001e001f00200022002400260028002a002c002e00300032003400360038003a003c003e004000ffff000000000192032404b5064607d609640af10c7c0e060f8d11121294141315901709187e19ef1b5d1cc61e2b1f8c20e7223d238e24da26202760289a29ce2afb2c212d412e5a2f6c307631793274336834533537361236e537b03871392b39db3a823b213bb63c423cc53d3f3daf3e153e723ec53f0f3f4f3f853fb13fd43fec3ffb40003ffb3fec3fd43fb13f853f4f3f0f3ec53e723e153daf3d3f3cc53c423bb63b213a8239db392b387137b036e536123537345333683274317930762f6c2e5a2d412c212afb29ce289a2760262024da238e223d20e71f8c1e2b1cc61b5d19ef187e170915901413129411120f8d0e060c7c0af1096407d6064604b5032401920000fe6efcdcfb4bf9baf82af69cf50ff384f1faf073eeeeed6cebedea70e8f7e782e611e4a3e33ae1d5e074df19ddc3dc72db26d9e0d8a0d766d632d505d3dfd2bfd1a6d094cf8ace87cd8ccc98cbadcac9c9eec91bc850c78fc6d5c625c57ec4dfc44ac3bec33bc2c1c251c1ebc18ec13bc0f1c0b1c07bc04fc02cc014c005c000c005c014c02cc04fc07bc0b1c0f1c13bc18ec1ebc251c2c1c33bc3bec44ac4dfc57ec625c6d5c78fc850c91bc9eecac9cbadcc98cd8cce87cf8ad094d1a6d2bfd3dfd505d632d766d8a0d9e0db26dc72ddc3df19e074e1d5e33ae4a3e611e782e8f7ea70ebeded6ceeeef073f1faf384f50ff69cf82af9bafb4bfcdcfe6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015900000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004feffff448d70302207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008eda271b028000c00000191fc0008eda26600002c2002c0fc001ed1c0d1fc0008edbc71d00c40464d6600001471a800020c0000016600000870016000000470004cd703024fef000c4a804e75205c4feffff848d7000524036100ff826700000641fab6da4cd700054fef000826c8487800104ef94000d51c4feffff848d70104242f000c6100ff566700002e203c000000a84c02080041f9800049d8d1c010bc00ff117c00010014203c000001004cd701044fef00084e754cd701044fef00084ef94000f45000000000000000000000000000000000000000000000000000000000000003000001000000000000000000000000030000030000000000000000000000000300000400000001800049d8000000000000fff10000000c000000a8000000000000fff10000001980001c80000000000000fff10000002046104d0c000000000000fff10000002740004008000000000000fff100000034800062a8000000000000fff10000003b800062a4000000000000fff100000042100b14f0000000000000fff10000005000024640000000000000fff10000005e00000448000000000000fff10000006e400aa294000000000000fff10000007800000003000000000000fff1000000850184cbb7000000000000fff10000008c01000000000000000000fff10000009400100000000000000000fff10000009e002ed1e0000000000000fff1000000a4000fffff000000000000fff1000000aa00000a44000000000000fff1000000b600000204000000000000fff1000000bf73000000000000000000fff1000000c7100b14ed000000000000fff1000000d0400d2cb0000000000000fff1000000d8400d2cd8000000000000fff1000000e1800065b2000000000000fff1000000ea800065b6000000000000fff1000000f30000000d000000000000fff1000000fb00000000000000000000fff10000010400000004000000000000fff10000010b00000008000000000000fff1000001120000000c000000000000fff10000011c00000010000000000000fff10000012400000014000000000000fff10000012c00000018000000000000fff1000001340000001c000000000000fff10000013d00000159000000000000fff100000149400d2ca8000000000000fff10000015280000810000000000000fff10000015a00000048000000000000fff1000001640000000b000000000000fff10000016c00000008000000000000fff1000001740000000e000000000000fff10000017b80004858000000000000fff100000185400d37f6000000000000fff10000018b00000192000000000000fff100000198400d31ae000000000000fff10000019f00000192000000000000fff1000001ad40031ed6000000000000fff1000001ba0008f04b000000000000fff1000001c30008f04a000000000000fff1000001cc1001614e000000000000fff1000001d800000003000000000000fff1000001e0460d5c30000000000000fff1000001ed400795c2000000000000fff1000001f84007981e000000000000fff1000002044005a850000000000000fff10000021040013a08000000000000fff10000021840013a08000000000000fff10000022000000500000000000000fff100000225ffffff80000000000000fff10000022f800000e0000000000000fff10000023780000110000000000000fff10000023f0000000c000000000000fff10000024600000003000000000000fff10000025046c80350000000000000fff10000025c40006862000000000000fff10000026500000080000000000000fff10000026f00000000000000000000fff10000027500000004000000000000fff10000027b00000008000000000000fff1000002810000000c000000000000fff10000028700000010000000000000fff10000028e00000014000000000000fff10000029500000018000000000000fff10000029c0000001c000000000000fff1000002a100000020000000000000fff1000002a900000024000000000000fff1000002ae00000026000000000000fff1000002b600000028000000000000fff1000002bc0000002c000000000000fff1000002c200000030000000000000fff1000002c900000034000000000000fff1000002d000000038000000000000fff1000002d50000003c000000000000fff1000002da00000040000000000000fff1000002e200000044000000000000fff1000002e600000048000000000000fff1000002ea0000004c000000000000fff1000002f200000050000000000000fff1000002f900000051000000000000fff10000030100000000000000000000fff10000030a00000004000000000000fff10000031300000008000000000000fff10000031b00000010000000000000fff10000032300000110000000000000fff10000032b00000052000000000000fff10000033000000054000000000000fff10000033700000058000000000000fff10000033e00000070000000000000fff10000034500000074000000000000fff10000034d00000078000000000000fff1000003550000005c000000000000fff10000035b00000040000000000000fff10000036146c82456000000000000fff10000036a100b14cf000000000000fff100000373100b14cc000000000000fff10000037c0008ee9a000000000000fff10000015b00000040000000000000fff10000038900000000000000000000fff10000038f00000004000000000000fff10000039500000008000000000000fff10000039b0000000c000000000000fff1000003a100000010000000000000fff1000003a800000014000000000000fff1000003af00000018000000000000fff1000003b60000001c000000000000fff1000003bb00000020000000000000fff1000003c300000024000000000000fff1000003cb00000025000000000000fff1000003d100000026000000000000fff1000003d900000028000000000000fff1000003df0000002c000000000000fff1000003e600000030000000000000fff1000000f400000034000000000000fff1000003ec00000038000000000000fff1000003f40000003c000000000000fff1000003fc0000003e000000000000fff10000040400000020000000000000fff10000040e00000000000000000000fff10000041600000001000000000000fff10000041b00000002000000000000fff10000042400000003000000000000fff10000042c00000004000000000000fff10000043500000008000000000000fff10000043d0000000c000000000000fff10000044500000010000000000000fff10000044f00000014000000000000fff10000045700000018000000000000fff1000004610000001c000000000000fff1000004690000001d000000000000fff1000004710000019d000000000000fff1000004780000008a000000000000fff100000480460d172a000000000000fff10000048b400d2cdc000000000000fff10000049540042d1c000000000000fff10000049e400e21e0000000000000fff1000004a61001614e000000000000fff1000004ae0000044d000000000000fff1000004b880000004000000000000fff1000004c080000002000000000000fff1000004c940042158000000000000fff1000004d446c7e956000000000000fff1000004dc00000000000000000000fff1000004e4400d3988000000000000fff1000004ea00000192000000000000fff1000004f70008f078000000000000fff10000050200000005000000000000fff10000050c40046d9c000000000000fff10000051780000034000000000000fff1000005214004271c000000000000fff10000052e0000007c000000000000fff1000005370000007d000000000000fff10000053e0000007e000000000000fff10000054600000025000000000000fff10000054d46c7dd26000000000000fff100000557800000a0000000000000fff10000056446c7d8ee000000000000fff10000056d00000038000000000000fff1000005770000012a000000000000fff10000058000000001000000000000fff1000005890000001f000000000000fff10000059400000003000000000000fff1000005a0000029100000000000000001000005aa000011ac0000000000000001000005b4000013a60000000000000001000005bf0000201c0000000000000001000005c9000019960000000000000001000005d100001e300000000000000001000005db000031f20000000000000001000005e60000192a0000000000000001000005f0000034f80000000000000001000005f90000025e000000000000000100000601000001d40000000000000001000006090000030e000000000000000100000614000001b200000000000000010000061a000021b6000000000000000100000626000000de00000000000000010000062e000000a4000000000000000100000636000000bc000000000000000100000641000000b600000000000000010000064a000000c800000000000000010000065000002e68000000000000000100000658000000ce00000000000000010000065f0000011400000000000000010000066e000042d0000000000000000100000677000001160000000000000001000006840000014a00000000000000010000069000000122000000000000000100000699000001500000000000000001000006a1000001920000000000000001000006ab00000bd00000000000000001000006b30000247e0000000000000001000006c20000019e0000000000000001000006cb00000bf60000000000000001000006d4000006f20000000000000001000006dc000001d00000000000000001000006e300000bac0000000000000001000006eb000002560000000000000001000006f500000234000000000000000100000700000007bc000000000000000100000708000002f6000000000000000100000711000048c000000000000000010000071e000002aa00000000000000010000072c0000029e00000000000000010000073c0000029600000000000000010000074c000002c4000000000000000100000759000004d000000000000000010000076200000332000000000000000100000770000008560000000000000001000007790008eda2000000000000fff10000078200000cba0000000000000001000007900000072200000000000000010000079d0000034e0000000000000001000007a900002e9e0000000000000001000007b2000003a40000000000000001000007bb000003f80000000000000001000007c6000003ee0000000000000001000007d1000003ce0000000000000001000007d9000004320000000000000001000007e50000048c0000000000000001000007ef0000046a0000000000000001000007f900002ff2000000000000000100000800000004ca00000000000000010000080c000030f2000000000000000100000814000004a800000000000000010000081f000004b800000000000000010000082a000006060000000000000001000008320000051000000000000000010000083b0000052c0000000000000001000008430000158600000000000000010000084b000032f40000000000000001000008520000055c00000000000000010000085a000005e800000000000000010000086200000d2e000000000000000100000869000015800000000000000001000008750000061400000000000000010000087d000006780000000000000001000008880000064e0000000000000001000008930000067a00000000000000010000089b0000068e0000000000000001000008a4000006de0000000000000001000008ad000006d20000000000000001000008b6000006da0000000000000001000008bf0000072e0000000000000001000008cb000007440000000000000001000008d70000075a0000000000000001000008e30000076a0000000000000001000008ee000007820000000000000001000008f80000077e0000000000000001000009030000078800000000000000010000090c00000790000000000000000100000917000008480000000000000001000009220000083a00000000000000010000092b0000084a00000000000000010000093700000894000000000000000100000941000040fc00000000000000010000094a000008ce00000000000000010000095600000d50000000000000000100000963000008a000000000000000010000096e000008c400000000000000010000097a000008d2000000000000000100000986000008fc0000000000000001000009930000231600000000000000010000099b0000093c0000000000000001000009a60000092e0000000000000001000009af000011c80000000000000001000009bb000009520000000000000001000009c800000d600000000000000001000009d1000009a80000000000000001000009dc000009aa0000000000000001000009e5000009ec0000000000000001000009ee00000a0c0000000000000001000009f700000a140000000000000001000009ff00000b90000000000000000100000a0a00000a48000000000000000100000a1500000a7c000000000000000100000a2000000aae000000000000000100000a2a00000aa4000000000000000100000a3800000af0000000000000000100000a4200000b32000000000000000100000a4c00000b28000000000000000100000a5600000b54000000000000000100000a6000000b8c000000000000000100000a6b00000b4c000000000000000100000a7600000b54000000000000000100000a8200000b6e000000000000000100000a8d000038f8000000000000000100000a9700000bb8000000000000000100000aa000000bdc000000000000000100000aa900000bea000000000000000100000ab200000c0c000000000000000100000abd00000c30000000000000000100000ac800000c22000000000000000100000ad300000c32000000000000000100000ade00000c36000000000000000100000ae9000040f8000000000000000100000af000000c92000000000000000100000af900000caa000000000000000100000b0100000ce2000000000000000100000b0c00000ce4000000000000000100000b1700000d1a000000000000000100000b2200000cf8000000000000000100000b2a00000d16000000000000000100000b3600000d6c000000000000000100000b4100000d78000000000000000100000b4d00000dd6000000000000000100000b5900000daa000000000000000100000b6200002444000000000000000100000b6f00000dce000000000000000100000b7b00000e4a000000000000000100000b8500000e48000000000000000100000b8f00000de8000000000000000100000b9900000e44000000000000000100000ba600000dfa000000000000000100000bb000000e06000000000000000100000bba00001472000000000000000100000bc200000e68000000000000000100000bcb00000f34000000000000000100000bd400000ee6000000000000000100000bdf00000e96000000000000000100000bea00000ed8000000000000000100000bf40000111e000000000000000100000bfe00000efc000000000000000100000c0800002308000000000000000100000c1400000f2a000000000000000100000c1e00000f5c000000000000000100000c2a00000f3e000000000000000100000c3400000f52000000000000000100000c3f00000f74000000000000000100000c4800000f96000000000000000100000c5100000fbe000000000000000100000c5c000041fc000000000000000100000c6400001a60000000000000000100000c6f00000fc2000000000000000100000c7b00002ede000000000000000100000c8500002fde000000000000000100000c8d00000fdc000000000000000100000c9a00001024000000000000000100000ca500001120000000000000000100000cb200000fe6000000000000000100000cba00000ff0000000000000000100000cc600000ffa000000000000000100000cd300001004000000000000000100000cdd00001512000000000000000100000ce60000111e000000000000000100000cf100001522000000000000000100000cfa00001060000000000000000100000d0500001066000000000000000100000d0f000010ce000000000000000100000d1a0000110a000000000000000100000d26000010dc000000000000000100000d31000010fa000000000000000100000d3c000011aa000000000000000100000d46000011a8000000000000000100000d4f00001162000000000000000100000d5a00001172000000000000000100000d6600004200000000000000000100000d6f00001178000000000000000100000d7700001194000000000000000100000d7f000011e0000000000000000100000d8b000011ec000000000000000100000d96000011f8000000000000000100000da20000122c000000000000000100000dab0000135c000000000000000100000db800001254000000000000000100000dc20000128e000000000000000100000dcd00001266000000000000000100000dd700001286000000000000000100000de10000129e000000000000000100000dea0000133c000000000000000100000df6000012d6000000000000000100000e020000132c000000000000000100000e0d00001356000000000000000100000e1800001364000000000000000100000e2200001380000000000000000100000e2d00001454000000000000000100000e3a00001416000000000000000100000e4500001448000000000000000100000e4f00001426000000000000000100000e5900001420000000000000000100000e6300001446000000000000000100000e6f00001470000000000000000100000e7900001510000000000000000100000e83000014b8000000000000000100000e8e000014c6000000000000000100000e97000014f8000000000000000100000ea2000014e0000000000000000100000eab000014fa000000000000000100000eb400001506000000000000000100000ebe0000157c000000000000000100000eca0000152e000000000000000100000ed300001578000000000000000100000edb0000153e000000000000000100000ee600001564000000000000000100000ef100001588000000000000000100000efa000015b2000000000000000100000f05000015a4000000000000000100000f11000015c0000000000000000100000f1e000015c8000000000000000100000f2a0000169a000000000000000100000f350000161c000000000000000100000f40000016a6000000000000000100000f4c000016ac000000000000000100000f56000016c8000000000000000100000f60000016b4000000000000000100000f6a000016d6000000000000000100000f750000170e000000000000000100000f8300001704000000000000000100000f900000173a000000000000000100000f9b0000186c000000000000000100000fa6000017ee000000000000000100000fb3000017b4000000000000000100000fbe000017cc000000000000000100000fcc000017c6000000000000000100000fd8000017d8000000000000000100000fe1000017de000000000000000100000feb00004404000000000000000100000ff6000044000000000000000001000010030000186a00000000000000010000100e0000180600000000000000010000101900002e6d0000000000000001000010210000189c00000000000000010000102e00002e7300000000000000010000103600001872000000000000000100001042000018840000000000000001000010490000188600000000000000010000105000002e9a000000000000000100001057000018e0000000000000000100001063000018fa00000000000000010000106e00002f5e0000000000000001000010770000190a0000000000000001000010800000191800000000000000010000108a00001926000000000000000100001094000045980000000000000001000010a2000045960000000000000001000010ae0000198a0000000000000001000010b90000194c0000000000000001000010c4000019540000000000000001000010d00000196e0000000000000001000010e0000019620000000000000001000010eb0000197a0000000000000001000010f6000019da00000000000000010000110200001a0400000000000000010000110e00001a5a00000000000000010000111900001a50000000000000000100001123000019fe00000000000000010000112d00001a4e00000000000000010000113a00001a1c00000000000000010000114600001a1e00000000000000010000115300001a4600000000000000010000116000001a7600000000000000010000116800001be600000000000000010000117000001a9400000000000000010000117600001a9600000000000000010000117c00001c4400000000000000010000118200001abe00000000000000010000118800001ac600000000000000010000118e00001c2400000000000000010000119600001ad800000000000000010000119c00001ae40000000000000001000011a200001aee0000000000000001000011a800001b120000000000000001000011ae00001afe0000000000000001000011b400001b260000000000000001000011ba00001b540000000000000001000011c000001b320000000000000001000011c600001b440000000000000001000011cd00001b640000000000000001000011d300001b660000000000000001000011d900001b860000000000000001000011df00001b920000000000000001000011e500001bc80000000000000001000011ed00001ba20000000000000001000011f300001ba40000000000000001000011f900001bba0000000000000001000011ff00001bca00000000000000010000120500001be800000000000000010000120b00001bea00000000000000010000121100001c1000000000000000010000121700001c2800000000000000010000121d00001c3600000000000000010000122300001c5800000000000000010000122a00001c5a00000000000000010000123300001c7000000000000000010000123900001cae00000000000000010000124100001c8800000000000000010000124700001c8a00000000000000010000124d00001ca000000000000000010000125300001cb400000000000000010000125900001e1a00000000000000010000126100001cd800000000000000010000126700001d5000000000000000010000126e00001d3600000000000000010000127600001d2400000000000000010000127f00001cf800000000000000010000128500001cfa00000000000000010000128b00001d8200000000000000010000129500001e1000000000000000010000129c00001d4e0000000000000001000012a500001d7e0000000000000001000012ab00001d660000000000000001000012b100001d920000000000000001000012b700001d940000000000000001000012bd00001df60000000000000001000012c300001dac0000000000000001000012ca00001dea0000000000000001000012d000001dd00000000000000001000012d700001e2e0000000000000001000012de00001f700000000000000001000012e800001f7c0000000000000001000012f500001f540000000000000001000012ff00001ec000000000000000010000130900001ed400000000000000010000131400001eec00000000000000010000131d00001f0200000000000000010000132700001f3c00000000000000010000133200001f9a00000000000000010000133e00001f9800000000000000010000134800001fa600000000000000010000135300001fb000000000000000010000135f0000218c00000000000000010000136800001ff600000000000000010000137500001fe80000000000000001000013800000201a00000000000000010000138a000020320000000000000001000013930000211e00000000000000010000139d0000206c0000000000000001000013a50000207c0000000000000001000013b0000021280000000000000001000013bc000020f20000000000000001000013c5000021060000000000000001000013ce0000222c0000000000000001000013d70000222e0000000000000001000013e10000223a0000000000000001000013f0000022a60000000000000001000013f9000022660000000000000001000014040000227e0000000000000001000014120000227800000000000000010000141e0000228a00000000000000010000142700002290000000000000000100001431000022a800000000000000010000143b000022b400000000000000010000144800002306000000000000000100001452000022fe00000000000000010000145c000023000000000000000001000014660000233a0000000000000001000014710000235600000000000000010000147b00002342000000000000000100001486000023540000000000000001000014900000236000000000000000010000149d000023700000000000000001000014a8000023960000000000000001000014b2000023940000000000000001000014bc0000238a0000000000000001000014c8000023a00000000000000001000014d5000023b00000000000000001000014e0000023cc0000000000000001000014ec000023c60000000000000001000014f7000023f0000000000000000100001501000023dc00000000000000010000150c000023d2000000000000000100001518000023e2000000000000000100001524000023fa000000000000000100001532000042c800000000000000010000153b000042c00000000000000001000015440000243a00000000000000010000154e000042400000000000000001000015570000247c0000000000000001000015610000252e00000000000000010000156e000024a60000000000000001000015790000250e000000000000000100001586000024ea0000000000000001000015960000257e0000000000000001000015a4000025780000000000000001000015b10000258e0000000000000001000015b8000025b80000000000000001000015c3000026380000000000000001000015d1000042f00000000000000001000015da0000260a0000000000000001000015e8000026620000000000000001000015f2000026780000000000000001000015fc0000267a0000000000000001000016060000268e000000000000000100001611000026ea00000000000000010000161b000027200000000000000001000016260000276a0000000000000001000016320000273c00000000000000010000163d00002742000000000000000100001648000027520000000000000001000016530000276800000000000000010000165e0000278c00000000000000010000166a000027f0000000000000000100001677000027b8000000000000000100001683000027e800000000000000010000168f000027bc00000000000000010000169b000027cc0000000000000001000016a7000028c80000000000000001000016b30000282a0000000000000001000016bd0000286a0000000000000001000016c7000028ba0000000000000001000016d2000028840000000000000001000016df000028b60000000000000001000016e90000290a0000000000000001000016f3000029020000000000000001000016fd400bebae000000000000fff10000170540051fd6000000000000fff10000171340051ff6000000000000fff10000172340052006000000000000fff100001730460d1736000000000000fff100001739460d174a000000000000fff100001743460d174c000000000000fff10000174d460d16f0000000000000fff100001757100b14cc000000000000fff100001761100b14d0000000000000fff1000017690000091a000000000000fff10000177400008ed8000000000000fff10000177f00000059000000000000fff1000017880009b332000000000000fff100001793100f8598000000000000fff10000179a40027e00000000000000fff1000017a240027de4000000000000fff1000017a94009da20000000000000fff1000017b1460d173a000000000000fff1000017ba460d1a9e000000000000fff1000017c3460d10dc000000000000fff1000017cc460d1750000000000000fff1000017d5400418e0000000000000fff1000017dd4004d948000000000000fff1000017e40008edaa000000000000fff1000017ef0000000c000000000000fff1000017f800000033000000000000fff1000017ff00002a6400000000000000010000180a00002a5e00000000000000010000181700002a5600000000000000010000182800002a480000000000000001000018360000295c0000000000000001000018400000296400000000000000010000184a000029ee000000000000000100001855000029b800000000000000010000186000002a6a00000000000000010000186d000029c0000000000000000100001876000029cc00000000000000010000187f00002aae00000000000000010000188b00002ac400000000000000010000189700002aca0000000000000001000018a100002ace0000000000000001000018ad00002b500000000000000001000018b90000472c0000000000000001000018c80000472a0000000000000001000018d500002b460000000000000001000018e000002afc0000000000000001000018eb00002e790000000000000001000018f200002b7c0000000000000001000018fd00002b5800000000000000010000190700002b7200000000000000010000191100002b8e00000000000000010000191800002e7f00000000000000010000192400002b9a00000000000000010000192d00002ba200000000000000010000193800002bb600000000000000010000193f00002bbe00000000000000010000194600002c3200000000000000010000195000002c6a00000000000000010000195c00002c6600000000000000010000196900002cfe00000000000000010000197300002d3600000000000000010000197d00002d1600000000000000010000198900002d4000000000000000010000199600002d320000000000000001000019a200002d6c0000000000000001000019b000002dde0000000000000001000019bb00002e5e0000000000000001000019c500002e100000000000000001000019d100002e540000000000000001000019dc00002e970000000000000001000019e3000048c00000000000000001000019ea000000200000000010000001000019f4000016e00000000010000001000019fc000016ea000000001000000100001a050000173c000000001000000100001a1000002490000000001000000100001a170000253a000000001000000100001a1f0000270a000000001000000100001a2800002778000000001000000100001a32000027fc000000001000000100001a3a000028da000000001000000100001a4300002bc0000000001000000100001a4f00002bcc000000001000000100001a5b00002be0000000001000000100001a6700002bf4000000001000000100001a7300002c08000000001000000100001a7e00002c38000000001000000100001a8900002c74000000001000000100001a9300002ca6000000001000000100001a9e00002cd2000000001000000100001aaa00002dc4000000001000000100001ab50000264a000000001000000100001abd000026b0000000001000000100001ac5000025be000000001000000100001acf00004930000000001000000100001ae20000495c000000001000000100564f4943455f4241534500564f4943455f53545249444500435552534f52004e4942424c450053544f434b5f52454e4445520046505f5054520052535f5054520053455454494e47535f424153450053455454494e47535f5350414e0053455454494e47535f5354524944450050495443485f544142004f55545055545f53484946540043345f494e4300454e565f4f4e4500454e565f464c4f4f52004b5f4e554d004b5f4d415800494e4445585f5343414c450046425f5343414c4500494e435f4d415800474c4944455f4154004b4559535f415400434c4f434b5f4154005345515f53544550005345515f5449434b0043565f484f4c4400434b5f5449434b5300434b5f54505300434b5f46505300434b5f4652414d455300434b5f4c41535400434b5f50494e4700434b5f5354455000434b5f4653544550004650535f44454641554c54005343414c455f41540043555256414c530043565f5354524944450043565f434852440043565f564f49430043565f52454c004c464f5f5354415445004c464f5f50004c464f5f444553435f4c454e00464c45585f5000464c45585f444553435f4c454e005245534f4c5645525f52455400534c4f545f4f464600534c4f545f434f4c00504152545f534841444f570043565f52415445004c49535457494e5f4d4143480041535349474e5f524554004d41434857494e5f524554004c49535457494e5f524554004c4f414453454c5f535052494e54460053454d490053484150455f454e440050494e475f4154004453505f5245430043565f41544b004355545f534849465400474c4f42414c5f574f5244004b494c4c5f5245540053545f53545249444500535f50484300535f50484d00535f454e5600535f494e4300535f494e434d00535f4945464600535f4741494e00535f464200535f4c4153544d00535f4f4e00535f475052455600535f43555200545f52454600545f4c41535400545f4d41534b00545f444b00545f524b00545f524154494f00545f4900545f5700545f5343414c4500545f504f4c5900535f4552414d500052545f43414c4c530052545f46495845440052545f484541440052545f52494e470052545f53495a4500545f414b00545f474d415800535f4854494d00545f5241575000545f534d41534b00545f4c4153544b00545f504f5300565f504f5300504152545f50545200504152545f4944580055495f545241434b004c464f5f504147455f4f464600565f50484300565f50484d00565f454e5600565f494e4300565f494e434d00565f4945464600565f4741494e00565f464200565f4c4153544d00565f535441544500565f4b455900565f495354455000565f43555200565f524f4f5400565f41474500565f4652414d4500565f475052455600565f4552414d500043525f5354524944450043525f535445500043525f4e0043525f57524f54450043525f504b45590043525f4652414d450043525f4b4559530043525f524157530043525f504652414d450043525f504354580043525f505449434b530043525f505241570043525f564f4943004b525f57494e004b525f434f4e46004c4956455245435f415400515a5f4f43545f4154005245435f54524947005041545f52414d005041545f4d4952005041545f4e55444745005041545f4355520042414e4b5f435552004c4f434b5f5752495445005245435f4354580043565f5054434800414d505f5000414d505f444553435f4c454e00414d505f50325f4f4646004c45475f534c4f5432004c45475f574944474554004d41535445525f4f4e005245435f545249474c45535300545f4c45474b455900545f4c45474e00535f495354455000545f4d4c45470046554e435f48454c440046554e435f454e435f4f4646004b45595f48454c4400505553485f434f4445004b4e5f534c4f5453004b4e5f524154494f004b4e5f524154494f5f4e004b4e5f535542554e49545900706f5f6f637461766500706f5f6c65676b657900706f5f6c65676d6f646500706f5f6b657972656c00706f5f6b6e6f6200706f5f6b657972656300706f5f686f6c6431323800706f5f7067646573630073795f73746174650073795f63616c6c0073795f69736f6e0073795f6d616368696e650073795f6e6f00706f5f69735f73796e74680073795f636f6c640073795f7363616e0073795f7363616e6e65640073795f7363616e310073795f666d0073795f6e616d650073795f636d700073795f636f6c645f6e6f686f6c6400706f5f636c6f636b0073795f636f6c645f686f6c640073795f7761726d5f656e760073795f636f6c64310073795f7761726d0073795f746f706f6c7900706f5f6661646500706f5f6d72696e675f72657365740073795f73796e746800706f5f63617272790073795f776f72640073795f73657400706f5f667265650073795f73616d706c650073795f6e65757472616c0073795f736c65770073795f766f69636500666d5f69735f6e617469766500666d5f73746f636b5f63616c6c00666d5f656d7074795f736f7572636500666d5f636c6561725f736f7572636500666d5f63616c6c5f646f6e650073795f636865636b0073795f6d6f6e6f5f6672616d6500706f5f6672616d65004d4143485f4f464600706f5f666164655f6672616d6500706f5f726174655f666f6c640073795f6d6f6e6f5f696e630073795f726174696f0073795f656e765f6b0073795f656e76646f6e650073795f6e6f64656361790073795f656e763100706f5f6d6f6e6f5f656e7600706f5f6d655f72656c00706f5f6d655f61746b00706f5f61746b00706f5f6d655f73746f726500706f5f72656c6b00706f5f6d655f72656c3000706f5f6d655f72656c310073795f646f6e650073795f616c6976650073795f6d6f6e6f00706f5f66696c6c0073795f7461620073795f6c6f6f700073795f67656e6400706f5f616e7900706f5f66696c6c5f61646400706f5f7469636b00706f5f746b5f646f6e6500706f5f746b5f7374657000706f5f7261746500706f5f726174653100706f5f726174653200706f5f726174653300706f5f726174653400706f5f72665f666f6c643100706f5f72665f666f6c643200706f5f72665f666f6c643300706f5f72665f7261746500706f5f72665f6f757400706f5f72665f646f776e0073795f676c69646500706f5f6c6567627974650073795f736c5f736e61700073795f736c5f75700073795f736c5f73746f726500706f5f66725f72656300706f5f63686f726400706f5f66725f737461727400706f5f766f696365735f6f6600706f5f66725f676f6e6500706f5f66725f676f6e653100706f5f66725f6d676f6e6500706f5f66725f6d676f6e653100706f5f68656c6400706f5f66725f6e6f6c6800706f5f66725f6c6800706f5f68616e646f76657200706f5f66725f706172616d7300706f5f737461727400706f5f66725f686f6c6400706f5f66725f646b00706f5f66725f726b00706f5f66725f616b00706f5f66725f7600706f5f66725f6e65787400706f5f66725f696e633100706f5f66725f656e766b00706f5f66725f656e7600706f5f66725f656e76686f6c6400706f5f66725f616d7000706f5f66725f72656c00706f5f66725f63757400706f5f66725f63617000706f5f66725f6761696e00706f5f66725f72656c3100706f5f66725f72656c316100706f5f66725f72656c3200706f5f766f6963657300706f5f667265653100706f5f666164653100706f5f666164653200706f5f63615f7069636b00706f5f63615f6672656500706f5f63615f6e65787400706f5f63615f74616b6500706f5f63615f636f707900706f5f73657100706f5f63615f677000706f5f63615f6700706f5f66665f7374657000706f5f66665f6c6f6f7000706f5f66665f6e65787400706f5f66665f3100706f5f66665f73746f726500706f5f73745f666f6c6400706f5f73745f666f6c643100706f5f73745f70616e656c00706f5f73745f6d3100706f5f6d72696e675f706f7000706f5f73745f6d646f6e6500706f5f73746172743100706f5f73745f6f757400706f5f73745f73773100706f5f73745f7377646f6e6500706f5f73745f73773200706f5f73745f73773300706f5f736e617000706f5f73745f316100706f5f73745f676f00706f5f73745f6473657100706f5f73745f646b657900706f5f73745f646b3100706f5f73745f64757000706f5f73745f64733100706f5f6d68656c645f6f6600706f5f73745f64733200706f5f73745f63686f726400706f5f73745f72656c00706f5f73745f72656c3100706f5f73745f6e7300706f5f73745f763100706f5f73745f726f773000706f5f7662756600706f5f766f6963696e6700706f5f73745f736861706500706f5f73686170657300706f5f676d617800706f5f73745f73696e676c6500706f5f73745f6e6f746500706f5f73745f63707265737300706f5f73745f6b00706f5f73745f636f756e7400706f5f73745f636f756e743100706f5f73745f63757400706f5f737465616c00706f5f73745f646f6e6500706f5f616c6c6f6300706f5f73745f7761726d00706f5f73745f656e7600706f5f73745f68696e6600706f5f73745f737461746500706f5f73745f6c69766500706f5f73745f6c6b657900706f5f63705f6f757400706f5f63705f6e6f00706f5f63705f6d69646900706f5f63705f636f756e7400706f5f6d68656c6400706f5f63705f3100706f5f63705f3200706f5f686f5f666f6c643000706f5f686f5f666f6c6400706f5f686f5f666f6c643100706f5f686f5f763100706f5f686f5f73696e676c6500706f5f686f5f636f6c00706f5f686f5f636f6c3200706f5f686f5f696e7300706f5f686f5f70757400706f5f686f5f617300706f5f686f5f657874726100706f5f686f5f667265736800706f5f686f5f6e65787400706f5f686f5f646f6e6500706f5f686f5f6e657700706f5f686f5f6e65773100706f5f6c6d5f73616d706c6500706f5f6c6d5f7061726100706f5f6c6d5f6f757400706f5f6c6d5f6c656700706f5f6c6d5f74776f00706f5f6c6d5f746872656500706f5f6c6d5f72657400706f5f736e5f72657400706f5f736e5f6469737400706f5f736e5f6c6f00706f5f736e5f646f776e00706f5f736e5f686900706f5f736e5f757000706f5f736e5f6f757400706f5f72616e6b5f61637400706f5f616c5f676f00706f5f72616e6b00706f5f616c5f6c6f6f7000706f5f616c5f6e65787400706f5f66696c6c3100706f5f66695f68616c6600706f5f66695f636c65617200706f5f66695f766f6963657300706f5f66695f766f69636500706f5f66695f6e65787400706f5f66695f6c6f6f7000706f5f66695f756e69747900706f5f66695f6f757400706f5f66695f73617400706f5f66695f70757400706f5f66695f7361747000706f5f6c666f335f646570746800706f5f6c33625f646570746800706f5f6c335f6261636b00706f5f6c705f646f6e6500666d5f6c666f5f636c6f6e6500706f5f6c705f7363616e00706f5f6c705f7363616e6e656400706f5f6c705f7363616e3100706f5f6c705f666d00706f5f6c705f636d7000706f5f6c666f6465736300706f5f6c666f5f6275696c7400706f5f6c705f6861766500706f5f6c705f636f707900706f5f6368726400706f5f666d745f63686f726400706f5f766f696300706f5f666d745f766f696300706f5f66763100706f5f66763200706f5f665f6400706f5f66635f736861706500706f5f66635f6e616d6500706f5f6e616d657300706f5f66635f637000706f5f66635f696e7600706f5f66635f656e6400706f5f7067646573635f62756600706f5f70675f6275696c7400706f5f70675f6861766500706f5f70675f636f707900706f5f70675f706174636800706f5f70675f6275696c745f6e6f7700706f5f70675f6279746500706f5f70675f6b6e6f6200706f5f6b6e5f706c61696e00706f5f6b6e5f726174696f00706f5f6b6e5f6a756d7000706f5f6b6e5f6f757400706f5f6b6e5f6f6e6500706f5f6b6e5f725f646f6e6500706f5f6b6e5f725f64657400706f5f6b6e5f725f7363616e00706f5f6b6e5f725f74616b650070765f6c6f61640070765f736f72740070765f64310070765f64320070765f70630070765f64330070765f64340070765f646964780070765f69310070765f69320070765f72310070765f72330070765f72320070765f62310070765f62330070765f62320070765f6232610070765f6f310070765f6f320070765f6f330070765f6f340070765f636f70790070765f70310070765f70320070765f70330070765f63310070765f73310070765f73320070765f73330070765f78310070765f78320070765f70633100706f5f6d6174636800706d5f6c3100706d5f6261736500706d5f703100706d5f703200706d5f703300706d5f693100706d5f7063313200706d5f653100706d5f74727900706d5f6b65657000706d5f666f756e6400706d5f653200706d5f653300706d5f7365636f6e6400706d5f6f757400706d5f6b5f6f757400706d5f6e6f00706d5f743100706d5f633100706d5f633200706d5f633400706d5f63326100706d5f633300706d5f63326200706d5f70633100706f5f6b725f6f757400706f5f6b725f736574746c6500706f5f6b725f6e657700706f5f6b725f616e7900706f5f6b725f6a6f696e00706f5f6b725f637000706f5f6b725f63703100706f5f6b725f66756c6c00706f5f6b725f777269746500706f5f6b735f6f757400706f5f6b775f726f6f7400706f5f6b775f726f6f743100706f5f6c6f636b7700706f5f6b775f72656d6f766500706f5f6b775f766f696300706f5f6b775f6f757400706f5f726c5f696400706f5f726c5f6f757400706f5f726c5f6b00706f5f726c5f68616e6400706f5f726c5f6e7564676500706f5f726c5f733100706f5f726c5f733200706f5f69735f6e6f00706f5f69735f6f757400706f5f736c6f745f6d61726b657200706f5f736d5f6e6f00706f5f736d5f7363616e00706f5f736d5f7363616e6e656400706f5f736d5f7363616e3100706f5f736d5f666d00706f5f736d5f636d7000706f5f736d5f6f757400706f5f706f6c79747261636b00706f5f70745f72657400706f5f70745f79657300706f5f70745f6f757400706f5f68645f6d69646900706f5f68645f6f757400706f5f68645f7363616e00706f5f68645f79657300706f5f6d68656c645f61646400706f5f6d615f7363616e00706f5f6d615f6f757400706f5f6d615f70757400706f5f6d615f736869667400706f5f6d68656c645f64656c00706f5f6d645f7363616e00706f5f6d645f666f756e6400706f5f6d645f6e6f6e6500706f5f6d645f6f757400706f5f6d645f6c61737400706f5f6d645f736869667400706f5f6d645f636f756e7400706f5f6d72696e675f7075736800706f5f6d7461696c00706f5f6d6865616400706f5f6d705f6f757400706f5f6d72696e6700706f5f6d715f6f757400706f5f6d6f6e5f73746f636b00706f5f6d6f6e5f72617700706f5f6d6f6e5f737461727400706f5f6d6f6e5f747269676c65737300706f5f6d6f66665f73746f636b00706f5f6d6f66665f6d6f726500706f5f72656c00706f5f72656c5f6f757400706f5f7265747269675f6f757400706f5f72746c6f6700706f5f7265747269675f6c6f6700706f5f73746f705f7400706f5f73746f705f7000706f5f73746f705f7600706f5f73746f705f763100706f5f6b696c6c5f7600706f5f6d675f7769646500706f5f6d675f73746f636b00706f5f6d675f6d61736b00706f5f6d675f7363616e00706f5f6d675f6e65787400706f5f6d675f6e6f6e6500706f5f6d6f675f7769646500706f5f6d6f675f73746f636b00706f5f6d6f675f6d61736b00706f5f6d6f675f6e6f6e6500706f5f6d6f675f7363616e00706f5f6d6f675f6e65787400706f5f6d725f73746f636b00706f5f6d725f72617700706f5f6d725f6c656700706f5f6d725f646f6e6500706f5f6d725f706c6163656400706f5f6d725f706f7000706f5f6d745f6f757400706f5f6d745f7261770053454c5f57494e004f43545f484f4f4b5f4f50454e004f43545f484f4f4b5f434c4f534544004f43545f484f4f4b5f52545300475249445f5245430048454c445f4d41534b0048454c445f5041474500545249475f4d4f444500545241434b5f435552005041545f4944580054524b5f535452494445005041545f535452494445004c4f434b5f4f46460042414e4b5f4449525459004449525459320045445f544944590045445f454e440045445f53454e540045445f464c4147310045445f574f5244530045445f574f5244360045445f464c4147320045445f43414c4c0052454452415700504c41595f534c4f5453004f43545f4a554d50004b45595f555000706f5f6f635f6f70656e00706f5f6f635f636c6f73656400706f5f6f635f706f705f636c6f73656400706f5f6f635f7377616c6c6f7700706f5f6f635f64697200706f5f6f635f62697400706f5f6f635f6e65787400706f5f6f635f6861766500706f5f706172745f7074636800706f5f6f635f6c6f00706f5f6f635f686900706f5f616d705f6b696e6400706f5f616b5f617564696f00706f5f616b5f6f757400706f5f616d705f6465736300706f5f61645f73746f636b00706f5f616d70646573635f62756600706f5f616d705f6275696c7400706f5f61645f6861766500706f5f61645f636f707900706f5f6c656700706f5f666d745f6c656700706f5f6c65676d617800706f5f6c785f6f757400706f5f666c3100706f5f6c65676e616d657300706f5f666c5f637000706f5f6c65676b6e6f6200706f5f6c6b3100706f5f6c6b3200706f5f61655f6f757400706f5f616c5f73746f726500706f5f616c5f73616d706c6500706f5f626563616d6500706f5f62635f6f757400706f5f62635f736c6f747300706f5f666c65785f736c6f7400706f5f62635f726573657400706f5f66696e655f726573657400706f5f6c735f63616c6c00706f5f6c735f6f757400706f5f6c735f747261636b00706f5f6c735f6e65787400706f5f665f7300706f5f656e640073795f72656e64657200706f5f6c666f3300706f5f6c666f336200706f5f6c666f7061676500706f5f6d6f6e00706f5f6d6f666600706f5f6d6761746500706f5f6d6f6761746500706f5f6d72656300706f5f6d7472696700706f5f616d70737461676500706f5f616d70647261773100706f5f616d70647261773200706f5f616d70647261773300706f5f616d706564697400706f5f616d706c616e6500706f5f61737369676e00706f5f6d61636877696e00706f5f6d6163686c69737400706f5f6c6f616473656c00706f5f73746f7000706f5f6b696c6c00706f5f72657472696700666d5f736f757263655f646973706174636800666d5f737461727400000000000000000101000029100000000400000101000011ac0000000800000101000013a60000000c000001010000201c000000100000010100001996000000140000010100001e300000001800000101000031f20000001c000001010000192a002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e62737300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000001000000060000000000000034000049aa000000000000000000000004000000000000001b0000000400000040000000000000916c000000600000000500000001000000040000000c00000026000000010000000300000000000049e000000000000000000000000000000004000000000000002c000000080000000300000000000049e0000000000000000000000000000000040000000000000001000000020000000000000000000049e000002ca000000006000002b10000000400000010000000090000000300000000000000000000768000001aeb0000000000000000000000010000000000000011000000030000000000000000000091cc0000003100000000000000000000000100000000", - "sha256": "872225a8616115b756941c86ee36e3d5d4d41ee8d728c7e75a1dccba7fe1332d", + "bytes": 42244, + "code": "7f454c46010201000000000000000000000100040000000100000000000000000000a3c40000002600340000000000280008000700000000000000000000000000000000000000000000000000000000000000004fefffd048d77cfc247980001c80242f00342602ef8b47fa3c04d7c32879800062a878007210b2af00406600023041f946104d0c41f028000810000467000194610002ce538066000168610027f86600006e263c000000a84c02380041f9800049d8207038082608670001460483100b14f00c8300024640640001362248263c000000ff7398670000140c810000002f66000004224853836600ffea323c464db25166000004548941fa34e2760573987b99b285660000fa53836600fff272484c02180041f98000081073b0180d0c810000007f67000026610004ec6600001e41fa383473f01a0041fa4b0a202800084c001000ee895281600000047200274100584a6b00266700000e610005026700002e61000d1e429342ab000442ab002042ab0018426b002642ab0014422b0051223c010000002741000860000012177c00ff0051223c01000000274100082079800062a442a8000472484c02180041f98000081073b0180855810c810000000253c1174100506600001861000c78610029bc41f9400d2cb0423028006000000e4a6b00266700000661000c986100070e2206e189e9892741002876016000002061000c06426b002641f9400d2cb04230280073d4e189e989274100287600174300244a2b00246700007c41f98000081072484c021800423018312239800000e0028100000001e189e3892002ed88d28041f980000110d1c14250317c7f000002317c7f0000047bec000673d4e08927410070610006884a2b0050660000066100074638bc4000397c7f0000067801610000b843eb005c22d822d822d822d822d86000000a7dd46100071e3886610025e66700004a202f004090af003c2480256f003c0004223c0400000025410008222f003ce189e189e189e5892541000c41ea0010d0806700000a429853806600fffa23c880001c8070006000001c2f2f00402f2f00402f2f00402f2f00404eb9400040084fef00102f40002c4a84670003bc3886394500066100002043eb005c20d920d920d920d920d94a2b0050670000466100071e60000394223c000000a84c02180041f9800049d8d1c141e800404e75207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008eda271b028004e7561000bc6610005a20c80730000006300000c700042ab001842ab00142740000c73ec0002e089e48941fa326473f01a00e0804c0108002740001073ec0008203c000002044c001000e0892741001c73ec000ae089707fb280640000484a816602720124014c021000203c002ed1e04c4100000c80000fffff6f000008203c000fffff222b00080481001000006f00001ee089e8894c001000e08993ab00086000000c223c010000002741000873ec0004203c00000a444c001000e089242b0008e08ae88a4c021000e089e88992ab001471ab00516700002624010c80000000ff6600000e2200202b000c61000230200122024c40180153801740005174104c4218013741007e242f00346100000660000246222b001870484c02080041f980000810d1c0082b000100246600005c202b0058670000165380274000586600000c08eb00010024600000400c81000080006c00007471a8000c41fa328871f00a00206b000c2f09227c000080006100005e225fd2800c81000080006f00004a223c000080006000004071a8000e41fa335471f00a006600000e082b000200246700002671d020414c001000e089e0896600000472012008908122000c81000000406e0000047200274100184e754feffff448d7000e2408760de6aa0c820000008064000008243c000000800c8200001000640000520c820000080064000034263c000080004c4230030c830000002064000020d6830483000000100c8300000020630000047620243c000080004c43200226094c032000760fe6aa5282b4806400000420027420b0826500003e2400e48ad082e68ad082760fe7a926094c431001263c0000800096816a00000476004c013000720fe2ab4c003000e08bea8be488d0836600000470014cd7000e4fef000c4e752f0141f9400d2cb071b02800670000340c80000000806400002841f9460d171db03028006700001441f9400d2cb0538022302c080101670000087001600000047000221f4a804e7572484c02180041f98000081073b0180855810c81000000026300002061000280202b0028e080e88090866a00000444800c80000002806400000670004e7570014e750c81000000ff66000030223c100000004a80670000224c4010010c8100000010650000140c81000000fe6300000c223c000000fe4e7572104e754a2b002467000130242f0034263c000000a84c023800610021a26600002441f9800049d84a30380066000016426b00264a2b005067000100610006f4600000f87faa0003670000f043ea00104a2b00506700000a426b00266000135a2f0a20132c2b00042a2b000c246b0010286b0020222b001875eb00269282760fe7a9e8812c41e7aa2a4241fa33067618220c4c2b1800001cd2862401e6aad48279702800757028029484e08973c14c012800e082e082d8822844736b007ed2ab0014274100144c014800d8802404e6aad48273702800757028029481e08c79c44c042800e082e082d282dbce240d4842c3c2e589e281424122c122c1d085dc8a53876600ff8c268027460004274c0020220d760fe6a16a000004720037410026245f242f0034610009a66700000e7faa000343ea00106000127a202f002c4cd77cfc4fef00304e7541fa442423c8400d2cd852a8000c73f9800065b2e18977b9800065b68283b2a80010670000402141001052905283b6a800046f00000621430004e089b2a8001867000022214100182228000c260196a8001c2141001c0c83000000106d000006214300084e752006a3430c4040006f00000a048000003c0076002200ea8041f9400aa29441f00c00781be9a92818e289a104a8984901a801080079ac001b6600002a28050c447f006c000020a1c04844e58c6400000ae28c44846000000ae28c068480000000a8000900a1c0e6a0223c0184cbb7a0010800a1c0eb884e757dd40486000040002006e58edc8071ec0006048000004000223c00000ccd4c010800e080e080dc800686000040004e752f052f062a3c00007f007e000c8600007c006f00000e048600003c0052876000ffec0c86000004006c00000e068600003c0053876000ffec0c87000000066f00000870ff6000001a6100ff0e4a87670000106b000008efa8600000064487eea02c1f2a1f4e7571b9100b14ed4e75207946c8245671b9100b14cf263c000018b24c030800d1c02602eb8b96829682d1fc0008f07871b038054e756100ffca22002006e188e9884a816700007c2e006100ffbe260020074a836700006c767f96812e3c00000d804c0730002203484173c177c32e3c000001e04c073000484377c341f9400aa31426303c00e08be48b2e3c000017c74c0730007e189e81eeab222b002890816a0000124480e0884c030000e088928060000012e0884c030000e088d28060000004220027410028e081e8812c014e754feffff848d70c002a4b2b46004841f9400d2cb820302c00222d00342b400034c1ad00744ab9460d172a6600001241fa3fe02602eb8b42303801423038034680c280670000346100071e71ae00240c80000000016600001871ae00256700001053800101670000081d7c000200244dee0040bdca6600ffd4610006ec71ae00240c80000000016600001e71ae00250c80000000806500001061001f2a660000081d7c000200244dee0040bdca6600ffce7fad007c67000030202d0048b0ad00306600001671ad007d52801b40007d0c800000000865000010422d007c61000b7a2879800062a841f946104d0c41f0280008100004670000066100067c73ec0002e089e48941fa2aac73f01a002b41004073ec0008203c000002044c001000e0892b41001c73ec000ae089707fb2806400002c4a816602720120014c001000203c002ed1e04c4100000c80000fffff6f00000e203c000fffff6000000470002b40003873ec0004203c00000a444c001000e0892b41004470484c02080041f980000810d1c071a8000e43fa2c7c71f10a006600000e082d000200246700000471d12b40003c71a8000c43fa2b5c71f10a004c2d00000054e088ee886600000470013b400052610005a04a2e0024670001be264e2c2d00489cad002cdcae002c6100fd866100fce80c80730000006300000e422e002442ae0018600001922d40000ce0804c2d080000402d400010202d003867000022222e00080481001000006f00001ee089e8894c001000e08993ae00086000000c223c010000002d410008222d0044202e0008e088e8884c001000e089e88992ae001471ee003e670000262f010c80000000ff6600000e2200202e000c6100f9ce2001221f4c40180153803d40003e70104c4018013d410026202d001c2d40001c202e00346700001253802d400034660000081d7c00020024222e001871ae00240c80000000026700006e0c80000000036700003c202d0054b2806c00007c77ed00524feffff448d703012240206e000c20036100f80826004cd703014fef000cd283b2806f00008e220060000088202d0054e6884feffff848d70300226d0054206e000c6100f7d64cd703004fef000826006000001c202d003c26014c003000e08be08b6600000a4a8067000004760192836a0000047200b2ad00546f000014202d00542600e68b9283b2806c00000422000c81000000406e00001a720071ee003c0c80000000406e00000a422e0024610003242d4100184dee0040bdca6600fe34202d00482b4000304cd70c004fef00084e7541fa3a622002ed88d1c0421042280010422800204228003041fa324a2002e188d1c070044228002442a800184268003c41e8004053806600ffec4e7541fa3a262002ed88d1c0421042280010422800204228003041fa320e2002e188d1c070044a28002467000008117c0003002441e8004053806600ffea4e752f0341fa31e62002e188d1c02248263c7fffffff70044a2800246700001e22280018b283640000062601224841e8004053806600ffe260000004224820496100013e204b700922d853806600fffa43e9ffdc137c0003002442290025202b002823400028e080e88090ab0030d0ab002c2340002c42a9003441fa3b7052902350003041fa3d3e2028000c2340003871eb00260c8000007fff63000008203c00007fff3340003c202b00180c8000007fff63000008203c00007fff2340001842ab0018426b0026261f4e754fefffe848d7031b41fa31162002e188d1c0262b0054e68b0c830000080064000008263c0000080070044a2800246700005e2f0028082028000c222b000c6700000ab08163000004200120402244222900182003227c000080006100f5a820442800201f92846a00000472000c81000000406e000016720079e8003c0c84000000406e000006422800242141001841e8004053806600ff944cd7031b4fef00184e75200843fa307a9089e48843fa3872d3c04e752f096100ffea4211225f4e75117c0003002442a800344dfa3a54529621560030610001a44a2e00246600000c204e4268003c70014e754dee0040bdca6600ffe62f096100ffaa2368002c000423680028000870ff1280225f70004e75203c000005004c030800d0ad0048610008fa28006100015471ae0024578066000034202e0028e080e88090846a00000444800c80000002806400001a4a6e003c67000012204e2f096100ff484a11225f670000104dee0040bdca6600ffbc70004e7570014e752f096100ff2871910c80000000ff660000342028002c21690004002c2340000421690008002823680034000842a800341368002500014228002512a80024117c00030024225f4e754feffff048d70303204e6100fed871916700007e0c80000000ff670000721d4000241d69000100252d690004002c2d6900080034202d004890ad002cd0a90004e188e9882d400028429642ae000442ae002042ae0018426e003c42ae0014426e003e203c010000002d40000841fa3acc2028000c2d40003871ae00256700001061001898660000081d7c0002002442114cd703034fef00104e7541fa2ec62002e188d1c071a8002473a80064808173a800a4808173a800e480814e754dfa2ea42002e188ddc045ee01004e756100ffee222d003092ad002c41fa36882002ed88d1c04a2e002467000010d3ae002c4a1067000006d3a800044dee004041e80010bdca6600ffde222d00482b41002c41f9400d2cb07fb02800423028000c8700000080650000362f2d00482f2d0070610019186700001e9097223c000005004c010800d0af00042b400048610000826000ffde588f2b5f00484e752f07610000702e1f4a87670000662f2d0048202d0034222d00744681c0816700004e7e000f006600000852876000fff6220704810000000c7139400d2cdc760c4c030800d28006810000004092ad0070203c000005004c010800d097610006a42b40004852876100000c6000ffa62b5f00484e754a876700001a0c87000000806400001020075380222d007401c12b41007470484c02080041f98000081071b0080be488660000b86100feb841fa39322628000c28280008e28c4a876700005271ae00240c80000000016600003671ae0025b087670002d44a8066000026202d0048b0ae002c6600001a200390ae0038b0846400000e1d47002542ae0034600002aa4dee0040bdca6600ffb66000005041f9400d2cb84ab02c006600000a610016926700003a71ae00240c8000000001660000224a2e00256700001a202d0048b0ae002c6600000e200390ae0038b084650002584dee0040bdca6600ffca4a87660000246100fdfc71ae00240c8000000001660000081d7c000200244dee0040bdca6600ffe4700073f9400d2ca80c8100004ef9660000084eb9400d2ca82b40004c70484c02080041f9800008107db0080853860c8600000003630000047c00528671b0080b2600e48b6700001c22000281000000032003260643fa373061000c4876016000000643fa220441fa230020306c002b4000544a836700000a6100f9e06000004e610001a46600019e760196866100fd4e204e70044a28002467000004528341e8004053806600ffee4a836f0000206100056c6100fb62117c000300244dfa35c452962156003053836000ffde77190c83ffffff806700014e6100fb986600006a6100054a6100fb304a68003c67000036203c000005004c030800d0ad00486100047c22280028e081e88192806a00003a44810c81000002806500002e6100fb0467000036429042a8000442a8002042a8001842a800144268003e203c010000002140000860000012317c00ff003e203c0100000021400008203c000005004c030800d0ad0048610004142140002ce188e988214000281147002542a800344dfa36da202e000c214000384a876700000c0c87000000806500003872484c02180049f98000081073b4180d0c810000007f6700001e49fa23c673f41a0049fa369c282c00084c041000ee895281214100342879800062a870014a87670000300c8700000080650000162f0020076100144c201f6600001870026000001222075381282d00340304660000047002114000244dfa34705296215600306100fabc53866600feaa4e7570004a876700008441fa362a2228000c200190ad00782b4100780c800000019d620000660c87000000806400001620075380222d003401814a816700004c6000001241fa35202002e7884a300801670000386100fb62720071ae0024670000160c8000000001660000204a2e00256700001852814dee0040bdca6600ffdcb2866500000670014e7570004e752f082f0141fa25ca2202ef89d1c11140007c4228007d221f205f4e750c87000000806400001020075380222d007401c12b4100746100faf4222d003092ad002c4a2e002467000006d3ae002c4dee0040bdca6600ffec222d00482b41002c70484c02080041f98000081077b0080b7db0080853860c8600000003630000047c0052862003e4886700012c2203028100000003260643fa343e610009564fefffec42af00106100fa8471ae00240c80000000016600002e222f00104a816700001c20771cfc2028002cb0ae002c6f00000c2f881c0053816000ffe22f8e1c0052af00104dee0040bdca6600ffbe760043fa33e4b6866400009e7119223c000005004c010800d0ad0048610001c0282f0010b6846400001620773c002140002c1147002542a800346000005c2f006100024c6100f832201f42a8000442a8002042a8001842a800144268003e223c01000000214100082140002ce188e988214000281147002542a800344dfa343e202e000c21400038117c000100244dfa325452962156003052830c83000000046500ff646000001c282f0010b6846400001220773c00117c0002002452836000ffe84fef00144e756100f97493c9780071ae00240c800000000166000010202e0030b084630000062800224e4dee0040bdca6600ffdc20096700fa56202d0048610000dc2340002c1347002542a900344e7561000f46670000a84feffff048d7011a207946c8245671b9100b14cf223c000018b24c010800d1c02202e7892001d281d280d1fc0008ee9a77b0180279b0180591fc0008ee9a2202eb8992829282d1fc0008f07871b0180555830c83000000026300000e4a806700003a7001600000340c800000000264000008700260000024e48c6600001c41fa234a2202ef89203a332690b018780c800000019d6300ffdc70034cd7011a4fef00104a804e754feffff848d701086100f0484cd701084fef00084a806700000470014e75282d004c670000984feffff82e822f4600042c00048600004000068600025a80243c000005004c4260060686000004382206740c4c4210012401d482d481e58a9c82240672002c029c816a00000806860000000c0d046600002e2c02dc810c860000000c6d00000804860000000c0d046600001652810c81000000066f00ffc872006000001044812c3c000005004c016800d08624172c2f00044fef00084e754feffff048d7064843fa00746000000e4feffff048d7064843fa00604dfa26482002e188ddc045fa2e3ee488d5c0204e78ff7c0471ae002471b108004a12670000047003e188e188e188e988262e003002830fffffff8680b684640000062803204e4dee004045ea001053866600ffc64cd706484fef00104e7500020100030201037801600000047800242f00344fefffe82e892f4700042f4b000c204920074a84660000104290508853806600fff8600000102210e2812081508853806600fff46100f6fc2f4a00084a2e0024670000cc264e22572e2f000420132c2b00042a2b000c246b0010286b0020736b00262f410014426b0026222b001875eb003c3741003c9282484142414c4718012f410010484242422a4241fa1f487618220c4c2b1800001cd2862401e6aad48279702800757028029484e08973c14c012800e082e082d8822844222b0014d2af0014274100144c014800d8802404e6aad48273702800757028029481e08c79c44c042800e082e082d282dbef0010240d4842c3c2e281d3915089d085dc8a53876600ff8e268027460004274c00204dee0040bdef00086600ff2422572e2f00042211d28169000016424122c122c153876600ffee4fef00186000ec426b00000c223c800000006000ffe0223c7fff00006000ffd6610000304ef940003cac220c048180004858660000140c82000000026600000a2f086100ec12205f610000084ef94000d04671722a1243f04a000c82000000026600001c220c048180004858e689ef8941fa20324a3018246700000470004e75203c400d37f675850c820000000166000112243c000018b24c012800d4892f002042200361000bd44cd700014fef0004660000722803e58cd883d4842042d1fc0008f04b75900c820000007f620000d4283c000004484c0248000684100b14f020442248283c000000ff7b98670000140c850000002f66000004224853846600ffea3a3c464dba5166000004548941fa190478057b987599ba826600008653846600fff243fa308a4a3a30826600007241f9400d37f6283c0000019212d853846600fffa43fa306a41fa18cf235800343350003841fa01aa234800de41fa18c1235800223350002641fa016c234800d2780123440072134400607804234400a242a901322829018e0284ff0ff0ff0084005005002344018e41fa301210bc000120094ef940031ed641f9400d5f3824700c00b5fc400d31ae6600002042a72f014eb940031da4508f0c80400d2e8a6700000a4a806700000424404ef94003c05845f9400d37f670012f002f014eb940031da4508f41fa2fbab1c06600000424484ef94003c0584feffff848d7005024030c820000001d63000004741d41f9400a72a86100006e2e002f0542a74eb94003249c508f260061000074280072014a836a00000672ff44834a836700002a2407d4816b000022701db4806e00001a41f9400a72806100002c01046600ffe42e0253836000ffd4240741f9400a72806100001226004cd700504fef00084ef94003932c20027c064c4608002c300c009c802006d080d086d080d0824e7570012f0071b9100b14cc2f004eb940031da4508f41fa2eeeb1c06600000a203c000009004e7570004e75202f000853800c800000000363000004700052802f00487a17582f2f000c4eb940013a084fef000c4e75207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a71b9100b14cce7882200d081d08171b0080255800c80000000026300000a700072006000001c202f00082200028100000003e48802800000001f660000047200e58841fa17a841f00800226f000470044a106700000a12d853806600fff44a816700000a06810000003012c142114e754feffff848d70c0045fa2fac4a3a2fa66600004e41f9400d31ae264a223c0000019216d853816600fffa71596d00001673d9205947f2080016d853816600fffa6000ffe841fa0026220847ea012a700626c153806600fffa41fa2f5a10bc0001200a4cd70c004fef00084e754feffff448d7001c242f0010222f0014202f00184ab946c7dd26670000284ab9800000a06600001e0c82000000016700003e41fa009077b028004c031800d0816000007826020683000000382043d683d688e78b41f946c7d8ee4ab03800670000082601e7899283d0816000004ee48802800000001f76014a816700003c6e000006448344812400d4830c820000001f620000260c82000000036d00001441fa15b079f02a000284000000ff6600ffda200253816600ffd0e5884cd7001c4fef000c4e750c00100a10104fefffd048d70cfc45ef002041fa15b841f00c00740018302800158428001582280452820c82000000046600ffea6100015674017a007d322800610001a82e067d3258006100019ebe866600001241f228041c1008c6000710866000000a5285ba826600ffd252820c82000000046600ffc4610001502a01ba84650000089a846000fff67db258087f3268007400b485640000207db2280841f268007d1006860000000cbc876f00fff6108652826000ffde610000d26100010c7daa00087f3268007a0074004a3228046a0000282c05700cbc846500000e9c8406800000000c6000fff07db268087d326800dc8015862800528552820c82000000046600ffc870007a0074007db22804028600000003bc806700001252820c82000000046600ffe66000000e7d3228009c871586580c52855280ba836500ffcc220353816f00002874007d32280c7132280db0866c00000a1580280c1586280d5282b4816600ffe453816600ffdc74001c32280c138628005282b4836600fff220044cd70cfc4fef00304e7578037a007d3258007f325801be866c00001a15875800158658011c3258041e325805158758041586580552850c85000000036600ffd053846600ffc84e75780074004a3228046b00000815824808528452820c82000000046600ffe84e750c860000000c6500000c04860000000c6000ffee4e754fefffc848d71cfc45ef002477ab000141eb000c740010302800158028005282b4836600fff2220353816f00002874007db2280071b22801b0866400000a15802800158628015282b4816600ffe453816600ffdc7d927400780071b228009086158028006100015a01c45282b4836600ffea7bb228ff700928407e007c01610000766100005852860c86000000206600ffeeb9fc00000009660000307c017e01610000546100003652870c87000000046600ffee52860c86000000206600ffe0b9fc0000000967000060202f002c22000281000000ffe088600000dc4a8067000014b08c6400000e28402006e18880872f4000304e752006220743ea00046100fd06be806400001e2200740010322800b03228046600000e5282b4836600ffee20014e7570004e7542af002c203c7fffffff2f4000307c017e002006220743ea00046100fcc2be806400005228407400720071b228046100006801c15282b4836600fff0b2846600002871b2380390856a0000044480e788d08cb0af0030640000102f4000302006e18880872f40002c52870c87000000046600ffa052860c86000000206600ff92202f002c22000281000000ffe0884cd71cfc4fef00384e750c800000000c6500000c04800000000c6000ffee4e754fefffd448d77cfc2a002c0a2803207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2002e7882200d081d08173b0080255810c8100000002620000fc548171b00805e488660000f047fa25c02002eb88d7c01741001d49fa27862e2c000c610000e24a856a0000b471ab0001670000c8220792ab00040c810000019d620000b841eb000822007198610005346600000c53816600fff26000009e71ab00012f0041eb000843eb00084beb000c4deb000c72007198b08467000010610005026700000812c01cd55281528d53976600ffe4588f422b00030c81000000046400002412841c8652811744000327470010203946c7e956274000142014274000181746001c1741000127470004610000544cd77cfc4fef002c70014e75168527470004174400081746000c177c00010001422b0002422b00034cd77cfc4fef002c70004e754a2b000367000016200790ab00100c800000008a65000006422b00034e7577ab000141eb000c787f20037398b28464000004280153806600fff220047200610001d00c8300000002650000306100fc904a8067000026e5888081720b610001b271ab001db0836300000420037208610001a0177c000100024e754a2b00026700001e203c000000ff720b61000184203c000000ff720861000178422b00024e754fefffcc48d71fff28000c840000008064000004528447fa24102002eb88d7c07bab0003670000dc49fa25d22e2c000c6100ff2e4a2b0003670000c8ba84670000c271ab00015380670000b841eb0008b8186700000c53806600fff6600000a471ab0001538017400001422b00036100ff0e4feffff82e82202b00142f4000044eb940042d1c4fef00084a806b0000742c0061000078168617450008102b001c1740000c177c00010001422b0002202b00102740000471ab001c7200610000b20c85000000806400003a206f002020086700003072034a2800026700000e508853816a00fff2206f002020055380108011460001117c00010002202b0018214000044cd71fff4fef00344e752002223c0000048d4c01080073b980000004263c0000476c4c031800d081d08606800000044d2600d68341f91001614ed1c332100281ffffe07f308173b980000002263c0004d9a04c031800d081d08041f9400e21e0d1c032100281ffffe07f30814e754fefffec2e822f4100042f40000871932f40000c203946c7e9562f4000104eb9400421584fef00144e75610000b0660000584feffff048d7030a207946c8245671b9100b14cf223c000018b24c010800d1c02248d3fc0008eda271b1280053806600001a2002e588d082d1c0d1fc0008f04b71906100011a6000000470004cd7030a4fef00104a804e754feffff848d702022248d3fc0008eda273b1080053816600002cc0fc001e2248d3c0d3fc0008edbc73d10c810000464d6600001273a9000253816600000870016000000470004cd702024fef00084a804e754feffff848d70102207946c8245671b9100b14cf223c000018b24c010800d1c020026100ff8a4cd701024fef00084a804e75205c4feffff848d7000524036100ffc06700000641fad7744cd700054fef000826c8487800104ef94000d51c4feffff848d70104242f000c6100ff9467000036203c000000a84c02080041f9800049d8d1c0203c000001004a1067000004700010bc00ff117c000100144cd701044fef00084e754cd701044fef00084ef94000f4504feffff048d7030a0c800000007f6200005c223c000004484c0100000680100b14f020402248263c000000ff7198670000140c800000002f66000004224853836600ffea303c464db05166000004548941fa0c42760571987399b0816600000e53836600fff270016000000470004cd7030a4fef00104a804e756100fe1e6700004c2f012f08207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2202e7892001d281d28071b0180255800c8000000002630000087000600000047001205f221f4a804e7543fa215c2002e788d3c04a114e754feffff448d702030c800000008064000014538043f9400d2cb822312c0001016000001e6100ffcc20177208b0196700000e53816600fff672006000000472014cd702034fef000c4e754feffff048d703096100ff9e204976087190b081670000204a8067000018528853836600ffec2049760710e8000153836600fff810814cd703094fef00104e754feffff448d703086100ff5e204976087190b081670000164a806700000a528853836600ffec70ff6000002653836700000c10e8000153836600fff84210700076084a196700000a528053836600fff44cd703084fef000c4e754feffff048d7030941fa212a77b028002003528002800000000743fa2110b03128006700001c43fa2084d683d3c32602e98bd3c31281261713430001118028004cd703094fef00104e7541fa20e073b0280043fa20e07e00b23128006700002443fa204a2002e988d3c0d3c1d3c17f9171a900015281028100000007118128004a874e7543fa20ae71b1280041fa209e118028004e756100fc426700009871930480000000146a0000047000224412802040739300810000008043fa10a82002ef88d3c0422900256100fe44670000466100ecc0080000006700003a6100fe880c80000000036600000820016100eaa643fa10722002ef88d3c0137c0001002520352c000080000001192b802c004ef94000e7546100fe5020086100fee443f9400d2cb013812800721d2b812c004ef94000e754721d2b812c004ef94000e74c6100fb986700003e73930081000000806100fe546b00002e2f002f08200191c86100f9de205f201f22006100fd4e670000144a81660000084ef94000dfdc4ef94000dff873107113b0816600fff24ef94000dfdc266f00722031bc00080000026600001c240bef8a41fa0fbad1c20828000000246700000808e8000100244ef94000b522226f00724fefffe848d70c1e28090c84000000086400008a73b0980045fa1f7a52922404ef8a47fa0f78d7c226012401028200000030670000184a2b00246700003272301181980052aa000460000024240102820000000f670000464a2b00246700003e0281000000f01181980052aa0004242a000852aa000802820000001fe78a45f22810243a1efc248275ab0024e18a8481e18a8483e18a8484254200044cd70c1e4fef0018103098004ef94000c63c4200138088004feffff848d7020424012001ef8841fa0ed8d1c04228002442a800184268002642a800586100e05e41fa12be2002e188d1c070044228002542a8003441e8004053806600fff04cd702044fef00084ef94000686271930c80000000486500000c0c80000000606300004c080300086700001675b9800000006100f9e4670000364ef94000e6e22f017200740705036700000c6100f9ca6700000405c153826a00ffec4a816700000c2601221f4ef94000e6e2221f200655807405b4804ef94000e45a0c82000000486500000c0c8200000060630000664feffff448d70007080400086700001e75b9800000006100f970670000404cd700074fef000c4ef94000df9e7200740705046700000c6100f9506700000405c153826a00ffec4a816700001228014cd700074fef000c4ef94000df9e4cd700074fef000c55807605b6804ef94000de16202a000423c046c7e9564fefffe848d71c1c24016100f902670000b277aa000228030484000000146a0000047800008300000080244447fa0d6a2002ef88d7c04a2b00256600002870ff6100f5464a806600006c4fefffec2e82203946c7e9562f4000044eb940042d1c6000001c422b00254fefffec2e82203946c7e9562f4000044eb94004271c4a806b00002e28002f40000c2f4a000842af0004203946c7e9562f4000104eb9400421584fef001420046100f4de600000064fef00144cd71c1c4fef00184ef940062d1c4cd71c1c4fef0018202a00044ef9400625ea752a0002204241f02c002f0224016100f82a241f4a806700001820020480000000146a000004700006800000016420404ef9400626344ab9400bebae6600014c4ab9460d17366700013c4a79460d174a670001324fefffd048d73cff75b9100b14cc6100f7d64a806700011220175380660000fc760c0c81000000336700000444837ff9460d174a7c000d07670000862006d0b9460d174ceb882202283c0000091a4c041800d08173b9100b14d0283c00008ed84c041800d081247946c82456d5c045ea005947f9100161a7d7c073920c81000000ff66000006610000b4d2836a00000472000c810000007f6f000004727f14811681207946c82456203c0009b33272012181080023c1100f85984eb940027e0052860c86000000106d00ff6c2f024eb94009da20588f700123c0460d173a41f9460d1a9e4250426800024268000442680006426800084279460d10dc23c0460d17504eb9400418e04878ffff4eb94004d948588f4eb940027de44cd73cff4fef00304ef9400520064cd73cff4fef00304ef940051ff64ef940051fd6207946c8245671b9100b14cf223c000018b24c010800d1c02208d28206810008eda222417191d0802200d080d280d1c12202eb8992829282d1c1d1fc0008edaa73904e7570000c82000000076600000c4a3980000034660000086100f64e54804a804e752f0275b9800000006100ffd6241f4a80670000702f0043fa1eec4a3a1ee66600005841f9400d3988223c0000019212d853816600fffa43fa1ecc41fa0415235800583350005c41fa0066234800f6223c40046d9c23410126422900692229018a0281ffff0fff0081000010002341018a41fa1e9010bc0001201f234000c620094e75203c400d39884e754feffff048d7030675b9800000006100ff4653806a00000470004cd703064fef00104e756100ffda2200202f0008b081630000042001e78841fa0393d1c0226f000412d86600fffc4e756100ffb42200202f000cd0af00086a0000047000b0816f00000420014e756100ff0c2f004ef940059d5c6100ff00204049e8003a47e800e24ef9400368666100feec20402f28018e2f28018a4ef9400368b86100fed820402f28018e2f28018a4ef9400369520680400d398822402069012a0c820000000b660000162f0224046100fe8a241f4a806700000641faff724ef94003ae4c41f98000083cb5fc00000005660000242f022f002404e68a6100f4c267000010201f241f423008004ef94003af14201f241f118208004ef94003af14d1fc0008f04a719010814feffff048d7001e26002801202f0018242f0014220b4eb9000000004cd7001e4fef00104ef9400795c2d1fc0008eda277104fefffe848d7031720032401220476ff6100003e4cd703174fef001877104ef94007981ed1fc0008eda279104fefffe848d7030f20042239460d5c3076ff610000104cd7030f4fef00184ef94005a8504fefffd848d703ff53816600002c4a836a0000066100002c2e0020046100f55c6700001653876600000c20036100f54c66000006610000384cd703ff4fef00284e75207946c8245677b9100b14cf283c000018b24c043800d1c32602e58bd682d1c3d1fc0008f04b779028034e754feffff048d70303207946c8245671b9100b14cf223c000018b24c0108002202eb8992829282d28006810008edb370401180180043f91001614e1380180072484c02180041f980000810118018032202ed8941f980000a50d1c1317c400000062202eb8941f980000db442b018004cd703034fef00104e754feffff048d700e47fba1be141fa1bdd42102f2f001c2f2f001c2f2f001c4eb940013a084fef000c7001b0846600008820036100f460b0876700007c2a007e002c037400207946c8245671b9100b14cf223c000018b24c010800d1c0200206800008eda271b008005380660000362002e588d08206800008f04b71b00800b086660000207001223c000000ff4eb9000000006600000e200572014eb9000000007e0152827008b0826600ff9a4a8767000006610000304cd700e44fef00104e75202f000453806700000870006000000a202f00086100f3be41fa1b1110804ef94002528873b9100b14cf700003c0207946c82456d1fc0009504873908280108143f9100b145e739182801281207946c82456d1fc0009b3327001208023c0100f85984ef940027e0053594e5448434852440000564f494300004c45470000004f464600000000004d4f4e4f00000000504f4c5900000000257300256400000040008000c0010001030140016a018001c002000203028003000380040004030480050005800600068007000780080009000a000b000c000d000e000f001000008080800003070c0004070c0002070c0005070c000a0307000a0407000b040700050a07000603090002040700020307000e04070009030700090407000a06030006030c0008040c000a0e03000a0e04000b0e0400090e0400050a0f00050c1100070c13000c182400030c0f00040c10000a0c16000b0c170007100c00070f0c2d2d2d2d4d494e004d414a0053553200535534004d493700444f37004d4137003753340044493700414432004d443200414439004d4936004d4136004d37350044494d00415547004d493900444f39004d4139004d3639005155410034544800355448004f435400334d4900334d4100374d4900374d41004d4153004d495300000080000000800000005a82000049e700004000080008000800080008000800076806d4064c05ce055a04ef048d043203de03910349030802cb029302600230020501dc01b7019501750158013d0125010e00f900e500d300c300b400a60099008d00820078006e0066005e00560050004a0044003e003a00350031002d002a002600230021001e001c001a001800160014001200110010000e000d000c000b000a000a000900080008000700060006000500050005000400040004000300030003000300020002000200020002000200010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100014de54de54de54de54de54de54de54de54de54de54de54de54de54de54de54de548e5440b3f753b21370d33382f9e2c3f29162622235f20cc1e651c291a141824165714ac131e11ae10580f1c0df60ce60beb0b020a2a096308ab0800076306d1064b05ce055c04f20490043503e20395034e030c02d0029802640235020901e001bb01980179015b01400127011000fb00e800d600c500b600a7009a008e0083007900700067005f00570051004a0045003f003a00360032002e002a002700240021001e001c001a001800160014001300110010000f000d000c000b000b000a00090008000800070006000600060005000500040004000400030003000300000001000100020003000400060008000b001000170020002d0040005b008000880090009800a000a800b000b800c000c800d000d800e000e800f000f8010001100120013001400150016001700180019001a001b001c001d001e001f00200022002400260028002a002c002e00300032003400360038003a003c003e004000440048004c005000540058005c006000640068006c007000740078007c008000880090009800a000a800b000b800c000c800d000d800e000e800f000f8010001100120013001400150016001700180019001a001b001c001d001e001f00200022002400260028002a002c002e00300032003400360038003a003c003e004000ffff000000000192032404b5064607d609640af10c7c0e060f8d11121294141315901709187e19ef1b5d1cc61e2b1f8c20e7223d238e24da26202760289a29ce2afb2c212d412e5a2f6c307631793274336834533537361236e537b03871392b39db3a823b213bb63c423cc53d3f3daf3e153e723ec53f0f3f4f3f853fb13fd43fec3ffb40003ffb3fec3fd43fb13f853f4f3f0f3ec53e723e153daf3d3f3cc53c423bb63b213a8239db392b387137b036e536123537345333683274317930762f6c2e5a2d412c212afb29ce289a2760262024da238e223d20e71f8c1e2b1cc61b5d19ef187e170915901413129411120f8d0e060c7c0af1096407d6064604b5032401920000fe6efcdcfb4bf9baf82af69cf50ff384f1faf073eeeeed6cebedea70e8f7e782e611e4a3e33ae1d5e074df19ddc3dc72db26d9e0d8a0d766d632d505d3dfd2bfd1a6d094cf8ace87cd8ccc98cbadcac9c9eec91bc850c78fc6d5c625c57ec4dfc44ac3bec33bc2c1c251c1ebc18ec13bc0f1c0b1c07bc04fc02cc014c005c000c005c014c02cc04fc07bc0b1c0f1c13bc18ec1ebc251c2c1c33bc3bec44ac4dfc57ec625c6d5c78fc850c91bc9eecac9cbadcc98cd8cce87cf8ad094d1a6d2bfd3dfd505d632d766d8a0d9e0db26dc72ddc3df19e074e1d5e33ae4a3e611e782e8f7ea70ebeded6ceeeef073f1faf384f50ff69cf82af9bafb4bfcdcfe6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015900000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000100000000000000000000000003000003000000000000000000000000030000040000000100000000000000000000fff10000000d46104d0c000000000000fff100000014400aa294000000000000fff10000001e80000810000000000000fff10000002600000048000000000000fff10000003080000a50000000000000fff10000003980000db4000000000000fff10000004146c82456000000000000fff10000004a100b14cf000000000000fff1000000531001614e000000000000fff10000005f0008eda2000000000000fff1000000680008edbc000000000000fff100000070400d31ae000000000000fff10000007700000192000000000000fff100000085800000e0000000000000fff1000002e140013a08000000000000fff10000008d46c7dd26000000000000fff100000097800000a0000000000000fff1000000a446c7d8ee000000000000fff1000000ad00000038000000000000fff1000000b70000012a000000000000fff1000000c0400d24d0000000000000fff1000000c80184cbb7000000000000fff1000000cf01000000000000000000fff1000000d773000000000000000000fff1000000df00000000000000000000fff1000000e800000004000000000000fff1000000ef00000008000000000000fff1000000f60000000c000000000000fff10000010000000080000000000000fff10000010a00000018000000000000fff10000011100000024000000000000fff10000011600000026000000000000fff10000002700000040000000000000fff10000011e00000018000000000000fff1000001250000003c000000000000fff10000012d800049d8000000000000fff100000138000000a8000000000000fff10000014580001c80000000000000fff10000014c40004008000000000000fff1000001594000d51c000000000000fff1000001634000f450000000000000fff10000016e800062a8000000000000fff100000175800062a4000000000000fff10000017c00000001000000000000fff100000185100b14f0000000000000fff10000019300024640000000000000fff1000001a100000448000000000000fff1000001b100100000000000000000fff1000001bb002ed1e0000000000000fff1000001c1000fffff000000000000fff1000001c700000a44000000000000fff1000001d300000204000000000000fff1000001dc100b14ed000000000000fff1000001e5460d171d000000000000fff1000001ed400d2cb0000000000000fff1000001f5400d2cd8000000000000fff1000001fe800065b2000000000000fff100000207800065b6000000000000fff1000002100000000d000000000000fff10000021800000010000000000000fff10000022000000014000000000000fff10000022800000018000000000000fff1000002300000001c000000000000fff10000023900000159000000000000fff100000245400d2ca8000000000000fff10000024e0000000b000000000000fff10000025600000008000000000000fff10000025e0000000e000000000000fff10000026580004858000000000000fff10000026f400d37f6000000000000fff10000027500000192000000000000fff10000028240031ed6000000000000fff10000028f0008f04b000000000000fff1000002980008f04a000000000000fff1000002a100000003000000000000fff1000002a9460d5c30000000000000fff1000002b6400795c2000000000000fff1000002c14007981e000000000000fff1000002cd4005a850000000000000fff1000002d940013a08000000000000fff1000002e900000500000000000000fff1000002eeffffff80000000000000fff1000002f880000110000000000000fff1000003000000000c000000000000fff10000030700000003000000000000fff10000031146c80350000000000000fff10000031d40006862000000000000fff10000032600000000000000000000fff10000032c00000004000000000000fff10000033200000008000000000000fff1000003380000000c000000000000fff10000033e00000010000000000000fff10000034500000014000000000000fff10000034c0000001c000000000000fff10000035100000020000000000000fff10000035900000028000000000000fff10000035f0000002c000000000000fff10000036500000030000000000000fff10000036c00000034000000000000fff10000037300000038000000000000fff1000003780000003c000000000000fff10000037d00000040000000000000fff10000038500000044000000000000fff10000038900000048000000000000fff10000038d0000004c000000000000fff10000039500000050000000000000fff10000039c00000051000000000000fff1000003a400000000000000000000fff1000003ad00000004000000000000fff1000003b600000008000000000000fff1000003be00000010000000000000fff1000003c600000110000000000000fff1000003ce00000052000000000000fff1000003d300000054000000000000fff1000003da00000058000000000000fff1000003e100000070000000000000fff1000003e800000074000000000000fff1000003f000000078000000000000fff1000003f80000005c000000000000fff1000003fe00000040000000000000fff100000404100b14cc000000000000fff10000040d0008ee9a000000000000fff10000041a00000000000000000000fff10000042000000004000000000000fff10000042600000008000000000000fff10000042c0000000c000000000000fff10000043200000010000000000000fff10000043900000014000000000000fff1000004400000001c000000000000fff10000044500000020000000000000fff10000044d00000024000000000000fff10000045500000025000000000000fff10000045b00000026000000000000fff10000046300000028000000000000fff1000004690000002c000000000000fff10000047000000030000000000000fff10000021100000034000000000000fff10000047600000038000000000000fff10000047e0000003e000000000000fff10000048600000020000000000000fff10000049000000000000000000000fff10000049800000001000000000000fff10000049d00000002000000000000fff1000004a600000003000000000000fff1000004ae00000004000000000000fff1000004b700000008000000000000fff1000004bf0000000c000000000000fff1000004c700000010000000000000fff1000004d100000014000000000000fff1000004d900000018000000000000fff1000004e30000001c000000000000fff1000004eb0000001d000000000000fff1000004f30000019d000000000000fff1000004fa0000008a000000000000fff100000502460d172a000000000000fff10000050d400d2cdc000000000000fff10000051740042d1c000000000000fff100000520400e21e0000000000000fff1000005281001614e000000000000fff1000005300000044d000000000000fff10000053a80000004000000000000fff10000054280000002000000000000fff10000054b40042158000000000000fff10000055646c7e956000000000000fff10000055e00000000000000000000fff100000566400d3988000000000000fff10000056c00000192000000000000fff1000005790008f078000000000000fff10000058400000005000000000000fff10000058e40046d9c000000000000fff10000059980000034000000000000fff1000005a34004271c000000000000fff1000005b00000007c000000000000fff1000005b90000007d000000000000fff1000005c00000007e000000000000fff1000005c800000025000000000000fff1000005cf00000001000000000000fff1000005d80000001f000000000000fff1000005e300000003000000000000fff1000005ef00002fae0000000000000001000005f90000166c0000000000000001000006030000186a00000000000000010000060e0000261800000000000000010000061800001f920000000000000001000006200000242c00000000000000010000062a0000393600000000000000010000063500001f2600000000000000010000063f00003c3c0000000000000001000006480000027c000000000000000100000650000001f200000000000000010000065800000330000000000000000100000663000001d000000000000000010000066900002864000000000000000100000673000000de00000000000000010000067b000000a4000000000000000100000683000000bc00000000000000010000068e000000b6000000000000000100000697000000c800000000000000010000069d000035ac0000000000000001000006a5000000ce0000000000000001000006ac0000011c0000000000000001000006bb000005e60000000000000001000006c600004c140000000000000001000006cf0000011e0000000000000001000006dc000001360000000000000001000006e50000062e0000000000000001000006ec0000015e0000000000000001000006f800000e520000000000000001000007010000016e000000000000000100000709000001b000000000000000010000071300000e1400000000000000010000071b00002b5c00000000000000010000072a000001bc000000000000000100000733000008cc00000000000000010000073b000001ee00000000000000010000074200000dd800000000000000010000074a000002740000000000000001000007540000025200000000000000010000075f0000099600000000000000010000076700000318000000000000000100000770000002cc000000000000000100000779000002c0000000000000000100000784000002b800000000000000010000078f000002e6000000000000000100000799000006aa0000000000000001000007a2000003540000000000000001000007b000000a300000000000000001000007b900000f1c0000000000000001000007c7000008fc0000000000000001000007d4000003700000000000000001000007e0000035e20000000000000001000007e9000003a20000000000000001000007f2000003f60000000000000001000007fd000003b6000000000000000100000805000003d600000000000000010000080d00000400000000000000000100000818000004500000000000000001000008230000044400000000000000010000082e0000067000000000000000010000083700000466000000000000000100000843000004dc00000000000000010000084d0000049e0000000000000001000008570000051a0000000000000001000008630000373600000000000000010000086a000005200000000000000001000008720000383600000000000000010000087a000004f8000000000000000100000885000005080000000000000001000008900000053e000000000000000100000898000005980000000000000001000008a0000005840000000000000001000008a90000057a0000000000000001000008b1000005dc0000000000000001000008bb000005c20000000000000001000008c3000006280000000000000001000008cd000006260000000000000001000008d6000006200000000000000001000008e0000006680000000000000001000008e90000065e0000000000000001000008f30000066c0000000000000001000008fd000006a8000000000000000100000908000006a6000000000000000100000912000007e000000000000000010000091a000006ea0000000000000001000009230000070600000000000000010000092b00001a5e00000000000000010000093300003a3800000000000000010000093a00000736000000000000000100000942000007c200000000000000010000094a000007c80000000000000001000009580000117400000000000000010000095f00001a5800000000000000010000096b000007ee0000000000000001000009730000085200000000000000010000097e00000828000000000000000100000989000008540000000000000001000009910000086800000000000000010000099a000008b80000000000000001000009a3000008ac0000000000000001000009ac000008b40000000000000001000009b5000009080000000000000001000009c10000091e0000000000000001000009cd000009340000000000000001000009d9000009440000000000000001000009e40000095c0000000000000001000009ee000009580000000000000001000009f900000962000000000000000100000a020000096a000000000000000100000a0d00000a22000000000000000100000a1800000a14000000000000000100000a2100000a24000000000000000100000a2d00000a6e000000000000000100000a3700004a40000000000000000100000a4000000aa8000000000000000100000a4c00001196000000000000000100000a5900000a7a000000000000000100000a6400000a9e000000000000000100000a7000000aac000000000000000100000a7c00000ad6000000000000000100000a89000029f4000000000000000100000a9100000b16000000000000000100000a9c00000b08000000000000000100000aa500001688000000000000000100000ab100000b2c000000000000000100000abe000011a6000000000000000100000ac700000b8c000000000000000100000ad200000b68000000000000000100000ada00000b8e000000000000000100000ae300000bd0000000000000000100000aec00000bf0000000000000000100000af500000bf8000000000000000100000afd00000dbc000000000000000100000b0800000c2c000000000000000100000b1300000c3c000000000000000100000b1e00000c64000000000000000100000b2c00000c6e000000000000000100000b3600000cb2000000000000000100000b4400000ca6000000000000000100000b5200000cdc000000000000000100000b5c00000d5a000000000000000100000b6600000d32000000000000000100000b7000000d7c000000000000000100000b7a00000db8000000000000000100000b8500000d74000000000000000100000b9000000d7c000000000000000100000b9c00000d96000000000000000100000ba7000010da000000000000000100000bb10000483c000000000000000100000bb90000403c000000000000000100000bc300000dfc000000000000000100000bcc00000e38000000000000000100000bd500000e46000000000000000100000bde00000e68000000000000000100000be900000e8c000000000000000100000bf400000e7e000000000000000100000bff00000e8e000000000000000100000c0a00000fd0000000000000000100000c1400000e98000000000000000100000c1f00004a3c000000000000000100000c2600000ef4000000000000000100000c2f00000f0c000000000000000100000c3700000f44000000000000000100000c4200000f46000000000000000100000c4d00000faa000000000000000100000c5800000f66000000000000000100000c6000000f88000000000000000100000c6800000fa6000000000000000100000c7400000fbe000000000000000100000c7e00000fdc000000000000000100000c8700000ff4000000000000000100000c9200001006000000000000000100000c9d0000102c000000000000000100000ca700001936000000000000000100000caf00001044000000000000000100000cba00001080000000000000000100000cc50000105e000000000000000100000ccd0000108e000000000000000100000cd700001092000000000000000100000cdf000010d6000000000000000100000ce90000116a000000000000000100000cf300001168000000000000000100000cfe000011bc000000000000000100000d09000011d2000000000000000100000d1500001234000000000000000100000d2100001208000000000000000100000d2a00002b22000000000000000100000d370000122c000000000000000100000d43000012a8000000000000000100000d4d000012a6000000000000000100000d5700001246000000000000000100000d61000012a2000000000000000100000d6e00001258000000000000000100000d7800001264000000000000000100000d82000012c6000000000000000100000d8b00001392000000000000000100000d9400001344000000000000000100000d9f000012f4000000000000000100000daa00001336000000000000000100000db4000015de000000000000000100000dbe0000135a000000000000000100000dc8000029e6000000000000000100000dd400001388000000000000000100000dde000013ba000000000000000100000dea0000139c000000000000000100000df4000013b0000000000000000100000dff000013d2000000000000000100000e08000013f4000000000000000100000e110000141c000000000000000100000e1c00004b40000000000000000100000e240000205c000000000000000100000e2f00001420000000000000000100000e3b00003622000000000000000100000e4500003722000000000000000100000e4d0000143a000000000000000100000e5a00001486000000000000000100000e65000015e0000000000000000100000e7200001444000000000000000100000e7a0000144e000000000000000100000e8600001458000000000000000100000e9300001462000000000000000100000e9d000019d6000000000000000100000ea6000015de000000000000000100000eb100001502000000000000000100000ebc000019e6000000000000000100000ec5000014de000000000000000100000ed000001512000000000000000100000eda00001554000000000000000100000ee50000158a000000000000000100000ef0000015c6000000000000000100000efc00001598000000000000000100000f07000015b6000000000000000100000f120000166a000000000000000100000f1c00001668000000000000000100000f2500001622000000000000000100000f3000001632000000000000000100000f3c00004b44000000000000000100000f4500001638000000000000000100000f4d00001654000000000000000100000f55000016a0000000000000000100000f61000016ac000000000000000100000f6c000016b8000000000000000100000f78000016ec000000000000000100000f8100001820000000000000000100000f8e00001714000000000000000100000f980000174e000000000000000100000fa300001726000000000000000100000fad00001746000000000000000100000fb70000175e000000000000000100000fc000001800000000000000000100000fcc00001796000000000000000100000fd8000017f0000000000000000100000fe30000181a000000000000000100000fee00001828000000000000000100000ff80000184400000000000000010000100300001918000000000000000100001010000018da00000000000000010000101b0000190c000000000000000100001025000018ea00000000000000010000102f000018e40000000000000001000010390000190a0000000000000001000010450000193400000000000000010000104f000019d40000000000000001000010590000197c0000000000000001000010640000198a00000000000000010000106d000019bc000000000000000100001078000019a4000000000000000100001081000019be00000000000000010000108a000019ca00000000000000010000109400001a540000000000000001000010a0000019f20000000000000001000010a900001a500000000000000001000010b100001a0a0000000000000001000010bc00001a1a0000000000000001000010c500001a380000000000000001000010d000001a600000000000000001000010d900001a8a0000000000000001000010e400001a7c0000000000000001000010f000001a980000000000000001000010fd00001aa000000000000000010000110900001b7200000000000000010000111400001af400000000000000010000111f00001b7e00000000000000010000112b00001b8400000000000000010000113500001ba000000000000000010000113f00001b8c00000000000000010000114900001bae00000000000000010000115400001bea00000000000000010000116200001be000000000000000010000116f00001c1600000000000000010000117a00001d3a00000000000000010000118500001cbc00000000000000010000119100001c8200000000000000010000119c00001c9a0000000000000001000011aa00001c940000000000000001000011b600001ca60000000000000001000011bf00001cac0000000000000001000011c900004d480000000000000001000011d400004d440000000000000001000011e100001d380000000000000001000011ec00001cd40000000000000001000011f7000035b10000000000000001000011ff00001e9800000000000000010000120c000035b700000000000000010000121400001e6e00000000000000010000122040031da4000000000000fff10000122d400d5f38000000000000fff100001239400d2e8a000000000000fff1000012444003c058000000000000fff1000012504003932c000000000000fff10000125a4003249c000000000000fff100001262400a72a8000000000000fff10000126a400a7280000000000000fff1000012720000001d000000000000fff10000127b00000900000000000000fff10000128700001d7200000000000000010000129100001d9800000000000000010000129b00001db40000000000000001000012a400001e2a0000000000000001000012ae00001e440000000000000001000012bb00001de00000000000000001000012c500001e0e0000000000000001000012cf00001de80000000000000001000012d800001e6a0000000000000001000012e300001e800000000000000001000012ea00001e820000000000000001000012f1000035de0000000000000001000012f800001edc00000000000000010000130400001ef600000000000000010000130f000036a200000000000000010000131800001f0600000000000000010000132100001f1400000000000000010000132b00001f2200000000000000010000133500004edc00000000000000010000134300004eda00000000000000010000134f00001f8600000000000000010000135a00001f4800000000000000010000136500001f5000000000000000010000137100001f6a00000000000000010000138100001f5e00000000000000010000138c00001f7600000000000000010000139700001fd60000000000000001000013a3000020000000000000000001000013af000020560000000000000001000013ba0000204c0000000000000001000013c400001ffa0000000000000001000013ce0000204a0000000000000001000013db000020180000000000000001000013e70000201a0000000000000001000013f40000204200000000000000010000140100002072000000000000000100001409000021e2000000000000000100001411000020900000000000000001000014170000209200000000000000010000141d00002240000000000000000100001423000020ba000000000000000100001429000020c200000000000000010000142f00002220000000000000000100001437000020d400000000000000010000143d000020e0000000000000000100001443000020ea0000000000000001000014490000210e00000000000000010000144f000020fa0000000000000001000014550000212200000000000000010000145b000021500000000000000001000014610000212e0000000000000001000014670000214000000000000000010000146e000021600000000000000001000014740000216200000000000000010000147a000021820000000000000001000014800000218e000000000000000100001486000021c400000000000000010000148e0000219e000000000000000100001494000021a000000000000000010000149a000021b60000000000000001000014a0000021c60000000000000001000014a6000021e40000000000000001000014ac000021e60000000000000001000014b20000220c0000000000000001000014b8000022240000000000000001000014be000022320000000000000001000014c4000022540000000000000001000014cb000022560000000000000001000014d40000226c0000000000000001000014da000022aa0000000000000001000014e2000022840000000000000001000014e8000022860000000000000001000014ee0000229c0000000000000001000014f4000022b00000000000000001000014fa00002416000000000000000100001502000022d40000000000000001000015080000234c00000000000000010000150f0000233200000000000000010000151700002320000000000000000100001520000022f4000000000000000100001526000022f600000000000000010000152c0000237e0000000000000001000015360000240c00000000000000010000153d0000234a0000000000000001000015460000237a00000000000000010000154c000023620000000000000001000015520000238e0000000000000001000015580000239000000000000000010000155e000023f2000000000000000100001564000023a800000000000000010000156b000023e6000000000000000100001571000023cc0000000000000001000015780000242a00000000000000010000157f0000256c00000000000000010000158900002578000000000000000100001596000025500000000000000001000015a0000024bc0000000000000001000015aa000024d00000000000000001000015b5000024e80000000000000001000015be000024fe0000000000000001000015c8000025380000000000000001000015d3000025960000000000000001000015df000025940000000000000001000015e9000025a20000000000000001000015f4000025ac00000000000000010000160000002788000000000000000100001609000025f2000000000000000100001616000025e40000000000000001000016210000261600000000000000010000162b0000262e0000000000000001000016340000271a00000000000000010000163e00002668000000000000000100001646000026780000000000000001000016510000272400000000000000010000165d000026ee0000000000000001000016660000270200000000000000010000166f00002810000000000000000100001679000028040000000000000001000016820000280600000000000000010000168c00002856000000000000000100001696000028580000000000000001000016a1000028ae0000000000000001000016ac0000290a0000000000000001000016b9000028f60000000000000001000016c5000029840000000000000001000016ce000029440000000000000001000016d90000295c0000000000000001000016e7000029560000000000000001000016f3000029680000000000000001000016fc0000296e000000000000000100001706000029860000000000000001000017100000299200000000000000010000171d000029e4000000000000000100001727000029dc000000000000000100001731000029de00000000000000010000173b00002a1800000000000000010000174600002a3400000000000000010000175000002a2000000000000000010000175b00002a3200000000000000010000176500002a3e00000000000000010000177200002a4e00000000000000010000177d00002a7400000000000000010000178700002a7200000000000000010000179100002a6800000000000000010000179d00002a7e0000000000000001000017aa00002a8e0000000000000001000017b500002aaa0000000000000001000017c100002aa40000000000000001000017cc00002ace0000000000000001000017d600002aba0000000000000001000017e100002ab00000000000000001000017ed00002ac00000000000000001000017f900002ad800000000000000010000180700004c0c00000000000000010000181000004c0400000000000000010000181900002b1800000000000000010000182300004b8400000000000000010000182c00002b5a00000000000000010000183600002c0c00000000000000010000184300002b8400000000000000010000184e00002bec00000000000000010000185b00002bc800000000000000010000186b00002c5c00000000000000010000187900002c5600000000000000010000188600002c6c00000000000000010000188d00002c9600000000000000010000189800002d3c0000000000000001000018a600004c340000000000000001000018af00002cec0000000000000001000018be00002d0e0000000000000001000018cc00002d880000000000000001000018d600002dbe0000000000000001000018e100002e080000000000000001000018ed00002dda0000000000000001000018f800002de000000000000000010000190300002df000000000000000010000190e00002e0600000000000000010000191900002e2a00000000000000010000192500002e8e00000000000000010000193200002e5600000000000000010000193e00002e8600000000000000010000194a00002e5a00000000000000010000195600002e6a00000000000000010000196200002f6600000000000000010000196e00002ec800000000000000010000197800002f0800000000000000010000198200002f5800000000000000010000198d00002f2200000000000000010000199a00002f540000000000000001000019a400002fa80000000000000001000019ae00002fa00000000000000001000019b8400bebae000000000000fff1000019c040051fd6000000000000fff1000019ce40051ff6000000000000fff1000019de40052006000000000000fff1000019eb460d1736000000000000fff1000019f4460d174a000000000000fff1000019fe460d174c000000000000fff100001a08460d16f0000000000000fff100001a12100b14cc000000000000fff100001a1c100b14d0000000000000fff100001a240000091a000000000000fff100001a2f00008ed8000000000000fff100001a3a00000059000000000000fff100001a430009b332000000000000fff100001a4e100f8598000000000000fff100001a5540027e00000000000000fff100001a5d40027de4000000000000fff100001a644009da20000000000000fff100001a6c460d173a000000000000fff100001a75460d1a9e000000000000fff100001a7e460d10dc000000000000fff100001a87460d1750000000000000fff100001a90400418e0000000000000fff100001a984004d948000000000000fff100001a9f0008edaa000000000000fff100001aaa0000000c000000000000fff100001ab300000033000000000000fff100001aba00003102000000000000000100001ac5000030fc000000000000000100001ad2000030f4000000000000000100001ae3000030e6000000000000000100001af100002ffa000000000000000100001afb00003002000000000000000100001b050000308c000000000000000100001b1000003056000000000000000100001b1b00003108000000000000000100001b280000305e000000000000000100001b310000306a000000000000000100001b3a0000314c000000000000000100001b4600003162000000000000000100001b5200003168000000000000000100001b5c0000316c000000000000000100001b68000031ee000000000000000100001b7400005070000000000000000100001b830000506e000000000000000100001b90000031e4000000000000000100001b9b0000319a000000000000000100001ba6000035bd000000000000000100001bad0000321a000000000000000100001bb8000031f6000000000000000100001bc200003210000000000000000100001bcc0000322c000000000000000100001bd3000035c3000000000000000100001bdf00003238000000000000000100001be800003240000000000000000100001bf300003254000000000000000100001bfa0000325c000000000000000100001c01000032d0000000000000000100001c0b00003308000000000000000100001c1700003304000000000000000100001c240000339e000000000000000100001c2e000033d6000000000000000100001c38000033b6000000000000000100001c44000033e0000000000000000100001c51000033d2000000000000000100001c5d0000340c000000000000000100001c6b0000506f000000000000000100001c7500003496000000000000000100001c800000353a000000000000000100001c8a000034c8000000000000000100001c9600003526000000000000000100001ca100003568000000000000000100001caa40025288000000000000fff100001cb100003554000000000000000100001cbc0000355c000000000000000100001cc800095048000000000000fff100001cd5100b145e000000000000fff100001ce50009b332000000000000fff100001cf0100f8598000000000000fff100001cfe40027e00000000000000fff100001d09000035db000000000000000100001d1000005204000000000000000100001d1700000020000000001000000100001d2100001bb8000000001000000100001d2900001bc2000000001000000100001d3200001c18000000001000000100001d3d00002b6e000000001000000100001d4400002c18000000001000000100001d4c00002da8000000001000000100001d5500002e16000000001000000100001d5f00002e9a000000001000000100001d6700002f78000000001000000100001d700000325e000000001000000100001d7c0000326a000000001000000100001d880000327e000000001000000100001d9400003292000000001000000100001da0000032a6000000001000000100001dab000032d6000000001000000100001db600003312000000001000000100001dc000003346000000001000000100001dcb00003372000000001000000100001dd700003484000000001000000100001de200002d4e000000001000000100001dea00003544000000001000000100001df400002c9c000000001000000100001dfe00002896000000001000000100001e0a000028c2000000001000000100001e15000027b2000000001000000100001e2100002812000000001000000100001e2800002918000000001000000100001e3700001d40000000001000000100001e4200001d9e000000001000000100001e4d00001d78000000001000000100001e5700000000000000001000000000001e6100000000000000001000000000001e6b00000000000000001000000000484156455f53594452554d004e4942424c450050495443485f5441420043555256414c530043565f5354524944450043565f574f5244530043565f534c455700504152545f50545200504152545f49445800504152545f534841444f57004d4143485f4f4646005349475f4f464600464c45585f5000464c45585f444553435f4c454e0050494e475f41540046554e435f48454c440046554e435f454e435f4f4646004b45595f48454c4400505553485f434f4445004b4e5f534c4f545300464d5f444553430043345f494e4300454e565f4f4e4500494e435f4d415800434b5f5449434b5300434b5f54505300434b5f46505300434b5f4652414d45530053545f53545249444500535f4741494e00535f4f4e00535f475052455600565f4741494e00565f475052455600564f4943455f4241534500564f4943455f53545249444500435552534f520053544f434b5f52454e44455200464d5352435f52455400464c45585f53544152540046505f5054520052535f5054520048454144524f4f4d0053455454494e47535f424153450053455454494e47535f5350414e0053455454494e47535f53545249444500454e565f464c4f4f52004b5f4e554d004b5f4d415800494e4445585f5343414c450046425f5343414c4500474c4944455f4154004c4b5f48454c44004b4559535f415400434c4f434b5f4154005345515f53544550005345515f5449434b0043565f484f4c4400434b5f4c41535400434b5f50494e4700434b5f5354455000434b5f4653544550004650535f44454641554c54005343414c455f41540043565f434852440043565f564f49430043565f52454c004c464f5f5354415445004c464f5f50004c464f5f444553435f4c454e005245534f4c5645525f52455400534c4f545f4f464600534c4f545f434f4c0043565f52415445004c49535457494e5f4d4143480041535349474e5f524554004d41434857494e5f524554004c49535457494e5f524554004c4f414453454c5f535052494e54460053454d490053484150455f454e44004453505f5245430043565f41544b004355545f534849465400474c4f42414c5f574f5244004b494c4c5f52455400535f50484300535f50484d00535f454e5600535f494e4300535f494e434d00535f4945464600535f464200535f4c4153544d00535f43555200545f52454600545f4c41535400545f4d41534b00545f444b00545f524b00545f524154494f00545f4900545f5700545f5343414c4500545f504f4c5900535f4552414d500052545f43414c4c530052545f46495845440052545f484541440052545f52494e470052545f53495a4500545f414b00545f474d415800535f4854494d00545f5241575000545f534d41534b00545f4c4153544b00545f504f5300565f504f530055495f545241434b004c464f5f504147455f4f464600565f50484300565f50484d00565f454e5600565f494e4300565f494e434d00565f4945464600565f464200565f4c4153544d00565f535441544500565f4b455900565f495354455000565f43555200565f524f4f5400565f41474500565f4652414d4500565f4552414d500043525f5354524944450043525f535445500043525f4e0043525f57524f54450043525f504b45590043525f4652414d450043525f4b4559530043525f524157530043525f504652414d450043525f504354580043525f505449434b530043525f505241570043525f564f4943004b525f57494e004b525f434f4e46004c4956455245435f415400515a5f4f43545f4154005245435f54524947005041545f52414d005041545f4d4952005041545f4e55444745005041545f4355520042414e4b5f435552004c4f434b5f5752495445005245435f4354580043565f5054434800414d505f5000414d505f444553435f4c454e00414d505f50325f4f4646004c45475f534c4f5432004c45475f574944474554004d41535445525f4f4e005245435f545249474c45535300545f4c45474b455900545f4c45474e00535f495354455000545f4d4c4547004b4e5f524154494f004b4e5f524154494f5f4e004b4e5f535542554e49545900706f5f6f637461766500706f5f6c65676b657900706f5f6c65676d6f646500706f5f6b657972656c00706f5f6b6e6f6200706f5f6b657972656300706f5f686f6c6431323800706f5f7067646573630073795f73746174650073795f63616c6c0073795f69736f6e0073795f6d616368696e650073795f6e6f00706f5f7369676e65640073795f636f6c640073795f7363616e0073795f7363616e6e65640073795f7363616e310073795f666d0073795f6e616d650073795f636d700073795f636f6c645f6e6f686f6c6400706f5f6c6976656b657900706f5f636c6f636b0073795f636f6c645f686f6c640073795f636f6c643100706f5f7866710073795f7761726d5f656e7600706f5f63617272790073795f7761726d0073795f746f706f6c7900706f5f6661646500706f5f6d72696e675f72657365740073795f73796e74680073795f776f72640073795f73657400706f5f667265650073795f73616d706c650073795f6e65757472616c0073795f736c65770073795f766f6963650073795f73746f636b0073795f6e61745f656e640073795f6e61745f636c720073795f63616c6c65640073795f636865636b0073795f6d6f6e6f5f6672616d6500706f5f6672616d6500706f5f666164655f6672616d6500706f5f726174655f666f6c640073795f6d6f6e6f5f696e630073795f726174696f0073795f656e765f6b0073795f6e6f64656361790073795f656e76300073795f656e76310073795f656e76646f6e650073795f696c5f646f6e650073795f696c5f6c65667400706f5f65726c656e00706f5f6d6f6e6f5f656e7600706f5f6d655f72656c00706f5f6d655f61746b00706f5f6d655f73746f726500706f5f61746b00706f5f616c617700706f5f72656c6b00706f5f6d655f72656c3000706f5f6d655f72656c3100706f5f616c5f7000706f5f616c5f7300706f5f616c5f736300706f5f616c5f6600706f5f616c5f6f757400706f5f616c5f7100706f5f6c6b5f6f757400706f5f6c6b5f6e6f00706f5f6c6b5f79657300706f5f78715f6e6f00706f5f78715f61627300706f5f78715f79657300706f5f65726c5f6f757400706f5f65726c5f31360073795f646f6e650073795f616c6976650073795f6d6f6e6f00706f5f66696c6c0073795f7461620073795f6c6f6f700073795f67656e640073795f6d6f6e6f5f7461696c7300706f5f616e7900706f5f66696c6c5f61646400706f5f7469636b00706f5f746b5f646f6e6500706f5f746b5f7374657000706f5f7261746500706f5f726174653100706f5f726174653200706f5f726174653300706f5f726174653400706f5f72665f666f6c643100706f5f72665f666f6c643200706f5f72665f666f6c643300706f5f72665f7261746500706f5f72665f6f757400706f5f72665f646f776e0073795f676c69646500706f5f6c6567627974650073795f736c5f736e61700073795f736c5f75700073795f736c5f73746f726500706f5f66725f72656300706f5f63686f726400706f5f66725f737461727400706f5f766f696365735f6f6600706f5f66725f676f6e6500706f5f66725f676f6e653100706f5f66725f6d676f6e6500706f5f66725f6d676f6e653100706f5f68656c6400706f5f66725f6e6f6c6800706f5f66725f6c6800706f5f68616e646f76657200706f5f66725f706172616d7300706f5f737461727400706f5f66725f686f6c6400706f5f66725f6b00706f5f66725f646b00706f5f66725f726b00706f5f66725f616b00706f5f66725f7600706f5f66725f6e65787400706f5f66725f696e633100706f5f66725f656e766b00706f5f66725f656e76686f6c6400706f5f66725f656e7600706f5f66725f696c5f646f6e6500706f5f66725f696c5f6c65667400706f5f66725f616d7000706f5f66725f72656c00706f5f66725f63757400706f5f66725f63617000706f5f66725f6761696e00706f5f66725f72656c3100706f5f66725f72656c316100706f5f66725f72656c3200706f5f70737461727400706f5f70656e6400706f5f766f6963657300706f5f667265653100706f5f666164653100706f5f666164653200706f5f63615f7069636b00706f5f63615f6672656500706f5f63615f6e65787400706f5f63615f74616b6500706f5f70636c65617200706f5f63615f636f707900706f5f73657100706f5f63615f677000706f5f63615f6700706f5f66665f7374657000706f5f66665f6c6f6f7000706f5f66665f6e65787400706f5f66665f6900706f5f66665f3100706f5f66665f73746f726500706f5f70656e646f6600706f5f73745f786600706f5f78665f7363616e00706f5f78665f6e65787400706f5f706d6174636800706f5f736e617000706f5f706d5f6c6f6f7000706f5f706d5f6e65787400706f5f706d5f6100706f5f706d5f79657300706f5f7066697800706f5f70665f6f757400706f5f70735f6f757400706f5f70735f64726f7000706f5f73745f666f6c6400706f5f73745f666f6c643100706f5f73745f70616e656c00706f5f73745f6d3100706f5f6d72696e675f706f7000706f5f73745f6d646f6e6500706f5f73746172743100706f5f73745f6f757400706f5f73745f73773100706f5f73745f7377646f6e6500706f5f73745f73773200706f5f73745f73773300706f5f73745f316100706f5f73745f676f00706f5f73745f6473657100706f5f73745f646b657900706f5f73745f646b3100706f5f73745f64757000706f5f73745f64733100706f5f6d68656c645f6f6600706f5f73745f64733200706f5f73745f63686f726400706f5f73745f72656c00706f5f73745f72656c3100706f5f73745f6e7300706f5f73745f763100706f5f73745f726f773000706f5f7662756600706f5f766f6963696e6700706f5f73745f736861706500706f5f73686170657300706f5f676d617800706f5f73745f73696e676c6500706f5f73745f6e6f746500706f5f73745f63707265737300706f5f73745f6b00706f5f73745f636f756e7400706f5f73745f636f756e743100706f5f73745f63757400706f5f737465616c00706f5f73745f646f6e6500706f5f73745f7761726d00706f5f616c6c6f6300706f5f73745f636f6c6400706f5f73745f656e7600706f5f73745f6873657100706f5f73745f68696e6600706f5f73745f737461746500706f5f73745f6c69766500706f5f73745f6c6b657900706f5f63705f6f757400706f5f63705f6e6f00706f5f63705f6d69646900706f5f63705f636f756e7400706f5f6d68656c6400706f5f63705f3100706f5f63705f3200706f5f686f5f666f6c643000706f5f686f5f666f6c6400706f5f686f5f666f6c643100706f5f686f5f763100706f5f686f5f73696e676c6500706f5f686f5f636f6c00706f5f686f5f636f6c3200706f5f686f5f696e7300706f5f686f5f70757400706f5f686f5f617300706f5f686f5f657874726100706f5f686f5f667265736800706f5f686f5f6e65787400706f5f686f5f646f6e6500706f5f686f5f6e657700706f5f686f5f6e65773100706f5f6c6d5f73616d706c6500706f5f6c6d5f7061726100706f5f6c6d5f6f757400706f5f6c6d5f6c656700706f5f6c6d5f74776f00706f5f6c6d5f746872656500706f5f6c6d5f72657400706f5f736e5f72657400706f5f736e5f6469737400706f5f736e5f6c6f00706f5f736e5f646f776e00706f5f736e5f686900706f5f736e5f757000706f5f736e5f6f757400706f5f72616e6b5f61637400706f5f616c5f676f00706f5f72616e6b00706f5f616c5f6c6f6f7000706f5f616c5f726b00706f5f616c5f6e65787400706f5f66696c6c3100706f5f66695f68616c6600706f5f66695f636c65617200706f5f66695f766f6963657300706f5f66695f766f69636500706f5f66695f6e65787400706f5f66695f6c6f6f7000706f5f66695f756e69747900706f5f66695f6f757400706f5f66695f73617400706f5f66695f70757400706f5f66695f7361747000706f5f6c666f335f646570746800706f5f6c33625f646570746800706f5f6c335f6261636b00706f5f6c705f646f6e6500706f5f6c705f636c6f6e6500706f5f6c705f7363616e00706f5f6c705f7363616e6e656400706f5f6c705f7363616e3100706f5f6c705f666d00706f5f6c705f636d7000706f5f6c666f6465736300706f5f6c666f5f6275696c7400706f5f6c705f6861766500706f5f6c705f636f707900706f5f6368726400706f5f666d745f63686f726400706f5f766f696300706f5f666d745f766f6963004c46445f5245534f4c564552004c46445f4d414348544142004c46445f4d4153544552004c46445f464d545f524554004c46445f53544f5245004c46445f454e43004c46445f563250004c46445f503256004c46445f4c415354004c46445f484944455f464d00706f5f6c6e5f6f757400706f5f6c6f5f6f757400706f5f6c655f6f6b00706f5f6c655f6d617000706f5f6c645f68696464656e00706f5f6c655f636e7400706f5f6c655f656e6400706f5f6c655f6e7800706f5f6c685f6e6f6e6500706f5f66763100706f5f66763200706f5f665f6400706f5f66635f736861706500706f5f66635f6e616d6500706f5f6e616d657300706f5f66635f637000706f5f66635f696e7600706f5f66635f656e6400706f5f7067646573635f62756600706f5f70675f6275696c7400706f5f70675f6861766500706f5f70675f636f707900706f5f70675f706174636800706f5f70675f6275696c745f6e6f7700706f5f70675f6279746500706f5f70675f6b6e6f6200706f5f6b6e5f706c61696e00706f5f6b6e5f726174696f00706f5f6b6e5f6a756d7000706f5f6b6e5f6f757400706f5f6b6e5f6f6e6500706f5f6b6e5f725f646f6e6500706f5f6b6e5f725f64657400706f5f6b6e5f725f7363616e00706f5f6b6e5f725f74616b650070765f6c6f61640070765f736f72740070765f64310070765f64320070765f70630070765f64330070765f64340070765f646964780070765f69310070765f69320070765f72310070765f72330070765f72320070765f62310070765f62330070765f62320070765f6232610070765f6f310070765f6f320070765f6f330070765f6f340070765f636f70790070765f70310070765f70320070765f70330070765f63310070765f73310070765f73320070765f73330070765f78310070765f78320070765f70633100706f5f6d6174636800706d5f6c3100706d5f6261736500706d5f703100706d5f703200706d5f703300706d5f693100706d5f7063313200706d5f653100706d5f74727900706d5f6b65657000706d5f666f756e6400706d5f653200706d5f653300706d5f7365636f6e6400706d5f6f757400706d5f6b5f6f757400706d5f6e6f00706d5f743100706d5f633100706d5f633200706d5f633400706d5f63326100706d5f633300706d5f63326200706d5f70633100706f5f6b725f6f757400706f5f6b725f736574746c6500706f5f6b725f6e657700706f5f6b725f616e7900706f5f6b725f6a6f696e00706f5f6b725f637000706f5f6b725f63703100706f5f6b725f66756c6c00706f5f6b725f777269746500706f5f6b735f6f757400706f5f6b775f726f6f7400706f5f6b775f726f6f743100706f5f6c6f636b7700706f5f6b775f72656d6f766500706f5f6b775f766f696300706f5f6b775f6f757400706f5f726c5f696400706f5f726c5f6f757400706f5f726c5f6b00706f5f726c5f68616e6400706f5f726c5f6e7564676500706f5f726c5f733100706f5f726c5f733200706f5f69735f72747300706f5f69735f6e6f00706f5f69735f6f757400706f5f7369675f6e6f00706f5f7369675f6f757400706f5f66735f6b65657000706f5f6673745f73746f636b00706f5f6673745f636f6c6400706f5f736d5f6e6f00706f5f736d5f7363616e00706f5f736d5f7363616e6e656400706f5f736d5f7363616e3100706f5f736d5f666d00706f5f736d5f636d7000706f5f736d5f6f757400706f5f706f6c79747261636b00706f5f70745f72657400706f5f70745f79657300706f5f70745f6f757400706f5f68645f6d69646900706f5f68645f6f757400706f5f68645f7363616e00706f5f68645f79657300706f5f6d68656c645f61646400706f5f6d615f7363616e00706f5f6d615f6f757400706f5f6d615f70757400706f5f6d615f736869667400706f5f6d68656c645f64656c00706f5f6d645f7363616e00706f5f6d645f666f756e6400706f5f6d645f6e6f6e6500706f5f6d645f6f757400706f5f6d645f6c61737400706f5f6d645f736869667400706f5f6d645f636f756e7400706f5f6d72696e675f7075736800706f5f6d7461696c00706f5f6d6865616400706f5f6d705f6f757400706f5f6d72696e6700706f5f6d715f6f757400706f5f6d6f6e5f73746f636b00706f5f6d6f6e5f72617700706f5f6d6f6e5f737461727400706f5f6d6f6e5f747269676c65737300706f5f6d6f66665f73746f636b00706f5f6d6f66665f6d6f726500706f5f72656c00706f5f72656c5f6f757400706f5f7265747269675f6f757400706f5f72746c6f6700706f5f7265747269675f6e6f737400706f5f7265747269675f6c6f6700706f5f6b696c6c5f7600706f5f6d675f7769646500706f5f6d675f73746f636b00706f5f6d675f6d61736b00706f5f6d675f7363616e00706f5f6d675f6e65787400706f5f6d675f6e6f6e6500706f5f6d6f675f7769646500706f5f6d6f675f73746f636b00706f5f6d6f675f6d61736b00706f5f6d6f675f6e6f6e6500706f5f6d6f675f7363616e00706f5f6d6f675f6e65787400706f5f6d725f73746f636b00706f5f6d725f72617700706f5f6d725f6c656700706f5f6d725f646f6e6500706f5f6d725f706c6163656400706f5f6d725f706f7000706f5f6d745f6f757400706f5f6d745f7261770053454c5f57494e004f43545f484f4f4b5f4f50454e004f43545f484f4f4b5f434c4f534544004f43545f484f4f4b5f52545300475249445f5245430048454c445f4d41534b0048454c445f5041474500545249475f4d4f444500545241434b5f435552005041545f4944580054524b5f535452494445005041545f535452494445004c4f434b5f4f46460042414e4b5f4449525459004449525459320045445f544944590045445f454e440045445f53454e540045445f464c4147310045445f574f5244530045445f574f5244360045445f464c4147320045445f43414c4c0052454452415700504c41595f534c4f5453004f43545f4a554d50004b45595f555000706f5f6f635f6f70656e00706f5f6f635f636c6f73656400706f5f6f635f706f705f636c6f73656400706f5f6f635f7377616c6c6f7700706f5f6f635f64697200706f5f6f635f62697400706f5f6f635f6e65787400706f5f6f635f6861766500706f5f706172745f7074636800706f5f6f635f6c6f00706f5f6f635f686900706f5f616d705f6b696e6400706f5f616b5f617564696f00706f5f616b5f6f757400706f5f616d705f6465736300706f5f61645f73746f636b00706f5f616d70646573635f62756600706f5f616d705f6275696c7400706f5f61645f6861766500706f5f61645f636f707900706f5f6c656700706f5f666d745f6c656700706f5f6c65676d617800706f5f6c785f6f757400706f5f666c3100706f5f6c65676e616d657300706f5f666c5f637000706f5f6c65676b6e6f6200706f5f6c6b3100706f5f6c6b3200706f5f61655f6f757400706f5f616c5f73746f726500706f5f616c5f73616d706c6500706f5f626563616d6500706f5f62635f6f757400706f5f62635f736c6f747300706f5f666c65785f736c6f7400706f5f62635f726573657400706f5f66696e655f726573657400706f5f6c735f77617300706f5f6c735f63616c6c00706f5f6c735f6f757400706f5f6c735f747261636b00706f5f6c735f6e65787400706f5f646972747900554e4c4f414400706f5f756c5f666c657800706f5f756c5f73746f726500504152545f554e534156454400504152545f554e53415645445f42520042414e4b5f57524954450042414e4b5f57524954455f425200554e444f5f524553455400706f5f665f7300706f5f656e640073795f72656e64657200706f5f6c666f3300706f5f6c666f336200706f5f6c666f7061676500706f5f6d6f6e00706f5f6d6f666600706f5f6d6761746500706f5f6d6f6761746500706f5f6d72656300706f5f6d7472696700706f5f616d70737461676500706f5f616d70647261773100706f5f616d70647261773200706f5f616d70647261773300706f5f616d706564697400706f5f616d706c616e6500706f5f61737369676e00706f5f6d61636877696e00706f5f6d6163686c69737400706f5f6c6f616473656c00706f5f6b696c6c00706f5f756e6c6f616400706f5f72657472696700706f5f666d736f7572636500706f5f666d737461727400706f5f69735f73796e746800706f5f73696700706f5f736c6f745f6d61726b657200706f5f6c66646e616d6500706f5f6c66646564697400706f5f6c66646c666f006d6c5f626563616d65006d6c5f6b696e646f66006d6c5f726573657400000000000000010100002fae00000004000001010000166c00000008000001010000186a0000000c0000010100002618000000100000010100001f9200000014000001010000242c0000001800000101000039360000001c0000010100001f26000033340003230100000000000035120003240100000000000035200003250100000000002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e6273730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000100000006000000000000003400005204000000000000000000000004000000000000001b0000000400000040000000000000a30c000000840000000500000001000000040000000c000000260000000100000003000000000000523800000000000000000000000000000004000000000000002c00000008000000030000000000005238000000000000000000000000000000040000000000000001000000020000000000000000000052380000326000000006000003040000000400000010000000090000000300000000000000000000849800001e7400000000000000000000000100000000000000110000000300000000000000000000a3900000003100000000000000000000000100000000", + "sha256": "54f841f49efcfc36214dd11aadbb5192ac8c87d910e26520d7564a8d79ff74e8", "stockCopies": [], "placement": "linked", "poolBaseLiterals": 0, @@ -263,16 +263,39 @@ "poly8" ], "withoutModules": [], - "bytes": 37696, - "code": "7f454c4601020100000000000000000000010004000000010000000000000000000092000000002600340000000000280008000700000000000000000000000000000000000000000000000000000000000000004fefffd048d77cfc247980001c80242f00342602ef8b47fa34c0d7c32879800062a878007210b2af00406600021241f946104d0c41f028000810000467000176610002ac53806600014a6100214a6600006e263c000000a84c02380041f9800049d8207038082608670001280483100b14f00c8300024640640001182248263c000000ff7398670000140c810000002f66000004224853836600ffea323c464db25166000004548941fa2d9e760573987b99b285660000dc53836600fff272484c02180041f98000081073b0180d0c810000007f6700001e41fa30f873f01a0041fa41ce202800084c001000ee895281600000047200274100584a6b00266600002a429342ab000442ab002042ab0018426b002642ab0014422b0051223c010000002741000860000008177c001000512079800062a442a8000472484c02180041f98000081073b0180855810c810000000253c1174100506600001861000a52610022fc41f9400d2cb0423028006000000e4a6b00266700000661000a5a610005522206e189e98927410028760160000020610009f8426b002641f9400d2cb04230280073d4e189e989274100287600174300244a2b00246700007c41f98000081072484c021800423018312239800000e0028100000001e189e3892002ed88d28041f980000110d1c14250317c7f000002317c7f0000047bec000673d4e08927410070610004cc4a2b0050660000066100058a38bc4000397c7f0000067801610000b443eb005c22d822d822d822d822d86000000a7dd46100056238866100466067000046202f004090af003c2480256f003c0004223c0400000025410008222f003c761ae7a92541000c41ea0010d0806700000a429853806600fffa23c880001c8070006000001c2f2f00402f2f00402f2f00402f2f00404eb9400040084fef00102f40002c4a84670002043886394500066100002043eb005c20d920d920d920d920d94a2b00506700004661000566600001dc223c000000a84c02180041f9800049d8d1c141e800404e75207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008eda271b028004e7561000986610003ea0c80730000006300000c700042ab001842ab00142740000c73ec0002e089e48941fa2b4273f01a00e0804c0108002740001073ec0008203c000002044c001000e0892741001c73ab00516700001e203c0100000090ab00084c410800d1ab00085381174100516000005673ec000ae0896700004224014c021000203c002ed1e04c4100000c80000fffff6f000008203c000fffff222b00080481001000006f00001ee089e8894c001000e08993ab00086000000c223c010000002741000873ec0004203c00000a444c001000e089242b0008e08ae88a4c021000e089e88992ab001474104c4218013741007e242f003461000006600000a0222b001870484c02080041f980000810d1c0082b0001002466000040202b0058670000165380274000586600000c08eb000100246000002471a8000c41fa2b8271f00a00d2800c81000080006f00004a223c000080006000004071a8000e41fa2c6071f00a006600000e082b000200246700002671d020414c001000e089e0896600000472012008908122000c81000000406e0000047200274100184e754a2b002467000130242f0034263c000000a84c023800610043d86600002441f9800049d84a30380066000016426b00264a2b005067000100610006a2600000f87faa0003670000f043ea00104a2b00506700000a426b00266000105c2f0a20132c2b00042a2b000c246b0010286b0020222b001875eb00269282760fe7a9e8812c41e7aa2a4241fa2d9c7618220c4c2b1800001cd2862401e6aad48279702800757028029484e08973c14c012800e082e082d8822844736b007ed2ab0014274100144c014800d8802404e6aad48273702800757028029481e08c79c44c042800e082e082d282dbce240d4842c3c2e589e681424122c122c1d085dc8a53876600ff8c268027460004274c0020220d760fe6a16a000004720037410026245f242f00346100073a6700000e7faa000343ea001060000f7c202f002c4cd77cfc4fef00304e7541fa3cba23c8400d2cd852a8000c73f9800065b2e18977b9800065b68283b2a80010670000402141001052905283b6a800046f00000621430004e089b2a8001867000022214100182228000c260196a8001c2141001c0c83000000106d000006214300084e752006a3430c4040006f00000a048000003c0076002200ea8041f9400aa29441f00c00781be9a92818e289a104a8984901a801080079ac001b6600002a28050c447f006c000020a1c04844e58c6400000ae28c44846000000ae28c068480000000a8000900a1c0e6a0223c0184cbb7a0010800a1c0eb884e757dd40486000040002006e58edc8071ec0006048000004000223c00000ccd4c010800e080e080dc800686000040004e752f052f062a3c00007f007e000c8600007c006f00000e048600003c0052876000ffec0c86000004006c00000e068600003c0053876000ffec0c87000000066f00000870ff6000001a6100ff0e4a87670000106b000008efa8600000064487eea02c1f2a1f4e7571b9100b14ed4e75207946c8245671b9100b14cf263c000018b24c030800d1c02602eb8b96829682d1fc0008f07871b038054e756100ffca22002006e188e9884a816700007c2e006100ffbe260020074a836700006c767f96812e3c00000d804c0730002203484173c177c32e3c000001e04c073000484377c341f9400aa31426303c00e08be48b2e3c000017c74c0730007e189e81eeab222b002890816a0000124480e0884c030000e088928060000012e0884c030000e088d28060000004220027410028e081e8812c014e754feffff848d70c002a4b2b46004841f9400d2cb820302c00222d00342b400034c1ad00744ab9460d172a6600001241fa38762602eb8b42303801423038034680c28067000034610004b271ae00240c80000000016600001871ae00256700001053800101670000081d7c000200244dee0040bdca6600ffd46100048071ae00240c80000000016600001e71ae00250c80000000806500001061001a26660000081d7c000200244dee0040bdca6600ffce7fad007c67000030202d0048b0ad00306600001671ad007d52801b40007d0c800000000865000010422d007c610008942879800062a841f946104d0c41f0280008100004670000066100041073ec0002e089e48941fa254273f01a002b41004073ec0008203c000002044c001000e0892b41001c73ec000ae0896700002620014c001000203c002ed1e04c4100000c80000fffff6f00000e203c000fffff6000000470002b40003873ec0004203c00000a444c001000e0892b41004470484c02080041f980000810d1c071a8000e43fa271c71f10a006600000e082d000200246700000471d12b40003c71a8000c43fa25fc71f10a004c2d00000054e088ee886600000470013b4000526100033e4a2e002467000176264e2c2d00489cad002cdcae002c6100fd906100fcf20c80730000006300000e422e002442ae00186000014a2d40000ce0804c2d080000402d40001073ee003e6700001e203c0100000090ae00084c410800d1ae000853813d41003e60000034202d003867000022222e00080481001000006f00001ee089e8894c001000e08993ae00086000000c223c010000002d410008222d0044202e0008e088e8884c001000e089e88992ae001470104c4018013d410026202d001c2d40001c202e00346700001253802d400034660000081d7c00020024222e001871ae00240c8000000002670000320c80000000036700001e202d0054b2806c00004077ed0052d283b2806f00006c220060000066262d0054e68b6000001c202d003c26014c003000e08be08b6600000a4a8067000004760192836a0000047200b2ad00546f000014202d00542600e68b9283b2806c00000422000c81000000406e000016720071ee003c0c80000000406e000006422e00242d4100184dee0040bdca6600fe7c202d00482b4000304cd70c004fef00084e7541fa2d4a2002e188d1c070044228002442a800184268003c41e8004053806600ffec4e7541fa2d262002e188d1c070044a28002467000008117c0003002441e8004053806600ffea4e752f0341fa2cfe2002e188d1c02248263c7fffffff70044a2800246700001e22280018b283640000062601224841e8004053806600ffe2600000042248204b700922d853806600fffa43e9ffdc137c0003002442290025202b002823400028e080e88090ab0030d0ab002c2340002c42a9003441fa348e52902350003041fa365c2028000c2340003871eb00260c8000007fff63000008203c00007fff3340003c202b00180c8000007fff63000008203c00007fff2340001842ab0018426b0026261f4e754fefffec48d7011b41fa2c342002e188d1c0262b0054e68b0c830000080064000008263c0000080070044a280024670000302228001892836a00000472000c81000000406e000016720079e8003c0c84000000406e000006422800242141001841e8004053806600ffc24cd7011b4fef00144e7541fa2bc82002e188d1c071a8002473a80064808173a800a4808173a800e480814e754dfa2ba62002e188ddc045ee01004e756100ffee222d003092ad002c4a2e002467000006d3ae002c4dee0040bdca6600ffec222d00482b41002c41f9400d2cb07fb02800423028000c8700000080650000362f2d00482f2d0070610016986700001e9097223c000005004c010800d0af00042b400048610000826000ffde588f2b5f00484e752f07610000702e1f4a87670000662f2d0048202d0034222d00744681c0816700004e7e000f006600000852876000fff6220704810000000c7139400d2cdc760c4c030800d28006810000004092ad0070203c000005004c010800d0976100063e2b40004852876100000c6000ffa62b5f00484e754a876700001a0c87000000806400001020075380222d007401c12b41007470484c02080041f98000081071b0080be488660000b86100fed041fa344c2628000c28280008e28c4a876700005271ae00240c80000000016600003671ae0025b087670002724a8066000026202d0048b0ae002c6600001a200390ae0038b0846400000e1d47002542ae0034600002484dee0040bdca6600ffb66000005041f9400d2cb84ab02c006600000a610014126700003a71ae00240c8000000001660000224a2e00256700001a202d0048b0ae002c6600000e200390ae0038b084650001f64dee0040bdca6600ffca4a87660000246100fe1471ae00240c8000000001660000081d7c000200244dee0040bdca6600ffe4700073f9400d2ca80c8100004ef9660000084eb9400d2ca82b40004c70484c02080041f9800008107db0080853860c8600000003630000047c00528671b0080b2600e48b6700001c22000281000000032003260643fa324a61000aaa76016000000643fa1f1e41fa201a20306c002b4000544a836700000a6100fbfa6000004a610001426600013c760196866100fd66204e70044a28002467000004528341e8004053806600ffee4a836f00001c61000506117c000300244dfa30e252962156003053836000ffe277190c83ffffff80670000f0610004f04a68003c66000026429042a8000442a8002042a8001842a800144268003e203c010000002140000860000008317c0010003e203c000005004c030800d0ad0048610003fc2140002ce188e988214000281147002542a800344dfa3242202e000c2140003872484c02180049f98000081073b4180d0c810000007f6700001e49fa213e73f41a0049fa3214282c00084c041000ee895281214100342879800062a870014a87670000300c8700000080650000162f0020076100122a201f6600001870026000001222075381282d00340304660000047002114000244dfa2fe852962156003053866600ff084e7570004a876700008441fa31a62228000c200190ad00782b4100780c800000019d620000660c87000000806400001620075380222d003401814a816700004c6000001241fa309c2002e7884a300801670000386100fbdc720071ae0024670000160c8000000001660000204a2e00256700001852814dee0040bdca6600ffdcb2866500000670014e7570004e752f082f0141fa23462202ef89d1c11140007c4228007d221f205f4e750c87000000806400001020075380222d007401c12b4100746100fb6e222d003092ad002c4a2e002467000006d3ae002c4dee0040bdca6600ffec222d00482b41002c70484c02080041f98000081077b0080b7db0080853860c8600000003630000047c0052862003e488670001282203028100000003260643fa2fba6100081a4fefffec42af00106100fafe71ae00240c80000000016600002e222f00104a816700001c20771cfc2028002cb0ae002c6f00000c2f881c0053816000ffe22f8e1c0052af00104dee0040bdca6600ffbe760043fa2f60b6866400009a7119223c000005004c010800d0ad0048610001bc282f0010b6846400001620773c002140002c1147002542a80034600000582f0061000248201f42a8000442a8002042a8001842a800144268003e223c01000000214100082140002ce188e988214000281147002542a800344dfa2fbe202e000c21400038117c000100244dfa2dd452962156003052830c83000000046500ff686000001c282f0010b6846400001220773c00117c0002002452836000ffe84fef00144e756100f9f293c9780071ae00240c800000000166000010202e0030b084630000062800224e4dee0040bdca6600ffdc20096700fabc202d0048610000dc2340002c1347002542a900344e7561000e0e670000a84feffff048d7011a207946c8245671b9100b14cf223c000018b24c010800d1c02202e7892001d281d280d1fc0008ee9a77b0180279b0180591fc0008ee9a2202eb8992829282d1fc0008f07871b0180555830c83000000026300000e4a806700003a7001600000340c800000000264000008700260000024e48c6600001c41fa20ca2202ef89203a2ea690b018780c800000019d6300ffdc70034cd7011a4fef00104a804e754feffff848d701086100f3324cd701084fef00084a806700000470014e75282d004c670000984feffff82e822f4600042c00048600004000068600025a80243c000005004c4260060686000004382206740c4c4210012401d482d481e58a9c82240672002c029c816a00000806860000000c0d046600002e2c02dc810c860000000c6d00000804860000000c0d046600001652810c81000000066f00ffc872006000001044812c3c000005004c016800d08624172c2f00044fef00084e754feffff448d7024843fa00606000000e4feffff448d7024843fa004c4dfa23c82002e188ddc0204e78ff7c0471ae002471b10800e188e188e188e988262e003002830fffffff8680b684640000062803204e4dee004053866600ffd24cd702484fef000c4e7500020100030201037801600000047800242f00344fefffe82e892f4700042f4b000c204920074a84660000104290508853806600fff8600000102210e2812081508853806600fff46100f78e2f4a00084a2e0024670000cc264e22572e2f000420132c2b00042a2b000c246b0010286b0020736b00262f410014426b0026222b001875eb003c3741003c9282484142414c4718012f410010484242422a4241fa1cdc7618220c4c2b1800001cd2862401e6aad48279702800757028029484e08973c14c012800e082e082d8822844222b0014d2af0014274100144c014800d8802404e6aad48273702800757028029481e08c79c44c042800e082e082d282dbef0010240d4842c3c2e681d3915089d085dc8a53876600ff8e268027460004274c00204dee0040bdef00086600ff2422572e2f00042211d28169000016424122c122c153876600ffee4fef00186000ef406b00000c223c800000006000ffe0223c7fff00006000ffd66100002c4ef940003cac220c048180004858660000100c8200000002660000066100ef12610000084ef94000d04671722a1243f04a000c82000000026600001c220c048180004858e689ef8941fa1dca4a3018246700000470004e75203c400d37f60c850000000166000122243c000018b24c012800d48920422803c8fc001ed1c4d1fc0008edbc7bd00c45464d6600000e7ba800020c050001670000722803e58cd883d4842042d1fc0008f04b75900c820000007f620000d4283c000004484c0248000684100b14f020442248283c000000ff7b98670000140c850000002f66000004224853846600ffea3a3c464dba5166000004548941fa168e78057b987599ba826600008653846600fff243fa2c144a3a2c0c6600007241f9400d37f6283c0000019212d853846600fffa43fa2bf441fa1659235800343350003841fa007c234800de41fa164b235800223350002641fa003e234800d2780123440072134400607804234400a242a901322829018e0284ff0ff0ff0084005005002344018e41fa2b9c10bc000120094ef940031ed6202f000853800c800000000363000004700052802f00487a16102f2f000c4eb940013a084fef000c4e75207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a71b9100b14cce7882200d081d08171b0080255800c80000000026300000a700072006000001c202f00082200028100000003e48802800000001f660000047200e58841fa166041f00800226f000470044a106700000a12d853806600fff44a816700000a06810000003012c142114e754feffff848d70c0045fa2c644a3a2c5e6600004e41f9400d31ae264a223c0000019216d853816600fffa71596d00001673d9205947f2080016d853816600fffa6000ffe841fa0026220847ea012a700626c153806600fffa41fa2c1210bc0001200a4cd70c004fef00084e754feffff448d7001c242f0010222f0014202f00184ab946c7dd26670000284ab9800000a06600001e0c82000000016700003e41fa009077b028004c031800d0816000007826020683000000382043d683d688e78b41f946c7d8ee4ab03800670000082601e7899283d0816000004ee48802800000001f76014a816700003c6e000006448344812400d4830c820000001f620000260c82000000036d00001441fa146879f02a000284000000ff6600ffda200253816600ffd0e5884cd7001c4fef000c4e750c00100a10104fefffd048d70cfc45ef002041fa147041f00c00740018302800158428001582280452820c82000000046600ffea6100015674017a007d322800610001a82e067d3258006100019ebe866600001241f228041c1008c6000710866000000a5285ba826600ffd252820c82000000046600ffc4610001502a01ba84650000089a846000fff67db258087f3268007400b485640000207db2280841f268007d1006860000000cbc876f00fff6108652826000ffde610000d26100010c7daa00087f3268007a0074004a3228046a0000282c05700cbc846500000e9c8406800000000c6000fff07db268087d326800dc8015862800528552820c82000000046600ffc870007a0074007db22804028600000003bc806700001252820c82000000046600ffe66000000e7d3228009c871586580c52855280ba836500ffcc220353816f00002874007d32280c7132280db0866c00000a1580280c1586280d5282b4816600ffe453816600ffdc74001c32280c138628005282b4836600fff220044cd70cfc4fef00304e7578037a007d3258007f325801be866c00001a15875800158658011c3258041e325805158758041586580552850c85000000036600ffd053846600ffc84e75780074004a3228046b00000815824808528452820c82000000046600ffe84e750c860000000c6500000c04860000000c6000ffee4e754fefffc848d71cfc45ef002477ab000141eb000c740010302800158028005282b4836600fff2220353816f00002874007db2280071b22801b0866400000a15802800158628015282b4816600ffe453816600ffdc7d927400780071b228009086158028006100015a01c45282b4836600ffea7bb228ff700928407e007c01610000766100005852860c86000000206600ffeeb9fc00000009660000307c017e01610000546100003652870c87000000046600ffee52860c86000000206600ffe0b9fc0000000967000060202f002c22000281000000ffe088600000dc4a8067000014b08c6400000e28402006e18880872f4000304e752006220743ea00046100fd06be806400001e2200740010322800b03228046600000e5282b4836600ffee20014e7570004e7542af002c203c7fffffff2f4000307c017e002006220743ea00046100fcc2be806400005228407400720071b228046100006801c15282b4836600fff0b2846600002871b2380390856a0000044480e788d08cb0af0030640000102f4000302006e18880872f40002c52870c87000000046600ffa052860c86000000206600ff92202f002c22000281000000ffe0884cd71cfc4fef00384e750c800000000c6500000c04800000000c6000ffee4e754fefffd448d77cfc2a002c0a2803207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2002e7882200d081d08173b0080255810c8100000002620000fc548171b00805e488660000f047fa22782002eb88d7c01741001d49fa243e2e2c000c610000e24a856a0000b471ab0001670000c8220792ab00040c810000019d620000b841eb000822007198610004526600000c53816600fff26000009e71ab00012f0041eb000843eb00084beb000c4deb000c72007198b08467000010610004206700000812c01cd55281528d53976600ffe4588f422b00030c81000000046400002412841c8652811744000327470010203946c7e956274000142014274000181746001c1741000127470004610000544cd77cfc4fef002c70014e75168527470004174400081746000c177c00010001422b0002422b00034cd77cfc4fef002c70004e754a2b000367000016200790ab00100c800000008a65000006422b00034e7577ab000141eb000c787f20037398b28464000004280153806600fff220047200610001d00c8300000002650000306100fc904a8067000026e5888081720b610001b271ab001db0836300000420037208610001a0177c000100024e754a2b00026700001e203c000000ff720b61000184203c000000ff720861000178422b00024e754fefffcc48d71fff28000c840000008064000004528447fa20c82002eb88d7c07bab0003670000dc49fa228a2e2c000c6100ff2e4a2b0003670000c8ba84670000c271ab00015380670000b841eb0008b8186700000c53806600fff6600000a471ab0001538017400001422b00036100ff0e4feffff82e82202b00142f4000044eb940042d1c4fef00084a806b0000742c0061000078168617450008102b001c1740000c177c00010001422b0002202b00102740000471ab001c7200610000b20c85000000806400003a206f002020086700003072034a2800026700000e508853816a00fff2206f002020055380108011460001117c00010002202b0018214000044cd71fff4fef00344e752002223c0000048d4c01080073b980000004263c0000476c4c031800d081d08606800000044d2600d68341f91001614ed1c332100281ffffe07f308173b980000002263c0004d9a04c031800d081d08041f9400e21e0d1c032100281ffffe07f30814e754fefffec2e822f4100042f40000871932f40000c203946c7e9562f4000104eb9400421584fef00144e754feffff048d7030a207946c8245671b9100b14cf223c000018b24c010800d1c02248d3fc0008eda271b1280053806600004622482002c0fc001ed3c0d3fc0008edbc71d10c40464d6600001471a900020c0000016600000870016000001c2002e588d082d1c0d1fc0008f04b7190610000146000000470004cd7030a4fef00104a804e754feffff048d7030a0c800000007f6200005c223c000004484c0100000680100b14f020402248263c000000ff7198670000140c800000002f66000004224853836600ffea303c464db05166000004548941fa0bdc760571987399b0816600000e53836600fff270016000000470004cd7030a4fef00104a804e756100ff006700004c2f012f08207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2202e7892001d281d28071b0180255800c8000000002630000087000600000047001205f221f4a804e7543fa1ef62002e788d3c04a114e754feffff448d702030c800000008064000014538043f9400d2cb822312c0001016000001e6100ffcc20177208b0196700000e53816600fff672006000000472014cd702034fef000c4e754feffff048d703096100ff9e204976087190b081670000204a8067000018528853836600ffec2049760710e8000153836600fff810814cd703094fef00104e754feffff448d703086100ff5e204976087190b081670000164a806700000a528853836600ffec70ff6000002653836700000c10e8000153836600fff84210700076084a196700000a528053836600fff44cd703084fef000c4e754feffff048d7030941fa1ec477b028002003528002800000000743fa1eaab03128006700001c43fa1e1ed683d3c32602e98bd3c31281261713430001118028004cd703094fef00104e7541fa1e7a73b0280043fa1e7a7e00b23128006700002443fa1de42002e988d3c0d3c1d3c17f9171a900015281028100000007118128004a874e7543fa1e4871b1280041fa1e38118028004e756100fd246700009871930480000000146a0000047000224412802040739300810000008043fa10422002ef88d3c0422900256100fe44670000466100eeda080000006700003a6100fe880c80000000036600000820016100ecc443fa100c2002ef88d3c0137c0001002520352c000080000001192b802c004ef94000e7546100fe5020086100fee443f9400d2cb013812800721d2b812c004ef94000e754721d2b812c004ef94000e74c6100fc7a6700003e73930081000000806100fe546b00002e2f002f08200191c86100fac0205f201f22006100fd4e670000144a81660000084ef94000dfdc4ef94000dff873107113b0816600fff24ef94000dfdc266f00722031bc00080000026600001c240bef8a41fa0f54d1c20828000000246700000808e8000100244ef94000b522226f00724fefffe848d70c1e28090c84000000086400006473b0980045fa1d14529224010282000000306700004e2404ef8a47fa0f06d7c226014a2b00246700000c72301181980052aa0004242a000852aa000802820000001fe78a45f22810243a1cbc248275ab0024e18a8481e18a8483e18a8484254200044cd70c1e4fef0018103098004ef94000c63c42b946c803504feffff048d7030641fa0e9e43fa129a72080828000000246700000e08e80001002408e800020024740471a900240c800000000166000008137c0002002443e9004053826600ffe441e8008053816600ffc24cd703064fef00104ef94000b2ce4200138088004feffff848d7020424012001ef8841fa0e32d1c04228002442a800184268002642a800586100e4d041fa12182002e188d1c070044228002542a8003441e8004053806600fff04cd702044fef00084ef94000686271930c80000000486500000c0c80000000606300004c080300086700001675b9800000006100fa86670000364ef94000e6e22f017200740705036700000c6100fa6c6700000405c153826a00ffec4a816700000c2601221f4ef94000e6e2221f200655807405b4804ef94000e45a0c82000000486500000c0c8200000060630000664feffff448d70007080400086700001e75b9800000006100fa12670000404cd700074fef000c4ef94000df9e7200740705046700000c6100f9f26700000405c153826a00ffec4a816700001228014cd700074fef000c4ef94000df9e4cd700074fef000c55807605b6804ef94000de16202a000423c046c7e9564fefffe848d71c1c24016100f9a4670000b277aa000228030484000000146a0000047800008300000080244447fa0cc42002ef88d7c04a2b00256600002870ff6100f5e84a806600006c4fefffec2e82203946c7e9562f4000044eb940042d1c6000001c422b00254fefffec2e82203946c7e9562f4000044eb94004271c4a806b00002e28002f40000c2f4a000842af0004203946c7e9562f4000104eb9400421584fef001420046100f580600000064fef00144cd71c1c4fef00184ef940062d1c4cd71c1c4fef0018202a00044ef9400625ea752a0002204241f02c002f0224016100f8cc241f4a806700001820020480000000146a000004700006800000016420404ef9400626344ab9400bebae6600014c4ab9460d17366700013c4a79460d174a670001324fefffd048d73cff75b9100b14cc6100f8784a806700011220175380660000fc760c0c81000000336700000444837ff9460d174a7c000d07670000862006d0b9460d174ceb882202283c0000091a4c041800d08173b9100b14d0283c00008ed84c041800d081247946c82456d5c045ea005947f9100161a7d7c073920c81000000ff66000006610000b4d2836a00000472000c810000007f6f000004727f14811681207946c82456203c0009b33272012181080023c1100f85984eb940027e0052860c86000000106d00ff6c2f024eb94009da20588f700123c0460d173a41f9460d1a9e4250426800024268000442680006426800084279460d10dc23c0460d17504eb9400418e04878ffff4eb94004d948588f4eb940027de44cd73cff4fef00304ef9400520064cd73cff4fef00304ef940051ff64ef940051fd6207946c8245671b9100b14cf223c000018b24c010800d1c02208d28206810008eda222417191d0802200d080d280d1c12202eb8992829282d1c1d1fc0008edaa73904e7570000c82000000076600000c4a3980000034660000086100f6f054804a804e752f0275b9800000006100ffd6241f4a80670000702f0043fa1c464a3a1c406600005841f9400d3988223c0000019212d853816600fffa43fa1c2641fa036f235800583350005c41fa0066234800f6223c40046d9c23410126422900692229018a0281ffff0fff0081000010002341018a41fa1bea10bc0001201f234000c620094e75203c400d39884e754feffff048d7030675b9800000006100ff4653806a00000470004cd703064fef00104e756100ffda2200202f0008b081630000042001e78841fa02edd1c0226f000412d86600fffc4e756100ffb42200202f000cd0af00086a0000047000b0816f00000420014e756100ff0c2f004ef940059d5c6100ff00204049e8003a47e800e24ef9400368666100feec20402f28018e2f28018a4ef9400368b86100fed820402f28018e2f28018a4ef9400369520680400d398822402069012a0c820000000b660000162f0224046100fe8a241f4a806700000641faff724ef94003ae4c41f98000083cb5fc00000005660000242f022f002404e68a6100f56467000010201f241f423008004ef94003af14201f241f118208004ef94003af14d1fc0008f04a719010814feffff048d7001e26002801202f0018242f0014220b610000684cd7001e4fef00104ef9400795c2d1fc0008eda277104fefffe848d7031720032401220476ff6100003e4cd703174fef001877104ef94007981ed1fc0008eda279104fefffe848d7030f20042239460d5c3076ff610000104cd7030f4fef00184ef94005a8504fefffd848d703ff53816600002c4a836a0000066100002c2e0020046100f51e6700001653876600000c20036100f50e66000006610000384cd703ff4fef00284e75207946c8245677b9100b14cf283c000018b24c043800d1c32602e58bd682d1c3d1fc0008f04b779028034e754feffff048d70303207946c8245671b9100b14cf223c000018b24c0108002202eb8992829282d28006810008edb370401180180043f91001614e1380180072484c02180041f980000810118018034cd703034fef00104e754feffff448d700c47e007001b0846600000a20036100f4602e002f2f00182f2f00182f2f00184eb940013a084fef000c7001b084660000644a876600005e20036100f434670000542c037400207946c8245671b9100b14cf223c000018b24c010800d1c0200206800008eda271b0080053806600001c2002e588d08206800008f04b71b00800b086660000066100ff1a52827008b0826600ffb44cd700c44fef000c4e7553594e5448434852440000564f494300004c45470000004f464600000000004d4f4e4f00000000504f4c5900000000257300256400000040008000c0010001030140016a018001c002000203028003000380040004030480050005800600068007000780080009000a000b000c000d000e000f001000008080800003070c0004070c0002070c0005070c000a0307000a0407000b040700050a07000603090002040700020307000e04070009030700090407000a06030006030c0008040c000a0e03000a0e04000b0e0400090e0400050a0f00050c1100070c13000c182400030c0f00040c10000a0c16000b0c170007100c00070f0c2d2d2d2d4d494e004d414a0053553200535534004d493700444f37004d4137003753340044493700414432004d443200414439004d4936004d4136004d37350044494d00415547004d493900444f39004d4139004d3639005155410034544800355448004f435400334d4900334d4100374d4900374d41004d4153004d495300000080000000800000005a82000049e700004000080008000800080008000800076806d4064c05ce055a04ef048d043203de03910349030802cb029302600230020501dc01b7019501750158013d0125010e00f900e500d300c300b400a60099008d00820078006e0066005e00560050004a0044003e003a00350031002d002a002600230021001e001c001a001800160014001200110010000e000d000c000b000a000a000900080008000700060006000500050005000400040004000300030003000300020002000200020002000200010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100014de54de54de54de54de54de54de54de54de54de54de54de54de54de54de54de548e5440b3f753b21370d33382f9e2c3f29162622235f20cc1e651c291a141824165714ac131e11ae10580f1c0df60ce60beb0b020a2a096308ab0800076306d1064b05ce055c04f20490043503e20395034e030c02d0029802640235020901e001bb01980179015b01400127011000fb00e800d600c500b600a7009a008e0083007900700067005f00570051004a0045003f003a00360032002e002a002700240021001e001c001a001800160014001300110010000f000d000c000b000b000a00090008000800070006000600060005000500040004000400030003000300000001000100020003000400060008000b001000170020002d0040005b008000880090009800a000a800b000b800c000c800d000d800e000e800f000f8010001100120013001400150016001700180019001a001b001c001d001e001f00200022002400260028002a002c002e00300032003400360038003a003c003e004000440048004c005000540058005c006000640068006c007000740078007c008000880090009800a000a800b000b800c000c800d000d800e000e800f000f8010001100120013001400150016001700180019001a001b001c001d001e001f00200022002400260028002a002c002e00300032003400360038003a003c003e004000ffff000000000192032404b5064607d609640af10c7c0e060f8d11121294141315901709187e19ef1b5d1cc61e2b1f8c20e7223d238e24da26202760289a29ce2afb2c212d412e5a2f6c307631793274336834533537361236e537b03871392b39db3a823b213bb63c423cc53d3f3daf3e153e723ec53f0f3f4f3f853fb13fd43fec3ffb40003ffb3fec3fd43fb13f853f4f3f0f3ec53e723e153daf3d3f3cc53c423bb63b213a8239db392b387137b036e536123537345333683274317930762f6c2e5a2d412c212afb29ce289a2760262024da238e223d20e71f8c1e2b1cc61b5d19ef187e170915901413129411120f8d0e060c7c0af1096407d6064604b5032401920000fe6efcdcfb4bf9baf82af69cf50ff384f1faf073eeeeed6cebedea70e8f7e782e611e4a3e33ae1d5e074df19ddc3dc72db26d9e0d8a0d766d632d505d3dfd2bfd1a6d094cf8ace87cd8ccc98cbadcac9c9eec91bc850c78fc6d5c625c57ec4dfc44ac3bec33bc2c1c251c1ebc18ec13bc0f1c0b1c07bc04fc02cc014c005c000c005c014c02cc04fc07bc0b1c0f1c13bc18ec1ebc251c2c1c33bc3bec44ac4dfc57ec625c6d5c78fc850c91bc9eecac9cbadcc98cd8cce87cf8ad094d1a6d2bfd3dfd505d632d766d8a0d9e0db26dc72ddc3df19e074e1d5e33ae4a3e611e782e8f7ea70ebeded6ceeeef073f1faf384f50ff69cf82af9bafb4bfcdcfe6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015900000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004feffff448d70302207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008eda271b028000c00000191fc0008eda26600002c2002c0fc001ed1c0d1fc0008edbc71d00c40464d6600001471a800020c0000016600000870016000000470004cd703024fef000c4a804e75205c4feffff848d7000524036100ff826700000641fab6da4cd700054fef000826c8487800104ef94000d51c4feffff848d70104242f000c6100ff566700002e203c000000a84c02080041f9800049d8d1c010bc00ff117c00010014203c000001004cd701044fef00084e754cd701044fef00084ef94000f45000000000000000000000000000000000000000000000000000000000000003000001000000000000000000000000030000030000000000000000000000000300000400000001800049d8000000000000fff10000000c000000a8000000000000fff10000001980001c80000000000000fff10000002046104d0c000000000000fff10000002740004008000000000000fff100000034800062a8000000000000fff10000003b800062a4000000000000fff100000042100b14f0000000000000fff10000005000024640000000000000fff10000005e00000448000000000000fff10000006e400aa294000000000000fff10000007800000003000000000000fff1000000850184cbb7000000000000fff10000008c01000000000000000000fff10000009400100000000000000000fff10000009e002ed1e0000000000000fff1000000a4000fffff000000000000fff1000000aa00000a44000000000000fff1000000b600000204000000000000fff1000000bf73000000000000000000fff1000000c7100b14ed000000000000fff1000000d0400d2cb0000000000000fff1000000d8400d2cd8000000000000fff1000000e1800065b2000000000000fff1000000ea800065b6000000000000fff1000000f30000000d000000000000fff1000000fb00000000000000000000fff10000010400000004000000000000fff10000010b00000008000000000000fff1000001120000000c000000000000fff10000011c00000010000000000000fff10000012400000014000000000000fff10000012c00000018000000000000fff1000001340000001c000000000000fff10000013d00000159000000000000fff100000149400d2ca8000000000000fff10000015280000810000000000000fff10000015a00000048000000000000fff1000001640000000b000000000000fff10000016c00000008000000000000fff1000001740000000e000000000000fff10000017b80004858000000000000fff100000185400d37f6000000000000fff10000018b00000192000000000000fff100000198400d31ae000000000000fff10000019f00000192000000000000fff1000001ad40031ed6000000000000fff1000001ba0008f04b000000000000fff1000001c30008f04a000000000000fff1000001cc1001614e000000000000fff1000001d800000003000000000000fff1000001e0460d5c30000000000000fff1000001ed400795c2000000000000fff1000001f84007981e000000000000fff1000002044005a850000000000000fff10000021040013a08000000000000fff10000021840013a08000000000000fff10000022000000500000000000000fff100000225ffffff80000000000000fff10000022f800000e0000000000000fff10000023780000110000000000000fff10000023f0000000c000000000000fff10000024600000003000000000000fff10000025046c80350000000000000fff10000025c40006862000000000000fff10000026500000080000000000000fff10000026f00000000000000000000fff10000027500000004000000000000fff10000027b00000008000000000000fff1000002810000000c000000000000fff10000028700000010000000000000fff10000028e00000014000000000000fff10000029500000018000000000000fff10000029c0000001c000000000000fff1000002a100000020000000000000fff1000002a900000024000000000000fff1000002ae00000026000000000000fff1000002b600000028000000000000fff1000002bc0000002c000000000000fff1000002c200000030000000000000fff1000002c900000034000000000000fff1000002d000000038000000000000fff1000002d50000003c000000000000fff1000002da00000040000000000000fff1000002e200000044000000000000fff1000002e600000048000000000000fff1000002ea0000004c000000000000fff1000002f200000050000000000000fff1000002f900000051000000000000fff10000030100000000000000000000fff10000030a00000004000000000000fff10000031300000008000000000000fff10000031b00000010000000000000fff10000032300000110000000000000fff10000032b00000052000000000000fff10000033000000054000000000000fff10000033700000058000000000000fff10000033e00000070000000000000fff10000034500000074000000000000fff10000034d00000078000000000000fff1000003550000005c000000000000fff10000035b00000040000000000000fff10000036146c82456000000000000fff10000036a100b14cf000000000000fff100000373100b14cc000000000000fff10000037c0008ee9a000000000000fff10000015b00000040000000000000fff10000038900000000000000000000fff10000038f00000004000000000000fff10000039500000008000000000000fff10000039b0000000c000000000000fff1000003a100000010000000000000fff1000003a800000014000000000000fff1000003af00000018000000000000fff1000003b60000001c000000000000fff1000003bb00000020000000000000fff1000003c300000024000000000000fff1000003cb00000025000000000000fff1000003d100000026000000000000fff1000003d900000028000000000000fff1000003df0000002c000000000000fff1000003e600000030000000000000fff1000000f400000034000000000000fff1000003ec00000038000000000000fff1000003f40000003c000000000000fff1000003fc0000003e000000000000fff10000040400000020000000000000fff10000040e00000000000000000000fff10000041600000001000000000000fff10000041b00000002000000000000fff10000042400000003000000000000fff10000042c00000004000000000000fff10000043500000008000000000000fff10000043d0000000c000000000000fff10000044500000010000000000000fff10000044f00000014000000000000fff10000045700000018000000000000fff1000004610000001c000000000000fff1000004690000001d000000000000fff1000004710000019d000000000000fff1000004780000008a000000000000fff100000480460d172a000000000000fff10000048b400d2cdc000000000000fff10000049540042d1c000000000000fff10000049e400e21e0000000000000fff1000004a61001614e000000000000fff1000004ae0000044d000000000000fff1000004b880000004000000000000fff1000004c080000002000000000000fff1000004c940042158000000000000fff1000004d446c7e956000000000000fff1000004dc00000000000000000000fff1000004e4400d3988000000000000fff1000004ea00000192000000000000fff1000004f70008f078000000000000fff10000050200000005000000000000fff10000050c40046d9c000000000000fff10000051780000034000000000000fff1000005214004271c000000000000fff10000052e0000007c000000000000fff1000005370000007d000000000000fff10000053e0000007e000000000000fff10000054600000025000000000000fff10000054d46c7dd26000000000000fff100000557800000a0000000000000fff10000056446c7d8ee000000000000fff10000056d00000038000000000000fff1000005770000012a000000000000fff10000058000000001000000000000fff1000005890000001f000000000000fff10000059400000003000000000000fff1000005a0000034f80000000000000001000005a90000025e0000000000000001000005b1000001d40000000000000001000005b90000030e0000000000000001000005c4000001b20000000000000001000005ca000021b60000000000000001000005d6000000de0000000000000001000005de000000a40000000000000001000005e6000000bc0000000000000001000005f1000000b60000000000000001000005fa000000c800000000000000010000060000002e68000000000000000100000608000000ce00000000000000010000060f0000011400000000000000010000061e000042d0000000000000000100000627000001160000000000000001000006340000014a00000000000000010000064000000122000000000000000100000649000001500000000000000001000006510000019200000000000000010000065b00000bd00000000000000001000006630000247e0000000000000001000006720000019e00000000000000010000067b00000bf6000000000000000100000684000006f200000000000000010000068c000001d000000000000000010000069300000bac00000000000000010000069b000002560000000000000001000006a5000002340000000000000001000006b0000007bc0000000000000001000006b8000002f60000000000000001000006c1000048c00000000000000001000006ce000002aa0000000000000001000006dc0000029e0000000000000001000006ec000002960000000000000001000006fc000002c4000000000000000100000709000004d000000000000000010000071200000332000000000000000100000720000008560000000000000001000007290008eda2000000000000fff10000073200000cba0000000000000001000007400000072200000000000000010000074d0000034e00000000000000010000075900002e9e000000000000000100000762000003a400000000000000010000076b000003f8000000000000000100000776000003ee000000000000000100000781000003ce000000000000000100000789000004320000000000000001000007950000048c00000000000000010000079f0000046a0000000000000001000007a900002ff20000000000000001000007b0000004ca0000000000000001000007bc000030f20000000000000001000007c4000004a80000000000000001000007cf000004b80000000000000001000007da000006060000000000000001000007e2000005100000000000000001000007eb0000052c0000000000000001000007f3000015860000000000000001000007fb000032f40000000000000001000008020000055c00000000000000010000080a000005e800000000000000010000081200000d2e000000000000000100000819000015800000000000000001000008250000061400000000000000010000082d000006780000000000000001000008380000064e0000000000000001000008430000067a00000000000000010000084b0000068e000000000000000100000854000006de00000000000000010000085d000006d2000000000000000100000866000006da00000000000000010000086f0000072e00000000000000010000087b000007440000000000000001000008870000075a0000000000000001000008930000076a00000000000000010000089e000007820000000000000001000008a80000077e0000000000000001000008b3000007880000000000000001000008bc000007900000000000000001000008c7000008480000000000000001000008d20000083a0000000000000001000008db0000084a0000000000000001000008e7000008940000000000000001000008f1000040fc0000000000000001000008fa000008ce00000000000000010000090600000d50000000000000000100000913000008a000000000000000010000091e000008c400000000000000010000092a000008d2000000000000000100000936000008fc0000000000000001000009430000231600000000000000010000094b0000093c0000000000000001000009560000092e00000000000000010000095f000011c800000000000000010000096b0000095200000000000000010000097800000d60000000000000000100000981000009a800000000000000010000098c000009aa000000000000000100000995000009ec00000000000000010000099e00000a0c0000000000000001000009a700000a140000000000000001000009af00000b900000000000000001000009ba00000a480000000000000001000009c500000a7c0000000000000001000009d000000aae0000000000000001000009da00000aa40000000000000001000009e800000af00000000000000001000009f200000b320000000000000001000009fc00000b28000000000000000100000a0600000b54000000000000000100000a1000000b8c000000000000000100000a1b00000b4c000000000000000100000a2600000b54000000000000000100000a3200000b6e000000000000000100000a3d000038f8000000000000000100000a4700000bb8000000000000000100000a5000000bdc000000000000000100000a5900000bea000000000000000100000a6200000c0c000000000000000100000a6d00000c30000000000000000100000a7800000c22000000000000000100000a8300000c32000000000000000100000a8e00000c36000000000000000100000a99000040f8000000000000000100000aa000000c92000000000000000100000aa900000caa000000000000000100000ab100000ce2000000000000000100000abc00000ce4000000000000000100000ac700000d1a000000000000000100000ad200000cf8000000000000000100000ada00000d16000000000000000100000ae600000d6c000000000000000100000af100000d78000000000000000100000afd00000dd6000000000000000100000b0900000daa000000000000000100000b1200002444000000000000000100000b1f00000dce000000000000000100000b2b00000e4a000000000000000100000b3500000e48000000000000000100000b3f00000de8000000000000000100000b4900000e44000000000000000100000b5600000dfa000000000000000100000b6000000e06000000000000000100000b6a00001472000000000000000100000b7200000e68000000000000000100000b7b00000f34000000000000000100000b8400000ee6000000000000000100000b8f00000e96000000000000000100000b9a00000ed8000000000000000100000ba40000111e000000000000000100000bae00000efc000000000000000100000bb800002308000000000000000100000bc400000f2a000000000000000100000bce00000f5c000000000000000100000bda00000f3e000000000000000100000be400000f52000000000000000100000bef00000f74000000000000000100000bf800000f96000000000000000100000c0100000fbe000000000000000100000c0c000041fc000000000000000100000c1400001a60000000000000000100000c1f00000fc2000000000000000100000c2b00002ede000000000000000100000c3500002fde000000000000000100000c3d00000fdc000000000000000100000c4a00001024000000000000000100000c5500001120000000000000000100000c6200000fe6000000000000000100000c6a00000ff0000000000000000100000c7600000ffa000000000000000100000c8300001004000000000000000100000c8d00001512000000000000000100000c960000111e000000000000000100000ca100001522000000000000000100000caa00001060000000000000000100000cb500001066000000000000000100000cbf000010ce000000000000000100000cca0000110a000000000000000100000cd6000010dc000000000000000100000ce1000010fa000000000000000100000cec000011aa000000000000000100000cf6000011a8000000000000000100000cff00001162000000000000000100000d0a00001172000000000000000100000d1600004200000000000000000100000d1f00001178000000000000000100000d2700001194000000000000000100000d2f000011e0000000000000000100000d3b000011ec000000000000000100000d46000011f8000000000000000100000d520000122c000000000000000100000d5b0000135c000000000000000100000d6800001254000000000000000100000d720000128e000000000000000100000d7d00001266000000000000000100000d8700001286000000000000000100000d910000129e000000000000000100000d9a0000133c000000000000000100000da6000012d6000000000000000100000db20000132c000000000000000100000dbd00001356000000000000000100000dc800001364000000000000000100000dd200001380000000000000000100000ddd00001454000000000000000100000dea00001416000000000000000100000df500001448000000000000000100000dff00001426000000000000000100000e0900001420000000000000000100000e1300001446000000000000000100000e1f00001470000000000000000100000e2900001510000000000000000100000e33000014b8000000000000000100000e3e000014c6000000000000000100000e47000014f8000000000000000100000e52000014e0000000000000000100000e5b000014fa000000000000000100000e6400001506000000000000000100000e6e0000157c000000000000000100000e7a0000152e000000000000000100000e8300001578000000000000000100000e8b0000153e000000000000000100000e9600001564000000000000000100000ea100001588000000000000000100000eaa000015b2000000000000000100000eb5000015a4000000000000000100000ec1000015c0000000000000000100000ece000015c8000000000000000100000eda0000169a000000000000000100000ee50000161c000000000000000100000ef0000016a6000000000000000100000efc000016ac000000000000000100000f06000016c8000000000000000100000f10000016b4000000000000000100000f1a000016d6000000000000000100000f250000170e000000000000000100000f3300001704000000000000000100000f400000173a000000000000000100000f4b0000186c000000000000000100000f56000017ee000000000000000100000f63000017b4000000000000000100000f6e000017cc000000000000000100000f7c000017c6000000000000000100000f88000017d8000000000000000100000f91000017de000000000000000100000f9b00004404000000000000000100000fa600004400000000000000000100000fb30000186a000000000000000100000fbe00001806000000000000000100000fc900002e6d000000000000000100000fd10000189c000000000000000100000fde00002e73000000000000000100000fe600001872000000000000000100000ff200001884000000000000000100000ff90000188600000000000000010000100000002e9a000000000000000100001007000018e0000000000000000100001013000018fa00000000000000010000101e00002f5e0000000000000001000010270000190a0000000000000001000010300000191800000000000000010000103a00001926000000000000000100001044000045980000000000000001000010520000459600000000000000010000105e0000198a0000000000000001000010690000194c000000000000000100001074000019540000000000000001000010800000196e0000000000000001000010900000196200000000000000010000109b0000197a0000000000000001000010a6000019da0000000000000001000010b200001a040000000000000001000010be00001a5a0000000000000001000010c900001a500000000000000001000010d3000019fe0000000000000001000010dd00001a4e0000000000000001000010ea00001a1c0000000000000001000010f600001a1e00000000000000010000110300001a4600000000000000010000111000001a7600000000000000010000111800001be600000000000000010000112000001a9400000000000000010000112600001a9600000000000000010000112c00001c4400000000000000010000113200001abe00000000000000010000113800001ac600000000000000010000113e00001c2400000000000000010000114600001ad800000000000000010000114c00001ae400000000000000010000115200001aee00000000000000010000115800001b1200000000000000010000115e00001afe00000000000000010000116400001b2600000000000000010000116a00001b5400000000000000010000117000001b3200000000000000010000117600001b4400000000000000010000117d00001b6400000000000000010000118300001b6600000000000000010000118900001b8600000000000000010000118f00001b9200000000000000010000119500001bc800000000000000010000119d00001ba20000000000000001000011a300001ba40000000000000001000011a900001bba0000000000000001000011af00001bca0000000000000001000011b500001be80000000000000001000011bb00001bea0000000000000001000011c100001c100000000000000001000011c700001c280000000000000001000011cd00001c360000000000000001000011d300001c580000000000000001000011da00001c5a0000000000000001000011e300001c700000000000000001000011e900001cae0000000000000001000011f100001c880000000000000001000011f700001c8a0000000000000001000011fd00001ca000000000000000010000120300001cb400000000000000010000120900001e1a00000000000000010000121100001cd800000000000000010000121700001d5000000000000000010000121e00001d3600000000000000010000122600001d2400000000000000010000122f00001cf800000000000000010000123500001cfa00000000000000010000123b00001d8200000000000000010000124500001e1000000000000000010000124c00001d4e00000000000000010000125500001d7e00000000000000010000125b00001d6600000000000000010000126100001d9200000000000000010000126700001d9400000000000000010000126d00001df600000000000000010000127300001dac00000000000000010000127a00001dea00000000000000010000128000001dd000000000000000010000128700001e2e00000000000000010000128e00001f7000000000000000010000129800001f7c0000000000000001000012a500001f540000000000000001000012af00001ec00000000000000001000012b900001ed40000000000000001000012c400001eec0000000000000001000012cd00001f020000000000000001000012d700001f3c0000000000000001000012e200001f9a0000000000000001000012ee00001f980000000000000001000012f800001fa600000000000000010000130300001fb000000000000000010000130f0000218c00000000000000010000131800001ff600000000000000010000132500001fe80000000000000001000013300000201a00000000000000010000133a000020320000000000000001000013430000211e00000000000000010000134d0000206c0000000000000001000013550000207c0000000000000001000013600000212800000000000000010000136c000020f20000000000000001000013750000210600000000000000010000137e0000222c0000000000000001000013870000222e0000000000000001000013910000223a0000000000000001000013a0000022a60000000000000001000013a9000022660000000000000001000013b40000227e0000000000000001000013c2000022780000000000000001000013ce0000228a0000000000000001000013d7000022900000000000000001000013e1000022a80000000000000001000013eb000022b40000000000000001000013f800002306000000000000000100001402000022fe00000000000000010000140c000023000000000000000001000014160000233a0000000000000001000014210000235600000000000000010000142b00002342000000000000000100001436000023540000000000000001000014400000236000000000000000010000144d00002370000000000000000100001458000023960000000000000001000014620000239400000000000000010000146c0000238a000000000000000100001478000023a0000000000000000100001485000023b0000000000000000100001490000023cc00000000000000010000149c000023c60000000000000001000014a7000023f00000000000000001000014b1000023dc0000000000000001000014bc000023d20000000000000001000014c8000023e20000000000000001000014d4000023fa0000000000000001000014e2000042c80000000000000001000014eb000042c00000000000000001000014f40000243a0000000000000001000014fe000042400000000000000001000015070000247c0000000000000001000015110000252e00000000000000010000151e000024a60000000000000001000015290000250e000000000000000100001536000024ea0000000000000001000015460000257e000000000000000100001554000025780000000000000001000015610000258e000000000000000100001568000025b800000000000000010000157300002638000000000000000100001581000042f000000000000000010000158a0000260a000000000000000100001598000026620000000000000001000015a2000026780000000000000001000015ac0000267a0000000000000001000015b60000268e0000000000000001000015c1000026ea0000000000000001000015cb000027200000000000000001000015d60000276a0000000000000001000015e20000273c0000000000000001000015ed000027420000000000000001000015f8000027520000000000000001000016030000276800000000000000010000160e0000278c00000000000000010000161a000027f0000000000000000100001627000027b8000000000000000100001633000027e800000000000000010000163f000027bc00000000000000010000164b000027cc000000000000000100001657000028c80000000000000001000016630000282a00000000000000010000166d0000286a000000000000000100001677000028ba0000000000000001000016820000288400000000000000010000168f000028b60000000000000001000016990000290a0000000000000001000016a3000029020000000000000001000016ad400bebae000000000000fff1000016b540051fd6000000000000fff1000016c340051ff6000000000000fff1000016d340052006000000000000fff1000016e0460d1736000000000000fff1000016e9460d174a000000000000fff1000016f3460d174c000000000000fff1000016fd460d16f0000000000000fff100001707100b14cc000000000000fff100001711100b14d0000000000000fff1000017190000091a000000000000fff10000172400008ed8000000000000fff10000172f00000059000000000000fff1000017380009b332000000000000fff100001743100f8598000000000000fff10000174a40027e00000000000000fff10000175240027de4000000000000fff1000017594009da20000000000000fff100001761460d173a000000000000fff10000176a460d1a9e000000000000fff100001773460d10dc000000000000fff10000177c460d1750000000000000fff100001785400418e0000000000000fff10000178d4004d948000000000000fff1000017940008edaa000000000000fff10000179f0000000c000000000000fff1000017a800000033000000000000fff1000017af00002a640000000000000001000017ba00002a5e0000000000000001000017c700002a560000000000000001000017d800002a480000000000000001000017e60000295c0000000000000001000017f0000029640000000000000001000017fa000029ee000000000000000100001805000029b800000000000000010000181000002a6a00000000000000010000181d000029c0000000000000000100001826000029cc00000000000000010000182f00002aae00000000000000010000183b00002ac400000000000000010000184700002aca00000000000000010000185100002ace00000000000000010000185d00002b500000000000000001000018690000472c0000000000000001000018780000472a00000000000000010000188500002b4600000000000000010000189000002afc00000000000000010000189b00002e790000000000000001000018a200002b7c0000000000000001000018ad00002b580000000000000001000018b700002b720000000000000001000018c100002b8e0000000000000001000018c800002e7f0000000000000001000018d400002b9a0000000000000001000018dd00002ba20000000000000001000018e800002bb60000000000000001000018ef00002bbe0000000000000001000018f600002c3200000000000000010000190000002c6a00000000000000010000190c00002c6600000000000000010000191900002cfe00000000000000010000192300002d3600000000000000010000192d00002d1600000000000000010000193900002d4000000000000000010000194600002d3200000000000000010000195200002d6c00000000000000010000196000002dde00000000000000010000196b00002e5e00000000000000010000197500002e1000000000000000010000198100002e5400000000000000010000198c00002e97000000000000000100001993000048c000000000000000010000199a000029100000000010000001000019a4000011ac0000000010000001000019ae000013a60000000010000001000019b90000201c0000000010000001000019c3000019960000000010000001000019cb00001e300000000010000001000019d5000031f20000000010000001000019e00000192a0000000010000001000019ea000000200000000010000001000019f4000016e00000000010000001000019fc000016ea000000001000000100001a050000173c000000001000000100001a1000002490000000001000000100001a170000253a000000001000000100001a1f0000270a000000001000000100001a2800002778000000001000000100001a32000027fc000000001000000100001a3a000028da000000001000000100001a4300002bc0000000001000000100001a4f00002bcc000000001000000100001a5b00002be0000000001000000100001a6700002bf4000000001000000100001a7300002c08000000001000000100001a7e00002c38000000001000000100001a8900002c74000000001000000100001a9300002ca6000000001000000100001a9e00002cd2000000001000000100001aaa00002dc4000000001000000100001ab50000264a000000001000000100001abd000026b0000000001000000100001ac5000025be000000001000000100001acf00004930000000001000000100001ae20000495c000000001000000100564f4943455f4241534500564f4943455f53545249444500435552534f52004e4942424c450053544f434b5f52454e4445520046505f5054520052535f5054520053455454494e47535f424153450053455454494e47535f5350414e0053455454494e47535f5354524944450050495443485f544142004f55545055545f53484946540043345f494e4300454e565f4f4e4500454e565f464c4f4f52004b5f4e554d004b5f4d415800494e4445585f5343414c450046425f5343414c4500494e435f4d415800474c4944455f4154004b4559535f415400434c4f434b5f4154005345515f53544550005345515f5449434b0043565f484f4c4400434b5f5449434b5300434b5f54505300434b5f46505300434b5f4652414d455300434b5f4c41535400434b5f50494e4700434b5f5354455000434b5f4653544550004650535f44454641554c54005343414c455f41540043555256414c530043565f5354524944450043565f434852440043565f564f49430043565f52454c004c464f5f5354415445004c464f5f50004c464f5f444553435f4c454e00464c45585f5000464c45585f444553435f4c454e005245534f4c5645525f52455400534c4f545f4f464600534c4f545f434f4c00504152545f534841444f570043565f52415445004c49535457494e5f4d4143480041535349474e5f524554004d41434857494e5f524554004c49535457494e5f524554004c4f414453454c5f535052494e54460053454d490053484150455f454e440050494e475f4154004453505f5245430043565f41544b004355545f534849465400474c4f42414c5f574f5244004b494c4c5f5245540053545f53545249444500535f50484300535f50484d00535f454e5600535f494e4300535f494e434d00535f4945464600535f4741494e00535f464200535f4c4153544d00535f4f4e00535f475052455600535f43555200545f52454600545f4c41535400545f4d41534b00545f444b00545f524b00545f524154494f00545f4900545f5700545f5343414c4500545f504f4c5900535f4552414d500052545f43414c4c530052545f46495845440052545f484541440052545f52494e470052545f53495a4500545f414b00545f474d415800535f4854494d00545f5241575000545f534d41534b00545f4c4153544b00545f504f5300565f504f5300504152545f50545200504152545f4944580055495f545241434b004c464f5f504147455f4f464600565f50484300565f50484d00565f454e5600565f494e4300565f494e434d00565f4945464600565f4741494e00565f464200565f4c4153544d00565f535441544500565f4b455900565f495354455000565f43555200565f524f4f5400565f41474500565f4652414d4500565f475052455600565f4552414d500043525f5354524944450043525f535445500043525f4e0043525f57524f54450043525f504b45590043525f4652414d450043525f4b4559530043525f524157530043525f504652414d450043525f504354580043525f505449434b530043525f505241570043525f564f4943004b525f57494e004b525f434f4e46004c4956455245435f415400515a5f4f43545f4154005245435f54524947005041545f52414d005041545f4d4952005041545f4e55444745005041545f4355520042414e4b5f435552004c4f434b5f5752495445005245435f4354580043565f5054434800414d505f5000414d505f444553435f4c454e00414d505f50325f4f4646004c45475f534c4f5432004c45475f574944474554004d41535445525f4f4e005245435f545249474c45535300545f4c45474b455900545f4c45474e00535f495354455000545f4d4c45470046554e435f48454c440046554e435f454e435f4f4646004b45595f48454c4400505553485f434f4445004b4e5f534c4f5453004b4e5f524154494f004b4e5f524154494f5f4e004b4e5f535542554e4954590073795f73746174650073795f63616c6c0073795f69736f6e0073795f6d616368696e650073795f6e6f00706f5f69735f73796e74680073795f636f6c640073795f7363616e0073795f7363616e6e65640073795f7363616e310073795f666d0073795f6e616d650073795f636d700073795f636f6c645f6e6f686f6c6400706f5f636c6f636b0073795f636f6c645f686f6c640073795f7761726d5f656e760073795f636f6c64310073795f7761726d0073795f746f706f6c7900706f5f6661646500706f5f6d72696e675f72657365740073795f73796e746800706f5f63617272790073795f776f72640073795f73657400706f5f667265650073795f73616d706c650073795f6e65757472616c0073795f736c65770073795f766f69636500666d5f69735f6e617469766500666d5f73746f636b5f63616c6c00666d5f656d7074795f736f7572636500666d5f636c6561725f736f7572636500666d5f63616c6c5f646f6e650073795f636865636b0073795f6d6f6e6f5f6672616d6500706f5f6672616d65004d4143485f4f464600706f5f666164655f6672616d6500706f5f726174655f666f6c640073795f6d6f6e6f5f696e630073795f726174696f0073795f656e765f6b0073795f656e76646f6e650073795f6e6f64656361790073795f656e763100706f5f6d6f6e6f5f656e7600706f5f6d655f72656c00706f5f6d655f61746b00706f5f61746b00706f5f6d655f73746f726500706f5f72656c6b00706f5f6d655f72656c3000706f5f6d655f72656c310073795f646f6e650073795f616c6976650073795f6d6f6e6f00706f5f66696c6c0073795f7461620073795f6c6f6f700073795f67656e6400706f5f616e7900706f5f66696c6c5f61646400706f5f7469636b00706f5f746b5f646f6e6500706f5f746b5f7374657000706f5f7261746500706f5f726174653100706f5f726174653200706f5f726174653300706f5f726174653400706f5f72665f666f6c643100706f5f72665f666f6c643200706f5f72665f666f6c643300706f5f72665f7261746500706f5f72665f6f757400706f5f72665f646f776e0073795f676c69646500706f5f6c6567627974650073795f736c5f736e61700073795f736c5f75700073795f736c5f73746f726500706f5f66725f72656300706f5f63686f726400706f5f66725f737461727400706f5f766f696365735f6f6600706f5f66725f676f6e6500706f5f66725f676f6e653100706f5f66725f6d676f6e6500706f5f66725f6d676f6e653100706f5f68656c6400706f5f66725f6e6f6c6800706f5f66725f6c6800706f5f68616e646f76657200706f5f66725f706172616d7300706f5f737461727400706f5f66725f686f6c6400706f5f66725f646b00706f5f66725f726b00706f5f66725f616b00706f5f66725f7600706f5f66725f6e65787400706f5f66725f696e633100706f5f66725f656e766b00706f5f66725f656e7600706f5f66725f656e76686f6c6400706f5f66725f616d7000706f5f66725f72656c00706f5f66725f63757400706f5f66725f63617000706f5f66725f6761696e00706f5f66725f72656c3100706f5f66725f72656c316100706f5f66725f72656c3200706f5f766f6963657300706f5f667265653100706f5f666164653100706f5f666164653200706f5f63615f7069636b00706f5f63615f6672656500706f5f63615f6e65787400706f5f63615f74616b6500706f5f63615f636f707900706f5f73657100706f5f63615f677000706f5f63615f6700706f5f66665f7374657000706f5f66665f6c6f6f7000706f5f66665f6e65787400706f5f66665f3100706f5f66665f73746f726500706f5f73745f666f6c6400706f5f73745f666f6c643100706f5f73745f70616e656c00706f5f73745f6d3100706f5f6d72696e675f706f7000706f5f73745f6d646f6e6500706f5f73746172743100706f5f73745f6f757400706f5f73745f73773100706f5f73745f7377646f6e6500706f5f73745f73773200706f5f73745f73773300706f5f736e617000706f5f73745f316100706f5f73745f676f00706f5f73745f6473657100706f5f73745f646b657900706f5f73745f646b3100706f5f73745f64757000706f5f73745f64733100706f5f6d68656c645f6f6600706f5f73745f64733200706f5f73745f63686f726400706f5f73745f72656c00706f5f73745f72656c3100706f5f73745f6e7300706f5f73745f763100706f5f73745f726f773000706f5f7662756600706f5f766f6963696e6700706f5f73745f736861706500706f5f73686170657300706f5f676d617800706f5f73745f73696e676c6500706f5f73745f6e6f746500706f5f73745f63707265737300706f5f73745f6b00706f5f73745f636f756e7400706f5f73745f636f756e743100706f5f73745f63757400706f5f737465616c00706f5f73745f646f6e6500706f5f616c6c6f6300706f5f73745f7761726d00706f5f73745f656e7600706f5f73745f68696e6600706f5f73745f737461746500706f5f73745f6c69766500706f5f73745f6c6b657900706f5f63705f6f757400706f5f63705f6e6f00706f5f63705f6d69646900706f5f63705f636f756e7400706f5f6d68656c6400706f5f63705f3100706f5f63705f3200706f5f686f5f666f6c643000706f5f686f5f666f6c6400706f5f686f5f666f6c643100706f5f686f5f763100706f5f686f5f73696e676c6500706f5f686f5f636f6c00706f5f686f5f636f6c3200706f5f686f5f696e7300706f5f686f5f70757400706f5f686f5f617300706f5f686f5f657874726100706f5f686f5f667265736800706f5f686f5f6e65787400706f5f686f5f646f6e6500706f5f686f5f6e657700706f5f686f5f6e65773100706f5f6c6d5f73616d706c6500706f5f6c6d5f7061726100706f5f6c6d5f6f757400706f5f6c6d5f6c656700706f5f6c6d5f74776f00706f5f6c6d5f746872656500706f5f6c6d5f72657400706f5f736e5f72657400706f5f736e5f6469737400706f5f736e5f6c6f00706f5f736e5f646f776e00706f5f736e5f686900706f5f736e5f757000706f5f736e5f6f757400706f5f72616e6b5f61637400706f5f616c5f676f00706f5f72616e6b00706f5f616c5f6c6f6f7000706f5f616c5f6e65787400706f5f66696c6c3100706f5f66695f68616c6600706f5f66695f636c65617200706f5f66695f766f6963657300706f5f66695f766f69636500706f5f66695f6e65787400706f5f66695f6c6f6f7000706f5f66695f756e69747900706f5f66695f6f757400706f5f66695f73617400706f5f66695f70757400706f5f66695f7361747000706f5f6c666f335f646570746800706f5f6c33625f646570746800706f5f6c335f6261636b00706f5f6c705f646f6e6500666d5f6c666f5f636c6f6e6500706f5f6c705f7363616e00706f5f6c705f7363616e6e656400706f5f6c705f7363616e3100706f5f6c705f666d00706f5f6c705f636d7000706f5f6c666f6465736300706f5f6c666f5f6275696c7400706f5f6c705f6861766500706f5f6c705f636f707900706f5f6368726400706f5f666d745f63686f726400706f5f766f696300706f5f666d745f766f696300706f5f66763100706f5f66763200706f5f665f6400706f5f66635f736861706500706f5f66635f6e616d6500706f5f6e616d657300706f5f66635f637000706f5f66635f696e7600706f5f66635f656e6400706f5f7067646573635f62756600706f5f70675f6275696c7400706f5f70675f6861766500706f5f70675f636f707900706f5f70675f706174636800706f5f70675f6275696c745f6e6f7700706f5f70675f6279746500706f5f70675f6b6e6f6200706f5f6b6e5f706c61696e00706f5f6b6e5f726174696f00706f5f6b6e5f6a756d7000706f5f6b6e5f6f757400706f5f6b6e5f6f6e6500706f5f6b6e5f725f646f6e6500706f5f6b6e5f725f64657400706f5f6b6e5f725f7363616e00706f5f6b6e5f725f74616b650070765f6c6f61640070765f736f72740070765f64310070765f64320070765f70630070765f64330070765f64340070765f646964780070765f69310070765f69320070765f72310070765f72330070765f72320070765f62310070765f62330070765f62320070765f6232610070765f6f310070765f6f320070765f6f330070765f6f340070765f636f70790070765f70310070765f70320070765f70330070765f63310070765f73310070765f73320070765f73330070765f78310070765f78320070765f70633100706f5f6d6174636800706d5f6c3100706d5f6261736500706d5f703100706d5f703200706d5f703300706d5f693100706d5f7063313200706d5f653100706d5f74727900706d5f6b65657000706d5f666f756e6400706d5f653200706d5f653300706d5f7365636f6e6400706d5f6f757400706d5f6b5f6f757400706d5f6e6f00706d5f743100706d5f633100706d5f633200706d5f633400706d5f63326100706d5f633300706d5f63326200706d5f70633100706f5f6b725f6f757400706f5f6b725f736574746c6500706f5f6b725f6e657700706f5f6b725f616e7900706f5f6b725f6a6f696e00706f5f6b725f637000706f5f6b725f63703100706f5f6b725f66756c6c00706f5f6b725f777269746500706f5f6b735f6f757400706f5f6b775f726f6f7400706f5f6b775f726f6f743100706f5f6c6f636b7700706f5f6b775f72656d6f766500706f5f6b775f766f696300706f5f6b775f6f757400706f5f726c5f696400706f5f726c5f6f757400706f5f726c5f6b00706f5f726c5f68616e6400706f5f726c5f6e7564676500706f5f726c5f733100706f5f726c5f733200706f5f69735f6e6f00706f5f69735f6f757400706f5f736c6f745f6d61726b657200706f5f736d5f6e6f00706f5f736d5f7363616e00706f5f736d5f7363616e6e656400706f5f736d5f7363616e3100706f5f736d5f666d00706f5f736d5f636d7000706f5f736d5f6f757400706f5f706f6c79747261636b00706f5f70745f72657400706f5f70745f79657300706f5f70745f6f757400706f5f68645f6d69646900706f5f68645f6f757400706f5f68645f7363616e00706f5f68645f79657300706f5f6d68656c645f61646400706f5f6d615f7363616e00706f5f6d615f6f757400706f5f6d615f70757400706f5f6d615f736869667400706f5f6d68656c645f64656c00706f5f6d645f7363616e00706f5f6d645f666f756e6400706f5f6d645f6e6f6e6500706f5f6d645f6f757400706f5f6d645f6c61737400706f5f6d645f736869667400706f5f6d645f636f756e7400706f5f6d72696e675f7075736800706f5f6d7461696c00706f5f6d6865616400706f5f6d705f6f757400706f5f6d72696e6700706f5f6d715f6f757400706f5f6d6f6e5f73746f636b00706f5f6d6f6e5f72617700706f5f6d6f6e5f737461727400706f5f6d6f6e5f747269676c65737300706f5f6d6f66665f73746f636b00706f5f6d6f66665f6d6f726500706f5f72656c00706f5f72656c5f6f757400706f5f7265747269675f6f757400706f5f72746c6f6700706f5f7265747269675f6c6f6700706f5f73746f705f7400706f5f73746f705f7000706f5f73746f705f7600706f5f73746f705f763100706f5f6b696c6c5f7600706f5f6d675f7769646500706f5f6d675f73746f636b00706f5f6d675f6d61736b00706f5f6d675f7363616e00706f5f6d675f6e65787400706f5f6d675f6e6f6e6500706f5f6d6f675f7769646500706f5f6d6f675f73746f636b00706f5f6d6f675f6d61736b00706f5f6d6f675f6e6f6e6500706f5f6d6f675f7363616e00706f5f6d6f675f6e65787400706f5f6d725f73746f636b00706f5f6d725f72617700706f5f6d725f6c656700706f5f6d725f646f6e6500706f5f6d725f706c6163656400706f5f6d725f706f7000706f5f6d745f6f757400706f5f6d745f7261770053454c5f57494e004f43545f484f4f4b5f4f50454e004f43545f484f4f4b5f434c4f534544004f43545f484f4f4b5f52545300475249445f5245430048454c445f4d41534b0048454c445f5041474500545249475f4d4f444500545241434b5f435552005041545f4944580054524b5f535452494445005041545f535452494445004c4f434b5f4f46460042414e4b5f4449525459004449525459320045445f544944590045445f454e440045445f53454e540045445f464c4147310045445f574f5244530045445f574f5244360045445f464c4147320045445f43414c4c0052454452415700504c41595f534c4f5453004f43545f4a554d50004b45595f555000706f5f6f635f6f70656e00706f5f6f635f636c6f73656400706f5f6f635f706f705f636c6f73656400706f5f6f635f7377616c6c6f7700706f5f6f635f64697200706f5f6f635f62697400706f5f6f635f6e65787400706f5f6f635f6861766500706f5f706172745f7074636800706f5f6f635f6c6f00706f5f6f635f686900706f5f616d705f6b696e6400706f5f616b5f617564696f00706f5f616b5f6f757400706f5f616d705f6465736300706f5f61645f73746f636b00706f5f616d70646573635f62756600706f5f616d705f6275696c7400706f5f61645f6861766500706f5f61645f636f707900706f5f6c656700706f5f666d745f6c656700706f5f6c65676d617800706f5f6c785f6f757400706f5f666c3100706f5f6c65676e616d657300706f5f666c5f637000706f5f6c65676b6e6f6200706f5f6c6b3100706f5f6c6b3200706f5f61655f6f757400706f5f616c5f73746f726500706f5f616c5f73616d706c6500706f5f626563616d6500706f5f62635f6f757400706f5f62635f736c6f747300706f5f666c65785f736c6f7400706f5f62635f726573657400706f5f66696e655f726573657400706f5f6c735f63616c6c00706f5f6c735f6f757400706f5f6c735f747261636b00706f5f6c735f6e65787400706f5f665f7300706f5f656e6400706f5f6f637461766500706f5f6c65676b657900706f5f6c65676d6f646500706f5f6b657972656c00706f5f6b6e6f6200706f5f6b657972656300706f5f686f6c6431323800706f5f7067646573630073795f72656e64657200706f5f6c666f3300706f5f6c666f336200706f5f6c666f7061676500706f5f6d6f6e00706f5f6d6f666600706f5f6d6761746500706f5f6d6f6761746500706f5f6d72656300706f5f6d7472696700706f5f616d70737461676500706f5f616d70647261773100706f5f616d70647261773200706f5f616d70647261773300706f5f616d706564697400706f5f616d706c616e6500706f5f61737369676e00706f5f6d61636877696e00706f5f6d6163686c69737400706f5f6c6f616473656c00706f5f73746f7000706f5f6b696c6c00706f5f72657472696700666d5f736f757263655f646973706174636800666d5f737461727400000000000000000101000029100000000400000101000011ac0000000800000101000013a60000000c000001010000201c000000100000010100001996000000140000010100001e300000001800000101000031f20000001c000001010000192a002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e62737300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000001000000060000000000000034000049aa000000000000000000000004000000000000001b0000000400000040000000000000916c000000600000000500000001000000040000000c00000026000000010000000300000000000049e000000000000000000000000000000004000000000000002c000000080000000300000000000049e0000000000000000000000000000000040000000000000001000000020000000000000000000049e000002ca000000006000002a90000000400000010000000090000000300000000000000000000768000001aeb0000000000000000000000010000000000000011000000030000000000000000000091cc0000003100000000000000000000000100000000", - "sha256": "139526bd2fc752ced11f71ad73f305d5d69721cf6bbe9d6fc379743bae3e0c64" + "bytes": 42244, + "code": "7f454c46010201000000000000000000000100040000000100000000000000000000a3c40000002600340000000000280008000700000000000000000000000000000000000000000000000000000000000000004fefffd048d77cfc247980001c80242f00342602ef8b47fa3c04d7c32879800062a878007210b2af00406600023041f946104d0c41f028000810000467000194610002ce538066000168610027f86600006e263c000000a84c02380041f9800049d8207038082608670001460483100b14f00c8300024640640001362248263c000000ff7398670000140c810000002f66000004224853836600ffea323c464db25166000004548941fa34e2760573987b99b285660000fa53836600fff272484c02180041f98000081073b0180d0c810000007f67000026610004ec6600001e41fa383473f01a0041fa4b0a202800084c001000ee895281600000047200274100584a6b00266700000e610005026700002e61000d1e429342ab000442ab002042ab0018426b002642ab0014422b0051223c010000002741000860000012177c00ff0051223c01000000274100082079800062a442a8000472484c02180041f98000081073b0180855810c810000000253c1174100506600001861000c78610029bc41f9400d2cb0423028006000000e4a6b00266700000661000c986100070e2206e189e9892741002876016000002061000c06426b002641f9400d2cb04230280073d4e189e989274100287600174300244a2b00246700007c41f98000081072484c021800423018312239800000e0028100000001e189e3892002ed88d28041f980000110d1c14250317c7f000002317c7f0000047bec000673d4e08927410070610006884a2b0050660000066100074638bc4000397c7f0000067801610000b843eb005c22d822d822d822d822d86000000a7dd46100071e3886610025e66700004a202f004090af003c2480256f003c0004223c0400000025410008222f003ce189e189e189e5892541000c41ea0010d0806700000a429853806600fffa23c880001c8070006000001c2f2f00402f2f00402f2f00402f2f00404eb9400040084fef00102f40002c4a84670003bc3886394500066100002043eb005c20d920d920d920d920d94a2b0050670000466100071e60000394223c000000a84c02180041f9800049d8d1c141e800404e75207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008eda271b028004e7561000bc6610005a20c80730000006300000c700042ab001842ab00142740000c73ec0002e089e48941fa326473f01a00e0804c0108002740001073ec0008203c000002044c001000e0892741001c73ec000ae089707fb280640000484a816602720124014c021000203c002ed1e04c4100000c80000fffff6f000008203c000fffff222b00080481001000006f00001ee089e8894c001000e08993ab00086000000c223c010000002741000873ec0004203c00000a444c001000e089242b0008e08ae88a4c021000e089e88992ab001471ab00516700002624010c80000000ff6600000e2200202b000c61000230200122024c40180153801740005174104c4218013741007e242f00346100000660000246222b001870484c02080041f980000810d1c0082b000100246600005c202b0058670000165380274000586600000c08eb00010024600000400c81000080006c00007471a8000c41fa328871f00a00206b000c2f09227c000080006100005e225fd2800c81000080006f00004a223c000080006000004071a8000e41fa335471f00a006600000e082b000200246700002671d020414c001000e089e0896600000472012008908122000c81000000406e0000047200274100184e754feffff448d7000e2408760de6aa0c820000008064000008243c000000800c8200001000640000520c820000080064000034263c000080004c4230030c830000002064000020d6830483000000100c8300000020630000047620243c000080004c43200226094c032000760fe6aa5282b4806400000420027420b0826500003e2400e48ad082e68ad082760fe7a926094c431001263c0000800096816a00000476004c013000720fe2ab4c003000e08bea8be488d0836600000470014cd7000e4fef000c4e752f0141f9400d2cb071b02800670000340c80000000806400002841f9460d171db03028006700001441f9400d2cb0538022302c080101670000087001600000047000221f4a804e7572484c02180041f98000081073b0180855810c81000000026300002061000280202b0028e080e88090866a00000444800c80000002806400000670004e7570014e750c81000000ff66000030223c100000004a80670000224c4010010c8100000010650000140c81000000fe6300000c223c000000fe4e7572104e754a2b002467000130242f0034263c000000a84c023800610021a26600002441f9800049d84a30380066000016426b00264a2b005067000100610006f4600000f87faa0003670000f043ea00104a2b00506700000a426b00266000135a2f0a20132c2b00042a2b000c246b0010286b0020222b001875eb00269282760fe7a9e8812c41e7aa2a4241fa33067618220c4c2b1800001cd2862401e6aad48279702800757028029484e08973c14c012800e082e082d8822844736b007ed2ab0014274100144c014800d8802404e6aad48273702800757028029481e08c79c44c042800e082e082d282dbce240d4842c3c2e589e281424122c122c1d085dc8a53876600ff8c268027460004274c0020220d760fe6a16a000004720037410026245f242f0034610009a66700000e7faa000343ea00106000127a202f002c4cd77cfc4fef00304e7541fa442423c8400d2cd852a8000c73f9800065b2e18977b9800065b68283b2a80010670000402141001052905283b6a800046f00000621430004e089b2a8001867000022214100182228000c260196a8001c2141001c0c83000000106d000006214300084e752006a3430c4040006f00000a048000003c0076002200ea8041f9400aa29441f00c00781be9a92818e289a104a8984901a801080079ac001b6600002a28050c447f006c000020a1c04844e58c6400000ae28c44846000000ae28c068480000000a8000900a1c0e6a0223c0184cbb7a0010800a1c0eb884e757dd40486000040002006e58edc8071ec0006048000004000223c00000ccd4c010800e080e080dc800686000040004e752f052f062a3c00007f007e000c8600007c006f00000e048600003c0052876000ffec0c86000004006c00000e068600003c0053876000ffec0c87000000066f00000870ff6000001a6100ff0e4a87670000106b000008efa8600000064487eea02c1f2a1f4e7571b9100b14ed4e75207946c8245671b9100b14cf263c000018b24c030800d1c02602eb8b96829682d1fc0008f07871b038054e756100ffca22002006e188e9884a816700007c2e006100ffbe260020074a836700006c767f96812e3c00000d804c0730002203484173c177c32e3c000001e04c073000484377c341f9400aa31426303c00e08be48b2e3c000017c74c0730007e189e81eeab222b002890816a0000124480e0884c030000e088928060000012e0884c030000e088d28060000004220027410028e081e8812c014e754feffff848d70c002a4b2b46004841f9400d2cb820302c00222d00342b400034c1ad00744ab9460d172a6600001241fa3fe02602eb8b42303801423038034680c280670000346100071e71ae00240c80000000016600001871ae00256700001053800101670000081d7c000200244dee0040bdca6600ffd4610006ec71ae00240c80000000016600001e71ae00250c80000000806500001061001f2a660000081d7c000200244dee0040bdca6600ffce7fad007c67000030202d0048b0ad00306600001671ad007d52801b40007d0c800000000865000010422d007c61000b7a2879800062a841f946104d0c41f0280008100004670000066100067c73ec0002e089e48941fa2aac73f01a002b41004073ec0008203c000002044c001000e0892b41001c73ec000ae089707fb2806400002c4a816602720120014c001000203c002ed1e04c4100000c80000fffff6f00000e203c000fffff6000000470002b40003873ec0004203c00000a444c001000e0892b41004470484c02080041f980000810d1c071a8000e43fa2c7c71f10a006600000e082d000200246700000471d12b40003c71a8000c43fa2b5c71f10a004c2d00000054e088ee886600000470013b400052610005a04a2e0024670001be264e2c2d00489cad002cdcae002c6100fd866100fce80c80730000006300000e422e002442ae0018600001922d40000ce0804c2d080000402d400010202d003867000022222e00080481001000006f00001ee089e8894c001000e08993ae00086000000c223c010000002d410008222d0044202e0008e088e8884c001000e089e88992ae001471ee003e670000262f010c80000000ff6600000e2200202e000c6100f9ce2001221f4c40180153803d40003e70104c4018013d410026202d001c2d40001c202e00346700001253802d400034660000081d7c00020024222e001871ae00240c80000000026700006e0c80000000036700003c202d0054b2806c00007c77ed00524feffff448d703012240206e000c20036100f80826004cd703014fef000cd283b2806f00008e220060000088202d0054e6884feffff848d70300226d0054206e000c6100f7d64cd703004fef000826006000001c202d003c26014c003000e08be08b6600000a4a8067000004760192836a0000047200b2ad00546f000014202d00542600e68b9283b2806c00000422000c81000000406e00001a720071ee003c0c80000000406e00000a422e0024610003242d4100184dee0040bdca6600fe34202d00482b4000304cd70c004fef00084e7541fa3a622002ed88d1c0421042280010422800204228003041fa324a2002e188d1c070044228002442a800184268003c41e8004053806600ffec4e7541fa3a262002ed88d1c0421042280010422800204228003041fa320e2002e188d1c070044a28002467000008117c0003002441e8004053806600ffea4e752f0341fa31e62002e188d1c02248263c7fffffff70044a2800246700001e22280018b283640000062601224841e8004053806600ffe260000004224820496100013e204b700922d853806600fffa43e9ffdc137c0003002442290025202b002823400028e080e88090ab0030d0ab002c2340002c42a9003441fa3b7052902350003041fa3d3e2028000c2340003871eb00260c8000007fff63000008203c00007fff3340003c202b00180c8000007fff63000008203c00007fff2340001842ab0018426b0026261f4e754fefffe848d7031b41fa31162002e188d1c0262b0054e68b0c830000080064000008263c0000080070044a2800246700005e2f0028082028000c222b000c6700000ab08163000004200120402244222900182003227c000080006100f5a820442800201f92846a00000472000c81000000406e000016720079e8003c0c84000000406e000006422800242141001841e8004053806600ff944cd7031b4fef00184e75200843fa307a9089e48843fa3872d3c04e752f096100ffea4211225f4e75117c0003002442a800344dfa3a54529621560030610001a44a2e00246600000c204e4268003c70014e754dee0040bdca6600ffe62f096100ffaa2368002c000423680028000870ff1280225f70004e75203c000005004c030800d0ad0048610008fa28006100015471ae0024578066000034202e0028e080e88090846a00000444800c80000002806400001a4a6e003c67000012204e2f096100ff484a11225f670000104dee0040bdca6600ffbc70004e7570014e752f096100ff2871910c80000000ff660000342028002c21690004002c2340000421690008002823680034000842a800341368002500014228002512a80024117c00030024225f4e754feffff048d70303204e6100fed871916700007e0c80000000ff670000721d4000241d69000100252d690004002c2d6900080034202d004890ad002cd0a90004e188e9882d400028429642ae000442ae002042ae0018426e003c42ae0014426e003e203c010000002d40000841fa3acc2028000c2d40003871ae00256700001061001898660000081d7c0002002442114cd703034fef00104e7541fa2ec62002e188d1c071a8002473a80064808173a800a4808173a800e480814e754dfa2ea42002e188ddc045ee01004e756100ffee222d003092ad002c41fa36882002ed88d1c04a2e002467000010d3ae002c4a1067000006d3a800044dee004041e80010bdca6600ffde222d00482b41002c41f9400d2cb07fb02800423028000c8700000080650000362f2d00482f2d0070610019186700001e9097223c000005004c010800d0af00042b400048610000826000ffde588f2b5f00484e752f07610000702e1f4a87670000662f2d0048202d0034222d00744681c0816700004e7e000f006600000852876000fff6220704810000000c7139400d2cdc760c4c030800d28006810000004092ad0070203c000005004c010800d097610006a42b40004852876100000c6000ffa62b5f00484e754a876700001a0c87000000806400001020075380222d007401c12b41007470484c02080041f98000081071b0080be488660000b86100feb841fa39322628000c28280008e28c4a876700005271ae00240c80000000016600003671ae0025b087670002d44a8066000026202d0048b0ae002c6600001a200390ae0038b0846400000e1d47002542ae0034600002aa4dee0040bdca6600ffb66000005041f9400d2cb84ab02c006600000a610016926700003a71ae00240c8000000001660000224a2e00256700001a202d0048b0ae002c6600000e200390ae0038b084650002584dee0040bdca6600ffca4a87660000246100fdfc71ae00240c8000000001660000081d7c000200244dee0040bdca6600ffe4700073f9400d2ca80c8100004ef9660000084eb9400d2ca82b40004c70484c02080041f9800008107db0080853860c8600000003630000047c00528671b0080b2600e48b6700001c22000281000000032003260643fa373061000c4876016000000643fa220441fa230020306c002b4000544a836700000a6100f9e06000004e610001a46600019e760196866100fd4e204e70044a28002467000004528341e8004053806600ffee4a836f0000206100056c6100fb62117c000300244dfa35c452962156003053836000ffde77190c83ffffff806700014e6100fb986600006a6100054a6100fb304a68003c67000036203c000005004c030800d0ad00486100047c22280028e081e88192806a00003a44810c81000002806500002e6100fb0467000036429042a8000442a8002042a8001842a800144268003e203c010000002140000860000012317c00ff003e203c0100000021400008203c000005004c030800d0ad0048610004142140002ce188e988214000281147002542a800344dfa36da202e000c214000384a876700000c0c87000000806500003872484c02180049f98000081073b4180d0c810000007f6700001e49fa23c673f41a0049fa369c282c00084c041000ee895281214100342879800062a870014a87670000300c8700000080650000162f0020076100144c201f6600001870026000001222075381282d00340304660000047002114000244dfa34705296215600306100fabc53866600feaa4e7570004a876700008441fa362a2228000c200190ad00782b4100780c800000019d620000660c87000000806400001620075380222d003401814a816700004c6000001241fa35202002e7884a300801670000386100fb62720071ae0024670000160c8000000001660000204a2e00256700001852814dee0040bdca6600ffdcb2866500000670014e7570004e752f082f0141fa25ca2202ef89d1c11140007c4228007d221f205f4e750c87000000806400001020075380222d007401c12b4100746100faf4222d003092ad002c4a2e002467000006d3ae002c4dee0040bdca6600ffec222d00482b41002c70484c02080041f98000081077b0080b7db0080853860c8600000003630000047c0052862003e4886700012c2203028100000003260643fa343e610009564fefffec42af00106100fa8471ae00240c80000000016600002e222f00104a816700001c20771cfc2028002cb0ae002c6f00000c2f881c0053816000ffe22f8e1c0052af00104dee0040bdca6600ffbe760043fa33e4b6866400009e7119223c000005004c010800d0ad0048610001c0282f0010b6846400001620773c002140002c1147002542a800346000005c2f006100024c6100f832201f42a8000442a8002042a8001842a800144268003e223c01000000214100082140002ce188e988214000281147002542a800344dfa343e202e000c21400038117c000100244dfa325452962156003052830c83000000046500ff646000001c282f0010b6846400001220773c00117c0002002452836000ffe84fef00144e756100f97493c9780071ae00240c800000000166000010202e0030b084630000062800224e4dee0040bdca6600ffdc20096700fa56202d0048610000dc2340002c1347002542a900344e7561000f46670000a84feffff048d7011a207946c8245671b9100b14cf223c000018b24c010800d1c02202e7892001d281d280d1fc0008ee9a77b0180279b0180591fc0008ee9a2202eb8992829282d1fc0008f07871b0180555830c83000000026300000e4a806700003a7001600000340c800000000264000008700260000024e48c6600001c41fa234a2202ef89203a332690b018780c800000019d6300ffdc70034cd7011a4fef00104a804e754feffff848d701086100f0484cd701084fef00084a806700000470014e75282d004c670000984feffff82e822f4600042c00048600004000068600025a80243c000005004c4260060686000004382206740c4c4210012401d482d481e58a9c82240672002c029c816a00000806860000000c0d046600002e2c02dc810c860000000c6d00000804860000000c0d046600001652810c81000000066f00ffc872006000001044812c3c000005004c016800d08624172c2f00044fef00084e754feffff048d7064843fa00746000000e4feffff048d7064843fa00604dfa26482002e188ddc045fa2e3ee488d5c0204e78ff7c0471ae002471b108004a12670000047003e188e188e188e988262e003002830fffffff8680b684640000062803204e4dee004045ea001053866600ffc64cd706484fef00104e7500020100030201037801600000047800242f00344fefffe82e892f4700042f4b000c204920074a84660000104290508853806600fff8600000102210e2812081508853806600fff46100f6fc2f4a00084a2e0024670000cc264e22572e2f000420132c2b00042a2b000c246b0010286b0020736b00262f410014426b0026222b001875eb003c3741003c9282484142414c4718012f410010484242422a4241fa1f487618220c4c2b1800001cd2862401e6aad48279702800757028029484e08973c14c012800e082e082d8822844222b0014d2af0014274100144c014800d8802404e6aad48273702800757028029481e08c79c44c042800e082e082d282dbef0010240d4842c3c2e281d3915089d085dc8a53876600ff8e268027460004274c00204dee0040bdef00086600ff2422572e2f00042211d28169000016424122c122c153876600ffee4fef00186000ec426b00000c223c800000006000ffe0223c7fff00006000ffd6610000304ef940003cac220c048180004858660000140c82000000026600000a2f086100ec12205f610000084ef94000d04671722a1243f04a000c82000000026600001c220c048180004858e689ef8941fa20324a3018246700000470004e75203c400d37f675850c820000000166000112243c000018b24c012800d4892f002042200361000bd44cd700014fef0004660000722803e58cd883d4842042d1fc0008f04b75900c820000007f620000d4283c000004484c0248000684100b14f020442248283c000000ff7b98670000140c850000002f66000004224853846600ffea3a3c464dba5166000004548941fa190478057b987599ba826600008653846600fff243fa308a4a3a30826600007241f9400d37f6283c0000019212d853846600fffa43fa306a41fa18cf235800343350003841fa01aa234800de41fa18c1235800223350002641fa016c234800d2780123440072134400607804234400a242a901322829018e0284ff0ff0ff0084005005002344018e41fa301210bc000120094ef940031ed641f9400d5f3824700c00b5fc400d31ae6600002042a72f014eb940031da4508f0c80400d2e8a6700000a4a806700000424404ef94003c05845f9400d37f670012f002f014eb940031da4508f41fa2fbab1c06600000424484ef94003c0584feffff848d7005024030c820000001d63000004741d41f9400a72a86100006e2e002f0542a74eb94003249c508f260061000074280072014a836a00000672ff44834a836700002a2407d4816b000022701db4806e00001a41f9400a72806100002c01046600ffe42e0253836000ffd4240741f9400a72806100001226004cd700504fef00084ef94003932c20027c064c4608002c300c009c802006d080d086d080d0824e7570012f0071b9100b14cc2f004eb940031da4508f41fa2eeeb1c06600000a203c000009004e7570004e75202f000853800c800000000363000004700052802f00487a17582f2f000c4eb940013a084fef000c4e75207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a71b9100b14cce7882200d081d08171b0080255800c80000000026300000a700072006000001c202f00082200028100000003e48802800000001f660000047200e58841fa17a841f00800226f000470044a106700000a12d853806600fff44a816700000a06810000003012c142114e754feffff848d70c0045fa2fac4a3a2fa66600004e41f9400d31ae264a223c0000019216d853816600fffa71596d00001673d9205947f2080016d853816600fffa6000ffe841fa0026220847ea012a700626c153806600fffa41fa2f5a10bc0001200a4cd70c004fef00084e754feffff448d7001c242f0010222f0014202f00184ab946c7dd26670000284ab9800000a06600001e0c82000000016700003e41fa009077b028004c031800d0816000007826020683000000382043d683d688e78b41f946c7d8ee4ab03800670000082601e7899283d0816000004ee48802800000001f76014a816700003c6e000006448344812400d4830c820000001f620000260c82000000036d00001441fa15b079f02a000284000000ff6600ffda200253816600ffd0e5884cd7001c4fef000c4e750c00100a10104fefffd048d70cfc45ef002041fa15b841f00c00740018302800158428001582280452820c82000000046600ffea6100015674017a007d322800610001a82e067d3258006100019ebe866600001241f228041c1008c6000710866000000a5285ba826600ffd252820c82000000046600ffc4610001502a01ba84650000089a846000fff67db258087f3268007400b485640000207db2280841f268007d1006860000000cbc876f00fff6108652826000ffde610000d26100010c7daa00087f3268007a0074004a3228046a0000282c05700cbc846500000e9c8406800000000c6000fff07db268087d326800dc8015862800528552820c82000000046600ffc870007a0074007db22804028600000003bc806700001252820c82000000046600ffe66000000e7d3228009c871586580c52855280ba836500ffcc220353816f00002874007d32280c7132280db0866c00000a1580280c1586280d5282b4816600ffe453816600ffdc74001c32280c138628005282b4836600fff220044cd70cfc4fef00304e7578037a007d3258007f325801be866c00001a15875800158658011c3258041e325805158758041586580552850c85000000036600ffd053846600ffc84e75780074004a3228046b00000815824808528452820c82000000046600ffe84e750c860000000c6500000c04860000000c6000ffee4e754fefffc848d71cfc45ef002477ab000141eb000c740010302800158028005282b4836600fff2220353816f00002874007db2280071b22801b0866400000a15802800158628015282b4816600ffe453816600ffdc7d927400780071b228009086158028006100015a01c45282b4836600ffea7bb228ff700928407e007c01610000766100005852860c86000000206600ffeeb9fc00000009660000307c017e01610000546100003652870c87000000046600ffee52860c86000000206600ffe0b9fc0000000967000060202f002c22000281000000ffe088600000dc4a8067000014b08c6400000e28402006e18880872f4000304e752006220743ea00046100fd06be806400001e2200740010322800b03228046600000e5282b4836600ffee20014e7570004e7542af002c203c7fffffff2f4000307c017e002006220743ea00046100fcc2be806400005228407400720071b228046100006801c15282b4836600fff0b2846600002871b2380390856a0000044480e788d08cb0af0030640000102f4000302006e18880872f40002c52870c87000000046600ffa052860c86000000206600ff92202f002c22000281000000ffe0884cd71cfc4fef00384e750c800000000c6500000c04800000000c6000ffee4e754fefffd448d77cfc2a002c0a2803207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2002e7882200d081d08173b0080255810c8100000002620000fc548171b00805e488660000f047fa25c02002eb88d7c01741001d49fa27862e2c000c610000e24a856a0000b471ab0001670000c8220792ab00040c810000019d620000b841eb000822007198610005346600000c53816600fff26000009e71ab00012f0041eb000843eb00084beb000c4deb000c72007198b08467000010610005026700000812c01cd55281528d53976600ffe4588f422b00030c81000000046400002412841c8652811744000327470010203946c7e956274000142014274000181746001c1741000127470004610000544cd77cfc4fef002c70014e75168527470004174400081746000c177c00010001422b0002422b00034cd77cfc4fef002c70004e754a2b000367000016200790ab00100c800000008a65000006422b00034e7577ab000141eb000c787f20037398b28464000004280153806600fff220047200610001d00c8300000002650000306100fc904a8067000026e5888081720b610001b271ab001db0836300000420037208610001a0177c000100024e754a2b00026700001e203c000000ff720b61000184203c000000ff720861000178422b00024e754fefffcc48d71fff28000c840000008064000004528447fa24102002eb88d7c07bab0003670000dc49fa25d22e2c000c6100ff2e4a2b0003670000c8ba84670000c271ab00015380670000b841eb0008b8186700000c53806600fff6600000a471ab0001538017400001422b00036100ff0e4feffff82e82202b00142f4000044eb940042d1c4fef00084a806b0000742c0061000078168617450008102b001c1740000c177c00010001422b0002202b00102740000471ab001c7200610000b20c85000000806400003a206f002020086700003072034a2800026700000e508853816a00fff2206f002020055380108011460001117c00010002202b0018214000044cd71fff4fef00344e752002223c0000048d4c01080073b980000004263c0000476c4c031800d081d08606800000044d2600d68341f91001614ed1c332100281ffffe07f308173b980000002263c0004d9a04c031800d081d08041f9400e21e0d1c032100281ffffe07f30814e754fefffec2e822f4100042f40000871932f40000c203946c7e9562f4000104eb9400421584fef00144e75610000b0660000584feffff048d7030a207946c8245671b9100b14cf223c000018b24c010800d1c02248d3fc0008eda271b1280053806600001a2002e588d082d1c0d1fc0008f04b71906100011a6000000470004cd7030a4fef00104a804e754feffff848d702022248d3fc0008eda273b1080053816600002cc0fc001e2248d3c0d3fc0008edbc73d10c810000464d6600001273a9000253816600000870016000000470004cd702024fef00084a804e754feffff848d70102207946c8245671b9100b14cf223c000018b24c010800d1c020026100ff8a4cd701024fef00084a804e75205c4feffff848d7000524036100ffc06700000641fad7744cd700054fef000826c8487800104ef94000d51c4feffff848d70104242f000c6100ff9467000036203c000000a84c02080041f9800049d8d1c0203c000001004a1067000004700010bc00ff117c000100144cd701044fef00084e754cd701044fef00084ef94000f4504feffff048d7030a0c800000007f6200005c223c000004484c0100000680100b14f020402248263c000000ff7198670000140c800000002f66000004224853836600ffea303c464db05166000004548941fa0c42760571987399b0816600000e53836600fff270016000000470004cd7030a4fef00104a804e756100fe1e6700004c2f012f08207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2202e7892001d281d28071b0180255800c8000000002630000087000600000047001205f221f4a804e7543fa215c2002e788d3c04a114e754feffff448d702030c800000008064000014538043f9400d2cb822312c0001016000001e6100ffcc20177208b0196700000e53816600fff672006000000472014cd702034fef000c4e754feffff048d703096100ff9e204976087190b081670000204a8067000018528853836600ffec2049760710e8000153836600fff810814cd703094fef00104e754feffff448d703086100ff5e204976087190b081670000164a806700000a528853836600ffec70ff6000002653836700000c10e8000153836600fff84210700076084a196700000a528053836600fff44cd703084fef000c4e754feffff048d7030941fa212a77b028002003528002800000000743fa2110b03128006700001c43fa2084d683d3c32602e98bd3c31281261713430001118028004cd703094fef00104e7541fa20e073b0280043fa20e07e00b23128006700002443fa204a2002e988d3c0d3c1d3c17f9171a900015281028100000007118128004a874e7543fa20ae71b1280041fa209e118028004e756100fc426700009871930480000000146a0000047000224412802040739300810000008043fa10a82002ef88d3c0422900256100fe44670000466100ecc0080000006700003a6100fe880c80000000036600000820016100eaa643fa10722002ef88d3c0137c0001002520352c000080000001192b802c004ef94000e7546100fe5020086100fee443f9400d2cb013812800721d2b812c004ef94000e754721d2b812c004ef94000e74c6100fb986700003e73930081000000806100fe546b00002e2f002f08200191c86100f9de205f201f22006100fd4e670000144a81660000084ef94000dfdc4ef94000dff873107113b0816600fff24ef94000dfdc266f00722031bc00080000026600001c240bef8a41fa0fbad1c20828000000246700000808e8000100244ef94000b522226f00724fefffe848d70c1e28090c84000000086400008a73b0980045fa1f7a52922404ef8a47fa0f78d7c226012401028200000030670000184a2b00246700003272301181980052aa000460000024240102820000000f670000464a2b00246700003e0281000000f01181980052aa0004242a000852aa000802820000001fe78a45f22810243a1efc248275ab0024e18a8481e18a8483e18a8484254200044cd70c1e4fef0018103098004ef94000c63c4200138088004feffff848d7020424012001ef8841fa0ed8d1c04228002442a800184268002642a800586100e05e41fa12be2002e188d1c070044228002542a8003441e8004053806600fff04cd702044fef00084ef94000686271930c80000000486500000c0c80000000606300004c080300086700001675b9800000006100f9e4670000364ef94000e6e22f017200740705036700000c6100f9ca6700000405c153826a00ffec4a816700000c2601221f4ef94000e6e2221f200655807405b4804ef94000e45a0c82000000486500000c0c8200000060630000664feffff448d70007080400086700001e75b9800000006100f970670000404cd700074fef000c4ef94000df9e7200740705046700000c6100f9506700000405c153826a00ffec4a816700001228014cd700074fef000c4ef94000df9e4cd700074fef000c55807605b6804ef94000de16202a000423c046c7e9564fefffe848d71c1c24016100f902670000b277aa000228030484000000146a0000047800008300000080244447fa0d6a2002ef88d7c04a2b00256600002870ff6100f5464a806600006c4fefffec2e82203946c7e9562f4000044eb940042d1c6000001c422b00254fefffec2e82203946c7e9562f4000044eb94004271c4a806b00002e28002f40000c2f4a000842af0004203946c7e9562f4000104eb9400421584fef001420046100f4de600000064fef00144cd71c1c4fef00184ef940062d1c4cd71c1c4fef0018202a00044ef9400625ea752a0002204241f02c002f0224016100f82a241f4a806700001820020480000000146a000004700006800000016420404ef9400626344ab9400bebae6600014c4ab9460d17366700013c4a79460d174a670001324fefffd048d73cff75b9100b14cc6100f7d64a806700011220175380660000fc760c0c81000000336700000444837ff9460d174a7c000d07670000862006d0b9460d174ceb882202283c0000091a4c041800d08173b9100b14d0283c00008ed84c041800d081247946c82456d5c045ea005947f9100161a7d7c073920c81000000ff66000006610000b4d2836a00000472000c810000007f6f000004727f14811681207946c82456203c0009b33272012181080023c1100f85984eb940027e0052860c86000000106d00ff6c2f024eb94009da20588f700123c0460d173a41f9460d1a9e4250426800024268000442680006426800084279460d10dc23c0460d17504eb9400418e04878ffff4eb94004d948588f4eb940027de44cd73cff4fef00304ef9400520064cd73cff4fef00304ef940051ff64ef940051fd6207946c8245671b9100b14cf223c000018b24c010800d1c02208d28206810008eda222417191d0802200d080d280d1c12202eb8992829282d1c1d1fc0008edaa73904e7570000c82000000076600000c4a3980000034660000086100f64e54804a804e752f0275b9800000006100ffd6241f4a80670000702f0043fa1eec4a3a1ee66600005841f9400d3988223c0000019212d853816600fffa43fa1ecc41fa0415235800583350005c41fa0066234800f6223c40046d9c23410126422900692229018a0281ffff0fff0081000010002341018a41fa1e9010bc0001201f234000c620094e75203c400d39884e754feffff048d7030675b9800000006100ff4653806a00000470004cd703064fef00104e756100ffda2200202f0008b081630000042001e78841fa0393d1c0226f000412d86600fffc4e756100ffb42200202f000cd0af00086a0000047000b0816f00000420014e756100ff0c2f004ef940059d5c6100ff00204049e8003a47e800e24ef9400368666100feec20402f28018e2f28018a4ef9400368b86100fed820402f28018e2f28018a4ef9400369520680400d398822402069012a0c820000000b660000162f0224046100fe8a241f4a806700000641faff724ef94003ae4c41f98000083cb5fc00000005660000242f022f002404e68a6100f4c267000010201f241f423008004ef94003af14201f241f118208004ef94003af14d1fc0008f04a719010814feffff048d7001e26002801202f0018242f0014220b4eb9000000004cd7001e4fef00104ef9400795c2d1fc0008eda277104fefffe848d7031720032401220476ff6100003e4cd703174fef001877104ef94007981ed1fc0008eda279104fefffe848d7030f20042239460d5c3076ff610000104cd7030f4fef00184ef94005a8504fefffd848d703ff53816600002c4a836a0000066100002c2e0020046100f55c6700001653876600000c20036100f54c66000006610000384cd703ff4fef00284e75207946c8245677b9100b14cf283c000018b24c043800d1c32602e58bd682d1c3d1fc0008f04b779028034e754feffff048d70303207946c8245671b9100b14cf223c000018b24c0108002202eb8992829282d28006810008edb370401180180043f91001614e1380180072484c02180041f980000810118018032202ed8941f980000a50d1c1317c400000062202eb8941f980000db442b018004cd703034fef00104e754feffff048d700e47fba1be141fa1bdd42102f2f001c2f2f001c2f2f001c4eb940013a084fef000c7001b0846600008820036100f460b0876700007c2a007e002c037400207946c8245671b9100b14cf223c000018b24c010800d1c0200206800008eda271b008005380660000362002e588d08206800008f04b71b00800b086660000207001223c000000ff4eb9000000006600000e200572014eb9000000007e0152827008b0826600ff9a4a8767000006610000304cd700e44fef00104e75202f000453806700000870006000000a202f00086100f3be41fa1b1110804ef94002528873b9100b14cf700003c0207946c82456d1fc0009504873908280108143f9100b145e739182801281207946c82456d1fc0009b3327001208023c0100f85984ef940027e0053594e5448434852440000564f494300004c45470000004f464600000000004d4f4e4f00000000504f4c5900000000257300256400000040008000c0010001030140016a018001c002000203028003000380040004030480050005800600068007000780080009000a000b000c000d000e000f001000008080800003070c0004070c0002070c0005070c000a0307000a0407000b040700050a07000603090002040700020307000e04070009030700090407000a06030006030c0008040c000a0e03000a0e04000b0e0400090e0400050a0f00050c1100070c13000c182400030c0f00040c10000a0c16000b0c170007100c00070f0c2d2d2d2d4d494e004d414a0053553200535534004d493700444f37004d4137003753340044493700414432004d443200414439004d4936004d4136004d37350044494d00415547004d493900444f39004d4139004d3639005155410034544800355448004f435400334d4900334d4100374d4900374d41004d4153004d495300000080000000800000005a82000049e700004000080008000800080008000800076806d4064c05ce055a04ef048d043203de03910349030802cb029302600230020501dc01b7019501750158013d0125010e00f900e500d300c300b400a60099008d00820078006e0066005e00560050004a0044003e003a00350031002d002a002600230021001e001c001a001800160014001200110010000e000d000c000b000a000a000900080008000700060006000500050005000400040004000300030003000300020002000200020002000200010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100014de54de54de54de54de54de54de54de54de54de54de54de54de54de54de54de548e5440b3f753b21370d33382f9e2c3f29162622235f20cc1e651c291a141824165714ac131e11ae10580f1c0df60ce60beb0b020a2a096308ab0800076306d1064b05ce055c04f20490043503e20395034e030c02d0029802640235020901e001bb01980179015b01400127011000fb00e800d600c500b600a7009a008e0083007900700067005f00570051004a0045003f003a00360032002e002a002700240021001e001c001a001800160014001300110010000f000d000c000b000b000a00090008000800070006000600060005000500040004000400030003000300000001000100020003000400060008000b001000170020002d0040005b008000880090009800a000a800b000b800c000c800d000d800e000e800f000f8010001100120013001400150016001700180019001a001b001c001d001e001f00200022002400260028002a002c002e00300032003400360038003a003c003e004000440048004c005000540058005c006000640068006c007000740078007c008000880090009800a000a800b000b800c000c800d000d800e000e800f000f8010001100120013001400150016001700180019001a001b001c001d001e001f00200022002400260028002a002c002e00300032003400360038003a003c003e004000ffff000000000192032404b5064607d609640af10c7c0e060f8d11121294141315901709187e19ef1b5d1cc61e2b1f8c20e7223d238e24da26202760289a29ce2afb2c212d412e5a2f6c307631793274336834533537361236e537b03871392b39db3a823b213bb63c423cc53d3f3daf3e153e723ec53f0f3f4f3f853fb13fd43fec3ffb40003ffb3fec3fd43fb13f853f4f3f0f3ec53e723e153daf3d3f3cc53c423bb63b213a8239db392b387137b036e536123537345333683274317930762f6c2e5a2d412c212afb29ce289a2760262024da238e223d20e71f8c1e2b1cc61b5d19ef187e170915901413129411120f8d0e060c7c0af1096407d6064604b5032401920000fe6efcdcfb4bf9baf82af69cf50ff384f1faf073eeeeed6cebedea70e8f7e782e611e4a3e33ae1d5e074df19ddc3dc72db26d9e0d8a0d766d632d505d3dfd2bfd1a6d094cf8ace87cd8ccc98cbadcac9c9eec91bc850c78fc6d5c625c57ec4dfc44ac3bec33bc2c1c251c1ebc18ec13bc0f1c0b1c07bc04fc02cc014c005c000c005c014c02cc04fc07bc0b1c0f1c13bc18ec1ebc251c2c1c33bc3bec44ac4dfc57ec625c6d5c78fc850c91bc9eecac9cbadcc98cd8cce87cf8ad094d1a6d2bfd3dfd505d632d766d8a0d9e0db26dc72ddc3df19e074e1d5e33ae4a3e611e782e8f7ea70ebeded6ceeeef073f1faf384f50ff69cf82af9bafb4bfcdcfe6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015900000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000100000000000000000000000003000003000000000000000000000000030000040000000100000000000000000000fff10000000d46104d0c000000000000fff100000014400aa294000000000000fff10000001e80000810000000000000fff10000002600000048000000000000fff10000003080000a50000000000000fff10000003980000db4000000000000fff10000004146c82456000000000000fff10000004a100b14cf000000000000fff1000000531001614e000000000000fff10000005f0008eda2000000000000fff1000000680008edbc000000000000fff100000070400d31ae000000000000fff10000007700000192000000000000fff100000085800000e0000000000000fff1000002e140013a08000000000000fff10000008d46c7dd26000000000000fff100000097800000a0000000000000fff1000000a446c7d8ee000000000000fff1000000ad00000038000000000000fff1000000b70000012a000000000000fff1000000c0400d24d0000000000000fff1000000c80184cbb7000000000000fff1000000cf01000000000000000000fff1000000d773000000000000000000fff1000000df00000000000000000000fff1000000e800000004000000000000fff1000000ef00000008000000000000fff1000000f60000000c000000000000fff10000010000000080000000000000fff10000010a00000018000000000000fff10000011100000024000000000000fff10000011600000026000000000000fff10000002700000040000000000000fff10000011e00000018000000000000fff1000001250000003c000000000000fff10000012d800049d8000000000000fff100000138000000a8000000000000fff10000014580001c80000000000000fff10000014c40004008000000000000fff1000001594000d51c000000000000fff1000001634000f450000000000000fff10000016e800062a8000000000000fff100000175800062a4000000000000fff10000017c00000001000000000000fff100000185100b14f0000000000000fff10000019300024640000000000000fff1000001a100000448000000000000fff1000001b100100000000000000000fff1000001bb002ed1e0000000000000fff1000001c1000fffff000000000000fff1000001c700000a44000000000000fff1000001d300000204000000000000fff1000001dc100b14ed000000000000fff1000001e5460d171d000000000000fff1000001ed400d2cb0000000000000fff1000001f5400d2cd8000000000000fff1000001fe800065b2000000000000fff100000207800065b6000000000000fff1000002100000000d000000000000fff10000021800000010000000000000fff10000022000000014000000000000fff10000022800000018000000000000fff1000002300000001c000000000000fff10000023900000159000000000000fff100000245400d2ca8000000000000fff10000024e0000000b000000000000fff10000025600000008000000000000fff10000025e0000000e000000000000fff10000026580004858000000000000fff10000026f400d37f6000000000000fff10000027500000192000000000000fff10000028240031ed6000000000000fff10000028f0008f04b000000000000fff1000002980008f04a000000000000fff1000002a100000003000000000000fff1000002a9460d5c30000000000000fff1000002b6400795c2000000000000fff1000002c14007981e000000000000fff1000002cd4005a850000000000000fff1000002d940013a08000000000000fff1000002e900000500000000000000fff1000002eeffffff80000000000000fff1000002f880000110000000000000fff1000003000000000c000000000000fff10000030700000003000000000000fff10000031146c80350000000000000fff10000031d40006862000000000000fff10000032600000000000000000000fff10000032c00000004000000000000fff10000033200000008000000000000fff1000003380000000c000000000000fff10000033e00000010000000000000fff10000034500000014000000000000fff10000034c0000001c000000000000fff10000035100000020000000000000fff10000035900000028000000000000fff10000035f0000002c000000000000fff10000036500000030000000000000fff10000036c00000034000000000000fff10000037300000038000000000000fff1000003780000003c000000000000fff10000037d00000040000000000000fff10000038500000044000000000000fff10000038900000048000000000000fff10000038d0000004c000000000000fff10000039500000050000000000000fff10000039c00000051000000000000fff1000003a400000000000000000000fff1000003ad00000004000000000000fff1000003b600000008000000000000fff1000003be00000010000000000000fff1000003c600000110000000000000fff1000003ce00000052000000000000fff1000003d300000054000000000000fff1000003da00000058000000000000fff1000003e100000070000000000000fff1000003e800000074000000000000fff1000003f000000078000000000000fff1000003f80000005c000000000000fff1000003fe00000040000000000000fff100000404100b14cc000000000000fff10000040d0008ee9a000000000000fff10000041a00000000000000000000fff10000042000000004000000000000fff10000042600000008000000000000fff10000042c0000000c000000000000fff10000043200000010000000000000fff10000043900000014000000000000fff1000004400000001c000000000000fff10000044500000020000000000000fff10000044d00000024000000000000fff10000045500000025000000000000fff10000045b00000026000000000000fff10000046300000028000000000000fff1000004690000002c000000000000fff10000047000000030000000000000fff10000021100000034000000000000fff10000047600000038000000000000fff10000047e0000003e000000000000fff10000048600000020000000000000fff10000049000000000000000000000fff10000049800000001000000000000fff10000049d00000002000000000000fff1000004a600000003000000000000fff1000004ae00000004000000000000fff1000004b700000008000000000000fff1000004bf0000000c000000000000fff1000004c700000010000000000000fff1000004d100000014000000000000fff1000004d900000018000000000000fff1000004e30000001c000000000000fff1000004eb0000001d000000000000fff1000004f30000019d000000000000fff1000004fa0000008a000000000000fff100000502460d172a000000000000fff10000050d400d2cdc000000000000fff10000051740042d1c000000000000fff100000520400e21e0000000000000fff1000005281001614e000000000000fff1000005300000044d000000000000fff10000053a80000004000000000000fff10000054280000002000000000000fff10000054b40042158000000000000fff10000055646c7e956000000000000fff10000055e00000000000000000000fff100000566400d3988000000000000fff10000056c00000192000000000000fff1000005790008f078000000000000fff10000058400000005000000000000fff10000058e40046d9c000000000000fff10000059980000034000000000000fff1000005a34004271c000000000000fff1000005b00000007c000000000000fff1000005b90000007d000000000000fff1000005c00000007e000000000000fff1000005c800000025000000000000fff1000005cf00000001000000000000fff1000005d80000001f000000000000fff1000005e300000003000000000000fff1000005ef00003c3c0000000000000001000005f80000027c000000000000000100000600000001f200000000000000010000060800000330000000000000000100000613000001d000000000000000010000061900002864000000000000000100000623000000de00000000000000010000062b000000a4000000000000000100000633000000bc00000000000000010000063e000000b6000000000000000100000647000000c800000000000000010000064d000035ac000000000000000100000655000000ce00000000000000010000065c0000011c00000000000000010000066b000005e600000000000000010000067600004c1400000000000000010000067f0000011e00000000000000010000068c000001360000000000000001000006950000062e00000000000000010000069c0000015e0000000000000001000006a800000e520000000000000001000006b10000016e0000000000000001000006b9000001b00000000000000001000006c300000e140000000000000001000006cb00002b5c0000000000000001000006da000001bc0000000000000001000006e3000008cc0000000000000001000006eb000001ee0000000000000001000006f200000dd80000000000000001000006fa000002740000000000000001000007040000025200000000000000010000070f0000099600000000000000010000071700000318000000000000000100000720000002cc000000000000000100000729000002c0000000000000000100000734000002b800000000000000010000073f000002e6000000000000000100000749000006aa0000000000000001000007520000035400000000000000010000076000000a3000000000000000010000076900000f1c000000000000000100000777000008fc00000000000000010000078400000370000000000000000100000790000035e2000000000000000100000799000003a20000000000000001000007a2000003f60000000000000001000007ad000003b60000000000000001000007b5000003d60000000000000001000007bd000004000000000000000001000007c8000004500000000000000001000007d3000004440000000000000001000007de000006700000000000000001000007e7000004660000000000000001000007f3000004dc0000000000000001000007fd0000049e0000000000000001000008070000051a0000000000000001000008130000373600000000000000010000081a000005200000000000000001000008220000383600000000000000010000082a000004f8000000000000000100000835000005080000000000000001000008400000053e00000000000000010000084800000598000000000000000100000850000005840000000000000001000008590000057a000000000000000100000861000005dc00000000000000010000086b000005c20000000000000001000008730000062800000000000000010000087d0000062600000000000000010000088600000620000000000000000100000890000006680000000000000001000008990000065e0000000000000001000008a30000066c0000000000000001000008ad000006a80000000000000001000008b8000006a60000000000000001000008c2000007e00000000000000001000008ca000006ea0000000000000001000008d3000007060000000000000001000008db00001a5e0000000000000001000008e300003a380000000000000001000008ea000007360000000000000001000008f2000007c20000000000000001000008fa000007c80000000000000001000009080000117400000000000000010000090f00001a5800000000000000010000091b000007ee0000000000000001000009230000085200000000000000010000092e00000828000000000000000100000939000008540000000000000001000009410000086800000000000000010000094a000008b8000000000000000100000953000008ac00000000000000010000095c000008b4000000000000000100000965000009080000000000000001000009710000091e00000000000000010000097d00000934000000000000000100000989000009440000000000000001000009940000095c00000000000000010000099e000009580000000000000001000009a9000009620000000000000001000009b20000096a0000000000000001000009bd00000a220000000000000001000009c800000a140000000000000001000009d100000a240000000000000001000009dd00000a6e0000000000000001000009e700004a400000000000000001000009f000000aa80000000000000001000009fc00001196000000000000000100000a0900000a7a000000000000000100000a1400000a9e000000000000000100000a2000000aac000000000000000100000a2c00000ad6000000000000000100000a39000029f4000000000000000100000a4100000b16000000000000000100000a4c00000b08000000000000000100000a5500001688000000000000000100000a6100000b2c000000000000000100000a6e000011a6000000000000000100000a7700000b8c000000000000000100000a8200000b68000000000000000100000a8a00000b8e000000000000000100000a9300000bd0000000000000000100000a9c00000bf0000000000000000100000aa500000bf8000000000000000100000aad00000dbc000000000000000100000ab800000c2c000000000000000100000ac300000c3c000000000000000100000ace00000c64000000000000000100000adc00000c6e000000000000000100000ae600000cb2000000000000000100000af400000ca6000000000000000100000b0200000cdc000000000000000100000b0c00000d5a000000000000000100000b1600000d32000000000000000100000b2000000d7c000000000000000100000b2a00000db8000000000000000100000b3500000d74000000000000000100000b4000000d7c000000000000000100000b4c00000d96000000000000000100000b57000010da000000000000000100000b610000483c000000000000000100000b690000403c000000000000000100000b7300000dfc000000000000000100000b7c00000e38000000000000000100000b8500000e46000000000000000100000b8e00000e68000000000000000100000b9900000e8c000000000000000100000ba400000e7e000000000000000100000baf00000e8e000000000000000100000bba00000fd0000000000000000100000bc400000e98000000000000000100000bcf00004a3c000000000000000100000bd600000ef4000000000000000100000bdf00000f0c000000000000000100000be700000f44000000000000000100000bf200000f46000000000000000100000bfd00000faa000000000000000100000c0800000f66000000000000000100000c1000000f88000000000000000100000c1800000fa6000000000000000100000c2400000fbe000000000000000100000c2e00000fdc000000000000000100000c3700000ff4000000000000000100000c4200001006000000000000000100000c4d0000102c000000000000000100000c5700001936000000000000000100000c5f00001044000000000000000100000c6a00001080000000000000000100000c750000105e000000000000000100000c7d0000108e000000000000000100000c8700001092000000000000000100000c8f000010d6000000000000000100000c990000116a000000000000000100000ca300001168000000000000000100000cae000011bc000000000000000100000cb9000011d2000000000000000100000cc500001234000000000000000100000cd100001208000000000000000100000cda00002b22000000000000000100000ce70000122c000000000000000100000cf3000012a8000000000000000100000cfd000012a6000000000000000100000d0700001246000000000000000100000d11000012a2000000000000000100000d1e00001258000000000000000100000d2800001264000000000000000100000d32000012c6000000000000000100000d3b00001392000000000000000100000d4400001344000000000000000100000d4f000012f4000000000000000100000d5a00001336000000000000000100000d64000015de000000000000000100000d6e0000135a000000000000000100000d78000029e6000000000000000100000d8400001388000000000000000100000d8e000013ba000000000000000100000d9a0000139c000000000000000100000da4000013b0000000000000000100000daf000013d2000000000000000100000db8000013f4000000000000000100000dc10000141c000000000000000100000dcc00004b40000000000000000100000dd40000205c000000000000000100000ddf00001420000000000000000100000deb00003622000000000000000100000df500003722000000000000000100000dfd0000143a000000000000000100000e0a00001486000000000000000100000e15000015e0000000000000000100000e2200001444000000000000000100000e2a0000144e000000000000000100000e3600001458000000000000000100000e4300001462000000000000000100000e4d000019d6000000000000000100000e56000015de000000000000000100000e6100001502000000000000000100000e6c000019e6000000000000000100000e75000014de000000000000000100000e8000001512000000000000000100000e8a00001554000000000000000100000e950000158a000000000000000100000ea0000015c6000000000000000100000eac00001598000000000000000100000eb7000015b6000000000000000100000ec20000166a000000000000000100000ecc00001668000000000000000100000ed500001622000000000000000100000ee000001632000000000000000100000eec00004b44000000000000000100000ef500001638000000000000000100000efd00001654000000000000000100000f05000016a0000000000000000100000f11000016ac000000000000000100000f1c000016b8000000000000000100000f28000016ec000000000000000100000f3100001820000000000000000100000f3e00001714000000000000000100000f480000174e000000000000000100000f5300001726000000000000000100000f5d00001746000000000000000100000f670000175e000000000000000100000f7000001800000000000000000100000f7c00001796000000000000000100000f88000017f0000000000000000100000f930000181a000000000000000100000f9e00001828000000000000000100000fa800001844000000000000000100000fb300001918000000000000000100000fc0000018da000000000000000100000fcb0000190c000000000000000100000fd5000018ea000000000000000100000fdf000018e4000000000000000100000fe90000190a000000000000000100000ff500001934000000000000000100000fff000019d40000000000000001000010090000197c0000000000000001000010140000198a00000000000000010000101d000019bc000000000000000100001028000019a4000000000000000100001031000019be00000000000000010000103a000019ca00000000000000010000104400001a54000000000000000100001050000019f200000000000000010000105900001a5000000000000000010000106100001a0a00000000000000010000106c00001a1a00000000000000010000107500001a3800000000000000010000108000001a6000000000000000010000108900001a8a00000000000000010000109400001a7c0000000000000001000010a000001a980000000000000001000010ad00001aa00000000000000001000010b900001b720000000000000001000010c400001af40000000000000001000010cf00001b7e0000000000000001000010db00001b840000000000000001000010e500001ba00000000000000001000010ef00001b8c0000000000000001000010f900001bae00000000000000010000110400001bea00000000000000010000111200001be000000000000000010000111f00001c1600000000000000010000112a00001d3a00000000000000010000113500001cbc00000000000000010000114100001c8200000000000000010000114c00001c9a00000000000000010000115a00001c9400000000000000010000116600001ca600000000000000010000116f00001cac00000000000000010000117900004d4800000000000000010000118400004d4400000000000000010000119100001d3800000000000000010000119c00001cd40000000000000001000011a7000035b10000000000000001000011af00001e980000000000000001000011bc000035b70000000000000001000011c400001e6e0000000000000001000011d040031da4000000000000fff1000011dd400d5f38000000000000fff1000011e9400d2e8a000000000000fff1000011f44003c058000000000000fff1000012004003932c000000000000fff10000120a4003249c000000000000fff100001212400a72a8000000000000fff10000121a400a7280000000000000fff1000012220000001d000000000000fff10000122b00000900000000000000fff10000123700001d7200000000000000010000124100001d9800000000000000010000124b00001db400000000000000010000125400001e2a00000000000000010000125e00001e4400000000000000010000126b00001de000000000000000010000127500001e0e00000000000000010000127f00001de800000000000000010000128800001e6a00000000000000010000129300001e8000000000000000010000129a00001e820000000000000001000012a1000035de0000000000000001000012a800001edc0000000000000001000012b400001ef60000000000000001000012bf000036a20000000000000001000012c800001f060000000000000001000012d100001f140000000000000001000012db00001f220000000000000001000012e500004edc0000000000000001000012f300004eda0000000000000001000012ff00001f8600000000000000010000130a00001f4800000000000000010000131500001f5000000000000000010000132100001f6a00000000000000010000133100001f5e00000000000000010000133c00001f7600000000000000010000134700001fd60000000000000001000013530000200000000000000000010000135f0000205600000000000000010000136a0000204c00000000000000010000137400001ffa00000000000000010000137e0000204a00000000000000010000138b000020180000000000000001000013970000201a0000000000000001000013a4000020420000000000000001000013b1000020720000000000000001000013b9000021e20000000000000001000013c1000020900000000000000001000013c7000020920000000000000001000013cd000022400000000000000001000013d3000020ba0000000000000001000013d9000020c20000000000000001000013df000022200000000000000001000013e7000020d40000000000000001000013ed000020e00000000000000001000013f3000020ea0000000000000001000013f90000210e0000000000000001000013ff000020fa0000000000000001000014050000212200000000000000010000140b000021500000000000000001000014110000212e0000000000000001000014170000214000000000000000010000141e000021600000000000000001000014240000216200000000000000010000142a000021820000000000000001000014300000218e000000000000000100001436000021c400000000000000010000143e0000219e000000000000000100001444000021a000000000000000010000144a000021b6000000000000000100001450000021c6000000000000000100001456000021e400000000000000010000145c000021e60000000000000001000014620000220c0000000000000001000014680000222400000000000000010000146e000022320000000000000001000014740000225400000000000000010000147b000022560000000000000001000014840000226c00000000000000010000148a000022aa000000000000000100001492000022840000000000000001000014980000228600000000000000010000149e0000229c0000000000000001000014a4000022b00000000000000001000014aa000024160000000000000001000014b2000022d40000000000000001000014b80000234c0000000000000001000014bf000023320000000000000001000014c7000023200000000000000001000014d0000022f40000000000000001000014d6000022f60000000000000001000014dc0000237e0000000000000001000014e60000240c0000000000000001000014ed0000234a0000000000000001000014f60000237a0000000000000001000014fc000023620000000000000001000015020000238e0000000000000001000015080000239000000000000000010000150e000023f2000000000000000100001514000023a800000000000000010000151b000023e6000000000000000100001521000023cc0000000000000001000015280000242a00000000000000010000152f0000256c0000000000000001000015390000257800000000000000010000154600002550000000000000000100001550000024bc00000000000000010000155a000024d0000000000000000100001565000024e800000000000000010000156e000024fe000000000000000100001578000025380000000000000001000015830000259600000000000000010000158f00002594000000000000000100001599000025a20000000000000001000015a4000025ac0000000000000001000015b0000027880000000000000001000015b9000025f20000000000000001000015c6000025e40000000000000001000015d1000026160000000000000001000015db0000262e0000000000000001000015e40000271a0000000000000001000015ee000026680000000000000001000015f6000026780000000000000001000016010000272400000000000000010000160d000026ee0000000000000001000016160000270200000000000000010000161f00002810000000000000000100001629000028040000000000000001000016320000280600000000000000010000163c0000285600000000000000010000164600002858000000000000000100001651000028ae00000000000000010000165c0000290a000000000000000100001669000028f60000000000000001000016750000298400000000000000010000167e000029440000000000000001000016890000295c000000000000000100001697000029560000000000000001000016a3000029680000000000000001000016ac0000296e0000000000000001000016b6000029860000000000000001000016c0000029920000000000000001000016cd000029e40000000000000001000016d7000029dc0000000000000001000016e1000029de0000000000000001000016eb00002a180000000000000001000016f600002a3400000000000000010000170000002a2000000000000000010000170b00002a3200000000000000010000171500002a3e00000000000000010000172200002a4e00000000000000010000172d00002a7400000000000000010000173700002a7200000000000000010000174100002a6800000000000000010000174d00002a7e00000000000000010000175a00002a8e00000000000000010000176500002aaa00000000000000010000177100002aa400000000000000010000177c00002ace00000000000000010000178600002aba00000000000000010000179100002ab000000000000000010000179d00002ac00000000000000001000017a900002ad80000000000000001000017b700004c0c0000000000000001000017c000004c040000000000000001000017c900002b180000000000000001000017d300004b840000000000000001000017dc00002b5a0000000000000001000017e600002c0c0000000000000001000017f300002b840000000000000001000017fe00002bec00000000000000010000180b00002bc800000000000000010000181b00002c5c00000000000000010000182900002c5600000000000000010000183600002c6c00000000000000010000183d00002c9600000000000000010000184800002d3c00000000000000010000185600004c3400000000000000010000185f00002cec00000000000000010000186e00002d0e00000000000000010000187c00002d8800000000000000010000188600002dbe00000000000000010000189100002e0800000000000000010000189d00002dda0000000000000001000018a800002de00000000000000001000018b300002df00000000000000001000018be00002e060000000000000001000018c900002e2a0000000000000001000018d500002e8e0000000000000001000018e200002e560000000000000001000018ee00002e860000000000000001000018fa00002e5a00000000000000010000190600002e6a00000000000000010000191200002f6600000000000000010000191e00002ec800000000000000010000192800002f0800000000000000010000193200002f5800000000000000010000193d00002f2200000000000000010000194a00002f5400000000000000010000195400002fa800000000000000010000195e00002fa0000000000000000100001968400bebae000000000000fff10000197040051fd6000000000000fff10000197e40051ff6000000000000fff10000198e40052006000000000000fff10000199b460d1736000000000000fff1000019a4460d174a000000000000fff1000019ae460d174c000000000000fff1000019b8460d16f0000000000000fff1000019c2100b14cc000000000000fff1000019cc100b14d0000000000000fff1000019d40000091a000000000000fff1000019df00008ed8000000000000fff1000019ea00000059000000000000fff1000019f30009b332000000000000fff1000019fe100f8598000000000000fff100001a0540027e00000000000000fff100001a0d40027de4000000000000fff100001a144009da20000000000000fff100001a1c460d173a000000000000fff100001a25460d1a9e000000000000fff100001a2e460d10dc000000000000fff100001a37460d1750000000000000fff100001a40400418e0000000000000fff100001a484004d948000000000000fff100001a4f0008edaa000000000000fff100001a5a0000000c000000000000fff100001a6300000033000000000000fff100001a6a00003102000000000000000100001a75000030fc000000000000000100001a82000030f4000000000000000100001a93000030e6000000000000000100001aa100002ffa000000000000000100001aab00003002000000000000000100001ab50000308c000000000000000100001ac000003056000000000000000100001acb00003108000000000000000100001ad80000305e000000000000000100001ae10000306a000000000000000100001aea0000314c000000000000000100001af600003162000000000000000100001b0200003168000000000000000100001b0c0000316c000000000000000100001b18000031ee000000000000000100001b2400005070000000000000000100001b330000506e000000000000000100001b40000031e4000000000000000100001b4b0000319a000000000000000100001b56000035bd000000000000000100001b5d0000321a000000000000000100001b68000031f6000000000000000100001b7200003210000000000000000100001b7c0000322c000000000000000100001b83000035c3000000000000000100001b8f00003238000000000000000100001b9800003240000000000000000100001ba300003254000000000000000100001baa0000325c000000000000000100001bb1000032d0000000000000000100001bbb00003308000000000000000100001bc700003304000000000000000100001bd40000339e000000000000000100001bde000033d6000000000000000100001be8000033b6000000000000000100001bf4000033e0000000000000000100001c01000033d2000000000000000100001c0d0000340c000000000000000100001c1b0000506f000000000000000100001c2500003496000000000000000100001c300000353a000000000000000100001c3a000034c8000000000000000100001c4600003526000000000000000100001c5100003568000000000000000100001c5a40025288000000000000fff100001c6100003554000000000000000100001c6c0000355c000000000000000100001c7800095048000000000000fff100001c85100b145e000000000000fff100001c950009b332000000000000fff100001ca0100f8598000000000000fff100001cae40027e00000000000000fff100001cb9000035db000000000000000100001cc000005204000000000000000100001cc700002fae000000001000000100001cd10000166c000000001000000100001cdb0000186a000000001000000100001ce600002618000000001000000100001cf000001f92000000001000000100001cf80000242c000000001000000100001d0200003936000000001000000100001d0d00001f26000000001000000100001d1700000020000000001000000100001d2100001bb8000000001000000100001d2900001bc2000000001000000100001d3200001c18000000001000000100001d3d00002b6e000000001000000100001d4400002c18000000001000000100001d4c00002da8000000001000000100001d5500002e16000000001000000100001d5f00002e9a000000001000000100001d6700002f78000000001000000100001d700000325e000000001000000100001d7c0000326a000000001000000100001d880000327e000000001000000100001d9400003292000000001000000100001da0000032a6000000001000000100001dab000032d6000000001000000100001db600003312000000001000000100001dc000003346000000001000000100001dcb00003372000000001000000100001dd700003484000000001000000100001de200002d4e000000001000000100001dea00003544000000001000000100001df400002c9c000000001000000100001dfe00002896000000001000000100001e0a000028c2000000001000000100001e15000027b2000000001000000100001e2100002812000000001000000100001e2800002918000000001000000100001e3700001d40000000001000000100001e4200001d9e000000001000000100001e4d00001d78000000001000000100001e5700000000000000001000000000001e6100000000000000001000000000001e6b00000000000000001000000000484156455f53594452554d004e4942424c450050495443485f5441420043555256414c530043565f5354524944450043565f574f5244530043565f534c455700504152545f50545200504152545f49445800504152545f534841444f57004d4143485f4f4646005349475f4f464600464c45585f5000464c45585f444553435f4c454e0050494e475f41540046554e435f48454c440046554e435f454e435f4f4646004b45595f48454c4400505553485f434f4445004b4e5f534c4f545300464d5f444553430043345f494e4300454e565f4f4e4500494e435f4d415800434b5f5449434b5300434b5f54505300434b5f46505300434b5f4652414d45530053545f53545249444500535f4741494e00535f4f4e00535f475052455600565f4741494e00565f475052455600564f4943455f4241534500564f4943455f53545249444500435552534f520053544f434b5f52454e44455200464d5352435f52455400464c45585f53544152540046505f5054520052535f5054520048454144524f4f4d0053455454494e47535f424153450053455454494e47535f5350414e0053455454494e47535f53545249444500454e565f464c4f4f52004b5f4e554d004b5f4d415800494e4445585f5343414c450046425f5343414c4500474c4944455f4154004c4b5f48454c44004b4559535f415400434c4f434b5f4154005345515f53544550005345515f5449434b0043565f484f4c4400434b5f4c41535400434b5f50494e4700434b5f5354455000434b5f4653544550004650535f44454641554c54005343414c455f41540043565f434852440043565f564f49430043565f52454c004c464f5f5354415445004c464f5f50004c464f5f444553435f4c454e005245534f4c5645525f52455400534c4f545f4f464600534c4f545f434f4c0043565f52415445004c49535457494e5f4d4143480041535349474e5f524554004d41434857494e5f524554004c49535457494e5f524554004c4f414453454c5f535052494e54460053454d490053484150455f454e44004453505f5245430043565f41544b004355545f534849465400474c4f42414c5f574f5244004b494c4c5f52455400535f50484300535f50484d00535f454e5600535f494e4300535f494e434d00535f4945464600535f464200535f4c4153544d00535f43555200545f52454600545f4c41535400545f4d41534b00545f444b00545f524b00545f524154494f00545f4900545f5700545f5343414c4500545f504f4c5900535f4552414d500052545f43414c4c530052545f46495845440052545f484541440052545f52494e470052545f53495a4500545f414b00545f474d415800535f4854494d00545f5241575000545f534d41534b00545f4c4153544b00545f504f5300565f504f530055495f545241434b004c464f5f504147455f4f464600565f50484300565f50484d00565f454e5600565f494e4300565f494e434d00565f4945464600565f464200565f4c4153544d00565f535441544500565f4b455900565f495354455000565f43555200565f524f4f5400565f41474500565f4652414d4500565f4552414d500043525f5354524944450043525f535445500043525f4e0043525f57524f54450043525f504b45590043525f4652414d450043525f4b4559530043525f524157530043525f504652414d450043525f504354580043525f505449434b530043525f505241570043525f564f4943004b525f57494e004b525f434f4e46004c4956455245435f415400515a5f4f43545f4154005245435f54524947005041545f52414d005041545f4d4952005041545f4e55444745005041545f4355520042414e4b5f435552004c4f434b5f5752495445005245435f4354580043565f5054434800414d505f5000414d505f444553435f4c454e00414d505f50325f4f4646004c45475f534c4f5432004c45475f574944474554004d41535445525f4f4e005245435f545249474c45535300545f4c45474b455900545f4c45474e00535f495354455000545f4d4c4547004b4e5f524154494f004b4e5f524154494f5f4e004b4e5f535542554e4954590073795f73746174650073795f63616c6c0073795f69736f6e0073795f6d616368696e650073795f6e6f00706f5f7369676e65640073795f636f6c640073795f7363616e0073795f7363616e6e65640073795f7363616e310073795f666d0073795f6e616d650073795f636d700073795f636f6c645f6e6f686f6c6400706f5f6c6976656b657900706f5f636c6f636b0073795f636f6c645f686f6c640073795f636f6c643100706f5f7866710073795f7761726d5f656e7600706f5f63617272790073795f7761726d0073795f746f706f6c7900706f5f6661646500706f5f6d72696e675f72657365740073795f73796e74680073795f776f72640073795f73657400706f5f667265650073795f73616d706c650073795f6e65757472616c0073795f736c65770073795f766f6963650073795f73746f636b0073795f6e61745f656e640073795f6e61745f636c720073795f63616c6c65640073795f636865636b0073795f6d6f6e6f5f6672616d6500706f5f6672616d6500706f5f666164655f6672616d6500706f5f726174655f666f6c640073795f6d6f6e6f5f696e630073795f726174696f0073795f656e765f6b0073795f6e6f64656361790073795f656e76300073795f656e76310073795f656e76646f6e650073795f696c5f646f6e650073795f696c5f6c65667400706f5f65726c656e00706f5f6d6f6e6f5f656e7600706f5f6d655f72656c00706f5f6d655f61746b00706f5f6d655f73746f726500706f5f61746b00706f5f616c617700706f5f72656c6b00706f5f6d655f72656c3000706f5f6d655f72656c3100706f5f616c5f7000706f5f616c5f7300706f5f616c5f736300706f5f616c5f6600706f5f616c5f6f757400706f5f616c5f7100706f5f6c6b5f6f757400706f5f6c6b5f6e6f00706f5f6c6b5f79657300706f5f78715f6e6f00706f5f78715f61627300706f5f78715f79657300706f5f65726c5f6f757400706f5f65726c5f31360073795f646f6e650073795f616c6976650073795f6d6f6e6f00706f5f66696c6c0073795f7461620073795f6c6f6f700073795f67656e640073795f6d6f6e6f5f7461696c7300706f5f616e7900706f5f66696c6c5f61646400706f5f7469636b00706f5f746b5f646f6e6500706f5f746b5f7374657000706f5f7261746500706f5f726174653100706f5f726174653200706f5f726174653300706f5f726174653400706f5f72665f666f6c643100706f5f72665f666f6c643200706f5f72665f666f6c643300706f5f72665f7261746500706f5f72665f6f757400706f5f72665f646f776e0073795f676c69646500706f5f6c6567627974650073795f736c5f736e61700073795f736c5f75700073795f736c5f73746f726500706f5f66725f72656300706f5f63686f726400706f5f66725f737461727400706f5f766f696365735f6f6600706f5f66725f676f6e6500706f5f66725f676f6e653100706f5f66725f6d676f6e6500706f5f66725f6d676f6e653100706f5f68656c6400706f5f66725f6e6f6c6800706f5f66725f6c6800706f5f68616e646f76657200706f5f66725f706172616d7300706f5f737461727400706f5f66725f686f6c6400706f5f66725f6b00706f5f66725f646b00706f5f66725f726b00706f5f66725f616b00706f5f66725f7600706f5f66725f6e65787400706f5f66725f696e633100706f5f66725f656e766b00706f5f66725f656e76686f6c6400706f5f66725f656e7600706f5f66725f696c5f646f6e6500706f5f66725f696c5f6c65667400706f5f66725f616d7000706f5f66725f72656c00706f5f66725f63757400706f5f66725f63617000706f5f66725f6761696e00706f5f66725f72656c3100706f5f66725f72656c316100706f5f66725f72656c3200706f5f70737461727400706f5f70656e6400706f5f766f6963657300706f5f667265653100706f5f666164653100706f5f666164653200706f5f63615f7069636b00706f5f63615f6672656500706f5f63615f6e65787400706f5f63615f74616b6500706f5f70636c65617200706f5f63615f636f707900706f5f73657100706f5f63615f677000706f5f63615f6700706f5f66665f7374657000706f5f66665f6c6f6f7000706f5f66665f6e65787400706f5f66665f6900706f5f66665f3100706f5f66665f73746f726500706f5f70656e646f6600706f5f73745f786600706f5f78665f7363616e00706f5f78665f6e65787400706f5f706d6174636800706f5f736e617000706f5f706d5f6c6f6f7000706f5f706d5f6e65787400706f5f706d5f6100706f5f706d5f79657300706f5f7066697800706f5f70665f6f757400706f5f70735f6f757400706f5f70735f64726f7000706f5f73745f666f6c6400706f5f73745f666f6c643100706f5f73745f70616e656c00706f5f73745f6d3100706f5f6d72696e675f706f7000706f5f73745f6d646f6e6500706f5f73746172743100706f5f73745f6f757400706f5f73745f73773100706f5f73745f7377646f6e6500706f5f73745f73773200706f5f73745f73773300706f5f73745f316100706f5f73745f676f00706f5f73745f6473657100706f5f73745f646b657900706f5f73745f646b3100706f5f73745f64757000706f5f73745f64733100706f5f6d68656c645f6f6600706f5f73745f64733200706f5f73745f63686f726400706f5f73745f72656c00706f5f73745f72656c3100706f5f73745f6e7300706f5f73745f763100706f5f73745f726f773000706f5f7662756600706f5f766f6963696e6700706f5f73745f736861706500706f5f73686170657300706f5f676d617800706f5f73745f73696e676c6500706f5f73745f6e6f746500706f5f73745f63707265737300706f5f73745f6b00706f5f73745f636f756e7400706f5f73745f636f756e743100706f5f73745f63757400706f5f737465616c00706f5f73745f646f6e6500706f5f73745f7761726d00706f5f616c6c6f6300706f5f73745f636f6c6400706f5f73745f656e7600706f5f73745f6873657100706f5f73745f68696e6600706f5f73745f737461746500706f5f73745f6c69766500706f5f73745f6c6b657900706f5f63705f6f757400706f5f63705f6e6f00706f5f63705f6d69646900706f5f63705f636f756e7400706f5f6d68656c6400706f5f63705f3100706f5f63705f3200706f5f686f5f666f6c643000706f5f686f5f666f6c6400706f5f686f5f666f6c643100706f5f686f5f763100706f5f686f5f73696e676c6500706f5f686f5f636f6c00706f5f686f5f636f6c3200706f5f686f5f696e7300706f5f686f5f70757400706f5f686f5f617300706f5f686f5f657874726100706f5f686f5f667265736800706f5f686f5f6e65787400706f5f686f5f646f6e6500706f5f686f5f6e657700706f5f686f5f6e65773100706f5f6c6d5f73616d706c6500706f5f6c6d5f7061726100706f5f6c6d5f6f757400706f5f6c6d5f6c656700706f5f6c6d5f74776f00706f5f6c6d5f746872656500706f5f6c6d5f72657400706f5f736e5f72657400706f5f736e5f6469737400706f5f736e5f6c6f00706f5f736e5f646f776e00706f5f736e5f686900706f5f736e5f757000706f5f736e5f6f757400706f5f72616e6b5f61637400706f5f616c5f676f00706f5f72616e6b00706f5f616c5f6c6f6f7000706f5f616c5f726b00706f5f616c5f6e65787400706f5f66696c6c3100706f5f66695f68616c6600706f5f66695f636c65617200706f5f66695f766f6963657300706f5f66695f766f69636500706f5f66695f6e65787400706f5f66695f6c6f6f7000706f5f66695f756e69747900706f5f66695f6f757400706f5f66695f73617400706f5f66695f70757400706f5f66695f7361747000706f5f6c666f335f646570746800706f5f6c33625f646570746800706f5f6c335f6261636b00706f5f6c705f646f6e6500706f5f6c705f636c6f6e6500706f5f6c705f7363616e00706f5f6c705f7363616e6e656400706f5f6c705f7363616e3100706f5f6c705f666d00706f5f6c705f636d7000706f5f6c666f6465736300706f5f6c666f5f6275696c7400706f5f6c705f6861766500706f5f6c705f636f707900706f5f6368726400706f5f666d745f63686f726400706f5f766f696300706f5f666d745f766f6963004c46445f5245534f4c564552004c46445f4d414348544142004c46445f4d4153544552004c46445f464d545f524554004c46445f53544f5245004c46445f454e43004c46445f563250004c46445f503256004c46445f4c415354004c46445f484944455f464d00706f5f6c6e5f6f757400706f5f6c6f5f6f757400706f5f6c655f6f6b00706f5f6c655f6d617000706f5f6c645f68696464656e00706f5f6c655f636e7400706f5f6c655f656e6400706f5f6c655f6e7800706f5f6c685f6e6f6e6500706f5f66763100706f5f66763200706f5f665f6400706f5f66635f736861706500706f5f66635f6e616d6500706f5f6e616d657300706f5f66635f637000706f5f66635f696e7600706f5f66635f656e6400706f5f7067646573635f62756600706f5f70675f6275696c7400706f5f70675f6861766500706f5f70675f636f707900706f5f70675f706174636800706f5f70675f6275696c745f6e6f7700706f5f70675f6279746500706f5f70675f6b6e6f6200706f5f6b6e5f706c61696e00706f5f6b6e5f726174696f00706f5f6b6e5f6a756d7000706f5f6b6e5f6f757400706f5f6b6e5f6f6e6500706f5f6b6e5f725f646f6e6500706f5f6b6e5f725f64657400706f5f6b6e5f725f7363616e00706f5f6b6e5f725f74616b650070765f6c6f61640070765f736f72740070765f64310070765f64320070765f70630070765f64330070765f64340070765f646964780070765f69310070765f69320070765f72310070765f72330070765f72320070765f62310070765f62330070765f62320070765f6232610070765f6f310070765f6f320070765f6f330070765f6f340070765f636f70790070765f70310070765f70320070765f70330070765f63310070765f73310070765f73320070765f73330070765f78310070765f78320070765f70633100706f5f6d6174636800706d5f6c3100706d5f6261736500706d5f703100706d5f703200706d5f703300706d5f693100706d5f7063313200706d5f653100706d5f74727900706d5f6b65657000706d5f666f756e6400706d5f653200706d5f653300706d5f7365636f6e6400706d5f6f757400706d5f6b5f6f757400706d5f6e6f00706d5f743100706d5f633100706d5f633200706d5f633400706d5f63326100706d5f633300706d5f63326200706d5f70633100706f5f6b725f6f757400706f5f6b725f736574746c6500706f5f6b725f6e657700706f5f6b725f616e7900706f5f6b725f6a6f696e00706f5f6b725f637000706f5f6b725f63703100706f5f6b725f66756c6c00706f5f6b725f777269746500706f5f6b735f6f757400706f5f6b775f726f6f7400706f5f6b775f726f6f743100706f5f6c6f636b7700706f5f6b775f72656d6f766500706f5f6b775f766f696300706f5f6b775f6f757400706f5f726c5f696400706f5f726c5f6f757400706f5f726c5f6b00706f5f726c5f68616e6400706f5f726c5f6e7564676500706f5f726c5f733100706f5f726c5f733200706f5f69735f72747300706f5f69735f6e6f00706f5f69735f6f757400706f5f7369675f6e6f00706f5f7369675f6f757400706f5f66735f6b65657000706f5f6673745f73746f636b00706f5f6673745f636f6c6400706f5f736d5f6e6f00706f5f736d5f7363616e00706f5f736d5f7363616e6e656400706f5f736d5f7363616e3100706f5f736d5f666d00706f5f736d5f636d7000706f5f736d5f6f757400706f5f706f6c79747261636b00706f5f70745f72657400706f5f70745f79657300706f5f70745f6f757400706f5f68645f6d69646900706f5f68645f6f757400706f5f68645f7363616e00706f5f68645f79657300706f5f6d68656c645f61646400706f5f6d615f7363616e00706f5f6d615f6f757400706f5f6d615f70757400706f5f6d615f736869667400706f5f6d68656c645f64656c00706f5f6d645f7363616e00706f5f6d645f666f756e6400706f5f6d645f6e6f6e6500706f5f6d645f6f757400706f5f6d645f6c61737400706f5f6d645f736869667400706f5f6d645f636f756e7400706f5f6d72696e675f7075736800706f5f6d7461696c00706f5f6d6865616400706f5f6d705f6f757400706f5f6d72696e6700706f5f6d715f6f757400706f5f6d6f6e5f73746f636b00706f5f6d6f6e5f72617700706f5f6d6f6e5f737461727400706f5f6d6f6e5f747269676c65737300706f5f6d6f66665f73746f636b00706f5f6d6f66665f6d6f726500706f5f72656c00706f5f72656c5f6f757400706f5f7265747269675f6f757400706f5f72746c6f6700706f5f7265747269675f6e6f737400706f5f7265747269675f6c6f6700706f5f6b696c6c5f7600706f5f6d675f7769646500706f5f6d675f73746f636b00706f5f6d675f6d61736b00706f5f6d675f7363616e00706f5f6d675f6e65787400706f5f6d675f6e6f6e6500706f5f6d6f675f7769646500706f5f6d6f675f73746f636b00706f5f6d6f675f6d61736b00706f5f6d6f675f6e6f6e6500706f5f6d6f675f7363616e00706f5f6d6f675f6e65787400706f5f6d725f73746f636b00706f5f6d725f72617700706f5f6d725f6c656700706f5f6d725f646f6e6500706f5f6d725f706c6163656400706f5f6d725f706f7000706f5f6d745f6f757400706f5f6d745f7261770053454c5f57494e004f43545f484f4f4b5f4f50454e004f43545f484f4f4b5f434c4f534544004f43545f484f4f4b5f52545300475249445f5245430048454c445f4d41534b0048454c445f5041474500545249475f4d4f444500545241434b5f435552005041545f4944580054524b5f535452494445005041545f535452494445004c4f434b5f4f46460042414e4b5f4449525459004449525459320045445f544944590045445f454e440045445f53454e540045445f464c4147310045445f574f5244530045445f574f5244360045445f464c4147320045445f43414c4c0052454452415700504c41595f534c4f5453004f43545f4a554d50004b45595f555000706f5f6f635f6f70656e00706f5f6f635f636c6f73656400706f5f6f635f706f705f636c6f73656400706f5f6f635f7377616c6c6f7700706f5f6f635f64697200706f5f6f635f62697400706f5f6f635f6e65787400706f5f6f635f6861766500706f5f706172745f7074636800706f5f6f635f6c6f00706f5f6f635f686900706f5f616d705f6b696e6400706f5f616b5f617564696f00706f5f616b5f6f757400706f5f616d705f6465736300706f5f61645f73746f636b00706f5f616d70646573635f62756600706f5f616d705f6275696c7400706f5f61645f6861766500706f5f61645f636f707900706f5f6c656700706f5f666d745f6c656700706f5f6c65676d617800706f5f6c785f6f757400706f5f666c3100706f5f6c65676e616d657300706f5f666c5f637000706f5f6c65676b6e6f6200706f5f6c6b3100706f5f6c6b3200706f5f61655f6f757400706f5f616c5f73746f726500706f5f616c5f73616d706c6500706f5f626563616d6500706f5f62635f6f757400706f5f62635f736c6f747300706f5f666c65785f736c6f7400706f5f62635f726573657400706f5f66696e655f726573657400706f5f6c735f77617300706f5f6c735f63616c6c00706f5f6c735f6f757400706f5f6c735f747261636b00706f5f6c735f6e65787400706f5f646972747900554e4c4f414400706f5f756c5f666c657800706f5f756c5f73746f726500504152545f554e534156454400504152545f554e53415645445f42520042414e4b5f57524954450042414e4b5f57524954455f425200554e444f5f524553455400706f5f665f7300706f5f656e6400706f5f6f637461766500706f5f6c65676b657900706f5f6c65676d6f646500706f5f6b657972656c00706f5f6b6e6f6200706f5f6b657972656300706f5f686f6c6431323800706f5f7067646573630073795f72656e64657200706f5f6c666f3300706f5f6c666f336200706f5f6c666f7061676500706f5f6d6f6e00706f5f6d6f666600706f5f6d6761746500706f5f6d6f6761746500706f5f6d72656300706f5f6d7472696700706f5f616d70737461676500706f5f616d70647261773100706f5f616d70647261773200706f5f616d70647261773300706f5f616d706564697400706f5f616d706c616e6500706f5f61737369676e00706f5f6d61636877696e00706f5f6d6163686c69737400706f5f6c6f616473656c00706f5f6b696c6c00706f5f756e6c6f616400706f5f72657472696700706f5f666d736f7572636500706f5f666d737461727400706f5f69735f73796e746800706f5f73696700706f5f736c6f745f6d61726b657200706f5f6c66646e616d6500706f5f6c66646564697400706f5f6c66646c666f006d6c5f626563616d65006d6c5f6b696e646f66006d6c5f726573657400000000000000010100002fae00000004000001010000166c00000008000001010000186a0000000c0000010100002618000000100000010100001f9200000014000001010000242c0000001800000101000039360000001c0000010100001f26000033340003230100000000000035120003240100000000000035200003250100000000002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e6273730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000100000006000000000000003400005204000000000000000000000004000000000000001b0000000400000040000000000000a30c000000840000000500000001000000040000000c000000260000000100000003000000000000523800000000000000000000000000000004000000000000002c00000008000000030000000000005238000000000000000000000000000000040000000000000001000000020000000000000000000052380000326000000006000002fc0000000400000010000000090000000300000000000000000000849800001e7400000000000000000000000100000000000000110000000300000000000000000000a3900000003100000000000000000000000100000000", + "sha256": "a3aac4ca0ffdc7d326825b2268cff67e58087c8449d4152e9624233b8dc08c42" } ] }, + { + "label": "fmmachine", + "moduleId": "synth", + "version": "0.1.5-experimental", + "key": "SYNTH MACHINE", + "author": "timhastie", + "nativeAuthor": "timhastie", + "cpu": "54455", + "dram": true, + "caveAddress": null, + "source": "modules/synth/upstream/synth/machine.s", + "sources": { + "modules/synth/upstream/synth/machine.s": "63ff52340e82f55535a81a678921f3be1c7a281dcd05f25aaea072221c01cd50", + "modules/synth/manifest.py": "f364cd97b5a2a416b3029752fe2ce0691fc977d59203419d9d5a52bbeb7913b7" + }, + "bytes": 7568, + "code": "7f454c460102010000000000000000000001000400000001000000000000000000001c50000000260034000000000028000800074fefffdc48d77f09207946c8245671b9100b14cf263c000018b24c030800d1c043f91001614ed3c02002c0fc001ed1c0d3c02448d5fc0008edbc2649d7fc0008edbc6100007a260020015b800c80000000016400003a4dfa07a82c760c0071ae0003b08367000006610001301496156e00010001156e000200021696176e00010001176e0002000272016000002870000c81000000016600000661000028b08367000006610000f4204a6100036c204b610003664cd77f094fef00244e754ef9000000002f086100001a2002c0fc0005d1c0d1fc0008f04b7190205f4ef9000000002f0071b9100b14cfc0fc18b2207946c82456d1c0201f4e750c8000000001660000382f086100ffda2002c0fc001ed1c0d1fc0008edbc610002b26b00001241fa06de20700c0071a80003205f4e75205f20014ef90000000070004e754fefffe848d7003f2a010c81000000016700000a6100002c2601280122036100ff9c2600200522046100ff92b083670000082205610000264cd7003f4fef00184e752f086100ff5e2202c2fc0005d1c1d1fc0008f04b7390205f4e754fefffd048d73f3f6100ff3e260896b946c8245643f91001614ed3c32602c6fc004806838000081024432602ed8b068380000a502643280067000016610001ca2a6d000872017601610000f26000002c2a0172012a79400d5f3c4bed005e7600610000da22054bf9400d5f382a751c004bed005e7601610000c42602e78b2003d683d68006830008ee9a49f9400d38547200719c0c84000000016600001a0c81000000026600000470010c8100000005660000047000610000f652810c81000000066600ffce49f9400d39e6719c610000de52810c810000000c6600fff02602eb8b9682968206830008f072720049f9400d385a610000f649f9400d39ec719c610000c652810c810000000c6600fff0721849f9400d385c610000d22002eb88068080000db42840429c429c429c429c42944cd73f3f4fef00304e754feffff448d700322002c0fc001e2801c8fc0006d08406800008edaa2a000685000001b0720079b5180011840800138408004a836700000c15841800e18c37841a0079b5180611845800138458004a8367000006158418205280528552810c81000000066600ffc04cd700324fef000c4e752f03d681118038001380380015801806e18837801a0c261f4e752f03d6811180380013803800261f0c810000001864000008158018264e75158018264e757a0371946100ffd6528171ac00016100ffcc528153856600ffea4e752f0172004bfa04582a751c00b02d00036700001452810c81000000016600ffe64bfa043c2a55221f4e754feffff848d70202700043fa042822710c007390b2116600001a73a80001b22900016600000e73a80002b22900026700001052800c80000000016600ffce70ff4cd702024fef00084a804e756100ffb26b00000c421042280001422800024e752f022f01240122046100fbca2801221f241f4ef9000000002239460d5c306100fbb44ef94005a61c2239460d5c306100fba44ef94005a8560c80000000016600005a4feffff048d70306227946c8245673b9100b14cf243c000018b24c021800d3c12408948904820008eda27208b48164000020c4fc001e2049d1c2d1fc0008edbc6100ff1c6b0000085a806000000470014cd703064fef00104e75202f00045b800c80000000016500000e2f02222f00084ef9400334de41fa031a20700c00202800044e756100ff7041f9400a78c80c80000000056c00000c22300c004ef94003d72241fa02da20700c002228000441f9400a78c84ef94003d72271104fef00186100ff344ef94003c98671106100ff282239460e7386d282b280660000084ef9400786ce4ef9400786fc16102f0071036100ff042600201f79034879400bb7044ef9400585e671106100feec2f004879460e73864ef9400788900c82000000056d00000474012002e788d48290824ef94003a5360c86000000056d0000047c012e06e78fdc869e864ef94003cda04eb94005213c4eb94007e94041f9400a78c843fa0234700522d853806600fffa2f0a2f02740045fa021c24722c00202a000441fa022821802c00206a000c4e9041f9400d5f4c21802c0052820c82000000016600ffd2241f245f4ef94005222848780006487800064ef94007925048780006487800054ef9400586027205b282650000084ef94003c9564ef94003c95a2039460e7386d0820c80000000056e00000e2f0020434e904ef9400786844ef9400786a27005b0826d0000084ef9400786d44ef9400786fc7205b2806c00000470054ef940078c2a7205b2806c00000470054ef940078cb87205b2806c00000470054ef940078d367205b2806c00000470054ef940078df67205b2806c00000470054ef940078e847605b6846d0000084ef9400797cc4ef94007990c4fefff8c48ef0c1c0060246f007895fc0008ed8076007400204ad1fc0008eda271b0280053806600003e2002c0fc001e204ad1c0d1fc0008edbc6100fca86b00002605c36100006e78061290136801b000061093116b000601b052885289528b53846600ffe652827008b4806600ffaa2f2f00786100006a588f740005036700001e2f0061000036201f780610911169000601b05288528953846600fff052827808b4846600ffd64cef0c1c00604fef00744e7543ef00046000000643ef00082002c0fc000cd3c02002c0fc001e204ad1c0d1fc0008edb047f9400d320c4e754fefffa048d77cfc4ef940002320400c20400028000000000000464d2053594e54480000464d01010000000000000000400d24d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000100000000000000000000000003000003000000000000000000000000030000040000000100000000000000000000fff10000000d46104d0c000000000000fff100000014400aa294000000000000fff10000001e80000810000000000000fff10000002600000048000000000000fff10000003080000a50000000000000fff10000003980000db4000000000000fff10000004146c82456000000000000fff10000004a100b14cf000000000000fff1000000531001614e000000000000fff10000005f0008eda2000000000000fff1000000680008edbc000000000000fff100000070400d31ae000000000000fff10000007700000192000000000000fff100000085800000e0000000000000fff10000008d40013a08000000000000fff10000009546c7dd26000000000000fff10000009f800000a0000000000000fff1000000ac46c7d8ee000000000000fff1000000b500000038000000000000fff1000000bf0000012a000000000000fff1000000c8400d24d0000000000000fff1000000d00184cbb7000000000000fff1000000d701000000000000000000fff1000000df73000000000000000000fff1000000e700000000000000000000fff1000000f000000004000000000000fff1000000f700000008000000000000fff1000000fe0000000c000000000000fff10000010800000080000000000000fff10000011200000018000000000000fff10000011900000024000000000000fff10000011e00000026000000000000fff10000002700000040000000000000fff10000012600000018000000000000fff10000012d0000003c000000000000fff10000013500000005000000000000fff10000013c00000001000000000000fff10000014500000005000000000000fff10000014d00000003000000000000fff10000015500000004000000000000fff10000015d00000008000000000000fff1000001660000000c000000000000fff10000016e00000010000000000000fff10000036500000001000000000000fff1000001770008ed80000000000000fff1000001800008edb0000000000000fff100000188000001b0000000000000fff10000019200000020000000000000fff10000019b460d5c30000000000000fff1000001a6400a78c8000000000000fff1000001ac400d5f38000000000000fff1000001b5400d320c000000000000fff1000001be400334de000000000000fff1000001c74003d722000000000000fff1000001d24003c986000000000000fff1000001e0400786ce000000000000fff1000001ec400786fc000000000000fff1000001f9400585e6000000000000fff10000020840078890000000000000fff1000002194003a536000000000000fff1000002234003cda0000000000000fff10000022d4005a61c000000000000fff10000023c4005a856000000000000fff10000024c4005213c000000000000fff1000002534007e940000000000000fff10000025a40052228000000000000fff10000026340079250000000000000fff10000027140058602000000000000fff10000027f4003c956000000000000fff10000028d4003c95a000000000000fff10000029c40078684000000000000fff1000002aa460e7386000000000000fff1000002b3400786a2000000000000fff1000002c2400786d4000000000000fff1000002ce400797cc000000000000fff1000002db4007990c000000000000fff1000002e940002320000000000000fff1000002f700000000000000000000000100000301000000be00000000000000010000030a0000008e0000000000000001000003160000006c000000000000000100000321000000b400000000000000010000032b0000009e000000000000000100000335000000c4000000000000000100000341000000a800000000000000010000034e00000418000000000000000100000358000000e20000000000000001000003600008f04b000000000000fff10000036a0000013a000000000000000100000376000003cc0000000000000001000003800000013000000000000000010000038d0000015a000000000000000100000399000001800000000000000001000003a5000001760000000000000001000003af400d37f6000000000000fff1000003b5400d3988000000000000fff1000003bb0000005e000000000000fff1000003c00008edaa000000000000fff1000003c70008ee9a000000000000fff1000003d10008f072000000000000fff1000003db00000006000000000000fff1000003e200000026000000000000fff1000003e80000003e000000000000fff1000003ee000001ea0000000000000001000003fa000003a2000000000000000100000405000002d600000000000000010000040f0000021400000000000000010000041b0000022c000000000000000100000425000002500000000000000001000004300000024400000000000000010000043b000003480000000000000001000004450000026600000000000000010000044f0000038600000000000000010000045a000002980000000000000001000004650000036200000000000000010000046f000002fc00000000000000010000047a000003180000000000000001000004860000032e0000000000000001000004910000038000000000000000010000049a000003880000000000000001000004a4000003a60000000000000001000004ae000003c80000000000000001000004b8000003d60000000000000001000004c2000003fe0000000000000001000004cd0000040c0000000000000001000004d70000042a0000000000000001000004e1000004640000000000000001000004e9000004c60000000000000001000004f3000004be0000000000000001000004fd000004bc000000000000000100000508000004e40000000000000001000005120000051000000000000000010000051c000005520000000000000001000005270000059400000000000000010000052d000005ae000000000000000100000533000005d400000000000000010000053e000005e2000000000000000100000548000006460000000000000001000005520000066a00000000000000010000055c0000067e000000000000000100000566000006e20000000000000001000005700000070000000000000000010000057b0000074e0000000000000001000005870000079c0000000000000001000005920000073200000000000000010000059b000007c80000000000000001000005ad000007640000000000000001000005b8000007860000000000000001000005c4000007a40000000000000001000005d0000007740000000000000001000005d9000007a80000000000000001000005e1000007d60000000000000001000005ed000007e20000000000000001000005f5000004c8000000001000000100000605000004f200000000100000010000060f0000080400000000100000010000061c0000052800000000100000010000062900000538000000001000000100000638000005580000000010000001000006460000057400000000100000010000065600000588000000001000000100000665000005a20000000010000001000006740000042c00000000100000010000068300000444000000001000000100000691000004540000000010000001000006a0000005bc0000000010000001000006a8000006e80000000010000001000006b40000061c0000000010000001000006c10000062a0000000010000001000006ce000006380000000010000001000006dc0000064c0000000010000001000006ea000006700000000010000001000006f6000006d40000000010000001000007030000068400000000100000010000070d00000694000000001000000100000717000006a4000000001000000100000721000006b400000000100000010000072b000006c40000000010000001000007350000080000000000100000010000073d000007ec0000000010000001000007440000019a00000000100000010000074d000000fa0000000010000001000007570000013e0000000010000001000007610000000000000000100000000000076d0000000000000000100000000000077c00000000000000001000000000484156455f53594452554d004e4942424c450050495443485f5441420043555256414c530043565f5354524944450043565f574f5244530043565f534c455700504152545f50545200504152545f49445800504152545f534841444f57004d4143485f4f4646005349475f4f464600464c45585f5000464c45585f444553435f4c454e0050494e475f415400535052494e54460046554e435f48454c440046554e435f454e435f4f4646004b45595f48454c4400505553485f434f4445004b4e5f534c4f545300464d5f444553430043345f494e4300454e565f4f4e4500494e435f4d415800434b5f5449434b5300434b5f54505300434b5f46505300434b5f4652414d45530053545f53545249444500535f4741494e00535f4f4e00535f475052455600565f4741494e00565f475052455600464d5f524f57004d4c5f434f554e54004d4c5f4c4153540052445f4b494e440052445f4e414d450052445f53454544530052445f444553430052445f464c41475300504152545f4f464600464c45585f50420053455455505f4741500043565f5345545550005352435f435552534f52004e414d45530050425f5441424c4500464c45585f444546004e414d455f524554004e414d455f415f5245540053455455505f524f575f5245540043484f4f5345525f4849540043484f4f5345525f4d4953530053455455505f4f50454e5f5245540043484f4f5345525f4f50454e5f5245540045444954365f5245540044524157365f524554005352435f434f4d4d49545f524554005352435f434f4d4d4954325f524554005449434b5f41005449434b5f42005449434b5f5245540057494e5f434f554e545f524554005352435f434f554e545f524554004e414d455f424f554e445f494e004e414d455f424f554e445f4f555400445241575f424f554e445f494e0057494e5f4c49535400445241575f424f554e445f4f55540048495f424f554e445f494e004355525f424f554e445f494e004355525f424f554e445f4f55540056414c49444154455f424f445900666d5f63686f6f7365006d6c5f6964656e7400666d5f63685f73746f636b00666d5f63685f7369676e00666d5f63685f6f757400666d5f63685f6e6577006d6c5f666c65786d61726b00666d5f63685f756e7369676e00666d5f756e7369676e006d6c5f7061727400534c4f545f464c4558006d6c5f6b6f5f73746f636b006d6c5f736967726f77006d6c5f6b6f5f6d61726b6572006d6c5f62635f736c6f7473006d6c5f666c6578736c6f74006d6c5f62635f6f7574004c464f5f5000414d505f5000444546310050425f4f46460050414745315f4f46460050414745325f4f46460043565f4c464f0043565f50320043565f4d54006d6c5f72735f73746f636b006d6c5f6b696e64726f77006d6c5f636f6c756d6e006d6c5f72735f7061676573006d6c5f72735f6c666f006d6c5f72735f6c666f31006d6c5f72735f63687264006d6c5f703162797465006d6c5f72735f616d70006d6c5f70327061697273006d6c5f72735f616d7073006d6c5f703262797465006d6c5f636c5f62797465006d6c5f636c5f7365747570006d6c5f636c5f6e657874006d6c5f70325f6d74006d6c5f70705f6c666f006d6c5f6b725f726f77006d6c5f6b725f6f7574006d6c5f73725f726f77006d6c5f73725f6e657874006d6c5f73725f6f757400666d5f75735f6f757400666d5f7479706500666d5f74795f72747300666d5f74795f6f757400666d5f74795f666c657800666d5f6d6e5f726f7700666d5f6e615f726f7700666d5f63725f6d69737300666d5f653600666d5f643600666d5f746b5f636f707900666d5f746b5f726f77006d6c5f6e625f6f7574006d6c5f64625f6f7574006d6c5f68625f6f7574006d6c5f63625f6f757400666d5f76645f7361766500666d5f76645f736e65787400666d5f76645f7074727300666d5f76645f733100666d5f73746f636b5f76616c696461746500666d5f76645f7265737400666d5f76645f726e65787400666d5f76645f707472733400666d5f76645f723100666d5f76645f7000666d5f64656661756c747300666d5f6e616d6500666d5f6d616368696e655f6e616d6500666d5f6e616d655f6100666d5f7372635f6e616d657300666d5f73657475705f726f7700666d5f63686f6f7365725f726f7700666d5f73657475705f6f70656e00666d5f63686f6f7365725f6f70656e00666d5f73657475705f656469743600666d5f73657475705f647261773600666d5f6d61696e5f636f6d6d697400666d5f7372635f636f6d6d697400666d5f7372635f636f6d6d69743200666d5f7469636b00666d5f76616c6964617465006d6c5f77696e5f636f756e74006d6c5f7372635f636f756e74006d6c5f6e616d655f626f756e64006d6c5f647261775f626f756e64006d6c5f68695f626f756e64006d6c5f6375725f626f756e64006d6c5f636c616d7031006d6c5f636c616d7032006d6c5f636c616d7033006d6c5f636c616d7034006d6c5f636c616d7035006d6c5f726f777300666d5f726f77006d6c5f7265736574006d6c5f6b696e646f66006d6c5f626563616d6500706f5f69735f73796e746800706f5f736c6f745f6d61726b657200706f5f6d61636877696e0000000007f000000101000007e2000007f400000101000007d60000080000000101000007ec000000c00000bc0100000000000000de0000bd0100000000000001360000bd0100000000000004400000be0100000000002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e627373000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000010000000600000000000000340000081c000000000000000000000004000000000000001b00000004000000400000000000001bc8000000540000000500000001000000040000000c000000260000000100000003000000000000085000000000000000000000000000000004000000000000002c000000080000000300000000000008500000000000000000000000000000000400000000000000010000000200000000000000000000085000000bf0000000060000009e0000000400000010000000090000000300000000000000000000144000000787000000000000000000000001000000000000001100000003000000000000000000001c1c0000003100000000000000000000000100000000", + "sha256": "cd42a49cce0f396f727726fc9a46dcd8de8f0f6a63ddd3315f90bc8f491b3859", + "stockCopies": [], + "placement": "linked", + "poolBaseLiterals": 0, + "variants": [] + }, { "label": "fm_qzk", "moduleId": "synth", - "version": "0.1.2-experimental", + "version": "0.1.5-experimental", "key": "SYNTH MACHINE", "author": "timhastie", "nativeAuthor": "timhastie", @@ -282,7 +305,7 @@ "source": "modules/synth/upstream/quantizer/keys.s", "sources": { "modules/synth/upstream/quantizer/keys.s": "e2ffdbfae34b0828794a4eb4cc229e3efcaed25dc953bf7c05c13f6e37f65587", - "modules/synth/manifest.py": "7854330aca001d2841de2fb384f0ca8adb7497db49dacc56da2815c1f444c0e6" + "modules/synth/manifest.py": "f364cd97b5a2a416b3029752fe2ce0691fc977d59203419d9d5a52bbeb7913b7" }, "bytes": 588, "code": "7f454c46010201000000000000000000000100040000000100000000000000000000013400000000003400000000002800070006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000100000000000000000000000003000002000000000000000000000000030000030000000100000000000000001000000100000009000000080000000010000001000000120000002800000000100000010000001b0000002c000000001000000100717a5f706b657900717a5f706d61736b00717a5f636c6f636b00717a5f6f637400002e73796d746162002e737472746162002e7368737472746162002e74657874002e64617461002e627373000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b000000010000000600000000000000340000002d0000000000000000000000040000000000000021000000010000000300000000000000640000000000000000000000000000000400000000000000270000000800000003000000000000006400000000000000000000000000000004000000000000000100000002000000000000000000000064000000800000000500000004000000040000001000000009000000030000000000000000000000e4000000220000000000000000000000010000000000000011000000030000000000000000000001060000002c00000000000000000000000100000000", @@ -295,7 +318,7 @@ { "label": "fm_qzc", "moduleId": "synth", - "version": "0.1.2-experimental", + "version": "0.1.5-experimental", "key": "SYNTH MACHINE", "author": "timhastie", "nativeAuthor": "timhastie", @@ -305,7 +328,7 @@ "source": "modules/synth/upstream/quantizer/core.s", "sources": { "modules/synth/upstream/quantizer/core.s": "2992b6c85cff440de03ae84eb22dee8b8a83a84f23e94b699404d28940b6f958", - "modules/synth/manifest.py": "7854330aca001d2841de2fb384f0ca8adb7497db49dacc56da2815c1f444c0e6" + "modules/synth/manifest.py": "f364cd97b5a2a416b3029752fe2ce0691fc977d59203419d9d5a52bbeb7913b7" }, "bytes": 976, "code": "7f454c4601020100000000000000000000010004000000010000000000000000000002b80000806500340000000000280007000671b9100b14ec0c800000001863064239100b14ec71b9100b14ed0c800000007f63064239100b14ed71b9100b14ee0c800000000b63064239100b14ee4a39100b14ae4e754239100b14ec4239100b14ed4239100b14ee42b9100b14d44e7571b9100b14ec672641fa0026d08071f008fe2f0173b9100b14ee6710e3a82200e089e8898081028000000fff221f4e750ab506ad05ab0ad506b505ad056b04a902950aad09ad055504e905b30b6d06db09cd018d04a30463018b09b3055b06d500000000000000000000000000000000000000000000000000000000000003000001000000000000000000000000030000020000000000000000000000000300000300000001100b14ec000000000000fff10000000a100b14ed000000000000fff100000013100b14ee000000000000fff10000001b00000014000000000000000100000026000000280000000000000001000000300000003c00000000000000010000003a0000008c0000000000000001000000440000008e00000000000000010000004d0000008a000000000000000100000057000000c00000000000000001000000630000000000000000100000010000006b000000440000000010000001000000770000005e0000000010000001004e565f5343414c45004e565f474c494445004e565f524f4f5400717a5f625f676c69646500717a5f625f726f6f7400717a5f625f6261636b00717a5f736d5f72657400717a5f6d61736b7300717a5f736d5f706f7000717a5f636f72655f656e6400717a5f626f6f7400717a5f64656661756c747300717a5f7363616c655f6d61736b00002e73796d746162002e737472746162002e7368737472746162002e74657874002e64617461002e62737300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b00000001000000060000000000000034000000c00000000000000000000000040000000000000021000000010000000300000000000000f4000000000000000000000000000000040000000000000027000000080000000300000000000000f4000000000000000000000000000000040000000000000001000000020000000000000000000000f400000110000000050000000e00000004000000100000000900000003000000000000000000000204000000850000000000000000000000010000000000000011000000030000000000000000000002890000002c00000000000000000000000100000000", @@ -318,7 +341,7 @@ { "label": "fm_qzs", "moduleId": "synth", - "version": "0.1.2-experimental", + "version": "0.1.5-experimental", "key": "SYNTH MACHINE", "author": "timhastie", "nativeAuthor": "timhastie", @@ -328,7 +351,7 @@ "source": "modules/synth/upstream/quantizer/scale.s", "sources": { "modules/synth/upstream/quantizer/scale.s": "82242c407847ac61a6f60fe15bdc5b0b4fc852bac82c39745b5c838ec9389c64", - "modules/synth/manifest.py": "7854330aca001d2841de2fb384f0ca8adb7497db49dacc56da2815c1f444c0e6" + "modules/synth/manifest.py": "f364cd97b5a2a416b3029752fe2ce0691fc977d59203419d9d5a52bbeb7913b7" }, "bytes": 568, "code": "7f454c4601020100000000000000000000010004000000010000000000000000000000f8000080650034000000000028000800074ef900000000000000000000000000000000000000000000000000000000000000000000030000010000000000000000000000000300000300000000000000000000000003000004000000010000000000000000100000010000000d00000000000000001000000000717a5f7363616c655f617400717a5f7363616c655f6d61736b0000000000020000050100000000002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e6273730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000100000006000000000000003400000006000000000000000000000004000000000000001b000000040000004000000000000000b80000000c0000000500000001000000040000000c000000260000000100000003000000000000003c00000000000000000000000000000004000000000000002c0000000800000003000000000000003c0000000000000000000000000000000400000000000000010000000200000000000000000000003c0000006000000006000000040000000400000010000000090000000300000000000000000000009c0000001b0000000000000000000000010000000000000011000000030000000000000000000000c40000003100000000000000000000000100000000", @@ -341,7 +364,7 @@ { "label": "fm_qz", "moduleId": "synth", - "version": "0.1.2-experimental", + "version": "0.1.5-experimental", "key": "SYNTH MACHINE", "author": "timhastie", "nativeAuthor": "timhastie", @@ -350,12 +373,12 @@ "caveAddress": null, "source": "modules/synth/upstream/quantizer/quantizer.s", "sources": { - "modules/synth/upstream/quantizer/quantizer.s": "d1737168a1750f540b3dfbefdd2a64b11084013b9f7b71b31aa4f189d33a24c5", - "modules/synth/manifest.py": "7854330aca001d2841de2fb384f0ca8adb7497db49dacc56da2815c1f444c0e6" + "modules/synth/upstream/quantizer/quantizer.s": "bdf7fe4815d0a742ceb77a4688db0eeb79f6c2d472fcc4f9bfb869a77db9ea8c", + "modules/synth/manifest.py": "f364cd97b5a2a416b3029752fe2ce0691fc977d59203419d9d5a52bbeb7913b7" }, - "bytes": 10216, - "code": "7f454c4601020100000000000000000000010004000000010000000000000000000026a80000002600340000000000280008000771b9100b14ed4e75614a67462f082f01207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2202e7892001d281d28071b0180255800c80000000026304700060027001221f205f4a804e754feffff048d7030a207946c8245671b9100b14cf223c000018b24c010800d1c02248d3fc0008eda271b1280053806600008a22482002c0fc001ed3c0d3fc0008edbc71d10c40464d660e71a900020c0000016604700160642002e588d082d1c0d1fc0008f04b7190727fb081624c223c000004484c0100000680100b14f020402248263c000000ff7198670e0c800000002f66022248538366ee303c464db0516602548941fa09b9760571987399b0816608538366f47001600270004cd7030a4fef00104a804e752f0275b9100b14cc6100ff2e241f4a804e7541f9100b14ec719067224a85661e4ab9460d16846616222b00160c8150544348660a220c670675866100009c14821a82320e4e752f0a45f9100b14ec719267784a8566744ab9460d1684666c222b00160c81505443486660220e675c2f0275b098590c82000000ff66482f032f04247946c8245675b9100b14cf263c000018b24c032800d5c2d5fc0008eda279b9100b14cc75b248002602e78b96829682508ad5c32604eb8b9684968475b23800281f261f61122802241f245f1184985973b9100b14cc4e752f002f012f032f042f052f062f072f082f094eb9400d2ca82600282b006a2a2b009ada84538541fa0b376100fefc43fa08a5128020016a0244804a816b04611c60026132538066f2225f205f2e1f2c1f2a1f281f261f221f201f4e752c025286bc856e10612a0c870000000c64f00f0367ec24064e752c025386bc846df661100c870000000c64f00f0367ec24064e752e069e844a3a083f671e2f002f060687000000382c07700c4c4060064c0060009e862c1f201f4e757fb078004e756100fda06600008c41f9100b14ec719067742f022f032f044eb9400d2ca8260041fa0a66240a73b9100b14eed4817806b2846f124ab9460d16fc670a0482000000186a0274007218b4816f0224017800220292846b0871b01800010366202202d2840c8100000018620871b018000103660c52840c840000001863d422022441281f261f241f45f2ac0471b9100b14cf4e752f032f04220a04810000000c7139400d2cdc760c4c030800d28041f9100b14ec4a10676271b9100b14eed28020010680000000782800760c4c4340044c034000908422404eb9400d2ca826007800200990846a0606800000000c010366222009d0840c800000000c6d0604800000000c0103661052840c84000000066fd0600692846002d2840681000000400c810000007f6f02727f4a816c0272002441281f261f71b9100b14cf4e756100fd2c6704700060062039460d16fc22004ef94005025c6100fd14673c41f9400d2cdc7110242f00080c8200000034670e52800c80000000046f107004600c53800c80fffffffc6c0270fc108042b9460d16fc4ef9400459207401b5b9460d16fc4ef9400459206100fcc4670642b9460d16fc7201b2b9460d16fc4ef9400449704ab980000012666a6100fca267087139400d2cdc601e2039460d16fc671641f9100b14ec4a10670c73b9100b14ee5f816b0270ff2f0041f9100b14ec4a10673441fa060e71b9100b14ee43fa083022710c007399670410c160f810fc002010fc002510fc00644210487a05e64ef9400449c62f004879400b465d4ef9400449c64ab946c7dd2666204a83673071398000004c08000000672461286712538066087047d0816100034c4ef94004fc446100fb34670820015380610001b24ef94004fc062079400d2cd82008670c2079400d64382068ffe84ed04e7541fa057c421042a800044ab946c7dd2667066100021e606061ce676253806738280041f9400d2cb82003538022302c0001c121812c0001814a81671a558466162079400d64382068ffe420034e9041fa052e10836022602641f9460d171d4a302800671a610001cc41f9460d171d1183280041fa050a10834ef94004fc9c41f9400d2cb0118328004ef94004fcb24ab946c7dd26663041fa04e64a1066286100fa6a671c2079400d2cd8200867122079400d64382068fff470ff4e904a80660c4ef94004fcf84ef94004fce84ef94004fd3e508f4a806b562f006100fa2e67462f082f092f012f042279400d2cd82009672c616a202f00101140000113c000000000200353801080117c000100022151000443fa046e2011670421400004281f221f225f205f201f4ef94004fd0c4ef94004fd3e4fef00146100f9d0671a2079400d2cd8200867102079400d64382068fff471ba04324e904ef94004fd3e41fa042a2202eb89d1c12f0872034a2800026704b0106718508853816af0205772034a2800026708508853816af42057588f4e754fefffec48d7031a2279400d2cd822096700008441fa03e22202eb89d1c12f092279400d64382269ffec4e91225f72034a2800026704b0106708508853816af0605442280002201190a80004ef8822290004660272064c4100002279400d64382269fff8720077f11a00b680640a52810c810000007e6fee71a80001487946c7e9562f002f014878000d2f024eb9400421584fef00144cd7031a4fef00144e754feffff448d7030241f9460d171d71b02800672a538041fa03402202eb89d1c172034a2800026704b0106708508853816af0600a2028000443fa031622804cd703024fef000c4e754feffff848d7010241fa03062202eb89d1c172034a280002670e2f012f0871906100fef6205f221f508853816ae64cd701024fef00084e7573b02800200a6100f850673c6100f7fe660cb28a5381b28a662e61ac602a200a6100febe41f9400d2cb820302c00280a09006714098021802c00661e41f9460d171d52841184280041f9460d171d73b02800200a4ef94004fbe47048d08a61064ef94004fd6842a72f002f024eb94003f3a84fef000c4e752439460e43d8e58a4fef00204ef940065bd041f9100b14ec719041fa034420300c004e756100f7664a80660841fa039620084e752f004879400b465d487a02064eb940013a084fef000c41fa01f820084e7571b9100b14ee41fa041e20300c004e7541f9100b14ec7218601241f9100b14ee720b600841f9100b14ed727f7190d0af00044aaf0008670cb0816e124a806c102001600cb0816f0220014a806c02700010804e752c2f05c0202f05c4671841f9100b14ec421041f9100b14ed421041f9100b14ee42104ef9400866d4122f048f71017a23ba806622204343fa00e26120672a204343fa010061166734204343fa0116610c673e7a2371014ef9400867aa719967067b98ba8067f64e7561460c80000000186302700041f9100b14ec602661320c800000007f6302700041f9100b14ed6012611e0c800000000b6302700041f9100b14ee4aaf003a660210804ef94008822470007a0a73980481000000300c810000000962084c050000d08160e84e7541f9100b14ec719041fa0054612471b9100b14ee41fa008f61186100f5fc41fa0062610e73398000004f2f014ef9400888b22f002f082f024e942f024e932f002f022f034e924fef001c4e752353455155454e4345525f5343414c453d002353455155454e4345525f5343414c453d25640d0a002353594e54485f474c4944453d002353594e54485f474c4944453d25640d0a002353455155454e4345525f524f4f543d002353455155454e4345525f524f4f543d25640d0a005343414c4500524f4f5400474c4944450053594e54480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f4646004d414a4f5200444f5249414e0050485259474e004c594449414e004d49584f4c5944004d494e4f52004c4f435249414e0050454e542e4d4e0050454e542e4d4a004d454c2e4d494e0048524d2e4d494e0057484f4c4500424c5545530050485259444f4d0057482e44494d0048572e44494d0048554e474d494e00484952414a4f5300494e2d53454e00495741544f0050454c4f470044424c4841524d00535550524c4f43004c59442e444f4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000430043230044004423004500460046230047004723004100412300420000000102030405060708090a0b000102030405060708090a0b0000ffffffff01ffffffff02ffffffff03ffffffff04ffffffff05ffffffff06ffffffff07ffffffff08ffffffff09ffffffff0affffffff0bffffffff00ffffffff01ffffffff02ffffffff03ffffffff04ffffffff05ffffffff06ffffffff07ffffffff08ffffffff09ffffffff0affffffff0bffffffff000000000000000000000000000000000000000000000000000000000003000001000000000000000000000000030000030000000000000000000000000300000400000001460d16fc000000000000fff10000000a400d2cb0000000000000fff100000012400d2cb0000000000000fff10000001a400d2cb8000000000000fff100000023400d2cd8000000000000fff10000002c400d2cdc000000000000fff100000033400d2ca8000000000000fff10000003c100b14cc000000000000fff10000004540042158000000000000fff10000005046c7e956000000000000fff1000000580000000d000000000000fff100000062400d6438000000000000fff100000071460d171d000000000000fff10000006c46c7dd26000000000000fff1000000764003f3a8000000000000fff10000008040013a08000000000000fff100000088400b465d000000000000fff10000008e100b14ec000000000000fff100000097100b14ed000000000000fff1000000a0100b14ee000000000000fff1000000a8100b14ed000000000000fff1000000b1100b14ee000000000000fff1000000b9000000000000000000000001000000c5000000080000000000000001000000d2000000540000000000000001000000de000000520000000000000001000000e80000004a0000000000000001000000f20000004c0000000000000001000000fc0000010e0000000000000001000001050000011000000000000000010000010f000000dc00000000000000010000011a000000ee000000000000000100000128000000ea000000000000000100000134000000f800000000000000010000013d00000ab300000000000000010000014a000000fe0000000000000001000001540000011c0000000000000001000001600000015a00000000000000010000016b000001f4000000000000000100000174000001e600000000000000010000017e000001e000000000000000010000018600000d5300000000000000010000018f00000ac90000000000000001000001990000022e0000000000000001000001a3000002360000000000000001000001ad000002500000000000000001000001b5000002380000000000000001000001bf0000026a0000000000000001000001c7000002520000000000000001000001d0000002680000000000000001000001d9000002840000000000000001000001e10000026c0000000000000001000001ea000002ac0000000000000001000001f4000003440000000000000001000001ff0000033800000000000000010000020b00000d3a000000000000000100000213000002f800000000000000010000021c00000300000000000000000100000229000003020000000000000001000002330000031000000000000000010000023b0000033000000000000000010000024600000324000000000000000100000250000003ca00000000000000010000025a000003920000000000000001000002650000039e00000000000000010000026e000003c4000000000000000100000279000003b4000000000000000100000282000003c800000000000000010000028b000003da000000000000000100000297000003e00000000000000001000002a3000003f80000000000000001000002af000003fe0000000000000001000002b9000004480000000000000001000002c50000042e0000000000000001000002d00000043a0000000000000001000002dc000004620000000000000001000002e6000004e20000000000000001000002f500000486000000000000000100000301000004a400000000000000010000030b000004e400000000000000010000031500000ac000000000000000010000031d00000cec00000000000000010000032a000004c2000000000000000100000335000004ca0000000000000001000003420000051800000000000000010000034d0000052c000000000000000100000359000005320000000000000001000003640000051e00000000000000010000036e00000862000000000000000100000379000006dc0000000000000001000003840000054800000000000000010000038e00000ac800000000000000010000039800000acc0000000000000001000003a1000005620000000000000001000003ae0000077c0000000000000001000003ba000005c20000000000000001000003c9000005c80000000000000001000003d4000005a20000000000000001000003df000005a00000000000000001000003ea000006100000000000000001000003f90000060a000000000000000100000406000006160000000000000001000004130000067800000000000000010000041e0000067000000000000000010000042800000668000000000000000100000432000006a800000000000000010000043f00000ad0000000000000000100000447000006a200000000000000010000045100000ad400000000000000010000045a000006b6000000000000000100000464000006c000000000000000010000046f000006d800000000000000010000047a000006ca0000000000000001000004850000077200000000000000010000048f0000070c00000000000000010000049a000007160000000000000001000004a50000071e0000000000000001000004b1000007320000000000000001000004bb000007420000000000000001000004c5000007540000000000000001000004d1000007ba0000000000000001000004db0000079e0000000000000001000004e6000007a80000000000000001000004f1000007b00000000000000001000004fd000007c4000000000000000100000508000007d8000000000000000100000513000007ec00000000000000010000051e0000084400000000000000010000052a0000081a0000000000000001000005350000085600000000000000010000054000000bd4000000000000000100000549000008a800000000000000010000055300000c3800000000000000010000055900000ab8000000000000000100000561000008f200000000000000010000056c0000090a000000000000000100000577000009140000000000000001000005810000091600000000000000010000058c000009100000000000000001000005950000093c00000000000000010000059f000009700000000000000001000005a900000a340000000000000001000005b0000009760000000000000001000005b9000009820000000000000001000005c400000a5c0000000000000001000005cc000009960000000000000001000005d700000a7c0000000000000001000005df000009aa0000000000000001000005e9000009800000000000000001000005f4000009ca0000000000000001000005fd0000098e000000000000000100000605000009bc000000000000000100000610000009a2000000000000000100000619000009b6000000000000000100000622000009c400000000000000010000062c000009ce000000000000000100000635000009e600000000000000010000064000000a4600000000000000010000064700000a1a00000000000000010000065200000a8d00000000000000010000065a00000a6a00000000000000010000066200000c3c00000000000000010000066800000c4200000000000000010000066e00000c4900000000000000010000067400000c5000000000000000010000067a00000c5700000000000000010000068000000c5f00000000000000010000068600000c6500000000000000010000068c00000c6d00000000000000010000069200000c7500000000000000010000069800000c7d00000000000000010000069f00000c850000000000000001000006a600000c8d0000000000000001000006ad00000c930000000000000001000006b400000c990000000000000001000006bb00000ca10000000000000001000006c200000ca80000000000000001000006c900000caf0000000000000001000006d000000cb70000000000000001000006d700000cbf0000000000000001000006de00000cc60000000000000001000006e500000ccc0000000000000001000006ec00000cd20000000000000001000006f300000cda0000000000000001000006fa00000ce200000000000000010000070100000d1c00000000000000010000070700000d1e00000000000000010000070d00000d2100000000000000010000071300000d2300000000000000010000071900000d2600000000000000010000071f00000d2800000000000000010000072500000d2a00000000000000010000072b00000d2d00000000000000010000073100000d2f00000000000000010000073700000d3200000000000000010000073d00000d3400000000000000010000074400000d3700000000000000010000074b00000dcc0000000000000001000007520000012e00000000100000010000075a00000162000000001000000100000763000002b200000000100000010000076c000008740000000010000001000007740000091a0000000010000001000007800000094200000000100000010000078b000009e800000000100000010000079100000886000000001000000100000798000008d600000000100000010000079f00000aa20000000010000001000007ac100b14ec000000001000fff1000007b5000008980000000010000001000007c2000008ea0000000010000001000007cf00000aad0000000010000001000007dc000004f00000000010000001000007e40000054a0000000010000001000007ec000007fc0000000010000001000007f4000005d80000000010000001000007fc0000061c0000000010000001000008040000067e00000000100000010000080c000008c6000000001000000100000818000008e000000000100000010000082400000aa8000000001000000100000830000003ee00000000100000010000083a000004060000000010000001000008440000045600000000100000010000084f000004700000000010000001004f43545f574f5244004b4559535f415400717a5f706b657900717a5f706d61736b00717a5f636c6f636b00717a5f6f6374005343414c455f41540055495f545241434b004c4f434b5f5752495445005245435f43545800464c41545f484f4c44004b494e445f464c45580046554e435f48454c44004d4944495f4e4f544500535052494e544600464d545f44004e565f5343414c45004e565f474c494445004e565f524f4f5400717a5f676c69646500717a5f726f6f7400717a5f676c6964655f6f6600717a5f706f6c79747261636b00717a5f69735f73796e746800717a5f70745f72657400717a5f70745f79657300717a5f70745f6f757400717a5f69735f6e6f00717a5f69735f6f757400717a5f69735f7363616e00717a5f69735f7363616e6e656400717a5f69735f7363616e3100717a5f69735f666d00717a5f73796e74686e616d6500717a5f69735f636d7000717a5f75695f73796e746800717a5f6b5f73746f726500717a5f7175616e7400717a5f705f646f6e6500717a5f705f676f00717a5f706372617700717a5f73796e74687100717a5f6b5f6c6f6f7000717a5f6b5f646f776e00717a5f6b5f757000717a5f6b5f6e65787400717a5f6b5f646e00717a5f6b5f75703100717a5f6b5f72657400717a5f70636f6600717a5f6b5f646e3100717a5f70635f74616200717a5f635f73796e746800717a5f635f7265706c617900717a5f7063323500717a5f635f746f7000717a5f635f696e72616e676500717a5f635f6c6f6f7000717a5f635f686900717a5f635f666f756e6400717a5f635f6d6f726500717a5f63735f72617700717a5f63735f6c6f6f7000717a5f63735f6c6f00717a5f63735f646f776e00717a5f63735f686900717a5f63735f757000717a5f63735f68695f6f6b00717a5f63735f6c6f5f6f6b00717a5f6b695f73746f636b00717a5f6b695f6f757400717a5f6f6b5f73746f636b00717a5f6f6b5f646f776e00717a5f6f6b5f73746f726500717a5f6f645f636d7000717a5f6f6e5f73746f636b666d7400717a5f6f6e5f73746f636b00717a5f6f6e5f6f757400717a5f6f6e5f666d7400717a5f6f666d7400717a5f726f6f746e616d657300717a5f6f6e5f636f707900717a5f6f6e5f636f7069656400717a5f67315f736b697000717a5f67315f73746f636b00717a5f6c65676d6f646500717a5f67315f656e6400717a5f6e6f74656f666600717a5f686f6c6472656c00717a5f6c6d5f72657400717a5f6c656761746f00717a5f636861696e00717a5f67325f6e6f66756e6300717a5f636861696e5f6f6600717a5f67325f747269676c65737300717a5f67325f7472696700717a5f67325f6d6f6e6f00717a5f67325f7061726100717a5f67335f747269676c65737300717a5f67335f73616d706c6500717a5f67335f6a6f696e656400717a5f67345f6e6f6e6500717a5f67345f6f757400717a5f67345f72657300717a5f736c6f745f66696e6400717a5f7374657000717a5f67355f6f757400717a5f707265737300717a5f73665f6b657900717a5f73665f6b65793100717a5f73665f74616b6500717a5f73665f6672656500717a5f68725f6f757400717a5f68725f66696e6400717a5f68725f6e65787400717a5f68725f666f756e6400717a5f68725f74707300717a5f68725f72617700717a5f68725f777269746500717a5f636f5f6f757400717a5f636f5f66696e6400717a5f636f5f6e65787400717a5f636f5f666f756e6400717a5f686f6c64616c6c00717a5f68615f6c6f6f7000717a5f68615f6e65787400717a5f67305f73746f636b00717a5f67305f7061726100717a5f67305f6d6f726500717a5f6e616d657300717a5f67675f6e756d00717a5f6e3000717a5f6762756600717a5f7365745f616e7900717a5f735f636c616d7000717a5f735f7a65726f00717a5f735f73746f726500717a5f735f6c6f7700717a5f655f6261636b00717a5f6c5f6261636b00717a5f6b657900717a5f6c5f636d7000717a5f6c5f7363616c6500717a5f6b65793200717a5f6c5f676c69646500717a5f6b65793300717a5f6c5f726f6f7400717a5f6c5f635f72657400717a5f6c5f64656300717a5f6c5f6f6b00717a5f6c5f73746f726500717a5f6c5f676f6b00717a5f6c5f726f6b00717a5f6c5f6e65787400717a5f6c5f64696700717a5f6c5f645f72657400717a5f666d7400717a5f77725f6c696e6500717a5f666d743300717a5f666d743200717a5f6e3100717a5f6e3200717a5f6e3300717a5f6e3400717a5f6e3500717a5f6e3600717a5f6e3700717a5f6e3800717a5f6e3900717a5f6e313000717a5f6e313100717a5f6e313200717a5f6e313300717a5f6e313400717a5f6e313500717a5f6e313600717a5f6e313700717a5f6e313800717a5f6e313900717a5f6e323000717a5f6e323100717a5f6e323200717a5f6e323300717a5f6e323400717a5f723000717a5f723100717a5f723200717a5f723300717a5f723400717a5f723500717a5f723600717a5f723700717a5f723800717a5f723900717a5f72313000717a5f72313100717a5f656e6400717a5f6b6e6f6200717a5f706c6f636b00717a5f6368726f6d00717a5f6472617700717a5f6c645f656e74727900717a5f6c645f6c696e6500717a5f777200717a5f67657400717a5f73657400717a5f6c626c5f7363616c6500717a5f7363616c6500717a5f6765745f676c69646500717a5f7365745f676c69646500717a5f6c626c5f676c69646500717a5f6c65673100717a5f6c65673200717a5f6c65673000717a5f6c65673300717a5f6c65673400717a5f6c65673500717a5f6765745f726f6f7400717a5f7365745f726f6f7400717a5f6c626c5f726f6f7400717a5f6b657969647800717a5f6f63746b657900717a5f6f63746472617700717a5f6f63746e756d00000000000006480000010100000ad000000bd40000010100000c3800000bd80000010100000c3c00000bdc0000010100000c4200000be00000010100000c4900000be40000010100000c5000000be80000010100000c5700000bec0000010100000c5f00000bf00000010100000c6500000bf40000010100000c6d00000bf80000010100000c7500000bfc0000010100000c7d00000c000000010100000c8500000c040000010100000c8d00000c080000010100000c9300000c0c0000010100000c9900000c100000010100000ca100000c140000010100000ca800000c180000010100000caf00000c1c0000010100000cb700000c200000010100000cbf00000c240000010100000cc600000c280000010100000ccc00000c2c0000010100000cd200000c300000010100000cda00000c340000010100000ce200000cec0000010100000d1c00000cf00000010100000d1e00000cf40000010100000d2100000cf80000010100000d2300000cfc0000010100000d2600000d000000010100000d2800000d040000010100000d2a00000d080000010100000d2d00000d0c0000010100000d2f00000d100000010100000d3200000d140000010100000d3400000d180000010100000d37002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e6273730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000100000006000000000000003400000dcc000000000000000000000004000000000000001b000000040000004000000000000024ac000001c80000000500000001000000040000000c0000002600000001000000030000000000000e0000000000000000000000000000000004000000000000002c00000008000000030000000000000e0000000000000000000000000000000004000000000000000100000002000000000000000000000e0000000e5000000006000000ca00000004000000100000000900000003000000000000000000001c50000008590000000000000000000000010000000000000011000000030000000000000000000026740000003100000000000000000000000100000000", - "sha256": "d11d49b2e5abb6b0120b1200b0289c2c41e0aa2e84df1fe791a6e46b0bd6381c", + "bytes": 11256, + "code": "7f454c460102010000000000000000000001000400000001000000000000000000002ab80000002600340000000000280008000771b9100b14ed4e75614a67462f082f01207946c8245671b9100b14cf223c000018b24c010800d1c0d1fc0008ee9a2202e7892001d281d28071b0180255800c80000000026304700060027001221f205f4a804e754feffff048d7030a207946c8245671b9100b14cf223c000018b24c010800d1c02248d3fc0008eda271b1280053806600008a22482002c0fc001ed3c0d3fc0008edbc71d10c800000464d660c71a9000253806604700160642002e588d082d1c0d1fc0008f04b7190727fb081624c223c000004484c0100000680100b14f020402248263c000000ff7198670e0c800000002f66022248538366ee303c464db0516602548941fa0b21760571987399b0816608538366f47001600270004cd7030a4fef00104a804e752f0275b9100b14cc6100ff2e241f4a804e7541f9100b14ec719067224a85661e4ab9460d16846616222b00160c8150544348660a220c670675866100015014821a82320e4e752f0a45f9100b14ec7192677a4a8566764ab9460d1684666e222b00160c81505443486662220e675e2f0275b098590c82000000ff66482f032f04247946c8245675b9100b14cf263c000018b24c032800d5c2d5fc0008eda279b9100b14cc75b248002602e78b96829682508ad5c32604eb8b9684968475b23800281f261f610000c62802241f245f1184985973b9100b14cc4e752400b2806c0224014a39100b14ec672a4ab98000001267224a06661e4a85661a2f01222b00160c814e4f5445660a220c6706759561000082221f4e7571b9100b14ec67624ab980000012675a200966564a856652202b00160c804e4f54456646220e67422f0275a849000c82000000ff662e2f08203c000018b275b9100b14cf4c002800207946c82456d1c275b9100b14cceb8ad1c2d1fc0008f1627590205f61162802241f1144490073b9100b14cc4e752f00700060042f0070ff2f012f032f042f052f062f072f082f092c004eb9400d2ca82600282b006a2a2b009ada84538541fa0be1200666046100fe3a43fa094b128020016a0244804a816b04611c60026132538066f2225f205f2e1f2c1f2a1f281f261f221f201f4e752c025286bc856e10612a0c870000000c64f00f0367ec24064e752c025386bc846df661100c870000000c64f00f0367ec24064e752e069e844a3a08e567246a042e0660060687000000382f002f062c07700c4c4060064c0060009e862c1f201f4e757fb078004e756100fcd86600008c41f9100b14ec719067742f022f032f044eb9400d2ca8260041fa0b06240a73b9100b14eed4817806b2846f124ab9460d16fc670a0482000000186a0274007218b4816f0224017800220292846b0871b01800010366202202d2840c8100000018620871b018000103660c52840c840000001863d422022441281f261f241f45f2ac0471b9100b14cf4e752f032f04220a04810000000c7139400d2cdc760c4c030800d28041f9100b14ec4a10676271b9100b14eed28020010680000000782800760c4c4340044c034000908422404eb9400d2ca826007800200990846a0606800000000c010366222009d0840c800000000c6d0604800000000c0103661052840c84000000066fd0600692846002d2840681000000400c810000007f6f02727f4a816c0272002441281f261f71b9100b14cf4e756100fc646704700060062039460d16fc22004ef94005025c6100fc4c673c41f9400d2cdc7110242f00080c8200000034670e52800c80000000046f107004600c53800c80fffffffc6c0270fc108042b9460d16fc4ef9400459207401b5b9460d16fc4ef9400459206100fbfc670642b9460d16fc7201b2b9460d16fc4ef9400449704ab980000012662c6100fbda67087139400d2cdc601e2039460d16fc671641f9100b14ec4a10670c73b9100b14ee5f816b0270ff2f0041f9100b14ec4a10673241fa06ae71b9100b14ee43fa08d022710c007399670410c160f810fc002010fc002510fc00644210487a06864ef9400449c64879400b465d4ef9400449c671b9100b14ec671471b9100b14eed083727fb0816f022001611226004878000173b9100b14cf4ef9400436b24feffff448d7001c24004eb9400d2ca826002202780c4c4410014c0410002002908122407800220292846b10200990846a0606800000000c0103662e2202d2840c810000007f6e162009d0840c800000000c6d0604800000000c0103660c52840c840000000b6fbe220220014cd7001c4fef000c4e754ab946c7dd2666204a83673071398000004c08000000672461286712538066087047d0816100034c4ef94004fc446100f9cc670820015380610001b24ef94004fc062079400d2cd82008670c2079400d64382068ffe84ed04e7541fa057c421042a800044ab946c7dd2667066100021e606061ce676253806738280041f9400d2cb82003538022302c0001c121812c0001814a81671a558466162079400d64382068ffe420034e9041fa052e10836022602641f9460d171d4a302800671a610001cc41f9460d171d1183280041fa050a10834ef94004fc9c41f9400d2cb0118328004ef94004fcb24ab946c7dd26663041fa04e64a1066286100f902671c2079400d2cd8200867122079400d64382068fff470ff4e904a80660c4ef94004fcf84ef94004fce84ef94004fd3e508f4a806b562f006100f8c667462f082f092f012f042279400d2cd82009672c616a202f00101140000113c000000000200353801080117c000100022151000443fa046e2011670421400004281f221f225f205f201f4ef94004fd0c4ef94004fd3e4fef00146100f868671a2079400d2cd8200867102079400d64382068fff471ba04324e904ef94004fd3e41fa042a2202eb89d1c12f0872034a2800026704b0106718508853816af0205772034a2800026708508853816af42057588f4e754fefffec48d7031a2279400d2cd822096700008441fa03e22202eb89d1c12f092279400d64382269ffec4e91225f72034a2800026704b0106708508853816af0605442280002201190a80004ef8822290004660272064c4100002279400d64382269fff8720077f11a00b680640a52810c810000007e6fee71a80001487946c7e9562f002f014878000d2f024eb9400421584fef00144cd7031a4fef00144e754feffff448d7030241f9460d171d71b02800672a538041fa03402202eb89d1c172034a2800026704b0106708508853816af0600a2028000443fa031622804cd703024fef000c4e754feffff848d7010241fa03062202eb89d1c172034a280002670e2f012f0871906100fef6205f221f508853816ae64cd701024fef00084e7573b02800200a6100f6e8673c6100f696660cb28a5381b28a662e61ac602a200a6100febe41f9400d2cb820302c00280a09006714098021802c00661e41f9460d171d52841184280041f9460d171d73b02800200a4ef94004fbe47048d08a61064ef94004fd6842a72f002f024eb94003f3a84fef000c4e752439460e43d8e58a4fef00204ef940065bd041f9100b14ec719041fa034420300c004e756100f5fe4a80660841fa039620084e752f004879400b465d487a02064eb940013a084fef000c41fa01f820084e7571b9100b14ee41fa041e20300c004e7541f9100b14ec7218601241f9100b14ee720b600841f9100b14ed727f7190d0af00044aaf0008670cb0816e124a806c102001600cb0816f0220014a806c02700010804e752c2f05c0202f05c4671841f9100b14ec421041f9100b14ed421041f9100b14ee42104ef9400866d4122f048f71017a23ba806622204343fa00e26120672a204343fa010061166734204343fa0116610c673e7a2371014ef9400867aa719967067b98ba8067f64e7561460c80000000186302700041f9100b14ec602661320c800000007f6302700041f9100b14ed6012611e0c800000000b6302700041f9100b14ee4aaf003a660210804ef94008822470007a0a73980481000000300c810000000962084c050000d08160e84e7541f9100b14ec719041fa0054612471b9100b14ee41fa008f61186100f49441fa0062610e73398000004f2f014ef9400888b22f002f082f024e942f024e932f002f022f034e924fef001c4e752353455155454e4345525f5343414c453d002353455155454e4345525f5343414c453d25640d0a002353594e54485f474c4944453d002353594e54485f474c4944453d25640d0a002353455155454e4345525f524f4f543d002353455155454e4345525f524f4f543d25640d0a005343414c4500524f4f5400474c4944450053594e54480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f4646004d414a4f5200444f5249414e0050485259474e004c594449414e004d49584f4c5944004d494e4f52004c4f435249414e0050454e542e4d4e0050454e542e4d4a004d454c2e4d494e0048524d2e4d494e0057484f4c4500424c5545530050485259444f4d0057482e44494d0048572e44494d0048554e474d494e00484952414a4f5300494e2d53454e00495741544f0050454c4f470044424c4841524d00535550524c4f43004c59442e444f4d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000430043230044004423004500460046230047004723004100412300420000000102030405060708090a0b000102030405060708090a0b0000ffffffff01ffffffff02ffffffff03ffffffff04ffffffff05ffffffff06ffffffff07ffffffff08ffffffff09ffffffff0affffffff0bffffffff00ffffffff01ffffffff02ffffffff03ffffffff04ffffffff05ffffffff06ffffffff07ffffffff08ffffffff09ffffffff0affffffff0bffffffff00000000000000000000000000000000000000000000000000000000000300000100000000000000000000000003000003000000000000000000000000030000040000000100000000000000000000fff10000000d460d16fc000000000000fff100000016400d2cb0000000000000fff10000001e400d2cb0000000000000fff100000026400d2cb8000000000000fff10000002f400d2cd8000000000000fff100000038400d2cdc000000000000fff10000003f400d2ca8000000000000fff100000048100b14cc000000000000fff10000005140042158000000000000fff10000005c46c7e956000000000000fff1000000640000000d000000000000fff10000006e400d6438000000000000fff10000007d460d171d000000000000fff10000007846c7dd26000000000000fff1000000824003f3a8000000000000fff10000008c40013a08000000000000fff100000094400b465d000000000000fff10000009a80000012000000000000fff1000000a446c82456000000000000fff1000000aa0008f162000000000000fff1000000b5100b14ec000000000000fff1000000be100b14ed000000000000fff1000000c7100b14ee000000000000fff1000000cf100b14ed000000000000fff1000000d8100b14ee000000000000fff1000000e0000000000000000000000001000000ec000000080000000000000001000000f9000000540000000000000001000001050000005200000000000000010000010f0000004a0000000000000001000001190000004c0000000000000001000001230000010e00000000000000010000012c000000ac00000000000000010000013b00000110000000000000000100000145000000dc000000000000000100000150000000ee00000000000000010000015e000000ea00000000000000010000016a000000f800000000000000010000017300000c1b000000000000000100000180000000fe00000000000000010000018a0000011c0000000000000001000001960000015a0000000000000001000001a1000002a80000000000000001000001aa000001e80000000000000001000001b4000001e00000000000000001000001bc000001fe0000000000000001000001c9000002300000000000000001000001d40000022e0000000000000001000001de000002ae0000000000000001000001e80000029c0000000000000001000001f4000002960000000000000001000001fd000002b200000000000000010000020500000ebb00000000000000010000020e000002e400000000000000010000021800000c31000000000000000100000222000002f000000000000000010000022c000002f80000000000000001000002360000031200000000000000010000023e000002fa0000000000000001000002480000032c000000000000000100000250000003140000000000000001000002590000032a0000000000000001000002620000034600000000000000010000026a0000032e0000000000000001000002730000037400000000000000010000027d000003560000000000000001000002870000035c0000000000000001000002910000040c00000000000000010000029c000004000000000000000001000002a800000ea20000000000000001000002b0000003c00000000000000001000002b9000003c80000000000000001000002c6000003ca0000000000000001000002d0000003d80000000000000001000002d8000003f80000000000000001000002e3000003ec0000000000000001000002ed000004920000000000000001000002f70000045a0000000000000001000003020000046600000000000000010000030b0000048c0000000000000001000003160000047c00000000000000010000031f00000490000000000000000100000328000004a2000000000000000100000334000004a8000000000000000100000340000004c000000000000000010000034c000004c600000000000000010000035600000510000000000000000100000362000004f600000000000000010000036d000005020000000000000001000003790000052a0000000000000001000003830000056c00000000000000010000038d0000054e000000000000000100000399000005aa0000000000000001000003a300000c280000000000000001000003ab00000e540000000000000001000003b80000058a0000000000000001000003c3000005920000000000000001000003d0000005d20000000000000001000003de000005ce0000000000000001000003ea000005e20000000000000001000003f3000006080000000000000001000003fe0000061e0000000000000001000004070000061a0000000000000001000004100000064c00000000000000010000041c000006400000000000000001000004270000063c0000000000000001000004300000068000000000000000010000043b000006940000000000000001000004470000069a0000000000000001000004520000068600000000000000010000045c000009ca00000000000000010000046700000844000000000000000100000472000006b000000000000000010000047c00000c3000000000000000010000048600000c3400000000000000010000048f000006ca00000000000000010000049c000008e40000000000000001000004a80000072a0000000000000001000004b7000007300000000000000001000004c20000070a0000000000000001000004cd000007080000000000000001000004d8000007780000000000000001000004e7000007720000000000000001000004f40000077e000000000000000100000501000007e000000000000000010000050c000007d8000000000000000100000516000007d00000000000000001000005200000081000000000000000010000052d00000c380000000000000001000005350000080a00000000000000010000053f00000c3c0000000000000001000005480000081e0000000000000001000005520000082800000000000000010000055d0000084000000000000000010000056800000832000000000000000100000573000008da00000000000000010000057d000008740000000000000001000005880000087e0000000000000001000005930000088600000000000000010000059f0000089a0000000000000001000005a9000008aa0000000000000001000005b3000008bc0000000000000001000005bf000009220000000000000001000005c9000009060000000000000001000005d4000009100000000000000001000005df000009180000000000000001000005eb0000092c0000000000000001000005f6000009400000000000000001000006010000095400000000000000010000060c000009ac00000000000000010000061800000982000000000000000100000623000009be00000000000000010000062e00000d3c00000000000000010000063700000a1000000000000000010000064100000da000000000000000010000064700000c2000000000000000010000064f00000a5a00000000000000010000065a00000a7200000000000000010000066500000a7c00000000000000010000066f00000a7e00000000000000010000067a00000a7800000000000000010000068300000aa400000000000000010000068d00000ad800000000000000010000069700000b9c00000000000000010000069e00000ade0000000000000001000006a700000aea0000000000000001000006b200000bc40000000000000001000006ba00000afe0000000000000001000006c500000be40000000000000001000006cd00000b120000000000000001000006d700000ae80000000000000001000006e200000b320000000000000001000006eb00000af60000000000000001000006f300000b240000000000000001000006fe00000b0a00000000000000010000070700000b1e00000000000000010000071000000b2c00000000000000010000071a00000b3600000000000000010000072300000b4e00000000000000010000072e00000bae00000000000000010000073500000b8200000000000000010000074000000bf500000000000000010000074800000bd200000000000000010000075000000da400000000000000010000075600000daa00000000000000010000075c00000db100000000000000010000076200000db800000000000000010000076800000dbf00000000000000010000076e00000dc700000000000000010000077400000dcd00000000000000010000077a00000dd500000000000000010000078000000ddd00000000000000010000078600000de500000000000000010000078d00000ded00000000000000010000079400000df500000000000000010000079b00000dfb0000000000000001000007a200000e010000000000000001000007a900000e090000000000000001000007b000000e100000000000000001000007b700000e170000000000000001000007be00000e1f0000000000000001000007c500000e270000000000000001000007cc00000e2e0000000000000001000007d300000e340000000000000001000007da00000e3a0000000000000001000007e100000e420000000000000001000007e800000e4a0000000000000001000007ef00000e840000000000000001000007f500000e860000000000000001000007fb00000e8900000000000000010000080100000e8b00000000000000010000080700000e8e00000000000000010000080d00000e9000000000000000010000081300000e9200000000000000010000081900000e9500000000000000010000081f00000e9700000000000000010000082500000e9a00000000000000010000082b00000e9c00000000000000010000083200000e9f00000000000000010000083900000f340000000000000001000008400000012e000000001000000100000848000001620000000010000001000008510000037a00000000100000010000085a000009dc00000000100000010000086200000a8200000000100000010000086e00000aaa00000000100000010000087900000b5000000000100000010000087f000009ee00000000100000010000088600000a3e00000000100000010000088d00000c0a00000000100000010000089a100b14ec000000001000fff1000008a300000a000000000010000001000008b000000a520000000010000001000008bd00000c150000000010000001000008ca000006580000000010000001000008d2000006b20000000010000001000008da000009640000000010000001000008e2000007400000000010000001000008ea000007840000000010000001000008f2000007e60000000010000001000008fa00000a2e00000000100000010000090600000a4800000000100000010000091200000c1000000000100000010000091e000004b6000000001000000100000928000004ce0000000010000001000009320000051e00000000100000010000093d00000538000000001000000100000947000001f60000000010000001000009500000023200000000100000010000095a000005b6000000001000000100484156455f53594452554d004f43545f574f5244004b4559535f415400717a5f706b657900717a5f706d61736b00717a5f636c6f636b00717a5f6f6374005343414c455f41540055495f545241434b004c4f434b5f5752495445005245435f43545800464c41545f484f4c44004b494e445f464c45580046554e435f48454c44004d4944495f4e4f544500535052494e544600464d545f44004d4944495f4d4f4445005041525453004d504152545f4e4f5445004e565f5343414c45004e565f474c494445004e565f524f4f5400717a5f676c69646500717a5f726f6f7400717a5f676c6964655f6f6600717a5f706f6c79747261636b00717a5f69735f73796e746800717a5f70745f72657400717a5f70745f79657300717a5f70745f6f757400717a5f69735f6e6f00717a5f69735f756e7369676e656400717a5f69735f6f757400717a5f69735f7363616e00717a5f69735f7363616e6e656400717a5f69735f7363616e3100717a5f69735f666d00717a5f73796e74686e616d6500717a5f69735f636d7000717a5f75695f73796e746800717a5f6b5f73746f726500717a5f7175616e7400717a5f705f646f6e6500717a5f705f676f00717a5f6d6b5f63617070656400717a5f6d6b5f646f6e6500717a5f6d6b5f706f7000717a5f7175616e746d00717a5f6d705f73746f726500717a5f6d705f676f00717a5f715f676f00717a5f706372617700717a5f715f6d6f646500717a5f73796e74687100717a5f6b5f6c6f6f7000717a5f6b5f646f776e00717a5f6b5f757000717a5f6b5f6e65787400717a5f6b5f646e00717a5f6b5f75703100717a5f6b5f72657400717a5f70636f6600717a5f6b5f646e3100717a5f70635f74616200717a5f70635f73796e00717a5f70635f6d6f6400717a5f635f73796e746800717a5f635f7265706c617900717a5f7063323500717a5f635f746f7000717a5f635f696e72616e676500717a5f635f6c6f6f7000717a5f635f686900717a5f635f666f756e6400717a5f635f6d6f726500717a5f63735f72617700717a5f63735f6c6f6f7000717a5f63735f6c6f00717a5f63735f646f776e00717a5f63735f686900717a5f63735f757000717a5f63735f68695f6f6b00717a5f63735f6c6f5f6f6b00717a5f6b695f73746f636b00717a5f6b695f6f757400717a5f6f6b5f73746f636b00717a5f6f6b5f646f776e00717a5f6f6b5f73746f726500717a5f6f645f636d7000717a5f6f6e5f6f757400717a5f6f6e5f73746f636b00717a5f6f6e5f666d7400717a5f6f666d7400717a5f726f6f746e616d657300717a5f6f6e5f636f707900717a5f6f6e5f636f7069656400717a5f6d6b795f7265706c617900717a5f6d6b795f736e617000717a5f6d736e617000717a5f6d735f6c6f6f7000717a5f6d735f686900717a5f6d735f6c6f00717a5f6d735f666f756e6400717a5f6d735f6e65787400717a5f6d735f757000717a5f67315f736b697000717a5f67315f73746f636b00717a5f6c65676d6f646500717a5f67315f656e6400717a5f6e6f74656f666600717a5f686f6c6472656c00717a5f6c6d5f72657400717a5f6c656761746f00717a5f636861696e00717a5f67325f6e6f66756e6300717a5f636861696e5f6f6600717a5f67325f747269676c65737300717a5f67325f7472696700717a5f67325f6d6f6e6f00717a5f67325f7061726100717a5f67335f747269676c65737300717a5f67335f73616d706c6500717a5f67335f6a6f696e656400717a5f67345f6e6f6e6500717a5f67345f6f757400717a5f67345f72657300717a5f736c6f745f66696e6400717a5f7374657000717a5f67355f6f757400717a5f707265737300717a5f73665f6b657900717a5f73665f6b65793100717a5f73665f74616b6500717a5f73665f6672656500717a5f68725f6f757400717a5f68725f66696e6400717a5f68725f6e65787400717a5f68725f666f756e6400717a5f68725f74707300717a5f68725f72617700717a5f68725f777269746500717a5f636f5f6f757400717a5f636f5f66696e6400717a5f636f5f6e65787400717a5f636f5f666f756e6400717a5f686f6c64616c6c00717a5f68615f6c6f6f7000717a5f68615f6e65787400717a5f67305f73746f636b00717a5f67305f7061726100717a5f67305f6d6f726500717a5f6e616d657300717a5f67675f6e756d00717a5f6e3000717a5f6762756600717a5f7365745f616e7900717a5f735f636c616d7000717a5f735f7a65726f00717a5f735f73746f726500717a5f735f6c6f7700717a5f655f6261636b00717a5f6c5f6261636b00717a5f6b657900717a5f6c5f636d7000717a5f6c5f7363616c6500717a5f6b65793200717a5f6c5f676c69646500717a5f6b65793300717a5f6c5f726f6f7400717a5f6c5f635f72657400717a5f6c5f64656300717a5f6c5f6f6b00717a5f6c5f73746f726500717a5f6c5f676f6b00717a5f6c5f726f6b00717a5f6c5f6e65787400717a5f6c5f64696700717a5f6c5f645f72657400717a5f666d7400717a5f77725f6c696e6500717a5f666d743300717a5f666d743200717a5f6e3100717a5f6e3200717a5f6e3300717a5f6e3400717a5f6e3500717a5f6e3600717a5f6e3700717a5f6e3800717a5f6e3900717a5f6e313000717a5f6e313100717a5f6e313200717a5f6e313300717a5f6e313400717a5f6e313500717a5f6e313600717a5f6e313700717a5f6e313800717a5f6e313900717a5f6e323000717a5f6e323100717a5f6e323200717a5f6e323300717a5f6e323400717a5f723000717a5f723100717a5f723200717a5f723300717a5f723400717a5f723500717a5f723600717a5f723700717a5f723800717a5f723900717a5f72313000717a5f72313100717a5f656e6400717a5f6b6e6f6200717a5f706c6f636b00717a5f6368726f6d00717a5f6472617700717a5f6c645f656e74727900717a5f6c645f6c696e6500717a5f777200717a5f67657400717a5f73657400717a5f6c626c5f7363616c6500717a5f7363616c6500717a5f6765745f676c69646500717a5f7365745f676c69646500717a5f6c626c5f676c69646500717a5f6c65673100717a5f6c65673200717a5f6c65673000717a5f6c65673300717a5f6c65673400717a5f6c65673500717a5f6765745f726f6f7400717a5f7365745f726f6f7400717a5f6c626c5f726f6f7400717a5f6b657969647800717a5f6f63746b657900717a5f6f63746472617700717a5f6f63746e756d00717a5f6d6b6e6f6200717a5f6d706c6f636b00717a5f6d6b6579000000000007b00000010100000c3800000d3c0000010100000da000000d400000010100000da400000d440000010100000daa00000d480000010100000db100000d4c0000010100000db800000d500000010100000dbf00000d540000010100000dc700000d580000010100000dcd00000d5c0000010100000dd500000d600000010100000ddd00000d640000010100000de500000d680000010100000ded00000d6c0000010100000df500000d700000010100000dfb00000d740000010100000e0100000d780000010100000e0900000d7c0000010100000e1000000d800000010100000e1700000d840000010100000e1f00000d880000010100000e2700000d8c0000010100000e2e00000d900000010100000e3400000d940000010100000e3a00000d980000010100000e4200000d9c0000010100000e4a00000e540000010100000e8400000e580000010100000e8600000e5c0000010100000e8900000e600000010100000e8b00000e640000010100000e8e00000e680000010100000e9000000e6c0000010100000e9200000e700000010100000e9500000e740000010100000e9700000e780000010100000e9a00000e7c0000010100000e9c00000e800000010100000e9f002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e6273730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000100000006000000000000003400000f34000000000000000000000004000000000000001b000000040000004000000000000028bc000001c80000000500000001000000040000000c0000002600000001000000030000000000000f6800000000000000000000000000000004000000000000002c00000008000000030000000000000f6800000000000000000000000000000004000000000000000100000002000000000000000000000f6800000ff000000006000000e100000004000000100000000900000003000000000000000000001f5800000962000000000000000000000001000000000000001100000003000000000000000000002a840000003100000000000000000000000100000000", + "sha256": "915796827c9020b969e3f6ca187d9d247d9852570d3204a11cc66b26b21273fe", "stockCopies": [], "placement": "linked", "poolBaseLiterals": 0, @@ -364,7 +387,7 @@ { "label": "fmpage", "moduleId": "synth", - "version": "0.1.2-experimental", + "version": "0.1.5-experimental", "key": "SYNTH MACHINE", "author": "timhastie", "nativeAuthor": "timhastie", @@ -373,58 +396,12 @@ "caveAddress": 1074603216, "source": "modules/synth/upstream/synth/page.s", "sources": { - "modules/synth/upstream/synth/page.s": "25e2a0624fcc30ee3a8db5966954113119fb5942855042f765e664a2f8544a70", - "modules/synth/manifest.py": "7854330aca001d2841de2fb384f0ca8adb7497db49dacc56da2815c1f444c0e6" - }, - "bytes": 4740, - "code": "7f454c460102010000000000000000000001000400000001000000000000000000001144000080650034000000000028000800072079400d64382068fffc43fa00d04ed020300c000c80400d31ae660000b4243c000018b24c012800d48920422803c8fc001ed1c4d1fc0008edbc7bd00c45464d6600000e7ba800020c050001670000722803e58cd883d4842042d1fc0008f04b75900c820000007f62000066283c000004484c0248000684100b14f020442248283c000000ff7b98670000140c850000002f66000004224853846600ffea3a3c464dba5166000004548941fa002a78057b987599ba826600001853846600fff22079400d64382068fffc43fa00104e904ef940031ed653594e5448000009000900000000001c001e000000000061000100000000006a000400000000009a00040000000000ca00180000000000fe000800000000010a000800000000012a0004000000000136000400000000018e000400000000ffff000000000000464d2053594e5448005241544f0000494e4458000046494e4500004644424b000044454300000040000000000000008000000000000000004003c178000000004003c178000000000000000000000000000000000000000055551551202f000804800000004041fa03996e00000841f9400b465d2f002f082f2f000c4eb940013a084fef000c4e75202f000804800000004041fa03716e00ffdc41fa036e6000ffd42f02202f000ce48802800000001f41fa036e73f00a002001e0880281000000ff74644c021000e0896700003e0c81000000326700001c2f012f00487a030a2f2f00144eb940013a084fef0010600000302f00487a02fa2f2f00104eb940013a084fef000c600000182f004879400b465d2f2f00104eb940013a084fef000c241f4e752f02202f000c6700007c22004c001000203c000007d04c010000068000001f80223c00003f014c4100000c80000003e86400001c2f004879400b465d2f2f00104eb940013a084fef000c60000048223c000003e824004c41200272644c4100002202e789d282d28290812f002f02487a02612f2f00144eb940013a084fef001060000012487a02522f2f000c4eb940013a08508f241f4e7570006000001470016000000e7002600000087003600000024fefffd448d77cfc2e002f2f00482f2f00482f2f00482f2f00482f2f00482f2f00482f2f00484eb9400479b44fef001c202f004008000001660001902c2f003c4a87670000125387670000185387670000b2600000c841fa02d0610001786000012641fa03906100016c220670034c001000707f4c401001740b948170087201610001742406700a4c002000707f4c40200267000012700672016100015a700a72016100015224060482000000146f00002270084c002000706b4c40200267000012700472016100012e700c72016100012624060482000000386f0000aa70064c00200070474c4020026700009a7002720161000102700e7201610000fa6000008641fa0268610000cc4a866700007841fa029e610000ce6000006c41fa02d6610000b270017201740b610000ca2a3c00007fff4a86670000122a06700d4c005000707f4c4050055485780b7e0224075382e98a4c4520020c82000000106f000004741041fa00d475b02800264220072202240461000080280b52870c870000000f6f00ffca202f0040080000006700001841fa0150701022100a81fff0000020c153806c00fff2202f00345e802f00202f003452802f002f2f0050487a00ce4eb9400128a84fef00104cd77cfc4fef002c4e7543fa010e701022d853806c00fffa4e7543fa00fe70102218839953806c00fff84e7541fa00ec41f00c00263c80000000e2ab8790e28b5281b4816c00fff64e7525642e253032640025642e350025642e25647300484f4c44002b2564002b25646300256463000b09070605040303020201010101010101000040008000c0010001030140016a018001c002000203028003000380040004030480050005800600068007000780080009000a000b000c000d000e000f001000000000110000000d000000010000000000000000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f80000020800000208000003f8000000400000004000000150000000e000000040000003f80000020800000208000003f800000000000000000000000000000000000000000000000000000000000003f80000020800000208000002080000020800000208000003f8000000000000000000000000000000000000000000000000000000000000007f000000e10000004100000001000000010000000100000001000000010000000100000001000000410000007f0000000000000000000000000000000000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000000000000000000000000000000000000000000000000000000000000000000003000001000000000000000000000000030000030000000000000000000000000300000400000001400d6438000000000000fff10000000b000000dc00000000000000010000001340031ed6000000000000fff100000020400d31ae000000000000fff1000000270008f04b000000000000fff100000030100b14f0000000000000fff10000003e00000448000000000000fff10000004e400479b4000000000000fff10000005b400128a8000000000000fff10000006040013a08000000000000fff100000068400b465d000000000000fff10000006e4003c178000000000000fff100000078000000d0000000000000000100000082000000c000000000000000010000008e000000860000000000000001000000960000009e0000000000000001000000a1000000980000000000000001000000aa000000aa0000000000000001000000b0000000d60000000000000001000000b8000000b00000000000000001000000bf0000013c0000000000000001000000c8000001450000000000000001000000d1000001630000000000000001000000da000001640000000000000001000000e2000001680000000000000001000000ea0000016c0000000000000001000000f2000001840000000000000001000000fc0000018c000000000000000100000106000001940000000000000001000001110000019800000000000000010000011d000001de00000000000000010000012a000001c400000000000000010000013600000260000000000000000100000143000002f8000000000000000100000150000002fe00000000000000010000015d000003040000000000000001000001690000030a0000000000000001000001760000053d000000000000000100000180000001b0000000000000000100000189000005410000000000000001000001940000054600000000000000010000019c0000055c0000000000000001000001a5000002460000000000000001000001af0000022e0000000000000001000001ba000005240000000000000001000001c20000025c0000000000000001000001cc0000052c0000000000000001000001d4000002e40000000000000001000001df000002ae0000000000000001000001e9000002f40000000000000001000001f3000005310000000000000001000001fa000005380000000000000001000002040000031000000000000000010000020b000004da000000000000000100000216000003660000000000000001000002220000037200000000000000010000022e000004120000000000000001000002390000042c00000000000000010000024500000638000000000000000100000252000004e400000000000000010000025a000004960000000000000001000002620000070400000000000000010000026e00000506000000000000000100000277000003b600000000000000010000027e000003e20000000000000001000002850000067c000000000000000100000291000006c000000000000000010000029d000004f40000000000000001000002a30000045a0000000000000001000002ac0000045e0000000000000001000002b7000004740000000000000001000002be0000054a0000000000000001000002c5000004b80000000000000001000002ce000005f40000000000000001000002d6000004a80000000000000001000002dd0000059c0000000000000001000002e4000004ea0000000000000001000002ed000004fa0000000000000001000002f4000005160000000000000001000002fe000005b00000000000000001000003060000074800000000000000010000030d0000000000000000100000010000031b000000100000000010000001004b494e445f464c45580070675f6f766572005245534f4c5645525f52455400464c45585f5000534c4f545f4f46460053455454494e47535f424153450053455454494e47535f5354524944450053544f434b5f57494447455400424c495400535052494e544600464d545f4400464d545f504c41494e0070675f725f646f6e6500666d5f70675f636c6f6e650070675f7363616e0070675f7363616e6e65640070675f7363616e310070675f666d0070675f6e616d650070675f636d700070675f7469746c650070675f6e616d65730070675f64656636340070675f7a65726f0070675f6c3132380070675f666d74730070675f776964735f610070675f776964735f620070675f6e6962626c65730070675f666d745f707463680070675f666d745f726174696f0070675f666d745f66696e650070675f666d745f64656361790070675f7769645f726174696f0070675f7769645f696e6465780070675f7769645f6664626b0070675f7769645f64656361790070675f665f706c75730070675f66705f676f0070675f665f706c7573630070675f665f64630070675f726174696f0070675f66725f696e740070675f66725f68616c660070675f665f64640070675f66725f6f75740070675f665f64350070675f66645f686f6c640070675f66645f7365630070675f66645f6f75740070675f665f730070675f665f686f6c640070675f7769640070675f7769645f6f75740070675f69635f726174696f0070675f69635f696e6465780070675f69635f6664626b0070675f69635f64656361790070675f696d675f726174696f0070675f636f70790070675f626c69740070675f696d675f626173650070675f766c696e650070675f6978320070675f6978330070675f696d675f6664626b0070675f696d675f6c6f6f700070675f6f720070675f64635f676f0070675f64635f6c6f6f700070675f6463310070675f656e760070675f626c6974310070675f646174610070675f696e760070675f626d700070675f636f7079310070675f6f72310070675f766c696e65310070675f6d61736b0070675f656e6400666d5f64657363726970746f720070675f7265736f6c7665000000000000e0000001010000013c000000e80000010100000145000000f00000010100000163000000f8000001010000016400000100000001010000016800000108000001010000016c00000110000001010000018400000118000001010000018c0000012000000101000001640000012800000101000001640000013000000101000001940000016c00000101000001980000017000000101000001de0000017800000101000001c40000018000000101000002600000018400000101000002f80000018800000101000002fe0000018c000001010000030400000190000001010000030a000005a800000101000005f4000005ac00000101000005b0002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e6273730000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000100000006000000000000003400000748000000000000000000000004000000000000001b00000004000000400000000000001014000000fc0000000500000001000000040000000c000000260000000100000003000000000000077c00000000000000000000000000000004000000000000002c0000000800000003000000000000077c0000000000000000000000000000000400000000000000010000000200000000000000000000077c00000570000000060000005500000004000000100000000900000003000000000000000000000cec000003260000000000000000000000010000000000000011000000030000000000000000000011100000003100000000000000000000000100000000", - "sha256": "0a01a6ebe99bec7fa37e0b6a3f19a8c77488e0ada4661f8d10d6c347f859561f", - "stockCopies": [], - "placement": "linked", - "poolBaseLiterals": 0, - "variants": [] - }, - { - "label": "fmmachine", - "moduleId": "synth", - "version": "0.1.2-experimental", - "key": "SYNTH MACHINE", - "author": "timhastie", - "nativeAuthor": "timhastie", - "cpu": "54455", - "dram": true, - "caveAddress": null, - "source": "modules/synth/machine.s", - "sources": { - "modules/synth/machine.s": "de9d849bf0b6e5d5ad747df32ae5806e7f9516674a14ac74b1c15d73d30d280a", - "modules/synth/manifest.py": "7854330aca001d2841de2fb384f0ca8adb7497db49dacc56da2815c1f444c0e6" - }, - "bytes": 3044, - "code": "7f454c460102010000000000000000000001000400000001000000000000000000000aa4000000260034000000000028000800074feffff848d702027200123008220c8100000001662a2200c2fc001e43f0183c12110c0100466618122900010c01004d660e122900020c01000166047001600270004cd702024fef00084e754feffff048d70207c0fc001e43f0083c6124240894b946c8245604820008ed800682100a4f0ad4802242610a4cd702074fef00104e754a816700005475d10c42464d660a75a900020c020001672e2f082f0041f90000000070051358fff4528953806af643e9fffa7005135801a4528953806af643e9fffa201f205f12bc0046137c004d0001137c000100024e7575d10c42464d661475a900020c020001660a421142290001422900024e75202f00040c8000000005670c2f02222f00084ef9400334de41fa023c20084e75400b3eac400b3e98400b7c67400b5413400b7a63000000004fefffec48ef030200082e882f4000044eb9000000004cef030200084fef00144e754feffff448d701032049d1c0d1fc0008ed8020010c840000000566146100ffc04a806726200172016100fed07801600672006100fec64cd701034fef000c77101084d0814ef9400798224cd701034fef000c48780030487a01ad4eb94005a2b8508f4ef94007989c48794005a61c600648794005a8562239460d5c304feffff448d701052049d1c0d1fc0008ed8020020c810000000566146100ff444a806720200272016100fe547201600a2f0172006100fe48221f4cd701054fef000c4e754cd701054fef000c7200121023c1460d5c3048780030487a012d4eb94005a2b8508f4e7516102f007103610000982600201f79034879400bb7044ef9400585e60c8200000005660274012002e788d48290824ef94003a5360c860000000566027c012e06e78fdc869e864ef94003cda07110610000502f004879460e73864ef940078890610e4ef94003d72261064ef94004c3746100002e41fafe6c22300c004e7571104fef0018611a4ef94003c98671106110b48066064ef9400786ce4ef9400786fc4fefffec48ef030200082e802f4800044eb9000000004cef030200084fef00144e7571056100ffda0c800000000566102f084eb900000000588f4ef940031ed641f9400d5f384ef940031ece4ef9000000004fefffa048d77cfc4ef9400023204eb94005213c4eb94007e9404eb9000000004ef940052228464d2053594e544800464d3a20494e56414c494420545241434b00000000000000000000000000000000000000000000000000000000000000000300000100000000000000000000000003000003000000000000000000000000030000040000000100000005000000000000fff10000000800000001000000000000fff10000000d460d5c30000000000000fff10000001846c82456000000000000fff100000021100b14cf000000000000fff10000002a000018b2000000000000fff1000000360008ed80000000000000fff10000003f100a4ece000000000000fff1000000490000003c000000000000fff100000051400d31ae000000000000fff100000058000001b0000000000000fff100000062400d5f38000000000000fff10000006b000000400000000000000001000000720000004200000000000000010000007b0000004c00000000000000010000008800000082000000000000000100000090000000da00000000000000010000009a0000009a0000000000000001000000a3000000c80000000000000001000000ac000000a60000000000000001000000ba000000b60000000000000001000000c8000000f60000000000000001000000d00000034e0000000000000001000000d8000001300000000000000001000000e1000001820000000000000001000000eb0000019c0000000000000001000000f6000001880000000000000001000001000000035700000000000000010000010f000001c800000000000000010000011d000001fe0000000000000001000001270000021200000000000000010000013200000208000000000000000100000139000002d6000000000000000100000145000002a6000000000000000100000152000000f80000000010000001000001620000011800000000100000010000016f0000015200000000100000010000017e000001ba00000000100000010000018c000002960000000010000001000001960000029e0000000010000001000001a0000002b40000000010000001000001ad000002c20000000010000001000001bc000002360000000010000001000001ca000002820000000010000001000001da000001c20000000010000001000001e9000002520000000010000001000001f80000026a000000001000000100000207000002f8000000001000000100000215000000000000000010000001000002220000032200000000100000010000022e0000000000000000100000000000023a00000000000000001000000000000249000000000000000010000000000002510000000000000000100000000000025f00000000000000001000000000000270000003280000000010000001000002820000033600000000100000010000028f00000000000000001000000000464d5f524f5700464c4558005352435f435552534f520042414e4b5f50545200504152545f49445800504152545f53545249444500504152545f4f4646005352414d5f50415254005349475f4f464600464c45585f500050414745325f4741500050425f5441424c45002e73635f6e6f002e73635f646f6e6500666d5f7369675f7772697465002e73775f6f6e65002e73775f636c656172002e73775f73656564002e73775f6d61726b002e73775f64656661756c747331002e73775f64656661756c747332002e73775f6f757400666d5f6e616d6500666d5f61646d6974002e6d635f6f74686572002e6d635f726566757365002e6d635f73746f726500666d5f72656a6563745f6e616d6500666d5f7372635f636f6d6d6f6e002e73635f6f74686572002e73635f726566757365002e73635f676f00666d5f726f775f7479706500666d5f6e616d655f7069636b00666d5f6d616368696e655f6e616d6500666d5f7372635f6e616d657300666d5f6d61696e5f636f6d6d697400666d5f7372635f636f6d6d697400666d5f6e616d655f6100666d5f6e616d655f6200666d5f73657475705f726f7700666d5f63686f6f7365725f726f7700666d5f73657475705f6f70656e00666d5f63686f6f7365725f6f70656e00666d5f7372635f636f6d6d69743200666d5f73657475705f656469743600666d5f73657475705f647261773600666d5f7265736f6c76655f706200666d5f7369675f636865636b00666d5f76616c696461746500666d5f64656661756c747300666d5f61646d69745f747261636b00666d5f7479706500666d5f747261636b5f7061676500666d5f76616c69646174655f7061727400666d5f73746f636b5f76616c696461746500666d5f7469636b5f686f6f6b00666d5f75695f7469636b0000000000012c000001010000034e000000a00000360100000000000001420000370100000000000002e800003801000000000000030a00003901000000000000032400003a01000000000000034400003d0100000000002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e627373000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000010000000600000000000000340000036a000000000000000000000004000000000000001b00000004000000400000000000000a1c000000540000000500000001000000040000000c00000026000000010000000300000000000003a000000000000000000000000000000004000000000000002c000000080000000300000000000003a0000000000000000000000000000000040000000000000001000000020000000000000000000003a0000003e00000000600000026000000040000001000000009000000030000000000000000000007800000029a000000000000000000000001000000000000001100000003000000000000000000000a700000003100000000000000000000000100000000", - "sha256": "a327f503367d899b8c290f175aba560aa9bd599bf427a1d7c5afde3bacaa7b77", - "stockCopies": [], - "placement": "linked", - "poolBaseLiterals": 0, - "variants": [] - }, - { - "label": "fmregistration", - "moduleId": "synth", - "version": "0.1.2-experimental", - "key": "SYNTH MACHINE", - "author": "timhastie", - "nativeAuthor": "timhastie", - "cpu": "54455", - "dram": true, - "caveAddress": null, - "source": "modules/synth/registration.s", - "sources": { - "modules/synth/registration.s": "d04e47d6d21287ea969e4d39d0fea8c90b262a577081ba8376915f96cd2d9d2d", - "modules/synth/manifest.py": "7854330aca001d2841de2fb384f0ca8adb7497db49dacc56da2815c1f444c0e6" + "modules/synth/upstream/synth/page.s": "8d85620ee336174eea12767745023897ff99b8f63589bff9dfd817642564965d", + "modules/synth/manifest.py": "f364cd97b5a2a416b3029752fe2ce0691fc977d59203419d9d5a52bbeb7913b7" }, - "bytes": 1408, - "code": "7f454c4601020100000000000000000000010004000000010000000000000000000003f0000000260034000000000028000a00092f02206f0008202f000c73b0082253814a816706241f42804e75c0fc001e724641f0083c7190b28066ea71a80001744db48066e010280002a341241fb3804a0057c0710044804e75af41b2af000854c0710044804e752f02203946c82456a3421239100b14cfb4af00086708202f0008241f4e75a742c48173fc18b206800008ed80206f000c4c012800d0822240af4243e9002291c9b48865d22f082f004ebaff60508f4a8067c4ab6f0008202f0008241f4e754ef9400d24d04eb9400d24d023c0400d5f4c4e754fefff7448d77cfc242f0090ad4e45ef002c4285284a42832f052f024ebaff1a508f4a80664052854dee001e49ec000cbdfc000000f666e02f024eb900000000588f4285ad480b03667a528541e8001e45ea000cb1fc000000f666ea4cd77cfc4fef008c4e75a340eba891c886802a7c400d320cab40dbc847f6882a224db08864342208ad474c47100043f60800700c43e901dad3c21991880052881295b08867842a7c400d320cab40dbc847f6882a224db08865cc4bf328001995880052881a9160aa428147f02800ab44b881651449f21800ab442242d3c11194982a5281b88164ec2c01ad474c47600449f21800528143f34800780c135401dab88166ca528541e8001e45ea000cb1fc000000f66600ff346000ff46400c20400028000000000000004743433a2028474e55292031362e322e300000000000000000000000000000000000000000000100000000000000000400fff10000000000000000000000000300000100000000000000000000000003000003000000000000000000000000030000040000001000000000000000480200000100000000000000000000000003000005000000000000000000000000030000060000001d000000480000000e120000010000002c000000560000005e1200000100000034000000b4000000061200000100000042000000ba0000000e120000010000004d000000c800000118120000010000005e00000000000000001000000000000070000000000000000c1100000500726567697374726174696f6e2e63007369676e65645f747261636b00666d5f61646d69745f747261636b00666d5f7479706500666d5f747261636b5f7061676500666d5f75695f7469636b00666d5f76616c69646174655f7061727400666d5f73746f636b5f76616c696461746500666d5f64656661756c7473000000010400000d0100000000002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e627373002e726f64617461002e636f6d6d656e74000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000001000000060000000000000034000001e0000000000000000000000004000000000000001b000000040000004000000000000003a00000000c0000000700000001000000040000000c000000260000000100000003000000000000021400000000000000000000000000000004000000000000002c00000008000000030000000000000214000000000000000000000000000000040000000000000031000000010000000200000000000002140000000c00000000000000000000000100000000000000390000000100000030000000000000022000000013000000000000000000000001000000010000000100000002000000000000000000000234000000f00000000800000008000000040000001000000009000000030000000000000000000003240000007c0000000000000000000000010000000000000011000000030000000000000000000003ac0000004200000000000000000000000100000000", - "sha256": "31cb5a2301e62ec960866523d850cd4066548d86e8832f495d0b8de1023b3a83", + "bytes": 4792, + "code": "7f454c460102010000000000000000000001000400000001000000000000000000001178000080650034000000000028000800072079400d64382068fffc43fa00d44ed020300c000c80400d31ae660000b8243c000018b24c012800d48920422803c8fc001ed1c4d1fc0008edbc7bd00c850000464d660000107ba800020c8500000001670000722803e58cd883d4842042d1fc0008f04b75900c820000007f62000066283c000004484c0248000684100b14f020442248283c000000ff7b98670000140c850000002f66000004224853846600ffea3a3c464dba5166000004548941fa002a78057b987599ba826600001853846600fff22079400d64382068fffc43fa00104e904ef940031ed653594e5448000009000900000000001c001e000000000061000100000000006a000400000000009a00040000000000ca00180000000000fe000800000000010a000800000000012a0004000000000136000400000000018e000400000000ffff000000000000464d2053594e5448005241544f0000494e4458000046494e4500004644424b000044454300000040000000000000008000000000000000004003c178000000004003c178000000000000000000000000000000000000000055551551202f000804800000004041fa03996e00000841f9400b465d2f002f082f2f000c4eb940013a084fef000c4e75202f000804800000004041fa03716e00ffdc41fa036e6000ffd42f02202f000ce48802800000001f41fa036e73f00a002001e0880281000000ff74644c021000e0896700003e0c81000000326700001c2f012f00487a030a2f2f00144eb940013a084fef0010600000302f00487a02fa2f2f00104eb940013a084fef000c600000182f004879400b465d2f2f00104eb940013a084fef000c241f4e752f02202f000c727fb081647822004c001000203c000007d04c010000068000001f80223c00003f014c4100000c80000003e8641a2f004879400b465d2f2f00104eb940013a084fef000c60000048223c000003e824004c41200272644c4100002202e789d282d28290812f002f02487a02612f2f00144eb940013a084fef001060000012487a02522f2f000c4eb940013a08508f241f4e7570006000001470016000000e7002600000087003600000024fefffd448d77cfc2e002f2f00482f2f00482f2f00482f2f00482f2f00482f2f00482f2f00484eb9400479b44fef001c202f004008000001660001902c2f003c4a87670000125387670000185387670000b2600000c841fa02d0610001786000012641fa03906100016c220670034c001000707f4c401001740b948170087201610001742406700a4c002000707f4c40200267000012700672016100015a700a72016100015224060482000000146f00002270084c002000706b4c40200267000012700472016100012e700c72016100012624060482000000386f0000aa70064c00200070474c4020026700009a7002720161000102700e7201610000fa6000008641fa0268610000cc4a866700007841fa029e610000ce6000006c41fa02d6610000b270017201740b610000ca2a3c00007fff707fbc8064102a06700d4c005000707f4c4050055485780b7e0224075382e98a4c4520020c82000000106f000004741041fa00d475b02800264220072202240461000080280b52870c870000000f6f00ffca202f0040080000006700001841fa0150701022100a81fff0000020c153806c00fff2202f00345e802f00202f003452802f002f2f0050487a00ce4eb9400128a84fef00104cd77cfc4fef002c4e7543fa010e701022d853806c00fffa4e7543fa00fe70102218839953806c00fff84e7541fa00ec41f00c00263c80000000e2ab8790e28b5281b4816c00fff64e7525642e253032640025642e350025642e25647300484f4c44002b2564002b25646300256463000b09070605040303020201010101010101000040008000c0010001030140016a018001c002000203028003000380040004030480050005800600068007000780080009000a000b000c000d000e000f001000000000110000000d000000010000000000000000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000fff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f80000020800000208000003f8000000400000004000000150000000e000000040000003f80000020800000208000003f800000000000000000000000000000000000000000000000000000000000003f80000020800000208000002080000020800000208000003f8000000000000000000000000000000000000000000000000000000000000007f000000e10000004100000001000000010000000100000001000000010000000100000001000000410000007f000000000000000000000000000000000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000080000000800000008000000000000000000000000000000000000000000000000000000000000000000000000300000100000000000000000000000003000003000000000000000000000000030000040000000140031ed6000000000000fff10000000e400d31ae000000000000fff100000015400d6438000000000000fff10000001f0008f04b000000000000fff1000000280008edbc000000000000fff100000030100b14f0000000000000fff10000003e00000448000000000000fff10000004e400479b4000000000000fff10000005b400128a8000000000000fff10000006040013a08000000000000fff100000068400b465d000000000000fff10000006e4003c178000000000000fff10000007800000000000000000000000100000086000000e000000000000000010000008e00000010000000000000000100000099000000d40000000000000001000000a3000000540000000000000001000000af000000c40000000000000001000000b80000008a0000000000000001000000c0000000a20000000000000001000000cb0000009c0000000000000001000000d4000000ae0000000000000001000000da000000da0000000000000001000000e2000000b40000000000000001000000e9000001400000000000000001000000f2000001490000000000000001000000fb000001670000000000000001000001040000016800000000000000010000010c0000016c0000000000000001000001140000017000000000000000010000011c00000188000000000000000100000126000001900000000000000001000001300000019800000000000000010000013b0000019c000000000000000100000147000001e2000000000000000100000154000001c80000000000000001000001600000026400000000000000010000016d000002fc00000000000000010000017a00000302000000000000000100000187000003080000000000000001000001930000030e0000000000000001000001a0000005410000000000000001000001aa000001b40000000000000001000001b3000005450000000000000001000001be0000054a0000000000000001000001c6000005600000000000000001000001cf0000024a0000000000000001000001d9000002320000000000000001000001e4000005280000000000000001000001ec000002600000000000000001000001f6000005300000000000000001000001fe000002e8000000000000000100000209000002b2000000000000000100000213000002f800000000000000010000021d000005350000000000000001000002240000053c00000000000000010000022e00000314000000000000000100000235000004de0000000000000001000002400000036a00000000000000010000024c00000376000000000000000100000258000004160000000000000001000002630000043000000000000000010000026f0000063c00000000000000010000027c000004e80000000000000001000002840000049a00000000000000010000028c000007080000000000000001000002980000050a0000000000000001000002a1000003ba0000000000000001000002a8000003e60000000000000001000002af000006800000000000000001000002bb000006c40000000000000001000002c7000004f80000000000000001000002cd0000045e0000000000000001000002d6000004620000000000000001000002e1000004780000000000000001000002e80000054e0000000000000001000002ef000004bc0000000000000001000002f8000005f8000000000000000100000300000004ac000000000000000100000307000005a000000000000000010000030e000004ee000000000000000100000317000004fe00000000000000010000031e0000051a000000000000000100000328000005b40000000000000001000003300000074c0000000000000001005245534f4c5645525f52455400464c45585f50004b494e445f464c455800534c4f545f4f4646005349475f4f46460053455454494e47535f424153450053455454494e47535f5354524944450053544f434b5f57494447455400424c495400535052494e544600464d545f4400464d545f504c41494e00666d5f64657363726970746f720070675f6f7665720070675f7265736f6c76650070675f725f646f6e650070675f756e7369676e65640070675f73796e74680070675f7363616e0070675f7363616e6e65640070675f7363616e310070675f666d0070675f6e616d650070675f636d700070675f7469746c650070675f6e616d65730070675f64656636340070675f7a65726f0070675f6c3132380070675f666d74730070675f776964735f610070675f776964735f620070675f6e6962626c65730070675f666d745f707463680070675f666d745f726174696f0070675f666d745f66696e650070675f666d745f64656361790070675f7769645f726174696f0070675f7769645f696e6465780070675f7769645f6664626b0070675f7769645f64656361790070675f665f706c75730070675f66705f676f0070675f665f706c7573630070675f665f64630070675f726174696f0070675f66725f696e740070675f66725f68616c660070675f665f64640070675f66725f6f75740070675f665f64350070675f66645f686f6c640070675f66645f7365630070675f66645f6f75740070675f665f730070675f665f686f6c640070675f7769640070675f7769645f6f75740070675f69635f726174696f0070675f69635f696e6465780070675f69635f6664626b0070675f69635f64656361790070675f696d675f726174696f0070675f636f70790070675f626c69740070675f696d675f626173650070675f766c696e650070675f6978320070675f6978330070675f696d675f6664626b0070675f696d675f6c6f6f700070675f6f720070675f64635f676f0070675f64635f6c6f6f700070675f6463310070675f656e760070675f626c6974310070675f646174610070675f696e760070675f626d700070675f636f7079310070675f6f72310070675f766c696e65310070675f6d61736b0070675f656e640000000000e40000010100000140000000ec0000010100000149000000f40000010100000167000000fc000001010000016800000104000001010000016c0000010c00000101000001700000011400000101000001880000011c00000101000001900000012400000101000001680000012c000001010000016800000134000001010000019800000170000001010000019c0000017400000101000001e20000017c00000101000001c80000018400000101000002640000018800000101000002fc0000018c000001010000030200000190000001010000030800000194000001010000030e000005ac00000101000005f8000005b000000101000005b4002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e64617461002e627373000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000010000000600000000000000340000074c000000000000000000000004000000000000001b00000004000000400000000000001048000000fc0000000500000001000000040000000c000000260000000100000003000000000000078000000000000000000000000000000004000000000000002c000000080000000300000000000007800000000000000000000000000000000400000000000000010000000200000000000000000000078000000590000000060000005900000004000000100000000900000003000000000000000000000d10000003370000000000000000000000010000000000000011000000030000000000000000000011440000003100000000000000000000000100000000", + "sha256": "b87dad7723b13017e01291146d1988700c7ffdd97f360f2bfa6ae1fa0830bce6", "stockCopies": [], "placement": "linked", "poolBaseLiterals": 0, @@ -874,6 +851,29 @@ "poolBaseLiterals": 0, "variants": [] }, + { + "label": "matrix", + "moduleId": "output-matrix", + "version": "0.1.0-experimental", + "key": "OUTPUT MATRIX", + "author": "npp1993", + "nativeAuthor": "npp1993", + "cpu": "54455", + "dram": true, + "caveAddress": null, + "source": "modules/output-matrix/matrix.s", + "sources": { + "modules/output-matrix/matrix.s": "41bb06ce2e43d46d32a4b12a1c91df6dddf03f89281c4bcfe1129baa22a7cca1", + "modules/output-matrix/manifest.py": "1e9f844eff11dc2e262fd7efc733ae309c4285fd0a14e494da6b568d24af1216" + }, + "bytes": 4952, + "code": "7f454c4601020100000000000000000000010004000000010000000000000000000011f0000000260034000000000028000900084a806c0470004e750c80000000026f0270024e7548780003487800034879460e43904eb94007ec604fef000c4e752f2f00044eb940065430588f2f004ab9460e438c661c7001b0b9460e43986c1223c0460e439823c0460e439442b9460e4390201f4e75720060067201600272022f02753980000037203c400b5e8eb4816606203c400b5e90241f4e75700060067001600270024fefffe048d70cfc2400773980000037b68267287202b4816712b681661e78004a82661078036100017a600878014a83660278022f0261000026241f13c28000003713c2100b14974cd70cfc4fef00204878ffff4eb94004d948588f4e75247c400e21e07a1093c9204ad1fc0008ed926156204ad1fc0009505c614cb5f946c824566610207c100a4ee0613c207c100ab1a8613420094a80671a204ad1fc0009b33270012080b5f946c82456660623c0100f8598d5fc0009b340538566a8207c80000c5061164e7526487c04204b610cd7fc000018b2538666f24e757e00739071a800017607be8366244a3980000034671c0c8400000001677e0c84000000026776760db08366027200700060720c8400000001672e0c84000000026732760db083630270002601243c000019dd010266027200243c0000066a0102660276002003603c4a8067304a816730700360300f398000000967204a80660c4ab98000009c6714700d60184a8167104ab98000009c6608700360087000600422007001b210670410815289b02800016706114000015289548852877608be836600ff404e757a007e00207c80000c517207be8166084a398000003466187190720db0816210223c0000066a010167067c10dc870dc5548852877208be8166d02039800000082c000280ff00ffff808523c08000000823c0100b14d4bb86028600ff00007e007010d0870106671a72000105670272012f0170332f002f074eb940033e3c4fef000c52877008be8066d64e754feffff048d7003c7139800000370c800000000267064ef94004e9944ab98000001267064ef94004eb18202f00184ab946c7debe67082200e7899280200172504c01080041f946c7d2bcd0902400223c000001004c41080028004c014800948420824a8066064ef94004eb1828006114d6846a027600720db6816f0226014ef94004ea1071b9100b14cf223c00000c594c01080073b9100b14ccd0810680000476c9207946c8245677b00a01720db681630276004e757139800000370c8000000002670c4879400b7b984ef94004dd6a61b241f90000000020703c002f0872ff2f0172382f01702d4a2800026602702f2f004879400bf10a4879400ba8764ef94004dd827139800000370c8000000002670a2f044879400b465d6018700db8806302780041f9000000002f304c00487900000000486efff84eb940013a084ef94004ddc67139800000370c800000000266244ab9460d168c66084ab946c7c7306614280270124c02080074124c0428004ef94004df9c70124c0208004ef94004df927139800000370c80000000026708426a0076600000943542006e357c00400052713980000032354000704feffff848d70a0041f980000c5143f90000000047ea00027600780071905488720db081630270007391b280660642290008601c4a2900086610137c000100084253426b000420016006128042290008e98b86805289508b52847204b88166063543007276007208b88166b0354300744cd70a004fef0008357c000100761639800000324e757139800000370c8000000002670c4879400b7b8c4ef94007c49e4879000000004ef94007c49e7139800000370c8000000002670c2ebc400b60404ef94007c5102ebc000000004ef94007c5107139800000370c8000000002670c4879400b576f4ef94007c5324879000000004ef94007c5320000400b44e1400b5eb000000000400651384006515400000000400655544006551400000000400b5eb0400b5eb7000000000000000000000000000000000000000000000000000000004f55542043464700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d41545249580050484e002d002b002573004d4e004355450050484e004d2b43004d2b5000432b5000414c4c004d4e4c004d4e520043554c004355520050484c00504852004f4646000000000000000000000000000000000000000000000000000000000000000300000100000000000000000000000003000003000000000000000000000000030000050000000180000037000000000000fff100000009100b1497000000000000fff1000000154004d948000000000000fff100000021460e4390000000000000fff10000002c460e438c000000000000fff1000000394007ec60000000000000fff10000004340065430000000000000fff10000004f400b5e90000000000000fff100000056400b5e8e000000000000fff10000005e0000006e0000000000000001000000660000009400000000000000010000006f00000236000000000000000100000082000000f200000000000000010000008e400e21e0000000000000fff1000000940009b340000000000000fff10000009e000018b2000000000000fff1000000bf0008ed92000000000000fff1000000cb0009505c000000000000fff1000000a80009b332000000000000fff1000000b246c82456000000000000fff1000000bb100a4ee0000000000000fff1000000c7100ab1a8000000000000fff1000000d4100f8598000000000000fff1000000df80000c50000000000000fff1000000e780000008000000000000fff1000000f00000066a000000000000fff1000000fa000019dd000000000000fff1000001068000009c000000000000fff100000110100b14d4000000000000fff10000011d0000000d000000000000fff10000012380000034000000000000fff10000012d40033e3c000000000000fff1000001340000015c00000000000000010000013f0000017000000000000000010000014b0000000d000000000000fff10000015400000100000000000000fff10000015e00000050000000000000fff10000016746c7d2bc000000000000fff10000016f46c7debe000000000000fff100000178000003460000000000000001000001810000000000000000000000030000018c0000005800000000000000030000019600000052000000000000fff10000019f0000006e000000000000fff1000001a700000070000000000000fff1000001b100000072000000000000fff1000001bc00000074000000000000fff1000001c700000076000000000000fff1000001d180000c51000000000000fff1000001da000000380000000000000003000001e5000000500000000000000003000001ed000000540000000000000003000001f7000000560000000000000003000002000000004800000000000000030000020a000000490000000000000003000002150000005b00000000000000030000021c0000005e000000000000000300000222000000620000000000000003000002290000006600000000000000030000022e0000006a0000000000000003000002330000006e0000000000000003000002380000007200000000000000030000023e000000760000000000000003000002440000007a00000000000000030000024a0000007e000000000000000300000250000000820000000000000003000002560000008600000000000000030000025c0000008a0000000000000003000002620000008e0000000000000003000002680000004000000000000000030000026f0000000000000000100000010000027a000000140000000010000001000002860000002e000000001000000100000291000000640000000010000001000002a0000000680000000010000001000002af0000006c0000000010000001000002be0000008a0000000010000001000002c90000008e0000000010000001000002d4000000920000000010000001000002df000000f00000000010000001000002e8000002c20000000010000001000002f600000378000000001000000100000302000003c600000000100000010000030e0000040600000000100000010000031700000444000000001000000100000323000004f40000000010000001000003330000051a000000001000000100000342000005400000000010000001000003520000056800000000100000010000035c00000574000000001000000100000367000005800000000010000001000003720000058c00000000100000010000037d00000598000000001000000100000389000005a4000000001000000100000395000005b00000000010000001004355455f434647004355455f4346475f435331004d454e555f52454452415700415544494f5f4c49535400415544494f5f434f4c554d4e004c4953545f494e495400415544494f5f4b4559533000424f585f4f4e00424f585f4f4646006375655f676574007365745f6d6f6465006375655f6d61736b5f66726f6d5f6c69766500636f6e766572745f616c6c0042414e4b300042414e4b5f53495a4500504152545f53495a450042414e4b5f53415645004355525f42414e4b004353315f574f524b5f4c56004353315f53415645445f4c56004353315f454449544544004c4956455f4c56004355455f4d41534b004355455f4445535453004c4556454c5f4445535453004355455f4d55544553004355455f4d41534b5f43533100445f4f4646004d41535445525f4f4e0043435f4f555400636f6e765f706172747300636f6e765f747261636b7300444553545f4d4158004449414c5f53544550004449414c5f414444004c45565f414343004c45565f5055534800706172745f63756500646573745f6e616d6573007374725f666d745f730050475f4d41494e300050475f4d41494e0050475f50484f4e45530050475f444553545f4c4f0050475f444553545f48490050475f4d4154524958004c4956455f4355450073656e745f636f646573007374725f70686e007374725f6d696e7573007374725f706c7573007374725f626c616e6b007374725f6d6174726978006e5f6d61696e006e5f637565006e5f70686e73006e5f6d63006e5f6d70006e5f6370006e5f616c6c006e5f6d6e6c006e5f6d6e72006e5f63756c006e5f637572006e5f70686c006e5f706872006e5f6f666600666164696e670070617273655f6d6f646500617564696f5f656e74657200617564696f5f6b657973006375655f6765745f6e6f726d616c006375655f6765745f73747564696f006375655f6765745f6d6174726978006163745f6e6f726d616c006163745f73747564696f006163745f6d6174726978006163745f6e6f6e65006375655f6c6576656c5f656e63006c65765f626f785f637565006c65765f626f785f76616c006c65765f6261727300706167655f6c6576656c73006d697865725f6d69785f6c6162656c006d697865725f6d69785f6c656674006d697865725f6d69785f72696768740074385f6c6162656c730074385f676574746572730074385f616374696f6e73006375655f6c6162656c73006375655f67657474657273006375655f616374696f6e73007374725f6f757463666700000003960000020100000000000003e800000201000000000000047e00000201000000380000053600000201000000540000057000000201000000480000058800000101000000f00000059400000201000000490000059800000101000000640000059c0000010100000068000005a0000001010000006c000005a4000001010000008a000005a8000001010000008e000005ac0000010100000092000003f200000201000000580000051000000201000000500000055c000002010000005600000000000002010000005b00000004000002010000005e0000000800000201000000620000000c000002010000006600000010000002010000006a00000014000002010000006e0000001800000201000000720000001c000002010000007600000020000002010000007a00000024000002010000007e0000002800000201000000820000002c000002010000008600000030000002010000008a00000034000002010000008e002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e72656c612e64617461002e627373000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000001000000060000000000000034000005b8000000000000000000000004000000000000001b00000004000000400000000000001050000000c00000000600000001000000040000000c0000002b000000010000000300000000000005ec00000092000000000000000000000004000000000000002600000004000000400000000000001110000000a80000000600000003000000040000000c00000031000000080000000300000000000006800000000000000000000000000000000400000000000000010000000200000000000000000000068000000630000000070000004a00000004000000100000000900000003000000000000000000000cb0000003a00000000000000000000000010000000000000011000000030000000000000000000011b80000003600000000000000000000000100000000", + "sha256": "511089430f6d0d7a22150e8fcfcd914552d70bc2f64b26d33e11a90d9cfe715b", + "stockCopies": [], + "placement": "linked", + "poolBaseLiterals": 0, + "variants": [] + }, { "label": "repitch", "moduleId": "repitch", @@ -1004,29 +1004,6 @@ "placement": "linked", "poolBaseLiterals": 0, "variants": [] - }, - { - "label": "matrix", - "moduleId": "output-matrix", - "version": "0.1.0-experimental", - "key": "OUTPUT MATRIX", - "author": "npp1993", - "nativeAuthor": "npp1993", - "cpu": "54455", - "dram": true, - "caveAddress": null, - "source": "modules/output-matrix/matrix.s", - "sources": { - "modules/output-matrix/matrix.s": "41bb06ce2e43d46d32a4b12a1c91df6dddf03f89281c4bcfe1129baa22a7cca1", - "modules/output-matrix/manifest.py": "1e9f844eff11dc2e262fd7efc733ae309c4285fd0a14e494da6b568d24af1216" - }, - "bytes": 4952, - "code": "7f454c4601020100000000000000000000010004000000010000000000000000000011f0000000260034000000000028000900084a806c0470004e750c80000000026f0270024e7548780003487800034879460e43904eb94007ec604fef000c4e752f2f00044eb940065430588f2f004ab9460e438c661c7001b0b9460e43986c1223c0460e439823c0460e439442b9460e4390201f4e75720060067201600272022f02753980000037203c400b5e8eb4816606203c400b5e90241f4e75700060067001600270024fefffe048d70cfc2400773980000037b68267287202b4816712b681661e78004a82661078036100017a600878014a83660278022f0261000026241f13c28000003713c2100b14974cd70cfc4fef00204878ffff4eb94004d948588f4e75247c400e21e07a1093c9204ad1fc0008ed926156204ad1fc0009505c614cb5f946c824566610207c100a4ee0613c207c100ab1a8613420094a80671a204ad1fc0009b33270012080b5f946c82456660623c0100f8598d5fc0009b340538566a8207c80000c5061164e7526487c04204b610cd7fc000018b2538666f24e757e00739071a800017607be8366244a3980000034671c0c8400000001677e0c84000000026776760db08366027200700060720c8400000001672e0c84000000026732760db083630270002601243c000019dd010266027200243c0000066a0102660276002003603c4a8067304a816730700360300f398000000967204a80660c4ab98000009c6714700d60184a8167104ab98000009c6608700360087000600422007001b210670410815289b02800016706114000015289548852877608be836600ff404e757a007e00207c80000c517207be8166084a398000003466187190720db0816210223c0000066a010167067c10dc870dc5548852877208be8166d02039800000082c000280ff00ffff808523c08000000823c0100b14d4bb86028600ff00007e007010d0870106671a72000105670272012f0170332f002f074eb940033e3c4fef000c52877008be8066d64e754feffff048d7003c7139800000370c800000000267064ef94004e9944ab98000001267064ef94004eb18202f00184ab946c7debe67082200e7899280200172504c01080041f946c7d2bcd0902400223c000001004c41080028004c014800948420824a8066064ef94004eb1828006114d6846a027600720db6816f0226014ef94004ea1071b9100b14cf223c00000c594c01080073b9100b14ccd0810680000476c9207946c8245677b00a01720db681630276004e757139800000370c8000000002670c4879400b7b984ef94004dd6a61b241f90000000020703c002f0872ff2f0172382f01702d4a2800026602702f2f004879400bf10a4879400ba8764ef94004dd827139800000370c8000000002670a2f044879400b465d6018700db8806302780041f9000000002f304c00487900000000486efff84eb940013a084ef94004ddc67139800000370c800000000266244ab9460d168c66084ab946c7c7306614280270124c02080074124c0428004ef94004df9c70124c0208004ef94004df927139800000370c80000000026708426a0076600000943542006e357c00400052713980000032354000704feffff848d70a0041f980000c5143f90000000047ea00027600780071905488720db081630270007391b280660642290008601c4a2900086610137c000100084253426b000420016006128042290008e98b86805289508b52847204b88166063543007276007208b88166b0354300744cd70a004fef0008357c000100761639800000324e757139800000370c8000000002670c4879400b7b8c4ef94007c49e4879000000004ef94007c49e7139800000370c8000000002670c2ebc400b60404ef94007c5102ebc000000004ef94007c5107139800000370c8000000002670c4879400b576f4ef94007c5324879000000004ef94007c5320000400b44e1400b5eb000000000400651384006515400000000400655544006551400000000400b5eb0400b5eb7000000000000000000000000000000000000000000000000000000004f55542043464700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d41545249580050484e002d002b002573004d4e004355450050484e004d2b43004d2b5000432b5000414c4c004d4e4c004d4e520043554c004355520050484c00504852004f4646000000000000000000000000000000000000000000000000000000000000000300000100000000000000000000000003000003000000000000000000000000030000050000000180000037000000000000fff100000009100b1497000000000000fff1000000154004d948000000000000fff100000021460e4390000000000000fff10000002c460e438c000000000000fff1000000394007ec60000000000000fff10000004340065430000000000000fff10000004f400b5e90000000000000fff100000056400b5e8e000000000000fff10000005e0000006e0000000000000001000000660000009400000000000000010000006f00000236000000000000000100000082000000f200000000000000010000008e400e21e0000000000000fff1000000940009b340000000000000fff10000009e000018b2000000000000fff1000000bf0008ed92000000000000fff1000000cb0009505c000000000000fff1000000a80009b332000000000000fff1000000b246c82456000000000000fff1000000bb100a4ee0000000000000fff1000000c7100ab1a8000000000000fff1000000d4100f8598000000000000fff1000000df80000c50000000000000fff1000000e780000008000000000000fff1000000f00000066a000000000000fff1000000fa000019dd000000000000fff1000001068000009c000000000000fff100000110100b14d4000000000000fff10000011d0000000d000000000000fff10000012380000034000000000000fff10000012d40033e3c000000000000fff1000001340000015c00000000000000010000013f0000017000000000000000010000014b0000000d000000000000fff10000015400000100000000000000fff10000015e00000050000000000000fff10000016746c7d2bc000000000000fff10000016f46c7debe000000000000fff100000178000003460000000000000001000001810000000000000000000000030000018c0000005800000000000000030000019600000052000000000000fff10000019f0000006e000000000000fff1000001a700000070000000000000fff1000001b100000072000000000000fff1000001bc00000074000000000000fff1000001c700000076000000000000fff1000001d180000c51000000000000fff1000001da000000380000000000000003000001e5000000500000000000000003000001ed000000540000000000000003000001f7000000560000000000000003000002000000004800000000000000030000020a000000490000000000000003000002150000005b00000000000000030000021c0000005e000000000000000300000222000000620000000000000003000002290000006600000000000000030000022e0000006a0000000000000003000002330000006e0000000000000003000002380000007200000000000000030000023e000000760000000000000003000002440000007a00000000000000030000024a0000007e000000000000000300000250000000820000000000000003000002560000008600000000000000030000025c0000008a0000000000000003000002620000008e0000000000000003000002680000004000000000000000030000026f0000000000000000100000010000027a000000140000000010000001000002860000002e000000001000000100000291000000640000000010000001000002a0000000680000000010000001000002af0000006c0000000010000001000002be0000008a0000000010000001000002c90000008e0000000010000001000002d4000000920000000010000001000002df000000f00000000010000001000002e8000002c20000000010000001000002f600000378000000001000000100000302000003c600000000100000010000030e0000040600000000100000010000031700000444000000001000000100000323000004f40000000010000001000003330000051a000000001000000100000342000005400000000010000001000003520000056800000000100000010000035c00000574000000001000000100000367000005800000000010000001000003720000058c00000000100000010000037d00000598000000001000000100000389000005a4000000001000000100000395000005b00000000010000001004355455f434647004355455f4346475f435331004d454e555f52454452415700415544494f5f4c49535400415544494f5f434f4c554d4e004c4953545f494e495400415544494f5f4b4559533000424f585f4f4e00424f585f4f4646006375655f676574007365745f6d6f6465006375655f6d61736b5f66726f6d5f6c69766500636f6e766572745f616c6c0042414e4b300042414e4b5f53495a4500504152545f53495a450042414e4b5f53415645004355525f42414e4b004353315f574f524b5f4c56004353315f53415645445f4c56004353315f454449544544004c4956455f4c56004355455f4d41534b004355455f4445535453004c4556454c5f4445535453004355455f4d55544553004355455f4d41534b5f43533100445f4f4646004d41535445525f4f4e0043435f4f555400636f6e765f706172747300636f6e765f747261636b7300444553545f4d4158004449414c5f53544550004449414c5f414444004c45565f414343004c45565f5055534800706172745f63756500646573745f6e616d6573007374725f666d745f730050475f4d41494e300050475f4d41494e0050475f50484f4e45530050475f444553545f4c4f0050475f444553545f48490050475f4d4154524958004c4956455f4355450073656e745f636f646573007374725f70686e007374725f6d696e7573007374725f706c7573007374725f626c616e6b007374725f6d6174726978006e5f6d61696e006e5f637565006e5f70686e73006e5f6d63006e5f6d70006e5f6370006e5f616c6c006e5f6d6e6c006e5f6d6e72006e5f63756c006e5f637572006e5f70686c006e5f706872006e5f6f666600666164696e670070617273655f6d6f646500617564696f5f656e74657200617564696f5f6b657973006375655f6765745f6e6f726d616c006375655f6765745f73747564696f006375655f6765745f6d6174726978006163745f6e6f726d616c006163745f73747564696f006163745f6d6174726978006163745f6e6f6e65006375655f6c6576656c5f656e63006c65765f626f785f637565006c65765f626f785f76616c006c65765f6261727300706167655f6c6576656c73006d697865725f6d69785f6c6162656c006d697865725f6d69785f6c656674006d697865725f6d69785f72696768740074385f6c6162656c730074385f676574746572730074385f616374696f6e73006375655f6c6162656c73006375655f67657474657273006375655f616374696f6e73007374725f6f757463666700000003960000020100000000000003e800000201000000000000047e00000201000000380000053600000201000000540000057000000201000000480000058800000101000000f00000059400000201000000490000059800000101000000640000059c0000010100000068000005a0000001010000006c000005a4000001010000008a000005a8000001010000008e000005ac0000010100000092000003f200000201000000580000051000000201000000500000055c000002010000005600000000000002010000005b00000004000002010000005e0000000800000201000000620000000c000002010000006600000010000002010000006a00000014000002010000006e0000001800000201000000720000001c000002010000007600000020000002010000007a00000024000002010000007e0000002800000201000000820000002c000002010000008600000030000002010000008a00000034000002010000008e002e73796d746162002e737472746162002e7368737472746162002e72656c612e74657874002e72656c612e64617461002e627373000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000001000000060000000000000034000005b8000000000000000000000004000000000000001b00000004000000400000000000001050000000c00000000600000001000000040000000c0000002b000000010000000300000000000005ec00000092000000000000000000000004000000000000002600000004000000400000000000001110000000a80000000600000003000000040000000c00000031000000080000000300000000000006800000000000000000000000000000000400000000000000010000000200000000000000000000068000000630000000070000004a00000004000000100000000900000003000000000000000000000cb0000003a00000000000000000000000010000000000000011000000030000000000000000000011b80000003600000000000000000000000100000000", - "sha256": "511089430f6d0d7a22150e8fcfcd914552d70bc2f64b26d33e11a90d9cfe715b", - "stockCopies": [], - "placement": "linked", - "poolBaseLiterals": 0, - "variants": [] } ], "groups": [ @@ -1790,6 +1767,39 @@ "writeLength": 8, "note": "page resolver LFO descriptor: a synth track gets the VOIC/CHRD clone" }, + { + "address": 1073987570, + "guardLength": 8, + "guardSha256": "646fea5c7ac7852156feed7591534de986df92af53074c25ecf54e4295d072ae", + "unit": "poly", + "symbol": "po_lfdname", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "LFO SETUP PMTR formatter, PLAYBACK names: the page resolver's descriptor (the FM SYNTH page on a synth track) instead of the machine table's; the master track and every other machine stock" + }, + { + "address": 1073987578, + "guardLength": 8, + "guardSha256": "c317505c7277fb592c805f2fcf7b5c789d8d1b43735f0677697b101a75473e71", + "unit": "poly", + "symbol": "po_lfdlfo", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "LFO SETUP PMTR formatter, LFO names: the VOIC / CHRD clone on a synth track (a stored SPD3 / DEP3 prints VOIC / CHRD); every other track stock" + }, + { + "address": 1073976012, + "guardLength": 8, + "guardSha256": "a57855f641d419b81f08ce2f502a1bf18363df142106b0b0ab926fc65b1c0948", + "unit": "poly", + "symbol": "po_lfdedit", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "LFO SETUP PMTR edit (audio tracks): stock's display-order walk, one shown destination a detent, stepping over SPD3 / DEP3 (VOIC / CHRD) on a synth track; the Part byte or the lock as stock stores it" + }, { "address": 1073801030, "guardLength": 6, @@ -1823,17 +1833,6 @@ "writeLength": 8, "note": "the frame builder's AMP-release consumer (mailbox 0x40, every note-off path): a synth voice takes the released flag -- plan B: the engine's own release starts (po_mono_env)" }, - { - "address": 1073787592, - "guardLength": 6, - "guardSha256": "172262e5ddbeacf59e683eccacdcbe33bfaa4609e2ff79f932b5e1b70a9a93bb", - "unit": "poly", - "symbol": "po_stop", - "target": null, - "kind": "jmp", - "writeLength": 6, - "note": "the frame builder's STOP / restart word consumer (after the DSP all-off): every engine voice releases (plan B: the DSP's envelope ends nothing, so no note may stick)" - }, { "address": 1073792564, "guardLength": 8, @@ -1897,7 +1896,7 @@ "symbol": "po_mtrig", "target": null, "kind": "jmp", - "writeLength": 8, + "writeLength": 10, "note": "MIDI IN with a trig held: a synth track's held trig gets its PTCH lock in semitones" }, { @@ -1988,6 +1987,28 @@ "writeLength": 8, "note": "slot assigner (both windows): a track that becomes a synth track by this slot write -- FLEX with an FMSYNTH*/SYNTH* slot, not one before -- gets FINE 0c (RATE := 64 in the Part, its shadow and the live lane); a synth track already, or a sample track: untouched" }, + { + "address": 1074239510, + "guardLength": 8, + "guardSha256": "45a9bc540eb5c34d7b8a12fa454a789212fdb4130e252b4e2b9d54f75849b0e3", + "unit": "fmmachine", + "symbol": "fm_main_commit", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "machine window, the machine-only write (the slot equal): row 5 (FM SYNTH) is stored as FLEX with the Part's \"FM\", 1 (seeded unless the track plays the FM voice already), any other row clears it (2.10, machine.s); then po_machwin: a track that becomes a synth track by the machine change gets FINE 0c" + }, + { + "address": 1074112584, + "guardLength": 8, + "guardSha256": "0e7c7d36b68c48dceaae800ce1f22cbac9a888c121739368ba12458bab9cbe93", + "unit": "poly", + "symbol": "po_machlist", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "sample-list window, the machine-only write: the same" + }, { "address": 1073882758, "guardLength": 6, @@ -2000,411 +2021,532 @@ "note": "file browser select: the slot's path write (stock's sprintf, called from the stub) -- a FLEX slot's file going non-marker -> FMSYNTH*/SYNTH* makes every FLEX track holding that slot a synth track: FINE 0c for each (the new-project case: T1 = FLEX slot 1, the first marker loaded into slot 1, no machine or slot byte changes)" }, { - "address": 1074090352, + "address": 1073882930, "guardLength": 6, - "guardSha256": "dc6543d672cdf223fc2ec7cc3c04b8a584dbd6e7bb1e3590b91cd3e469e0b324", - "unit": "fm_qz", - "symbol": "qz_knob", + "guardSha256": "1828d38aca11808e502a8f12cd4843db42612d2a1317d5ed1225d617cfc5b5e4", + "unit": "poly", + "symbol": "po_unload", "target": null, "kind": "jsr", "writeLength": 6, - "note": "knob handler 0x40055008: the store -- PTCH steps by scale degree" + "note": "file browser load callback: the slot unload before the select (2.12) -- the FLEX slot's marker state is kept for po_loadsel (the unload clears the slot's record), then the stock unload" }, { - "address": 1074073184, - "guardLength": 10, - "guardSha256": "ccf689e403ebd88a651c829b4ae191d609c85baa2a99409bd0a2870d20aaa73e", - "unit": "fm_qz", - "symbol": "qz_plock", + "address": 1073951960, + "guardLength": 6, + "guardSha256": "d6c9f1f60272197a17511f496fc19dd21ddd7df5f1f1ec0cab7a16cad60b0609", + "unit": "fmmachine", + "symbol": "fm_machine_name", "target": null, - "kind": "jsr", - "writeLength": 10, - "note": "p-lock editor (TRIG held): the lock store -- PTCH steps by scale degree" + "kind": "jmp", + "writeLength": 6, + "note": "the machine-name formatter: row 5 reads FM SYNTH (the machine window's sixth row)" }, { - "address": 1074068568, - "guardLength": 10, - "guardSha256": "ddfca03eed59b4f4fffffc9061b138ce3e2a33c30712b5e377d94d054fa7b0f0", - "unit": "fm_qz", - "symbol": "qz_chrom", + "address": 1073989928, + "guardLength": 6, + "guardSha256": "237cd1acd4da7acb61dc7fda7c88e648a5186b36732f433b4dd91966c5a669af", + "unit": "fmmachine", + "symbol": "fm_src_names", "target": null, - "kind": "jsr", - "writeLength": 10, - "note": "CHROMATIC trig key -> pitch: transpose the key index by ROOT (a sample track: within -12..+12 st, the other position an octave below for ROOT G..B) and snap it to the scale" + "kind": "lea", + "writeLength": 6, + "note": "SRC SETUP's machine-name table: six rows, the stock five (copied at run time) and FM SYNTH" }, { - "address": 1074068446, + "address": 1073993496, "guardLength": 6, - "guardSha256": "7daba4e508057c5320565e7ccc62d4e55d0fa501b0c2586445df51ff9503b3de", - "unit": "fm_qz", - "symbol": "qz_leg0", + "guardSha256": "53b8d23acf2c3fe0f794c0a5f37475b17c3433bc6a466e0e1861515989cfecda", + "unit": "fmmachine", + "symbol": "fm_name_a", "target": null, "kind": "jmp", "writeLength": 6, - "note": "CHROMATIC key release on a paraphonic synth track (VOIC 2..4): the held-key mask decides the note-off" + "note": "a track's machine name: FM SYNTH for a chosen track (FLEX with the Part's \"FM\", 1)" }, { - "address": 1074068478, - "guardLength": 8, - "guardSha256": "f974dcd132df96148fe8c38e662bf638aba29290a8ca4467402e8ac707eba123", - "unit": "fm_qz", - "symbol": "qz_leg1", + "address": 1073990016, + "guardLength": 6, + "guardSha256": "237e017ccd95444195ca390a3e6ca6076fbfa1f0931f2e7dfbdbee8c5ec72ca3", + "unit": "fmmachine", + "symbol": "fm_setup_row", "target": null, "kind": "jmp", - "writeLength": 8, - "note": "CHROMATIC key held + a new key: the LEG gate (the engine's po_legmode) -- mono legato keeps the held key with no voice note-off, paraphonic ends nothing, LEG OFF ends the note as stock" + "writeLength": 6, + "note": "SRC SETUP's row highlight: a chosen track's row is FM SYNTH" }, { - "address": 1074068628, - "guardLength": 8, - "guardSha256": "617ecbc4d5386f7ed57bfdc6e37dd7a1e1cc60850934d5c635ead626999e847e", - "unit": "fm_qz", - "symbol": "qz_leg2", + "address": 1074235080, + "guardLength": 6, + "guardSha256": "c611970befafba97f882ab297bd19f0efe0c7d927bfd6110c53f03ad4b7da1c7", + "unit": "fmmachine", + "symbol": "fm_chooser_row", "target": null, "kind": "jmp", - "writeLength": 8, - "note": "CHROMATIC key: mono legato = the trigless trig, the new key becomes the held key; paraphonic = a fresh voice, the key posted for the engine; paraphonic legato (LEG POLY) = the key joins the held set, po_legkey, the trigless trig" + "writeLength": 6, + "note": "the machine window's row highlight: the same" }, { - "address": 1074068704, - "guardLength": 8, - "guardSha256": "e31f7bb195174d49c320879317409c3d2187cc42f337ac0845f41acaebf509bf", - "unit": "fm_qz", - "symbol": "qz_leg3", + "address": 1074103772, + "guardLength": 10, + "guardSha256": "22fb6e2bea4520003d2fae7035c0cd57bcd6fe6f2cfdd5b70580a3f6f2a68034", + "unit": "fmmachine", + "symbol": "fm_setup_open", "target": null, "kind": "jmp", - "writeLength": 8, - "note": "CHROMATIC key -> the live recorder: a legato press records a trigless trig, as FUNC + key does" + "writeLength": 10, + "note": "SRC SETUP opens on FM SYNTH for a chosen track (its page: the descriptor table's slot 5, fm_tick)" }, { - "address": 1074068742, - "guardLength": 6, - "guardSha256": "c8e4de3e3a3d28c808f812c9316fd22097f36ec2afb558ef6b81b2a8d66cebe7", - "unit": "fm_qz", - "symbol": "qz_leg4", + "address": 1074235526, + "guardLength": 10, + "guardSha256": "fe6cb52458c7d968dc467c1cd7721d832baf6bc2fac1826c8c10ed07fee2e7f8", + "unit": "fmmachine", + "symbol": "fm_chooser_open", "target": null, "kind": "jmp", - "writeLength": 6, - "note": "CHROMATIC key recorded: note the step and the time; the release writes the note length as the HOLD lock" + "writeLength": 10, + "note": "the machine window opens on FM SYNTH for a chosen track" }, { - "address": 1074068768, - "guardLength": 6, - "guardSha256": "902ec18f60fbd1fe19c6a380f31a74290976edd45422b224a3905d550636c94f", - "unit": "fm_qz", - "symbol": "qz_leg5", - "target": null, - "kind": "jmp", - "writeLength": 6, - "note": "CHROMATIC key recorded, after its PTCH lock: the synth engine's po_keyrec recognises a fingered chord (PTCH root, CHRD, VOIC locks on the first key's step)" - }, - { - "address": 1074070100, + "address": 1073980718, "guardLength": 8, - "guardSha256": "cee8c24bcad796ec96a96aad2d0073265ca9900179292d7c1c942604aeafd8c3", - "unit": "fm_qz", - "symbol": "qz_keyidx", + "guardSha256": "cae34bf50383462ed4f92d8044e3021247edb8bf69c17a9c7ead62f6edf79510", + "unit": "fmmachine", + "symbol": "fm_setup_edit6", "target": null, "kind": "jmp", "writeLength": 8, - "note": "CHROMATIC key -> index: a synth track's index is the key itself (the octave is qz_oct)" + "note": "SRC SETUP's editor addresses row 5's Part bytes as FLEX's" }, { - "address": 1074026776, + "address": 1073991064, "guardLength": 8, - "guardSha256": "112b7d2434bae65950506429fa2e8d01b20316d7ab6d1848ba91f10c451e11ac", - "unit": "fm_qz", - "symbol": "qz_octkey", + "guardSha256": "ed71ef874312890edc87cef7e19cd8c0f8ac4ac9b90182c2364f78d3a4567f45", + "unit": "fmmachine", + "symbol": "fm_setup_draw6", "target": null, "kind": "jmp", "writeLength": 8, - "note": "FUNC + LEFT/RIGHT in CHROMATIC mode: a synth track's octave steps -4..+4 (qz_oct), stock's word stays 0" + "note": "SRC SETUP's drawer: the same" }, { - "address": 1074022760, - "guardLength": 8, - "guardSha256": "d6d1c7b3caf3215f6590e479a282b5c09d27c1d52c221d596ba9c7fa293896e9", - "unit": "fm_qz", - "symbol": "qz_octdraw", + "address": 1074078238, + "guardLength": 10, + "guardSha256": "8f0fa4f7d6db857f695996b185b38b788fa6af0eacd86d7dd01e2ff61201ffed", + "unit": "fmmachine", + "symbol": "fm_tick", "target": null, "kind": "jmp", - "writeLength": 8, - "note": "CHROMATIC drawer: a synth track draws the keyboard at stock octave 0 (the word is cleared)" + "writeLength": 10, + "note": "the UI tick (its two calls replayed): SRC SETUP's six names refreshed and the PLAYBACK descriptor table's spare slot 5 (0x400d5f4c) := the FM SYNTH page (page.s fm_descriptor)" }, { - "address": 1074022846, - "guardLength": 8, - "guardSha256": "494435e3f72313a14453f642bdba5b064022c657c63c015752fe23092eabd19f", - "unit": "fm_qz", - "symbol": "qz_octnum", + "address": 1074112022, + "guardLength": 6, + "guardSha256": "7a7350a69423ec41ec468a189ba167059626cb7f499d9862394d2388a23c8b59", + "unit": "fmmachine", + "symbol": "fm_src_commit", "target": null, "kind": "jmp", - "writeLength": 8, - "note": "CHROMATIC drawer common argument push preserves MIDI stack; audio octave is qz_oct on a synth track, the position 0 / 1 / -1 on a sample track, with ROOT's name before it when a scale is on" + "writeLength": 6, + "note": "SRC SETUP's machine-byte write: row 5 is stored as FLEX with the signature, any other row clears it" }, { - "address": 1074158538, + "address": 1074112592, "guardLength": 6, - "guardSha256": "484a241b3cea4a13c5b18550e3ab44eaffc021673bd5677a424bb05cc6486a37", - "unit": "fm_qz", - "symbol": "qz_draw", + "guardSha256": "7a7350a69423ec41ec468a189ba167059626cb7f499d9862394d2388a23c8b59", + "unit": "fmmachine", + "symbol": "fm_src_commit2", "target": null, "kind": "jmp", "writeLength": 6, - "note": "SEQUENCER window draw loop: index the row tables from the scroll offset" + "note": "the same on SRC SETUP's second path (po_machlist's detour returns to this site)" }, { - "address": 1074292428, + "address": 1073750808, "guardLength": 8, - "guardSha256": "de4845a149f0373201be2e55cd61ae41f52a03afd7c4cb717b70d9c6bd64c5ad", - "unit": "fm_qz", - "symbol": "qz_ld_entry", + "guardSha256": "3d25d06bf11f3a7bccef894d0016b52adbb4ae468a0f475dd497d7b5966e2694", + "unit": "fmmachine", + "symbol": "fm_validate", "target": null, "kind": "jmp", "writeLength": 8, - "note": "project loader entry: a storing load starts from SCALE = OFF, ROOT = C, GLIDE = OFF" + "note": "the Part validator: a chosen track's twelve FLEX bytes sit out the stock clamps (FLEX's PTCH 4..124; FM SYNTH's is 0..127) -- the stock defaults in their place while it runs, the FM bytes back after" }, { - "address": 1074292642, + "address": 1073796372, "guardLength": 8, - "guardSha256": "51d1dc3c3bf8d2441df47bad2355b22c942d333b44557d35171c891cb573f3fb", - "unit": "fm_qz", - "symbol": "qz_ld_line", + "guardSha256": "39173055fb04ac0a6c355f89561b48834bc5b31ad67609e3fe53efbff7de1866", + "unit": "poly", + "symbol": "po_fmsource", "target": null, "kind": "jmp", "writeLength": 8, - "note": "project loader '#' line: read #SEQUENCER_SCALE=n / #SEQUENCER_ROOT=n / #SYNTH_GLIDE=n" + "note": "the frame builder's second supplier call: a chosen track's supplier is sy_render (an empty FLEX voice gets stock's silent one)" }, { - "address": 1074301098, + "address": 1074238024, "guardLength": 8, - "guardSha256": "ef2b6bc9ba56b8cd1d05117b22c2fafc71e1aa07952c0999da4d4eb54e0489e6", - "unit": "fm_qz", - "symbol": "qz_wr", + "guardSha256": "3ec3e33dc9eae9cfbe075a2a5e2697563293203896e3649ae7e7a718a6d7f34c", + "unit": "fmmachine", + "symbol": "ml_win_count", "target": null, "kind": "jmp", "writeLength": 8, - "note": "project writer: #SEQUENCER_SCALE=n, #SEQUENCER_ROOT=n, #SYNTH_GLIDE=n after PATTERN_CHANGE_CHAIN_BEHAVIOR" + "note": "the machine window's list init: 5 + the remix's machine-list rows (6 with FM SYNTH, 7 with SY DRUM), six visible" }, { - "address": 1073807890, - "guardLength": 6, - "guardSha256": "8728078b48ce7f9e9ac9b2d41c454e293ecc0dc4af302ea1885264064442c9e4", - "unit": "fm_qzc", - "symbol": "qz_boot", + "address": 1074103802, + "guardLength": 8, + "guardSha256": "28992093b4deda97ffa595ce8acd14c8a2a391c67871c1e8dd3421d97467a29d", + "unit": "fmmachine", + "symbol": "ml_src_count", "target": null, - "kind": "jsr", - "writeLength": 6, - "note": "boot: stock's sanitiser of the battery-RAM settings block -- SCALE/GLIDE/ROOT out of range -> OFF / C (core.s, ROM)" + "kind": "jmp", + "writeLength": 8, + "note": "SRC SETUP's machine selector: 5 + the machine-list rows, five visible" }, { - "address": 1073896130, + "address": 1073989968, "guardLength": 6, - "guardSha256": "46484a409ff4c92ca7811222b2a050a935c53122a69184d8a18db71d69820ca9", - "unit": "fm_qzc", - "symbol": "qz_defaults", + "guardSha256": "6fa1f2f2fc3d83f1e1917364e374491dd04fc060d1beea8b77f03bdd332e2b75", + "unit": "fmmachine", + "symbol": "ml_name_bound", "target": null, - "kind": "jsr", + "kind": "jmp", "writeLength": 6, - "note": "project defaults (cold boot, no project, before a load, new project): SCALE/GLIDE := OFF, ROOT := C (core.s, ROM)" + "note": "SRC SETUP's name lookup admits the machine-list rows" }, { - "address": 1073951960, - "guardLength": 6, - "guardSha256": "d6c9f1f60272197a17511f496fc19dd21ddd7df5f1f1ec0cab7a16cad60b0609", + "address": 1074235000, + "guardLength": 12, + "guardSha256": "d0853f882affa9f487c3a82a5e08c3b3bc4c3082692c8af90dd2949eb191a733", "unit": "fmmachine", - "symbol": "fm_machine_name", + "symbol": "ml_draw_bound", "target": null, "kind": "jmp", - "writeLength": 6, - "note": "format machine row 5 as FM SYNTH" + "writeLength": 12, + "note": "the machine window's row drawer draws name(row + top) up to the last row: SELECT MACHINE TYPE scrolls like SRC SETUP" }, { - "address": 1073989928, + "address": 1074235086, "guardLength": 6, - "guardSha256": "237cd1acd4da7acb61dc7fda7c88e648a5186b36732f433b4dd91966c5a669af", + "guardSha256": "2c62e6999a40a15016ea1fe57b7ca93f9d4fe1558127921ae736814a30b5455b", "unit": "fmmachine", - "symbol": "fm_src_names", + "symbol": "ml_hi_bound", "target": null, - "kind": "lea", + "kind": "jmp", "writeLength": 6, - "note": "point SRC SETUP at six machine names" + "note": "the machine window highlights the machine-list rows" }, { - "address": 1073993496, - "guardLength": 6, - "guardSha256": "53b8d23acf2c3fe0f794c0a5f37475b17c3433bc6a466e0e1861515989cfecda", + "address": 1074239748, + "guardLength": 8, + "guardSha256": "845ca10fcd33861c3a74cd9bb5f9da1fd78bb0cf44e30f2fc0ae43db045a633e", "unit": "fmmachine", - "symbol": "fm_name_a", + "symbol": "ml_cur_bound", "target": null, "kind": "jmp", - "writeLength": 6, - "note": "main page name lookup for the signed FM track" + "writeLength": 8, + "note": "the machine window applies a machine-list row under its cursor" }, { - "address": 1074053994, - "guardLength": 6, - "guardSha256": "53b8d23acf2c3fe0f794c0a5f37475b17c3433bc6a466e0e1861515989cfecda", + "address": 1074236450, + "guardLength": 8, + "guardSha256": "d749208f2ebf1e144a87c325281e533ef582759f54549234fcc92c90dc89a595", "unit": "fmmachine", - "symbol": "fm_name_b", + "symbol": "ml_clamp1", "target": null, "kind": "jmp", - "writeLength": 6, - "note": "second main page name lookup for the signed FM track" + "writeLength": 8, + "note": "the machine window's row clamp after a list move: 0 .. the last row (stock 0..5)" }, { - "address": 1073990016, - "guardLength": 6, - "guardSha256": "237e017ccd95444195ca390a3e6ca6076fbfa1f0931f2e7dfbdbee8c5ec72ca3", + "address": 1074236592, + "guardLength": 8, + "guardSha256": "d749208f2ebf1e144a87c325281e533ef582759f54549234fcc92c90dc89a595", "unit": "fmmachine", - "symbol": "fm_setup_row", + "symbol": "ml_clamp2", "target": null, "kind": "jmp", - "writeLength": 6, - "note": "SRC SETUP row highlights FM on a signed FLEX track" + "writeLength": 8, + "note": "the machine window's row clamp after a list move: 0 .. the last row (stock 0..5)" }, { - "address": 1074235080, - "guardLength": 4, - "guardSha256": "f829e3570d9d9e95b7d7b9b0433b1aad2f13c0ac087873dbf981482521301309", + "address": 1074236718, + "guardLength": 8, + "guardSha256": "d749208f2ebf1e144a87c325281e533ef582759f54549234fcc92c90dc89a595", "unit": "fmmachine", - "symbol": "fm_chooser_row", + "symbol": "ml_clamp3", "target": null, "kind": "jmp", - "writeLength": 6, - "note": "main chooser row highlights FM on a signed FLEX track" + "writeLength": 8, + "note": "the machine window's row clamp after a list move: 0 .. the last row (stock 0..5)" }, { - "address": 1074103772, - "guardLength": 10, - "guardSha256": "22fb6e2bea4520003d2fae7035c0cd57bcd6fe6f2cfdd5b70580a3f6f2a68034", + "address": 1074236910, + "guardLength": 8, + "guardSha256": "d749208f2ebf1e144a87c325281e533ef582759f54549234fcc92c90dc89a595", "unit": "fmmachine", - "symbol": "fm_setup_open", + "symbol": "ml_clamp4", "target": null, "kind": "jmp", - "writeLength": 10, - "note": "SRC SETUP opens on FM SYNTH for the FM track" + "writeLength": 8, + "note": "the machine window's row clamp after a list move: 0 .. the last row (stock 0..5)" }, { - "address": 1074078238, - "guardLength": 10, - "guardSha256": "8f0fa4f7d6db857f695996b185b38b788fa6af0eacd86d7dd01e2ff61201ffed", + "address": 1074237052, + "guardLength": 8, + "guardSha256": "d749208f2ebf1e144a87c325281e533ef582759f54549234fcc92c90dc89a595", "unit": "fmmachine", - "symbol": "fm_tick_hook", + "symbol": "ml_clamp5", "target": null, "kind": "jmp", - "writeLength": 10, - "note": "publish the FM Synth SRC SETUP descriptor" + "writeLength": 8, + "note": "the machine window's row clamp after a list move: 0 .. the last row (stock 0..5)" }, { - "address": 1074235526, + "address": 1074090352, + "guardLength": 6, + "guardSha256": "dc6543d672cdf223fc2ec7cc3c04b8a584dbd6e7bb1e3590b91cd3e469e0b324", + "unit": "fm_qz", + "symbol": "qz_knob", + "target": null, + "kind": "jsr", + "writeLength": 6, + "note": "knob handler 0x40055008: the store -- PTCH steps by scale degree" + }, + { + "address": 1074073184, "guardLength": 10, - "guardSha256": "fe6cb52458c7d968dc467c1cd7721d832baf6bc2fac1826c8c10ed07fee2e7f8", - "unit": "fmmachine", - "symbol": "fm_chooser_open", + "guardSha256": "ccf689e403ebd88a651c829b4ae191d609c85baa2a99409bd0a2870d20aaa73e", + "unit": "fm_qz", + "symbol": "qz_plock", "target": null, - "kind": "jmp", + "kind": "jsr", "writeLength": 10, - "note": "the track's machine list opens on FM SYNTH for the FM track" + "note": "p-lock editor (TRIG held): the lock store -- PTCH steps by scale degree" }, { - "address": 1073946228, + "address": 1074090882, "guardLength": 8, - "guardSha256": "bba32826863751ec568719c202735da1ba8f06560e4d375a80637ae6653caa8f", - "unit": "fmmachine", - "symbol": "fm_resolve_pb", + "guardSha256": "03bcaf390b2fd396821abc005afa9853b59e2fcaf10ddc223c001b3a9d276e08", + "unit": "fm_qz", + "symbol": "qz_mknob", + "target": null, + "kind": "jsr", + "writeLength": 8, + "note": "knob handler, the MIDI tracks' branch: the clamp to the max just before the store 0x4005538a -- a MIDI track's NOTE steps by scale degree from ROOT (2.13); the store stays stock, so MIDI SCENES' detour there composes" + }, + { + "address": 1074072472, + "guardLength": 10, + "guardSha256": "9cb4af08e685b7a46a08340cfec555868a0568533c1fb6fc2ec37159dfd980d9", + "unit": "fm_qz", + "symbol": "qz_mplock", + "target": null, + "kind": "jsr", + "writeLength": 10, + "note": "MIDI p-lock editor (TRIG held): the lock store -- a NOTE lock steps by scale degree from ROOT (2.13)" + }, + { + "address": 1074017960, + "guardLength": 10, + "guardSha256": "2ab0a5529e7748a00dea86e66bc4fbb6105ec7f36e73e481bb840f6570da98cc", + "unit": "fm_qz", + "symbol": "qz_mkey", "target": null, "kind": "jmp", "writeLength": 10, - "note": "resolve the signed track's FM page without changing other FLEX tracks" + "note": "MIDI track CHROMATIC key press: the note + ROOT snapped to the nearest scale note (key 1 sounds the root); the note-on, the arp, the held note (so the note-off) and the recorder take it (2.13)" }, { - "address": 1074239516, + "address": 1074068568, + "guardLength": 10, + "guardSha256": "ddfca03eed59b4f4fffffc9061b138ce3e2a33c30712b5e377d94d054fa7b0f0", + "unit": "fm_qz", + "symbol": "qz_chrom", + "target": null, + "kind": "jsr", + "writeLength": 10, + "note": "CHROMATIC trig key -> pitch: transpose the key index by ROOT (a sample track: within -12..+12 st, the other position an octave below for ROOT G..B) and snap it to the scale" + }, + { + "address": 1074068446, "guardLength": 6, - "guardSha256": "89c50ac3ca041fdf5647ec5ee2c3580f9a67dd79f0b5cc15ceadda4b9e9bede0", - "unit": "fmmachine", - "symbol": "fm_main_commit", + "guardSha256": "7daba4e508057c5320565e7ccc62d4e55d0fa501b0c2586445df51ff9503b3de", + "unit": "fm_qz", + "symbol": "qz_leg0", "target": null, "kind": "jmp", "writeLength": 6, - "note": "admit FM Synth at main chooser commit" + "note": "CHROMATIC key release on a paraphonic synth track (VOIC 2..4): the held-key mask decides the note-off" }, { - "address": 1074112022, + "address": 1074068478, + "guardLength": 8, + "guardSha256": "f974dcd132df96148fe8c38e662bf638aba29290a8ca4467402e8ac707eba123", + "unit": "fm_qz", + "symbol": "qz_leg1", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "CHROMATIC key held + a new key: the LEG gate (the engine's po_legmode) -- mono legato keeps the held key with no voice note-off, paraphonic ends nothing, LEG OFF ends the note as stock" + }, + { + "address": 1074068628, + "guardLength": 8, + "guardSha256": "617ecbc4d5386f7ed57bfdc6e37dd7a1e1cc60850934d5c635ead626999e847e", + "unit": "fm_qz", + "symbol": "qz_leg2", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "CHROMATIC key: mono legato = the trigless trig, the new key becomes the held key; paraphonic = a fresh voice, the key posted for the engine; paraphonic legato (LEG POLY) = the key joins the held set, po_legkey, the trigless trig" + }, + { + "address": 1074068704, + "guardLength": 8, + "guardSha256": "e31f7bb195174d49c320879317409c3d2187cc42f337ac0845f41acaebf509bf", + "unit": "fm_qz", + "symbol": "qz_leg3", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "CHROMATIC key -> the live recorder: a legato press records a trigless trig, as FUNC + key does" + }, + { + "address": 1074068742, "guardLength": 6, - "guardSha256": "7a7350a69423ec41ec468a189ba167059626cb7f499d9862394d2388a23c8b59", - "unit": "fmmachine", - "symbol": "fm_src_commit", + "guardSha256": "c8e4de3e3a3d28c808f812c9316fd22097f36ec2afb558ef6b81b2a8d66cebe7", + "unit": "fm_qz", + "symbol": "qz_leg4", "target": null, "kind": "jmp", "writeLength": 6, - "note": "admit FM Synth at SRC SETUP commit" + "note": "CHROMATIC key recorded: note the step and the time; the release writes the note length as the HOLD lock" }, { - "address": 1074112592, + "address": 1074068768, "guardLength": 6, - "guardSha256": "7a7350a69423ec41ec468a189ba167059626cb7f499d9862394d2388a23c8b59", - "unit": "fmmachine", - "symbol": "fm_src_commit2", + "guardSha256": "902ec18f60fbd1fe19c6a380f31a74290976edd45422b224a3905d550636c94f", + "unit": "fm_qz", + "symbol": "qz_leg5", "target": null, "kind": "jmp", "writeLength": 6, - "note": "the same at SRC SETUP's second commit path" + "note": "CHROMATIC key recorded, after its PTCH lock: the synth engine's po_keyrec recognises a fingered chord (PTCH root, CHRD, VOIC locks on the first key's step)" }, { - "address": 1073980718, + "address": 1074070100, "guardLength": 8, - "guardSha256": "cae34bf50383462ed4f92d8044e3021247edb8bf69c17a9c7ead62f6edf79510", - "unit": "fmmachine", - "symbol": "fm_setup_edit6", + "guardSha256": "cee8c24bcad796ec96a96aad2d0073265ca9900179292d7c1c942604aeafd8c3", + "unit": "fm_qz", + "symbol": "qz_keyidx", "target": null, "kind": "jmp", "writeLength": 8, - "note": "SETUP editor addresses FM SYNTH as FLEX" + "note": "CHROMATIC key -> index: a synth track's index is the key itself (the octave is qz_oct)" }, { - "address": 1073991064, + "address": 1074026776, "guardLength": 8, - "guardSha256": "ed71ef874312890edc87cef7e19cd8c0f8ac4ac9b90182c2364f78d3a4567f45", - "unit": "fmmachine", - "symbol": "fm_setup_draw6", + "guardSha256": "112b7d2434bae65950506429fa2e8d01b20316d7ab6d1848ba91f10c451e11ac", + "unit": "fm_qz", + "symbol": "qz_octkey", "target": null, "kind": "jmp", "writeLength": 8, - "note": "SETUP drawer addresses FM SYNTH as FLEX" + "note": "FUNC + LEFT/RIGHT in CHROMATIC mode: a synth track's octave steps -4..+4 (qz_oct), stock's word stays 0" }, { - "address": 1073750808, + "address": 1074022760, "guardLength": 8, - "guardSha256": "3d25d06bf11f3a7bccef894d0016b52adbb4ae468a0f475dd497d7b5966e2694", - "unit": "fmmachine", - "symbol": "fm_validate", + "guardSha256": "d6d1c7b3caf3215f6590e479a282b5c09d27c1d52c221d596ba9c7fa293896e9", + "unit": "fm_qz", + "symbol": "qz_octdraw", "target": null, "kind": "jmp", "writeLength": 8, - "note": "the Part validator reads an FM track's controls without stock FLEX clamping" + "note": "CHROMATIC drawer: a synth track draws the keyboard at stock octave 0 (the word is cleared)" }, { - "address": 1073796372, + "address": 1074022846, "guardLength": 8, - "guardSha256": "39173055fb04ac0a6c355f89561b48834bc5b31ad67609e3fe53efbff7de1866", - "unit": "poly", - "symbol": "fm_source_dispatch", + "guardSha256": "494435e3f72313a14453f642bdba5b064022c657c63c015752fe23092eabd19f", + "unit": "fm_qz", + "symbol": "qz_octnum", "target": null, "kind": "jmp", "writeLength": 8, - "note": "" + "note": "CHROMATIC drawer, the common push of the octave number both branches reach (2.13, Modwerk #264: the MIDI branch jumps here, so the push is never skipped): a MIDI track keeps stock's number, a synth track prints qz_oct, a sample track the position 0 / 1 / -1, with ROOT's name before it when a scale is on" }, { - "address": 1073946318, + "address": 1074158538, "guardLength": 6, - "guardSha256": "5aed68f7498659f87e414631493741e91f3442c5319f46e6893b567c423ce24f", - "unit": "fmpage", - "symbol": "pg_resolve", + "guardSha256": "484a241b3cea4a13c5b18550e3ab44eaffc021673bd5677a424bb05cc6486a37", + "unit": "fm_qz", + "symbol": "qz_draw", "target": null, "kind": "jmp", "writeLength": 6, - "note": "" + "note": "SEQUENCER window draw loop: index the row tables from the scroll offset" + }, + { + "address": 1074292428, + "guardLength": 8, + "guardSha256": "de4845a149f0373201be2e55cd61ae41f52a03afd7c4cb717b70d9c6bd64c5ad", + "unit": "fm_qz", + "symbol": "qz_ld_entry", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "project loader entry: a storing load starts from SCALE = OFF, ROOT = C, GLIDE = OFF" + }, + { + "address": 1074292642, + "guardLength": 8, + "guardSha256": "51d1dc3c3bf8d2441df47bad2355b22c942d333b44557d35171c891cb573f3fb", + "unit": "fm_qz", + "symbol": "qz_ld_line", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "project loader '#' line: read #SEQUENCER_SCALE=n / #SEQUENCER_ROOT=n / #SYNTH_GLIDE=n" + }, + { + "address": 1074301098, + "guardLength": 8, + "guardSha256": "ef2b6bc9ba56b8cd1d05117b22c2fafc71e1aa07952c0999da4d4eb54e0489e6", + "unit": "fm_qz", + "symbol": "qz_wr", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "project writer: #SEQUENCER_SCALE=n, #SEQUENCER_ROOT=n, #SYNTH_GLIDE=n after PATTERN_CHANGE_CHAIN_BEHAVIOR" + }, + { + "address": 1073807890, + "guardLength": 6, + "guardSha256": "8728078b48ce7f9e9ac9b2d41c454e293ecc0dc4af302ea1885264064442c9e4", + "unit": "fm_qzc", + "symbol": "qz_boot", + "target": null, + "kind": "jsr", + "writeLength": 6, + "note": "boot: stock's sanitiser of the battery-RAM settings block -- SCALE/GLIDE/ROOT out of range -> OFF / C (core.s, ROM)" + }, + { + "address": 1073896130, + "guardLength": 6, + "guardSha256": "46484a409ff4c92ca7811222b2a050a935c53122a69184d8a18db71d69820ca9", + "unit": "fm_qzc", + "symbol": "qz_defaults", + "target": null, + "kind": "jsr", + "writeLength": 6, + "note": "project defaults (cold boot, no project, before a load, new project): SCALE/GLIDE := OFF, ROOT := C (core.s, ROM)" } ], "refs": [ @@ -2420,71 +2562,36 @@ { "address": 1074619444, "guardLength": 4, - "guardSha256": "97f0e8a3a010d3298daa5af020a86806f3390bf4b5843ef4db6c3e02da79bfa8", - "unit": "poly", - "symbol": "sy_render", - "addend": 0, - "note": "kind table STATIC renderer -> sy_render too (2.8: a STATIC sample's PTCH slides with LEG MONO + GLIDE; no marker scan, the stock call otherwise)" - }, - { - "address": 1074619480, - "guardLength": 4, - "guardSha256": "64edd2d66f0f90ef62de874e70dccdbe79c05d815aac6ce0c6eaf351b0e97698", - "unit": "poly", - "symbol": "fm_start", - "addend": 0, - "note": "Signed FM starts without sample resources; ordinary FLEX keeps stock start" - } - ], - "pokes": [ - { - "address": 1074158716, - "guardLength": 4, - "guardSha256": "a3e547e08c1f288e512f2e52ac9becc3502ccd0dcfcf55e5ab9863859f34e2bc", - "code": "48780006", - "note": "SEQUENCER window: 3 -> 6 rows (SCALE, ROOT, GLIDE; 3 visible, scrolls)" - }, - { - "address": 1074238024, - "guardLength": 4, - "guardSha256": "8fbd3190028fe564c11b56e3e73e8eaf36925cbabf94164176eeb4c4f57bc6ac", - "code": "48780006", - "note": "machine chooser has six rows, FM SYNTH the last" - }, - { - "address": 1074103802, - "guardLength": 4, - "guardSha256": "8fbd3190028fe564c11b56e3e73e8eaf36925cbabf94164176eeb4c4f57bc6ac", - "code": "48780006", - "note": "SRC SETUP selector has six rows" - }, - { - "address": 1073989968, - "guardLength": 2, - "guardSha256": "74d01dadcbe6a57ef61bc53a5a65404cd8446c6738ff0ac86b1b1a78d8ae7d41", - "code": "7205", - "note": "SRC SETUP name lookup admits FM" + "guardSha256": "97f0e8a3a010d3298daa5af020a86806f3390bf4b5843ef4db6c3e02da79bfa8", + "unit": "poly", + "symbol": "sy_render", + "addend": 0, + "note": "kind table STATIC renderer -> sy_render too (2.8: a STATIC sample's PTCH slides with LEG MONO + GLIDE; no marker scan, the stock call otherwise)" }, { - "address": 1074235000, - "guardLength": 2, - "guardSha256": "04cd1c6d463ba6752ff68f9125991b1c4af983ae355b62366122ba2d47eee3b2", - "code": "7005", - "note": "machine chooser draws FM" - }, + "address": 1074619480, + "guardLength": 4, + "guardSha256": "64edd2d66f0f90ef62de874e70dccdbe79c05d815aac6ce0c6eaf351b0e97698", + "unit": "poly", + "symbol": "po_fmstart", + "addend": 0, + "note": "kind table FLEX START callback -> po_fmstart (2.10): a track whose FM SYNTH is chosen in the machine list starts its voice without a sample; every other FLEX track the stock callback" + } + ], + "pokes": [ { - "address": 1074235086, - "guardLength": 2, - "guardSha256": "04cd1c6d463ba6752ff68f9125991b1c4af983ae355b62366122ba2d47eee3b2", - "code": "7005", - "note": "machine chooser highlights FM" + "address": 1074158716, + "guardLength": 4, + "guardSha256": "a3e547e08c1f288e512f2e52ac9becc3502ccd0dcfcf55e5ab9863859f34e2bc", + "code": "48780006", + "note": "SEQUENCER window: 3 -> 6 rows (SCALE, ROOT, GLIDE; 3 visible, scrolls)" }, { - "address": 1074239748, - "guardLength": 2, - "guardSha256": "577d7ba37f9786ab02a7dc9eeb612bb55936018179b550b14f4b9f000a89bf20", - "code": "7605", - "note": "machine chooser persists FM" + "address": 1073946318, + "guardLength": 6, + "guardSha256": "5aed68f7498659f87e414631493741e91f3442c5319f46e6893b567c423ce24f", + "code": "4ef9400d24e0", + "note": "page resolver: the kind-0 PLAYBACK table load jumps to the page cave (pg_resolve, PAGE_AT + 16)" } ], "tables": [ @@ -4168,30 +4275,252 @@ { "address": 1074360766, "guardLength": 2, - "guardSha256": "63540692485e9982808a3ad2d9eda3ef9f9b6b45ea3d491ee894cc3f0b7579e9", - "code": "7001", - "note": "operational resolver aliases stored POLY to FLEX" + "guardSha256": "63540692485e9982808a3ad2d9eda3ef9f9b6b45ea3d491ee894cc3f0b7579e9", + "code": "7001", + "note": "operational resolver aliases stored POLY to FLEX" + }, + { + "address": 1074619464, + "guardLength": 4, + "guardSha256": "05df58a4f64b8999ce8749ed35fd491ed015ef68c5538d825dca26b8c0b6be24", + "code": "40004008", + "note": "POLY uses the STATIC/FLEX playback-increment builder" + }, + { + "address": 1074619496, + "guardLength": 4, + "guardSha256": "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119", + "code": "4000f450", + "note": "POLY uses the STATIC/FLEX common voice initializer" + }, + { + "address": 1073791020, + "guardLength": 2, + "guardSha256": "04cd1c6d463ba6752ff68f9125991b1c4af983ae355b62366122ba2d47eee3b2", + "code": "7005", + "note": "machine-change bookkeeping accepts raw POLY" + } + ], + "tables": [] + }, + { + "moduleId": "output-matrix", + "key": "OUTPUT MATRIX", + "author": "npp1993", + "nativeAuthor": "npp1993", + "activeWith": null, + "detours": [ + { + "address": 1074295594, + "guardLength": 14, + "guardSha256": "968adf63466a054aefd6609f18536e3719bbcc144b6dc0a43de49794c3bb3718", + "unit": "matrix", + "symbol": "parse_mode", + "target": null, + "kind": "jsr", + "writeLength": 14, + "note": "project load: CUE_STUDIO_MODE clamps to 0..2, not 0..1" + }, + { + "address": 1074063756, + "guardLength": 8, + "guardSha256": "4c0570fc6ef0682508e64de2367e111c27f895b6b78e58c300b3f2e7b8010118", + "unit": "matrix", + "symbol": "cue_level_enc", + "target": null, + "kind": "jmp", + "writeLength": 8, + "note": "CUE + LEVEL: in MATRIX, step the track's destination" + }, + { + "address": 1074060644, + "guardLength": 6, + "guardSha256": "4a0e8b026de2be316135a324da1ec8860f198cd3d26d2c6e1477772403baa3f0", + "unit": "matrix", + "symbol": "lev_box_cue", + "target": null, + "kind": "jmp", + "writeLength": 6, + "note": "LEV box with CUE held: in MATRIX, labelled with the track's destination" + }, + { + "address": 1074060724, + "guardLength": 18, + "guardSha256": "599199441e3ed6b14d404d9165c6960c172372cb3afb1138eb987a85056f356c", + "unit": "matrix", + "symbol": "lev_box_val", + "target": null, + "kind": "jmp", + "writeLength": 18, + "note": "LEV box with CUE held: in MATRIX, the destination's name" + }, + { + "address": 1074061196, + "guardLength": 6, + "guardSha256": "f8f99e5fbedfdea641ce1ee91dd4b343c5f584bdbdcfdb906cc7b2ff700b81e4", + "unit": "matrix", + "symbol": "lev_bars", + "target": null, + "kind": "jmp", + "writeLength": 6, + "note": "LEV box bars: in MATRIX both show the level, solid on every track" + }, + { + "address": 1074250904, + "guardLength": 6, + "guardSha256": "44ed799109c451f24e666752e6cbb64d01b2569a031747ca70862d4e993655c9", + "unit": "matrix", + "symbol": "mixer_mix_label", + "target": null, + "kind": "jmp", + "writeLength": 6, + "note": "MIXER: in MATRIX, MIX is labelled PHN (the PHONES level)" + }, + { + "address": 1074251018, + "guardLength": 6, + "guardSha256": "8094ae1fc8f6652016413873ceeff62f4c61f3626e4cd0d1aea9eb7365b2e681", + "unit": "matrix", + "symbol": "mixer_mix_left", + "target": null, + "kind": "jmp", + "writeLength": 6, + "note": "MIXER: in MATRIX, the PHN slider's left end reads -" + }, + { + "address": 1074251052, + "guardLength": 6, + "guardSha256": "89256b8a6327e056fd997c1d763d596c02d198f79acb7ee65049c8771cb8b5ce", + "unit": "matrix", + "symbol": "mixer_mix_right", + "target": null, + "kind": "jmp", + "writeLength": 6, + "note": "MIXER: in MATRIX, the PHN slider's right end reads +" + }, + { + "address": 1073795562, + "guardLength": 6, + "guardSha256": "84ba946536b14dc94d29f98643355d547c696eaf466a6aa97f9e4d654e1c9296", + "unit": "matrix", + "symbol": "page_levels", + "target": null, + "kind": "jsr", + "writeLength": 6, + "note": "level page: unity MAIN word, real levels and destinations in $37..$3b" + } + ], + "refs": [ + { + "address": 1074156150, + "guardLength": 4, + "guardSha256": "28661e3de100b684f6ca83ab64a3283c276481436356429e3275d0ad1b4a5e7c", + "unit": "matrix", + "symbol": "t8_labels", + "addend": 0, + "note": "AUDIO: TRACK 8 labels, a third (blank) row" + }, + { + "address": 1074156156, + "guardLength": 4, + "guardSha256": "4b9b373d835bb489b6794d443dfce3d18339bf742d1c3cd477a62e50a0e3e712", + "unit": "matrix", + "symbol": "t8_getters", + "addend": 0, + "note": "AUDIO: TRACK 8 checkboxes" + }, + { + "address": 1074156332, + "guardLength": 4, + "guardSha256": "d4ea2d8e557d50f2fb4c255b28ca5bc70d34efdfef6ab6b96276db6ffd389dc0", + "unit": "matrix", + "symbol": "str_outcfg", + "addend": 0, + "note": "AUDIO: the CUE CFG box is titled OUT CFG" + }, + { + "address": 1074156380, + "guardLength": 4, + "guardSha256": "a56725c1818295b0a417cfb8a6a383296dc599132ffc414c4146476e58264e65", + "unit": "matrix", + "symbol": "cue_labels", + "addend": 0, + "note": "AUDIO: CUE CFG labels + MATRIX" + }, + { + "address": 1074156392, + "guardLength": 4, + "guardSha256": "5f8314f70aa32088324642fabf401b6b797ed6c5d3475512975c52aa0b7b787c", + "unit": "matrix", + "symbol": "cue_getters", + "addend": 0, + "note": "AUDIO: CUE CFG checkboxes" + }, + { + "address": 1074156722, + "guardLength": 4, + "guardSha256": "93f7f722f83f2caf2d244df72d6d5204d139b2d4fcd321187556ae8d488c27d4", + "unit": "matrix", + "symbol": "t8_actions", + "addend": 0, + "note": "AUDIO: TRACK 8 YES actions" + }, + { + "address": 1074156736, + "guardLength": 4, + "guardSha256": "78f80a662dca288a1493f16890041afdf6d2d5c6d1065aa5cf87cb70707763c8", + "unit": "matrix", + "symbol": "cue_actions", + "addend": 0, + "note": "AUDIO: CUE CFG YES actions" + }, + { + "address": 1074576852, + "guardLength": 4, + "guardSha256": "e60d8005417ede6ff81d7493e3538f7d5a7a2bae9fa2100830c0cba2f447af27", + "unit": "matrix", + "symbol": "audio_enter", + "addend": 0, + "note": "AUDIO page: three rows" + }, + { + "address": 1074576864, + "guardLength": 4, + "guardSha256": "e094ec5b09c62add1a5833fcfb04cdb0d894f11c49bfc6045532ee37bca1e51f", + "unit": "matrix", + "symbol": "audio_keys", + "addend": 0, + "note": "AUDIO page: keep TRACK 8's cursor on its two rows" + } + ], + "pokes": [ + { + "address": 1074156338, + "guardLength": 2, + "guardSha256": "06b6b4095e023805a3cd41879f15ba5ab2f2e33a6f53d916904496062f324995", + "code": "001d", + "note": "AUDIO: the CUE CFG box grows by one row (22 -> 29 px)" }, { - "address": 1074619464, - "guardLength": 4, - "guardSha256": "05df58a4f64b8999ce8749ed35fd491ed015ef68c5538d825dca26b8c0b6be24", - "code": "40004008", - "note": "POLY uses the STATIC/FLEX playback-increment builder" + "address": 1074156346, + "guardLength": 2, + "guardSha256": "f09a7a12954169ae595d12d870e69a4c0092003157d72523d626d2a3990241e2", + "code": "0004", + "note": "AUDIO: ... downward: its bottom edge 11 -> 4, so the top stays put" }, { - "address": 1074619496, - "guardLength": 4, - "guardSha256": "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119", - "code": "4000f450", - "note": "POLY uses the STATIC/FLEX common voice initializer" + "address": 1073807550, + "guardLength": 2, + "guardSha256": "0b14b394f74bdc2f13a4efc3e6bcec310ba2288bcf64811943a1fe443229d05b", + "code": "7402", + "note": "power-up CS1 check: CUE CFG up to 2 is valid (1 counted 2 as damage)" }, { - "address": 1073791020, + "address": 1073807556, "guardLength": 2, - "guardSha256": "04cd1c6d463ba6752ff68f9125991b1c4af983ae355b62366122ba2d47eee3b2", - "code": "7005", - "note": "machine-change bookkeeping accepts raw POLY" + "guardSha256": "1f2e6d00279b4659b6d6f1ad57e12568e4a50f80908af50d1e861001b2be2a3b", + "code": "7002", + "note": "... and above 2 clamps to 2" } ], "tables": [] @@ -4476,228 +4805,6 @@ ], "pokes": [], "tables": [] - }, - { - "moduleId": "output-matrix", - "key": "OUTPUT MATRIX", - "author": "npp1993", - "nativeAuthor": "npp1993", - "activeWith": null, - "detours": [ - { - "address": 1074295594, - "guardLength": 14, - "guardSha256": "968adf63466a054aefd6609f18536e3719bbcc144b6dc0a43de49794c3bb3718", - "unit": "matrix", - "symbol": "parse_mode", - "target": null, - "kind": "jsr", - "writeLength": 14, - "note": "project load: CUE_STUDIO_MODE clamps to 0..2, not 0..1" - }, - { - "address": 1074063756, - "guardLength": 8, - "guardSha256": "4c0570fc6ef0682508e64de2367e111c27f895b6b78e58c300b3f2e7b8010118", - "unit": "matrix", - "symbol": "cue_level_enc", - "target": null, - "kind": "jmp", - "writeLength": 8, - "note": "CUE + LEVEL: in MATRIX, step the track's destination" - }, - { - "address": 1074060644, - "guardLength": 6, - "guardSha256": "4a0e8b026de2be316135a324da1ec8860f198cd3d26d2c6e1477772403baa3f0", - "unit": "matrix", - "symbol": "lev_box_cue", - "target": null, - "kind": "jmp", - "writeLength": 6, - "note": "LEV box with CUE held: in MATRIX, labelled with the track's destination" - }, - { - "address": 1074060724, - "guardLength": 18, - "guardSha256": "599199441e3ed6b14d404d9165c6960c172372cb3afb1138eb987a85056f356c", - "unit": "matrix", - "symbol": "lev_box_val", - "target": null, - "kind": "jmp", - "writeLength": 18, - "note": "LEV box with CUE held: in MATRIX, the destination's name" - }, - { - "address": 1074061196, - "guardLength": 6, - "guardSha256": "f8f99e5fbedfdea641ce1ee91dd4b343c5f584bdbdcfdb906cc7b2ff700b81e4", - "unit": "matrix", - "symbol": "lev_bars", - "target": null, - "kind": "jmp", - "writeLength": 6, - "note": "LEV box bars: in MATRIX both show the level, solid on every track" - }, - { - "address": 1074250904, - "guardLength": 6, - "guardSha256": "44ed799109c451f24e666752e6cbb64d01b2569a031747ca70862d4e993655c9", - "unit": "matrix", - "symbol": "mixer_mix_label", - "target": null, - "kind": "jmp", - "writeLength": 6, - "note": "MIXER: in MATRIX, MIX is labelled PHN (the PHONES level)" - }, - { - "address": 1074251018, - "guardLength": 6, - "guardSha256": "8094ae1fc8f6652016413873ceeff62f4c61f3626e4cd0d1aea9eb7365b2e681", - "unit": "matrix", - "symbol": "mixer_mix_left", - "target": null, - "kind": "jmp", - "writeLength": 6, - "note": "MIXER: in MATRIX, the PHN slider's left end reads -" - }, - { - "address": 1074251052, - "guardLength": 6, - "guardSha256": "89256b8a6327e056fd997c1d763d596c02d198f79acb7ee65049c8771cb8b5ce", - "unit": "matrix", - "symbol": "mixer_mix_right", - "target": null, - "kind": "jmp", - "writeLength": 6, - "note": "MIXER: in MATRIX, the PHN slider's right end reads +" - }, - { - "address": 1073795562, - "guardLength": 6, - "guardSha256": "84ba946536b14dc94d29f98643355d547c696eaf466a6aa97f9e4d654e1c9296", - "unit": "matrix", - "symbol": "page_levels", - "target": null, - "kind": "jsr", - "writeLength": 6, - "note": "level page: unity MAIN word, real levels and destinations in $37..$3b" - } - ], - "refs": [ - { - "address": 1074156150, - "guardLength": 4, - "guardSha256": "28661e3de100b684f6ca83ab64a3283c276481436356429e3275d0ad1b4a5e7c", - "unit": "matrix", - "symbol": "t8_labels", - "addend": 0, - "note": "AUDIO: TRACK 8 labels, a third (blank) row" - }, - { - "address": 1074156156, - "guardLength": 4, - "guardSha256": "4b9b373d835bb489b6794d443dfce3d18339bf742d1c3cd477a62e50a0e3e712", - "unit": "matrix", - "symbol": "t8_getters", - "addend": 0, - "note": "AUDIO: TRACK 8 checkboxes" - }, - { - "address": 1074156332, - "guardLength": 4, - "guardSha256": "d4ea2d8e557d50f2fb4c255b28ca5bc70d34efdfef6ab6b96276db6ffd389dc0", - "unit": "matrix", - "symbol": "str_outcfg", - "addend": 0, - "note": "AUDIO: the CUE CFG box is titled OUT CFG" - }, - { - "address": 1074156380, - "guardLength": 4, - "guardSha256": "a56725c1818295b0a417cfb8a6a383296dc599132ffc414c4146476e58264e65", - "unit": "matrix", - "symbol": "cue_labels", - "addend": 0, - "note": "AUDIO: CUE CFG labels + MATRIX" - }, - { - "address": 1074156392, - "guardLength": 4, - "guardSha256": "5f8314f70aa32088324642fabf401b6b797ed6c5d3475512975c52aa0b7b787c", - "unit": "matrix", - "symbol": "cue_getters", - "addend": 0, - "note": "AUDIO: CUE CFG checkboxes" - }, - { - "address": 1074156722, - "guardLength": 4, - "guardSha256": "93f7f722f83f2caf2d244df72d6d5204d139b2d4fcd321187556ae8d488c27d4", - "unit": "matrix", - "symbol": "t8_actions", - "addend": 0, - "note": "AUDIO: TRACK 8 YES actions" - }, - { - "address": 1074156736, - "guardLength": 4, - "guardSha256": "78f80a662dca288a1493f16890041afdf6d2d5c6d1065aa5cf87cb70707763c8", - "unit": "matrix", - "symbol": "cue_actions", - "addend": 0, - "note": "AUDIO: CUE CFG YES actions" - }, - { - "address": 1074576852, - "guardLength": 4, - "guardSha256": "e60d8005417ede6ff81d7493e3538f7d5a7a2bae9fa2100830c0cba2f447af27", - "unit": "matrix", - "symbol": "audio_enter", - "addend": 0, - "note": "AUDIO page: three rows" - }, - { - "address": 1074576864, - "guardLength": 4, - "guardSha256": "e094ec5b09c62add1a5833fcfb04cdb0d894f11c49bfc6045532ee37bca1e51f", - "unit": "matrix", - "symbol": "audio_keys", - "addend": 0, - "note": "AUDIO page: keep TRACK 8's cursor on its two rows" - } - ], - "pokes": [ - { - "address": 1074156338, - "guardLength": 2, - "guardSha256": "06b6b4095e023805a3cd41879f15ba5ab2f2e33a6f53d916904496062f324995", - "code": "001d", - "note": "AUDIO: the CUE CFG box grows by one row (22 -> 29 px)" - }, - { - "address": 1074156346, - "guardLength": 2, - "guardSha256": "f09a7a12954169ae595d12d870e69a4c0092003157d72523d626d2a3990241e2", - "code": "0004", - "note": "AUDIO: ... downward: its bottom edge 11 -> 4, so the top stays put" - }, - { - "address": 1073807550, - "guardLength": 2, - "guardSha256": "0b14b394f74bdc2f13a4efc3e6bcec310ba2288bcf64811943a1fe443229d05b", - "code": "7402", - "note": "power-up CS1 check: CUE CFG up to 2 is valid (1 counted 2 as damage)" - }, - { - "address": 1073807556, - "guardLength": 2, - "guardSha256": "1f2e6d00279b4659b6d6f1ad57e12568e4a50f80908af50d1e861001b2be2a3b", - "code": "7002", - "note": "... and above 2 clamps to 2" - } - ], - "tables": [] } ], "composition": { diff --git a/src/engine/assets/resident-dsp.json b/src/engine/assets/resident-dsp.json index 6e43c86b4..909b629be 100644 --- a/src/engine/assets/resident-dsp.json +++ b/src/engine/assets/resident-dsp.json @@ -351,7 +351,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", diff --git a/src/engine/assets/rom-packages.json b/src/engine/assets/rom-packages.json index 01d73d61d..d0d62d321 100644 --- a/src/engine/assets/rom-packages.json +++ b/src/engine/assets/rom-packages.json @@ -415,7 +415,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", diff --git a/src/engine/assets/usb-audio-packages.json b/src/engine/assets/usb-audio-packages.json index 2c60e7dd9..dd20abd22 100644 --- a/src/engine/assets/usb-audio-packages.json +++ b/src/engine/assets/usb-audio-packages.json @@ -569,7 +569,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", diff --git a/src/engine/assets/utility-packages.json b/src/engine/assets/utility-packages.json index 9c64837dc..227db56f8 100644 --- a/src/engine/assets/utility-packages.json +++ b/src/engine/assets/utility-packages.json @@ -19,7 +19,7 @@ "midi-scenes": "0.2.5-experimental", "usb-audio-out-tracks-main-cue": "0.2.0-experimental", "quantizer": "0.1.3-experimental", - "synth": "0.1.2-experimental", + "synth": "0.1.5-experimental", "previewvol": "0.1.2-experimental", "cc-map": "0.1.2-experimental", "sidechain-compressor": "0.1.2-experimental", From 6e69cd4ef334006263a6a55e600a7dac9142045b Mon Sep 17 00:00:00 2001 From: timhastie Date: Mon, 12 Oct 2026 12:39:54 -0700 Subject: [PATCH 5/7] FM Synth 0.1.5: evidence revision 7a5988a (the measured source); import inventory refreshed on main 263aa1b --- sdk/imports/synth-48b89e7.json | 2 +- sdk/octabam/modules/synth/octamod.module.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/imports/synth-48b89e7.json b/sdk/imports/synth-48b89e7.json index 3f402c176..9568fe42e 100644 --- a/sdk/imports/synth-48b89e7.json +++ b/sdk/imports/synth-48b89e7.json @@ -229,7 +229,7 @@ }, { "path": "octamod.module.json", - "sha256": "c116a69c3ea6c3ff568fff823b4a3b329274b0cd1e1feadd7fa6d64ae910bcee", + "sha256": "7a579b7ee03d9656ebd9b735c13c79120a0496c6db5797492d4951eabd657446", "previousSha256": "2650be7edc1198c4864f9018925965cd1ff6b1d2ca2fbf31abae13a5d173a5a6" }, { diff --git a/sdk/octabam/modules/synth/octamod.module.json b/sdk/octabam/modules/synth/octamod.module.json index d7ddc1223..6b5a278d7 100644 --- a/sdk/octabam/modules/synth/octamod.module.json +++ b/sdk/octabam/modules/synth/octamod.module.json @@ -253,7 +253,7 @@ "report": "TESTING.md", "summary": "Emulator qualification of the native FM Synth 0.1.5 image (MAIN OS 37661aee..., built from the author's 48b89e7: the octatrick 2.12 engine, page and chooser, byte-identical in the image to 0.1.4's, and his 2.13 quantizer): the quantizer's MIDI-track gates against a model of the rules (NOTE knob and NOTE locks 409 of 409 over eight SCALE / ROOT settings; CHROMATIC keys 245 of 245 with balanced note-offs and the arpeggiator fed snapped notes; a LIVE RECORDING lock as the author's build), SCALE OFF equal to stock, CHROMATIC on a MIDI track draws and plays, and its added cost per call. Frame-interrupt instruction counts for 1 to 8 FM tracks at VOIC 1, 2 and 4 are 0.1.4's (image 00fa6caa, the same FM objects), with the bounding cards rerun twice on this image, converted at the hardware CF METER calibration (9.44 ns/instruction, 264 MHz). One FM track at VOIC 4 keeps every block, including the first START, inside the 16-sample block (81,459 of 95,779 cycles); eight VOIC 4 tracks do not. Exact memory from the link map: 44,404 module-owned bytes plus the shared 10,460,844-byte sample-RAM platform reserve. Software verification on this image: the module's own native Part smoke (verify.py); two FM tracks pass Part save/reload, project save/reload and a fresh project load in the emulator (the battery-RAM-only restart is the hardware test); the machine-change init; seven LCD captures; the author's odd-address check. The native/browser coverage record is not refreshed: POLY8's chooser bridge stops on this chooser (TESTING.md). A hardware report for this exact image is pending; Modwerk's 0.1.1/0.1.2 owner approvals stay with those versions.", "hardwareStatus": "historical", - "evidenceRevision": "651dc0f2d233eb9dce69fbe7f5f0d064ee33ddf5", + "evidenceRevision": "7a5988a753534e523593f0100d5230429abbf10e", "gates": [ "verify.py: native Part smoke on image 37661aee (eight-track selection, Part and shadow signature, sample slots unchanged, reselection keeps controls, return to FLEX): passed (evidence/native-smoke.json)", "2.13 quantizer on MIDI track 1 of image 37661aee in the emulator: NOTE knob and NOTE locks 409 of 409 against the model of the rules (eight SCALE / ROOT settings, SCALE OFF included); CHROMATIC keys 1..16 at five octave words 245 of 245, note-offs balanced, keys held across SCALE / ROOT changes released, the arpeggiator fed snapped notes; LIVE RECORDING lock as the author's build (TESTING.md)", @@ -404,7 +404,7 @@ "model": "MKI", "testedOn": "TIM: YYYY-MM-DD", "tester": "TIM: credited handle", - "sourceRevision": "651dc0f2d233eb9dce69fbe7f5f0d064ee33ddf5", + "sourceRevision": "7a5988a753534e523593f0100d5230429abbf10e", "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", "summary": "TIM: what was played on the unit and what happened (MAIN OS 37661aee..., the hardware test sheet)", "limitations": [ From 1f1710aa205666e6d2102e7357c810b054a0b0a7 Mon Sep 17 00:00:00 2001 From: timhastie Date: Tue, 13 Oct 2026 07:39:45 -0700 Subject: [PATCH 6/7] FM Synth 0.1.5 README: chords, TRAN and the arpeggiator following the scale, and a per-track exemption, are planned; a drum track is programmed with SCALE OFF until then --- sdk/octabam/modules/synth/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/octabam/modules/synth/README.md b/sdk/octabam/modules/synth/README.md index 86b543187..0d9b99be1 100644 --- a/sdk/octabam/modules/synth/README.md +++ b/sdk/octabam/modules/synth/README.md @@ -55,7 +55,7 @@ Start with one audio track and a simple trig pattern; the tutorial below selects FM SYNTH is also the sixth row of SELECT MACHINE TYPE: press the track key twice quickly, press LEFT on `<< MACHINE`, go DOWN to FM SYNTH and press YES. Turn DEC fully right for HOLD (the index does not decay). A FLEX slot holding a file named `FMSYNTH*.wav` or `SYNTH*.wav` still plays FM as in earlier versions. -**MIDI tracks (0.1.5).** On a MIDI track with a scale on, one NOTE click steps to the next note of the scale in the turn direction (from C3 up in MINOR with ROOT A: D3, E3, F3 …); a note between scale notes, set while the scale was off, goes to the nearest scale note in the turn direction. A NOTE lock (a TRIG key held, or a turn during LIVE RECORDING) does the same. In CHROMATIC mode every key plays a note of the scale: the key’s note plus ROOT, then the nearest scale note (the lower one on a tie); the note-on, the note-off at the key’s release, the arpeggiator fed by the keys and LIVE RECORDING all carry that note. The chord a trig plays is still NOTE plus NOT2–NOT4 in semitones, TRAN transposes in semitones and the arpeggiator’s own notes are stock; use ARP SETUP > KEY to force everything a MIDI track sends into a key. The author’s [quantizer documentation](upstream/quantizer/README.md) (“MIDI tracks (2.13)”) has the details and his measurements. +**MIDI tracks (0.1.5).** On a MIDI track with a scale on, one NOTE click steps to the next note of the scale in the turn direction (from C3 up in MINOR with ROOT A: D3, E3, F3 …); a note between scale notes, set while the scale was off, goes to the nearest scale note in the turn direction. A NOTE lock (a TRIG key held, or a turn during LIVE RECORDING) does the same. In CHROMATIC mode every key plays a note of the scale: the key’s note plus ROOT, then the nearest scale note (the lower one on a tie); the note-on, the note-off at the key’s release, the arpeggiator fed by the keys and LIVE RECORDING all carry that note. The chord a trig plays is still NOTE plus NOT2–NOT4 in semitones, TRAN transposes in semitones and the arpeggiator’s own notes are stock in this version; an option for them to follow the scale, and a per-track switch that exempts a MIDI track from the scale, are planned. Until then a drum track is programmed with SCALE OFF: stored notes are never changed, only the NOTE knob, the locks and the chromatic keys snap while a scale is on. Stock’s ARP SETUP > KEY still forces everything a MIDI track sends into a key, per track. The author’s [quantizer documentation](upstream/quantizer/README.md) (“MIDI tracks (2.13)”) has the details and his measurements. Trigs, note starts and stops come from the instrument’s existing sequencer and panel/MIDI paths. The audio oscillator advances at the audio sample rate, including live playing while transport is stopped; it is not a second sequencer clock. Tempo changes, non-default track speed/length, swing and loop/pattern transitions are not tested in this version. From 8798df492266e1386e125aa27d082024da9af089 Mon Sep 17 00:00:00 2001 From: timhastie Date: Tue, 13 Oct 2026 08:39:32 -0700 Subject: [PATCH 7/7] FM Synth 0.1.5: the author's MKI hardware report for image 37661aee; evidence revision bcf3b7f (the measured source on main c181b76); import inventory refreshed --- docs/VERIFICATION.md | 8 +++---- sdk/imports/synth-48b89e7.json | 12 +++++----- sdk/octabam/modules/synth/README.md | 4 ++-- sdk/octabam/modules/synth/TESTING.md | 11 +++++++-- sdk/octabam/modules/synth/octamod.module.json | 23 ++++++++++--------- src/community/module-changelogs.json | 2 +- 6 files changed, 34 insertions(+), 26 deletions(-) diff --git a/docs/VERIFICATION.md b/docs/VERIFICATION.md index 2bf9863bf..1f4c279fc 100644 --- a/docs/VERIFICATION.md +++ b/docs/VERIFICATION.md @@ -1425,8 +1425,8 @@ memory of 10,505,016 bytes, of which 43,812 are module-owned, and a 352-byte stack bound from the assembled runtime. Two FM tracks with distinct settings pass Part save/reload, project save/reload and a fresh project load in the emulator; the battery-RAM-only restart needs the unit. -The hardware fields are `TIM:` placeholders pending the author's MKI report of -this exact image, so the publication gate fails there. +0.1.4's hardware fields were never filled: 0.1.5 (below) superseded it before +submission, and its report covers the 0.1.5 image only. POLY8 0.2.6's shared-chooser bridge is bound to 0.1.2's chooser claims, which this update replaces: a selection with POLY8 and FM Synth stops at the @@ -1482,8 +1482,8 @@ the larger loader); stack bound 352 B, unchanged. Cycles: 0.1.4's table the rig's spread, and the record's bounds over all seven runs give one VOIC 4 track 81,459 of 95,779 cycles/block. Seven LCD captures (five byte-identical to 0.1.4's; a MIDI track's CHROMATIC page is new). The -hardware fields are `TIM:` placeholders pending the author's MKI report of -this exact image. +hardware fields hold the author's MKI report of this exact image +(timhastie, 2026-10-13). POLY8's bridge still stops on the author's chooser, as for 0.1.4, so `sdk/native-comparisons/synth.json` is not refreshed. Without POLY8 in the pool (a local diff --git a/sdk/imports/synth-48b89e7.json b/sdk/imports/synth-48b89e7.json index 9568fe42e..dd6003689 100644 --- a/sdk/imports/synth-48b89e7.json +++ b/sdk/imports/synth-48b89e7.json @@ -3,7 +3,7 @@ "id": "synth", "version": "0.1.5-experimental", "status": "local-draft-unpublished", - "basedOn": "263aa1b9a8db278b1da96a8a33e07f6f65a94a74", + "basedOn": "c181b76b0155c68db4f0efc558f34cfa3a5c8b58", "originalImport": "synth-949f3be.json", "previousUpdate": "synth-ca13ed8.json", "folder": "sdk/octabam/modules/synth", @@ -42,12 +42,12 @@ }, { "path": "README.md", - "sha256": "581d224a2fae36253ca1ae919aaad5d92897b51a8e22505655ea871fac580829", + "sha256": "039258d085b3462c7bfd43ef9879844d10e265f6c14bf0e2b79e7b568d3d7fe6", "previousSha256": "827248dce8d0577732c416ff2921d8ef34d7383740e341b1b752b4b8b63b4fb5" }, { "path": "TESTING.md", - "sha256": "78c25654ea7ff2802946bd093ddc5d3963fb126c3f2911d0ff0b224d7981c005", + "sha256": "b99be0de58e6f54c05cb5368d080b44edaa4e3fad0c09527bcc44eadea1f1ff0", "previousSha256": "08b83d4b002489695b300efae2b58c4d023085dbc7668671e0e96c3ae688e0ad" }, { @@ -229,7 +229,7 @@ }, { "path": "octamod.module.json", - "sha256": "7a579b7ee03d9656ebd9b735c13c79120a0496c6db5797492d4951eabd657446", + "sha256": "c927dd96262dbaac406f8506aa4eea73ae69489571379d81c83673e17095b858", "previousSha256": "2650be7edc1198c4864f9018925965cd1ff6b1d2ca2fbf31abae13a5d173a5a6" }, { @@ -409,7 +409,7 @@ "media/ot-scale.png" ], "notes": [ - "The author's 48b89e7 is the head of his quant-2.13 line (local tag v2.13; to be pushed to timhastie/octatrick-modules after his hardware test). From v2.12 (ca13ed8) it changes quantizer/quantizer.s, quantizer/manifest.py, quantizer/README.md, LICENSE (a sentence crediting Modwerk's #264 form of the drawer hook), README.md and tools/align_check.py; synth/ is unchanged. The bundled quantizer's quantizer.s, core.s, keys.s, scale.s and README.md are his 48b89e7 files byte for byte.", - "0.1.4-experimental (synth-ca13ed8.json) is still unpublished; this record follows it. The published version before both is 0.1.2-experimental." + "The author's 48b89e7 is the head of his quant-2.13 line (tag v2.13, pushed to timhastie/octatrick-modules on 13 October 2026). From v2.12 (ca13ed8) it changes quantizer/quantizer.s, quantizer/manifest.py, quantizer/README.md, LICENSE (a sentence crediting Modwerk's #264 form of the drawer hook), README.md and tools/align_check.py; synth/ is unchanged. The bundled quantizer's quantizer.s, core.s, keys.s, scale.s and README.md are his 48b89e7 files byte for byte.", + "0.1.4-experimental (synth-ca13ed8.json) was never published on its own; this record follows it, and both are submitted together. The published version before both is 0.1.2-experimental." ] } diff --git a/sdk/octabam/modules/synth/README.md b/sdk/octabam/modules/synth/README.md index 0d9b99be1..70623fc9b 100644 --- a/sdk/octabam/modules/synth/README.md +++ b/sdk/octabam/modules/synth/README.md @@ -19,7 +19,7 @@ Version 0.1.4 moved the module to the author’s 2.12 line (`timhastie/octatrick 0.1.4 kept 0.1.2’s MIDI chromatic drawer fix in the bundled quantizer; since 0.1.5 the quantizer carries the author’s own form of it. **The engine leaves 6 dB of headroom** (the author’s decision): the mono voice and each paraphonic voice are 6.02 dB below his earlier 2.10 builds, so AMP VOL 0 is the reference and VOL up to about +6 stays clean. This replaces 0.1.2’s 18.06 dB `OUTPUT_SHIFT`: at the same mixer settings 0.1.4 plays about 12 dB louder than 0.1.2. Lower track LEVEL or AMP VOL if a mix clips. -Worst-case ColdFire cost, exact memory and two-track Part/project save and reload are measured in the emulator for this build; see TESTING.md. The hardware report for this exact build is pending. +Worst-case ColdFire cost, exact memory and two-track Part/project save and reload are measured in the emulator for this build; see TESTING.md. The author's MKI report for this exact build is in TESTING.md. ## Controls @@ -71,7 +71,7 @@ External MIDI note/CC traffic, Part/project reloads and a power cycle on hardwar ## Tests and measurements -See [TESTING.md](TESTING.md). The native image used for the captures and measurements is built by Modwerk’s own native exporter from this source. The author’s page links byte for byte to the bytes he ratified. Worst-case ColdFire instruction counts and exact memory come from the pinned emulator and the image’s link map. The hardware report fields stay pending until the author’s MKI test of this exact image (MAIN OS `37661aee…`). Release notes are in [release-notes.json](release-notes.json). +See [TESTING.md](TESTING.md). The native image used for the captures and measurements is built by Modwerk’s own native exporter from this source. The author’s page links byte for byte to the bytes he ratified. Worst-case ColdFire instruction counts and exact memory come from the pinned emulator and the image’s link map. The hardware report fields hold the author’s MKI test of this exact image (MAIN OS `37661aee…`, 13 October 2026). Release notes are in [release-notes.json](release-notes.json). ## Authorship and licences diff --git a/sdk/octabam/modules/synth/TESTING.md b/sdk/octabam/modules/synth/TESTING.md index d07c2f257..3a0888c3a 100644 --- a/sdk/octabam/modules/synth/TESTING.md +++ b/sdk/octabam/modules/synth/TESTING.md @@ -2,7 +2,7 @@ 0.1.5 updates FM Synth 0.1.4 (the author's octatrick 2.12 line, still unpublished) to the author's 2.13 quantizer, `timhastie/octatrick-modules` at `48b89e7e5315971bf5c29a3fd86183abe8df48f3` (tag `v2.13`). At that commit the engine, page and chooser files (`synth/`) are the same bytes as at `v2.12` (`ca13ed8`); the quantizer (`quantizer/`), the repository's `LICENSE` and README change. The published version before 0.1.4 is 0.1.2. This file is the report that `tests.qualification` names. Earlier reports are kept unchanged: `evidence/release-0.1.1/TESTING.md` and `evidence/release-0.1.2/TESTING.md`. 0.1.4's measurements are carried below where its objects are byte-identical in this image, and named as such. -**Status.** On the exact 0.1.5 image (MAIN OS `37661aee…`), in the pinned emulator: the quantizer's new MIDI-track behaviour against a model of its rules, SCALE OFF against stock, the quantizer's added cost per call, the module's own native Part smoke, the 8 October persistence and two-instance checks, the 2.12 machine-change init, exact memory, the stack bound, the LCD captures and the author's odd-address check. The frame-interrupt cycle table is 0.1.4's (the FM engine and chooser are the same compiled objects at the same addresses), with its bounding cards rerun on this image. The hardware fields are `TIM:` placeholders until the author reports his MKI test of this exact image (`hardwareStatus` stays `historical` until then). Modwerk's owner approvals are kept as they are: 0.1.1's in `evidence/release-0.1.1/` (`owner-hardware.md`, `build-approval.json`), and 0.1.2's in `sdk/synth-build-approval.json`, copied to `evidence/release-0.1.2/build-approval.json`. They cover only those versions and are not extended to 0.1.4 or 0.1.5. +**Status.** On the exact 0.1.5 image (MAIN OS `37661aee…`), in the pinned emulator: the quantizer's new MIDI-track behaviour against a model of its rules, SCALE OFF against stock, the quantizer's added cost per call, the module's own native Part smoke, the 8 October persistence and two-instance checks, the 2.12 machine-change init, exact memory, the stack bound, the LCD captures and the author's odd-address check. The frame-interrupt cycle table is 0.1.4's (the FM engine and chooser are the same compiled objects at the same addresses), with its bounding cards rerun on this image. The author's MKI report of this exact image is below (`hardwareStatus` `reported`). Modwerk's owner approvals are kept as they are: 0.1.1's in `evidence/release-0.1.1/` (`owner-hardware.md`, `build-approval.json`), and 0.1.2's in `sdk/synth-build-approval.json`, copied to `evidence/release-0.1.2/build-approval.json`. They cover only those versions and are not extended to 0.1.4 or 0.1.5. ## What changed from 0.1.4 @@ -262,7 +262,14 @@ The author tests this image (the update file above, MAIN OS `37661aee…`) with - FM SYNTH still plays and chooses as on 0.1.4; - a power cycle with the card in (the battery-RAM case the emulator cannot test). -His report fills `tests.qualification.hardware`, whose `TIM:` fields are placeholders, and sets `hardwareStatus` to `reported`. Until then this version is not publishable under the qualification gate. 0.1.4's image (`00fa6caa…`) has its own pending hardware sheet; it does not qualify this image. +**Report** (timhastie, MKI, 2026-10-13, MAIN OS `37661aee…`): The author flashed this exact image (FM Synth 0.1.5 alone, MAIN OS 37661aee...) on his Octatrack MKI and reported every item of its test sheet working: on a MIDI track the NOTE knob and NOTE locks stepped through the scale, the CHROMATIC keys followed ROOT, chords and the arpeggiator played as before, CHROMATIC trig mode no longer froze the unit, and SCALE OFF behaved as before; FM SYNTH played as in 0.1.4; after SAVE and a power-cycle with the card in, SCALE, ROOT and the FM SYNTH track were kept and the NOTE knob still followed the scale. + +Limitations: + +- Play time, the MIDI gear used and the number of tracks were not recorded. +- A functional check of the test sheet's items, not a load or stress qualification: the cycle and memory figures in this record are emulator measurements, not hardware times. + +0.1.4's image (`00fa6caa…`) is not covered by this report. Historical hardware: the 2.3–2.9 builds and the first 2.10 build ran on the author's MKI, and his 7–8 October report on that build produced the 2.10 fixes. On 10 October he reported the metronome cut-out and the machine-change request on the 2.11 build (`248d987e…`); 2.12 is his answer to both. Those reports do not qualify this exact image. diff --git a/sdk/octabam/modules/synth/octamod.module.json b/sdk/octabam/modules/synth/octamod.module.json index 6b5a278d7..4cff0a92b 100644 --- a/sdk/octabam/modules/synth/octamod.module.json +++ b/sdk/octabam/modules/synth/octamod.module.json @@ -24,7 +24,7 @@ "label": "Two-operator FM", "family": "Synth machines", "summary": "Two-operator FM with a dedicated track machine, one to four voices, chords and glide; scale and root on MIDI tracks too; octatrick 2.12 engine, 2.13 quantizer.", - "overview": "FM SYNTH provides one to four paraphonic FM voices through the stock AMP and effects. Version 0.1.5 carries Tim Hastie's 2.13 quantizer: with a scale on, a MIDI track's NOTE knob, NOTE locks and CHROMATIC keys follow SCALE and ROOT as the audio tracks do (chord offsets, TRAN, the arpeggiator and SCALE OFF unchanged), and the CHROMATIC drawer fix for #264 is now the author's own. The engine, chooser and page are 0.1.4's, his octatrick 2.12 line: his own FM SYNTH chooser (adapted from Modwerk's, same Part signature) with the machine list he shares with his SY DRUM machine (a row table; SELECT MACHINE TYPE scrolls), a machine change that resets the track's machine parameters to init (locks kept, as in stock), held notes that no longer stop at the metronome click, DEC HOLD at 127, click-free note starts, FM names in the LFO destination list, the held key as gate and the fixes from his MKI test of the first 2.10 build. With FM Synth alone the list keeps its six rows. The engine leaves 6 dB of headroom, the author's choice in place of 0.1.2's 18 dB attenuation. Worst-case ColdFire cost and exact memory are emulator-measured; the hardware report for this exact build is pending.", + "overview": "FM SYNTH provides one to four paraphonic FM voices through the stock AMP and effects. Version 0.1.5 carries Tim Hastie's 2.13 quantizer: with a scale on, a MIDI track's NOTE knob, NOTE locks and CHROMATIC keys follow SCALE and ROOT as the audio tracks do (chord offsets, TRAN, the arpeggiator and SCALE OFF unchanged), and the CHROMATIC drawer fix for #264 is now the author's own. The engine, chooser and page are 0.1.4's, his octatrick 2.12 line: his own FM SYNTH chooser (adapted from Modwerk's, same Part signature) with the machine list he shares with his SY DRUM machine (a row table; SELECT MACHINE TYPE scrolls), a machine change that resets the track's machine parameters to init (locks kept, as in stock), held notes that no longer stop at the metronome click, DEC HOLD at 127, click-free note starts, FM names in the LFO destination list, the held key as gate and the fixes from his MKI test of the first 2.10 build. With FM Synth alone the list keeps its six rows. The engine leaves 6 dB of headroom, the author's choice in place of 0.1.2's 18 dB attenuation. Worst-case ColdFire cost and exact memory are emulator-measured; the author's MKI hardware report of this exact build is in TESTING.md.", "highlights": [ "Dedicated machine chooser entry in SRC SETUP and SELECT MACHINE TYPE; no sample required.", "Pitch, ratio, index, fine tuning, feedback and index decay (HOLD at 127) on SRC.", @@ -208,7 +208,7 @@ ] }, "resources": { - "recorded": "2026-10-12; emulator cycles and exact memory, image 37661aee; hardware report pending", + "recorded": "2026-10-12; emulator cycles and exact memory, image 37661aee; MKI hardware report 2026-10-13", "storage": { "label": "FM code, state and image bytes (bundled quantizer included)", "display": "44,052 bytes plus shared platform reserve", @@ -251,9 +251,9 @@ }, "tests": { "report": "TESTING.md", - "summary": "Emulator qualification of the native FM Synth 0.1.5 image (MAIN OS 37661aee..., built from the author's 48b89e7: the octatrick 2.12 engine, page and chooser, byte-identical in the image to 0.1.4's, and his 2.13 quantizer): the quantizer's MIDI-track gates against a model of the rules (NOTE knob and NOTE locks 409 of 409 over eight SCALE / ROOT settings; CHROMATIC keys 245 of 245 with balanced note-offs and the arpeggiator fed snapped notes; a LIVE RECORDING lock as the author's build), SCALE OFF equal to stock, CHROMATIC on a MIDI track draws and plays, and its added cost per call. Frame-interrupt instruction counts for 1 to 8 FM tracks at VOIC 1, 2 and 4 are 0.1.4's (image 00fa6caa, the same FM objects), with the bounding cards rerun twice on this image, converted at the hardware CF METER calibration (9.44 ns/instruction, 264 MHz). One FM track at VOIC 4 keeps every block, including the first START, inside the 16-sample block (81,459 of 95,779 cycles); eight VOIC 4 tracks do not. Exact memory from the link map: 44,404 module-owned bytes plus the shared 10,460,844-byte sample-RAM platform reserve. Software verification on this image: the module's own native Part smoke (verify.py); two FM tracks pass Part save/reload, project save/reload and a fresh project load in the emulator (the battery-RAM-only restart is the hardware test); the machine-change init; seven LCD captures; the author's odd-address check. The native/browser coverage record is not refreshed: POLY8's chooser bridge stops on this chooser (TESTING.md). A hardware report for this exact image is pending; Modwerk's 0.1.1/0.1.2 owner approvals stay with those versions.", - "hardwareStatus": "historical", - "evidenceRevision": "7a5988a753534e523593f0100d5230429abbf10e", + "summary": "Emulator qualification of the native FM Synth 0.1.5 image (MAIN OS 37661aee..., built from the author's 48b89e7: the octatrick 2.12 engine, page and chooser, byte-identical in the image to 0.1.4's, and his 2.13 quantizer): the quantizer's MIDI-track gates against a model of the rules (NOTE knob and NOTE locks 409 of 409 over eight SCALE / ROOT settings; CHROMATIC keys 245 of 245 with balanced note-offs and the arpeggiator fed snapped notes; a LIVE RECORDING lock as the author's build), SCALE OFF equal to stock, CHROMATIC on a MIDI track draws and plays, and its added cost per call. Frame-interrupt instruction counts for 1 to 8 FM tracks at VOIC 1, 2 and 4 are 0.1.4's (image 00fa6caa, the same FM objects), with the bounding cards rerun twice on this image, converted at the hardware CF METER calibration (9.44 ns/instruction, 264 MHz). One FM track at VOIC 4 keeps every block, including the first START, inside the 16-sample block (81,459 of 95,779 cycles); eight VOIC 4 tracks do not. Exact memory from the link map: 44,404 module-owned bytes plus the shared 10,460,844-byte sample-RAM platform reserve. Software verification on this image: the module's own native Part smoke (verify.py); two FM tracks pass Part save/reload, project save/reload and a fresh project load in the emulator (the battery-RAM-only restart is the hardware test); the machine-change init; seven LCD captures; the author's odd-address check. The native/browser coverage record is not refreshed: POLY8's chooser bridge stops on this chooser (TESTING.md). timhastie's MKI report for this exact image is in TESTING.md; Modwerk's 0.1.1/0.1.2 owner approvals stay with those versions.", + "hardwareStatus": "reported", + "evidenceRevision": "bcf3b7fde7b15dbd7ee744fc2b5b6ea862f4db9f", "gates": [ "verify.py: native Part smoke on image 37661aee (eight-track selection, Part and shadow signature, sample slots unchanged, reselection keeps controls, return to FLEX): passed (evidence/native-smoke.json)", "2.13 quantizer on MIDI track 1 of image 37661aee in the emulator: NOTE knob and NOTE locks 409 of 409 against the model of the rules (eight SCALE / ROOT settings, SCALE OFF included); CHROMATIC keys 1..16 at five octave words 245 of 245, note-offs balanced, keys held across SCALE / ROOT changes released, the arpeggiator fed snapped notes; LIVE RECORDING lock as the author's build (TESTING.md)", @@ -267,7 +267,7 @@ "Exact memory inventory reconciled with the native link map; static stack bound from the assembled runtime", "The author's tools/align_check.py (2.13): no instruction and no detour target at an odd address", "verify-audio.py and verify-regressions.py (Octemu audio and the #264 MIDI selector): not rerun; 0.1.2 results in evidence/release-0.1.2", - "Hardware report for this source and image: pending (TIM placeholders)" + "Hardware report for this source and image: timhastie, MKI, 2026-10-13 (reported; TESTING.md)" ], "qualification": { "moduleVersion": "0.1.5-experimental", @@ -402,13 +402,14 @@ "kind": "functional", "status": "reported", "model": "MKI", - "testedOn": "TIM: YYYY-MM-DD", - "tester": "TIM: credited handle", - "sourceRevision": "7a5988a753534e523593f0100d5230429abbf10e", + "testedOn": "2026-10-13", + "tester": "timhastie", + "sourceRevision": "bcf3b7fde7b15dbd7ee744fc2b5b6ea862f4db9f", "imageSha256": "37661aee03a49f310d47e1447f043489c1bce9430c13824dd49ab93ad835e236", - "summary": "TIM: what was played on the unit and what happened (MAIN OS 37661aee..., the hardware test sheet)", + "summary": "The author flashed this exact image (FM Synth 0.1.5 alone, MAIN OS 37661aee...) on his Octatrack MKI and reported every item of its test sheet working: on a MIDI track the NOTE knob and NOTE locks stepped through the scale, the CHROMATIC keys followed ROOT, chords and the arpeggiator played as before, CHROMATIC trig mode no longer froze the unit, and SCALE OFF behaved as before; FM SYNTH played as in 0.1.4; after SAVE and a power-cycle with the card in, SCALE, ROOT and the FM SYNTH track were kept and the NOTE knob still followed the scale.", "limitations": [ - "TIM: duration, track and voice counts, untested cases" + "Play time, the MIDI gear used and the number of tracks were not recorded.", + "A functional check of the test sheet's items, not a load or stress qualification: the cycle and memory figures in this record are emulator measurements, not hardware times." ], "report": "TESTING.md" }, diff --git a/src/community/module-changelogs.json b/src/community/module-changelogs.json index 674cb2de5..7f7709e70 100644 --- a/src/community/module-changelogs.json +++ b/src/community/module-changelogs.json @@ -382,7 +382,7 @@ "chord offsets (NOT2–NOT4), the arpeggiator, TRAN, MIDI IN and the sequencer are unchanged on MIDI tracks: the quantizer acts where a note is edited or played from the panel (ARP SETUP KEY still forces every note a track sends into a key). SCALE OFF is unchanged.", "the MIDI chromatic freeze is fixed in the author’s own line: since 2.13 his quantizer hooks the CHROMATIC drawer at its common argument push (0x400449be), the form of Modwerk’s 0.1.2 fix for #264, credited to the Modwerk contributors. The bundled quantizer’s sources are now the author’s, byte for byte (its manifest in Modwerk’s stock_guard form); FM Synth images carried the fix already.", "the FM engine, the FM SYNTH chooser and the page are unchanged from 0.1.4: the same compiled objects at the same addresses in the image.", - "Emulator checks on this exact image (TESTING.md): the NOTE knob and locks against a model of the rules (409 of 409), the CHROMATIC keys (245 of 245), SCALE OFF equal to stock, the quantizer’s added cost, exact memory, two-track Part/project save and reload. The author’s MKI report of this exact image is pending; physical reboot survival is not yet verified." + "Emulator checks on this exact image (TESTING.md): the NOTE knob and locks against a model of the rules (409 of 409), the CHROMATIC keys (245 of 245), SCALE OFF equal to stock, the quantizer’s added cost, exact memory, two-track Part/project save and reload. The author’s MKI report of this exact image (13 October 2026): every item of its test sheet worked, including a power-cycle with the card in (TESTING.md)." ] }, {