Signals and sound

Post Reply
Henko
Posts: 822
Joined: Tue Apr 09, 2013 12:23 pm
My devices: iPhone,iPad
Windows
Location: Groningen, Netherlands
Flag: Netherlands

Signals and sound

Post by Henko »

I'm still playing around with this topic and the tools lib is growing.
Here are some extensions to create a signal stream with sine, sawtooth, triangular, and block signals, together with a viewer and a FFT transform of such a stream.
I'll add options to export such constructed stream into a .WAV file and (of course) play it then using the MUSIC statement in SB.
After that i might add an ADSR mechanism.

Code: Select all

graphics ! graphics clear .8,.8,.8
N=4096 ! pi=4*atan(1)
dim v(N+1),x(N+1),reX(N/2+1),imX(N/2+1)

sine_signal (N,v,2.5,500,0.0,0,"0")
saw_signal  (N,v,2.5,327,0.3,0,"+")
tri_signal  (N,v,2.5,681,0.4,0,"+")
block_signal(N,v,2.5,235,0.7,0,"+")

ytop=50 ! ns=N ! c=N/2! io=0 ! first_time=1 ! sc=0
for i=0 to N ! x(i)= v(i) ! next i
r_fft(N,x,reX,imX) ! graph_magn(N/2,reX,imX,440)  
update: 
  tt$="combined signal  (touch graph for toggling the control bar)"
  sc=graph(tt$,ns,io,v,ytop,sc)
  so=720*io/N ! sw=720*ns/N
  if not first_time then sbar(24,356,720,so,sw)
control: slowdown
  get touch 0 as xt,yt
  if xt<>-1 then ! cntrl(124,278) ! sbar(24,356,720,so,sw) ! end if
  if b_p("+") then
    ns=max(6,ns/2) ! io+=ns/2
    goto update
    end if
  if b_p("-") then
    ns=min(N,2*ns) ! io=min(N,max(0,io-ns/4)+ns)-ns
    goto update
    end if
  if b_p("<<") then ! io=0 ! goto update ! end if
  if b_p(">>") then ! io=N-ns ! goto update ! end if
  if b_p("<") then ! io=max(0,io-ns) ! goto update ! end if
  if b_p(">") then ! io=min(N-ns,io+ns) ! goto update ! end if
  if b_p("x") then ! page "buttons" hide ! page "bar" hide ! end if
  if not first_time then control
  first_time=0
  goto control
exit: stop
end

{signal_util}
' def sine_signal(N,v(),ampl,freq,shift,vdc,mode$)
' def saw_signal(N,v(),ampl,freq,shift,vdc,mode$)
' def tri_signal(N,v(),ampl,freq,shift,vdc,mode$)
' def block_signal(N,v(),ampl,freq,shift,vdc,mode$)
' def noise(N,v(),ampl,distr$,mode$)
' def hex2dec(h$)
' def dec2hex$(num,npos)
' def h2d(h$)
' def d2h$(num)
' def play_music(mus$)
' def wav(m$)
' def wav_info(m$,xs,ys,ww,hh,R,G,B,alpha)
' def r_fft(N,x(),reX(),imX())
' def c_fft(M,x())
' def graph(txt$,N,v(),ytop,sc)
' def graph_magn(M,rx(),ix(),ytop)
' def sigstat(N,v(),st())
' def box(ytop)
' def box2(ytop)
' def cntrl(xs,ys)
' def sbar(xs,ys,ww,ss,sw)
' def b_p(b$)
And here the actual library.

Code: Select all

' sine signal generator
' N = # of samples
' v() contains (cumulative) generated signal
' ampl = amplitude
' freq = frequency (# of complete cycles)
' shift = phase shift in radians
' vdc = base value for signal
' mode = "+" (add to v()), "0" (init v()), or "-" (subtract from (v))
'
def sine_signal(N,v(),ampl,freq,shift,vdc,mode$)
if mode$<>"+" and mode$<>"0" and mode$<>"-" then return 0
dt=2*freq*.pi/N
for i=0 to N-1
  sig=vdc+ampl*sin(i*dt+shift)
  if mode$="0" then ! v(i)=sig
    else ! if mode$="+" then v(i)+=sig else v(i)-=sig
    end if
  next i
