TinyChan

Topic: Bytebeat/Floatbeat/Funcbeat

+Anonymous A1.2 year ago #65,293

Bytebeat is a type of music made from mathematical formulas.

(Type: Floatbeat, Hz: 22050, LINK)
//Weird ripple sound.
f=x=>((2+.5-((t/24e4)%.5))*sin(cos(x))),
rf16=x=>f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(x)))))))))))))))),
(rf16(t/1e3)/2)-.25


Template to copy:
(Type: ◯, Hz: ◯, [url=◯][b]LINK[/b][/url])
[code=javascript]
//Insert code here
[/code]


⚠️ Please replace the # character with %23 in the URL link.

+Anonymous B1.2 year ago, 7 hours later[T] [B] #655,506

2309_bj_kyomori_sp.jpg(Type: Funcbeat, Hz: 11025, LINK)
//Started: 19 Aug 2024 13:56:32. Finished: 14:19:48.
//Moving points connected with lines to make a waveform.
rand=Math.random;
pToLine=(a,b,x)=>a.y+((b.y-a.y)*(x-a.x)/(b.x-a.x));
function makeChordSynth(count=5){
  let p=[];
  for(let i=0;i<count;i++){
    p.push({x:rand(),y:rand(),vx:(rand()*2)-1,vy:(rand()*2)-1});
  }
  p.sort((a,b)=>a.x-b.x);
  function step(speed=1){
     for(let i=0;i<p.length;i++){
       p[i].x+=p[i].vx*speed;
            if(p[i].x>1){p[i].x=1-(p[i].vx*speed);p[i].vx*=-1;}
       else if(p[i].x<0){p[i].x=0-(p[i].vx*speed);p[i].vx*=-1;}
       p[i].y+=p[i].vy*speed;
            if(p[i].y>1){p[i].y=1-(p[i].vy*speed);p[i].vy*=-1;}
       else if(p[i].y<0){p[i].y=0-(p[i].vy*speed);p[i].vy*=-1;}
     }
     p.sort((a,b)=>a.x-b.x);
  }
  function wave(x){
    //p must be in order first
    //p positions must be within the range [0,1] (but this returns in range [-1,1])
    if(x>=0&&x<=p[0].x){return(pToLine({x:p[p.length-1].x-1,y:p[p.length-1].y},p[0],x)*2)-1;}
    if(x>=p[p.length-1].x&&x<=1){return(pToLine(p[p.length-1],{x:p[0].x+1,y:p[0].y},x)*2)-1;}
    for(let i=0;i<p.length-1;i++){
      if(x>=p[i].x&&x<=p[i+1].x){ //This could be improved? (By going backwards and only using one comparison?)
        return(pToLine(p[i],p[i+1],x)*2)-1;
      }
    }
  }
  return(t)=>{
    step(1e-6+(sin(t/20)*1e-5));
    return(wave((t*60)%1)+wave((t*90)%1)+wave((t*200)%1)+wave((t*302)%1)+wave((t*480)%1))*.3;
  };
}
a=[
  //[synth,speed,LFO]
  [makeChordSynth(4),1.61,.2],
  [makeChordSynth(6),1.2,.6],
  [makeChordSynth(12),.401,.5]
];
return(t)=>{
  let m=[0,0];
  for(let i=0;i<a.length;i++){
    m[0]+=a[i][0](t*a[i][1])*sin(t*a[i][2]);
    m[1]+=a[i][0](t*a[i][1])*cos(t*a[i][2]);
  }
  return[m[0]/2,m[1]/2];
};

·Anonymous B1.2 year ago, 10 minutes later, 7 hours after the original post[T] [B] #655,507

@previous (B)
I was writing this on a mobile phone (not sure how it sounds in stereo, this phone I'm using is monophonic).

(Edited 4 minutes later.)


+Anonymous C1.2 year ago, 3 days later, 4 days after the original post[T] [B] #655,640

(Type: Funcbeat, Hz: 32000)
let mod=(n,m)=>(n%m+m)%m,
    arr0=l=>new Array(l).fill(0),
    FIR=(c=[1])=>{
  let l=c.length,a=arr0(l);
  return{
    p:x=>{ //process
      let o=0;
      a.unshift(x);a.pop();
      for(let i=0;i<l;i++){o+=c[i]*a[i];}
      return o;
    },
    c:c //array itself
  };
},pwm=(a,t)=>mod(t,1)>=a?1:-1,
  cho=t=>(pwm(.3+(sin(t)*.2),t*340)+
          pwm(.5+(sin(t*2.2)*.4),t*338)+
          pwm(.3+(sin(t/2)*.2),t*265)-
          pwm(.5+(sin(t*2.2)*.4),t*268)-
          pwm(.3+(sin(t/3)*.2),t*257)-
          pwm(.5+(sin(t*2.2)*.4),t*256)+
          pwm(.3+(sin(t/2)*.2),t*230)+
          pwm(.5+(sin(t*2.2)*.4),t*228)+
          pwm(.3+(sin(t)*.2),t*180)+
          pwm(.6+(sin(t*2.2)*.4),t*178)-
          pwm(.3+(sin(t)*.2),t*170)-
          pwm(.7+(sin(t*2.2)*.4),t*169)+
          sin(t*PI*818)-sin(t*PI*1370))/8,
fc=(fun,cd=32e3)=>{
  let fa=[],f,ct=0;
  for(let l=64,i=0;i<l;i++)
    fa.push((sin((i/l)*PI)**3)/30);
  f=FIR(fa);
  return t=>{
    let o=f.p(fun(t));
    ct++;if(ct>=cd){
      let n=1+random()*20;
      for(let l=fa.length,i=0;i<l;i++){
        f.c[i]=(sin((i/l)*PI)**n)/30;
      }
      ct=0;
    }
    return o;
  };
},fca=[[fc(cho),1],[fc(cho,8e3),1.5],[fc(cho,16e3),2.25]];
return t=>{
  let o=0;
  for(let i=0;i<fca.length;i++){
    o+=fca[i][0](t*fca[i][1]);
  }
  return o/2;
};

·Anonymous C1.2 year ago, 10 minutes later, 4 days after the original post[T] [B] #655,641

I hate chiptune FYI
Need to implement declicking, also forgot what the global sample-rate variable was though. I'm tired, I just wanna make music and learn more about effects and filters but I can only use my phone ATM. It would be great if you could load samples of any audio format (takes time to reconstruct them within the script itself if you're gonna avoid loading external files, there are different methods (e.g. FM, spectral) that suit different sounds (e.g. periodic waves, vocal peaks, harmonic bells, noise drums)).

(Edited 9 minutes later.)

·Anonymous C1.2 year ago, 1 hour later, 4 days after the original post[T] [B] #655,642

Type: Funcbeat, this is just a shitty random noisy metallic loop generator (could record a few bits to use as a drum loop)…
let arr0=l=>new Array(l).fill(0),
    delay=length=>{
      let a=arr0(length),i=0;
      return x=>{
        a[i]=x;
        i=(i+1)%length;
        return a[i];
      };
    },
    reverb=()=>{
      let s=72,d=[],init=()=>{
        let p=300+(random()*5000);
        for(let i=0;i<s;i++){
          d[i]=[
            delay(4+floor(random()*p)),
            (.1+(random()*.23))*(random()<.5?1:-1)
          ];
        }
      };init();
      return{
        p:x=>{
          let o=x;
          for(let i=0;i<s;i++){
            o+=d[i][0](i===0?x:o)*d[i][1];
          }
          return o;
        },
        i:init
      };
    };
let c={c:5e3,t:0,a:0,ar:true},r=reverb(),a=arr0(c.c);
c.c2=c.c;
return t=>{
  let m=0;
  c.t++;
  if(c.t>=c.c2){c.t=0;
    r.i();
    m++;
    c.c2=c.c/(1+floor(random()*2));
  }
  c.a++;
  if(c.a>=c.c*4){c.a=0;
    c.ar=random()<.3;
  }
  m=r.p(m);
  if(c.ar){a[c.a]=m;}else{
    m=a[c.a%c.c]+a[floor(c.a/2)%c.c]+a[floor(c.a/4)];
  }
  return m;
};

(Edited 8 minutes later.)


+Anonymous D1.2 year ago, 14 hours later, 4 days after the original post[T] [B] #655,659

misuzu-kawana-2.jpgBeats me

·Anonymous C1.2 year ago, 2 hours later, 4 days after the original post[T] [B] #655,662

A good graphical editor in music production might be significantly more vital than the actual signal processing and instruments themselves as they don't dictate the overall flow and structure of the song, because we're usually more interested in the compositional aspect (meticulously crafted arrangements, patterns, sequencing, layering, rhythms, melodies, progressions, style and genres).

·Anonymous C1.2 year ago, 18 hours later, 5 days after the original post[T] [B] #655,704

I also like to freehand draw waveforms for one.

+Anonymous E1.2 year ago, 5 days later, 1 week after the original post[T] [B] #655,927

5/4 beat rhythmn

+Anonymous F11 months ago, 2 months later, 2 months after the original post[T] [B] #659,224

ice cweam.jpgSilly filter test (did this on my phone)…
var sampleRate=32000,
    PI=Math.PI,TAU=PI*2;
