This copy paste maneuver might mess with the indentation so you might want to look it over after you paste it into the text editor.
Code: Select all
#!/usr/bin/python
# PAD Synthesis for python
# From a description by Paul Nasca
# By Rock Hardbuns, 2007
# Released to the Public Domain by the author.
import os, sys, math, struct
from numpy import *
# Length of the output sample
WaveNumSamples = int(math.pow(2, 15)) #useful range: 14 - 18
# PAD export config
SampleRate = 44100.0
F = 172.0
variations = 10 #the number of waves generated
outFilePrefix = "PadWave_"
# Set up export for unsigned short int
sampleFormat = 'H'
uIntMaxVal = 65535
### FUNCTIONS
#take an array of floats(0-1) and convert to unsigned ints
#and write to a raw binary file
def writeArrayRaw(N, outArray, filename, sampleFormat, uIntMaxVal):
outFile = open(filename, 'wb')
for i in range(N):
binInt = struct.pack(sampleFormat, int( outArray[i] * uIntMaxVal ))
outFile.write(binInt)
outFile.close
print 'Wrote sample ', filename
###
# For the PAD algo.
# Gives the shape of the harmonic
def profile(fi, bwi):
x = float(fi / bwi)
return (math.exp(-x*x) / bwi)
###
# Normalize
def normalize(Array):
daMax = Array.max()
if math.fabs(Array.min()) > daMax:
daMax = math.fabs(Array.min())
if daMax < 1e-5:
daMax = 1e-5
factor = (daMax * 1.4142)
Array = Array / factor
Array = Array * 0.5 ##move to 0-1 range
Array = Array + 0.5
return Array
###
""" <-- Block Commented
# write testone ******
A = 2.0 * math.sin(math.pi * 0.0019501133786848073) # 86Hz -> 512 sample length at 44100
s1 = 0.4
s2 = 0.0
index = 0
for index in range(WaveNumSamples):
s1 = s1 - A * s2
s2 = s2 + A * s1
outArray[index] = s1 + 0.5
writeArrayRaw(WaveNumSamples, outArray, 'sin_u16.raw', sampleFormat, uIntMaxVal)
""" #end block comment
#write PAD synthesized samples ******
random.seed()
for iter in range(variations):
print 'Started variation ', iter + 1, ' of ', variations
#Bandwidth of first harmonic, random in the range 10-70
BW = 10.0 + 60.0 * random.ranf()
#Bandwidth scaling factor
#(lower than 1 means a "cleaner" tone, above more HF fuzz)
BWScale = 1.0
# Number of Harmonics
# random selection between 8, 16, 32 or 64 harmonics,
# with 64 being only half as likely as the others
X = int(random.ranf() * 3.5) + 3
NumHarm = int(math.pow(2, X))
# Harmonics Table generation
# Low volume harmonic noise + some dominant harmonics
HarmAmpTbl = random.rand(NumHarm) #fill with random floats
HarmAmpTbl *= 0.2 #reduce amp
for i in range( int(NumHarm * random.ranf()) ): #Emphasize a random number of harms
HarmAmpTbl[int(NumHarm * random.ranf())] = 1.0 - (random.ranf() * random.ranf())
#make work arrays
freq_amp = zeros(WaveNumSamples / 2 - 1)
freq_complex = zeros(WaveNumSamples / 2 - 1 , dtype=complex64 )
#Paul Nascas PAD Algo
for nh in range(1,NumHarm):
bw_Hz= ( pow( 2.0 , BW / 1200.0) - 1.0 ) * F * pow( nh, BWScale )
bwi = float(bw_Hz / (2.0 * SampleRate))
fi = float(F * nh / SampleRate)
for i in range(0, WaveNumSamples / 2 - 1 ):
hprofile = profile((float(i) / float(WaveNumSamples))- fi, bwi)
freq_amp[i] = freq_amp[i] + hprofile * HarmAmpTbl[nh]
for i in range(0, WaveNumSamples / 2 - 1):
phase = random.ranf() * 2.0 * math.pi
freq_complex[i] = complex(freq_amp[i] * math.cos(phase), freq_amp[i] * math.sin(phase))
outArray = fft.irfft(freq_complex, WaveNumSamples)
outArray = normalize(outArray)
filename = outFilePrefix + str(iter + 1) + '.raw'
writeArrayRaw(WaveNumSamples, outArray, filename, sampleFormat, uIntMaxVal)
print 'All Done'
sys.exit(0)