Advertisement
Kitomas

wav synthesis demo

Feb 23rd, 2022
1,118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.00 KB | None | 0 0
  1. from os.path import abspath as resolvePath, getsize
  2. from math import sin
  3. from time import time
  4. from _io import BufferedWriter, TextIOWrapper #just used for a type comparison
  5.  
  6. #converts a -1 -> 1 number to an integer (unsigned if bytesNum == 1)
  7. #maxValue should be (256**bytesNum)/2
  8. def unitToBytes(unit,bytesNum=2,maxValue=32768,byteOrder="little"):
  9.     if bytesNum > 1:
  10.         temp=round(unit*(maxValue-1))
  11.     else:
  12.         temp=min(round(((unit+1)*(maxValue))),255)
  13.     if temp >= 0:
  14.         return (temp).to_bytes(bytesNum,byteOrder)
  15.     return (maxValue*2+temp).to_bytes(bytesNum,byteOrder)
  16.  
  17. #populates an existing "wb" file handle with a wav file header
  18. def generateWavHeader(file,sampleRate=48000,numChannels=1,bytesPerSample=2):
  19.     #validate args
  20.     errors=[]
  21.     #file handle
  22.     if type(file) != BufferedWriter and type(file) != TextIOWrapper:
  23.         errors.append("file argument must be a file handle")
  24.     elif file.mode != "wb":
  25.         errors.append('file argument must be mode "wb", is "'+file.mode+'" instead')
  26.     #sample rate
  27.     if type(sampleRate) != int:
  28.         errors.append('sampleRate argument must be type "int"')
  29.     elif sampleRate <= 0 or sampleRate >= 2**32:
  30.         errors.append('sampleRate argument must be > 0 and < 2^32')
  31.     #number of audio channels
  32.     if type(numChannels) != int:
  33.         errors.append('numChannels argument must be type "int"')
  34.     elif numChannels <= 0 or numChannels >= 2**16:
  35.         errors.append('numChannels argument must be > 0 and < 2^16')
  36.     #sample resolution
  37.     if type(bytesPerSample) != int:
  38.         errors.append('bytesPerSample argument must be type "int"')
  39.     elif bytesPerSample <= 0 or bytesPerSample >= 5:
  40.         errors.append('bytesPerSample argument must be > 0 and < 5')
  41.     if len(errors) > 0:
  42.         return False,errors
  43.     file.write(bytes('RIFF','ascii'))                 #4 chunkID
  44.     file.write((2**32-1).to_bytes(4,'little'))        #4 chunkSize
  45.     file.write(bytes('WAVE','ascii'))                 #4 format
  46.     file.write(bytes('fmt ','ascii'))                 #4 subchunk1ID
  47.     file.write((16).to_bytes(4,'little'))             #4 subchunk1Size
  48.     file.write((1).to_bytes(2,'little'))              #2 audioFormat (PCM=1)
  49.     file.write(numChannels.to_bytes(2,'little'))      #2 numChannels
  50.     file.write(sampleRate.to_bytes(4,'little'))       #4 sampleRate
  51.     file.write((sampleRate*bytesPerSample).to_bytes(4,'little'))#4 byteRate
  52.     file.write((numChannels*bytesPerSample).to_bytes(2,'little'))#2 blockAlign
  53.     file.write((8*bytesPerSample).to_bytes(2,'little'))#2 bitsPerSample
  54.     file.write(bytes('data','ascii'))                 #4 subchunk2ID
  55.     file.write((2**32-1).to_bytes(4,'little'))        #4 subchunk2Size
  56.     return True,sampleRate,bytesPerSample,numChannels
  57.  
  58. #edits a wav's header to include an accurate byte size
  59. def finalizeWav(path):
  60.     fileSize=getsize(path)
  61.     if fileSize >= 256**4: #file size must be less than 256**4 bytes
  62.         return None,"fileSize in header can't be bigger than 4GiB"
  63.     file=open(path,'r+b')
  64.     file.seek(int('4',16),0)#chunksize 0x4=fileSize-8
  65.     file.write((fileSize-8).to_bytes(4,'little'))
  66.     file.seek(int('28',16),0)#subchunk2size 0x28=fileSize-44
  67.     file.write((fileSize-44).to_bytes(4,'little'))
  68.     return True
  69.  
  70. def getSampleUnit(sample,hertz,sampleRate=48000):
  71.     if hertz <= 0: #hertz can't be <= nothing
  72.         return 0
  73.     return sin((6.2831853071795864769252866*(sample/sampleRate))*hertz)#~6.2=2PI
  74.  
  75.  
  76. coolPath="cool.wav"
  77. secondHertz=100 #amount of change in hertz every second
  78. sampleRate=2000 #if sampleRate is like <4x the hertz, there's a cool fade-out
  79. seconds=3
  80.  
  81. timeStart=time()
  82. coolPath=resolvePath(coolPath)
  83. coolFile=open(coolPath,"wb")
  84. generateWavHeader(coolFile,sampleRate)
  85. for i in range(sampleRate*seconds):
  86.     unit=getSampleUnit(i,secondHertz*(i/sampleRate),sampleRate)
  87.     coolFile.write(unitToBytes(unit))
  88. coolFile.close()
  89. finalizeWav(coolPath)
  90. print("Finished in:"+str((time()-timeStart)*1000)+" milliseconds")
  91.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement