Saving a wave pattern to a .WAV file

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

Re: Saving a wave pattern to a .WAV file

Post by Henko »

Yes, we are still in the "research" phase.
Here follows a first try for defining and playing chords.
Would you try out different chords in the "Definition of a chord" section, and hear if correct chords are produced by the speaker?

Also note, that i have packed several functions in a new "wav_util" library. The code follows the testprogram for chords.

Code: Select all

pi=4*atan(1)

' INPUT DATA
S=4000   ' sample rate for .WAV file
T=2      ' duration of tone in seconds
F=440    ' frequency of a tone

' CALCULATED DATA
N=T*S    ' # of bytes for one tone in a chord
C=T*F    ' # of cycles for function sine_wave()
N3=3*N   ' # of bytes for a chord of three tones

dim tone1(N),tone2(N),tone3(N),sig(N3)

' DEFINITION OF A CHORD
sine_signal(N,tone1,100,T*hertz("C5"),0,0,"0")
sine_signal(N,tone2,100,T*hertz("e"),0,0,"0")
sine_signal(N,tone3,100,T*hertz("a"),0,0,"0")

for i=0 to N-1
  sig(3*i)=tone1(i) ! sig(3*i+1)=tone2(i) ! sig(3*i+2)=tone3(i)
  next i
f$="test.wav"
wav_file_chord(N3,sig,3,S,8,f$)
play_music(f$)
end

' function to encapsulate a series of samples in a .WAV file.
' the sample values are normalized to integers in the 0-255 range.
' n_bytes = the number of sample values in the sample array s().
' n_channels = the number of (parallel) channels
' s_rate = the sample rate (8000, 11025, 44100, etc)
' bps = bits per sample
' file$ = the .WAV file to wich the data are to be saved.
'
def wav_file_chord(n_bytes,s(),n_channels,s_rate,bps,file$)
if file_exists(file$) then file file$ delete
ob=option_base() ! option base 0
dim snd(n_bytes),out(44),x(4)
bmax=-1000000 ! bmin=1000000 
for i=0 to n_bytes-1 
  bmax=max(bmax,s(i)) ! bmin=min(bmin,s(i))
  next i
fac=255/(bmax-bmin) ! bps=8  ' as long as normalization to 255
for i=0 to n_bytes-1
  snd(i)=s(i)-bmin ! snd(i)=floor(fac*snd(i))
  next i
for i=0 to 43 ! read out(i) ! next i
b_rate=n_channels*bps/8*s_rate
num2hex4(n_bytes+36,x)     ' file size
for i=0 to 3 ! out(i+4)=x(i) ! next i
num2hex4(n_channels,x)  ' number of channels
for i=0 to 1 ! out(i+22)=x(i) ! next i
num2hex4(s_rate,x)      ' sample rate
for i=0 to 3 ! out(i+24)=x(i) ! next i
num2hex4(b_rate,x)      ' byte rate
for i=0 to 3 ! out(i+28)=x(i) ! next i
num2hex4(bps,x)         ' bits per sample
for i=0 to 1 ! out(i+34)=x(i) ! next i
num2hex4(n_bytes,x)  ' chunk size
for i=0 to 3 ! out(i+40)=x(i) ! next i
file file$ writedim out,44,0
file file$ writedim snd,n_bytes,0
file file$ setpos 0
data 82,73,70,70,0,0,0,0,87,65,86
data 69,102,109,116,32,16,0,0,0,1,0
data 1,0,0,0,0,0,0,0,0,0,3
data 0,8,0,100,97,116,97,0,0,0,0
option base ob
end def

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

{signal_util}    '  code to be found in a post dd dec.4,2016
' 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$)

{wav_util}    ' .WAV related functions
' def wav(m$)
' def wav_info(m$,xs,ys,ww,hh,R,G,B,alpha)
' def wav_file(n_bytes,s(),n_channels,s_rate,bps,file$)
' def wav_add_stream(file$,ns,str(),max_value)
' def hertz(t$)
' def num2hex4(x,a())
HEREAFTER THE NEW WAV LIBRARY

