#!BPY
# -*- coding: latin-1 -*-
# """
# Name: 'Audio Sound Tracker'
# Blender: 248
# Group: 'Misc'
# Tooltip: 'Generates Ipo curves from wav files'
# """

__author__ = "technoestupido"  
__url__ = ("http://wiki.blender.org/index.php/Extensions:Py/Scripts/Manual/Misc/Sound_tracker")
__version__ = "1.0"
__bpydoc__ = """\

the reading of wav files is handled by the python module wave (http://docs.python.org/lib/module-wave.html), 

which must be present for the script to work. 

the generated ipo curves are of Object type by default

and show up in the locX channel.

i have added three new curve types: step oscillating,

step accumulating and step random. 

these curves change value abruptly

when the envelope passes the step threshold value from below.
	
"""

# ---------------------------------------------------------------------
#    Copyright (c) 2009 technoestupido
# 
# ***** BEGIN GPL LICENSE BLOCK ***** 
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# ---------------------------------------------------------------------

######################################################
# Import modules
######################################################
from Blender import Ipo
import Blender
import wave, audioop
from Blender.BGL import *
from Blender.Draw import *
from Blender.Window import FileSelector
from Blender.Window import DrawProgressBar
from Blender.Scene import *
import math
import random
from math import *

######################################################
# Global parameters
######################################################
#Envelope detection parameters
T_Attack = Create(0.005)
T_Release = Create(0.2)
T_Threshold = Create(0.0)
T_HPCutoff = Create(0.0)
T_LPCutoff = Create(0.0)
T_FilterBypass = Create(1)

#Plot parameters
T_DrawStart = Create(0.0)
T_DrawEnd = Create(0.0)
T_DrawEnvelope = Create(1)
T_DrawInput = Create(1)
T_DrawFiltered = Create(1)
T_DrawEnvelope = Create(1)
T_DrawKeyFrames = Create(1)
T_DrawScaleMode = Create(1)

#Ipoparams
T_CurveMode= Create(0)
T_IpoType= Create(1)
T_IpoChannel= Create(1)
T_IpoName= Create("SoundIpo")
T_IpoSoundStart= Create(0.0)
T_IpoSoundEnd= Create(0.0)
T_IpoStart= Create(0.0)
T_IpoKeyInterval = Create(0.05) #seconds
T_IpoStepThreshold = Create(0.1)

#Other params
fileName = ""
fileLoaded = 0
sampleRate = 0.00000001
bitDepth = 0
soundLength = 0.00000001
nChannels = 0
soundData = None
isGenerating = 0
drawScale = 1.0

#Preview arrays
wavePreview = [0]
filteredPreview = [0]
envelopePreview = [0]

# Event numbers
EVENT_NOEVENT = 1
EVENT_GENERATE = 2
EVENT_THRESHCHANGE  = 3
EVENT_LPCHANGE  = 4
EVENT_HPCHANGE  = 5
EVENT_REDRAW  = 6
EVENT_LOAD = 7
EVENT_SOUNDSTARTCHANGE = 8
EVENT_SOUNDENDCHANGE = 9
EVENT_DRAWSTARTCHANGE = 10
EVENT_DRAWENDCHANGE = 11
EVENT_SELECTFILE = 12
EVENT_LOADFILE = 13
EVENT_SCALECHANGE = 13
EVENT_EXIT = 14

#GUI grid
lineHeight = int(14)
topMargin = 3
line = [int((topMargin+lineHeight)*i)+150-28 for i in range(0,100)]
colWidth = int(220)
leftMargin = 5
col = [int((leftMargin+colWidth)*i)+leftMargin for i in range(0,50)]
plotWidth = 2*colWidth+leftMargin
plotHeight = 200