class Filter{ //generated by o1
  constructor(fs=sampleRate,freq=1000,Q=0.707){
    this.fs=fs; //sampling frequency
    this.g=0; //coefficient
    this.R=1; //damping factor (computed from Q)
    this.S1=0;this.S2=0; //state variables
    this.type=0; //filter type parameter (continuous LP/BP/HP control, 0 to 2)
    this.freq=freq; //cutoff frequency
    this.Q=Q; //Q(uality) factor
  }
  setParams(freq,Q,type){
    this.freq=freq;this.Q=Q;this.type=type;
    var wd=TAU*freq, //digital angular frequency
        T=1/this.fs, //sampling period
        wa=(2/T)*Math.tan(wd*T/2); //pre-warping the frequency for bilinear transform
        this.g=wa*T/2; //compute filter coefficient
        this.R=1/(2*Q); //compute damping factor from Q
  }
  process(x){
    //calculate the intermediate value 'v' using the state variables
    var v=(x-this.S2-this.R*this.S1)/(1+this.g*this.R+this.g*this.g),
        //update the state variables for the next sample
        S1_next=this.g*v+this.S1,
        S2_next=this.g*S1_next+this.S2,
        //compute the filter outputs
        LP=S2_next, //low-pass
        BP=S1_next, //band-pass
        HP=x-this.R*S1_next-S2_next; //high-pass
    //update the state variables
    this.S1=S1_next;this.S2=S2_next;
    //blend the outputs based on the 'type' parameter
    return this.type<=1?
      LP*(1-this.type)+BP*this.type: //between low-pass and band-pass
      BP*(2-this.type)+HP*(this.type-1); //between band-pass and high-pass
  }
}
//GPT-4o mini's miniFilter
function miniFilter(){this.z=0;this.fc=0.5;this.q=1;this.a=0;this.b=0;this.set_fc(0.5);this.set_q(1);}
miniFilter.prototype.set_fc=function(fc){this.fc=fc<0?0:fc>1?1:fc;this.a=Math.sin(PI*this.fc)/(2*this.q);};
miniFilter.prototype.set_q=function(q){this.q=q<0.1?0.1:q>10?10:q;};
miniFilter.prototype.run=function(x){this.b=this.a*(x-this.z)+this.z;this.z=this.b;return this.b;};
/* This is what a real/working filter looks like:
function DiodeFilter(){
  this.k=0;
  this.A=0;
  this.z=[0,0,0,0,0];
  this.ah;this.bh;this.fc;
  this.set_q(0);
  this.set_hpf(0.5);
  this.set_fc(.5);
}
DiodeFilter.prototype.set_hpf=function(fc){
  var K=fc*PI;
  this.ah=(K-2)/(K+2);
  this.bh=2/(K+2);
};
DiodeFilter.prototype.reset=function(){if(this.k<17)this.z=[0,0,0,0,0];};
DiodeFilter.prototype.set_q=function(q){
  this.k=20*q;this.A=1+.5*this.k;
};
DiodeFilter.prototype.set_fc=function(cutoff){
  cutoff*=cutoff;
  this.fc=cutoff<=0
    ?.02
    :(cutoff>=1?.999:cutoff);
};
function clip(x){return x/(1+Math.abs(x));}
DiodeFilter.prototype.run=function(x){
  var a=PI*this.fc;
  a=2*Math.tan(.5*a); //dewarping, not required with 2x oversampling
  var ainv=1/a,
      a2=a*a,
      b=2*a+1,
      b2=b*b,
      c=1/(2*a2*a2-4*a2*b2+b2*b2),
      g0=2*a2*a2*c,
      g=g0*this.bh,
      //current state
      s0=(a2*a*this.z[0]+a2*b*this.z[1]+this.z[2]*(b2-2*a2)*a+this.z[3]*(b2-3*a2)*b)*c,
      s=this.bh*s0-this.z[4],
      //solve feedback loop (linear)
      y5=(g*x+s)/(1+g*this.k),
      //input clipping
      y0=clip(x-this.k*y5);
  y5=g*y0+s;
  //compute integrator outputs
  var y4=g0*y0+s0,
      y3=(b*y4-this.z[3])*ainv,
      y2=(b*y3-a*y4-this.z[2])*ainv,
      y1=(b*y2-a*y3-this.z[1])*ainv;
  //update filter state
  this.z[0]+=4*a*(y0-y1+y2);
  this.z[1]+=2*a*(y1-2*y2+y3);
  this.z[2]+=2*a*(y2-2*y3+y4);
  this.z[3]+=2*a*(y3-2*y4);
  this.z[4]=this.bh*y4+this.ah*y5;
  return this.A*y4;
};
*/
function note(n,base=440,keys=12){return base*Math.pow(2,n/keys);}
var f=[],//new Filter(),
    c=[0,3,7,10,14,15,17,22,26];
for(let i=0;i<c.length;i++){f.push(new Filter());}
function dsp(t){
  /*var x=(t*60)%1<.5;
  f.setParams(
    1300+(Math.sin(t*3)*650),
    4,.2
  );
  return f.process(x)*.5;*/
  var x=0,i=0;
  for(;i<c.length;i++){
    f[i].setParams(
      1400+(Math.sin(t*(1.3+i)*1.3)*650),
      14,(((Math.sin((t+(i*TAU/c.length))*5)+1)/2)**7)*2
    );
    x+=f[i].process(
      ((t*note(c[i]+[5,10,3,8,1,6,-1][Math.floor(t*.8)%7],131.4,12.18))%1)-.5
    );
  }
  return x/16;
}
return dsp;

·Anonymous F11 months ago, 19 hours later, 2 months after the original post[T] [B] #659,247

table03-1.pngChord progression interpreter/reader thing, sorry for using an AI again for this as there could be some shoddy mistakes (still using my phone, gotta shower and eat food), I also like reading code from others that isn't mine but AIs don't easily impress me that much (too verbose, too simple, or doesn't think outside the box, can't correct certain mistakes that require visualization/hearing/runtime)…
> Error
> Your post has too many lines.
Fuck off, fix the code formatting.
https://files.catbox.moe/4kxs6c.txt
https://pastebin.com/eT51vj0e

+Anonymous G10 months ago, 1 month later, 4 months after the original post[T] [B] #661,666

Occasionally loud whispers (I am retarded)…
var sampleRate=8000,
    f=t=>10*(t>>7|t|t>>6)+4*(t&t>>13|t>>6);
function mod(a,b){return(a%b+b)%b;}
function clamp(x,mn,mx){return min(max(x,mn),mx);}
function bytebeatToFloat(f,t){return((mod(f(floor(t*sampleRate)),256)/255)*2)-1;}
class biquad{
  constructor(){
    this.y1=0;this.x1=0;
    this.y2=0;this.x2=0;
  }
  process(x,b0,b1,b2,a1,a2){
    let o=b0*x+b1*this.x1+b2*this.x2-a1*this.y1-a2*this.y2;
    if(!isFinite(o))o=x;
    this.x2=this.x1;this.x1=x;
    this.y2=this.y1;this.y1=o;
    return o;
  }
}
class compressor{
  constructor(){
    this.minDiv=1e-4;this.div=1;
  }
  process(x,speed=1){
    this.div=max(this.minDiv,abs(x),this.div-(speed/sampleRate));
    return x/this.div;
  }
}
class convolution{
  constructor(l=16){
    this.l=l;
    this.b=new Array(l).fill(0);
    this.m=[];
    for(let i=0;i<l;i++)
      this.m.push(((1-(abs((i/l)-.5)*2))**51)+((Math.random()*2)-1)*.3);
    this.i=0;
  }
  process(x,t){
    let s=0;
    this.b[this.i]=x;
    for(let j=0;j<this.l;j++)
      /*s+=this.b[j]*this.m[j];*/s+=this.b[floor(mod(this.i-j,this.l))]*this.m[j]; //the indexing is wrong
    this.i=(this.i+1)%this.l;
    return s;
    //return this.b[this.i]-this.b[mod(this.i+1+floor((sin(t*6)+1)*2000),this.l)]; //cool wonky effect
    //return this.b[mod(this.i+(floor((t*14e3)+sin(t*5)*500)%9300),this.l)];
  }
}
let b=[],comp=new compressor(),conv=new convolution(1300);
for(let i=0;i<16;i++)b.push(new biquad());
return t=>{
  let m=bytebeatToFloat(f,t*41.14);
  for(let i=0;i<b.length;i++){
    m=b[i].process(m,
    sin(t/(i+1))/2,cos(t*(i+1))*0.3,sin(t+i)/5,sin(t*(1+i)-i)/4,cos(t*.2)/20);
  }
  m=conv.process(m,t);
  return clamp(comp.process(clamp(m,-1,1),2e10)*2,-.9,.9);
};

(Edited 8 minutes later.)

·Anonymous G10 months ago, 53 minutes later, 4 months after the original post[T] [B] #661,674

Supersaw thing…
c=(x,a,b=Infinity)=>min(x*b,a===0?1:1-x**(a<.5?1/(2*a):2-2*a));
s=(t,p=220)=>{
  let o=0;
  for(let i=0;i<30;i++)
    o+=c(t*(p+(sin(i*68.63)*.01)+(i-15)*.2)%1,(sin((t*2.2)+i*6.5)+1)*.5,4+(sin(t*.4)+1)*57)-.5;
  return o*.1;
};
return t=>{
  b=[180,140,135][((t*.3)%3)|0];
  return s(t,b*.5)+s(t,b)+s(t,271)*.8+s(t,402)*.6;
};

+Anonymous H10 months ago, 8 minutes later, 4 months after the original post[T] [B] #661,675

wheres the preview link

·Anonymous G10 months ago, 2 minutes later, 4 months after the original post[T] [B] #661,677

@previous (H)
I'm too lazy to manually escape URLs and it doesn't work for long URLs (because TC truncates the rest of long links, thus the default bytebeat loads), just copy-and-paste these code snippets into Dollchan and set to Funcbeat (most of them are).

(Edited 7 minutes later.)

·Anonymous G10 months ago, 3 hours later, 4 months after the original post[T] [B] #661,692

