UNPKG

sfumato

Version:

This library is able to play soundfonts (sf2 files) with the Web Audio API.

2 lines (1 loc) 6 kB
(function(a,d){typeof exports=="object"&&typeof module<"u"?d(exports,require("soundfont2")):typeof define=="function"&&define.amd?define(["exports","soundfont2"],d):(a=typeof globalThis<"u"?globalThis:a||self,d(a.sfumato={},a.soundfont2))})(this,function(a,d){"use strict";const j=e=>{var r;if(typeof e!="string")return[];const[t,n="",o]=((r=e.match(/^([a-gA-G])([#bs]*)([0-9])?$/))==null?void 0:r.slice(1))||[];return t?[t,n,o?Number(o):void 0]:[]},q={"#":1,b:-1,s:1},H=e=>{if(typeof e=="number")return e;const[t,n,o]=j(e);if(!t)throw new Error('not a note: "'+e+'"');const r={c:0,d:2,e:4,f:5,g:7,a:9,b:11}[t.toLowerCase()],l=(n==null?void 0:n.split("").reduce((s,c)=>s+q[c],0))||0;return(Number(o)+1)*12+r+l},m=e=>Math.pow(2,e/1200),$=e=>Math.round(Math.log2(e)*1200),N=e=>e/1e3,y=(e,t)=>{const n=Math.pow(10,t);return Math.round(e*n)/n};typeof AudioParam<"u"&&(AudioParam.prototype.dahdsr=function(e,t,n,o,r,l,s,c,u){r=Math.max(y(r,4),.001),s=Math.max(y(s,4),.001),u=y(u,4),t=Math.max(t,.001);let i=e;return this.setValueAtTime(t,i),this.setValueAtTime(t,i+=o),this.exponentialRampToValueAtTime(n,i+=r),this.setValueAtTime(n,i+=l),this.exponentialRampToValueAtTime(Math.max(c*n,.001),i+=s),(f,v)=>{this.cancelAndHoldAtTime(f);const g=Math.max(v!=null?v:t,.001);this.exponentialRampToValueAtTime(g,f+u)}});const h={0:"startAddrOffset",1:"endAddrOffset",2:"startloopAddrsOffset",3:"endloopAddrsOffset",4:"startAddrsCoarseOffset",5:"modLfoToPitch",6:"vibLfoToPitch",7:"modEnvToPitch",8:"initialFilterFc",9:"initialFilterQ",10:"modLfoToFilterFc",11:"modEnvToFilterFc",12:"endAddrsCoarseOffset",13:"modLfoToVolume",14:"unused1",15:"chorusEffectsSend",16:"reverbEffectsSend",17:"pan",18:"unused2",19:"unused3",20:"unused4",21:"delayModLFO",22:"freqModLFO",23:"delayVibLFO",24:"freqVibLFO",25:"delayModEnv",26:"attackModEnv",27:"holdModEnv",28:"decayModEnv",29:"sustainModEnv",30:"releaseModEnv",31:"keyNumToModEnvHold",32:"keyNumToModEnvDecay",33:"delayVolEnv",34:"attackVolEnv",35:"holdVolEnv",36:"decayVolEnv",37:"sustainVolEnv",38:"releaseVolEnv",39:"keyNumToVolEnvHold",40:"keyNumToVolEnvDecay",41:"instrument",42:"reserved1",43:"keyRange",44:"velRange",45:"startloopAddrsCoarseOffset",46:"keyNum",47:"velocity",48:"initialAttenuation",49:"reserved2",50:"endloopAddrsCoarseOffset",51:"coarseTune",52:"fineTune",53:"sampleID",54:"sampleModes",55:"reserved3",56:"scaleTuning",57:"exclusiveClass",58:"overridingRootKey",59:"unused5",60:"endOper"},Q=Object.fromEntries(Object.entries(d.DEFAULT_GENERATOR_VALUES).map(([e,t])=>[h[e],t])),k=(e,t,n,o,r)=>{var b,A,T,V,O,M,G;const l=d.DEFAULT_GENERATOR_VALUES[e];if(typeof l!="number")throw new Error(`no default value found for generator with index ${e}`);const s=t.generators[e],c=(A=(b=n.globalZone)==null?void 0:b.generators)==null?void 0:A[e],u=(T=o==null?void 0:o.generators)==null?void 0:T[e],i=(O=(V=r.globalZone)==null?void 0:V.generators)==null?void 0:O[e],f=s&&"value"in s?s.value:void 0,v=c&&"value"in c?c.value:void 0,g=u&&"value"in u?u.value:void 0,E=i&&"value"in i?i.value:void 0,p=(M=f!=null?f:v)!=null?M:l,F=(G=g!=null?g:E)!=null?G:0;return p+F},D=e=>d.DEFAULT_GENERATOR_VALUES[e]!==void 0,P=(e,t,n)=>{var o,r,l,s;return Object.fromEntries(Array.from(new Set([Object.keys((r=(o=n.globalZone)==null?void 0:o.generators)!=null?r:{}),Object.keys(t.generators),Object.keys((s=(l=t.instrument.globalZone)==null?void 0:l.generators)!=null?s:{}),Object.keys(e.generators)].flat())).filter(D).map(c=>[h[c],k(parseInt(c),e,t.instrument,t,n)]))};async function J(e){const t=await fetch(e).then(o=>o.arrayBuffer()),n=new Uint8Array(t);return new d.SoundFont2(n)}function C(e,t,n){let{time:o=e.currentTime}=n;const{midi:r,start:l,velocity:s=.3,startLoop:c,endLoop:u,sampleRate:i,originalPitch:f,pitchCorrection:v,type:g,sampleModes:E=0,overridingRootKey:p,fineTune:F=0,startloopAddrsOffset:b=0,startloopAddrsCoarseOffset:A=0,endloopAddrsOffset:T=0,endloopAddrsCoarseOffset:V=0,delayVolEnv:O=-12e3,attackVolEnv:M=-12e3,holdVolEnv:G=-12e3,decayVolEnv:z=-12e3,sustainVolEnv:U=0,releaseVolEnv:B=-12e3,pan:x=0,...ee}=n,te=100*(p!==void 0&&p!==-1?p:f)+v-F,ne=r*100-te,oe=1*Math.pow(2,ne/1200);t.playbackRate.value=oe;const I=c+b+A*32768,K=u+T+V*32768;K>I&&E===1?(t.loopStart=I/i,t.loopEnd=K/i,t.loop=!0):E===3&&console.warn("unimplemented sampleMode 3 (play till end on note off)"),Object.keys(ee).filter(R=>!["name","instrument","keyRange","sampleID","end"].includes(R)).length;const L=e.createGain(),re=[o,0,s,m(O),m(M),m(G),m(z),U>=960?0:1-N(U),m(B)],ae=L.gain.dahdsr(...re),w=e.createStereoPanner();return w.pan.value=x/1e3,L.connect(w),t.connect(L),w.connect(e.destination),t.start(o),(R=e.currentTime)=>{t.stop(R+m(B)),ae(R)}}function Z(e,t,n={}){const{header:o,data:r}=t,l=new Float32Array(r.length);for(let i=0;i<r.length;i++)l[i]=r[i]/32768;const s=e.createBuffer(1,l.length,o.sampleRate);s.getChannelData(0).set(l);const u=e.createBufferSource();return u.buffer=s,n={...o,...n},C(e,u,n)}function W(e){return Object.fromEntries(Object.entries(e).map(([t,n])=>{const o=h[t];return["keyRange","velRange"].includes(o)?[o,n.range]:[o,n.value]}))}const S=(e,t)=>!e.keyRange||e.keyRange.lo<=t&&t<=e.keyRange.hi,X=(e,t,n,o)=>{Object.fromEntries(Object.entries(h).map(([r,l])=>[l,k(parseInt(r),n,t,o,e)]))},_=(e,t)=>e.zones.filter(o=>S(o,t)&&o.instrument).map(o=>o.instrument.zones.filter(r=>S(r,t)).map(r=>{const l=P(r,o,e);return{...r,mergedGenerators:l}})).flat(),Y=(e,t,n,o=e.currentTime)=>{const l=_(t,n).map(s=>Z(e,s.sample,{...s.mergedGenerators,midi:n,time:o}));return(s=e.currentTime)=>{l.forEach(c=>c(s))}};a.applyOptions=C,a.defaultGeneratorValues=Q,a.generators=h,a.getActiveZones=_,a.getBufferSourceFromSample=Z,a.getGeneratorValue=k,a.getGeneratorValues=P,a.hasDefaultValue=D,a.isActiveZone=S,a.loadSoundfont=J,a.mergeGenerators=X,a.normalizePermille=N,a.precision=y,a.readableGenerators=W,a.s2tc=$,a.startPresetNote=Y,a.tc2s=m,a.toMidi=H,a.tokenizeNote=j,Object.defineProperties(a,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});