end def

' sawtooth signal generator
' shift = part of one cycle ( 0 <= shift <= 1 )
' other parameters: see under sine_signal
'
def saw_signal(N,v(),ampl,freq,shift,vdc,mode$)
if mode$<>"+" and mode$<>"0" and mode$<>"-" then return 0
dy=2*ampl*freq/N ! sig=2*ampl*shift ! if sig>=ampl then sig-=2*ampl
for i=1 to N-1
  if mode$="0" then ! v(i)=sig+vdc
    else ! if mode$="+" then v(i)+=sig+vdc else v(i)-=sig+vdc
    end if
  sig+=dy ! if sig>=ampl then sig=-ampl
  next i
end def

' triangular signal generator
' shift = part of one cycle ( 0 <= shift <= 1 )
' other parameters: see under sine_signal
'
def tri_signal(N,v(),ampl,freq,shift,vdc,mode$)
if mode$<>"+" and mode$<>"0" and mode$<>"-" then return 0
dy=4*ampl*freq/N
if shift>.25 and shift<.75 then
  fac=-1 ! sig=ampl*(2-4*shift)
  else
  fac=1 ! sig=4*ampl*shift ! if sig>ampl then sig-=4*ampl
  end if
for i=1 to N-1
  if mode$="0" then ! v(i)=sig+vdc
    else ! if mode$="+" then v(i)+=sig+vdc else v(i)-=sig+vdc
    end if
  sig+=fac*dy
  if sig>=ampl then ! sig=ampl ! fac=-1 ! end if
  if sig<=-ampl then ! sig=-ampl ! fac=1 ! end if
  next i
end def

def block_signal(N,v(),ampl,freq,shift,vdc,mode$)
if mode$<>"+" and mode$<>"0" and mode$<>"-" then return 0
m_cnt=N/freq/2 ! cnt=2*shift*m_cnt ! if cnt>=m_cnt then cnt-=m_cnt
if shift<=.5 then sig=ampl else sig=-ampl
for i=1 to N-1
  if mode$="0" then ! v(i)=sig+vdc
    else ! if mode$="+" then v(i)+=sig+vdc else v(i)-=sig+vdc
    end if
  cnt+=1 ! if cnt>=m_cnt then ! sig=-sig! cnt-=m_cnt ! end if
  next i
end def

' random noise signal generator
' distr$ = Gaussian ("n") or uniform ("u")
' mode$ = add, init, or subtract ("+", "0", or "-")
'
def noise(N,v(),ampl,distr$,mode$)
randomize
if distr$<>"n" and distr$<>"u" then return
if mode$<>"+" and mode$<>"0" and mode$<>"-" then return
for i=0 to N-1
  if distr$="u" then ! p=rnd(1)
    else ! p=0 ! for j=1 to 12 ! p+=rnd(1) ! next j ! p/=12
    end if
  p=2*ampl*p-ampl
  if mode$="0" then ! v(i)=p
    else ! if mode$="+" then v(i)+=p else v(i)-=p
    end if
  next i
end def

def hex2dec(h$)
ls=len(h$) ! dec=0
for i=0 to ls/2-1 ! dec+=h2d(mid$(h$,2*i,2))*256^i ! next i
return dec
end def

def dec2hex$(num,npos)
h$=""
do ! h$ &= d2h$(num%256) ! num=floor(num/256) ! until num<256
h$ &= d2h$(num%256)
 while len(h$)<2*npos ! h$ &= "00" ! end while
return h$
end def

def h2d(h$)
a=asc(left$(h$,1)) !  if a<65 then a-=48 else a-=55
b=asc(right$(h$,1)) ! if b<65 then b-=48 else b-=55
return 16*a+b
end def

def d2h$(num)
a=floor(num/16) ! b=num-16*a
if a<10 then a$=str$(a) else a$=chr$(a+55)
if b<10 then b$=str$(b) else b$=chr$(b+55)
return a$&b$
end def

def play_music(mus$)
music m$ load mus$
ml=music_length(m$)
music m$ play
pause ml
end def