Musical monophonic melody test (I'm still on my phone by the way)…
let notes="C,Db,D,Eb,E,F,Gb,G,Ab,A,Bb,B".split(",").reduce((o,k,i)=>(o[k]=i,o),{}),noteHz=n=>Math.pow(2,(n-49)/12)*440,read=note=>{
  if(note.includes("-"))return note.split("-").map(read);
  let n=40;
  while(note.startsWith("_")){n-=12;note=note.replace("_","");}
  while(note.startsWith("^")){n+=12;note=note.replace("^","");}
  return n+notes[note];
},melody=`\
D Gb _B D
E G _A Db
Gb A _B D
E G _A Gb-E
D-_A Gb-G _B-_G Db-D
E-_A A-G Gb-_B Gb-A
_E-_G _B-Db _A-D E-^A
_Gb-_A Db-E Gb-_A Gb-E`.trim().split(/\s+/gm).map(read),tempo=160/60;
return t=>{
  let at=(t*tempo)%melody.length,
      e=melody[floor(at)],
      swing=.66,
      f=(typeof e==="number"?noteHz(e):noteHz(e[+(at%1>swing)]))-4.5; //I like to detune
  return(((t*(f*(at%1<=swing?.25:.5))*1.002%1>at%1)+(t*f%1))-1)*(.3-((at%1<=swing?at%1:(at%1)-swing)**.3)*.38)*2;
};

(Edited 3 minutes later.)

·Anonymous G10 months ago, 16 minutes later, 4 months after the original post[T] [B] #661,696

@previous (G)
Feel free to fuck around with the lower part:
f=t=>{
  if(t<0)return 0;
  let at=(t*tempo)%melody.length,
      e=melody[floor(at)],
      swing=.4,
      f=(typeof e==="number"?noteHz(e):noteHz(e[+(at%1>swing)]))+16.9;
  return(((t*(f*(at%1<=swing?.25:.5))*1.702%1>at%1)+(t*f%1))-1)*(.3-((at%1<=swing?at%1:(at%1)-swing)**.3)*.28)*2;
};
return t=>{
  t*=.87;
  return f(t*2)*.4+f(t*1.5)+f(t)+f(t-.502)*.7+f(t-1.04)*.4;
};

+Anonymous I10 months ago, 18 hours later, 4 months after the original post[T] [B] #661,748

@661,692 (G)
Polyphonic chord progression test, this shit might be laggy (especially on mobile, where it just mutes the entire thing):
let TAU=Math.PI*2,notes="C,Db,D,Eb,E,F,Gb,G,Ab,A,Bb,B".split(",").reduce((o,k,i)=>(o[k]=i,o),{}),noteHz=n=>Math.pow(2,(n-49)/12)*440,read=note=>{
  if(note.includes("-"))return note.split("-").map(read);
  let n=40;
  while(note.startsWith("_")){n-=12;note=note.replace("_","");}
  while(note.startsWith("^")){n+=12;note=note.replace("^","");}
  return n+notes[note];
},chords=`\
__C-C-Eb-G-Ab-Bb //Cm7b6
__F-_Bb-C-Eb-G-Ab-^Db //Fm11b6
__Bb-C-Db-F-Ab-Bb-^C-^Eb //Bbm11
__Eb-C-Db-Eb-F-Ab-Bb-^C //Eb13sus4`.replace(/\/\/.+$/gm,"").trim().split(/\s+/gm).map(read);/*,tempo=40/60;return t=>{
  let at=(t*tempo)%chords.length,
      a=chords[floor(at)],
      o=0;
  for(let i=0;i<a.length;i++)
    o+=sin(t*noteHz(a[i])*TAU)**3;
  return o*.1
};*/
let sampleRate=8000,timer={bar:0,maxCount:2**5,count:0,wait:sampleRate/13,tick:0},voices=[];
class synth{
  constructor(n){
    this.duration=timer.wait+(timer.wait*7*Math.random());
    this.t=0;
    this.channel=+(Math.random()<.5);
    this.freq=noteHz(n+.3)+3*((Math.random()*2)-1);
    this.tm=1/sampleRate;
    this.e=1+floor(Math.random()*10);this.sign=Math.random()<.5?-1:1;
  }
  process(){
    let{t,tm,freq,duration,e,sign}=this,time=t*tm;
    let o=(sin(time*freq*TAU)**e)*sign*(1-(t/duration))*min(t*.001,1);
    this.t++;
    return o;
  }
}
return t=>{
  with(timer){
    if(tick>=wait){
      tick%=wait; //could be decimal
      count++;
      for(let i=0;i<chords[bar].length;i++)voices.push(new synth(chords[bar][i]-.1),new synth(chords[bar][i]),new synth(chords[bar][i]+.1),new synth(chords[bar][i]-12),new synth(chords[bar][i]-24));
      if(count>=maxCount){ //next bar
        count=0;
        bar=(bar+1)%chords.length;
      }
    }
    tick++;
  }
  let o=[0,0];
  for(let i=0;i<voices.length;i++){
    let v=voices[i];
    o[v.channel]+=v.process();
    if(v.t>=v.duration){
      voices.splice(i,1);i--;
    }
  }
  return[o[0]*.15,o[1]*.15];
};

·Anonymous I10 months ago, 34 minutes later, 4 months after the original post[T] [B] #661,749

@previous (I)
Could've written
voices.splice(i--,1);
without curly-braces but whatever. Dynamic size-changing arrays are also bad for this (due to memory management reasons), people usually use fixed-size cyclic/circular arrays for polyphonic synth voices.
Gonna focus on sample-esque wavetables next time as well, not sure if there are any good resources for that (
Math.sin()
is also slow that you should probably pre-calculate a table for that)…

(Edited 9 minutes later.)


+Anonymous J10 months ago, 1 week later, 4 months after the original post[T] [B] #662,762

Glitchy noise thing…
let t=0,b=0,c=0,a=-1,d=1,l=(a,b,t)=>a+t*(b-a),bl=16e3,ba=[],bc=[0];
function f1(T){
  t=t+.01+(Math.sin(t+.1)*4.01);
  t+=b^(T%4);b=(t*(T%2.25))&5;
  c=(t+c)/2.005;t=c-2;
  t=(t%256+256)%256;
  let o=(2*(t/255))-1;
  a=l(-1,max(a,o),.98);d=l(1,min(d,o),.98);
  return ((o-a)/max(.001,abs(d-a)))-1;
}
function f2(T){
  let o=f1(T);
  if(ba.length<bl){
    ba.push(o);
  }else{
    bc=ba.reverse();ba=[];
  }
  return(((2*o-bc[(bl-1)-(Math.floor(T*2.9e3)%bl)]-bc[Math.floor(T*6.4e3)%bl])%2+1)%2)*.5;
}
for(let i=0;i<bl;i++)f2(i/8e3);
return f2;

·Anonymous J10 months ago, 1 hour later, 4 months after the original post[T] [B] #662,769

gigo.pngKey reminder: Garbage In, Garbage Out (GIGO).
It's better to teach oneself before writing.
On another hand, I need an SVG graphing calculator because canvases sometimes would fail to render properly in Firefox on mobile. I also wish you could import audio samples/files without having to convert them into large arrays or encoded strings and putting them directly inside the code (you also need to read cue markers for slices).

(Edited 3 minutes later.)


+Phoneposter10 months ago, 2 days later, 4 months after the original post[T] [B] #663,131

Not bytebeat, but here's how I'd play notes in JavaScript…
var audioCtx=new(window.AudioContext||window.webkitAudioContext)();
let semitones={C:-9,D:-7,E:-5,F:-4,G:-2,A:0,B:2};
function noteToFreq(note){
    //Notes can either be integers or strings.
    let semitone;
    if(typeof note==="number"){semitone=note;}
    else{
        let match=note.match(/^([A-G])([b#]?)(-?\d+)$/i);
        if(!match)throw new Error("Invalid note: "+note);
        semitone=semitones[match[1].toUpperCase()];
        let accidental=match[2],octave=parseInt(match[3],10);
        if(accidental==="#")semitone++;
        if(accidental==="b")semitone--;
        semitone+=(octave-4)*12; //4 is the octave of A4
    }
    return 440*Math.pow(2,semitone/12);
}
function playNote(note,duration,velocity,start){
    let osc=audioCtx.createOscillator(),
        gain=audioCtx.createGain(),
        freq=noteToFreq(note);
    osc.frequency.setValueAtTime(freq,audioCtx.currentTime+start);
    gain.gain.setValueAtTime(velocity,audioCtx.currentTime+start);
    osc.connect(gain);
    gain.connect(audioCtx.destination);
    osc.start(audioCtx.currentTime+start);
    osc.stop(audioCtx.currentTime+start+duration);
}
function scheduleNotes(notes){
    notes=notes.slice().sort((a,b)=>a.start-b.start);
    let startTime=audioCtx.currentTime,
        index=0,
        interval=setInterval(()=>{
        let currentTime=audioCtx.currentTime-startTime;
        while(index<notes.length&&notes[index].start<=currentTime*1000){
            let{note,duration,velocity,start}=notes[index];
            playNote(note,duration,velocity,start);
            index++;
        }
        if(index>=notes.length)
            clearInterval(interval); //Stop
    },50);
}
//scheduleNotes([{note:"C4",start:0,duration:10,velocity:.5},{note:"A4",start:5,duration:5,velocity:1}]);
scheduleNotes((()=>{let a=[],n=[..."CDEFGA"];for(let i=0;i<100;i++){let note=n[Math.floor(Math.random()*n.length)]+(4+Math.floor(Math.random()*3)),duration=.1+Math.random()*.2,velocity=Math.random()*.25,start=i/10;a.push({note,duration,velocity,start});}return a;})());

(Edited 9 minutes later.)


+Anonymous L10 months ago, 5 hours later, 4 months after the original post[T] [B] #663,189

@OP
I get that making music from math is cool, but I’m so triggered you wrote f 16 times instead of using a for loop.

·Anonymous K10 months ago, 33 minutes later, 4 months after the original post[T] [B] #663,196

@previous (L)
function recurse(f,x,n){for(var o=x,i=0;i<n;i++)o=f(o);return o;}

Other suitable names:
applyFunctionNTimes
,
iterateFunction
,
repeatFunction
,
iterate

(Edited 8 minutes later.)


·Anonymous L10 months ago, 3 minutes later, 4 months after the original post[T] [B] #663,197

@previous (K)
A function called recurse that solves a problem using iteration instead of recursion. Beautiful.

·Anonymous K10 months ago, 4 minutes later, 4 months after the original post[T] [B] #663,198

@previous (L)
I thought
iterate
would've been something more like this:
function iterate(f,n,...a){for(let i=0;i<n-1;i++)f(...a);return f(...a);}

or
function iterate(f,n){for(let i=0;i<n-1;i++)f(i);return f(n-1);}

Had to edit multiple times to get it right, lol.

(Edited 6 minutes later.)


·Anonymous L10 months ago, 4 minutes later, 4 months after the original post[T] [B] #663,199

@previous (K)
Recursion would be

function recursiveFunction(/*some arguments*/){
//some stuff
return recursiveFunction(/*some input parameters*/);

}

It doesn’t really matter though. Anything that can be done using recursion can be done with iteration. It’s just, some things are easier to do one way than the other. Really I was joking. You can call the function whatever you want. It is recursion in the mathematical sense.

·Anonymous K10 months ago, 1 minute later, 4 months after the original post[T] [B] #663,200

@previous (L)
> Anything that can be done using recursion can be done with iteration.
Yeah, those done with iterations instead of recursions are called imperative, recursions also rely on the call stack so it's no wonder (it can be done with
.push()
/
.pop()
).

(Edited 3 minutes later.)


·Anonymous L10 months ago, 7 minutes later, 4 months after the original post[T] [B] #663,201

I think this would be the recursive code for it. It might be off by one, I don’t really feel like testing it, but:
function recurse(f, x, n, i = 0){
    if ( i < n ) {
        return f(recurse(f, x, n, i + 1));
    }
    return x;
}

(Edited 2 minutes later.)


·17-EDO chord10 months ago, 1 week later, 4 months after the original post[T] [B] #664,670

https://bytebeat.demozoo.org/#t=1&e=0&s=11000&bb=5d00000100c50000000000000000330f49c409cd8b3abbe436d603d01d1458cb38f1f17c59b42d4dd1aa09a401f9aba05f93e7bc516af6f23ba7570eeafd69bbc6e363925dade2427cfd097e23ecaf011d45ffddc13778b912e95bbb0c5c1e1da8bb4e113431d87bd53e138ccce98e88acac03e221baf455999f0ffdf06f93f71904798cb2eefaad5e6920ee8b30c872f6b20d568423841cb037c6ff9d7aabd0094a5ad200f92f25cbbd75c099fcffc5c38000
f=n=>437*Math.pow(2,n/17),TAU=Math.PI*2,T=t/11e3,
a=[-7,-3,3,4,7,10,17,0,-14,-13,-3.5,20,21,24,31,37,33.8/*,7,20,17,10*/],a.reduce((p,c)=>p+Math.sin(f(c)*T*TAU)-((f(c)*T*.995)%1),0)*.06

(Edited 2 minutes later.)

·tuning idea10 months ago, 1 hour later, 4 months after the original post[T] [B] #664,671

SR=32000; //sample rate
SRi=1/SR;
makeRatios=n=>{
  let a=[];
  for(let i=1;i<n;i++)
    a.push(i/n);
  return a;
};
a=[0,...[2,3,5,7/*prime numbers*/].flatMap(makeRatios),1].sort((a,b)=>a-b);
//return t=>((t*440*a[Math.floor(t*20)%a.length])%1)-.5;
st=0;
return t=>{
  //let f=600*a[Math.floor(t*10)%a.length];
  let f=220+220*a[Math.floor(t*10)%a.length]; //I'm pretty sure you're not supposed to multiply it like that if it's non-linear?
  //f=440;
  st=(st+f*SRi)%1;
  //return Math.sin(t*f*Math.PI*2)*.5;
  return(Math.sin(st*Math.PI*2)**5)*.75;
};

(Edited 9 minutes later.)

·Anonymous F10 months ago, 14 minutes later, 4 months after the original post[T] [B] #664,672

@previous (tuning idea)
let r=a[Math.floor(t*5)%a.length],
    f=220*Math.pow(2,r);

Maybe it's supposed to be like this?

·Calculating the sample rate.10 months ago, 20 hours later, 4 months after the original post[T] [B] #664,919

This apparently only works fine with Dollchan's bytebeat player, but Greggman's is fucking buggy as shit.
Type: Funcbeat
let ticks=0,start=0,
    seconds=3, //until calculation
    sampleRate,
    oscT=0; //the time for oscillator
const TAU=Math.PI*2;
function main(t){
  let freq=440;
  oscT=(oscT+(freq/sampleRate))%1;
  return(t%2<1?Math.sin(oscT*TAU):Math.sin(t*freq*TAU))*.5;
}
return time=>{
  if(time<.1){ //Reset on playback.
    ticks=0;sampleRate=undefined;
  }
  if(sampleRate!==undefined)
    return main(time); //Play main function if there's a sample rate.
  if(ticks===0)
    start=time; //Set start at current time.
  ticks++;
  if(time-start>=seconds) //Check if specified duration has elapsed.
    sampleRate=Math.max(ticks/Math.max(1e-4,time-start),1); //Divide ticks by elapsed duration to get the sample rate.
  return(Math.random()-.5)*.1; //Play noise if there's no calculated sample rate.
};

+Anonymous M10 months ago, 2 days later, 4 months after the original post[T] [B] #665,317

The beat goes on and on.

+dynamic delay line9 months ago, 1 week later, 5 months after the original post[T] [B] #666,157

var PI=Math.PI,TAU=PI*2,
    mod=(n,m)=>(n%m+m)%m;

class Delay{
	constructor(maximumDelayInSamples=0){
		this.sampleRate=44100; //Default sample rate
		this.totalSize=Math.max(4,maximumDelayInSamples+1);
		this.bufferData=new Float32Array(this.totalSize);
		this.writePos=0;
		this.readPos=0;
		this.delay=0;
		this.delayInt=0;
		this.delayFrac=0;
		this.v=new Float32Array(this.totalSize);
		this.feedback=0; //Feedback factor
	}
	setDelay(newDelayInSamples){
		const upperLimit=this.getMaximumDelayInSamples();
		if(newDelayInSamples<0||newDelayInSamples>upperLimit)
			throw new Error("Delay out of bounds");
		this.delay=newDelayInSamples;
		this.delayInt=Math.floor(this.delay);
		this.delayFrac=this.delay-this.delayInt;
	}
	getDelay(){
		return this.delay;
	}
	setFeedback(feedback){
		this.feedback=feedback; //Set feedback factor (0 to 1)
	}
	prepare(numChannels,sampleRate){
		this.sampleRate=sampleRate;
		this.bufferData=new Float32Array(numChannels*this.totalSize);
		this.writePos=new Array(numChannels).fill(0);
		this.readPos=new Array(numChannels).fill(0);
		this.v=new Float32Array(numChannels);
		this.reset();
	}
	setMaximumDelayInSamples(maxDelayInSamples){
		this.totalSize=Math.max(4,maxDelayInSamples+1);
		this.bufferData=new Float32Array(this.totalSize);
		this.reset();
	}
	reset(){
		this.writePos.fill(0);
		this.readPos.fill(0);
		this.v.fill(0);
		this.bufferData.fill(0);
	}
	pushSample(channel,sample){
		const index=(this.writePos[channel]+this.totalSize-1)%this.totalSize;
		this.bufferData[index]=sample;
		this.writePos[channel]=index;
	}
	popSample(channel,delayInSamples=-1,updateReadPointer=true){
		if(delayInSamples>=0)
			this.setDelay(delayInSamples);
		const result=this.interpolateSample(channel);
		if(updateReadPointer)
			this.readPos[channel]=(this.readPos[channel]+this.totalSize-1)%this.totalSize;
		return result;
	}
	interpolateSample(channel){
		const index=(this.readPos[channel]+this.delayInt)%this.totalSize,
		      value1=this.bufferData[index],
		      value2=this.bufferData[(index+1)%this.totalSize];
		return value1+this.delayFrac*(value2-value1);
	}
	processSample(channel,inputSample){
		//Get the delayed sample
		const delayedSample=this.popSample(channel);
		//Calculate the output sample with feedback
		const outputSample=inputSample+delayedSample*this.feedback;
		//Push the new sample into the delay line
		this.pushSample(channel,outputSample);
		return outputSample;
	}
	getMaximumDelayInSamples(){return this.totalSize-1;}
}

let d=new Delay(32000);
d.prepare(1,32000); //Prepare the delay line
d.setFeedback(.85); //Set the feedback factor (0 to 1)
//d.setDelay(2000);
return function dsp(time){
	let o=sin(time*2e3*TAU)*(1-mod(time,.5))**40;
	//d.setDelay((.05+(time%.1))*8000);
	d.setDelay(500+(((1+sin(time*20))/2)*100));
	o=d.processSample(0,o);
	return o*.5;
};

This one can change its delay time in real-time with no lag.

·Anonymous F9 months ago, 11 hours later, 5 months after the original post[T] [B] #666,171

@previous (dynamic delay line)
I cannot fucking stand the
this
keyword in JavaScript, it is the UGLIEST FUCKING SHIT I've ever seen,
with(this){…}
would only ever make it worse. God, I hope there will be some kind of new syntactic sugar to get rid of
this
overuse completely. Might be better to rewrite it as a function (and so you could also avoid
new
in exchange).
It also would've been better if the delay effect wouldn't throw an error when it's out of bounds.

(Edited 9 minutes later.)


+Anonymous O9 months ago, 2 weeks later, 5 months after the original post[T] [B] #668,504

@previous (F)
THIS

·retardedly processed layered kicks harsh noise8 months ago, 4 days later, 5 months after the original post[T] [B] #669,234

https://pastebin.com/raw/7i4JeVLJ
https://files.catbox.moe/9hsb5i.js

+shitty reverb + sampling8 months ago, 2 weeks later, 6 months after the original post[T] [B] #671,298

function mod(n,m){return(n%m+m)%m;}
class Delay{
  constructor(maxDelay){
    this.maxDelay=maxDelay;
    this.buffer=new Array(maxDelay+1).fill(0);
    this.writeIndex=0;
    this.delay=maxDelay;
  }
  setDelay(delay){this.delay=Math.min(delay,this.maxDelay);}
  tick(input){
    this.buffer[this.writeIndex]=input;
    const readIndex=mod(this.writeIndex-this.delay+this.buffer.length,this.buffer.length),
          output=this.buffer[readIndex];
    this.writeIndex=(this.writeIndex+1)%this.buffer.length;
    return output;
  }
  nextOut(){return this.buffer[mod(this.writeIndex-this.delay+this.buffer.length,this.buffer.length)];}
  clear(){this.buffer.fill(0);this.writeIndex=0;}
}
class OnePole{
  constructor(){this.a1=0;this.b0=1;this.state=0;}
  setPole(pole){
    this.a1=pole;
    this.b0=1-pole; //Lowpass with unity DC gain
  }
  tick(input){
    this.state=input*this.b0+this.a1*this.state;
    return this.state;
  }
  clear(){this.state=0;}
}
function isPrime(n){
  if(n<=1)return false;if(n<=3)return true;
  if(n%2===0||n%3===0)return false;
  let i=5;while(i*i<=n){if(n%i===0||n%(i+2)===0)return false;i+=6;}
  return true;
}
class JCRev{
  constructor(T60,sampleRate=44100){
    if(T60<=0)throw new Error("T60 must be positive");
    this.sampleRate=sampleRate;
    this.lastFrame=[0,0];
    const originalLengths=[1116,1356,1422,1617,225,341,441,211,179],
          scaler=sampleRate/44100;
    //Adjust delay lengths to be odd primes
    this.delayLengths=originalLengths.map(len=>{
      let delay=Math.floor(scaler*len);
      if(delay%2===0)delay++;
      while(!isPrime(delay))delay+=2;
      return delay;
    });
    //Initialize allpass delays (using indices 4,5,6 from original lengths)
    this.allpassDelays=[];
    for(let i=0;i<3;i++){
      const len=this.delayLengths[i+4],
            delay=new Delay(len);
      delay.setDelay(len);
      this.allpassDelays.push(delay);
    }
    //Initialize comb delays (indices 0-3)
    this.combDelays=[];
    for(let i=0;i<4;i++){
      const len=this.delayLengths[i],
            delay=new Delay(len);
      delay.setDelay(len);
      this.combDelays.push(delay);
    }
    //Initialize comb lowpass filters (one-pole with pole at 0.2)
    this.combFilters=[];
    for(let i=0;i<4;i++){
      const filter=new OnePole();
      filter.setPole(.2);
      this.combFilters.push(filter);
    }
    //Decorrelation delays (indices 7 and 8)
    this.outLeftDelay=new Delay(this.delayLengths[7]);
    this.outLeftDelay.setDelay(this.delayLengths[7]);
    this.outRightDelay=new Delay(this.delayLengths[8]);
    this.outRightDelay.setDelay(this.delayLengths[8]);
    this.allpassCoefficient=.7;
    this.effectMix=.3;
    this.combCoefficients=new Array(4).fill(0);
    this.setT60(T60);
    this.clear();
  }
  clear(){
    this.allpassDelays.forEach(d=>d.clear());
    this.combDelays.forEach(d=>d.clear());
    this.combFilters.forEach(f=>f.clear());
    this.outLeftDelay.clear();
    this.outRightDelay.clear();
    this.lastFrame=[0,0];
  }
  setT60(T60){
    if(T60<=0)throw new Error("T60 must be positive");
    for(let i=0;i<4;i++){
      const delayTime=this.combDelays[i].delay;
      this.combCoefficients[i]=Math.pow(10,(-3*delayTime)/(T60*this.sampleRate));
    }
  }
  tick(input){
    let current=input;
    for(let i=0;i<3;i++){ //Process through three allpass filters in series
      const delay=this.allpassDelays[i],
            delayed=delay.nextOut(),
            allpassOutput=-current+delayed,
            delayInput=current+delayed*this.allpassCoefficient;
      delay.tick(delayInput);
      current=allpassOutput;
    }
    let combSum=0;
    for(let i=0;i<4;i++){ //Process through four comb filters in parallel
      const combDelay=this.combDelays[i],
            combFilter=this.combFilters[i],
            coeff=this.combCoefficients[i],
            delayed=combDelay.nextOut(),
            filtered=combFilter.tick(delayed),
            feedback=filtered*coeff;
      combDelay.tick(current+feedback);
      combSum+=delayed;
    }
    //Apply decorrelation delays to left and right channels
    const left=this.outLeftDelay.tick(combSum),
          right=this.outRightDelay.tick(combSum);
    //Mix wet/dry signals
    const dryMix=1-this.effectMix,wetMix=this.effectMix;
    this.lastFrame[0]=input*dryMix+left*wetMix;
    this.lastFrame[1]=input*dryMix+right*wetMix;
    return this.lastFrame;
  }
}
let r=new JCRev(2.1,32000),tick=0,p=(x,y)=>Math.pow(Math.abs(x),y)*Math.sign(x);
function f(t){
  let o=0;
  for(let i=0;i<5;i++){
    o+=p(sin(t*2000*(2**((i**1.75)/9))),2)*p(sin((t-8.4)*(1+i)),3000)*sin(t*(10-i)*8)*.1;
  }
  //if(tick%10000===0)r.clear();
  tick++;
  o=r.tick(o);
  for(let i=0;i<o.length;i++){
    o[i]=Math.tanh(o[i]);
  }
  return o;
}
let array=[],SR=16000,len=SR*10;
for(let i=0;i<len;i++){array.push([...f(i/SR)]);}
function lerp(a,b,x){return a+x*(b-a);}
function lerp2(a,b,x){return[a[0]+x*(b[0]-a[0]),a[1]+x*(b[1]-a[1])];}
function read(a,i){return lerp2(a[mod(floor(i),a.length)],a[mod(floor(i)+1,a.length)],(i%1+1)%1);}
let r2=new JCRev(3,32000);
return function DSP(t){
  let o=[0,0];
  for(let i=0;i<6;i++){
    //let add=read(array,mod((t*-.5*SR*(1+i**1.4))+(sin(t*(i+2)*.4)*sin(t*(i+1))*SR*1.2),max(100,min((1-abs(sin(t*.001+i)))*array.length,array.length))));
    let add=read(array,SR*sin(t*.3+6*i)*(i+1));
    o[0]+=add[0]*.6;o[1]+=add[1]*.6;
  }
  return r2.tick((o[0]-o[1])*.4);
}

·FUNCBEAT MY ASS8 months ago, 13 hours later, 6 months after the original post[T] [B] #671,333

function skew(x,a){return Math.min(x/a,(1-x)/(1-a));} //x:[0–1],a:[0–1]
function powCurve(x,a){return a===0?1:(a>0?Math.pow(1-x,a):Math.pow(x,-a));} //x:[0–1],a:[-∞–∞]

// 1. Sigmoid envelope (S-shaped curve with adjustable steepness)
//    a=0 → linear, a>0 → steeper transition
function sigmoidEnv(x, a) {
    if (a === 0) return x;
    const k = a * 20; // Steepness multiplier
    return 1 / (1 + Math.exp(-k * (x - 0.5)));
}

// 2. Quadratic pulse (double parabola peaking at `a`)
//    Creates a sharp "n̂" shape with parabolic segments
function quadraticPulse(x, a) {
    a = Math.min(Math.max(a, 1e-4), 1 - 1e-4); // Avoid division by zero
    return x < a ? (x * x) / (a * a) : ((1 - x) * (1 - x)) / ((1 - a) * (1 - a));
}

// 3. Bell curve (Gaussian-like peak centered at `a`, fixed width)
//    Returns values in [0,1], peaking at x=a
function bellCurve(x, a) {
    const spread = 0.15; // Adjust this to change curve width
    const dist = x - a;
    return Math.exp(-(dist * dist) / (2 * spread * spread));
}

// 4. Smoothstep with variable steepness (generalized logistic)
//    a=1 → linear, a>1 → steeper, a<1 → smoother transition
function smoothStep(x, a) {
    a = Math.max(a, 0); // Ensure non-negative
    x = Math.min(Math.max(x, 0), 1);
    return Math.pow(x, a) / (Math.pow(x, a) + Math.pow(1 - x, a));
}

// 5. Double-exponential (custom asymmetric envelope)
//    a>0: left side exponential, a<0: right side exponential
function expDouble(x, a) {
    return a === 0 ? 1 : 
           a > 0 ? 1 - Math.pow(1 - x, a * 4 + 1) : 
                   Math.pow(x, -a * 4 + 1);
}

let envFunctions=[skew,powCurve,sigmoidEnv,quadraticPulse,bellCurve,smoothStep,expDouble];
function makeEnv(n=5){
    let layers=[];
    for(let i=0;i<n;i++){
        layers.push({f:Math.floor(Math.random()*envFunctions.length),a:Math.random()});
    }
    return x=>layers.reduce((p,c)=>envFunctions[c.f](p,c.a),x);
}
function mod(n,m){return(n%m+m)%m;}
function wavefold(x){return Math.abs(mod(x-1,4)-2)-1;}
function lerp(a,b,x){return a+x*(b-a);}
let envs=[],tick=0,last=0,first=0,duration=lerp(200,8000,Math.random());
for(let i=0;i<3;i++){envs.push(makeEnv());}
first=envs.reduce((p,c)=>c(p),tick);
if(isNaN(first))first=0;
function DSP_1(time){
    let o=envs.reduce((p,c)=>c(p),tick);
    tick+=1/duration;
    if(tick>=1){
        envs=[];
        if(!isNaN(o))last=o;
        for(let i=0;i<3;i++){envs.push(makeEnv());}
        first=envs.reduce((p,c)=>c(p),0);if(isNaN(first))first=0;
        tick-=1;
        duration=lerp(200,8000,Math.random())
    }
    if(isNaN(o)){return Math.random();}
    return wavefold(o-first);
}
function safe(x){return isNaN(x)?0:x;}
let sample=[],writeIndex=0;
for(let i=0;i<1100;i++)sample[i]=0;
return function DSP(time){
    sample[floor(writeIndex)]=wavefold((sample[floor(writeIndex)]+DSP_1(time))*.9);
    writeIndex=mod(writeIndex+1+safe(envs[0](time%1)*3.4),sample.length);
    if(Math.random()<1e-4){
        let newLen=floor(lerp(300,8000,Math.random())),oldLen=sample.length,newArray=[];
        for(let i=0;i<newLen;i++){
            let t=(i/newLen)*oldLen;
            newArray[i]=safe(lerp(sample[floor(t)],sample[(floor(t)+1)%oldLen],t%1));
        }
        sample=newArray;
        writeIndex=(writeIndex/oldLen)*newLen;
    }
    return sample[floor(writeIndex)];
}

Thinking about envelopes, textures, curves, waveshaping, modulation…

·phoneposting boredom8 months ago, 23 hours later, 6 months after the original post[T] [B] #671,420

let easeFunctions=[(x,a)=>x>=a?1:0,(x,a)=>Math.pow(x,1+a),(x,a)=>Math.pow(x,1/(1+a)),(x,a)=>((Math.pow(Math.abs(2*(x-.5)),1+2*a)*Math.sign(x-.5))+1)/2,(x,a)=>((Math.pow(Math.abs(2*(x-.5)),1/(1+a))*Math.sign(x-.5))+1)/2,(x,a)=>Math.min(x/(1-a),Math.max(.5,1-((1-x)/(1-a)))),x=>.5+Math.sin(Math.PI*(x-.5))*.5,x=>(3*x*x)-(2*x*x*x),(x,a)=>a===0?x:(Math.exp(a*x)-1)/(Math.exp(a)-1),(x,a)=>x+a*x*(1-x),(x,a)=>{if(a===0)return x;a=(1/(1-a))-1;let s=a*(x-.5),mn=Math.atan(-a*.5),mx=-mn;return(Math.atan(s)-mn)/(mx-mn);},(x,a)=>a===0?x:Math.floor(x/a)*a],
    mod=(n,m)=>(n%m+m)%m,oscify=(f,x,...p)=>2*f(1-2*Math.abs(.5-mod(x,1)),...p)-1,
    noteHz=n=>440*Math.pow(2,n/12),TAU=Math.PI*2;

let notes=[ //(n)ote, (s)tart, (d)uration…
    /*{n:0,s:0,d:.3},{n:-3,s:.2,d:.5},
    {n:2,s:2,d:1},{n:5,s:2.4,d:1},
    {n:2,s:4.5,d:1},{n:7,s:5.5,d:1},
    
    {n:-15,s:0,d:2},{n:-5,s:0,d:2},
    {n:-10,s:2,d:2},{n:-3,s:2,d:2},
    {n:-14,s:4,d:2},{n:-3,s:4,d:2},
    {n:-12,s:6,d:2},{n:-5,s:6,d:2}*/
    //console.log(JSON.stringify([...notes,...(notes.map(x=>({n:x.n-2,s:x.s+4,d:x.d})))]));
    {n:-20,s:0,d:4},{n:-17,s:.5,d:4},{n:-13,s:1,d:4},{n:-10,s:1.5,d:4},{n:-6,s:0,d:4},{n:-5,s:0,d:4},{n:-3,s:0,d:4},
    {n:-22,s:4,d:4},{n:-19,s:4,d:4},{n:-15,s:4,d:4},{n:-12,s:4,d:4},{n:-8,s:4,d:4},{n:-7,s:4.5,d:4},{n:-5,s:5,d:4},
    {n:28,s:0,d:9,v:.1},{n:14,s:0,d:9,v:.2},{n:11,s:3.5,d:2,v:.4},{n:16,s:7.5,d:.5,v:1.4}
],humanizeNote=({n,s,d,...rest})=>({n:n+(Math.random()-.5)*.4,s:s+(Math.random()-.5)*.05,d:d+Math.random()*.05,...rest});
notes=notes.flatMap(n=>[humanizeNote(n),humanizeNote(n),humanizeNote(n)]);
notes=notes.map(({...keys})=>({...keys,p:(Math.random()*2)-1}));

return function DSP(t){
    let output=[0,0];
    t=mod(t*1.05,8);
    for(const note of notes){
        let start=note.s,dur=note.d,end=start+dur;
        if(t>=start&&t<end){
            let localT=t-start,freq=noteHz(note.n),
                attack=.01,release=1,env=Math.min(localT/attack,1,(dur-localT)/release);
            //output+=Math.sin(TAU*freq*localT)*env;
            //output-=oscify(easeFunctions[1],freq*localT,(sin(localT)+1)*.5)*env;
            let osc=oscify(easeFunctions[mod(floor(note.n+localT*4),easeFunctions.length)],freq*localT,(sin(localT*3+note.n+t*.1)+1)*.5)*env*("v"in note?note.v:1);
            output[0]-=osc*(1-note.p);output[1]-=osc*note.p;
        }
    }
    return[Math.tanh(output[0]*.1),Math.tanh(output[1]*.1)];
};

·yet another filter8 months ago, 2 hours later, 6 months after the original post[T] [B] #671,432

let SAMPLE_RATE=32000,TAU=Math.PI*2;
class BiquadFilter{ //musicdsp.org/files/Audio-EQ-Cookbook.txt
  constructor(type="lowpass",freq=1000,Q=1){
    this.type=type;
    this.Fs=SAMPLE_RATE;
    this.f0=freq;this.Q=Q;
    this.dBGain=0;this.S=1;
    this.BW=undefined;
    this.a0=1;this.a1=0;this.a2=0;
    this.b0=1;this.b1=0;this.b2=0;
    this.x1=0;this.x2=0;
    this.y1=0;this.y2=0;
    this.updateCoefficients();
  }
  setType(type){this.type=type;this.updateCoefficients();}
  setFrequency(f0){this.f0=f0;this.updateCoefficients();}
  setQ(Q){this.Q=Q;this.updateCoefficients();}
  setGain(dB){this.dBGain=dB;this.updateCoefficients();}
  setSlope(S){this.S=S;this.updateCoefficients();}
  setBandwidth(BW){this.BW=BW;this.updateCoefficients();}
  updateCoefficients(){
    const{type,Fs,f0,Q,dBGain,S,BW}=this; 
    let A=1;
    if(type==="peaking"||type==="lowshelf"||type==="highshelf")A=Math.pow(10,dBGain/40);
    const w0=TAU*f0/Fs,cosw0=Math.cos(w0),sinw0=Math.sin(w0);
    let alpha=0;
    switch(type){
      case"lowpass":case"highpass":case"bandpass":case"notch":case"allpass":case"peaking":
        if(BW!==undefined&&(type==="bandpass"||type==="notch")){
          const numerator=(Math.log(2)/2)*BW*w0;
          alpha=sinw0*Math.sinh(numerator/sinw0);
        }else{
          alpha=sinw0/(2*Q);
        }
      break;
      case"lowshelf":case"highshelf":
        const sqrtTerm=Math.sqrt((A+1/A)*(1/S-1)+2);
        alpha=(sinw0/2)*sqrtTerm;
      break;
      default:throw new Error(`Unsupported filter type: ${type}`);
    }
    switch(type){
      case"lowpass":
        this.b0=(1-cosw0)/2;this.b1=1-cosw0;this.b2=this.b0;
        this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;
      break;
      case"highpass":
        this.b0=(1+cosw0)/2;this.b1=-(1+cosw0);this.b2=this.b0;
        this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;
      break;
      case"bandpass":
        this.b0=sinw0/2;this.b1=0;this.b2=-this.b0;
        this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;
      break;
      case"notch":
        this.b0=1;this.b1=-2*cosw0;this.b2=1;
        this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;
      break;
      case"allpass":
        this.b0=1-alpha;this.b1=-2*cosw0;this.b2=1+alpha;
        this.a0=1+alpha;this.a1=-2*cosw0;this.a2=1-alpha;
      break;
      case"peaking":
        this.b0=1+alpha*A;this.b1=-2*cosw0;this.b2=1-alpha*A;
        this.a0=1+alpha/A;this.a1=-2*cosw0;this.a2=1-alpha/A;
      break;
      case"lowshelf":{
        const twoSqrtAlpha=2*Math.sqrt(A)*alpha;
        this.b0=A*((A+1)-(A-1)*cosw0+twoSqrtAlpha);
        this.b1=2*A*((A-1)-(A+1)*cosw0);
        this.b2=A*((A+1)-(A-1)*cosw0-twoSqrtAlpha);
        this.a0=(A+1)+(A-1)*cosw0+twoSqrtAlpha;
        this.a1=-2*((A-1)+(A+1)*cosw0);
        this.a2=(A+1)+(A-1)*cosw0-twoSqrtAlpha;
        break;
      }
      case"highshelf":{
        const twoSqrtAlpha=2*Math.sqrt(A)*alpha;
        this.b0=A*((A+1)+(A-1)*cosw0+twoSqrtAlpha);
        this.b1=-2*A*((A-1)+(A+1)*cosw0);
        this.b2=A*((A+1)+(A-1)*cosw0-twoSqrtAlpha);
        this.a0=(A+1)-(A-1)*cosw0+twoSqrtAlpha;
        this.a1=2*((A-1)-(A+1)*cosw0);
        this.a2=(A+1)-(A-1)*cosw0-twoSqrtAlpha;
        break;
      }
      default:throw new Error(`Unsupported filter type: ${type}`);
    }
    const a0=this.a0;
    this.b0/=a0;this.b1/=a0;this.b2/=a0;this.a1/=a0;this.a2/=a0;
  }
  process(sample){
    const x=sample,
          y=this.b0*x+this.b1*this.x1+this.b2*this.x2-this.a1*this.y1-this.a2*this.y2;
    this.x2=this.x1;this.x1=x;this.y2=this.y1;this.y1=y;
    return y;
  }
}
//Example usage…
function noteHz(n){return 440*2**(n/12);}
const chord=[0,4,7,11,14],filters=chord.map(n=>new BiquadFilter(Math.random()<.5?"bandpass":"lowpass",noteHz(n),100));
return function DSP(time){
  let o=0;
  for(let i=0,f;i<filters.length;i++){
    f=filters[i];
    f.setFrequency(noteHz(chord[i]+[0,3,5,-2][floor(time/2%4)]+sin(time*10)*.1)*.56)
    o+=f.process((Math.random()-.5)+.4*sin(1/((time%(.2+i*.1))**1.3+.001)));
  }
  return Math.tanh(o*.1);
}

·even more filters8 months ago, 14 hours later, 6 months after the original post[T] [B] #671,505

DSP filter test:

·perlin noise test8 months ago, 2 hours later, 6 months after the original post[T] [B] #671,515

let SEED=4,hash=[
  208,34,231,213,32,248,233,56,161,78,24,140,71,48,140,254,245,255,247,247,40,
  185,248,251,245,28,124,204,204,76,36,1,107,28,234,163,202,224,245,128,167,204,
  9,92,217,54,239,174,173,102,193,189,190,121,100,108,167,44,43,77,180,204,8,81,
  70,223,11,38,24,254,210,210,177,32,81,195,243,125,8,169,112,32,97,53,195,13,
  203,9,47,104,125,117,114,124,165,203,181,235,193,206,70,180,174,0,167,181,41,
  164,30,116,127,198,245,146,87,224,149,206,57,4,192,210,65,210,129,240,178,105,
  228,108,245,148,140,40,35,195,38,58,65,207,215,253,65,85,208,76,62,3,237,55,89,
  232,50,217,64,244,157,199,121,252,90,17,212,203,149,152,140,187,234,177,73,174,
  193,100,192,143,97,53,145,135,19,103,13,90,135,151,199,91,239,247,33,39,145,
  101,120,99,3,186,86,99,41,237,203,111,79,220,135,158,42,30,154,120,67,87,167,
  135,176,183,191,253,115,184,21,233,58,129,233,142,39,128,211,118,137,139,255,
  114,20,218,113,154,27,127,246,250,1,8,198,250,209,92,222,173,21,88,102,219
];
function mod(x,y){return(x%y+y)%y;}
function noise2(x,y){
  let tmp=hash[mod(Math.floor(y)+SEED,256)];
  return hash[mod(tmp+Math.floor(x),256)];
}
function linInter(x,y,s){return x+s*(y-x);}
function smoothInter(x,y,s){return linInter(x,y,s*s*(3-2*s));}
function noise2D(x,y){
  let xInt=Math.floor(x),yInt=Math.floor(y),
      xFrac=x-xInt,yFrac=y-yInt,
      s=noise2(xInt,yInt),
      t=noise2(xInt+1,yInt),
      u=noise2(xInt,yInt+1),
      v=noise2(xInt+1,yInt+1),
      low=smoothInter(s,t,xFrac),
      high=smoothInter(u,v,xFrac);
  return smoothInter(low,high,yFrac);
}
function perlin2D(x,y,freq,depth){
  let xa=x*freq,ya=y*freq,
      amp=1,fin=0,div=0;
  for(let i=0;i<depth;i++){
    div+=256*amp;
    fin+=noise2D(xa,ya)*amp;
    amp/=2;
    xa*=2;ya*=2;
  }
  return fin/div;
}
let TAU=Math.PI*2,sine=x=>Math.sin(TAU*x),noteHz=n=>440*2**(n/12),chord=[0,3,7,10,14,15,17,22],slots=Array(chord.length*2).fill(0),SR=16000,mix=(a,b,x)=>a+x*(b-a),readArray=(a,i)=>{let fi=Math.floor(i),li=(i%1+1)%1,al=a.length;return mix(a[mod(fi,al)],a[mod(fi+1,al)],li);};
return function DSP(time){
  let o=0,s=1.2,trans=readArray([-15,-12,-10,-5,-3,-7,-8],floor(time/s)+mod(time/s,1)**24);
  for(let i=0;i<chord.length;i++){
    o+=sine(slots[2*i])*perlin2D(i,time,10,3)**8+sine(slots[(2*i)+1])*perlin2D(chord.length+i,time+3,10,2)**8;
    slots[2*i]+=(noteHz(chord[i]+.05+trans)+perlin2D(i-10,time*.5,10,2)*10)/SR;
    slots[(2*i)+1]+=(noteHz(chord[i]+-.05+trans)+perlin2D(i-15,time*.8,7,2)*10)/SR;
  }
  return o*.3;
};

(Edited 9 minutes later.)

·better delay line8 months ago, 4 hours later, 6 months after the original post[T] [B] #671,528

@666,157 (dynamic delay line)
class DelayLine{
  constructor(options={}){
    const{
      maxDelayInSamples=44100,numChannels=2,sampleRate=44100,interpolationType="linear"
    }=options;
    this.interpolationType=interpolationType;
    this.sampleRate=sampleRate;
    this.numChannels=numChannels;
    this.maxDelay=maxDelayInSamples;
    this.totalSize=Math.max(4,maxDelayInSamples+2);
    //circular buffer for each channel
    this.buffer=Array.from({length:numChannels},()=>new Array(this.totalSize).fill(0));
    this.writePos=new Array(numChannels).fill(0);
    this.readPos=new Array(numChannels).fill(0);
    this.v=new Array(numChannels).fill(0); //for Thiran interpolation
    this.setDelay(0); //initialize delay parameters
  }
  setDelay(newDelay){
    const upperLimit=this.totalSize-2;
    this.delay=Math.max(0,Math.min(newDelay,upperLimit));
    this.delayInt=Math.floor(this.delay);
    this.delayFrac=this.delay-this.delayInt;
    this.updateInternalVariables();
  }
  updateInternalVariables(){
    switch(this.interpolationType){
      case"lagrange3rd":
        if(this.delayFrac<2&&this.delayInt>=1){
          this.delayFrac++;this.delayInt--;
        }
      break;
      case"thiran":
        if(this.delayFrac<.618&&this.delayInt>=1){
          this.delayFrac++;this.delayInt--;
        }
        this.alpha=(1-this.delayFrac)/(1+this.delayFrac);
      break;
    }
  }
  pushSample(channel,sample){
    this.buffer[channel][this.writePos[channel]]=sample;
    this.writePos[channel]=(this.writePos[channel]+this.totalSize-1)%this.totalSize;
  }
  popSample(channel,delayInSamples=-1,updateReadPointer=true){
    if(delayInSamples>=0)this.setDelay(delayInSamples);
    let result;
    switch(this.interpolationType){
      case"none":result=this.interpolateNone(channel);break;
      case"linear":result=this.interpolateLinear(channel);break;
      case"lagrange3rd":result=this.interpolateLagrange3rd(channel);break;
      case"thiran":result=this.interpolateThiran(channel);break;
    }
    if(updateReadPointer)
      this.readPos[channel]=(this.readPos[channel]+this.totalSize-1)%this.totalSize;
    return result;
  }
  //interpolation methods
  interpolateNone(channel){
    const index=(this.readPos[channel]+this.delayInt)%this.totalSize;
    return this.buffer[channel][index];
  }
  interpolateLinear(channel){
    const idx1=(this.readPos[channel]+this.delayInt)%this.totalSize,
          idx2=(idx1+1)%this.totalSize,
          y1=this.buffer[channel][idx1],y2=this.buffer[channel][idx2];
    return y1+this.delayFrac*(y2-y1);
  }
  interpolateLagrange3rd(channel){
    const idx1=(this.readPos[channel]+this.delayInt)%this.totalSize,
          idx2=(idx1+1)%this.totalSize,idx3=(idx2+1)%this.totalSize,idx4=(idx3+1)%this.totalSize,
          [y1,y2,y3,y4]=[
      this.buffer[channel][idx1],
      this.buffer[channel][idx2],
      this.buffer[channel][idx3],
      this.buffer[channel][idx4]
    ],d=this.delayFrac,d1=d-1,d2=d-2,d3=d-3;
    return y1*(-d1*d2*d3/6)+d*(
      y2*(d2*d3*.5)+
      y3*(-d1*d3*.5)+
      y4*(d1*d2/6)
    );
  }
  interpolateThiran(channel){
    const idx1=(this.readPos[channel]+this.delayInt)%this.totalSize,idx2=(idx1+1)%this.totalSize,
          [y1,y2]=[this.buffer[channel][idx1],this.buffer[channel][idx2]],
          output=this.delayFrac<1e-9?y1:y2+this.alpha*(y1-this.v[channel]);
    this.v[channel]=output;
    return output;
  }
  //utility methods
  reset(){
    this.writePos.fill(0);this.readPos.fill(0);this.v.fill(0);
    this.buffer.forEach(ch=>ch.fill(0));
  }
  setMaxDelay(samples){
    this.totalSize=Math.max(4,samples+2);
    this.buffer=Array.from({length:this.numChannels},()=>new Array(this.totalSize).fill(0));
    this.reset();
  }
}
// === Usage example ===
let delay=new DelayLine({
  maxDelayInSamples:8000,
  numChannels:1,
  interpolationType:"lagrange3rd"
}),last=0;
return t=>{
  let o=(sin(PI*2*300*t)*(1-t%.3)**90)+(sin(PI*2*800*t)*(1-t%.45)**100)*.5;
  delay.pushSample(0,o-last*.7);
  delay.setDelay(1000+sin(t*2.5)*1000);
  last=delay.popSample(0);
  return Math.tanh(o+last);
};

·drum-like metallic material8 months ago, 1 hour later, 6 months after the original post[T] [B] #671,531

@previous (better delay line)
Replace the example below with this:
function shuffle(array){
  for(let i=array.length-1;i>=0;i--){
    const j=Math.floor(Math.random()*(i+1));
    [array[i],array[j]]=[array[j],array[i]];
  }
}
let mix=(a,b,x)=>a+x*(b-a),delays=[],last=[],route=[],mult=[],del=[],sampleRate=8000;
for(let i=0;i<30;i++){
  delays.push(new DelayLine({
    maxDelayInSamples:sampleRate,
    numChannels:1,
    interpolationType:"linear"
  }));
  del.push(mix(10,200,Math.random()**.5));
  delays[i].setDelay(del[i]);
  last.push(0);route.push(i);mult.push(mix(.85,.94,Math.random()));
}
shuffle(route);
let processDelays=(x,time)=>{
  let o=x;
  for(let i=0;i<delays.length;i++){
    delays[i].pushSample(0,o-last[route[i]]*mult[i])
    delays[i].setDelay(del[i]+sin(i**3.8+time*1.4)*60);
    last[route[i]]=delays[i].popSample(0);
    o+=last[route[i]]*.12
  }
  return o;
};
return t=>{
  let o=(sin(PI*2*200*t)*(1-t%[.23,.3,.23,.55][(t*.7|0)%4])**90)+(Math.random()*(1-t%[.2,.5,.63,.7][(t*1.3|0)%4])**100)*.2;
  o=processDelays(o,t);
  return Math.tanh(o);
};

·Phoneposter7 months ago, 1 week later, 7 months after the original post[T] [B] #672,562

can_t you take a hint.jpgOne thing I've noticed about Greggman's HTML5 Bytebeat is that instead of simply setting
window.location.hash
(which would add a new entry to the history), it uses
window.location.replace()
to change the URL without adding a new history entry. This method updates the URL without creating additional entries in the browser's history stack, whereas Dollchan does
window.location.hash=`#v3b64${btoa(String.fromCharCode.apply(undefined,deflateRaw(JSON.stringify(songData)))).replaceAll("=","")}`;
every time you type a character which is annoying ('cause it floods your history). Besides, there's also
window.history.replaceState(null,"",URL)

·Phoneposter7 months ago, 5 days later, 7 months after the original post[T] [B] #673,014

thumb.jpg
let nHz=n=>459*(2**(n/12)),TAU=Math.PI*2,sine=x=>Math.sin(x*TAU),absPow=(x,y)=>Math.pow(Math.abs(x),y)*Math.sign(x),list=[[-16,0],[-4,4],[-1,5],[-5,8],[-4,10],[-9,12],[-16,14], [3,0],[7.8,1],[13.2,2],[1,5],[-1.3,10],[-2,12], [20.2,15,.25], [4,1],[6,3.5],[4,8],[3,13],[0,0],[1,4],[7,11]].flatMap(a=>[a,[a[0]+(Math.random()-.5)*.2,a[1]+Math.random()*.1,...(a[2]?[a[2]]:[])],[a[0]+(Math.random()-.5)*.3,a[1]+Math.random()*.1,...(a[2]?[a[2]]:[])]]).sort((a,b)=>a[1]-b[1]),len=16,voices=[],sampleRate=16e3,timer={time:0,add:(65/(60/4))/sampleRate,noteIdx:0};
return t=>{
  timer.time+=timer.add;
  if(timer.time>=len){timer.time%=len;timer.noteIdx=0;}
  while(timer.noteIdx<list.length&&
     list[timer.noteIdx][1]<=timer.time){
    voices.push({f:nHz(list[timer.noteIdx][0]),d:10,ot:0,t:0,v:list[timer.noteIdx][2]||(.5+Math.random()*.5),c:Math.random()<.5?0:1});
    timer.noteIdx++;
  }
  let o=[0,0];
  for(let i=0;i<voices.length;i++){
    let v=voices[i];
    o[v.c]+=absPow(sine((v.ot+=v.f/sampleRate)*.5001),1/v.v)*sine(v.ot*.255)**2*((1-(v.t/v.d))**Math.max(7-t*.2,.8))*Math.cos(v.t*v.v*2)*v.v;
    v.t+=timer.add;v.f+=v.t*.8/sampleRate;
    if(v.t>=v.d){voices.splice(i,1);i--;}
  }
  let k=(absPow(sine(2/(.1+timer.time%2)),1+timer.time*.2)*Math.min(t*.05,1.5))+Math.random()*.1*(1-((timer.time+1)*.5%1))**20;
  o[0]-=k;o[1]-=k;
  return[Math.tanh(o[0]*.2),Math.tanh(o[1]*.2)];
}

+Anonymous Q7 months ago, 6 minutes later, 7 months after the original post[T] [B] #673,015

I love how somebody’s just posting JavaScript snippets with no dependencies and no explanations and expects us to be able to run it.

·Anonymous Q7 months ago, 1 minute later, 7 months after the original post[T] [B] #673,016

Tbh I haven’t taken signal processing though…

·Phoneposter7 months ago, 5 minutes later, 7 months after the original post[T] [B] #673,017

@673,015 (Q)
Google “bytebeat” and the first result should be Dollchan's bytebeat composer, then you just copy one of these snippets into the editor, and change from Bytebeat to Funcbeat, sometimes the sample rate needs to be changed as well.

(Edited 4 minutes later.)


+Anonymous R7 months ago, 1 day later, 7 months after the original post[T] [B] #673,101

I don't get any of this.

(Edited 24 seconds later.)


·FFT test (WIP)6 months ago, 3 weeks later, 8 months after the original post[T] [B] #674,102

const TAU=Math.PI*2;
function sineWindow(x){ //x: 0 to 1
	return Math.sin(TAU*(x-.25))*.5+.5;
}
function multiply(a,b){return{r:a.r*b.r-a.i*b.i,i:a.r*b.i+a.i*b.r};}
function add(a,b){return{r:a.r+b.r,i:a.i+b.i};}
function subtract(a,b){return{r:a.r-b.r,i:a.i-b.i};}
function FFT(a,invert=false){
	let n=a.length;
	if(n===1)return;
	if(Math.log2(n)%1!==0)throw new Error("Length isn't a power of 2.");
	let a0=new Array(n/2),a1=new Array(n/2);
	for(let i=0;2*i<n;i++){a0[i]=a[2*i];a1[i]=a[2*i+1];}
	FFT(a0,invert);FFT(a1,invert);
	let ang=TAU/n*(invert?-1:1),
	    wn={r:Math.cos(ang),i:Math.sin(ang)},
	    w={r:1,i:0};
	for(let i=0;i<n/2;i++){
		let even=a0[i],odd=multiply(w,a1[i]),
		    sum=add(even,odd),difference=subtract(even,odd);
		a[i]=sum;a[i+n/2]=difference;
		if(invert){
			a[i].r/=2;a[i].i/=2;
			a[i+n/2].r/=2;a[i+n/2].i/=2;
		}
		w=multiply(w,wn);
	}
}
let mod=(n,m)=>(n%m+m)%m,
    read=(a,i)=>a[mod(Math.floor(i),a.length)],
    sine=x=>Math.sin(x*TAU),
    mix=(a,b,x)=>a+(b-a)*x;
class realtimeFFT{
  constructor(size=11){
    this.FFT_SIZE=2**size;
    this.inputBuffer=[new Float32Array(this.FFT_SIZE).fill(0),new Float32Array(this.FFT_SIZE).fill(0)];
    this.outputBuffer=[new Float32Array(this.FFT_SIZE).fill(0),new Float32Array(this.FFT_SIZE).fill(0)];
    this.bufferIndex=[0,-(2**size)*.5];
  }
  process(x,t){
    //record input
    this.inputBuffer[0][this.bufferIndex[0]]=x;
    if(this.bufferIndex[1]>=0)
      this.inputBuffer[1][this.bufferIndex[1]]=x;
    //windowing
    let win=sineWindow,
        o=this.outputBuffer[0][this.bufferIndex[0]]*win(this.bufferIndex[0]/this.FFT_SIZE)+this.outputBuffer[1][this.bufferIndex[1]]*win(this.bufferIndex[1]/this.FFT_SIZE);
    for(let bI=0;bI<2;bI++){
    this.bufferIndex[bI]++;
    if(this.bufferIndex[bI]>=this.FFT_SIZE){
      let a=[...this.inputBuffer[bI]].map(x=>({r:x,i:0}));
      FFT(a,false);
      let b=a.map(x=>({r:x.r,i:x.i}));
      for(let i=0;i<a.length;i++){
        a[i]=read(b,i*(1+sin(t*3)*.1));
      }
      FFT(a,true);
      for(let i=0;i<a.length;i++){
        this.outputBuffer[bI][i]=(a[i].r+a[i].i)*.5;//Math.sqrt(a[i].r**2+a[i].i**2);
      }
      this.bufferIndex[bI]=0;
    }
    }
    return o;
  }
}
let eff=new realtimeFFT(10);
return t=>{
  let o=(sine(t*440)+sine(t*550)+sine(t*163))*.1;
  return eff.process(o,t);
};

+Anonymous S6 months ago, 2 days later, 8 months after the original post[T] [B] #674,182

This rocks, man.

·sampled string wave equation test6 months ago, 6 days later, 8 months after the original post[T] [B] #674,342

let mod=(n,m)=>(n%m+m)%m,
    read=(a,i)=>a[mod(Math.floor(i),a.length)],
    mix=(a,b,x)=>a+(b-a)*x,
    noteHz=n=>440*2**(n/12),
    rand=(...a)=>a.length===2?mix(a[0],a[1],Math.random()):a.length===1?Math.random()*x[0]:Math.random(),
    pow2=(x,y)=>Math.pow(Math.abs(x),y)*Math.sign(x);
class StringWave{
  constructor(N,damp=1.001,c=.2){
    this.N=N;this.heights=new Array(N);this.velocities=new Array(N);
    this.pluck(rand(),pow2(rand(-1,1),.3));
    this.damp=damp;this.c=c;
  }
  pluck(x,y){
    let xi=x*this.N;
    for(let i=0;i<this.N;i++){
      this.heights[i]=i<xi?y*(i/xi):y*(1-(i/xi)/(this.N-xi));
      this.velocities[i]=0;
    }
  }
  update(){
    const v=this.velocities,h=this.heights;
    for(let i=1;i<this.N-1;i++)
      v[i]=(v[i]+2*(h[i-1]+h[i+1]-2*h[i]))/this.damp;
    for(let i=0;i<this.N;i++)
      h[i]+=v[i]*this.c;
  }
}
function normalize(array){
  let mn=Math.min(...array),
      mx=Math.max(...array),
      o=[];
  if(mn===mx)return array;
  for(let i=0;i<array.length;i++)
    o[i]=mix(-1,1,(array[i]-mn)/(mx-mn));
  return o;
}
let SR=8000,
    N=70,chord=[0,3,7,10,14,15,17,22],w=[],a=[];
for(let i=0;i<8;i++){
  w[i]=new StringWave(N,1.0005,.29);
  a[i]=[];
  for(let j=0;j<SR*2.8;j++){
    w[i].update();
    a[i][j]=w[i].heights[floor(N*(.25+sin((j/SR)*3)*.2))];
  }
  a[i]=normalize(a[i]);
  //declicking
  let fade=128;
  for(let j=0;j<fade;j++){
    a[i][j]*=(j+1)/(fade+1);
    a[i][(a[i].length-1)-j]*=(j+1)/(fade+1);
  }
}


let samp=()=>{
  let t=0;
  return(si,sa)=>{
    t+=sa/32e3;
    let a0=read(a[mod(floor(si),a.length)],t),
        a1=read(a[mod(floor(si)+1,a.length)],t);
    return mix(a0,a1,mod(si,1));
  };
},s=chord.map(n=>[samp(),samp()]);
return t=>{
  let o=0;
  for(let i=0;i<chord.length;i++){
    //o+=read(a[mod(floor(i+t),a.length)],t*noteHz(chord[i])*N)-read(a[mod(floor(i+t*.5),a.length)],-t*noteHz(chord[i])*1.02*N);
    o+=s[i][0](t+i,noteHz(chord[i]+sin(t*7+i*.3)*.05)*N)-s[i][1](t+i*2,-noteHz(chord[i]+sin(t*6.5+i*.6)*.06)*N);
  }
  return tanh(o*.3);
};

Click here to play


+Anonymous T6 months ago, 3 hours later, 8 months after the original post[T] [B] #674,347

@previous (sampled string wave equation test)
There's an x[0] instead of a[0] in rand…

·stack-oriented math expression6 months ago, 3 days later, 8 months after the original post[T] [B] #674,437

let mod=(n,m)=>(n%m+m)%m,
    oper={
  "+":(a,b)=>a+b,"-":(a,b)=>a-b,
  "*":(a,b)=>a*b,"/":(a,b)=>a/b,
  "%":mod,"**":(a,b)=>a**b
},fun={ //.length for N of arguments
  sin:Math.sin,rand:Math.random,
  abs:Math.abs,floor:Math.floor
},chooseRand=a=>a[Math.floor(Math.random()*a.length)];
/*
Examples of Reverse Polish Notation (RPN):
  t 220 * 1 % → (t*220)%1
  rand 1 t 1 % - 5 ** * → rand()*((1-(t%1))**5)
  .5 1 10 t * 2 % - abs - → .5-abs(1-((10*t)%2))
*/
function genExpr(){
  const ops=Object.keys(oper),
        unaries=Object.keys(fun).filter(k=>fun[k].length===1),
        terminals=["t",...Object.keys(fun).filter(k=>fun[k].length===0)];
  let stack=[],esd=0,maxLen=10; //eval stack depth
  while(esd<1||Math.random()>.2||stack.length<3){
    let options=["terminal"];
    if(esd>=1)options.push("unary");
    if(esd>=2)options.push("binary");
    const choice=chooseRand(options);
    switch(choice){
      case"terminal":
        if(Math.random()<.2){
          stack.push(chooseRand(terminals));
        }else{
          stack.push(floor(random()*21)*.5-5);
        }
        esd++;
      break;
      case"unary":
        stack.push(chooseRand(unaries));
      break;
      case"binary":
        stack.push(chooseRand(ops));
        esd--;
      break;
    }
    if(stack.length>=maxLen)break;
  }
  //ensure minimum size and balance
  while(esd!==1||stack.length<3){
    if(esd<1){
      stack.push(chooseRand([.5,1])); //push dummy operand
      esd++;
    }else if(esd>1){
      stack.push("+"); //try to reduce stack
      esd--;
    }else{break;}
  }
  return stack;
}
function evalExpr(stack){
  return function(tVal){
    let s=[];
    for(let token of stack){
      try{
        if(typeof token==="number"){
          s.push(token);
        }else if(token==="t"){
          s.push(tVal);
        }else if(oper.hasOwnProperty(token)){
          if(s.length<2)return 0; //not enough
          const b=s.pop(),a=s.pop(),
                result=oper[token](a,b);
          s.push(isFinite(result)?result:0);
        }else if(fun.hasOwnProperty(token)){
          if(fun[token].length===0){s.push(fun[token]());continue;} //e.g. rand
          if(!s.length)return 0; //no args to push
          const result=fun[token](s.pop()); //TODO: multiple args
          s.push(isFinite(result)?result:0);
        }else{return 0;}
      }catch(e){
        console.error("Eval error:",e,"at",token);
        return 0;
      }
    }
    const result=s.pop()||0;
    return isNaN(result)||!isFinite(result)?0:result;
  };
}
/*let timer={
  t:0,l:.1
},expr=genExpr(),eFun=evalExpr(expr);
return(t,SR)=>{
  timer.t+=1/SR;
  if(timer.t>=timer.l){
    timer.t%=timer.l;
    expr=genExpr();eFun=evalExpr(expr);
  }
  return eFun(t);
};*/
let split=str=>str.split(" ").map(e=>{let n=parseFloat(e);return isNaN(n)?e:n;}),
    eFun=[
  evalExpr(split(".5 1 440 t * 2 % - abs -")),
  evalExpr(split("rand .5 - 1 t 1 % - 5 ** *")),
  ...(new Array(5).fill(0).map(()=>evalExpr(genExpr())))
];
return t=>{
  return eFun[floor(t/2)%eFun.length](t);
};

Interesting to think about in theory, but boring when it's put into practice.

(Edited 7 minutes later.)

·Phoneposter6 months ago, 1 day later, 8 months after the original post[T] [B] #674,468

Slow-as-shit spectral composition (needs further optimizations)…

·Phoneposter6 months ago, 1 day later, 8 months after the original post[T] [B] #674,486

I'm fucking bored…



Make bytebeat URL link TinyChan-safe:
url.replace(/https:\/\/([^#]+)(#.+)/,(m,p1,p2)=>p1+encodeURIComponent(p2));

(Edited 1 minute later.)

·Phoneposter6 months ago, 29 minutes later, 8 months after the original post[T] [B] #674,487

Here's an older funcbeat (28.03.25)…

·Phoneposter5 months ago, 1 week later, 9 months after the original post[T] [B] #674,760

I don't fucking know what I'm doing.

·Phoneposter2 months ago, 3 months later, 1 year after the original post[T] [B] #676,264

Audio processing example in JavaScript (how to use without Dollchan):
var{sin,cos,floor,random,PI}=Math,TAU=Math.PI*2,mod=(n,m)=>(n%m+m)%m,
    audioCtx=new(AudioContext||webkitAudioContext)(),
    bufferSize=1024,sampleRate=audioCtx.sampleRate,globalTime=0,δt=1/sampleRate,
    scriptNode,isPlaying=false;
function DSP(t){
    //return sin(TAU*220*t);
    return[sin(TAU*440*t)*sin(t),sin(TAU*440*t)*cos(t)];
}
function toggleAudio(){
    if(!scriptNode){
        scriptNode=audioCtx.createScriptProcessor(bufferSize,0,2); //AudioWorklet is recommended today but requires external files.
        scriptNode.onaudioprocess=e=>{
            let outBuffer=e.outputBuffer,signal,
                outL=outBuffer.getChannelData(0),
                outR=outBuffer.getChannelData(1);
            for(let i=0;i<bufferSize;i++){
                signal=DSP(globalTime);
                if(typeof signal==="number"){
                    outL[i]=outR[i]=signal;
                }else{
                    outL[i]=signal[0];outR[i]=signal[1];
                }
                globalTime+=δt;
            }
        };
        scriptNode.connect(audioCtx.destination);
        audioCtx.resume();isPlaying=true;
    }else if(isPlaying){
        scriptNode.disconnect(audioCtx.destination);isPlaying=false;
    }else{
        scriptNode.connect(audioCtx.destination);audioCtx.resume();isPlaying=true;
    }
}
toggleAudio(); //Play, but call again to pause.
:

You are required to fill in a captcha for your first 5 posts. Sorry, but this is required to stop people from posting while drunk. Please be responsible and don't drink and post!
If you receive this often, consider not clearing your cookies.



Please familiarise yourself with the rules and markup syntax before posting.