Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
1234567891011121314151617181920212223import {test} from 'node:test';import assert from 'node:assert/strict';import {mkdtemp,readFile,writeFile,rm,symlink,cp} from 'node:fs/promises';import {tmpdir} from 'node:os';import {join} from 'node:path';import {pathToFileURL} from 'node:url';import {create,run,renderScore} from '../src/media/sound.mjs';import {readWavMono} from '../media/sound/pop/wav.mjs';const fixture=async t=>{const root=await mkdtemp(join(tmpdir(),'easel-sound-'));t.after(()=>rm(root,{recursive:true,force:true}));return root;};test('phrase renders actual bounded PCM, editable source and matching measured waveform',async t=>{const root=await fixture(t);const result=await create({root});assert.equal(result.preview.mime,'audio/wav');const wav=await readFile(join(root,'sound.wav'));assert.equal(wav.toString('ascii',0,4),'RIFF');assert.equal(wav.readUInt16LE(20),1);const{samples,sampleRate}=readWavMono(join(root,'sound.wav'));assert.equal(samples.length/sampleRate,4.8);assert.ok(result.analysis.rms>.02);assert.ok(result.analysis.peak<=.95);assert.equal(samples.at(-1),0);assert.equal(result.analysis.waveform.length,256);const previous=Buffer.from(wav);await run({root,action:'render'});assert.deepEqual(await readFile(join(root,'sound.wav')),previous);});test('pitch changes real waveform and effects change PCM; silence stays silent',()=>{const score={bpm:120,beats:2,notes:[{at:0,midi:69,duration:1,gain:.3}]};const a=renderScore(score).wav,b=renderScore({...score,notes:[{at:0,midi:81,duration:1,gain:.3}]}).wav;assert.notDeepEqual(a,b);assert.notDeepEqual(a,renderScore({...score,effects:[{type:'bitcrush',bits:3}]}).wav);const silent=renderScore({...score,notes:[]}).wav;assert.ok(silent.subarray(44).every(x=>x===0));// An octave doubles zero crossings over a sustained fundamental-rich section.const crossings=wav=>{let n=0;for(let i=2446;i<14400;i+=2)if(wav.readInt16LE(i-2)<=0&&wav.readInt16LE(i)>0)n++;return n;};assert.ok(crossings(b)>crossings(a)*1.8);});test('rhythm uses requested onsets, loops and analyze preserves exact WAV',async t=>{const root=await fixture(t);await create({root});await run({root,action:'rhythm',input:{hits:3,pulses:8,midi:55}});const score=JSON.parse(await readFile(join(root,'score.json')));assert.equal(score.notes.length,3);assert.equal(score.loop,true);const before=await readFile(join(root,'sound.wav'));await run({root,action:'analyze'});assert.deepEqual(await readFile(join(root,'sound.wav')),before);});test('invalid renders preserve previous audio and reject pathological scores',async t=>{const root=await fixture(t);await create({root});const before=await readFile(join(root,'sound.wav'));for(const score of [{notes:[],beats:64,bpm:30},{notes:[{at:0,midi:Infinity}]},{notes:[],effects:[{type:'exec'}]}])await assert.rejects(run({root,action:'set_score',input:{score}}));assert.deepEqual(await readFile(join(root,'sound.wav')),before);});test('symlink outputs cannot escape artifact root',async t=>{const root=await fixture(t),outside=await fixture(t);await writeFile(join(outside,'target'),'untouched');await symlink(join(outside,'target'),join(root,'sound.wav'));await assert.rejects(create({root}),/Unsafe artifact path/);assert.equal(await readFile(join(outside,'target'),'utf8'),'untouched');});test('portable copied toolkit runs without monorepo or dependencies',async t=>{const root=await fixture(t);await cp(new URL('../media/sound/',import.meta.url),join(root,'media/sound'),{recursive:true});await cp(new URL('../src/media/',import.meta.url),join(root,'src/media'),{recursive:true});const isolated=await import(pathToFileURL(join(root,'src/media/sound.mjs')).href);const result=await isolated.create({root:join(root,'artifact')});assert.ok(result.analysis.rms>0);});test('malformed WAV cannot allocate from forged data lengths',async t=>{const root=await fixture(t);await create({root});const wav=await readFile(join(root,'sound.wav'));wav.writeUInt32LE(0xfffffffe,40);await writeFile(join(root,'sound.wav'),wav);await assert.rejects(run({root,action:'analyze'}),/Expected an aesel mono PCM WAV/);});test('loops are whole beats and every render reports the shared grid it plays on',async t=>{const root=await fixture(t);const result=await create({root});assert.deepEqual(result.grid,{bpm:100,beats:8,beatSec:.6,loopSec:4.8,bars:2});assert.match(result.summary,/8 beats \(2 bars\) @ 100bpm/); await assert.rejects(run({root,action:'set_score',input:{score:{bpm:120,beats:2.5,loop:true,notes:[]}}}),/whole number of beats/); const phrase=await run({root,action:'set_score',input:{score:{bpm:120,beats:2.5,loop:false,notes:[]}}});assert.equal(phrase.grid.bars,null); await run({root,action:'set_score',input:{score:{bpm:120,beats:4,notes:[]}}});const loop=await run({root,action:'rhythm',input:{hits:4,pulses:8}});assert.equal(loop.grid.loopSec,2);assert.equal(loop.grid.bars,1);assert.equal(JSON.parse(await readFile(join(root,'score.json'))).notes[1].at,1);});