def wav(m$)
dim t(4)
file m$ readdim t,n,4 ! f_riff$=""
for i=0 to 3 ! f_riff$ &= chr$(t(i)) ! next i
file m$ readdim t,n,4 ! f_size=0
for i=0 to 3 ! f_size += t(i)*256^i ! next i
file m$ readdim t,n,4 ! f_wave$=""
for i=0 to 3 ! f_wave$ &= chr$(t(i)) ! next i
file m$ readdim t,n,4 ! f_fmt$=""
for i=0 to 3 ! f_fmt$ &= chr$(t(i)) ! next i
file m$ readdim t,n,4 ! fmt_size=0
for i=0 to 3 ! fmt_size += t(i)*256^i ! next i
file m$ readdim t,n,2 ! audio_fmt=t(0)+t(1)*256
file m$ readdim t,n,2 ! num_channels=t(0)+t(1)*256
file m$ readdim t,n,4 ! sample_rate=0
for i=0 to 3 ! sample_rate += t(i)*256^i ! next i
file m$ readdim t,n,4 ! byte_rate=0
for i=0 to 3 ! byte_rate += t(i)*256^i ! next i
file m$ readdim t,n,2 ! block_align=t(0)+t(1)*256
file m$ readdim t,n,2 ! bits_per_sample=t(0)+t(1)*256
file m$ readdim t,n,4 ! f_data$=""
for i=0 to 3 ! f_data$ &= chr$(t(i)) ! next i
file m$ readdim t,n,4 ! data_size=0
for i=0 to 3 ! data_size += t(i)*256^i ! next i
start_data_adress=44
end def

def wav_info(m$,xs,ys,ww,hh,R,G,B,alpha)
dim w$(14)
wav(m$)
name$="wavinfo"
page name$ set 
page name$ frame xs,ys,ww,hh
page name$ color R,G,B,alpha
button "w_close" title "❎" at ww-30,5 size 22,22
set buttons custom ! draw color 0,0,0
button "bottom" title "" at -6,hh-3 size ww+12,3
button "left" title "" at 0,-6 size 3,hh+12
button "right" title "" at ww-3,-6 size 3,hh+12
button "upper1" title "" at -6,0 size ww+12,3
button "upper2" title "" at -6,30 size ww+12,3
field "tt" text "info: " & m$ at 20,7 size ww-60,20 RO
field "tt" back color R,G,B ! field "tt" font color 0,0,1
field "tt" back alpha alpha
w$(1)="File descriptor     : "      & wav.f_riff$
w$(2)="File size                : " & wav.f_size
w$(3)="Format type         : "      & wav.f_wave$
w$(4)="Fmt ident.             : "   & wav.f_fmt$
w$(5)="Fmt chunk size    : "        & wav.fmt_size
w$(6)="Audio format        : "      & wav.audio_fmt
w$(7)="# of channels       : "      & wav.num_channels
w$(8)="Sample rate          : "     & wav.sample_rate
w$(9)="Byte rate               : "  & wav.byte_rate
w$(10)="Block align            : "  & wav.block_align
w$(11)="Bits per sample    : "      & wav.bits_per_sample
w$(12)="Data block ident   : "      & wav.f_data$
w$(13)="Data block size     : "     & wav.data_size
for i=1 to 13
  fd$="w"&i
  field fd$ text w$(i) at 10,15+25*i size ww-20,25 RO
  field fd$ back color R,G,B ! field fd$ font color 0,0,0
  field fd$ back alpha alpha
  next i
wait: while not button_pressed("w_close") ! goto wait ! end while
page name$ hide
end def

' real FFT (using complex FFT)
' N = # samples in input x()
' x() input samples (N samples)
' reX() output: N/2+1 amplitudes of cosine components
' imX() output: N/2+1 amplitudes of sine components
'
def r_fft(N,x(),reX(),imX())
c_fft(log2(N),x) ! n2=N/2
for i=0 to n2 ! reX(i)=real(x(i))/n2 ! imX(i)=-imag(x(i))/n2 ! next i
reX(0)/=2 ! imX(M)/=2
end def