#####################################################################################
# GUI drawing
#####################################################################################
def draw():
		global col, line, colWidth, lineHeight, fileName, fileLoaded, soundLength, nChannels,sampleRate,bitDepth
		global T_Attack, T_Release, T_Threshold, T_HPCutoff, T_LPCutoff, T_FilterBypass
		global T_DrawStart, T_DrawEnd, T_DrawEnvelope, T_DrawInput, T_DrawFiltered, T_DrawEnvelope, T_DrawKeyFrames, T_DrawScaleMode
		global T_CurveMode, T_IpoType, T_IpoChannel, T_IpoName, T_IpoSoundStart, T_IpoSoundEnd, T_IpoStart, T_IpoKeyInterval, T_IpoStepThreshold
		global EVENT_NOEVENT, EVENT_GENERATE, EVENT_THRESHCHANGE, EVENT_LPCHANGE, EVENT_HPCHANGE
		global EVENT_REDRAW, EVENT_LOAD, EVENT_SOUNDSTARTCHANGE, EVENT_SOUNDENDCHANGE, EVENT_SELECTFILE
		global EVENT_LOADFILE, EVENT_EXIT, EVENT_DRAWENDCHANGE, EVENT_DRAWSTARTCHANGE
 		glClear(GL_COLOR_BUFFER_BIT)

		#File section
		glColor3f(0,0,0)
		glRasterPos2d(col[0], line[22]+topMargin)
		Text("WAV file")
		glRasterPos2d(col[0], line[20]+topMargin)
		Button("Open a wav file",EVENT_SELECTFILE , col[0], line[21], colWidth/2, lineHeight)
		String("", EVENT_NOEVENT, col[0]+colWidth/2+leftMargin,line[21],3*colWidth/2-leftMargin,lineHeight, fileName,50,"Current wav file")
		if fileLoaded == 1:
			Text(" File info: " + str(sampleRate)+ " Hz, "+ str(bitDepth)+" bits, "+str(int(100*soundLength)/100.0)+" s, "+ str(nChannels) + " channels", "tiny")
		else:
			Text(" No file loaded", "tiny")

		#Envelope section
		glColor3f(0,0,0)			
		glRasterPos2d(col[0], line[19])
		Text("Envelope generator parameters")
		T_Attack = Slider("Attack time: ", EVENT_NOEVENT, col[0], line[18], colWidth, lineHeight, T_Attack.val, 0.0001, 2.0, 1, "Attack time in seconds")
		T_Release = Slider("Release time: ", EVENT_NOEVENT, col[0], line[17], colWidth, lineHeight,	T_Release.val, 0.001, 5.0, 1, "Release time in seconds")
		T_LPCutoff = Slider("LP cutoff: ", EVENT_LPCHANGE, col[1], line[18], colWidth, lineHeight, T_LPCutoff.val, 5, 0.99*sampleRate/2, 1, "Low pass filter cutoff frequency in Hz")
		T_HPCutoff = Slider("HP cutoff: ", EVENT_HPCHANGE, col[1], line[17], colWidth, lineHeight, T_HPCutoff.val, 5, 0.99*sampleRate/2, 1, "High pass filter cutoff frequency in Hz")
		T_Threshold = Slider("Threshold: ", EVENT_THRESHCHANGE, col[0], line[16], colWidth, lineHeight, T_Threshold.val, 0.0, 1.0, 1, "Detection threshold (the red line in the curve preview)")
		T_FilterBypass = Toggle("Bypass filters", EVENT_NOEVENT, col[1], line[16], colWidth, lineHeight, T_FilterBypass.val, "Toggle input filtering")			

		#IPO section
		glColor3f(0,0,0)
		glRasterPos2d(col[0], line[15])
		Text("Curve parameters")
		T_CurveMode = Menu("Curve shape %t|Oscillating %x0|Accumulating %x1|Oscillating accumulating %x2|Step oscillating %x3|Step accumulating %x4|Step random %x5", EVENT_NOEVENT, col[1],line[12],colWidth,lineHeight,	T_CurveMode.val,"Shape of generated curve")
		T_IpoName = String("Ipo name: ", EVENT_NOEVENT, col[0],line[12],colWidth,lineHeight, str(T_IpoName.val),50,"Name of Ipo")
		#T_IpoType = Menu("Ipo type %x1 alt 1", EVENT_NOEVENT, col[1],line[14],colWidth,lineHeight,T_IpoType.val,"Ipo type")
		#T_IpoChannel = Menu("Ipo channel %x1 alt 1", EVENT_NOEVENT, col[1],line[13],colWidth,lineHeight,T_IpoChannel.val,"Ipo channel")
		T_IpoSoundStart = Slider("Sound start ", EVENT_SOUNDSTARTCHANGE, col[0], line[14], colWidth, lineHeight,T_IpoSoundStart.val, 0.0, soundLength, 1, "Location in seconds of the first sample to process.")
		T_IpoSoundEnd = Slider("Sound end ", EVENT_SOUNDENDCHANGE, col[0], line[13], colWidth, lineHeight,T_IpoSoundEnd.val, 0.0, soundLength, 1, "Location in seconds of the last sample to process.")
		T_IpoStart = Number("Start frame", EVENT_NOEVENT, col[1], line[14], colWidth, lineHeight,T_IpoStart.val, 1, 1000000, "Starting frame of the generated curve")
		T_IpoKeyInterval = Number("Keyframe interval", EVENT_NOEVENT, col[1], line[13], colWidth, lineHeight,T_IpoKeyInterval.val, 0.02, 0.25, "Distance in seconds between keyframes")
		if T_CurveMode.val == 3 or T_CurveMode.val == 4 or T_CurveMode.val == 5:
			T_IpoStepThreshold = Number("Step threshold", EVENT_NOEVENT, col[0], line[11], colWidth, lineHeight,T_IpoStepThreshold.val, 0, 1, "Step trigger level (the orange line in the curve preview)")
		Button("Generate ipo",EVENT_GENERATE , col[1], line[11], colWidth, lineHeight)
		
		#Preview section
		glRasterPos2d(col[0], line[10])
		Text("Curve preview")
		T_DrawStart = Slider("Plot start ", EVENT_DRAWSTARTCHANGE, col[1], line[9], colWidth, lineHeight,T_DrawStart.val, 0.0, soundLength, 1, "Location in seconds of the first sample to process.")
		T_DrawEnd = Slider("Plot end ", EVENT_DRAWENDCHANGE, col[1], line[8], colWidth, lineHeight,T_DrawEnd.val, 0.0, soundLength, 1, "Location in seconds of the last sample to process.")
		Button("Recalculate preview",EVENT_REDRAW , col[1], line[6], colWidth, lineHeight)
		T_DrawEnvelope = Toggle("Draw linear interpolation", EVENT_NOEVENT, col[0], line[7], colWidth, lineHeight,T_DrawEnvelope.val, "Draw lines between keyframes")	
		T_DrawKeyFrames = Toggle("Draw keyframes", EVENT_NOEVENT, col[0], line[6], colWidth, lineHeight,T_DrawKeyFrames.val, "Draw keyframes as light green squares")	
		T_DrawInput = Toggle("Draw input", EVENT_NOEVENT, col[0], line[9], colWidth, lineHeight,T_DrawInput.val, "Draw the input signal")	
		T_DrawFiltered = Toggle("Draw filtered", EVENT_NOEVENT, col[0], line[8], colWidth, lineHeight,T_DrawFiltered.val, "Draw the filtered input signal")	
		T_DrawScaleMode = Menu("Auto scale %t|Normal scale %x1|Scale to fit input %x2|Scale to fit filtered %x3|Scale to fit envelope %x4", EVENT_SCALECHANGE, col[1],line[7],colWidth,lineHeight, T_DrawScaleMode.val,"Auto scaling mode")
		
		#About/exit section
		glColor3f(0.4,0.4,0.4)
		glRasterPos2d(col[0], line[6]-plotHeight-lineHeight)
		Text("Sound tracker (beta)")
		Button("Exit",EVENT_EXIT , col[1]+colWidth/2, line[6]-2*topMargin-plotHeight-lineHeight, colWidth/2,lineHeight)	
		drawPreview()		

