PageSourceSearch

https://building-audio-apps-with-js.vercel.app/assets/md-X5Ery80N.js

js building-audio-apps-with-js.vercel.app collected 2026-10-03 06:19:56 UTC 13,761 bytes, 97 lines download raw bytes

1import{_ as se}from"./LanguageSwitcher-DtvM7PCS.js";import{r as U,O as Z,d as ie,z as ce,L as fe,f as K,o as E,i as T,T as R,g as n,t as x,ag as V,F as ee,b as le,aj as Y,aT as pe,ak as te,w as oe,e as H,v as de,x as ve}from"./modules/vue-Bw_6t8Ev.js";import{ak as ae,I as re,_ as ge,aj as he}from"./index-BgHgLzyu.js";import{W as me}from"./WaveformVisualizer-BqwSlklJ.js";import{_ as ye}from"./slidev/VClicks-QV3wQd1S.js";import{I as be}from"./MyDefault-BmotRWjN.js";import"./modules/shiki-BPPYVDiq.js";const W=2048,Pe=3,we=`
2struct Params {
3  irLen : u32,
4  prevLen : u32,
5  blockSize : u32,
6};
7
8@group(0) @binding(0) var<storage, read> extIn : array<f32>;        // length = prevLen + block
9@group(0) @binding(1) var<storage, read> ir : array<f32>;           // length = irLen
10@group(0) @binding(2) var<storage, read_write> outBuf : array<f32>; // length = block
11@group(0) @binding(3) var<uniform> P : Params;
12
13@compute @workgroup_size(128)
14fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
15  let i = gid.x;
16  if (i >= P.blockSize) { return; }
17  var acc : f32 = 0.0;
18  let base = P.prevLen + i;
19  for (var k:u32 = 0u; k < P.irLen; k = k + 1u) {
20    acc = acc + extIn[base - k] * ir[k];
21  }
22  outBuf[i] = acc;
23}
24`;function ue(){const M=U(!1),w=U(!1),c=U(null),d=U(0),G=U(0);let a=null,b=null,v=null,g=null,t=null,f=null,u=null,k=null,h=null;function m(e,o){const y=Math.min(Math.floor((e==="room"?.6:e==="hall"?2.2:e==="plate"?1.2:e==="box"?.35:1)*o),Math.floor(Pe*o)),s=new Float32Array(y),i=(p,S)=>{p>=0&&p<y&&(s[p]+=S)};e==="room"?(i(Math.floor(.004*o),.6),i(Math.floor(.009*o),.4),i(Math.floor(.013*o),.3)):e==="hall"?(i(Math.floor(.01*o),.5),i(Math.floor(.021*o),.35),i(Math.floor(.035*o),.25)):e==="plate"?(i(Math.floor(.002*o),.7),i(Math.floor(.006*o),.5)):e==="box"&&(i(Math.floor(.0015*o),.8),i(Math.floor(.0032*o),.6),i(Math.floor(.005*o),.45));const L=e==="room"?.25:e==="hall"?.9:e==="plate"?.45:e==="box"?.12:.3,F=p=>{const S=p/y;return e==="plate"?.6+.8*S:e==="box"?.9-.5*Math.abs(S-.4):e==="hall"?.8+.2*S:1};let $=0,O=0;for(let p=0;p<y;p++){const S=p/o,j=Math.exp(-S/L),q=(Math.random()*2-1)*.6;$=$*.97+q*.03,O=O*.9+$*.1,s[p]+=O*j*F(p)}let z=1e-9;for(let p=0;p<y;p++)z=Math.max(z,Math.abs(s[p]));const Q=1/z;for(let p=0;p<y;p++)s[p]*=Q;return s}async function l(){try{if(!("gpu"in navigator))throw new Error("WebGPU not supported in this browser");const e=await navigator.gpu.requestAdapter();if(!e)throw new Error("No GPU adapter available");a=await e.requestDevice(),b=a.queue;const o=a.createShaderModule({code:we});v=a.createComputePipeline({layout:"auto",compute:{module:o,entryPoint:"main"}}),M.value=!0,w.value=!0,c.value=null}catch(e){M.value=!1,w.value=!1,c.value=e instanceof Error?e.message:"Unknown GPU initialization error",console.error("WebGPU initialization failed:",e)}}function B(e,o){if(!a||!b||!v){console.error("GPU not initialized");return}h=m(e,o),d.value=h.length,G.value=h.length/o;const P=Math.max(0,h.length-1),y=P+W;A();const s=(L,F)=>a.createBuffer({size:L*4,usage:F,mappedAtCreation:!1});g=s(y,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST),t=s(h.length,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST),f=s(W,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC);const i=new Uint32Array([h.length,P,W]);u=a.createBuffer({size:12,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),b.writeBuffer(u,0,i.buffer),k=a.createBindGroup({layout:v.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:g}},{binding:1,resource:{buffer:t}},{binding:2,resource:{buffer:f}},{binding:3,resource:{buffer:u}}]}),b.writeBuffer(t,0,h.buffer)}async function D(e,o){if(!a||!b||!v||!k||!g||!f)throw new Error("GPU resources not initialized");const P=o.length,y=new Float32Array(P+e.length);P>0&&y.set(o,0),y.set(e,P),b.writeBuffer(g,0,y.buffer);const s=a.createCommandEncoder(),i=s.beginComputePass();i.setPipeline(v),i.setBindGroup(0,k),i.dispatchWorkgroups(Math.ceil(W/128)),i.end();const L=a.createBuffer({size:W*4,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});s.copyBufferToBuffer(f,0,L,0,W*4),b.submit([s.finish()]),await L.mapAsync(GPUMapMode.READ);const F=new Float32Array(L.getMappedRange().slice(0));return L.unmap(),F}function A(){g?.destroy(),t?.destroy(),f?.destroy(),u?.destroy(),g=null,t=null,f=null,u=null,k=null}function C(){A(),a?.destroy(),a=null,b=null,v=null,h=null,w.value=!1}return ae(()=>{C()}),Z(()=>{C()}),{isSupported:M,isReady:w,error:c,irLength:d,irDuration:G,initializeGPU:l,setIR:B,processBlock:D,cleanup:C}}const N=2048,ne="gpu-convolver-processor";function _e(){const M=U(!1),w=U(!1),c=U(.5),d=U(.5),G=U(!1),a=U("room"),b=U(null),v=U("Initializing..."),g=ue();let t=null,f=null,u=null,k=null,h=null,m=null,l=null,B=null,D=null,A=null,C=null,e=null,o=null,P=null,y=!1,s=null,i=!1;function L(){return`
25class GPUConvolverProcessor extends AudioWorkletProcessor {
26  constructor() {
27    super();
28    this.BLOCK = ${N};
29    this.inputBuf = new Float32Array(this.BLOCK);
30    this.inputFill = 0;
31    this.outputQueue = [];
32    this.port.onmessage = (e) => {
33      const m = e.data;
34      if (m?.type === 'outBlock') {
35        this.outputQueue.push(m.data);
36      } else if (m?.type === 'reset') {
37        // Clear all state on reset
38        this.inputFill = 0;
39        this.inputBuf.fill(0);
40        this.outputQueue.length = 0;
41      }
42    };
43  }
44
45  static get parameterDescriptors() {
46    return [];
47  }
48
49  process(inputs, outputs) {
50    const input = inputs[0];
51    const output = outputs[0];
52    const chL = input[0] || new Float32Array(128);
53    const chR = input[1] || null;
54
55    // Accumulate 128-frame chunks into ${N}-frame blocks
56    // Convert stereo to mono: -6dB summing
57    const n = chL.length || 128;
58    for (let i = 0; i < n; i++) {
59      const l = chL[i] || 0;
60      const r = chR ? chR[i] : 0;
61      this.inputBuf[this.inputFill++] = (l + r) * 0.5;
62    }
63
64    // When block is full, send to main thread for GPU processing
65    if (this.inputFill >= this.BLOCK) {
66      const block = this.inputBuf.slice(0, this.BLOCK);
67      this.port.postMessage({ type: 'inBlock', data: block }, [block.buffer]);
68      this.inputFill = 0;
69    }
70
71    // Output GPU-processed blocks (wet signal only)
72    const outL = output[0];
73    const have = this.outputQueue.length ? this.outputQueue[0] : null;
74    if (have && have.length >= n) {
75      const chunk = have.subarray(0, n);
76      // Hard clip to prevent any possibility of clipping
77      for (let i = 0; i < chunk.length; i++) {
78        outL[i] = Math.max(-0.95, Math.min(0.95, chunk[i]));
79      }
80      if (have.length === n) {
81        this.outputQueue.shift();
82      } else {
83        const rest = have.subarray(n);
84        const copy = new Float32Array(rest.length);
85        copy.set(rest);
86        this.outputQueue[0] = copy;
87      }
88    } else {
89      outL.fill(0);
90    }
91
92    return true;
93  }
94}
95
96registerProcessor('${ne}', GPUConvolverProcessor);
97`}async function F(){if(t)return;t=new AudioContext;const r=L(),_=new Blob([r],{type:"application/javascript"}),I=URL.createObjectURL(_);await t.audioWorklet.addModule(I),URL.revokeObjectURL(I),f=new AudioWorkletNode(t,ne,{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[1]}),h=t.createGain(),m=t.createGain(),l=t.createDelay(1),l.delayTime.value=N/t.sampleRate,B=t.createChannelSplitter(2),D=t.createGain(),D.gain.value=.5,A=t.createGain(),A.gain.value=.5,C=t.createGain(),e=t.createGain(),e.gain.value=d.value,o=t.createDynamicsCompressor(),o.threshold.value=-6,o.knee.value=0,o.ratio.value=100,o.attack.value=1e-4,o.release.value=.01,b.value=e,z(c.value),$(),g.isReady.value||await g.initializeGPU(),g.isSupported.value&&(g.setIR(a.value,t.sampleRate),P=new Float32Array(Math.max(0,(g.irLength.value||0)-1))),v.value="Audio system initialized"}function $(){f&&(f.port.addEventListener("message",async r=>{const _=r.data;_?.type==="inBlock"&&(s=_.data,!y&&M.value&&O())}),f.port.start())}async function O(){if(!(!s||y||!M.value||!f||!P)){y=!0;try{const r=await g.processBlock(s,P);f.port.postMessage({type:"outBlock",data:r},[r.buffer]);const _=P.length;if(_>0){const I=new Float32Array(_+s.length);I.set(P,0),I.set(s,_),P.set(I.subarray(I.length-_))}}catch(r){console.error("GPU processing error:",r),v.value="GPU processing error"}s=null,y=!1,M.value&&s&&O()}}function z(r){if(!h||!m)return;const _=r*Math.PI/2;h.gain.value=Math.sin(_)*.4,m.gain.value=Math.cos(_),e&&(e.gain.value=d.value)}async function Q(r){w.value=!0,v.value="Loading audio file...";try{if(await F(),!t)throw new Error("AudioContext not initialized");const I=await(await fetch(r)).arrayBuffer();k=await t.decodeAudioData(I),G.value=!0,v.value=`Audio loaded: ${k.duration.toFixed(1)}s, ${k.sampleRate}Hz`}catch(_){console.error("Failed to load audio:",_),v.value="Failed to load audio file",G.value=!1}finally{w.value=!1}}function p(){if(!t||!k||!f||!h||!m||!C||!l||!B||!D||!A||!e||!o){console.error("Audio system not ready");return}t.state!=="running"&&t.resume();const r=M.value;if(u)try{u.onended=null,u.stop(),u.disconnect()}catch{}f&&r&&f.port.postMessage({type:"reset"}),s=null,y=!1,r||(P=new Float32Array(Math.max(0,(g.irLength.value||0)-1))),l.delayTime.value=N/t.sampleRate,u=t.createBufferSource(),u.buffer=k,u.loop=!1,i||(f.connect(h),h.connect(e),e.connect(o),o.connect(t.destination),B.connect(D,0),B.connect(A,1),D.connect(C),A.connect(C),C.connect(l),l.connect(m),m.connect(e),i=!0),u.connect(f),u.connect(B),u.onended=()=>{M.value=!1,v.value="Ready"},u.start(),M.value=!0,v.value=`Playing (${(N/t.sampleRate*1e3).toFixed(0)}ms latency)`}function S(r){c.value=Math.max(0,Math.min(1,r)),z(c.value)}function j(r){d.value=Math.max(0,Math.min(1,r)),e&&(e.gain.value=d.value)}function q(r){a.value=r,t&&g.isSupported.value&&(g.setIR(r,t.sampleRate),P=new Float32Array(Math.max(0,(g.irLength.value||0)-1)),v.value=`IR: ${r} (${(g.irDuration.value||0).toFixed(3)}s)`)}function J(){if(u){try{u.stop(),u.disconnect()}catch{}u=null}t&&(t.close(),t=null),f=null,k=null,h=null,m=null,l=null,B=null,D=null,A=null,C=null,e=null,o=null,b.value=null,P=null,s=null,y=!1,i=!1,G.value=!1,M.value=!1}return ae(()=>{J()}),Z(()=>{J()}),{isPlaying:M,isLoading:w,wetMix:c,volume:d,audioReady:G,irPreset:a,outputNode:b,statusMessage:v,loadAudioFile:Q,trigger:p,setWetMix:S,setVolume:j,setIRPreset:q}}const Ue={class:"convolution-reverb-demo"},Me={key:0,class:"error-message"},Be={class:"controls"},Ge={class:"control-row"},ke=["disabled"],xe={class:"control-label"},Ce={class:"flex flex-col gap-2"},Le={class:"control-group"},Ae={class:"control-value"},Se={class:"control-group"},Ie={class:"control-value"},Re={class:"status"},X=-60,De=0,Fe=ie({__name:"ConvolutionReverbDemo",setup(M){re();const w=ue(),c=_e(),d=U("room"),G=U(0),a=U(X);function b(m){return Math.pow(10,m/20)}const v=ce(()=>a.value===X?"-∞":`${a.value.toFixed(1)} dB`);async function g(){try{await w.initializeGPU(),w.isSupported.value&&(
97await c.loadAudioFile("/sample.wav"),c.setVolume(b(a.value)))}catch(m){console.error("Initialization failed:",m)}}function t(){c.trigger()}function f(){c.setIRPreset(d.value)}function u(){c.setWetMix(G.value)}function k(){const m=b(a.value);c.setVolume(m)}function h(m){(m.key==="t"||m.key==="T")&&t()}return fe(()=>{g(),window.addEventListener("keydown",h)}),Z(()=>{window.removeEventListener("keydown",h)}),(m,l)=>(E(),K("div",Ue,[T(" WebGPU Support Check "),R(w).error.value?(E(),K("div",Me,[n("div",null,x(R(w).error.value),1),l[3]||(l[3]=n("div",{class:"browser-help"},[V(" WebGPU requires Chrome 113+ or Edge 113+. "),n("a",{href:"https://caniuse.com/webgpu",target:"_blank",rel:"noopener"},"Check browser compatibility")],-1))])):R(w).isSupported.value?(E(),K(ee,{key:1},[T(" Controls "),n("div",Be,[n("div",Ge,[n("button",{onClick:t,disabled:!R(c).audioReady.value,class:"trigger-button"}," Trigger Sound ( t ) ",8,ke),n("label",xe,[l[5]||(l[5]=V(" IR Preset: ")),Y(n("select",{"onUpdate:modelValue":l[0]||(l[0]=B=>d.value=B),onChange:f,class:"preset-select"},l[4]||(l[4]=[n("option",{value:"room"},"Room",-1),n("option",{value:"hall"},"Hall",-1),n("option",{value:"plate"},"Plate",-1),n("option",{value:"box"},"Wood Box",-1)]),544),[[pe,d.value]])])]),n("div",Ce,[n("div",Le,[l[6]||(l[6]=n("span",{class:"control-label-text"},"Wet Mix:",-1)),n("span",Ae,x(G.value.toFixed(2)),1),Y(n("input",{type:"range","onUpdate:modelValue":l[1]||(l[1]=B=>G.value=B),onInput:u,min:"0",max:"1",step:"0.01",class:"control-slider"},null,544),[[te,G.value,void 0,{number:!0}]])]),n("div",Se,[l[7]||(l[7]=n("span",{class:"control-label-text"},"Volume:",-1)),n("span",Ie,x(v.value),1),Y(n("input",{type:"range","onUpdate:modelValue":l[2]||(l[2]=B=>a.value=B),onInput:k,min:X,max:De,step:"1",class:"control-slider"},null,544),[[te,a.value,void 0,{number:!0}]])])]),T(" Waveform Visualization "),R(c).outputNode.value?(E(),le(me,{key:0,source:R(c).outputNode.value,width:600,height:150},null,8,["source"])):T("v-if",!0),n("div",Re,x(R(c).statusMessage.value),1)])],2112)):(E(),K(ee,{key:2},[T(" Loading State "),l[8]||(l[8]=n("div",{class:"loading"}," Initializing WebGPU... ",-1))],2112))]))}}),Oe=ge(Fe,[["__scopeId","data-v-8b899eed"]]),ze={class:"text-sm mb-4"},Qe={__name:"slides.md__slidev_33",setup(M){const{$clicksContext:w,$frontmatter:c}=re();return w.setup(),(d,G)=>{const a=ye,b=Oe,v=se;return E(),le(be,de(ve(R(he)(R(c),32))),{default:oe(()=>[n("h1",null,x(d.$t("slide32.heading")),1),n("div",ze,[n("p",null,[n("strong",null,x(d.$t("slide32.convolution")),1),V(" = "+x(d.$t("slide32.convolutionDesc")),1)]),H(a,null,{default:oe(()=>[n("ul",null,[n("li",null,[n("strong",null,x(d.$t("slide32.cpuImpl")),1),V(": "+x(d.$t("slide32.cpuImplDesc")),1)]),n("li",null,[n("strong",null,x(d.$t("slide32.gpuImpl")),1),V(": "+x(d.$t("slide32.gpuImplDesc")),1)])])]),_:1})]),H(b),H(v)]),_:1},16)}}};export{Qe as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.