' complex FFT (converted Fortran function)
' M = power of 2 -> N=2^M
' x() is array of type complex
' internally, the function works with option base 1
' input AND results via complex array x()
'
def c_fft(M,x())
pi=4*atan(1) ! N=2^M ! ob=option_base() ! option base 1
for k=1 to M
  ke=2^(M-k+1) ! ke2=ke/2 ! u=1+0i ! angl=pi/ke2
  s=cos(angl)-sin(angl)*1i
  for j=1 to ke2
    for i=j to N step ke
      ip=i+ke2 ! t=x(i)+x(ip)
      x(ip)=u*(x(i)-x(ip)) ! x(i)=t
      next i
    u*=s
    next j
  next k
nd2=N/2 ! nm1=N-1 ! j=1
for i=1 to nm1
  if i<j then ! t=x(j) ! x(j)=x(i) ! x(i)=t ! end if
  k=nd2
  while k<j ! j-=k ! k/=2 ! end while
  j+=k
  next i
option base ob
end def

' graphs a series of N samples in v(), starting with samplenumber io
' ytop = top of graph-box
' sc = scale, if scale=0, maximum scale will be calculated
' function returns the scale used.
'
def graph(txt$,N,io,v(),ytop,sc)
dim stat(4)
sigstat(N,v,stat) ! maxi=stat(1) ! mini=stat(0)
box(ytop) ! yc=150+ytop ! draw size 1 ! draw color 1,0,0
if sc=0 then
  if maxi>0 then sc1=150/maxi else sc1=9999
  if mini<0 then sc2=-150/mini else sc2=9999
  sc=min(sc1,sc2)
  end if
dx=720/(N-1)
for i=0 to N-1
  if i=0 then
    draw to 24,yc-sc*v(io+i)
    else
    draw line to 24+i*dx,yc-sc*v(io+i)
    end if
  next i
draw font size 12 ! draw color 0,0,1
draw text txt$ at 24,ytop-16
draw font size 20
return sc
end def

' graphs the transform result in polar form
' M = # of cosine components in rx() and sine components in ix()
' output shows magnitudes in the frequency domain
' ytop = top of graph-box
'
def graph_magn(M,rx(),ix(),ytop)
dim mag(M+1),stat(4)
pi=4*atan(1) ! eps=0.00001
for i=0 to M
  re=rx(i) ! im=ix(i)
  mag(i)=sqrt(re*re+im*im)
  next i
sigstat(M,mag,stat) ! sc1=200/stat(1)
box2(ytop) ! ybot=200+ytop
draw size 1 ! draw color 1,0,0 ! fill color .8,0,0
dx=360/(M-1) ! x1=16
for i=0 to M-1
  if mag(i) then draw line x1+i*dx,ybot to x1+i*dx,ybot-sc1*(mag(i))
  next i
draw font size 12 ! draw color 0,0,1 ! draw alpha 1
draw text "FFT frequency domain, magnitudes" at x1,ytop-15
draw font size 20 ! draw color 0,0,0
end def

' base statistics of signal
' returns st(4), resp. minimum, maximun, mean, standard deviation
'
def sigstat(N,v(),st())
mini=999999 ! maxi=-999999 ! mean=0 ! st_dev=0
for i=0 to N-1
  num=v(i) ! mini=min(mini,num) ! maxi=max(maxi,num) ! mean+=num
  next i ! mean/=N
for i=0 to N-1 ! variance+=(v(i)-mean)^2 ! next i
st(0)=mini ! st(1)=maxi ! st(2)=mean ! st(3)=sqrt(variance/(N-1))
return
end def

def box(ytop)
refresh off
get screen size sw,sh ! xc=384 ! yc=150+ytop ! ybot=150+yc
draw size 3 ! draw color 0,0,0 ! fill color .8,.8,.8
fill rect 24,ytop to 744,ybot
draw rect 24,ytop to 744,ybot
draw size 1 ! draw alpha .3
for x=24 to 744 step 20 ! draw line x,ytop to x,ybot ! next x
for y=ytop to ybot step 15 ! draw line 24,y to 744,y ! next y
draw size 2 ! draw color 0,0,1 ! draw alpha 1
draw line 24,yc to 744,yc
refresh on
end def

