(function ( w, d, PKAE ) {
	'use strict';

	function PKEng ( app ) {
		var q = this;
		
		var wavesurfer = WaveSurfer.create ({
			container: '#' + 'pk_av_' + app.id,
			scrollParent: false,
			hideScrollbar:true,
			partialRender:false,
			fillParent:false,
			pixelRatio:1,
			progressColor:'rgba(128,85,85,0.24)',
			splitChannels:true,
			autoCenter:true,
			height:w.innerHeight - 168,
			plugins: [
				WaveSurfer.regions.create({
					dragSelection: {
						slop: 5
					}
				})
			]
		});
		this.wavesurfer = wavesurfer;

		var AudioUtils = new app._deps.audioutils ( app, wavesurfer );
		q.is_ready = false;
		
		this.TrimTo = function( val, num ) {
			var nums = {'0':1, '1':10, '2':100,'3':1000,'4':10000,'5':100000};
			var dec = nums[num];
			return ((val *dec) >> 0) / dec;
		}

		this.LoadArrayBuffer = function ( e ) {
			var func = function () {
				app.listenFor ('RequestCancelModal', function() {
					wavesurfer.cancelBufferLoad ();
					if (wavesurfer.arraybuffer) q.is_ready = true;

					app.fireEvent ('RequestResize');
					setTimeout(function() { app.fireEvent ('DidDownloadFile'); }, 12);
					app.stopListeningForName ('RequestCancelModal');

					OneUp ('Canceled Loading', 1350);
				});

				app.fireEvent ('RequestZoomUI', 0);

				app.fireEvent ('WillDownloadFile');
				q.is_ready = false;
				wavesurfer.loadBlob( e );
				app.fireEvent ('DidUnloadFile');

				wavesurfer.regions && wavesurfer.regions.clear();
			};

			if ( q.is_ready )
			{
					//  ----------------
					new PKSimpleModal ({
						title : 'Open or append',
						clss  : 'pk_modal_anim pk_fnt10',
						ondestroy : function ( q ) {
							app.ui.InteractionHandler.on = false;
							app.ui.KeyHandler.removeCallback ('modalTempErr');
						},
						buttons:[
							{
								title:'OPEN NEW',
								callback: function( q ) {
									wavesurfer.backend._add = 0;
									func ();
									q.Destroy ();
								}
							},
							{
								title:'ADD IN EXISTING',
								callback: function( q ) {
									wavesurfer.backend._add = 1;
									func ();
									q.Destroy ();
								}
							}
						],
						body    : '<p>Append file to existing track?</p>',
						setup   : function( q ) {
							app.ui.InteractionHandler.checkAndSet ('modal');
							app.ui.KeyHandler.addCallback ('modalTempErr', function ( e ) {
								q.Destroy ();
							}, [27]);
						}
					}).Show ();

					return ;
			}

			wavesurfer.backend._add = 0;
			func ();
			// --- 
		};

		this.LoadDB = function ( e ) {
			var new_buffer = wavesurfer.backend.ac.createBuffer (
					e.data.length,
					e.data[0].byteLength / 4,
					e.samplerate
			);

			for (var i = 0; i < e.data.length; ++i) {

				var arr = new Float32Array (e.data[i]);

				if (new_buffer.copyToChannel)
				{
					new_buffer.copyToChannel (arr, i, 0);
				}
				else
				{
					var chan = new_buffer.getChannelData (i);
					chan.set (arr);
				}
			}

			var append = wavesurfer.backend._add;
			var old_durr = wavesurfer.getDuration ();

			PKAudioEditor.engine.wavesurfer.loadDecodedBuffer (new_buffer);
			_compute_channels ();
			var new_durr = wavesurfer.getDuration ();
			app.fireEvent ('DidUpdateLen', new_durr);

			if (!append) app.fireEvent ('RequestSeekTo', 0);
			else {
					wavesurfer.regions.clear();
					wavesurfer.regions.add({
						start:old_durr,
						end:new_durr,
						id:'t'
					});
			}
			// --------
		};

		this.LoadFile = function ( e ) {
			if (e.files.length > 0)
			{
				if (e.files[0].type == "audio/mp3"
					|| e.files[0].type == "audio/wave"
					|| e.files[0].type == "audio/mpeg"
					|| e.files[0].type == "audio/aiff"
					|| e.files[0].type == "audio/flac"
					|| e.files[0].type == "audio/ogg")
				{

							var func = function () {
									app.listenFor ('RequestCancelModal', function() {
										wavesurfer.cancelBufferLoad ();
										AudioUtils.DownloadFileCancel ();
										if (wavesurfer.arraybuffer) q.is_ready = true;

										app.fireEvent ('RequestResize');
										setTimeout(function() { 
											app.fireEvent ('DidDownloadFile');
										}, 12);
										app.stopListeningForName ('RequestCancelModal');

										OneUp ('Canceled Loading', 1350);
									});

									app.fireEvent ('WillDownloadFile');
									q.is_ready = false;
									wavesurfer.loadBlob( e.files[0] );
									app.fireEvent ('DidUnloadFile');
									wavesurfer.regions && wavesurfer.regions.clear();
							};

							if ( q.is_ready )
							{
									//  ----------------
									new PKSimpleModal ({
										title : 'Open or append',
										clss  : 'pk_modal_anim pk_fnt10',
										ondestroy : function ( q ) {
											app.ui.InteractionHandler.on = false;
											app.ui.KeyHandler.removeCallback ('modalTempErr');
										},
										buttons:[
											{
												title:'OPEN NEW',
												callback: function( q ) {
													wavesurfer.backend._add = 0;
													func ();
													q.Destroy ();
												}
											},
											{
												title:'ADD IN EXISTING',
												callback: function( q ) {
													wavesurfer.backend._add = 1;
													func ();
													q.Destroy ();
												}
											}
										],
										body    : '<p>Append file to existing track?</p>',
										setup   : function( q ) {
											app.ui.InteractionHandler.checkAndSet ('modal');
											app.ui.KeyHandler.addCallback ('modalTempErr', function ( e ) {
												q.Destroy ();
											}, [27]);
										}
									}).Show ();

									return ;
							}

							wavesurfer.backend._add = 0;
							func ();

							// ----					
				}
			}
		};

		this.DownloadFile = function ( name, format, kbps, selection, stereo ) {
			if (!q.is_ready) return ;

			app.fireEvent ('WillDownloadFile');

			app.listenFor ('RequestCancelModal', function() {
				AudioUtils.DownloadFileCancel ();
				if (wavesurfer.arraybuffer) q.is_ready = true;

				app.fireEvent ('RequestResize');
				setTimeout(function() { app.fireEvent ('DidDownloadFile'); }, 12);
				app.stopListeningForName ('RequestCancelModal');
			});

			setTimeout(function() {
				AudioUtils.DownloadFile ( name, format, kbps, selection, stereo, function ( val ) {
					if (val === 'done')
					{
						setTimeout(function() { app.fireEvent ('DidDownloadFile'); }, 12);
						app.stopListeningForName ('RequestCancelModal');
					}
					else
						app.fireEvent ('DidProgressModal', val);
				});
			}, 220);
		}
		this.LoadSample = function () {

			app.fireEvent ('WillDownloadFile');
			
			setTimeout(function () {

				app.listenFor ('RequestCancelModal', function() {
					if (wavesurfer.cancelAjax ())
					{
						if (wavesurfer.arraybuffer) q.is_ready = true;

						app.fireEvent ('RequestResize');
						setTimeout(function() { app.fireEvent ('DidDownloadFile'); }, 12);
						app.stopListeningForName ('RequestCancelModal');

						OneUp ('Canceled Loading', 1380);
					}
				});

				app.fireEvent ('RequestZoomUI', 0);
				q.is_ready = false;
				wavesurfer.load ('test.mp3');
			}, 180);
		}
		this.LoadURL = function ( url ) {
			app.fireEvent ('WillDownloadFile');

			/*
			var context =  new AudioContext (); // wavesurfer.backend.ac;
			var audio_el = d.createElement ('audio');
			audio_el.autoplay = true;
			audio_el.controls = true;
			audio_el.preload = true;
//			audio_el.playbackRate = 0.5;
			d.body.appendChild( audio_el );
			audio_el.src = url;

			setTimeout(function() {
				var source = context.createMediaElementSource (audio_el);
				// source.connect(context.destination);

				var time_duration = audio_el.duration / 1;
				var first = true;
				var audio_buffer = null;
				var sample_rate = 0;
				var chans = 0;

				var scriptNode = context.createScriptProcessor (4096, 1, 1);
				window.sss = scriptNode;
				window.eee = source;
				window.ccc = context;

				var prev_time = 0;

				scriptNode.onaudioprocess = function( ev ) {
					if (audio_el.paused) return ;

					var ctime = audio_el.currentTime / 1;

					if ((ctime + 0.0001) >= time_duration)
					{
						//if (!first) {
						//	console.log ("done");
						//	first = 100;
						//}
						return ;
					}

					var inputBuffer = ev.inputBuffer;
					// var outputBuffer = ev.outputBuffer;

					if (first) {
						first = false;

						sample_rate = inputBuffer.sampleRate;
						chans      = inputBuffer.numberOfChannels;
						audio_buffer = context.createBuffer (
							inputBuffer.numberOfChannels,
							time_duration * inputBuffer.sampleRate,
							inputBuffer.sampleRate
						);

						window.audio_buffer = audio_buffer;
					}

					var curr_time = (ctime * sample_rate) >> 0;

					 // console.log( curr_time, "   ", (curr_time - prev_time), "  ", ((curr_time - prev_time) > (4096*2))?true:false  );
					 // prev_time = curr_time;

					  for (var channel = 0; channel < inputBuffer.numberOfChannels; ++channel) {
					    var inputData = inputBuffer.getChannelData(channel);
					    // var outputData = outputBuffer.getChannelData(channel);
					    var final_data = audio_buffer.getChannelData(channel);

					    // console.log( inputData );

					    // Loop through the 4096 samples
					    for (var sample = 0; sample < inputBuffer.length; ++sample) {
					      // make output equal to the same as the input
					      // outputData[sample] = inputData[sample];

					      final_data[curr_time + sample] = inputData[sample];

					      // add noise to each output sample
					      // outputData[sample] += ((Math.random() * 2) - 1) * 0.2;         
					    }
					  }
				};

				source.connect(scriptNode);
				scriptNode.connect(context.destination);
			},2000);
			*/

			setTimeout(function () {
				app.listenFor ('RequestCancelModal', function() {
					if (wavesurfer.cancelAjax())
					{
						if (wavesurfer.arraybuffer) q.is_ready = true;

						app.fireEvent ('RequestResize');
						setTimeout(function() { app.fireEvent ('DidDownloadFile'); }, 12);
						app.stopListeningForName ('RequestCancelModal');

						OneUp ('Canceled Loading', 1350);
					}
				});

				wavesurfer.load ( url );
				q.is_ready = false;
			}, 180);
		}

		app.listenFor ('RequestResize', function () {
			wavesurfer.fireEvent ('resize');

			var h = window.innerHeight;
			var bottom = 0;

			if (app.ui && app.ui.BarBtm) {
				bottom = (app.ui.BarBtm.on ? app.ui.BarBtm.height : 0);
			}

			wavesurfer.setHeight( (h < 280 ? 280 : h) - 168 - bottom);
			// app.fireEvent ('DidResize');
		});

		wavesurfer.on ('ready', function () {
			app.fireEvent ('DidReadyFire');

			if (wavesurfer.backend._add) {
				wavesurfer.backend._add = 0;
			}

			if (q.is_ready) return ;
			q.is_ready = true;

			// dirty hack for default message
			var dirtymsg = document.getElementsByClassName('pk_tmpMsg');
			if (dirtymsg.length > 0)
			{
				dirtymsg = dirtymsg[0];
				dirtymsg.parentNode.removeChild( dirtymsg );
			}

			copy_buffer = null;
			app.fireEvent ('DidDownloadFile');

			app.fireEvent ('StateRequestClearAll');
			app.fireEvent ('DidLoadFile');
			app.fireEvent ('DidUpdateLen', wavesurfer.getDuration ());
			app.fireEvent ('DidSetClipboard', 0);
			app.fireEvent ('RequestSeekTo', 0);

			app.fireEvent ('RequestResize');
			wavesurfer.getWaveEl().style.opacity = '1';

			// loaded successfully
			app.stopListeningForName ('RequestCancelModal');

			setTimeout(function () {OneUp ('Loaded Successfully')}, 180);

			// check if the audio file is mono or stereo and rebuild both UI and audio engine accordingly...
			if (wavesurfer.backend.buffer.numberOfChannels === 1) {
				wavesurfer.backend.SetNumberOfChannels (1);
				wavesurfer.ActiveChannels = [1];
				wavesurfer.drawer.params.ActiveChannels = wavesurfer.ActiveChannels;
				wavesurfer.SelectedChannelsLen = 1;

				app.el.classList.add ('pk_mono');
			} else if (wavesurfer.backend.buffer.numberOfChannels === 2) {
				wavesurfer.backend.SetNumberOfChannels (2);
				wavesurfer.ActiveChannels = [1, 1];
				wavesurfer.drawer.params.ActiveChannels = wavesurfer.ActiveChannels;
				wavesurfer.SelectedChannelsLen = 2;

				app.el.classList.remove ('pk_mono');
			}
			// ---
		});

		wavesurfer.on ('pause', function() {
			app.fireEvent ('DidStopPlay');
		});
		wavesurfer.on ('play', function() {
			app.fireEvent ('DidPlay');
		});
		wavesurfer.on ('seek', function ( where, stamp ) {
			var time = wavesurfer.getCurrentTime();
			var loudness = wavesurfer.getLoudness();

			app.fireEvent ('DidAudioProcess', [time, loudness, stamp]);
		});

		app.listenFor ('RequestStop', function( val ) {
			if (app.rec.isActive ()) {
				app.fireEvent ('RequestActionRecordStop');
				return (false);
			}

			var region = wavesurfer.regions.list[0];
			if (region) wavesurfer.ActiveMarker = region.start / wavesurfer.getDuration ();

			wavesurfer.stop ( val );
		});
		app.listenFor ('RequestPlay', function ( x ) { // unique listener
			if (q.in_fx) return ;

			app.fireEvent ('RequestActionRecordStop');

			if ( !x && wavesurfer.isPlaying ()) {
				wavesurfer.stop ();
				wavesurfer.play ();
			}
			else {
				if (!app.rec.isActive ()) {
					wavesurfer.play ();
				} else {
					setTimeout(function() {
						if (!app.rec.isActive () && !x && !wavesurfer.isPlaying ()) {
							wavesurfer.play ();
						}
					}, 220);
				}
			}
		});
		app.listenFor ('RequestPause', function () {
			app.fireEvent ('RequestActionRecordStop');
			wavesurfer.pause();
		});

		app.listenFor ('RequestSetLoop', function () {
			if (!q.is_ready) return ;

			var skip_seek = false;

			if (wavesurfer.regions.list[0])
			{
				if (wavesurfer.regions.list[0].loop)
					wavesurfer.regions.list[0].loop = false;
				else
					wavesurfer.regions.list[0].loop = true;
			}
			else
			{
				skip_seek = true;
				wavesurfer.regions.add({
					start:0.01,
					end:wavesurfer.getDuration() - 0.01,
					id:'t'
				});
				wavesurfer.regions.list[0].loop = true;
			}
			
			var will_loop = wavesurfer.regions.list[0].loop;
			app.fireEvent('DidSetLoop', will_loop);
			if (will_loop && !skip_seek /*&& wavesurfer.isPlaying ()*/) {
				app.fireEvent ('RequestSeekTo', wavesurfer.regions.list[0].start / wavesurfer.getDuration ());
			}
		});
		app.listenFor ('RequestSkipBack', function( val ) {
			wavesurfer.skipBackward ( val )
		});
		app.listenFor ('RequestSkipFront', function( val ) {
			wavesurfer.skipForward ( val );
		});
		app.listenFor ('RequestSeekTo', function( val ) {
			if (val > 1.0) return ;
			wavesurfer.seekTo( val );
		});
		app.ui.KeyHandler.addCallback ('zkA', function ( key, m, e ) {
			e.preventDefault ();
		}, [38]);
		app.ui.KeyHandler.addCallback ('zkD', function ( key, m, e ) {
			e.preventDefault ();
		}, [40]);
		app.ui.KeyHandler.addSingleCallback ('KeyPlayPause', function ( e ) {
			if (app.ui.InteractionHandler.on) return ;
			e.preventDefault();
			//e.stopPropagation();
		}, 32);

		app.ui.KeyHandler.addSingleCallback ('KeyTilda', function ( e ) {
			var open_el = app.ui.TopHeader.getOpenElement();

			if (open_el)
			{
				app.ui.TopHeader.closeMenu ();
				e.preventDefault();
				return ;
			}

			if (app.ui.InteractionHandler.on) return ;
			e.preventDefault();

			app.ui.TopHeader.openMenu (-1);
		}, 96);

		app.ui.KeyHandler.addSingleCallback ('KeyQ', function ( e ) {
			if (app.ui.InteractionHandler.on) return ;
			e.preventDefault();
			app.fireEvent ('RequestDeselect');
		}, 113);


		app.ui.KeyHandler.addCallback ('kF12', function ( k, i, e ) {
			e.preventDefault();
			e.stopPropagation();
		}, [123]);
		/*app.ui.KeyHandler.addCallback ('kF5', function ( k, i, e ) {
			e.preventDefault();
			e.stopPropagation();
		}, [116]);
		*/

		app.ui.KeyHandler.addCallback ('KeyShiftSpace' + app.id, function ( key ) {
			if (app.ui.InteractionHandler.on) return ;
			wavesurfer.playPause();
		}, [16, 32]);
		app.ui.KeyHandler.addCallback ('KeySpace' + app.id, function ( key, map ) {
			if (app.ui.InteractionHandler.on) return ;			
			if (map[16] === 1) return ;

			if (PKAudioEditor.engine.wavesurfer.isPlaying())
			{
				app.fireEvent ('RequestStop');
			}
			else
			{
				app.fireEvent ('RequestPlay');
			}
		}, [32]);
		app.ui.KeyHandler.addCallback ('KeyShiftCopy' + app.id, function ( key ) {
			if (app.ui.InteractionHandler.on) return ;
			
			app.fireEvent( 'RequestActionCopy');
		}, [16, 67]);
		app.ui.KeyHandler.addCallback ('KeyShiftUndo' + app.id, function ( key ) {
			if (app.ui.InteractionHandler.on) return ;
			
			app.fireEvent ('StateRequestUndo');
		}, [16, 90]);
		app.ui.KeyHandler.addCallback ('KeyShiftRedo' + app.id, function ( key ) {
			if (app.ui.InteractionHandler.on) return ;
			
			app.fireEvent ('StateRequestRedo');
		}, [16, 89]);
		app.ui.KeyHandler.addCallback ('KeyShiftPaste' + app.id, function ( key ) {
			if (app.ui.InteractionHandler.on) return ;
			
			app.fireEvent( 'RequestActionPaste');
		}, [16, 86]);
		app.ui.KeyHandler.addCallback ('KeyShiftCut' + app.id, function ( key ) {
			if (app.ui.InteractionHandler.on) return ;
			
			app.fireEvent( 'RequestActionCut', 1);
		}, [16, 88]);
		app.ui.KeyHandler.addCallback ('KeyDel' + app.id, function ( key ) {
			if (app.ui.InteractionHandler.on) return ;

			app.fireEvent( 'RequestActionCut');
		}, [8]);
		app.ui.KeyHandler.addCallback ('KeyShiftSelectAll' + app.id, function ( key ) {
			if (app.ui.InteractionHandler.on) return ;
			app.fireEvent ('RequestSelect');
		}, [16, 65]);
		app.ui.KeyHandler.addSingleCallback ('KeyLoopToggle', function ( e ) {
			if (app.ui.InteractionHandler.on) return ;
			e.preventDefault();
			e.stopPropagation();
			app.fireEvent ('RequestSetLoop');
		}, 108);
		app.ui.KeyHandler.addCallback ('KeyShiftSave' + app.id, function ( key ) {
			if (app.ui.InteractionHandler.on) return ;
			
			// fire event to open the save menu
			document.querySelector('.pk_opt[data-id="dl"]').click();
		}, [16, 83]);

		wavesurfer.container.addEventListener('mousedown', function(e) {
			if (e.which === 3) {
				// wavesurfer.regions.clear();
				e.preventDefault();
			}
		},false);
		
		// select all... ####
		app.listenFor ('RequestSelect', function( ifnot, custom ) {
			if (!q.is_ready) return ;

			if (ifnot)
			{
				var region = wavesurfer.regions.list[0];
				if (region) return (false);
			}

			if (!custom)
			{
				wavesurfer.regions.add({
					start:0.000,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				
				if (!wavesurfer.isPlaying ())
				setTimeout(function () {
					app.fireEvent ('RequestSeekTo', 0.00);
				},0);	
			}
			else
			{
				wavesurfer.regions.add({
					start:custom[0],
					end:custom[1],
					id:'t'
				});
				if (!wavesurfer.isPlaying ())
				setTimeout(function () {
					app.fireEvent ('RequestSeekTo', custom[0]/wavesurfer.getDuration ());
				},0);	
			}
		});
		app.listenFor ('RequestDeselect', function() {
			wavesurfer.regions.clear ();
			app.fireEvent ('RequestSeekTo', 0);
		});
		
		(function() {
			var input = null;
			app.listenFor ('RequestLoadLocalFile', function () {
					wavesurfer.pause();
					
					if (input)
					{
						input.parentNode.removeChild( input );
						input.onchange = null;
					}

					input = d.createElement( 'input' );
					input.setAttribute ('type', 'file');
					input.setAttribute ('accept', 'audio/*');
					input.className = 'pk_inpfile';
					input.onchange = function () {
						q.LoadFile ( input );

						input.parentNode.removeChild( input );
						input.onchange = null;
						input = null;
					};
					app.el.appendChild ( input ); // maybe not append?

					input.click ();
			});
		})();
		
		wavesurfer.container.addEventListener('dblclick', function(e){
			app.fireEvent ('RequestSelect', false, 
				[ wavesurfer.LeftProgress,
				wavesurfer.LeftProgress + wavesurfer.VisibleDuration ]
			);
		}, false);
		wavesurfer.container.addEventListener ('click', function( e ) {
			if (!q.is_ready) return ;
			
			if (!app.ui.KeyHandler.keyMap[16])
				wavesurfer.regions.clear();
		}, false);
		
		var dbncr = null;
		w.addEventListener('resize', function() {
			if (!wavesurfer) return ;

			if (dbncr) {
				clearTimeout (dbncr);
			}

			dbncr = setTimeout(function(){

				//requestAnimationFrame(function (){
					app.fireEvent ('RequestResize');

					if (app.isMobile) {
						window.scrollTo (0, 200);
					}
				//});
			},84);
		}, false);

//		w.addEventListener ('orientationchange', function () {
//  			app.fireEvent ('RequestResize');
//		});

		w.addEventListener('beforeunload', function (e) {
		  app.fireEvent ('WillUnload');

		  // e.preventDefault();
		  // e.returnValue = '';
		});

		wavesurfer.on('error', function (error_msg) {

			// if loading - cancel loading
			setTimeout(function() {
				app.fireEvent ('DidDownloadFile'); // just hides the interface
				q.is_ready = false;
			}, 20);

			app.fireEvent ('ShowError', error_msg);
		});

		wavesurfer.on('audioprocess', function ( time, stamp ) {
			// var time = wavesurfer.getCurrentTime();
			var loudness = wavesurfer.getLoudness();
		
			app.fireEvent ('DidAudioProcess', [time, loudness, stamp], wavesurfer.backend.FreqArr);
		});
		wavesurfer.on('DidZoom', function ( e ) {
			app.fireEvent ('DidZoom', [wavesurfer.ZoomFactor, (wavesurfer.LeftProgress/wavesurfer.getDuration()) * 100, wavesurfer.params.verticalZoom], e);
		});
		 wavesurfer.on('region-removed', function (){
			app.fireEvent('DidSetLoop', 0);
			app.fireEvent('DidDestroyRegion');
		 });
		 app.listenFor ('RequestRegionClear', function () {
			wavesurfer.regions.clear();
		 });
		app.listenFor ('RequestRegionSet', function ( start, end ) {
			if (!q.is_ready) return ;

			if (!start) {
				start =  wavesurfer.LeftProgress / 1;
			}
			if (!end) {
				end = (wavesurfer.LeftProgress + wavesurfer.VisibleDuration) / 1;
			}

			// add a region where the paste happened
			wavesurfer.regions.clear();
			wavesurfer.regions.add({
				start: start,
				end:   end,
				id:'t'
			});
		});

		var copy_buffer = null;

		this.GetCopyBuff = function () {
			return (copy_buffer);
		};

		this.GetSel = function () {
			var region = wavesurfer.regions.list[0];
			if (!region) return (false);

			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);
			
			var copybuffer = AudioUtils.Copy (
				start,
				end
			);

			return (copybuffer);
		};

		this.PlayBuff = function ( buff_arr, chans, sample_rate, aud_cont ) {
			var audio_ctx;

			if (aud_cont) audio_ctx = aud_cont;
			else audio_ctx = new (w.AudioContext || w.webkitAudioContext)();

			if (!audio_ctx) return ;

			var bytes = buff_arr[0].byteLength / 4;

			var buffer = audio_ctx.createBuffer (
				chans,
				bytes,
				sample_rate
			);

			for (var i = 0; i < chans; ++i) {
				buffer.getChannelData ( i ).set (
					new Float32Array (buff_arr[ i ])
				);
			}

			var source = audio_ctx.createBufferSource ();
			source.buffer = buffer;

			source.connect ( audio_ctx.destination );
			source.start ( 0 );

			return (source);
		};

		this.GetFX = function ( fx, val ) {
			return AudioUtils.FXBank[fx]( val );
		};

		this.GetWave = function ( buffer, ww, hh, offset, llen, cnv, cx ) {
			var chan_data = buffer.getChannelData ( 0 );
			var sample_rate = buffer.sampleRate;

			var peaks = [];
			var curr_time = 0;
			var width = ww || 200;
			var height = hh || 80;
			var half_height = (height / 2);
			var new_width = width;
			var pixels = 0;
			var raw_pixels = 0;

			var start_offset = offset || 0;
			var end_offset   = llen || ((buffer.duration * sample_rate) >> 0);
			var length       = end_offset - start_offset;
			var mod          = (length / width) >> 0;

			var max   = 0;
			var min   = 0;

			for (var i = 0; i < new_width; ++i) {
				var new_offset = start_offset + (mod * i);

				max = 0;
				min = 0;

				if (new_offset >= 0)
				{
					for (var j = 0; j < mod; j += 3) {
						if ( chan_data[ new_offset + j] > max ) {
							max = chan_data[ new_offset + j];
						}
						else if ( chan_data[ new_offset + j] < min ) {
							min = chan_data[ new_offset + j];
						}
					}
				}

				peaks[2 * i] = max;
				peaks[2 * i + 1 ] = min;
			}

			var canvas = cnv;
			var ctx = cx;

			if (!canvas) {
				canvas = document.createElement('canvas');
				canvas.width = width;
				canvas.height = height;

				ctx = canvas.getContext ('2d', {alpha:false,antialias:false});
			}

			ctx.fillStyle = "#000";
			ctx.fillRect ( 0, 0, width, height );
			ctx.fillStyle = '#99c2c6';
			
			ctx.beginPath ();
	        ctx.moveTo ( 0, half_height );

			for (var i = 0; i < width; ++i) {
				var peak = peaks[i * 2];
				var _h = Math.round (peak * half_height);
				ctx.lineTo ( i, half_height - _h);
			}

			for (var i = width - 1; i >= 0; --i) {
				var peak = peaks[ (i * 2) + 1];
				var _h = Math.round (peak * half_height);
				ctx.lineTo ( i, half_height - _h);
			}

			ctx.closePath();
			ctx.fill();

			return (canvas.toDataURL('image/jpeg', 0.56));
			// ---
		};

		app.listenFor ('RequestActionCut', function ( use_clipboard ) {
			if (!q.is_ready) return ;
			
			var region = wavesurfer.regions.list[0];
			if (!region) return (false);

			app.fireEvent ('RequestPause');

			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ( (region.end - region.start), 3)

			app.fireEvent ('StateRequestPush', {
				desc : use_clipboard ? 'Cut' : 'Delete',
				meta : [ start, end ],
				data : wavesurfer.backend.buffer
			});

			var cutbuffer = AudioUtils.Trim (
				start,
				end
			);
			wavesurfer.regions.clear();

			var tmp = (start - 0.03);
			if (tmp < 0) tmp = 0;

			app.fireEvent ('RequestSeekTo', tmp / wavesurfer.getDuration ());
			
			if (use_clipboard) {
				copy_buffer = cutbuffer;

				app.fireEvent ('DidSetClipboard', 1);
				app.fireEvent ('DidCut', cutbuffer);

				OneUp ('Cut :: ' + q.TrimTo (start, 2) + ' to ' + q.TrimTo (start/1 + end/1, 2), 1100);
			}
			else {
				OneUp ('Delete :: ' + q.TrimTo (start, 2) + ' to ' + q.TrimTo (start/1 + end/1, 2), 1100);
			}

			/*
			var image = app.engine.GetWave (copy_buffer);
			var eel = document.getElementsByClassName('pk_tb')[0];
			var imm = new Image();
			imm.src = image;
			eel.appendChild( imm );
			*/
		});
		
		app.listenFor ('RequestActionCopy', function () {
			if (!q.is_ready) return ;
			
			var region = wavesurfer.regions.list[0];
			if (!region) return (false);

			app.fireEvent('RequestPause');

			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);
			
			var copybuffer = AudioUtils.Copy (
				start,
				end
			);

			copy_buffer = copybuffer;
			app.fireEvent ('DidSetClipboard', 1);
			app.fireEvent ('DidCopy', copybuffer);

			OneUp ('Copied range');
		});
		
		app.listenFor ('RequestActionSilence', function ( offset, silence_duration ) {
			if (!q.is_ready) return ;

			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];
			
			if (!silence_duration || silence_duration < 0) silence_duration = 1;

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Silence',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}

			var start = offset;
			var end = silence_duration;

			handleStateInline ( start, end );
			dims = AudioUtils.Insert (
				offset, 
				AudioUtils.MakeSilence ( silence_duration )
			);

			// add a region where the paste happened
			wavesurfer.regions.clear();
			wavesurfer.regions.add({
				start:dims[0],
				end:dims[1],
				id:'t'
			});

			app.fireEvent ('RequestSeekTo', (dims[0]/wavesurfer.getDuration()));
			
			OneUp ('Inserted Silence');
		});

		app.listenFor ('RequestActionPaste', function () {
			if (!q.is_ready) return ;
			if (!copy_buffer) return (false);

			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Paste',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				var offset = q.TrimTo (wavesurfer.getCurrentTime(), 3);
				
				handleStateInline ( offset );
				dims = AudioUtils.Insert ( offset, copy_buffer );
			}
			else {
				var start = q.TrimTo (region.start, 3);
				var end = q.TrimTo ((region.end - region.start), 3);

				handleStateInline ( start, end );

				dims = AudioUtils.Replace (
					start,
					end,
					copy_buffer
				);
			}

			// add a region where the paste happened
			wavesurfer.regions.clear();
			wavesurfer.regions.add({
				start:dims[0],
				end:dims[1],
				id:'t'
			});

			var new_seek = 0;
			if (wavesurfer.getDuration () > 0.0001) {
				new_seek = dims[0]/wavesurfer.getDuration ();
			}
			app.fireEvent ('RequestSeekTo', new_seek);

			OneUp ('Paste to ' + dims[0].toFixed(2), 982);
		});

		var _sk = false;
		app.listenFor ('RequestActionRecordToggle', function () {
			if (!q.is_ready) {
				// if not ready then bring up the new recording toggle!
				app.fireEvent('RequestActionNewRec');

				return ;
			}
			
			if (app.rec.isActive ()) {
				app.fireEvent('RequestActionRecordStop');
			} else {
				// skipping the sounds of keyboard
				if (_sk) return ;

				_sk = true;
				setTimeout(function () {
					app.fireEvent('RequestActionRecordStart');
					setTimeout(function() {
						_sk = false;
					}, 50);
				},26);
			}
		});

		app.listenFor ('RequestActionRecordStop', function () {
			if (!q.is_ready) return ;
			if (!app.rec.isActive ()) return (false);

			app.rec.stop ();
		});

		app.listenFor ('RequestActionRecordStart', function () {
			if (!q.is_ready) return ;

			app.fireEvent ('RequestPause');

			if (app.rec.isActive ()) return (false);

			var pos = wavesurfer.getCurrentTime () * wavesurfer.backend.buffer.sampleRate;
			app.rec.start ( pos, function ( offset, buffers ) {

				// app.fireEvent ('RequestPause');
				function handleStateInline ( start, end ) {
					app.fireEvent ('StateRequestPush', {
						desc : 'Record Audio',
						meta : [ start, end ],
						data : wavesurfer.backend.buffer
					});
				}

				// fire did record event!
				app.fireEvent ('DidActionRecordStop', !!buffers);
				if (!buffers)
				{
					return ;
				}

				handleStateInline ( offset );
				var dims = AudioUtils.ReplaceFloatArrays ( offset, buffers );

				// add a region where the paste happened
				wavesurfer.regions.clear();
				wavesurfer.regions.add({
					start:dims[0],
					end:dims[1],
					id:'t'
				});

				app.fireEvent ('RequestSeekTo', (dims[0]/wavesurfer.getDuration()));
				OneUp ('Recorded Audio ' + dims[0].toFixed(2), 982);
			}, function () {
				// on start
				app.fireEvent ('DidActionRecordStart');
			});

			// --- ending offset is song full duration...
			// if we have a selected area - mark that one as the end
			var region = wavesurfer.regions.list[0];
			if (region)
				app.rec.setEndingOffset ( region.end * wavesurfer.backend.buffer.sampleRate );
			else
				app.rec.setEndingOffset ( wavesurfer.getDuration () * wavesurfer.backend.buffer.sampleRate );
		});
		
		app.listenFor ('RequestActionFX_PREVIEW_HardLimit', function ( val ) {
			if (!q.is_ready) return ;
			if (AudioUtils.previewing) {
				AudioUtils.FXPreviewStop ();
				app.fireEvent ('DidStopPreview');
				return ;
			}

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			AudioUtils.FXPreview( start, end, AudioUtils.FXBank.HardLimit ( val ) );
			app.fireEvent ('DidStartPreview');
		});
		app.listenFor ('RequestActionFX_HardLimit', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Hard Limit (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.HardLimit ( val ) );
			
			OneUp ('Applied Hard Limit (fx)');
		});
		
		app.listenFor ('RequestActionFX_PARAMEQ', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Parametric EQ (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.ParametricEQ ( val ) );
			
			OneUp ('Applied Parametric EQ (fx)');
		});
		app.listenFor ('RequestActionFX_PREVIEW_PARAMEQ', function ( val ) {
			if (!q.is_ready || !val) return ;
			if (AudioUtils.previewing) {
				AudioUtils.FXPreviewStop ();
				app.fireEvent ('DidStopPreview');
				return ;
			}

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			AudioUtils.FXPreview( start, end, AudioUtils.FXBank.ParametricEQ ( val ) );
			app.fireEvent ('DidStartPreview');
		});

		app.listenFor ('RequestActionFX_PREVIEW_DISTORT', function ( val ) {
			if (!q.is_ready) return ;
			if (AudioUtils.previewing) {
				AudioUtils.FXPreviewStop ();
				app.fireEvent ('DidStopPreview');
				return ;
			}

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			AudioUtils.FXPreview( start, end, AudioUtils.FXBank.Distortion ( val ) );
			app.fireEvent ('DidStartPreview');
		});
		app.listenFor ('RequestActionFX_DISTORT', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Distortion (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.Distortion ( val ) );
			
			OneUp ('Applied Distortion (fx)');
		});

		app.listenFor ('RequestActionFX_PREVIEW_DELAY', function ( val ) {
			if (!q.is_ready) return ;
			if (AudioUtils.previewing) {
				AudioUtils.FXPreviewStop ();
				app.fireEvent ('DidStopPreview');
				return ;
			}

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			AudioUtils.FXPreview( start, end, AudioUtils.FXBank.Delay ( val ) );
			app.fireEvent ('DidStartPreview');
		});
		app.listenFor ('RequestActionFX_DELAY', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Delay (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.Delay ( val ) );
			
			OneUp ('Applied Delay (fx)');
		});

		app.listenFor ('RequestActionFX_PREVIEW_REVERB', function ( val ) {
			if (!q.is_ready) return ;
			if (AudioUtils.previewing) {
				AudioUtils.FXPreviewStop ();
				app.fireEvent ('DidStopPreview');
				return ;
			}

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			AudioUtils.FXPreview( start, end, AudioUtils.FXBank.Reverb ( val ) );
			app.fireEvent ('DidStartPreview');
		});
		app.listenFor ('RequestActionFX_REVERB', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Reverb (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.Reverb ( val ) );
			
			OneUp ('Applied Reverb (fx)');
		});

		app.listenFor ('RequestActionFX_PREVIEW_COMPRESSOR', function ( val ) {
			if (!q.is_ready) return ;
			if (AudioUtils.previewing) {
				AudioUtils.FXPreviewStop ();
				app.fireEvent ('DidStopPreview');
				return ;
			}

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			AudioUtils.FXPreview( start, end, AudioUtils.FXBank.Compressor ( val ) );
			app.fireEvent ('DidStartPreview');
		});

		app.listenFor ('RequestActionFX_Compressor', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Compressor (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.Compressor ( val ) );
			
			OneUp ('Applied Compressor (fx)');
		});
		app.listenFor ('RequestActionFX_Normalize', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Normalize ',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3)
			var end = q.TrimTo ((region.end - region.start), 3)

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.Normalize ( val ) );
			
			OneUp ('Applied Normalize');
		});

		app.listenFor ('RequestActionFX_Invert', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Invert ',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3)
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.Invert() );
			
			OneUp ('Applied Invert');
		});

		app.listenFor ('RequestActionFX_RemSil', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Remove Silence ',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3)
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );

						var originalBuffer = wavesurfer.backend.buffer;
						var sil_arr = [];
						var sil_offset = 210;
						var vol_offset = 56;
						var count = 0;
						var inv_count = 0;
						var start = 0;
						var end   = 0;
						var found = false;
						var jump = 500;

						for (var i = 0; i < 1; ++i)
						{
							var channel = originalBuffer.getChannelData (i);

							for (var j = 0; j < channel.length; ++j)
							{
								if (Math.abs (channel[j]) < 0.000368)
								{
									if (count === 0) {

										if (j > jump)
											start = j - jump;
										else
											start = j;
									}
									if (++count > sil_offset)
									{
										inv_count = 0;
										end = j;
										found = true;
									}
								}
								else
								{
									if (found)
									{
										if (++inv_count > vol_offset)
										{
											sil_arr.push([start, end]);
											j += jump;

											count = 0;
											start = 0;
											end =   0;
											found = false;
											inv_count = 0;
										}
										else
										{
											end = j;
										}
									}
									else
									{
										count = 0;
										start = 0;
										end =   0;
										found = false;
										inv_count = 0;
									}
								}
							}

							if (found) {
								sil_arr.push([start, end]);
							}
						}

						if (sil_arr.length > 0)
						{
								var reduce = 0;
								for (var i = 0; i < sil_arr.length; ++i)
								{
									reduce += (sil_arr[i][1] - sil_arr[i][0]);
								}

								var emptySegment   = wavesurfer.backend.ac.createBuffer (
									originalBuffer.numberOfChannels,
									originalBuffer.length - reduce,
									originalBuffer.sampleRate
								);

								for (var i = 0; i < originalBuffer.numberOfChannels; ++i)
								{
									var channel = originalBuffer.getChannelData ( i );
									var new_channel = emptySegment.getChannelData ( i );

									var sil_offset = 0;
									var o = 0;
									var sil_curr = sil_arr[o];
									var sil_curr_start = sil_curr[0];
									var sil_curr_end   = sil_curr[1];
									var h = 0;
									var use_old = false;
									var old_h = 0;

									for (var j = 0; j < new_channel.length; ++j)
									{
										h = j + sil_offset;
										if (h > sil_curr_start && h < sil_curr_end)
										{
											if (h < sil_curr_start + jump)
											{
												var perc = (jump - (h - sil_curr_start)) / jump;
												new_channel[ j ] = (channel[ h ] * perc) ; // / (h - sil_curr_start));
												new_channel[ j ] += (1 - perc) * channel[ j + (sil_offset + (sil_curr_end - sil_curr_start)) ];

												continue;
											}
											else
											{
												sil_offset = sil_offset + (sil_curr_end - sil_curr_start);
												sil_curr = sil_arr[++o];
												if (sil_curr)
												{
													sil_curr_start = sil_curr[0];
													sil_curr_end   = sil_curr[1];
												}
												h = j + sil_offset;
											}
										}

										new_channel[ j ] = channel[ h ];
									}
								}

								AudioUtils.FullReplace (
									emptySegment
								);
						}

			setTimeout (function() {
				wavesurfer.drawBuffer();
			},40);

			OneUp ('Applied :: Remove Silence');
		});


		var _compute_channels = function () {
			var buff = wavesurfer.backend.buffer;
			var chans = buff.numberOfChannels;

			if (chans === 1) {
				wavesurfer.ActiveChannels = [1];
				app.el.classList.add ('pk_mono');
			}
			else {
				wavesurfer.ActiveChannels = [1, 1];
				app.el.classList.remove ('pk_mono');
			}

			wavesurfer.drawer.params.ActiveChannels = wavesurfer.ActiveChannels;
			wavesurfer.SelectedChannelsLen = chans;
		};

		app.listenFor ('RequestActionFX_Flip', function ( val, val2 ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var start = 0;
			var end   = wavesurfer.getDuration();

			function handleStateInline ( start, end, title, cb ) {
				app.fireEvent ('StateRequestPush', {
					desc : title,
					meta : [ start, end ],
					data : wavesurfer.backend.buffer,
					cb   : cb
				});
			}

			if (val === 'flip')
			{
				handleStateInline ( start, end, 'Flip Channels' );
				AudioUtils.FX ( start, end, AudioUtils.FXBank.Flip ( val ) );
			}
			else if (val === 'stereo')
			{
				handleStateInline ( start, end, 'Make Stereo', function(){_compute_channels ()});

				var originalBuffer = wavesurfer.backend.buffer;
				var emptySegment   = wavesurfer.backend.ac.createBuffer (
					2,
					originalBuffer.length,
					originalBuffer.sampleRate
				);
				emptySegment.getChannelData ( 0 ).set (
					originalBuffer.getChannelData ( 0 )
				);
				emptySegment.getChannelData ( 1 ).set (
					originalBuffer.getChannelData ( 0 )
				);

				AudioUtils.FullReplace (
					emptySegment
				);

				wavesurfer.regions.clear();
				wavesurfer.regions.add({
					start:start,
					end:end,
					id:'t'
				});

				_compute_channels ();

				app.fireEvent ('RequestSeekTo', 0.00);
			}
			else if (val === 'mono')
			{
				handleStateInline ( start, end, 'Make Mono', function(){_compute_channels()} );

				var originalBuffer = wavesurfer.backend.buffer;
				var emptySegment   = wavesurfer.backend.ac.createBuffer (
					1,
					originalBuffer.length,
					originalBuffer.sampleRate
				);
				emptySegment.getChannelData ( 0 ).set (
					originalBuffer.getChannelData ( val2 )
				);
				AudioUtils.FullReplace (
					emptySegment
				);

				wavesurfer.regions.clear();
				wavesurfer.regions.add({
					start:start,
					end:end,
					id:'t'
				});

				_compute_channels ();

				app.fireEvent ('RequestSeekTo', 0.00);
			}

			OneUp ('Applied Channel Change: ' + val);
		});

		app.listenFor ('RequestActionFX_Reverse', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Reverse ',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3)
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.Reverse() );
			
			OneUp ('Applied Reverse');
		});

		var noisernn_load = !1;
        app.listenFor("RequestActionFX_NoiseRNN", function (a) {
        	var h = wavesurfer;
            if (q.is_ready) { // n -> q
                app.fireEvent("RequestPause");
                var b = function () {
                    var f = h.regions.list[0];
                    f || (h.regions.add({ start: 0, end: h.getDuration() - 0, id: "t" }), (f = h.regions.list[0]));
                    var m = q.TrimTo(f.start, 3),
                        k = q.TrimTo(f.end - f.start, 3);
                    f = f.end - f.start;
                    f = q.TrimTo(f, 3);
                    app.fireEvent("StateRequestPush", { desc: "Apply Noise RNN (fx)", meta: [m, k], data: h.backend.buffer });
                    for (var u = AudioUtils.Copy(m, k), w = 0; w < u.numberOfChannels; w++) {
                        var x = u.getChannelData(w),
                            z = wasm_denoise_stream_perf(x);
                        x.set(z);
                    }
                    AudioUtils.Replace(m, k, u);
                    app.fireEvent("RequestSeekTo", m / h.getDuration());
                    wavesurfer.regions.clear();
                    h.regions.add({ start: m, end: m + f, id: "t" });
                    OneUp("Applied Noise RNN (fx)");
                };
                noisernn_load
                    ? b()
                    : ((a = document.createElement("script")),
                      (a.src = "rnn_denoise.js"),
                      (a.onload = function () {
                          noisernn_load = !0;
                          var f = function () {
                              window.Module && window.Module.asm && window.Module.asm.malloc
                                  ? b()
                                  : setTimeout(function () {
                                        f();
                                    }, 350);
                          };
                          setTimeout(function () {
                              f();
                          }, 100);
                      }),
                      (a.onerror = function () {
                          alert("Could not download noise Reduction script");
                      }),
                      document.head.appendChild(a));
            }
        });

		app.listenFor ('RequestActionFX_FadeIn', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Fade In (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.FadeIn() );
			
			OneUp ('Applied Fade In (fx)');
		});
		app.listenFor ('RequestActionFX_FadeOut', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Fade Out (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}
			
			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.FadeOut() );
			
			OneUp ('Applied Fade Out (fx)');
		});
		
		
		var fx_preview_debounce = null;
		app.listenFor ('RequestActionFX_UPDATE_PREVIEW', function ( val ) {
			if (!AudioUtils.previewing) return ;
			
			clearTimeout (fx_preview_debounce);
			fx_preview_debounce = setTimeout(function () {
				AudioUtils.FXPreviewUpdate ( val );
			}, 44);
		});
		app.listenFor ('RequestActionFX_TOGGLE', function ( val ) {

			if (val)
			{
				AudioUtils.FXPreviewInit (true);
				return ;
			}

			app.fireEvent ('DidTogglePreview', AudioUtils.FXPreviewToggle ());
		});
		app.listenFor ('RequestActionFX_PREVIEW_STOP', function () {
			AudioUtils.FXPreviewStop ();
			app.fireEvent ('DidStopPreview');
		});
		app.listenFor ('RequestActionFX_PREVIEW_GAIN', function ( val ) {
			if (!q.is_ready) return ;
			if (AudioUtils.previewing) {
				AudioUtils.FXPreviewStop ();
				app.fireEvent ('DidStopPreview');
				return ;
			}

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}

			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			AudioUtils.FXPreview( start, end, AudioUtils.FXBank.Gain( val ) );
			
			app.fireEvent ('DidStartPreview');
		});

		app.listenFor ('RequestActionFX_GAIN', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Gain (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			handleStateInline ( start, end );
			AudioUtils.FX( start, end, AudioUtils.FXBank.Gain( val ) );

			OneUp ('Applied Gain (fx)');
		});

		app.listenFor ('RequestActionFX_PREVIEW_SPEED', function ( val ) {
			if (!q.is_ready) return ;
			if (AudioUtils.previewing) {
				AudioUtils.FXPreviewStop ();
				app.fireEvent ('DidStopPreview');
				return ;
			}

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}

			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);

			AudioUtils.FXPreview( start, end, AudioUtils.FXBank.Speed( val ) );
			
			app.fireEvent ('DidStartPreview');
		});

		app.listenFor ('RequestActionFX_RATE', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Rate (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}
			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);
			var duration = (region.end - region.start) / val;
			duration = q.TrimTo (duration, 3);

			handleStateInline ( start, end );

			var fx_buffer = AudioUtils.Copy ( start, end );
			var originalBuffer = wavesurfer.backend.buffer;
			var new_offset = ((start/1)   * originalBuffer.sampleRate) >> 0;
			var new_len    = ((duration/1) * originalBuffer.sampleRate) >> 0;
			var old_len    = ((end/1) * originalBuffer.sampleRate) >> 0;
			var stretch_ratio = new_len / old_len;

			var getOfflineAudioContext = function (channels, sampleRate, duration) {
					return new (window.OfflineAudioContext ||
					window.webkitOfflineAudioContext)(channels, duration, sampleRate);
			};
			var audio_ctx = getOfflineAudioContext ( // offlineCtx
					wavesurfer.SelectedChannelsLen, // orig_buffer.numberOfChannels,
					fx_buffer.sampleRate,
					new_len
			);

			/*
			var TimeStretcher = function(o){o=o||{};this.ws=o.windowSize||1024;this.or=o.overlapRatio||0.5;this.sw=o.seekWindowMs||30;}
			TimeStretcher.prototype.stretch=function(buf,ts){
			  if(ts<=0) throw new Error("Stretch ratio must be positive");
			  var ch=buf.numberOfChannels, sr=buf.sampleRate, il=buf.length, ol=Math.floor(il*ts),
			      out=new AudioBuffer({numberOfChannels:ch,length:ol,sampleRate:sr});
			  for(var c=0;c<ch;c++){
			    var inD=buf.getChannelData(c), outD=out.getChannelData(c), ws=this.ws,
			        ov=Math.floor(ws*this.or), hs=ws-ov, sw=Math.floor(this.sw*sr/1000),
			        win=this._hannWindow(ws), iIdx=0, oIdx=0;
			    while(oIdx<ol-ws){
			      var nIdx=Math.min(Math.floor(oIdx/ts),il-ws-sw);
			      if(iIdx>0 && Math.abs(nIdx-iIdx)>hs){
			        var ssi=Math.max(0,nIdx-sw), sei=Math.min(il-ws,nIdx+sw);
			        iIdx=this._findBestMatch(inD,iIdx+hs,ssi,sei,ws);
			      } else { iIdx=nIdx; }
			      for(var i=0;i<ws;i++){
			        if(iIdx+i<il && oIdx+i<ol)
			          outD[oIdx+i]+= inD[iIdx+i]*win[i];
			      }
			      oIdx+=hs;
			    }
			    this._normalizeOutput(outD);
			  }
			  return out;
			};
			TimeStretcher.prototype._hannWindow=function(l){var w=new Float32Array(l);for(var i=0;i<l;i++)w[i]=0.5*(1-Math.cos(2*Math.PI*i/(l-1)));return w;};
			TimeStretcher.prototype._findBestMatch=function(d,ref,s,e,ws){
			  var bp=s,be=Number.MAX_VALUE, refArr=new Float32Array(ws);
			  for(var i=0;i<ws;i++) if(ref+i<d.length) refArr[i]=d[ref+i];
			  for(var pos=s;pos<=e;pos++){
			    var err=0;
			    for(var i=0;i<ws;i+=4) {
			      if(pos+i<d.length){var diff=refArr[i]-d[pos+i];err+=diff*diff;}
			    }
			    if(err<be){be=err;bp=pos;}
			  }
			  return bp;
			};
			TimeStretcher.prototype._normalizeOutput=function(d){
			  var m=0; for(var i=0;i<d.length;i++) m=Math.max(m,Math.abs(d[i]));
			  if(m>1){var g=0.95/m; for(var i=0;i<d.length;i++) d[i]*=g;}
			};*/
			var stretchAudio = function (input_buffer, sampleRate, stretchRatio) {
			  // Parameters (in seconds)
			  let channels_len = input_buffer.numberOfChannels;
			  let samples = [ input_buffer.getChannelData(0) ];
			  for (let i = 1; i < channels_len; ++i) {
			  	samples.push ( input_buffer.getChannelData(i) );
			  }

			  let grainDurationSec = 0.05; // 50 ms
			  const analysisHopSec = 0.025; // 25 ms (50% overlap)
			  const desiredOverlap = 0.5;
			  const synthesisHopSec = analysisHopSec * stretchRatio; // output hop

			  // Adjust grain duration for ratios > 1
			  if (stretchRatio > 1) {
			    grainDurationSec = synthesisHopSec / (1 - desiredOverlap);
			  }

			  // Convert durations to samples
			  const grainSize = Math.floor(grainDurationSec * sampleRate);
			  const analysisHop = Math.floor(analysisHopSec * sampleRate);
			  const synthesisHop = Math.floor(synthesisHopSec * sampleRate);

			  // Precompute Hann window
			  const window = new Float32Array(grainSize);
			  for (let i = 0; i < grainSize; i++) {
			    // Using (grainSize - 1) so that the window spans [0, grainDurationSec]
			    window[i] = 0.5 * (1 - Math.cos((2 * Math.PI * i) / (grainSize - 1)));
			  }

			  // Estimate number of grains and output length
			  const numGrains = Math.floor((samples[0].length - grainSize) / analysisHop);
			  const outputLength = synthesisHop * numGrains + grainSize;
			  let output = [ new Float32Array(outputLength) ];
			  for (let i = 1; i < channels_len; ++i) {
			  	output.push ( new Float32Array(outputLength) );
			  }

			  // Process each grain: copy, window, and add to output
			  let inputIndex = 0;
			  let outputIndex = 0;
			  for (let n = 0; n < numGrains; ++n) {
			    // For each sample in the grain, multiply by the window and add to the output
			    for (let i = 0; i < grainSize; ++i) {
			    	for (let j = 0; j < channels_len; ++j) {
			      		output[j][outputIndex + i] += samples[j][inputIndex + i] * window[i];
			      	}
			    }
			    inputIndex += analysisHop;
			    outputIndex += synthesisHop;
			  }

			  	/*
				let normalization = new Float32Array(outputLength);
				inputIndex = 0;
				outputIndex = 0;
				for (let n = 0; n < numGrains; ++n) {
				  for (let i = 0; i < grainSize; ++i) {
				    normalization[outputIndex + i] += window[i];
				  }
				  inputIndex += analysisHop;
				  outputIndex += synthesisHop;
				}

				// Normalize each channel's output:
				for (let j = 0; j < channels_len; ++j) {
				  for (let i = 0; i < outputLength; ++i) {
				    if (normalization[i] > 0) {
				      output[j][i] /= normalization[i];
				    }
				  }
				}
				*/
			  
			  return output;
			};

			/// -----
			var filter = [];
			//if (stretch_ratio < 1) {
			//	var fx = AudioUtils.FXBank.Rate( stretch_ratio );
			//	var source = {buffer:null, disconnect:function(){}};
			//	source.buffer = fx_buffer;
			//	filter = fx.filter ( audio_ctx, audio_ctx.destination, source, duration );
			//}
			//else
			//{
				// use timestretcher here...
				// var ts = new TimeStretcher({windowSize:2048,overlapRatio:0.75,seekWindowMs:20}).stretch(fx_buffer,stretch_ratio);
				const stretchedSamples = stretchAudio(fx_buffer, fx_buffer.sampleRate, stretch_ratio);

				// Optionally, if you need an AudioBuffer from the stretchedSamples:
				// const offlineCtx = new OfflineAudioContext(stretchedSamples.length, stretchedSamples[0].length, fx_buffer.sampleRate);
				const newBuffer = audio_ctx.createBuffer(stretchedSamples.length, stretchedSamples[0].length, fx_buffer.sampleRate);
				
				for (let i = 0; i < stretchedSamples.length; ++i) {
					newBuffer.copyToChannel(stretchedSamples[i], i);
				}

				var source = audio_ctx.createBufferSource ();
				source.buffer = newBuffer;
				source.connect ( audio_ctx.destination );
				source.start ();
			//}

			q.in_fx = true;
			app.ui.InteractionHandler.on = true;
			// OneUp ('Please wait, applying FX', 2600);

			var offline_callback = function( rendered_buffer ) {
				AudioUtils.Replace (start, end, rendered_buffer);

				wavesurfer.regions.clear();
				wavesurfer.regions.add({
					start:start,
					end: start + duration,
					id:'t'
				});

				app.fireEvent ('RequestSeekTo', (start/wavesurfer.getDuration()));

				OneUp ('Applied Rate (fx)');

				if (filter.length > 0) {
					for (var i = 0; i < filter.length; ++i) filter[i].disconnect ();
				} else filter && filter.disconnect && filter.disconnect ();

				rendered_buffer = fx_buffer = filter = null;
				source.disconnect ();

				q.in_fx = false;
				app.ui.InteractionHandler.on = false;
			};

			var offline_renderer = audio_ctx.startRendering(); 
			if (offline_renderer)
				offline_renderer.then( offline_callback ).catch(function(err) {
					console.log('Rendering failed: ' + err);
				});
			else
				audio_ctx.oncomplete = function ( e ) {
					offline_callback ( e.renderedBuffer );
				};
		});

		app.listenFor ('RequestActionFX_PREVIEW_RATE', function ( val ) {
			if (!q.is_ready) return ;
			if (AudioUtils.previewing) {
				AudioUtils.FXPreviewStop ();
				app.fireEvent ('DidStopPreview');
				return ;
			}

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}

			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);
			var duration = (region.end - region.start) / val;

			var originalBuffer = wavesurfer.backend.buffer;
			var new_offset = ((start/1)   * originalBuffer.sampleRate) >> 0;
			var new_len    = ((duration/1) * originalBuffer.sampleRate) >> 0;
			var old_len    = ((end/1) * originalBuffer.sampleRate) >> 0;

			// -----
			var stretch_ratio = new_len / old_len;

			AudioUtils.FXPreview( start, end, AudioUtils.FXBank.Rate( stretch_ratio ) );

			app.fireEvent ('DidStartPreview');
		});

		app.listenFor ('RequestActionFX_SPEED', function ( val ) {
			if (!q.is_ready) return ;
			
			app.fireEvent('RequestPause');

			var region = wavesurfer.regions.list[0];
			var dims = [ 0, 0 ];

			function handleStateInline ( start, end ) {
				app.fireEvent ('StateRequestPush', {
					desc : 'Apply Speed (fx)',
					meta : [ start, end ],
					data : wavesurfer.backend.buffer
				});
			}

			if (!region) {
				wavesurfer.regions.add({
					start:0.00,
					end:wavesurfer.getDuration() - 0.00,
					id:'t'
				});
				region = wavesurfer.regions.list[0];
			}

			
			var start = q.TrimTo (region.start, 3);
			var end = q.TrimTo ((region.end - region.start), 3);
			var duration = (region.end - region.start) / val;
			duration = q.TrimTo (duration, 3);

			handleStateInline ( start, end );

			var fx_buffer = AudioUtils.Copy ( start, end );
			var originalBuffer = wavesurfer.backend.buffer;
			var new_offset = ((start/1)   * originalBuffer.sampleRate) >> 0;
			var new_len    = ((duration/1) * originalBuffer.sampleRate) >> 0;
			var old_len    = ((end/1) * originalBuffer.sampleRate) >> 0;

			/*
			var emptySegment = wavesurfer.backend.ac.createBuffer (
				wavesurfer.SelectedChannelsLen,
				new_len,
				originalBuffer.sampleRate
			);*/

			q.in_fx = true;
			app.ui.InteractionHandler.on = true;
			var fx = AudioUtils.FXBank.Speed( val );

			var getOfflineAudioContext = function (channels, sampleRate, duration) {
					return new (window.OfflineAudioContext ||
					window.webkitOfflineAudioContext)(channels, duration, sampleRate);
			};
			var audio_ctx = getOfflineAudioContext (
					wavesurfer.SelectedChannelsLen, // orig_buffer.numberOfChannels,
					originalBuffer.sampleRate,
					new_len
			);

			var source = audio_ctx.createBufferSource ();
			source.buffer = fx_buffer;

			var filter = fx.filter ( audio_ctx, audio_ctx.destination, source, duration );

			source.start ();

			var offline_callback = function( rendered_buffer ) {

				AudioUtils.Replace (start, end, rendered_buffer);

				wavesurfer.regions.clear();
				wavesurfer.regions.add({
					start:start,
					end: start + duration,
					id:'t'
				});

				app.fireEvent ('RequestSeekTo', (start/wavesurfer.getDuration()));

				OneUp ('Applied Speed (fx)');

				if (filter.length > 0) {
					for (var i = 0; i < filter.length; ++i) filter[i].disconnect ();
				} else filter && filter.disconnect && filter.disconnect ();

				// is this needed?
				rendered_buffer = fx_buffer = filter = null;
				source.disconnect ();
				// audio_ctx.close ();
				// -
				q.in_fx = false;
				app.ui.InteractionHandler.on = false;
			};

			var offline_renderer = audio_ctx.startRendering(); 
			if (offline_renderer)
				offline_renderer.then( offline_callback ).catch(function(err) {
					console.log('Rendering failed: ' + err);
				});
			else
				audio_ctx.oncomplete = function ( e ) {
					offline_callback ( e.renderedBuffer );
				};
		});
		
		app.listenFor ('StateDidPop', function ( state, undo ) {
			if (!q.is_ready) return ;
			app.fireEvent ('RequestPause');

			wavesurfer.regions.clear();
			wavesurfer.loadDecodedBuffer (state.data);

			if (state.cb) state.cb ();

			var new_durr = wavesurfer.getDuration ();
			app.fireEvent ('DidUpdateLen', new_durr);

			if (state.meta && state.meta.length > 0)
			{
				if (state.meta[1])
				{
					wavesurfer.regions.add({
						start:state.meta[0]/1,
						end:state.meta[0]/1 + state.meta[1]/1,
						id:'t'
					});
				}
				else
				{
					if (!new_durr) new_durr = 0.0001;
					app.fireEvent ('RequestSeekTo', (state.meta[0]/new_durr));
				}
			}
			
			if (undo) OneUp ('Undo ' + state.desc);
			else OneUp ('Redo ' + state.desc);
		});


		// --- 
		app.listenFor ('RequestChanToggle', function ( chan_index, force_val ) {
			if (!q.is_ready) return (false);

			if (wavesurfer.ActiveChannels.length <= chan_index) return (false);

			var oldval = wavesurfer.ActiveChannels[ chan_index ];
			var val = -1;

			if (force_val) val = force_val;
			else {
				if (oldval === 1) val = 0;
				else val = 1;
			}

			if (oldval !== val)
			{
				wavesurfer.ActiveChannels[ chan_index ] = val;
				if (val === 0) {
					--wavesurfer.SelectedChannelsLen;
					// silece the channel itself

					if (chan_index === 0) {
						wavesurfer.backend.gainNode2.gain.value = 0.0;
					} else {
						wavesurfer.backend.gainNode1.gain.value = 0.0;
					}
				}
				else {
					++wavesurfer.SelectedChannelsLen;

					if (chan_index === 0) {
						wavesurfer.backend.gainNode2.gain.value = 1.0;
					} else {
						wavesurfer.backend.gainNode1.gain.value = 1.0;
					}
				}

				wavesurfer.ForceDraw ();
				app.fireEvent ('DidChanToggle', chan_index, val);
			}
		});

		// ----
		wavesurfer.on( 'region-updated', function () {
			if (wavesurfer.regions.list[0])
			{
				app.fireEvent ('DidCreateRegion', wavesurfer.regions.list[0]);
			}
		});
		wavesurfer.on( 'region-update-end', function () {
			app.fireEvent ('DidCreateRegion', wavesurfer.regions.list[0]);

			var start = wavesurfer.regions.list[0].start;
			if (!wavesurfer.isPlaying ())
				app.fireEvent ('RequestSeekTo', (start/wavesurfer.getDuration() ));
		});
		wavesurfer.on( 'cursorcenter', function ( e ) {
			app.fireEvent ('DidCursorCenter', e, wavesurfer.ZoomFactor);
		});
		
		var wave = wavesurfer.drawer.canvases[0].wave.parentNode;

		var drag_x = 0;
		var drag_move = function ( e ) {
			
			var diff = drag_x - e.clientX;
			
			// find diff percentage from full width...
			
			// drag the waveform now
			app.fireEvent ('RequestPan', diff );
			
			drag_x = e.clientX;
		};
		
		app.listenFor ('RequestZoom', function ( diff, mode ) {
			var wv = wavesurfer;

			// compute new ZoomFactor...
			diff *= wv.ZoomFactor;

			// compute availabel left ZoomFactor
			if (mode === -1)
			{
				var width = wv.drawer.width;
				var available_pixels = width - width/wv.ZoomFactor;
				var target = wv.ZoomFactor - 1;
				if (target <= 0) return ;

				 var old_zoomfactor = wv.ZoomFactor;
				 wv.ZoomFactor += (diff*target)/available_pixels;
				 if (wv.ZoomFactor < 1) wv.ZoomFactor = 1;
				 
				 var new_vis_dur = wv.getDuration() / wv.ZoomFactor;

				 if (new_vis_dur <= 0.5)
				 {
				 	wv.ZoomFactor = old_zoomfactor;
				 	return ;
				 }

				 wv.VisibleDuration = new_vis_dur;
				 
				var time_moved = wv.VisibleDuration * (diff / wv.drawer.width);
				wv.LeftProgress += time_moved;
				
				if (wv.LeftProgress + wv.VisibleDuration >= wv.getDuration ())
				{
					wv.LeftProgress = wv.getDuration () - wv.VisibleDuration;
				}
				else if (wv.LeftProgress < 0) {
					wv.LeftProgress = 0;
				}
			}
			else if (mode === 1)
			{
				var width = wv.drawer.width;
				var available_pixels = width - width/wv.ZoomFactor;
				var target = wv.ZoomFactor - 1;
				if (target <= 0) return ;

				var old_factor = wv.ZoomFactor; 
				wv.ZoomFactor -= (diff*target)/available_pixels;
				if (wv.ZoomFactor < 1) wv.ZoomFactor = 1;
				var temp = wv.getDuration() / wv.ZoomFactor;
				if (temp + wv.LeftProgress > wv.getDuration()) {
					wv.ZoomFactor = old_factor;
				}
				else
				{
					if (temp <= 0.5)
					{
						wv.ZoomFactor = old_factor;
						return ;
					}

					wv.VisibleDuration = temp;
				}
				// -
			}
			
			// wv.ZoomFactor -= Math.abs (diff / (wv.drawer.width / 2));
			// console.log( diff + " BLAH " + wv.ZoomFactor + '   ' +  (diff / wv.drawer.width) );
			wv.ForceDraw ();
			app.fireEvent ('DidZoom', [wavesurfer.ZoomFactor, (wavesurfer.LeftProgress/wavesurfer.getDuration()) * 100, wavesurfer.params.verticalZoom]);
		});
		
		app.listenFor ('RequestPan', function( diff, mode ) {
			var wv = wavesurfer;
			
			if (mode === 1) diff *= wv.ZoomFactor;
			else if (mode === 2) {
				var time_moved = wv.getDuration() * (diff / wv.drawer.width);
				wv.LeftProgress = time_moved;

				wv.ForceDraw ();
				app.fireEvent ('DidZoom', [wavesurfer.ZoomFactor, (wavesurfer.LeftProgress/wavesurfer.getDuration()) * 100, wavesurfer.params.verticalZoom]);

				return ;
			}
			
			if (wv.ZoomFactor > 0)
			{
				// drag and draw by X pixels...
				var time_moved = wv.VisibleDuration * (diff / wv.drawer.width);
				wv.LeftProgress += time_moved;
				
				if (wv.LeftProgress + wv.VisibleDuration >= wv.getDuration ())
				{
					wv.LeftProgress = wv.getDuration () - wv.VisibleDuration;
				}
				else if (wv.LeftProgress < 0) {
					wv.LeftProgress = 0;
				}

				wv.ForceDraw ();
				app.fireEvent ('DidZoom', [wavesurfer.ZoomFactor, (wavesurfer.LeftProgress/wavesurfer.getDuration()) * 100, wavesurfer.params.verticalZoom]);
			}
		});
		

		wave.addEventListener ('mousedown', function( e ) {
			if (e.which === 3) {
				e.preventDefault();
				
				wavesurfer.Interacting |= (1 << 1);

				drag_x = e.clientX;
				wave.className = 'pk_grabbing';
				
				document.addEventListener ('mousemove', drag_move, false);
				return (false);
			} else {
				app.fireEvent ('MouseDown');
				app.fireEvent ('RequestChanToggle', 0, 1);
				app.fireEvent ('RequestChanToggle', 1, 1);
			}
		}, false);
		wave.addEventListener ('mouseleave', function( e ) {
			document.removeEventListener ('mousemove', drag_move);

			if (wave.className !== '')
			{
				wave.className = '';
				setTimeout(function () {
					wavesurfer.Interacting &= ~(1 << 1);
				}, 20);

			}
		}, false);
		wave.addEventListener ('mouseup', function( e ) {
			if (e.which === 3)
			{
				if (wave.className !== '')
				{
					wave.className = '';
					setTimeout(function () {
						wavesurfer.Interacting &= ~(1 << 1);
					}, 20);
				}
				document.removeEventListener ('mousemove', drag_move);
			}
		}, false);

		app.fireEvent ('RequestResize');

		app.listenFor ('RequestViewFollowCursorToggle', function () {
			var val = !wavesurfer.FollowCursor;
			wavesurfer.FollowCursor = val;

			// jump to curr cursor position
			if (val && q.is_ready) {
				wavesurfer.CursorCenter ();
			}

			app.fireEvent ( 'DidViewFollowCursorToggle', val );
		});
		app.listenFor ('RequestViewPeakSeparatorToggle', function () {
			if (!q.is_ready) return ;

			var val = !wavesurfer.params.limits ;
			wavesurfer.params.limits = val;

			wavesurfer.ForceDraw ();

			app.fireEvent ( 'DidViewPeakSeparatorToggle', val );
		});

		
		app.listenFor ('RequestViewTimelineToggle', function () {
			if (!q.is_ready) return ;

			var val = !wavesurfer.params.timeline ;
			wavesurfer.params.timeline = val;

			wavesurfer.ForceDraw ();

			app.fireEvent ( 'DidViewTimelineToggle', val );
		});

		app.listenFor ('RequestViewCenterToCursor', function () {
			if (!q.is_ready) return ;
			wavesurfer.CursorCenter ();
		});


		app.listenFor ('RequestZoomUI', function (type, val) {
			if (!q.is_ready) return ;

			if (type === 0) {
				wavesurfer.ResetZoom ();
				return ;
			}

			if (type === 'h') {
				wavesurfer.SetZoom ( 0.5, val );
			}

			if (type === 'v') {
				wavesurfer.SetZoomVertical ( val );
			}
		});
		// -


		this.ID3 = function ( arraybuffer ) {
			var tags = null;
			// var ttt = window.performance.now();

            var bytes = new Uint8Array( arraybuffer );
            if (bytes.length < 64) {
            	app.fireEvent ('RequestActionID3', 1, tags);
            	return tags;
            }

            if (bytes[0] === 73 && bytes[1] === 68 && bytes[2] === 51) {
				tags = ID3v2.ReadTags ( arraybuffer );

				//console.log( window.performance.now() - ttt );
				// console.log( tags );
            }
            else if (bytes[4] === 102 && bytes[5] === 116 && bytes[6] === 121 && 
            	bytes[7] === 112 && bytes[8] === 77 && bytes[9] === 52) {

            	tags = ID4.ReadTags ( arraybuffer );
				// console.log( window.performance.now() - ttt );
				// console.log( tags );
            }
            bytes = null;

            app.fireEvent ('RequestActionID3', 1, tags);

            return (tags);
		};
		// ---
	};

	PKAE._deps.engine = PKEng;

})( window, document, PKAudioEditor );