#Function for drawing wave and envelope preview
def drawPreview():
		global T_DrawStart, T_DrawEnd, T_DrawEnvelope, T_DrawInput, T_DrawFiltered, T_DrawEnvelope, isGenerating, T_IpoKeyInterval, drawScale, T_DrawKeyFrames
		
		#draw background
		topY = line[6]-topMargin
		glColor3f(0.1,0.1,0.1)
		glBegin(GL_POLYGON)
		glVertex2i(leftMargin+plotWidth,topY)
		glVertex2i(leftMargin+plotWidth,topY-plotHeight)
		glVertex2i(leftMargin,topY-plotHeight)
		glVertex2i(leftMargin,topY)
		glEnd()

		#draw y grid
		glColor3f(0.15,0.15,0.15)		
		for i in range(1,11):
			if int(drawScale*i*plotHeight/10) <= plotHeight:
				glBegin(GL_LINE_STRIP)
				glVertex2i(leftMargin,topY-plotHeight+int(drawScale*i*plotHeight/10))
				glVertex2i(leftMargin+plotWidth,topY-plotHeight+int(drawScale*i*plotHeight/10))
				glEnd()

		#plot curves if a soundfile is loaded and if the envelope generator is not runnning
		if fileLoaded == 1 and soundData != None and isGenerating == 0:
			#draw input signal
			global wavePreview
			if T_DrawInput.val == 1:
				glColor3f(0.1,0.2,0.1)		
				
				for n in range(0,len(wavePreview)):
					glBegin(GL_LINE_STRIP)
					glVertex2i(leftMargin+n,min(topY,1+topY-plotHeight+int(drawScale*wavePreview[n]*plotHeight)))
					glVertex2i(leftMargin+n,1+topY-plotHeight)
					#print wavePreview[n]
					glEnd()
			
			#draw filtered signal
			if T_DrawFiltered.val == 1:
				glColor3f(0.1,0.35,0.1)		
				for n in range(0,len(filteredPreview)):
					glBegin(GL_LINE_STRIP)
					glVertex2i(leftMargin+n,min(topY,1+topY-plotHeight+int(drawScale*filteredPreview[n]*plotHeight)))
					glVertex2i(leftMargin+n,1+topY-plotHeight)
					glEnd()

			#draw envelope
			global envelopePreview
			if T_DrawEnvelope.val == 1 and len(envelopePreview) > 1:
				glColor3f(0.1,0.8,0.1)		
				glBegin(GL_LINE_STRIP)
				for n in range(0,len(envelopePreview)):
					glVertex2i(leftMargin+n*(plotWidth)/(len(envelopePreview)-1),min(topY,1+topY-plotHeight+int(drawScale*envelopePreview[n]*plotHeight)))
				glEnd()
			if T_DrawKeyFrames.val == 1 and len(envelopePreview) > 1:
			#mark keyframes
				for n in range(0,len(envelopePreview)):
					glColor3f(0.5,1,0.5)	
					if drawScale*envelopePreview[n] <= 1:
						glBegin(GL_POLYGON)
						glVertex2i(leftMargin+n*(plotWidth)/(len(envelopePreview)-1)+2,2+topY-plotHeight+int(drawScale*envelopePreview[n]*plotHeight))
						glVertex2i(leftMargin+n*(plotWidth)/(len(envelopePreview)-1)+2,-1+topY-plotHeight+int(drawScale*envelopePreview[n]*plotHeight))
						glVertex2i(leftMargin+n*(plotWidth)/(len(envelopePreview)-1)-1,-1+topY-plotHeight+int(drawScale*envelopePreview[n]*plotHeight))
						glVertex2i(leftMargin+n*(plotWidth)/(len(envelopePreview)-1)-1,+2+topY-plotHeight+int(drawScale*envelopePreview[n]*plotHeight))
						glEnd()

			#draw threshold
			glColor3f(0.7,0.2,0)			
			glBegin(GL_LINE_STRIP)
			glVertex2i(leftMargin,topY-int((1-T_Threshold.val*drawScale)*plotHeight))
			glVertex2i(leftMargin+plotWidth,topY-int((1-T_Threshold.val*drawScale)*plotHeight))
			glEnd()
			
			#draw step threshold
			if T_CurveMode.val == 3 or T_CurveMode.val == 4 or T_CurveMode.val == 5:
				glColor3f(0.7,0.8,0)			
				glBegin(GL_LINE_STRIP)
				glVertex2i(leftMargin,topY-int((1-T_IpoStepThreshold.val*drawScale)*plotHeight))
				glVertex2i(leftMargin+plotWidth,topY-int((1-T_IpoStepThreshold.val*drawScale)*plotHeight))
				glEnd()
							
		#draw frame
		glColor3f(0.33,0.33,0.33)		
		glBegin(GL_LINE_LOOP)
		glVertex2i(leftMargin+plotWidth,topY)
		glVertex2i(leftMargin+plotWidth,topY-plotHeight)
		glVertex2i(leftMargin,topY-plotHeight)
		glVertex2i(leftMargin,topY)
		glEnd()

		#draw numbers
		glColor3f(0.7,0.7,0.7)	
		for i in range(1,11):
			if int(drawScale*i*plotHeight/10) <= plotHeight:
				glRasterPos2d(leftMargin+2,topY-plotHeight+int(drawScale*i*plotHeight/10)+3)
				Text(str(i/10.0),"tiny")
		if isGenerating == 1:
			glRasterPos2d(leftMargin+plotWidth/2-20,topY/2)
			Text(str("workning..."),"tiny")
			

#####################################################################################
#EVENT HANDLING
#####################################################################################
def event(evt, val):
        if (evt == QKEY and not val):
                Exit()
def bevent(evt):
		global fileLoaded, isGenerating, wavePreview, filteredPreview, envelopePreview
		global T_Attack, T_Release, T_Threshold, T_HPCutoff, T_LPCutoff, T_FilterBypass
		global T_DrawStart, T_DrawEnd, T_DrawEnvelope, T_DrawInput, T_DrawFiltered, T_DrawEnvelope, T_DrawScaleMode, drawScale
		global T_CurveMode, T_IpoType, T_IpoChannel, T_IpoName, T_IpoSoundStart, T_IpoSoundEnd, T_IpoStart
		global EVENT_NOEVENT, EVENT_GENERATE, EVENT_THRESHCHANGE, EVENT_LPCHANGE, EVENT_HPCHANGE
		global EVENT_REDRAW, EVENT_LOAD, EVENT_SOUNDSTARTCHANGE, EVENT_SOUNDENDCHANGE, EVENT_SELECTFILE
		global EVENT_LOADFILE, EVENT_EXIT, EVENT_DRAWENDCHANGE, EVENT_DRAWSTARTCHANGE

        ######### Manages GUI events
		if (evt == EVENT_EXIT):
			Exit()
		elif evt == EVENT_SELECTFILE:
			FileSelector(loadFile,"Load wav file")
		elif evt == EVENT_REDRAW:
			if fileLoaded != 1:
				PupMenu("No wav file loaded")
			else:
				isGenerating = 1
				Blender.Window.RedrawAll()
				generateEnvelope(1)
				isGenerating = 0
		elif evt == EVENT_GENERATE:
			if fileLoaded != 1:
				PupMenu("No wav file loaded")
			else:
				isGenerating = 1
				Blender.Window.RedrawAll()
				generateEnvelope(0)
				isGenerating = 0	
				
		elif (evt== EVENT_LPCHANGE):
			if T_LPCutoff.val < T_HPCutoff.val:
				T_LPCutoff.val = T_HPCutoff.val
				PupMenu("LP cutoff can't be lower than HP cutoff")
		elif (evt== EVENT_HPCHANGE):
			if T_HPCutoff.val > T_LPCutoff.val:
				T_HPCutoff.val = T_LPCutoff.val
				PupMenu("HP cutoff can't be higher than LP cutoff")    
		elif evt == EVENT_SOUNDSTARTCHANGE:
			if T_IpoSoundStart.val > T_IpoSoundEnd.val:
				T_IpoSoundStart.val = T_IpoSoundEnd.val - 2*T_IpoKeyInterval.val
				PupMenu("Invalid interval")
		elif evt == EVENT_SOUNDENDCHANGE:
			if T_IpoSoundStart.val > T_IpoSoundEnd.val:
				T_IpoSoundEnd.val = T_IpoSoundStart.val + 2*T_IpoKeyInterval.val
				PupMenu("Invalid interval")				
		elif evt == EVENT_DRAWSTARTCHANGE:
			if T_DrawStart.val > T_DrawEnd.val:
				T_DrawStart.val = T_DrawEnd.val - 2*T_IpoKeyInterval.val
				PupMenu("Invalid interval")
		elif evt == EVENT_DRAWENDCHANGE:
			if T_DrawStart.val > T_DrawEnd.val:
				T_DrawEnd.val = T_DrawStart.val + 2*T_IpoKeyInterval.val
				PupMenu("Invalid interval")
		elif evt == EVENT_SCALECHANGE:
		#Auto scale off %x1|Auto scale input %x2|Auto scale filtered %x3|Auto scale envelope %x4
			if T_DrawScaleMode.val == 1:
				drawScale = 1
			elif T_DrawScaleMode.val == 2:
				if len(wavePreview) > 1: #ANVÄND MAX ISTÄLLET!!!
					maximum = 0
					for i in range(0,len(wavePreview)):
						if wavePreview[i] > maximum:
							maximum = wavePreview[i]
					if maximum != 0:
						drawScale = 1/maximum
					else:
						drawScale = 1
				else:
					drawScale = 1
			elif T_DrawScaleMode.val == 3:
				if len(filteredPreview) > 1:
					maximum = 0
					for i in range(0,len(filteredPreview)):
						if filteredPreview[i] > maximum:
							maximum = filteredPreview[i]
					if maximum != 0:
						drawScale = 1/maximum
					else:
						drawScale = 1
				else:
					drawScale = 1
			elif T_DrawScaleMode.val == 4:
				if len(envelopePreview) > 1:
					maximum = 0
					for i in range(0,len(envelopePreview)):
						if envelopePreview[i] > maximum:
							maximum = envelopePreview[i]
					if maximum != 0:
						drawScale = 1/maximum
					else:
						drawScale = 1
				else:
					drawScale = 1
		Redraw()

