将分层反馈强化版提升为正式 main,统一协作入口并移除外部构建路径
This commit is contained in:
@@ -1,6 +1,17 @@
|
||||
import fs from 'node:fs';import path from 'node:path';
|
||||
// 原版音效保持不变;路径点新增同音色的短C大调庆祝音。
|
||||
export function buildAudio(root){const out=path.join(root,'audio');fs.mkdirSync(out,{recursive:true});const cues={checkpoint:[[523,.065,0],[784,.38,.05],[392,.25,.065]],correct:[[659,.09,0],[880,.14,.075]],error:[[330,.10,0],[247,.15,.09]],undo:[[587,.07,0],[440,.10,.055]],restart:[[523,.075,0],[392,.09,.065],[523,.11,.14]],complete:[[392,.10,0],[494,.10,.075],[587,.13,.15],[523,.65,.26],[659,.55,.28],[784,.48,.30],[196,.18,0,.28],[261.5,.22,.075,.25],[392,.24,.15,.25]]};for(let n=1;n<=36;n++)cues['move-'+n]=[[523*Math.pow(2,(n-1)/12),.07,0]];
|
||||
for(const [name,tones]of Object.entries(cues)){const rate=22050,length=Math.ceil((Math.max(...tones.map(([,d,t])=>t+d))+.03)*rate),b=Buffer.alloc(44+length*2);b.write('RIFF');b.writeUInt32LE(b.length-8,4);b.write('WAVEfmt ',8);b.writeUInt32LE(16,16);b.writeUInt16LE(1,20);b.writeUInt16LE(1,22);b.writeUInt32LE(rate,24);b.writeUInt32LE(rate*2,28);b.writeUInt16LE(2,32);b.writeUInt16LE(16,34);b.write('data',36);b.writeUInt32LE(length*2,40);for(let i=0;i<length;i++){const t=i/rate;let sample=0;for(const [f,d,offset,weight=1]of tones){const u=t-offset;if(u>=0&&u<d){const themed=name==='checkpoint'||name==='complete',gain=themed?.20:name==='error'?.24:.32;
|
||||
const env=u<.008?u/.008:Math.pow(.0001/gain,(u-.008)/(d-.008));
|
||||
sample+=(Math.sin(2*Math.PI*f*u)+(themed?.08*Math.sin(4*Math.PI*f*u)*Math.exp(-u/.05):0))*gain*env*weight;}}b.writeInt16LE(Math.round(Math.max(-1,Math.min(1,sample))*32767),44+i*2);}fs.writeFileSync(path.join(out,name+'.wav'),b);}}
|
||||
export function buildAudio(root){
|
||||
const out=path.join(root,'audio');fs.mkdirSync(out,{recursive:true});
|
||||
const cues={ui:[[659,.055,0],[784,.06,.025]],hint:[[523,.09,0],[659,.10,.055],[784,.13,.11]],correct:[[659,.11,0]],error:[[330,.09,0],[247,.09,.045]],undo:[[440,.065,0]],restart:[[523,.08,0],[392,.09,.06]],complete:[[261.6256,.96,0,.8],[329.6276,.68,0,.62],[392,.62,0,.48],[523.251,.5,0,.16]],checkpoint:[[523,.14,0]]};
|
||||
for(let n=1;n<=36;n++){const f=523*Math.pow(2,(n-1)/12);cues['move-'+n]=[[f,.09,0]];cues['checkpoint-'+n]=[[f,.14,0]];}
|
||||
for(const [name,tones]of Object.entries(cues)){
|
||||
const rate=22050,length=Math.ceil((Math.max(...tones.map(([,d,t])=>d+t))+.02)*rate),b=Buffer.alloc(44+length*2);
|
||||
b.write('RIFF');b.writeUInt32LE(b.length-8,4);b.write('WAVEfmt ',8);b.writeUInt32LE(16,16);b.writeUInt16LE(1,20);b.writeUInt16LE(1,22);b.writeUInt32LE(rate,24);b.writeUInt32LE(rate*2,28);b.writeUInt16LE(2,32);b.writeUInt16LE(16,34);b.write('data',36);b.writeUInt32LE(length*2,40);
|
||||
for(let i=0;i<length;i++){const t=i/rate;let sample=0;for(const [f,d,offset,weight=1]of tones){const u=t-offset;if(u<0||u>=d)continue;
|
||||
const move=name.startsWith('move-'),node=name.startsWith('checkpoint'),attenuation=1/(1+Math.pow(f/2300,1.35));
|
||||
const gain=(name==='complete'?.20:node?.38:move?.28:.23)*attenuation*weight;
|
||||
const tau=name==='complete'?.11:node?.032:move?.022:.025;
|
||||
const env=(1-Math.exp(-u/(node?.0015:.003)))*Math.exp(-u/tau)*Math.min(1,(d-u)/.008);
|
||||
sample+=(Math.sin(2*Math.PI*f*u)+.10*Math.exp(-u/.012)*Math.sin(4*Math.PI*f*u))*gain*env;
|
||||
}b.writeInt16LE(Math.round(Math.max(-1,Math.min(1,sample))*32767),44+i*2);}fs.writeFileSync(path.join(out,name+'.wav'),b);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user