Code: Select all

' def wav(m$)
' def wav_info(m$,xs,ys,ww,hh,R,G,B,alpha)
' def wav_file(n_bytes,s(),n_channels,s_rate,bps,file$)
' def wav_add_stream(file$,ns,str(),max_value)
' def hertz(t$)
' def num2hex4(x,a())

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
if fmt_size>16 then file m$ readdim t,n,fmt_size-16
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 24,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

' function to encapsulate a series of samples in a .WAV file.
' the sample values are normalized to integers in the 0-255 range.
' n_bytes = the number of sample values in the sample array s().
' n_channels = the number of (parallel) channels
' s_rate = the sample rate (8000, 11025, 44100, etc)
' bps = bits per sample
' file$ = the .WAV file to wich the data are to be saved.
'
def wav_file(n_bytes,s(),n_channels,s_rate,bps,file$)
if file_exists(file$) then file file$ delete
ob=option_base() ! option base 0
dim snd(n_bytes),out(44),x(4)
bmax=-1000000 ! bmin=1000000 
for i=0 to n_bytes-1 
  bmax=max(bmax,s(i)) ! bmin=min(bmin,s(i))
  next i
fac=255/(bmax-bmin) ! bps=8  ' as long as normalization to 255
for i=0 to n_bytes-1
  snd(i)=s(i)-bmin ! snd(i)=floor(fac*snd(i))
  next i
for i=0 to 43 ! read out(i) ! next i
b_rate=n_channels*bps/8*s_rate
num2hex4(n_bytes+36,x)     ' file size
for i=0 to 3 ! out(i+4)=x(i) ! next i
num2hex4(n_channels,x)  ' number of channels
for i=0 to 1 ! out(i+22)=x(i) ! next i
num2hex4(s_rate,x)      ' sample rate
for i=0 to 3 ! out(i+24)=x(i) ! next i
num2hex4(b_rate,x)      ' byte rate
for i=0 to 3 ! out(i+28)=x(i) ! next i
num2hex4(bps,x)         ' bits per sample
for i=0 to 1 ! out(i+34)=x(i) ! next i
num2hex4(n_bytes,x)  ' chunk size
for i=0 to 3 ! out(i+40)=x(i) ! next i
file file$ writedim out,44,0
file file$ writedim snd,n_bytes,0
file file$ setpos 0
data 82,73,70,70,0,0,0,0,87,65,86
data 69,102,109,116,32,16,0,0,0,1,0
data 1,0,0,0,0,0,0,0,0,0,1
data 0,8,0,100,97,116,97,0,0,0,0
option base ob
end def

' add a byte stream (samples) to an existing .wav file
' the stream is appended to the last existing subchunk in the file
' hence the stream will be interpreted accordingly
' the data are normalized between 0 and max_value
'
def wav_add_stream(file$,ns,str(),max_value)
ob=option_base() ! option base 0
dim x(4)
if odd(ns) or not file_exists(file$) then return 0
bmax=-1000000 ! bmin=1000000 ! max_value=min(255,max_value)
for i=0 to ns-1 
  bmax=max(bmax,str(i)) ! bmin=min(bmin,str(i))
  next i
fac=max_value/(bmax-bmin)
for i=0 to ns-1
  str(i)-=bmin ! str(i)=floor(fac*str(i))
  next i
wav(file$)
fs=wav.f_size+ns ! ds=wav.data_size+ns
file file$ setpos 4
num2hex4(fs,x) ! file file$ writedim x,4,0
file file$ setpos 40
num2hex4(ds,x) ! file file$ writedim x,4,0
file file$ setpos wav.f_size+8
file file$ writedim str,ns,0
option base ob
file file$ setpos 0
return 1
end def

