diff --git a/.github/workflows/rawlease.yml b/.github/workflows/rawlease.yml index 6515889..eb30f5a 100644 --- a/.github/workflows/rawlease.yml +++ b/.github/workflows/rawlease.yml @@ -38,6 +38,6 @@ jobs: path=Path(name.replace('\\','/')) assert hashlib.sha256(path.read_bytes()).hexdigest()==wanted, name assert all(row['passed'] for row in receipt['results']) - assert {row['name'] for row in receipt['results']} == {'winamp','speaker','paired'} + assert {row['name'] for row in receipt['results']} == {'winamp','speaker','paired','fxmerge','rebase'} print('PASS shared sound ownership source receipt') PY diff --git a/docs/BUILD.TXT b/docs/BUILD.TXT index fcc2067..f0e7b86 100644 --- a/docs/BUILD.TXT +++ b/docs/BUILD.TXT @@ -9,7 +9,7 @@ Host UI checks: python libtandywin/TESTS/RUN.PY Host production format, MOD and RAWCLK checks: python libtandywin/AUDIO/HOST/RUNHOST.PY --out -Host shared PSG/PIT ownership checks: +Host shared PSG/PIT ownership and pure FX state merger checks: python libtandywin/AUDIO/HOST/RUNRAW.PY --out Host layout and typed facade checks: python libtandywin/AUDIO/TESTS/FACADE/RUN.PY diff --git a/docs/FX-MERGE.md b/docs/FX-MERGE.md new file mode 100644 index 0000000..510f0df --- /dev/null +++ b/docs/FX-MERGE.md @@ -0,0 +1,44 @@ +# Prepared effects and music + +`sound/FXMERGE.H` and `FXMERGE.C` provide a pure C89 state merger and delta +encoder. They own no timer, hardware, memory allocation, or sound claim. +The application supplies a validated base PSG/PIT state and five effect +slots: tone0, tone1, tone2, noise, and speaker. Periods are 1..1023, +attenuation is 0..15, noise mode is 0..7, and PIT is 0 (off) or 151..65535. +Inactive slots restore the current base state. A new trigger replaces its +lane's prior effect; cursor, retrigger and clock policy belong to the producer. + +Pitched noise sets `borrow2` in slot3. It wins over both music and an effect +on tone2, sets that divider, and mutes tone2's audible output. The reservation +continues during gated/silent rows. When the noise slot becomes inactive, +tone2's current state returns, including a tone effect that continued advancing +while hidden. Fixed-rate noise leaves tone2 alone. An active speaker slot +replaces the current base PIT divisor, including zero for a gate-off row. + +`FXMMerge(out,base,layers)` produces a complete state. `FXMBytes` compares it +with the last emitted state and writes at most 11 bytes into a 16-byte buffer. +Set `initial` only at whole-owner startup. Set `noiseAttack` for an actual +noise retrigger, including restoration of a sounding base noise voice. +Other updates write the noise level without resetting the LFSR. Buffer and +input objects must not overlap; out may alias base. Inputs must be validated. + +An owning application should merge first, publish the resulting timestamped +PSG/PIT writes to its existing transport, then cache the complete state. +Never insert Effects Lab `FXBytes`' initial whole-PSG mute sequence into an +individual song lane. The compiler's trajectory rows can feed the slots +directly. These are synthesized PSG/PIT effects; there is no PCM layer. + +OEMSound Beats Lab supplies the application producer, TFX1 patch import, +per-step transposition and DWA2 song persistence. libwintandy's +`libtandywin/AUDIO/SOURCE/FXMERGE.*` is the matching Windows source mirror. +Existing MIDI banks, driver APIs, and transport ordinals are unchanged. + +Run the standalone check on Windows with the managed GCC launcher: + +``` +python /sound/tests/fxmerge.py --source libtandywin/AUDIO/SOURCE --out +``` + +The merger uses 78 bytes of state/layers on a 16-bit target, plus the +caller's previous state and output buffer. It contains no tables. Host checks +do not establish native code size, IRQ timing, or audible quality. diff --git a/docs/FX-ROLL.TXT b/docs/FX-ROLL.TXT new file mode 100644 index 0000000..88ef199 --- /dev/null +++ b/docs/FX-ROLL.TXT @@ -0,0 +1,36 @@ +Prepared burst clock rolling origin + +The canonical libtandywin/AUDIO/SOURCE RAWCLK adds TCBRebase(DWORD amount), +ordinal37. Ordinals1-36 and frame-mode behavior are unchanged. This is an +owner-task operation for an active, non-ended, error-free burst session. +It also works while user-paused. It does not acquire another owner or timer. + +Return0 means success;1 means wrong owner/mode/state;3 means zero or an +unsafe amount. The amount must not exceed the logical clock, latest published +stamp, last emitted deadline or last literal-burst write stamp. Validation and +all changes occur under one SaveIRQ/RestoreIRQ interval. + +The API advances one DWORD burst origin and subtracts the amount from logical +clock/deadline fields. Published ring entries retain modulo32 physical stamps; +queuedAt subtracts the origin. It never walks the ring, writes sound, pauses, +restarts a timer, or changes the physical epoch/lastStamp. Flush/open reset the +origin. A consumer that never calls ordinal37 retains origin0. + +The producer must preflight a phase-aligned rebase of its own cursor, then +adjust it and any prepared-but-unpublished timestamp immediately after the +successful call, without yielding between those operations. Keep all logical +timestamps in the original safe horizon. This does not automatically turn an +arbitrary producer into an unbounded session. + +Beats uses a14400-state (four-minute) shift once its logical clock reaches +eight minutes.14400 preserves every supported integer tempo's eight-step +phase and the exact rational60Hz PIT clock. Rebase never resets a producer to +tick0, where an initial complete-state/noise attack would be inappropriate. + +HOST/RUNRAW.PY includes actual RAWCLK mocks, a three-virtual-hour physical +DWORD-wrap test (44 rolls), pending FULL retry, partial/full queue, pause, +wrong owner/frame mode, and checks for no added PSG/PIT output at rebases. +Native timing, Win16 ABI and sound recordings are separate application evidence. + +The distinct sound/TIMECLK.C compatibility producer is intentionally unchanged. +The matching OEMSound engine mirror supplies the same current Windows source. diff --git a/libtandywin/AUDIO/HOST/RUNRAW.PY b/libtandywin/AUDIO/HOST/RUNRAW.PY index 6a5398c..3d6a222 100644 --- a/libtandywin/AUDIO/HOST/RUNRAW.PY +++ b/libtandywin/AUDIO/HOST/RUNRAW.PY @@ -27,7 +27,7 @@ if os.name == 'nt': rows = [] for name, source, extra in [('winamp', 'TEST_BURST.C', []), ('speaker', 'TEST_BURST.C', [ '-DCLOCK_SOURCE="../../../sound/TIMECLK.C"', '-DSPEAKER_CLOCK']), - ('paired', 'TEST_PAIR.C', [])]: + ('paired', 'TEST_PAIR.C', []), ('fxmerge', 'TEST_FX.C', []), ('rebase', 'TEST_REBASE.C', [])]: exe = out / (name + ('.exe' if os.name == 'nt' else '')) command = cc + ['-x', 'c', '-std=c89', '-Wall', '-Wextra', '-I', str(HERE/'STUB'), *extra, @@ -51,10 +51,10 @@ for name, source, extra in [('winamp', 'TEST_BURST.C', []), ('speaker', 'TEST_BU 'scope': 'Actual RAWCLK source, stub Win16/hardware; native IRQ timing separate', 'source_sha256': { path.resolve().relative_to(HERE.parents[2].resolve()).as_posix(): hashlib.sha256(path.read_bytes()).hexdigest() - for path in (HERE/'TEST_BURST.C', HERE/'TEST_PAIR.C', HERE/'STUB/windows.h', + for path in (HERE/'TEST_BURST.C', HERE/'TEST_PAIR.C', HERE/'TEST_FX.C', HERE/'TEST_REBASE.C', HERE/'STUB/windows.h', *(HERE/'../SOURCE'/name for name in ('TIMECLK.C', 'TIMECLK.H', 'PSGOWN.H', 'TCBURST.H', - 'TCXBASS.H', 'PITLINK.H', 'WINHOOK.ASM', 'TIMECLK.DEF')), + 'TCXBASS.H', 'PITLINK.H', 'WINHOOK.ASM', 'TIMECLK.DEF', 'FXMERGE.C', 'FXMERGE.H')), *(HERE/'../../../sound'/name for name in ('TIMECLK.C', 'TIMECLK.H', 'PSGOWN.H', 'TCBURST.H', 'TCXBASS.H', 'TCNOISE.H', 'PITLINK.H')))}, diff --git a/libtandywin/AUDIO/HOST/TEST_FX.C b/libtandywin/AUDIO/HOST/TEST_FX.C new file mode 100644 index 0000000..f6714e8 --- /dev/null +++ b/libtandywin/AUDIO/HOST/TEST_FX.C @@ -0,0 +1,30 @@ +#include "../SOURCE/FXMERGE.C" +/* SPDX-License-Identifier: MIT */ +#include +#include +#include "../SOURCE/FXMERGE.H" +static unsigned checks; +#define CK(x) do{++checks;if(!(x)){printf("FAIL %u %s\n",(unsigned)__LINE__,#x);return 1;}}while(0) +int main(void) +{ + FXMSTATE base,out,old;FXMLAYER fx[5];unsigned i,n;unsigned char bytes[16]; + memset(&base,0,sizeof(base));memset(fx,0,sizeof(fx)); + for(i=0;i<3;++i){base.period[i]=100+i;base.level[i]=i+2;} + base.noise=4;base.level[3]=6;base.pit=21000; + FXMMerge(&out,&base,fx);CK(!memcmp(&out,&base,sizeof(out))); + fx[0].active=1;fx[0].period=400;fx[0].level=1; + fx[2].active=1;fx[2].period=500;fx[2].level=3; + fx[3].active=fx[3].borrow2=1;fx[3].period=600;fx[3].level=2;fx[3].noise=7; + fx[4].active=1;fx[4].pit=1234;FXMMerge(&out,&base,fx); + CK(out.period[0]==400&&out.level[0]==1&&out.period[1]==101&&out.level[1]==3); + CK(out.period[2]==600&&out.level[2]==15&&out.level[3]==2&&out.noise==7&&out.pit==1234); + fx[3].level=15;FXMMerge(&out,&base,fx);CK(out.level[2]==15); + fx[3].active=fx[4].active=0;FXMMerge(&out,&base,fx); + CK(out.period[2]==500&&out.level[2]==3&&out.noise==4&&out.pit==21000); + old=out;CK(!FXMBytes(&out,&old,0,0,bytes)); + out.level[3]=4;n=FXMBytes(&out,&old,0,0,bytes);CK(n==1&&bytes[0]==0xf4); + n=FXMBytes(&out,&old,0,1,bytes);CK(n==2&&bytes[0]==0xe4&&bytes[1]==0xf4); + n=FXMBytes(&out,&old,1,0,bytes);CK(n==11); + for(i=0;i<3;++i)CK(bytes[i*3+2]!=(0x9f|(i<<5))); + printf("PASS shared FX merger checks=%u\n",checks);return 0; +} diff --git a/libtandywin/AUDIO/HOST/TEST_REBASE.C b/libtandywin/AUDIO/HOST/TEST_REBASE.C new file mode 100644 index 0000000..6d1481e --- /dev/null +++ b/libtandywin/AUDIO/HOST/TEST_REBASE.C @@ -0,0 +1,48 @@ +/* Actual RAWCLK, three virtual hours, physical DWORD wraps and no extra I/O. */ +#define SPEAKER_CLOCK 1 +#define main BurstBaselineMain +#include "TEST_BURST.C" +#undef main +int main(void) +{ + TCXBURST b;unsigned long i;DWORD period=286363200UL,physical,logical,oldBase; + WORD n,rebases=0,oldTrace,oldNoise,oldPit,oldWrites; + C(BurstBaselineMain()==0);C(TCBRebase(1)==1); + C(TCXOpen(1)==0);now=0;memset(&b,0,sizeof(b));b.count=1;b.data[0]=0x90;b.pitDivisor=1000; + C(TCXPush(&b)==0);b.at=119318UL;b.count=0;b.pitDivisor=1001;C(TCXPush(&b)==0); + C(TCStart()==0);tick(0);C(TCBRebase(0)==3); + for(i=1;i<=108000UL;++i){ + tick((DWORD)(i*119318UL));C(!error&&count==0);C(bassLast==1000+(i&1)); + b.at=TCClock()+119318UL;b.pitDivisor=(WORD)(1000+((i+1)&1));C(TCXPush(&b)==0); + if(TCClock()>=period*2UL){ + physical=bursts[head].at;logical=queuedAt(head);oldBase=burstBase; + oldTrace=traceCount;oldWrites=writes;oldNoise=noiseWrites;oldPit=pitDivisor;n=count; + C(TCBRebase(clockCycles+1)==3);C(burstBase==oldBase); + task=2;C(TCBRebase(period)==1);task=1; + C(TCBRebase(period)==0);++rebases; + C(burstBase==(DWORD)(oldBase+period)&&bursts[head].at==physical); + C(queuedAt(head)==logical-period&&count==n&&traceCount==oldTrace&&writes==oldWrites); + C(noiseWrites==oldNoise&&pitDivisor==oldPit&&hi&&owner); + } + } + C(rebases>=40&&burstBase!=0);C(TCPause()==0);oldTrace=traceCount;oldWrites=writes; + physical=bursts[head].at;logical=queuedAt(head);C(TCBRebase(1000)==0); + C(!hi&&!pitDivisor&&traceCount==oldTrace&&writes==oldWrites&&bursts[head].at==physical&&queuedAt(head)==logical-1000); + C(TCUnpause()==0);C(TCClose()==0);C(!owner&&!sound&&!pitToken&&!unsafe); + C(TCBOpen()==0&&burstBase==0);C(TCClose()==0); + /* A pending FULL retry and the published ring retain exact deadlines. */ + C(TCBOpen()==0);memset(&b,0,sizeof(b));b.count=1;b.data[0]=0xf8; + for(i=0;iperiod[i]=fx[i].period;out->level[i]=fx[i].level; + } + if(fx[3].active){ + out->noise=fx[3].noise;out->level[3]=fx[3].level; + if(fx[3].borrow2){out->period[2]=fx[3].period;out->level[2]=15;} + } + if(fx[4].active)out->pit=fx[4].pit; +} +unsigned FXMBytes(const FXMSTATE *s,const FXMSTATE *old,unsigned initial, + unsigned noiseAttack,unsigned char *b) +{ + unsigned i,n=0,p; + for(i=0;i<3;++i){p=s->period[i]; + if(initial||p!=old->period[i]){ + b[n++]=(unsigned char)(0x80|(i<<5)|(p&15)); + b[n++]=(unsigned char)(p>>4); + } + if(initial||s->level[i]!=old->level[i]) + b[n++]=(unsigned char)(0x90|(i<<5)|s->level[i]); + } + if(initial||noiseAttack||s->noise!=old->noise) + b[n++]=(unsigned char)(0xe0|s->noise); + if(initial||s->level[3]!=old->level[3]) + b[n++]=(unsigned char)(0xf0|s->level[3]); + return n; +} diff --git a/libtandywin/AUDIO/SOURCE/FXMERGE.H b/libtandywin/AUDIO/SOURCE/FXMERGE.H new file mode 100644 index 0000000..d4f6470 --- /dev/null +++ b/libtandywin/AUDIO/SOURCE/FXMERGE.H @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: MIT. Pure C89 PSG/PIT state merger. No IO/clock. */ +#ifndef FXMERGE_H +#define FXMERGE_H +typedef struct { + unsigned period[3],level[4],noise,pit; +} FXMSTATE; +/* Slots 0..2 are tones, 3 noise, 4 speaker. Inactive slots restore base. + borrow2 is valid only for noise: it owns tone2's divider AND mutes tone2. + It wins over slot2 until inactive, even during a gated/silent noise row. + A speaker slot replaces base.pit. Retrigger/cursor ownership is caller's. */ +typedef struct { + unsigned active,period,level,noise,pit,borrow2; +} FXMLAYER; +void FXMMerge(FXMSTATE *,const FXMSTATE *,const FXMLAYER *); +/* Delta encoder: at most 11 bytes. Noise latch written only on mode change, + explicit noise attack, or initial state; level-only rows never reset LFSR. + initial is a whole-owner start, never a per-effect global mute prefix. */ +unsigned FXMBytes(const FXMSTATE *,const FXMSTATE *,unsigned,unsigned, + unsigned char *); +#endif diff --git a/libtandywin/AUDIO/SOURCE/TCBURST.H b/libtandywin/AUDIO/SOURCE/TCBURST.H index d70deb9..bf67e84 100644 --- a/libtandywin/AUDIO/SOURCE/TCBURST.H +++ b/libtandywin/AUDIO/SOURCE/TCBURST.H @@ -14,5 +14,7 @@ WORD TCAPI TCBGetCaps(WORD,TCBINFO FAR *,WORD); WORD TCAPI TCBOpen(void); WORD TCAPI TCBPush(TCBURST FAR *); WORD TCAPI TCBEnd(void); +/* Optional ordinal37: shift active burst clock/deadlines without sound writes. */ +WORD TCAPI TCBRebase(DWORD); /* 0 success,1 ownership/lifecycle,2 FULL,3 invalid,4 spacing/horizon. */ #endif diff --git a/libtandywin/AUDIO/SOURCE/TIMECLK.C b/libtandywin/AUDIO/SOURCE/TIMECLK.C index 02ef19d..2331b70 100644 --- a/libtandywin/AUDIO/SOURCE/TIMECLK.C +++ b/libtandywin/AUDIO/SOURCE/TIMECLK.C @@ -38,7 +38,7 @@ HTASK owner=0;static HTASK probeOwner; WORD timer=0,probeTimer=0,head=0,tail=0,count=0,end=0,active=0,finished=0,error=0; static DWORD epoch,published,lastDue,lastStamp,clockCycles; static WORD burstMode,burstLatch,burstRegs[8],burstPublished; -static DWORD burstLastWrite; +static DWORD burstLastWrite,burstBase; static WORD noiseWrites,burstHash,burstWriteCount; static WORD bassQueue[TC_CAP],bassMode,bassEnabled,bassLast,bassSuspended; static WORD bassStatus=TCX_UNAVAILABLE; @@ -95,7 +95,7 @@ static void bassRelease(void){ bassLease.token=0;bassGet=0;bassMode=bassEnabled=bassLast=bassSuspended=0;bassStatus=TCX_UNAVAILABLE; pairMode=pairUsingFold=0; } -static DWORD queuedAt(WORD i){return burstMode?bursts[i].at:queue[i].at;} +static DWORD queuedAt(WORD i){return burstMode?bursts[i].at-burstBase:queue[i].at;} static WORD queueMask(void){return burstMode?TCB_CAPACITY-1:TC_CAP-1;} static WORD wideClock,userPaused; static WORD due(DWORD at,DWORD now){return wideClock?(LONG)(now-at)>=0:now>=at;} @@ -197,7 +197,7 @@ WORD TCAPI TCOpen(void){WORD flags; if(OpenSound()<0)return 1; owner=GetCurrentTask();if(!owner){CloseSound();return 1;}StopSound(); if(rawClaim()){CloseSound();owner=0;return 1;} - flags=SaveIRQ();head=tail=count=end=active=finished=error=0;calls=sent=maxBatch=maxLate=maxGap=overflows=writes=underruns=0;published=lastDue=0;lastValid=restorePending=wideClock=userPaused=burstMode=burstPublished=noiseWrites=0; + flags=SaveIRQ();head=tail=count=end=active=finished=error=0;calls=sent=maxBatch=maxLate=maxGap=overflows=writes=underruns=0;published=lastDue=burstBase=0;lastValid=restorePending=wideClock=userPaused=burstMode=burstPublished=noiseWrites=0; bassMode=bassEnabled=bassLast=bassSuspended=pairMode=pairUsingFold=0; bassStatus=TCX_UNAVAILABLE;mute();RestoreIRQ(flags);return 0; } @@ -270,7 +270,7 @@ WORD TCAPI TCUnpause(void){WORD r;if(!owns()||!userPaused)return 1;userPaused=0; WORD TCAPI TCFlush(void){WORD flags,n;if(!owns())return 1; if(timer&&HiMetric(9)&&HiStop())return 1; for(n=0;timer&&HiStatus()&&n<4000;n++)Yield();if(timer&&HiStatus())return 1; - flags=SaveIRQ();active=0;userPaused=0;restorePending=lastValid=0;head=tail=count=end=finished=error=0;published=lastDue=clockCycles=lastStamp=burstLastWrite=0;burstPublished=0;epoch=0xffffffffUL;sent=0;bassLast=pairUsingFold=0;mute();RestoreIRQ(flags);return 0;} + flags=SaveIRQ();active=0;userPaused=0;restorePending=lastValid=0;head=tail=count=end=finished=error=0;published=lastDue=clockCycles=lastStamp=burstLastWrite=burstBase=0;burstPublished=0;epoch=0xffffffffUL;sent=0;bassLast=pairUsingFold=0;mute();RestoreIRQ(flags);return 0;} WORD TCAPI TCRearm(void){WORD r;if(!owns()||!timer||active||!count)return 1; active=1;HiPrepare(nextInterval(0));epoch=0xffffffffUL;lastStamp=clockCycles=0;r=HiRestart();if(r)active=0;return r;} @@ -313,7 +313,7 @@ static WORD burstPush(TCBURST FAR *source,WORD divisor,WORD extended){WORD f,t,i if(at>2147483647UL||((count||active||sent)&&atcount;bassQueue[t]=divisor; + bursts[t].at=at+burstBase;bursts[t].count=source->count;bassQueue[t]=divisor; for(i=0;icount;i++)bursts[t].data[i]=source->data[i]; f=SaveIRQ();if(error||end||(active&&atclockCycles||amount>published||amount>lastDue|| + (burstPublished&&amount>burstLastWrite)){RestoreIRQ(f);return 3;} + burstBase+=amount;clockCycles-=amount;published-=amount;lastDue-=amount; + if(burstPublished)burstLastWrite-=amount; + RestoreIRQ(f);return 0; +} WORD TCAPI TCBPush(TCBURST FAR *s){return burstPush(s,0,0);} WORD TCAPI TCXGetCaps(WORD version,TCXINFO FAR *out,WORD bytes){WORD status; diff --git a/libtandywin/AUDIO/SOURCE/TIMECLK.DEF b/libtandywin/AUDIO/SOURCE/TIMECLK.DEF index 75774f7..3a27cbc 100644 --- a/libtandywin/AUDIO/SOURCE/TIMECLK.DEF +++ b/libtandywin/AUDIO/SOURCE/TIMECLK.DEF @@ -38,6 +38,7 @@ EXPORTS TCXPairCaps @34 TCXPairPush @35 TCXPairLive @36 + TCBRebase @37 IMPORTS CreateSystemTimer=SYSTEM.2 KillSystemTimer=SYSTEM.3 diff --git a/libtandywin/AUDIO/TESTS/RAWLEASE.JSON b/libtandywin/AUDIO/TESTS/RAWLEASE.JSON index 1838290..587092f 100644 --- a/libtandywin/AUDIO/TESTS/RAWLEASE.JSON +++ b/libtandywin/AUDIO/TESTS/RAWLEASE.JSON @@ -3,15 +3,19 @@ "source_sha256": { "libtandywin/AUDIO/HOST/TEST_BURST.C": "7cd5743eedfb8f46ba30943e26a42c4da25db81c95c93b6a62086fb90c9a3d29", "libtandywin/AUDIO/HOST/TEST_PAIR.C": "bb6863e0afc50e47c24aa529d58e03e8b78093a53de4302028ed25de14a34e8b", + "libtandywin/AUDIO/HOST/TEST_FX.C": "b4bff2351132d5ef2c20647c8715dd23aa4c1cc832604e240d99125dba5b4289", + "libtandywin/AUDIO/HOST/TEST_REBASE.C": "b4e42a14dce63e4cc58bc659b967adf549abc2cb427a5c069e5808cfabadb76d", "libtandywin/AUDIO/HOST/STUB/windows.h": "11942ac291c34cb921275ea27111ae03bb9796fcea55b171eeaaf5f4c76f2906", - "libtandywin/AUDIO/SOURCE/TIMECLK.C": "f3bdd89bb83aa9ed612b3daa6d054cd667996d4ceebac2f1a9d3bc188f8ca369", + "libtandywin/AUDIO/SOURCE/TIMECLK.C": "27707d5cff137b247c9ab8a8b3002069a0a9ccd7b446b43f7bb426e7e5c4b6e7", "libtandywin/AUDIO/SOURCE/TIMECLK.H": "478d911587645d6d2acb2965dc7b78e8b6e3b67373e8a8a4933395398ff9d193", "libtandywin/AUDIO/SOURCE/PSGOWN.H": "aaae2ff214e7737be79e7284a382648155cd4dee20ee168652b10b08b8cd31d5", - "libtandywin/AUDIO/SOURCE/TCBURST.H": "7249a5885cfde8afe0c3c405ac129d92ee3b654a65923f49ceff8f295bb57776", + "libtandywin/AUDIO/SOURCE/TCBURST.H": "d29b49a70b213e980b548e45bce2f72e8f4ab7c24d8ba28a6ed00264c0c3144e", "libtandywin/AUDIO/SOURCE/TCXBASS.H": "8ebcc407a7af1fc3166c92de5c3140fb9c82dfdf112573050bcd42b5cac274fe", "libtandywin/AUDIO/SOURCE/PITLINK.H": "b6a9d41c5765b1b166caf0ff1aa7d83edfd2662a7b8f46d802bf08367b5cb452", "libtandywin/AUDIO/SOURCE/WINHOOK.ASM": "6e44cbcb0ce6eb2a29f039aaebd5dc6534ba76d5f1648780c5010b2ae94a3520", - "libtandywin/AUDIO/SOURCE/TIMECLK.DEF": "a9fb0ab6e1afd0547eebd1e152546c999bd6f782ac0b38bd3a860567d1284271", + "libtandywin/AUDIO/SOURCE/TIMECLK.DEF": "5b2036d25bf0b93812593fa2799d63e6a8fc72e9f7c497bb2bc55c0bf47eb9bd", + "libtandywin/AUDIO/SOURCE/FXMERGE.C": "cd4bb236a5bf935027b321dbc4bed399edcfe2e4a22acc8ffb8c5c931e4cd5f1", + "libtandywin/AUDIO/SOURCE/FXMERGE.H": "a9a8e0d58e2b2c4c7f7319aca31fe84d37380fffd9460f292c54a95be247ebcd", "sound/TIMECLK.C": "f8ce32ebef5e1d51a6ccfe44b2650b47fd4d16e2b6904f29510a32fbe1d279fe", "sound/TIMECLK.H": "478d911587645d6d2acb2965dc7b78e8b6e3b67373e8a8a4933395398ff9d193", "sound/PSGOWN.H": "aaae2ff214e7737be79e7284a382648155cd4dee20ee168652b10b08b8cd31d5", @@ -33,16 +37,16 @@ "-Wall", "-Wextra", "-I", - "C:\\Users\\chad\\Documents\\Codex\\2026-10-24\\task-4\\unified-bank\\libwintandy\\libtandywin\\AUDIO\\HOST\\STUB", - "C:\\Users\\chad\\Documents\\Codex\\2026-10-24\\task-4\\unified-bank\\libwintandy\\libtandywin\\AUDIO\\HOST\\TEST_BURST.C", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\STUB", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\TEST_BURST.C", "-o", - "C:\\Users\\chad\\Documents\\Codex\\2026-10-24\\task-4\\unified-bank-qa\\RAWCI1\\winamp.exe" + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\host-raw-roll-final\\winamp.exe" ], "compile": 0, "passed": true, "exit": 0, "output": "actual RAWCLK burst: 3298 checks, 0 failures, unsafe IRQ calls=0", - "executable_sha256": "d09efd34409c58a6585e0ea10743da0c9f3290a8a238a9fd3d4a11c2108067b2" + "executable_sha256": "bb98382e132453cb0d6b6615dc35b649ee19b8f187245b2844a4ff5b0677e342" }, { "name": "speaker", @@ -56,18 +60,18 @@ "-Wall", "-Wextra", "-I", - "C:\\Users\\chad\\Documents\\Codex\\2026-10-24\\task-4\\unified-bank\\libwintandy\\libtandywin\\AUDIO\\HOST\\STUB", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\STUB", "-DCLOCK_SOURCE=\"../../../sound/TIMECLK.C\"", "-DSPEAKER_CLOCK", - "C:\\Users\\chad\\Documents\\Codex\\2026-10-24\\task-4\\unified-bank\\libwintandy\\libtandywin\\AUDIO\\HOST\\TEST_BURST.C", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\TEST_BURST.C", "-o", - "C:\\Users\\chad\\Documents\\Codex\\2026-10-24\\task-4\\unified-bank-qa\\RAWCI1\\speaker.exe" + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\host-raw-roll-final\\speaker.exe" ], "compile": 0, "passed": true, "exit": 0, "output": "actual RAWCLK burst: 3311 checks, 0 failures, unsafe IRQ calls=0", - "executable_sha256": "a269feded202e0f1adadb0f41537ef75ccc921ff945b83bd7b5a7b68a7cf1e97" + "executable_sha256": "362bb1ad900b3674797036b55f3106f68524f29409f9f2caedf885fad209f3cf" }, { "name": "paired", @@ -81,16 +85,62 @@ "-Wall", "-Wextra", "-I", - "C:\\Users\\chad\\Documents\\Codex\\2026-10-24\\task-4\\unified-bank\\libwintandy\\libtandywin\\AUDIO\\HOST\\STUB", - "C:\\Users\\chad\\Documents\\Codex\\2026-10-24\\task-4\\unified-bank\\libwintandy\\libtandywin\\AUDIO\\HOST\\TEST_PAIR.C", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\STUB", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\TEST_PAIR.C", "-o", - "C:\\Users\\chad\\Documents\\Codex\\2026-10-24\\task-4\\unified-bank-qa\\RAWCI1\\paired.exe" + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\host-raw-roll-final\\paired.exe" ], "compile": 0, "passed": true, "exit": 0, "output": "actual RAWCLK burst: 3311 checks, 0 failures, unsafe IRQ calls=0\npaired RAWCLK: 3435 total checks, 0 failures, unsafe IRQ calls=0", - "executable_sha256": "b75e20c4ac15afbdd0f4a0c54018a3d47a74d5ee2150f227095031cc60676df7" + "executable_sha256": "e2e9b33495b7a91d330ea68c96fa638a9e4b6c8335cd55c49f3ed7c1c6696dc8" + }, + { + "name": "fxmerge", + "command": [ + "C:\\Users\\chad\\Miniconda3\\python.exe", + "C:\\Users\\chad\\Documents\\Codex\\toolchains\\gcc-ucrt64.py", + "--", + "-x", + "c", + "-std=c89", + "-Wall", + "-Wextra", + "-I", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\STUB", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\TEST_FX.C", + "-o", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\host-raw-roll-final\\fxmerge.exe" + ], + "compile": 0, + "passed": true, + "exit": 0, + "output": "PASS shared FX merger checks=12", + "executable_sha256": "f211819ff40611f824c2363d74c5a42203e2c7f5bc80e9d617c2b5e5eab221ab" + }, + { + "name": "rebase", + "command": [ + "C:\\Users\\chad\\Miniconda3\\python.exe", + "C:\\Users\\chad\\Documents\\Codex\\toolchains\\gcc-ucrt64.py", + "--", + "-x", + "c", + "-std=c89", + "-Wall", + "-Wextra", + "-I", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\STUB", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\libwintandy\\libtandywin\\AUDIO\\HOST\\TEST_REBASE.C", + "-o", + "C:\\Users\\chad\\Documents\\Codex\\2026-10-27\\task-11\\host-raw-roll-final\\rebase.exe" + ], + "compile": 0, + "passed": true, + "exit": 0, + "output": "actual RAWCLK burst: 3312 checks, 0 failures, unsafe IRQ calls=0\nPASS rolling RAWCLK three-hour/wrap/ownership checks=328160 failures=0 rebases=44", + "executable_sha256": "d7cf031da0a96ef6c78a3c21ef574f228451bd318c120c754343a9e3a06eab8e" } ] }