def box2(ytop)
refresh off
x1=16 ! ybot=ytop+200
draw size 3 ! draw color 0,0,0
draw rect x1,ytop to x1+360,ybot
draw size 1 ! draw alpha .3
for x=x1 to x1+360 step 18
  draw line x,ytop to x,ybot
  next x
for y=ytop to ybot step 20
  draw line x1,y to x1+360,y
  next y
refresh on
end def

def cntrl(xs,ys)
name$="buttons" ! ww=520 ! hh=70
page name$ set 
page name$ frame xs,ys,ww,hh
set buttons custom ! draw color 0,0,0
button "bottom" title "" at -6,hh-3 size ww+12,3
button "left" title "" at 0,-6 size 3,hh+12
button "right" title "" at ww-3,-6 size 3,hh+12
button "upper1" title "" at -6,0 size ww+12,3
page name$ color .8,.8,0,1
set buttons font size 50
button "1" text "" at xs,ys size ww,hh
button "+" text "➕" at 10,10 size 50,50
button "-" text "➖" at 80,10 size 50,50
button "<<" text "⏮" at 160,10 size 50,50
button "<" text "◀️" at 230,10 size 50,50
button ">" text "▶️" at 300,10 size 50,50
button ">>" text "⏭" at 370,10 size 50,50
button "x" title "❎" at 460,10 size 50,50
page name$ show
end def

def sbar(xs,ys,ww,ss,sw)
name$="bar" ! hh=16
page name$ set 
page name$ frame xs,ys,ww,hh
set buttons custom ! draw color 0,0,0
button "bar" text "" at 0,0 size ww,hh
fill color 0,1,0
button "sel" text "" at ss,1 size sw,hh-2
page name$ show
fill color .8,.8,.8
end def

def b_p(b$) = button_pressed(b$)
IMG_1313.PNG
IMG_1313.PNG (511.18 KiB) Viewed 2394 times

Henko
Posts: 822
Joined: Tue Apr 09, 2013 12:23 pm
My devices: iPhone,iPad
Windows
Location: Groningen, Netherlands
Flag: Netherlands

Re: Signals and sound

Post by Henko »

And here the .WAV header inspector.

Code: Select all

graphics ! graphics clear .8,.8,.8
f$="sound.wav"
wav_info(f$,200,20,240,370,.7,.7,.7,.3)
end

{signal_util}
' def sine_signal(N,v(),ampl,freq,shift,vdc,mode$)
' def saw_signal(N,v(),ampl,freq,shift,vdc,mode$)
' def tri_signal(N,v(),ampl,freq,shift,vdc,mode$)
' def block_signal(N,v(),ampl,freq,shift,vdc,mode$)
' def noise(N,v(),ampl,distr$,mode$)
' def hex2dec(h$)
' def dec2hex$(num,npos)
' def h2d(h$)
' def d2h$(num)
' def play_music(mus$)
' def wav(m$)
' def wav_info(m$,xs,ys,ww,hh,R,G,B,alpha)
' def r_fft(N,x(),reX(),imX())
' def c_fft(M,x())
' def graph(txt$,N,v(),ytop,sc)
' def graph_magn(M,rx(),ix(),ytop)
' def sigstat(N,v(),st())
' def box(ytop)
' def box2(ytop)
' def cntrl(xs,ys)
' def sbar(xs,ys,ww,ss,sw)
' def b_p(b$)
IMG_1314.PNG
IMG_1314.PNG (340.29 KiB) Viewed 2392 times

Operator
Posts: 138
Joined: Mon May 06, 2013 5:52 am

Re: Signals and sound

Post by Operator »

:shock: WOW :shock:
Can't await the export function -> .wav file
Good to see you are back again :D
And let us hope together the sB mic input comes soon

User avatar
rbytes
Posts: 1338
Joined: Sun May 31, 2015 12:11 am
My devices: iPhone 11 Pro Max
iPad Pro 11
MacBook
Dell Inspiron laptop
CHUWI Plus 10 convertible Windows/Android tablet
Location: Calgary, Canada
Flag: Canada
Contact:

Re: Signals and sound

Post by rbytes »

Yes, welcome back, Henko. I hope your wrists are rested!

You set the bar very high on how to take a basic idea and develop it. Your wave library is impressive!
The only thing that gets me down is gravity...

Post Reply