' function that returns the frequency for a given note
' the frequency is given with one decimal precision
' input format as defined by Rbytes in his post dd. jan. 4, 2018
' uppercase and lowercase note digits are accepted
' octave number range is from 0 through 9
' if an octave number is present, a global octave number will be set
' if an octave number is missing, the global value will be taken
' # or $, when present, must be the second token in the input
' in case of an error in the input, the value 0 is returned
'
def hertz(t$)
ob=option_base() ! option base 0
tl=len(t$) ! if tl=0 or tl>3 then return 0
c$=capstr$(left$(t$,1)) ! if c$<"A" or c$>"G" then return 0
rnr=asc(c$)-67 ! if rnr<0 then rnr+=7
rnr*=2 ! if rnr>5 then rnr-=1
if tl=1 then label2
m$=mid$(t$,1,1)
if not (m$="#" or m$="$") then
  octnum=val(m$) ! goto label1
  end if
if m$="#" then rnr+=1 else rnr-=1
if tl=3 then octnum=val(right$(t$,1)) else goto label2
label1: if octnum<0 or octnum>9 then return 0
.octave=octnum
label2: octnum=.octave
option base ob
return int(275*2^(octnum+(rnr-9)/12))/10
end def

def num2hex4(x,a())
a(0)=0 ! a(1)=0 ! a(2)=0 ! a(3)=0
f1=256 ! f2=f1*f1 ! f3=f1*f2
a(3)=floor(x/f3)
if a(3) then x-=a(3)*f3
a(2)=floor(x/f2)
if a(2) then x-=a(2)*f2
a(1)=floor(x/f1)
if a(1) then x-=a(1)*f1
a(0)=x
return
end def

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: Saving a wave pattern to a .WAV file

Post by rbytes »

I copied both files and tried to run the top one.
I got this error in wav_util
Recopied them a second time, with same result.

E9A33068-5108-4B33-9E38-D71217912951.png
E9A33068-5108-4B33-9E38-D71217912951.png (301.01 KiB) Viewed 3945 times

I took a quick look but didn't find the problem in the code.
The only thing that gets me down is gravity...

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

Re: Saving a wave pattern to a .WAV file

Post by Henko »

Oh, i tranferred one or two functions from the "signal_util" to the new "wav.util", so your old signal lib still contains those functions.

Hereafter the new version of "signal.lib"

Code: Select all

' signal_util, version january 10, 2018
'
' 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 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$)

' 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

' 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 ! draw line xc,ytop to xc,ybot
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 ! sw=max(sw,8)
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$)

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

Re: Saving a wave pattern to a .WAV file

Post by Henko »

As far as i can judge, there's no more than a double tone. With a headphone, the 1st channel is heard left, the 2nd is heard at the right side, the 3rd channel is not heard at all. The documentation indicates that more than 2 channels can be defined. Perhaps it's the "music file$ play" command that processes only two channels (interpreting the stream as "stereo").

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: Saving a wave pattern to a .WAV file

Post by rbytes »

Yes, I just tried this and you are right. Only two tones, full left and full right. It is a nice bonus to be able to access stereo, though, and we should decide on a way to indicate that in the note data. Hope you can find a way to produce chords. 3 note chords are good. A lot of music uses 4-note chords (eg. chorales), so that would be good to have.
The only thing that gets me down is gravity...

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

Re: Saving a wave pattern to a .WAV file

Post by Henko »

Solved! It is surprisingly simple. The chord can be prefab'd entirely before it is fed to a .wav file as a mono stream, see the example hereafter.
Together with the stereo feature, it must be possible to play a chord with the left channel, and a melody tone over the right channel (or vice versa). With the present knowledge they will have the same duration.

I made a small adjustment in the "signal_util" library. It is placed after the test program.

Code: Select all

pi=4*atan(1)

' INPUT DATA
S=4000   ' sample rate for .WAV file
T=2      ' duration of chord
F=440    ' frequency of tone (taken from "hertz()" function)

' CALCULATED DATA
N=T*S    ' number of samples in data chunk
C=T*F    ' # of cycles for function sine_wave()

dim sig(N)

f$="test.wav"
sine_signal(N,sig,100,T*hertz("C5"),0,0,"0")
sine_signal(N,sig,100,T*hertz("e"),0,0,"+")
sine_signal(N,sig,100,T*hertz("g"),0,0,"+")
wav_file(N,sig,1,S,8,f$)
play_music(f$)

