Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from os.path import abspath as resolvePath, getsize
- from math import sin
- from time import time
- from _io import BufferedWriter, TextIOWrapper #just used for a type comparison
- #converts a -1 -> 1 number to an integer (unsigned if bytesNum == 1)
- #maxValue should be (256**bytesNum)/2
- def unitToBytes(unit,bytesNum=2,maxValue=32768,byteOrder="little"):
- if bytesNum > 1:
- temp=round(unit*(maxValue-1))
- else:
- temp=min(round(((unit+1)*(maxValue))),255)
- if temp >= 0:
- return (temp).to_bytes(bytesNum,byteOrder)
- return (maxValue*2+temp).to_bytes(bytesNum,byteOrder)
- #populates an existing "wb" file handle with a wav file header
- def generateWavHeader(file,sampleRate=48000,numChannels=1,bytesPerSample=2):
- #validate args
- errors=[]
- #file handle
- if type(file) != BufferedWriter and type(file) != TextIOWrapper:
- errors.append("file argument must be a file handle")
- elif file.mode != "wb":
- errors.append('file argument must be mode "wb", is "'+file.mode+'" instead')
- #sample rate
- if type(sampleRate) != int:
- errors.append('sampleRate argument must be type "int"')
- elif sampleRate <= 0 or sampleRate >= 2**32:
- errors.append('sampleRate argument must be > 0 and < 2^32')
- #number of audio channels
- if type(numChannels) != int:
- errors.append('numChannels argument must be type "int"')
- elif numChannels <= 0 or numChannels >= 2**16:
- errors.append('numChannels argument must be > 0 and < 2^16')
- #sample resolution
- if type(bytesPerSample) != int:
- errors.append('bytesPerSample argument must be type "int"')
- elif bytesPerSample <= 0 or bytesPerSample >= 5:
- errors.append('bytesPerSample argument must be > 0 and < 5')
- if len(errors) > 0:
- return False,errors
- file.write(bytes('RIFF','ascii')) #4 chunkID
- file.write((2**32-1).to_bytes(4,'little')) #4 chunkSize
- file.write(bytes('WAVE','ascii')) #4 format
- file.write(bytes('fmt ','ascii')) #4 subchunk1ID
- file.write((16).to_bytes(4,'little')) #4 subchunk1Size
- file.write((1).to_bytes(2,'little')) #2 audioFormat (PCM=1)
- file.write(numChannels.to_bytes(2,'little')) #2 numChannels
- file.write(sampleRate.to_bytes(4,'little')) #4 sampleRate
- file.write((sampleRate*bytesPerSample).to_bytes(4,'little'))#4 byteRate
- file.write((numChannels*bytesPerSample).to_bytes(2,'little'))#2 blockAlign
- file.write((8*bytesPerSample).to_bytes(2,'little'))#2 bitsPerSample
- file.write(bytes('data','ascii')) #4 subchunk2ID
- file.write((2**32-1).to_bytes(4,'little')) #4 subchunk2Size
- return True,sampleRate,bytesPerSample,numChannels
- #edits a wav's header to include an accurate byte size
- def finalizeWav(path):
- fileSize=getsize(path)
- if fileSize >= 256**4: #file size must be less than 256**4 bytes
- return None,"fileSize in header can't be bigger than 4GiB"
- file=open(path,'r+b')
- file.seek(int('4',16),0)#chunksize 0x4=fileSize-8
- file.write((fileSize-8).to_bytes(4,'little'))
- file.seek(int('28',16),0)#subchunk2size 0x28=fileSize-44
- file.write((fileSize-44).to_bytes(4,'little'))
- return True
- def getSampleUnit(sample,hertz,sampleRate=48000):
- if hertz <= 0: #hertz can't be <= nothing
- return 0
- return sin((6.2831853071795864769252866*(sample/sampleRate))*hertz)#~6.2=2PI
- coolPath="cool.wav"
- secondHertz=100 #amount of change in hertz every second
- sampleRate=2000 #if sampleRate is like <4x the hertz, there's a cool fade-out
- seconds=3
- timeStart=time()
- coolPath=resolvePath(coolPath)
- coolFile=open(coolPath,"wb")
- generateWavHeader(coolFile,sampleRate)
- for i in range(sampleRate*seconds):
- unit=getSampleUnit(i,secondHertz*(i/sampleRate),sampleRate)
- coolFile.write(unitToBytes(unit))
- coolFile.close()
- finalizeWav(coolPath)
- print("Finished in:"+str((time()-timeStart)*1000)+" milliseconds")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement