Post by Kyle Gagner on Mar 6, 2012 13:33:52 GMT -5
I wrote some code to help me generate wave files. Down at the bottom is an example which will run if you run the script. It takes a while... Reasonably good bass sound for my first attempt?
Edit: I see it murdered my indentation, E-mail me if you want the code.
micro.farad2@gmail.com
import wave
import struct
import math
twopi=2*math.pi #2*pi
class Writer: #Writes .wav files
def __init__(self,name,channels=1,bytespersample=4,framerate=44100):
self.file=wave.open(name,'wb')
if bytespersample==4:
self.convert=struct.Struct('<i')
elif bytespersample==2:
self.convert=struct.Struct('<h')
elif bytespersample==1:
self.convert=struct.Struct('<b')
self.multiply,=self.convert.unpack(((bytespersample-1)*b'\xFF')+b'\x7F')
self.file.setparams((channels,bytespersample,framerate,0,'NONE','noncompressed'))
self.buffer=b''
def Write(self,value):
self.buffer+=self.convert.pack(int(value*self.multiply))
if len(self.buffer)==4096:
self.file.writeframes(self.buffer)
buffer=b''
def Close(self):
self.file.writeframes(self.buffer)
self.file.close()
class Oscillator: #Base class for wave generators
def __init__(self,framerate):
self.framerate=framerate
self.accumulator=0
def Sample(self,frequency):
self.accumulator+=frequency/self.framerate
if self.accumulator>0:
self.accumulator-=1
return self.Wave(self.accumulator)
class SineGenerator(Oscillator): #Sine wave
def Wave(self,at):
return math.sin(twopi*at)
class RampGenerator(Oscillator): #Ramp wave
def Wave(self,at):
return 2*(math.fmod(at,1)+.5)
class SawGenerator(Oscillator): #Sawtooth wave
def Wave(self,at):
return 2*(-math.fmod(at,1)-.5)
class SquareGenerator(Oscillator): #Square wave
def Wave(self,at):
if math.fmod(at,1)>-.5:
return 1
else:
return -1
class TriangleGenerator(Oscillator): #Triangle wave
def Wave(self,at):
if math.fmod(at,1)>-.5:
return (at+.25)*4
else:
return (math.fmod(at,1)+.75)*(-4)
class LowPassFilter: #Cuts out high frequencies from a signal
def __init__(self,framerate):
self.framerate=framerate
self.value=0
def Sample(self,frequency,value):
rc=1.0/(twopi*frequency)
a=1.0/(self.framerate*(rc+(1.0/self.framerate)))
self.value=self.value+(a*(value-self.value))
if self.value>1:
self.value=1
elif self.value<-1:
self.value=-1
return self.value
class HighPassFilter: #Cuts out low frequencies from a signal
def __init__(self,framerate):
self.framerate=framerate
self.value=0
self.old=0
def Sample(self,frequency,value):
rc=1.0/(twopi*frequency)
a=rc/(rc+(1.0/self.framerate))#1.0/(self.framerate*(rc+(1.0/self.framerate)))
self.value=a*(self.value+value-self.old)
if self.value>1:
self.value=1
elif self.value<-1:
self.value=-1
self.old=value
return self.value
class BandPassFilter: #Cuts out frequencies outside of a certain range
def __init__(self,framerate):
self.highpass=HighPassFilter(framerate)
self.lowpass=LowPassFilter(framerate)
def Sample(self,lowfreq,highfreq,value):
return self.lowpass.Sample(highfreq,self.highpass.Sample(lowfreq,value))
class BandRejectFilter: #Cuts out frequencies inside a certain range
def __init__(self,framerate):
self.highpass=HighPassFilter(framerate)
self.lowpass=LowPassFilter(framerate)
def Sample(self,lowfreq,highfreq,value):
return (self.lowpass.Sample(lowfreq,value)+self.highpass.Sample(highfreq,value))/2
class Misc: #Miscellaneous
toneclasses = {'A':0,'B':2,'C':3,'D':5,'E':7,'F':8,'G':10}
def Note(notation): #Turn a musical note (A4 Bb3 D#2 etc...) into a frequency
if (notation[1] == '#'):
marker = 1
octave = int(notation[2:])
elif (notation[1] == 'b'):
marker = -1
octave = int(notation[2:])
else:
marker = 0
octave = int(notation[1:])
return 440*(2**((Misc.toneclasses[notation[0]]+marker+(12*(octave-4)))/float(12)))
fr=44100 #framerate
write=Writer("Testwave.wav",framerate=fr) #file
#waves and filters
main=TriangleGenerator(fr)
mod=SineGenerator(fr)
filt=LowPassFilter(fr)
wub=SineGenerator(fr)
low=SineGenerator(fr)
saw=SawGenerator(fr)
wow=TriangleGenerator(fr)
for n in range(44100*6): #write 6 seconds of sound
t=math.fmod(n/44100,3)
if n%44100==0:
print(n/44100)
if t<3/4:
freq=Misc.Note('A2')
elif t<6/4:
freq=Misc.Note('G1')
elif t<9/4:
freq=Misc.Note('A2')
elif t<11/4:
freq=Misc.Note('B2')
else:
freq=Misc.Note('C2')
fpass=((wub.Sample(4)*2.5)+3)*freq
a=main.Sample(freq+(freq*.2*mod.Sample(freq*4.7)))*4
if a>1:
a=1
elif a<-1:
a=-1
b=saw.Sample(freq+(freq*.8*wow.Sample(freq*2.4)))
write.Write(filt.Sample(fpass,(a+b)/2)) #add sample to the file
write.Close() #close the file
Edit: I see it murdered my indentation, E-mail me if you want the code.
micro.farad2@gmail.com