end

{signal_util}   ' version january 10, 2018
' 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$)

{wav_util}    ' version january 10, 2018
' def wav(m$)
' def wav_info(m$,xs,ys,ww,hh,R,G,B,alpha)
' def wav_file(n_bytes,s(),n_channels,s_rate,bps,file$)
' def wav_add_stream(file$,ns,str(),max_value)
' def wav_file_stereo(n_bytes,s(),s_rate,bps,file$)
' def hertz(t$)
' def num2hex4(x,a())
HERE THE "SIGNAL_UTIL" library

Code: Select all

' signal_util, version january 10, 2018
'
' 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 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$)

' 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

' 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 ! draw line xc,ytop to xc,ybot
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 ! sw=max(sw,8)
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$)

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: Saving a wave pattern to a .WAV file

Post by rbytes »

Much fun! I had no problem creating chords with up to seven notes. Here is a big band chord called a major ninth, containing 5 notes.

Code: Select all

pi=4*atan(1)

' INPUT DATA
S=4000   ' sample rate for .WAV file
T=2      ' duration of chord
F=440    ' frequency of tone (taken from "hertz()" function)

' CALCULATED DATA
N=T*S    ' number of samples in data chunk
C=T*F    ' # of cycles for function sine_wave()

dim sig(N)

f$="test.wav"
sine_signal(N,sig,100,T*hertz("C5"),0,0,"0")
sine_signal(N,sig,100,T*hertz("e"),0,0,"+")
sine_signal(N,sig,100,T*hertz("g"),0,0,"+")
sine_signal(N,sig,100,T*hertz("b"),0,0,"+")
sine_signal(N,sig,100,T*hertz("d6"),0,0,"+")
wav_file(N,sig,1,S,8,f$)
play_music(f$)

end

{signal_util}   ' version january 10, 2018
' 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$)

{wav_util}    ' version january 10, 2018
' def wav(m$)
' def wav_info(m$,xs,ys,ww,hh,R,G,B,alpha)
' def wav_file(n_bytes,s(),n_channels,s_rate,bps,file$)
' def wav_add_stream(file$,ns,str(),max_value)
' def wav_file_stereo(n_bytes,s(),s_rate,bps,file$)
' def hertz(t$)
' def num2hex4(x,a())

The only thing that gets me down is gravity...

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

Re: Saving a wave pattern to a .WAV file

Post by Henko »

Sound quality
The "sine waveform" is made up of a number of samples.
Every sine in the stream is represented by fac=N/C samples. If the sine is too coarse, the sound will suffer from noise, or will become quite uunstable.
As N=T*S and C=T*F, the coarsiness of the sine can be written: fac=S/F, which can be written as : S = fac * F, or, if we impose a minimum quality: S >= fac * F. Hence the sample rate for the .WAV can be calculated, depending on the note frequency.

In the program hereafter, the sound quality of single notes and of chords is tested for decreasing values of fac. My judgement (far inferior to the trained audio ears of mr. rbytes) is as follows:

SINGLE NOTES
fac > 5 : good quality, no noise
2.5 < fac < 5 : stable note, but noise appearing
fac < 2.5 : unstable sound

CHORDS (frequency of highest note in chord taken)
Same as single note, but noise in the middle segment is less apparent

Fac=6 seems a save value in most cases.