########################################################################
#FILE LOADER
########################################################################
def loadFile(path):
	global fileName, fileLoaded, sampleRate, bitDepth, soundLength, nChannels, soundData
	global T_IpoSoundEnd, T_DrawEnd, envelopePreview, wavePreview
	fileName = path
	try:
		w = wave.open(path, "r")
		nChannels = w.getnchannels()
		bitDepth = w.getsampwidth()*8
		sampleRate = w.getframerate()
		soundLength = float(w.getnframes())/sampleRate
		T_IpoSoundEnd.val = T_DrawEnd.val = soundLength
		T_LPCutoff.val = sampleRate/2
		T_HPCutoff.val = 0
		if T_DrawEnd.val > 3:
			T_DrawEnd.val = 3
		fileLoaded = 1
		soundData = w
		envelopePreview = [0]
		wavePreview = [0]
	except:
		fileLoaded = 0
		soundLength = sampleRate= 0.00000001
		T_IpoSoundEnd.val = T_DrawEnd.val = soundLength
		PupMenu("Error reading wav file")
		soundData = None

##########################################################################
#ENVELOPE GENERATOR
###########################################################################
def generateEnvelope(generatePreview):
	global T_Attack, T_Release, T_Threshold, T_IpoStepThreshold, soundData, sampleRate, bitDepth, envelopePreview, wavePreview
	global filteredPreview, T_CurveMode
	global T_IpoName, isGenerating, T_IpoKeyInterval, T_LPCutoff, T_HPCutoff, T_FilterBypass, plotWidth
	
	#calculate the interval between keyframes in samples
	keyIntervalSamp = int(sampleRate*T_IpoKeyInterval.val)

	#make sure we only read samples in a valid interval
	if generatePreview == 1:
		nStart = int(sampleRate*T_DrawStart.val)
		nEnd = int(sampleRate*T_DrawEnd.val)
	else:
		nStart = int(sampleRate*T_IpoSoundStart.val)
		nEnd = int(sampleRate*T_IpoSoundEnd.val)

	#get current fps
	fps = Blender.Scene.GetCurrent().getRenderingContext().framesPerSec()
	
	#start progress bar
	DrawProgressBar(0, "Working...")
	
	#curve mode
	mode = T_CurveMode.val

	#calculate normalization factor to get output values in the interval [0,1]
	normalization = 1/math.pow(2,bitDepth-1)
	
	#detection threshold
	thresh = T_Threshold.val/normalization

	#keeps track of accumulated peaks
	accum = 0

	#keeps track of the state of the step curve
	stepState = 0

	if generatePreview == 1:
		envelopePreview = [0]
		wavePreview = [0]
		filteredPreview = [0]

	#calculate envelope follower filter coefficients from attack and release times
	attRelThresh = 0.1 #threshold to define att/rel times
	bAtt = math.exp(math.log(attRelThresh)/(sampleRate*T_Attack.val-1))
	aAtt = 1-bAtt;
	bRel = math.exp(math.log(attRelThresh)/(sampleRate*T_Release.val-1))
	aRel = 1-bRel;

	#calculate HP and LP coefficients from T_LPCutoff.val and T_HPCutoff.val
	a_LP = []
	b_LP = []
	a_HP = []
	b_HP = []
	if T_FilterBypass.val != 1: #if the filters are not bypassed
		k_1 = 0.7653668647
		k_2 = 1.8477590650
		#calculate LP coefficients
		Omega_c_LP = 2*T_LPCutoff.val*math.pi/sampleRate#desired digital cutoff
		omega_c_LP = 2*math.tan(Omega_c_LP/2)		#prewarped analog cutoff
		o4 = math.pow(omega_c_LP,4)
		b_LP = [o4, 4*o4, 6*o4, 4*o4, o4]
		c_1 = 4+2*k_1*omega_c_LP+omega_c_LP*omega_c_LP;
		d_1 = 4-2*k_1*omega_c_LP+omega_c_LP*omega_c_LP;
		c_2 = 4+2*k_2*omega_c_LP+omega_c_LP*omega_c_LP;
		d_2 = 4-2*k_2*omega_c_LP+omega_c_LP*omega_c_LP;
		a_LP = [c_1*c_2, 
			(c_1+c_2)*(2*omega_c_LP*omega_c_LP-8), 
			c_1*d_2+c_2*d_1+(2*omega_c_LP*omega_c_LP-8)*(2*omega_c_LP*omega_c_LP-8) ,
                        (d_1+d_2)*(2*omega_c_LP*omega_c_LP-8),
			d_1*d_2]
		#calculate HP coefficients
		Omega_c_HP = 2*T_HPCutoff.val*math.pi/sampleRate #desired digital cutoff
		omega_c_HP = 2*math.tan(Omega_c_HP/2)		 #prewarped analog cutoff
		b_HP = [16, -4*16, 6*16, -4*16, 16]
		c_1 = 4+2*k_1*omega_c_HP+omega_c_HP*omega_c_HP;
		d_1 = 4-2*k_1*omega_c_HP+omega_c_HP*omega_c_HP;
		c_2 = 4+2*k_2*omega_c_HP+omega_c_HP*omega_c_HP;
		d_2 = 4-2*k_2*omega_c_HP+omega_c_HP*omega_c_HP;
		a_HP = [c_1*c_2, 
			(c_1+c_2)*(2*omega_c_HP*omega_c_HP-8), 
			c_1*d_2+c_2*d_1+(2*omega_c_HP*omega_c_HP-8)*(2*omega_c_HP*omega_c_HP-8) ,
                        (d_1+d_2)*(2*omega_c_HP*omega_c_HP-8),
			d_1*d_2]
		#normalize coefficients
		b_LP = [b_LP[0]/a_LP[0], b_LP[1]/a_LP[0], b_LP[2]/a_LP[0], b_LP[3]/a_LP[0], b_LP[4]/a_LP[0]]
		a_LP = [1, a_LP[1]/a_LP[0], a_LP[2]/a_LP[0], a_LP[3]/a_LP[0], a_LP[4]/a_LP[0]]
		b_HP = [b_HP[0]/a_HP[0], b_HP[1]/a_HP[0], b_HP[2]/a_HP[0], b_HP[3]/a_HP[0], b_HP[4]/a_HP[0]]		
		a_HP = [1, a_HP[1]/a_HP[0], a_HP[2]/a_HP[0], a_HP[3]/a_HP[0], a_HP[4]/a_HP[0]]

	if nStart < 0:
		nStart = 0
	if nEnd >= soundData.getnframes():
		nEnd = soundData.getnframes()-1
	soundData.setpos(nStart)

	#initialize envelope filters	
	frame_n_1 = soundData.readframes(1)
	absAvg_n_1 = absAvg_n = 0 #math.fabs(audioop.avg(frame_n_1,soundData.getsampwidth()))		
	envelope_n_1 = envelope_n = 0

	#initialize input filters absAvg=LPIn -> LP -> HP -> filtered signal
	LPIn_n_1 = LPIn_n_2 = LPIn_n_3 = LPIn_n_4 = 0
	HPIn_n_1 = HPIn_n_2 = HPIn_n_3 = HPIn_n_4 = 0 #the same as LPOut
	HPOut_n_1 = HPOut_n_2 = HPOut_n_3 = HPOut_n_4 = 0
	
	#initialize ipo
	if generatePreview == 0:
		ipoType = "Object"
		ipoName = T_IpoName.val
		curveType = "LocX"
			
		envelopeIpo = Ipo.New(ipoType,ipoName)
		envelopeIpoCurve = envelopeIpo.addCurve(curveType)
		#gör kuran till bezier va .. envelopeIpoCurve.interpolation(2)
		#envelopeIpo[property] = [1,2] 
	
	sampleMax = 0	#keeps track of the max value between two keyframes. used for wave plot
	filteredMax = 0
	previewPlotCounter = 0
	lastAddedValue = 0
	stepAccumValue = 0
	stepRandomValue = 0

	#calculate envlope for each sample and save the values of it at key frame positions
	for n in range (nStart+1,nEnd): 
		frame_n = soundData.readframes(1)	
		sample_n = audioop.getsample(frame_n,soundData.getsampwidth(),0)
		#print str(sample_n)
		absAvg_n = math.fabs(sample_n)
		if absAvg_n > sampleMax:
			sampleMax = absAvg_n

		if T_FilterBypass.val != 1: #if input filtering is on
			#low pass stage
			LPIn_n = sample_n
			LPOut_n =  b_LP[0]*LPIn_n + b_LP[1]*LPIn_n_1 + b_LP[2]*LPIn_n_2 + b_LP[3]*LPIn_n_3 + b_LP[4]*LPIn_n_4- a_LP[1]*HPIn_n_1- a_LP[2]*HPIn_n_2- a_LP[3]*HPIn_n_3- a_LP[4]*HPIn_n_4

			#high pass stage
			HPIn_n = LPOut_n 
			HPOut_n =  b_HP[0]*HPIn_n + b_HP[1]*HPIn_n_1 + b_HP[2]*HPIn_n_2 + b_HP[3]*HPIn_n_3 + b_HP[4]*HPIn_n_4 - a_HP[1]*HPOut_n_1- a_HP[2]*HPOut_n_2- a_HP[3]*HPOut_n_3- a_HP[4]*HPOut_n_4

			LPIn_n_4 = LPIn_n_3
			LPIn_n_3 = LPIn_n_2
			LPIn_n_2 = LPIn_n_1
			LPIn_n_1 = LPIn_n

			HPIn_n_4 = HPIn_n_3
			HPIn_n_3 = HPIn_n_2
			HPIn_n_2 = HPIn_n_1
			HPIn_n_1 = LPOut_n

			HPOut_n_4 = HPOut_n_3
			HPOut_n_3 = HPOut_n_2
			HPOut_n_2 = HPOut_n_1
			HPOut_n_1 = HPOut_n

			absAvg_n = math.fabs(HPOut_n) #set the signal to be used to the filtered one

			if absAvg_n > filteredMax:
				filteredMax = absAvg_n

		#do thresholding
		if thresh != 0:
			if absAvg_n < thresh:
				absAvg_n = 0
		
		#calulate new envelope value
		if absAvg_n > envelope_n_1:	#use attack parameters
			envelope_n = aAtt*absAvg_n_1 + bAtt*envelope_n_1
		else:				#use release paramters
			envelope_n = aRel*absAvg_n_1 + bRel*envelope_n_1

		#do accumulation
		if envelope_n > envelope_n_1:
			accum = accum + envelope_n - envelope_n_1		

		#check if it's time to add a curve preview point
		if previewPlotCounter > 1 and generatePreview == 1:
			wavePreview.append(sampleMax/math.pow(2,bitDepth-1))
			sampleMax = 0
			filteredPreview.append(filteredMax/math.pow(2,bitDepth-1))
			filteredMax = 0
			previewPlotCounter = previewPlotCounter - 1
			#print str(float(plotWidth)/(nEnd-nStart+1))
			#print str(len(wavePreview))
		else:
			previewPlotCounter =  previewPlotCounter + (float(plotWidth)/(nEnd-nStart+1))

		#check if it's time to add a keyframe
		if (n-nStart+1) % keyIntervalSamp == 0:
			DrawProgressBar(float(n-nStart)/(nEnd-nStart), "Working... "+str(int(100*float(n-nStart) / (nEnd-nStart) ))+ "%")
			value = 0
			if mode == 0: #oscillating
				value = (envelope_n)*normalization
			elif mode == 1: #accumulating
				value = (accum)*normalization				
			elif mode == 2: #accumulationg ocillating
				value = (envelope_n+accum)*normalization
			elif mode == 3 or mode == 4 or mode == 5: #step shapes
				#check if it's time to take a step
				if envelope_n*normalization > T_IpoStepThreshold.val and lastAddedValue < T_IpoStepThreshold.val:
					stepState = (stepState+1) % 2
					stepRandomValue = random.random()
					stepAccumValue = stepAccumValue + 0.1
					#print "accumvalue:",stepAccumValue
				if mode == 3:
					value = stepState
				if mode == 4:
					value = stepAccumValue
				if mode == 5:
					value = stepRandomValue
				
			#if the actual ipo curve should be created
			if generatePreview == 0:
				envelopeIpoCurve[fps*float((n-nStart+1))/sampleRate] = value	
			else:			
				envelopePreview.append(value)
				
			lastAddedValue = envelope_n*normalization
		envelope_n_1 = envelope_n
		absAvg_n_1 = absAvg_n
	#stop progress bar
	DrawProgressBar(1, "finished")


############
Register(draw, event, bevent)