The standard calculation for a given note T,F (or chord with highest note F) would become:
S = 6 * F (the sample rate for the .WAV file)
N = T * S (the total # of samples in the sine_signal() and wav_file() functions
C = T * F (the # of cycles in the sine_signal() function (wich is wrongly named as the "freq" parameter))

Richard, after playing with the test program, do you agree with the findings?

Code: Select all

pi=4*atan(1)
dim sig(10000)

  '  quality test for single note
f$="test.wav"

T=1            ' seconds
F=hertz("c6")  ' frequency of tested note

for fac=6 to 1 step -.5
print fac
S=fac*F ! N=T*S ! C=T*F
sine_signal(N,sig,100,C,0,0,"0")
wav_file(N,sig,1,S,8,f$)
play_music(f$)
next fac


/*    ' quality test for chord
f$="test.wav"

T=1            ' seconds
F=hertz("d6")  ' frequency of highest note in chord

for fac=6 to 1 step -.5
print fac
S=fac*F ! N=T*S ! C=T*F
sine_signal(N,sig,100,T*hertz("C5"),0,0,"0")
sine_signal(N,sig,100,T*hertz("e"),0,0,"+")
sine_signal(N,sig,100,T*hertz("g"),0,0,"+")
sine_signal(N,sig,100,T*hertz("b"),0,0,"+")
sine_signal(N,sig,100,T*hertz("d6"),0,0,"+")
wav_file(N,sig,1,S,8,f$)
play_music(f$)
next fac
*/
end

{signal_util}   ' version january 10, 2018
{wav_util}      ' version january 10, 2018

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: Saving a wave pattern to a .WAV file

Post by rbytes »

Yes, 5 seems the best compromise between storage space and sound quality. Based on how sound is digitally encoded, higher frequencies are always allotted fewer bytes per cycle. The typical sampling rate is 44KHz, which only allows just over two samples per cycle of the highest audible frequency of 20KHz. As pitch lowers, proportionally more samples are stored of each cycle of the wave. It is understood that frequencies above 10k are more subject to quantization errors, so while being decoded the data is processed by a filter (Nyquist) to smooth the high frequency waveforms.

When I first ran this program, I got a "read out of data" error in wav_util on the second iteration. I inserted a restore just before the error line:

Code: Select all

.......
restore
for i=0 to 43 ! read out(i) ! next i
b_rate=n_channels*bps/8*s_rate
num2hex4(n_bytes+36,x)     ' file size
for i=0 to 3 ! out(i+4)=x(i) ! next i
num2hex4(n_channels,x)  ' number of channels
for i=0 to 1 ! out(i+22)=x(i) ! next i
........
Then the program completed ok.
The only thing that gets me down is gravity...

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

Re: Saving a wave pattern to a .WAV file

Post by Henko »

A last little exercise on the music topic: a stereo stream with a chord on the left channel and the individual tones on the right channel.
Given the standard possibilities of SmartBasic, i think further development towards a music composer with this material is a dead end.
I will continue a little bit with digital filtering.

Code: Select all

' Test on stereo sound via .WAV file
' chord of [c4,f,a] on left channel
' notes c,f,a serially on right channel
' use headphones
'
pi=4*atan(1)
T=0.5 ! F=hertz("a5") ! S=5*F ! N=T*S ! C=T*F ! N3=3*N
dim v(N),tones(N3),chord(N3),stereo(6*N)

' *** GENERATE CHORD SIGNAL
sine_signal(N3,chord,100,hertz("c4"),0,0,"0")
sine_signal(N3,chord,100,hertz("f"),0,0,"+")
sine_signal(N3,chord,100,hertz("a"),0,0,"+")

' *** GENERATE TONES SIGNAL 
sine_signal(N,v,100,hertz("c"),0,0,"0")
for i=0 to N-1 ! tones(i)=v(i) ! next i
sine_signal(N,v,100,hertz("f"),0,0,"0")
for i=0 to N-1 ! tones(N+i)=v(i) ! next i
sine_signal(N,v,100,hertz("a"),0,0,"0")
for i=0 to N-1 ! tones(2*N+i)=v(i) ! next i

' *** MIX BOTH STREAMS INTO A STEREO STREAM
for i=0 to N3-1
  stereo(2*i)=chord(i) ! stereo(2*i+1)=tones(i)
  next i

' *** PUT IT IN A .WAV FILE AND PLAY IT
wav_file_stereo(2*N3,stereo,S,8,"out.wav")
play_music("out.wav")
end

{signal_util}   ' version january 10, 2018
{wav_util}      ' version january 10, 2018


Post Reply