(function ( w, d, PKAE ) { 'use strict'; var modal_name = 'modalfx'; var modal_esc_key = modal_name + 'esc'; var max_db_val = 35; function PK_FX_PGEQ () { var q = this; var _id = 0; var _is_render_scheduled = false; var _is_render_scheduled2 = false; q.act = null; q.ranges = []; q.ui = {}; this.Callback = function(){}; this.Init = function ( container ) { var q = this; q.el = container; _make_ui ( q ); _make_evs ( q ); q.Render (); }; this.Add = function (type, is_on, freq, gain, qval, coords_x, coords_y) { var q = this; var new_range = { id: (++_id), type: type ? type : 'peaking', freq: freq || 0, gain: gain || 0, q: qval || 5, // interface _on: is_on, _hov: false, _el: null, _coords: { x: coords_x || 0, y: coords_y || 0 }, _arr:[] }; q.ranges.push (new_range); q.ranges.sort (_compare); if (q.act) { q.act.el.classList.remove ('pk_act'); } q.act = new_range; _range_compute_arr (new_range); new_range.el = _range_render_el (q, new_range, ' pk_act'); q.Callback && q.Callback (); q.Render (); }; this.Remove = function (range) { var q = this; var l = q.ranges.length; while (l-- > 0) { if (q.ranges[l] === range) { q.ranges.splice (l, 1); break; } } if (range.el) { range.el.parentNode.removeChild (range.el); range.el = null; } if (q.act && q.act === range) { q.act = null; } q.Render (); }; var _fillstyle = '#d9d955'; var _anim_render = function () { _render ( q ); }; this.Render = function () { var q = this; if (_is_render_scheduled) return ; _is_render_scheduled = true; requestAnimationFrame (_anim_render); }; this.RenderBars = function (_, freq) { var q = this; if (_is_render_scheduled2) return ; _is_render_scheduled2 = true; requestAnimationFrame (function () { _render_bars ( q, freq ); }); }; var _render_bars = function( q, freq ) { _is_render_scheduled2 = false; if (!freq) return ; var ctx = q.ui.ctx_bars; var canvas = q.ui.canvas_bars; var cw = canvas.width; var ch = canvas.height; // ctx.fillStyle = '#000'; // ctx.fillRect (0, 0, cw, ch); ctx.clearRect (0, 0, cw, ch); var bufferLength = 512; // 256 var max_bars = 117 * 2; var barWidth = (cw / max_bars).toFixed(1)/1; var barHeight = 0; var x = 0; // for (var i = 0; i < 117; ++i) { barHeight = freq[i]; // map.push ( i * 43 ); var newheight = ((barHeight / 256) * ch) >> 0; ctx.fillRect (x, ch - newheight, barWidth, newheight); x += barWidth;// + 1; } for (var i = 0; i < 117; ++i) { // (116*3.4) barHeight = freq[117 + ((i * 3.34) >> 0)]; // map.push ( (120 + (i * 3)) * 43 ); var newheight = ((barHeight / 256) * ch) >> 0; ctx.fillRect (x, ch - newheight, barWidth, newheight); x += barWidth;// + 1; } // console.log( map ); // what if we care for the small bars first /* var steps = (total_freq/bufferLength) >> 0; // we care for // 256 bars // 32 // 64 // 125 // 250 // 500 // 1000 // 2000 // 4000 // 8000 // 16000 // 20000 var arr = [32, 64, 125, 250, 500, 1000, 2000, 4000, 8000, 16000, 20000]; var curr = 0; var curr_bars = 0; var bars_per_entry = (bufferLength / 10) >> 0; for (var i = 0; i < bufferLength; ++i) { if (++curr_bars < bars_per_entry) { var ff = arr[ curr ]; var ff_next = arr[ curr + 1]; var m = 0; for (; m < bufferLength; ++m) { if (m * steps > ff) { --m; break; } } barHeight = freq[ m ]; var newheight = ((barHeight / 256) * ch) >> 0; ctx.fillRect (x, ch - newheight, barWidth, newheight); x += barWidth;// + 1; } else { ++curr; curr_bars = 0; } } */ // for (var i = 0; i < bufferLength; ++i) { // barHeight = freq[i]; // var newheight = ((barHeight / 256) * ch) >> 0; // ctx.fillRect (x, ch - newheight, barWidth, newheight); // x += barWidth;// + 1; // } }; var line_arr = new Array (1000); var _render = function ( q ) { _is_render_scheduled = false; var ctx = q.ui.ctx_eq; var canvas = q.ui.canvas_eq; var cw = canvas.width; var ch = canvas.height; var ch_half = ch / 2; // -------------------- ctx.clearRect (0, 0, cw, ch); ctx.fillStyle = _fillstyle; if (q.ranges.length === 0) { ctx.beginPath (); ctx.moveTo (0, ch_half); ctx.lineTo (cw, ch_half); ctx.stroke (); return ; } // render the line based on the elements var first = true; var arr = []; for (var i = 0; i < total; ++i) { arr[i] = 0; } for (var o = 0; o < q.ranges.length; ++o) { var curr = q.ranges[ o ]; if (!curr._on) continue; if (first) { first = false; for (var i = 0; i < total; ++i) { line_arr[i] = curr._arr[i]; } } else { for (var i = 0; i < total; ++i) { line_arr[i] += curr._arr[i]; } } // --- } if (first) { ctx.beginPath (); ctx.moveTo (0, ch_half); ctx.lineTo (cw, ch_half); ctx.stroke (); } else { // -- ctx.beginPath (); ctx.moveTo ( 0, ch_half - (line_arr[ 0 ] * (ch_half / max_db_val)) ); for (var i = 0; i < (total / 4); i += 1) { var el = line_arr[ i ]; var x = (i * 2) * (cw / total); var y = ch_half - (el * (ch_half / max_db_val)); ctx.lineTo ( x, y ); } var hh = 0; for (var i = (total / 4); i < total; i += 3) { var el = line_arr[ i ]; hh += 2; var x = ((total / 2) + hh) * (cw / total); var y = ch_half - (el * (ch_half / max_db_val)); ctx.lineTo ( x, y ); } ctx.stroke (); } // --- // draw the dots var radius = 6; for (var o = 0; o < q.ranges.length; ++o) { var curr = q.ranges[ o ]; var center_x = curr._coords.x; var center_y = curr._coords.y; ctx.beginPath (); ctx.arc (center_x, center_y, radius, 0, 2 * Math.PI, false); if (curr === q.act) { ctx.shadowBlur = 24; if (curr._on) ctx.fillStyle = '#fff'; else ctx.fillStyle = '#686868'; ctx.stroke (); ctx.fill (); ctx.shadowBlur = 0; ctx.fillStyle = _fillstyle; } else if (curr._hov) { if (curr._on) ctx.fillStyle = 'blue'; else ctx.fillStyle = 'darkblue'; ctx.stroke (); ctx.fill (); ctx.fillStyle = _fillstyle; } else if (curr._on) { ctx.fill (); } else { ctx.fillStyle = '#555'; ctx.fill (); ctx.fillStyle = _fillstyle; } } // --- }; //////////////////////////////////////////// // helpers var _dbncr = null; var total_freq = 20000; // 22000 var total = 1000; var jump = (total_freq / total) >> 0; function _range_update ( q, range, new_range, compute_coords ) { var modified = false; var old_val = null; for (var key in new_range) { if (range[key] !== new_range[key]) { modified = true; old_val = range[key]; range[key] = new_range[key]; if (key === '_on') { var el = document.getElementById ('pgon' + range.id); el.checked = range[key]; } else if (key === 'freq') { var el = range.el.getElementsByClassName('pk_freq')[0]; //requestAnimationFrame (function () { el.value = range[key]; //}); } else if (key === 'gain') { var el = range.el.getElementsByClassName('pk_gain')[0]; //requestAnimationFrame (function () { el.value = range[key]; //}); } else if (key === 'q') { var el = range.el.getElementsByClassName('pk_q')[0]; //requestAnimationFrame (function () { el.value = range[key]; //}); } else if (key === 'type') { // ----- var el = range.el.getElementsByTagName('select')[0]; if (range[key] === 'peaking') el.options[0].selected = true; else if (range[key] === 'lowpass') el.options[1].selected = true; else if (range[key] === 'highpass') el.options[2].selected = true; _range_compute_arr (range); q.ranges.sort (_compare); } // --- } } if (modified) { if (compute_coords) { // compute coords of the canvas var canvas = q.ui.canvas_eq; var cw = canvas.width; var ch = canvas.height; var tmp_x = 0; if (range.freq <= 5000) { range._coords.x = ((range.freq / 5000) * (cw / 2) ).toFixed(1)/1; } else { range._coords.x = ((cw / 2) + (((range.freq - 5000) / 15000) * (cw / 2))).toFixed(1)/1; } // range._coords.x = ((range.freq / total_freq) * cw).toFixed(1)/1; if (range.type === 'peaking') range._coords.y = ((1.0 - ((range.gain + max_db_val) / (max_db_val * 2))) * ch).toFixed(1)/1; else range._coords.y = (ch / 2).toFixed(1)/1; } if (_dbncr) { clearTimeout (_dbncr); } _dbncr = setTimeout (function () { q.Callback (); _dbncr = null; }, 38); q.Render (); } // --- }; function _ease (t) { return t*t*t*t*t }; function _ease_out (t) { return t*t*t*t }; function _range_compute_arr ( range ) { var arr = []; for (var i = 0; i < total; ++i) { arr[i] = 0; } range._arr = arr; // ------------- var rounding = total_freq * (2 / range.q); var half_rounding = (rounding / jump) >> 0; if (range.type === 'peaking') { var edge_left = range.freq - (rounding / 2); var edge_right = range.freq + (rounding / 2); var start = (edge_left / jump) >> 0; var end = (edge_right / jump) >> 0; var j = 0; for (var i = start; i < end; ++i) { var ii = (i * jump); if (ii < range.freq) { ++j; arr[i] += _ease (j / (half_rounding / 2)) * range.gain; } else { --j; arr[i] += _ease (j / (half_rounding / 2)) * range.gain; } } return ; } if (range.type === 'highpass') { var edge_left = range.freq - rounding; var start = (edge_left / jump) >> 0; var end = (range.freq / jump) >> 0; for (var i = 0; i < start; ++i) { arr[i] = -max_db_val; } // todo improve this!!! var j = half_rounding; for (var i = start; i < end; ++i) { --j; arr[i] -= _ease_out (j / half_rounding) * max_db_val; } return ; } if (range.type === 'lowpass') { var edge_right = range.freq + rounding; var start = (range.freq / jump) >> 0; var end = (edge_right / jump) >> 0; for (var i = end; i < total; ++i) { arr[i] = -max_db_val; } // todo improve this!!! var j = 0; for (var i = start; i < end; ++i) { ++j; arr[i] -= _ease_out (j / half_rounding) * max_db_val; } return ; } // ------------- } function _make_ui ( q ) { var el_drawer = d.createElement ('div'); el_drawer.className = 'pk_row'; var canvas_bars = d.createElement ('canvas'); var canvas_eq = d.createElement ('canvas'); canvas_bars.className = 'pk_peq2'; canvas_eq.className = 'pk_peq'; canvas_bars.width = 450 / 2; canvas_bars.height = 224 / 2; canvas_eq.width = 450; canvas_eq.height = 225; var ctx_bars = canvas_bars.getContext ('2d', {alpha:true, antialias:false}); var ctx_eq = canvas_eq.getContext ('2d', {alpha:true, antialias:false}); ctx_bars.fillStyle = '#365457'; // '#486a6e'; // ctx_eq.lineWidth = 2; ctx_eq.strokeStyle = '#FF0000'; ctx_eq.shadowColor = '#FF2222'; ctx_eq.shadowBlur = 0; // render the decibel and the frequencies var marker_freqs = d.createElement ('div'); marker_freqs.className = 'pk_peq3 pk_noselect'; marker_freqs.innerHTML = '32' + // '32' + // '64' + // '128' + // '250' + // '500' + '500' + '1k' + '2k' + '4k' + '5k' + '8k' + '12k' + '16k' + '20k'; var marker_dbs = d.createElement ('div'); marker_dbs.className = 'pk_peq4 pk_noselect'; marker_dbs.innerHTML = '35' + '28' + '21' + '14' + '7' + '0' + '-7' + '-14' + '-21' + '-28' + '35'; el_drawer.appendChild ( canvas_bars ); el_drawer.appendChild ( canvas_eq ); el_drawer.appendChild ( marker_freqs ); el_drawer.appendChild ( marker_dbs ); q.el.appendChild ( el_drawer ); // element's area var el_list = document.createElement ('div'); el_list.className = 'pk_row pk_noselect pk_pglst'; el_list.innerHTML = '
' + ' #typegainfreqQ' + '
'; q.el.appendChild ( el_list ); q.ui.ctx_bars = ctx_bars; q.ui.ctx_eq = ctx_eq; q.ui.canvas_bars = canvas_bars; q.ui.canvas_eq = canvas_eq; q.ui.el_list = el_list; } function _make_evs ( q ) { var ctx = q.ui.ctx_eq; var canvas = q.ui.canvas_eq; var click_time = 0; var is_dragging = false; var _move = function ( e ) { if (!is_dragging || !q.act) return ; var ex = 0; var ey = 0; if (e.touches) { if (e.touches.length > 1) { return ; } ex = e.touches[0].clientX; ey = e.touches[0].clientY; } else { ex = e.clientX; ey = e.clientY; } var bounds = canvas.getBoundingClientRect (); var cw = canvas.width; var ch = canvas.height; var posx = ex - bounds.left; var posy = ey - bounds.top; var rel_x = posx / cw; var rel_y = posy / ch; q.act._coords.x = posx; q.act._coords.y = posy; // up until half it's 0 - 5000, second half 5000 -> 2200 var freq = 0; if (rel_x <= 0.5) { freq = (5000 * (rel_x * 2)) >> 0 } else { freq = 5000 + ((((rel_x - 0.5) * 2) * 15000) >> 0); } // var freq = (rel_x * (total_freq) + 0) >> 0; // + 16 (min freq) var gain = (((rel_y - 0.5) * -2) * max_db_val).toFixed (2) / 1; _range_update (q, q.act, { 'freq': freq, 'gain': gain }); _range_compute_arr (q.act); }; var _end = function ( e ) { is_dragging = false; canvas.removeEventListener ('mousemove', _move); canvas.removeEventListener ('mouseup', _end); canvas.removeEventListener ('touchmove', _move); canvas.removeEventListener ('touchup', _end); }; var mdown = function ( e ) { var unchecked = !!q.act; if (q.ranges.length === 0) { if (unchecked) q.Render (); return ; } var bounds = canvas.getBoundingClientRect (); var cw = canvas.width; var ch = canvas.height; var posx = e.clientX - bounds.left; var posy = e.clientY - bounds.top; var dist_x = e.is_touch ? 20 : 10; var dist_y = e.is_touch ? 20 : 9; for (var o = 0; o < q.ranges.length; ++o) { var curr = q.ranges[ o ]; if ( Math.abs (curr._coords.x - posx) < dist_x && Math.abs (curr._coords.y - posy) < dist_y) { if (unchecked) { q.act.el.classList.remove ('pk_act'); } q.act = curr; q.act.el.classList.add ('pk_act'); is_dragging = true; q.Render (); // check if we are targetting a circle if (!e.is_touch) { canvas.addEventListener ('mousemove', _move, false); canvas.addEventListener ('mouseup', _end, false); } else { e.ev.preventDefault (); e.ev.stopPropagation (); canvas.addEventListener ('touchmove', _move, false); canvas.addEventListener ('touchup', _end, false); } return ; } // --- } if (unchecked) { q.act.el.classList.remove ('pk_act'); // un-highlight q.act = null; q.Render (); } // ---- }; canvas.addEventListener ('mousedown', mdown, false); canvas.addEventListener ('touchstart', function ( e ) { if (e.touches.length > 1) { e.preventDefault (); e.stopPropagation (); return ; } var ev = { clientX : e.touches[0].clientX, clientY : e.touches[0].clientY, is_touch : true, ev : e }; mdown ( ev ); }); canvas.addEventListener ('click', function ( e ) { if (e.timeStamp - click_time < 260) { var bounds = canvas.getBoundingClientRect (); var cw = canvas.width; var ch = canvas.height; var posx = e.clientX - bounds.left; var posy = e.clientY - bounds.top; var rel_x = posx / cw; var rel_y = posy / ch; var freq = 0; if (rel_x <= 0.5) { freq = (5000 * (rel_x * 2)) >> 0 } else { freq = 5000 + ((((rel_x - 0.5) * 2) * 15000) >> 0); } // var freq = (rel_x * (total_freq) + 0) >> 0; // + 16 (min freq) var gain = (((rel_y - 0.5) * -2) * max_db_val).toFixed(2)/1; var qval = 5; var type = 'peaking'; q.Add (type, true, freq, gain, qval, posx, posy); } click_time = e.timeStamp; }, false); // --- } function _range_render_el ( q, range, clss ) { var el_list = q.ui.el_list; var el = d.createElement ('div'); el.className = 'pk_pgeq_els' + (clss ? clss : ''); el.setAttribute ('data-id', range.id); el.addEventListener ('click', function ( e ) { if (!range.el) return ; if (range !== q.act) { if (q.act) { q.act.el.classList.remove ('pk_act'); } q.act = range; q.act.el.classList.add ('pk_act'); q.Render (); } }, false); el.addEventListener ('mouseover', function ( e ) { if (!range.el) return ; if (!range._hov) { range._hov = true; q.Render (); } }, false); el.addEventListener ('mouseleave', function ( e ) { if (!range.el) return ; if (range._hov) { range._hov = false; q.Render (); } }, false); // # & on or off var chckd = range._on ? 'checked' : ''; var num = '' + range.id + ''; var el_num = d.createElement ('div'); el_num.className = 'pk_txlft'; el_num.innerHTML = num + '' + ''; el_num.getElementsByTagName('input')[0].onchange = function ( e ) { _range_update (q, range, {'_on': !!this.checked}); var lbl = this.parentNode.getElementsByTagName('label')[0]; lbl.innerHTML = this.checked ? 'ON' : 'OFF'; }; el.appendChild (el_num); // type var sel1 = range.type === 'lowpass' ? 'selected' : ''; var sel2 = range.type === 'highpass' ? 'selected' : ''; var el_type = d.createElement ('div'); el_type.innerHTML = ''; el_type.getElementsByTagName('select')[0].onchange = function ( e ) { var val = this.options[this.selectedIndex].value; if (val === 'peaking') { el.classList.remove ('pk_dis'); } else { el.classList.add ('pk_dis'); } _range_update (q, range, {'type': val}, 1); }; el.appendChild (el_type); // gain var el_gain = d.createElement ('div'); el_gain.innerHTML = ''; el_gain.getElementsByClassName('pk_gain')[0].onchange = function ( e ) { if (!this.value) { this.value = 0; } if (this.hasAttribute ('data-open')) { this.parentNode.getElementsByClassName('pk_horiz')[0].value = this.value; } _range_update (q, range, {'gain': this.value / 1}, 1); _range_compute_arr (range); }; el_gain.getElementsByClassName('pk_gain')[0].onfocus = function ( e ) { if (this.hasAttribute ('data-open')) return ; var self = this; var parent = this.parentNode; var bar = document.createElement ('div'); bar.className = 'pk_pgeq_freq pk_gain'; bar.innerHTML = '
'; bar.getElementsByClassName('pk_horiz')[0].oninput = function ( e ) { if (self.value != this.value) { self.value = this.value; self.onchange (); } }; parent.appendChild (bar); this.setAttribute('data-open', '1'); var down = function ( e ) { if ( !e.target.classList.contains ('pk_gain') || (e.target.type === self.type && e.target !== self) ) { self.removeAttribute ('data-open'); parent.removeChild (bar); q.el.removeEventListener ('mousedown', down); return ; } }; q.el.addEventListener ('mousedown', down, false); }; el.appendChild (el_gain); // freq var el_freq = d.createElement ('div'); el_freq.innerHTML = ''; el_freq.getElementsByClassName('pk_freq')[0].onchange = function ( e ) { if (!this.value) { this.value = 500; } if (this.hasAttribute ('data-open')) { this.parentNode.getElementsByClassName('pk_horiz')[0].value = this.value; } _range_update (q, range, {'freq': this.value / 1}, 1); _range_compute_arr (range); }; el_freq.getElementsByClassName('pk_freq')[0].onfocus = function ( e ) { if (this.hasAttribute ('data-open')) return ; var self = this; var parent = this.parentNode; var bar = document.createElement ('div'); bar.className = 'pk_pgeq_freq pk_freq'; bar.innerHTML = '
'; bar.getElementsByClassName('pk_horiz')[0].oninput = function ( e ) { if (self.value != this.value) { self.value = this.value; self.onchange (); } }; parent.appendChild (bar); this.setAttribute('data-open', '1'); var down = function ( e ) { if ( !e.target.classList.contains ('pk_freq') || (e.target.type === self.type && e.target !== self) ) { self.removeAttribute ('data-open'); parent.removeChild (bar); q.el.removeEventListener ('mousedown', down); } }; q.el.addEventListener ('mousedown', down, false); }; el.appendChild (el_freq); // q var el_q = d.createElement ('div'); el_q.innerHTML = ''; el_q.getElementsByClassName('pk_q')[0].onchange = function ( e ) { if (!this.value) { this.value = 1; } if (this.hasAttribute ('data-open')) { this.parentNode.getElementsByClassName('pk_horiz')[0].value = this.value; } _range_update (q, range, {'q': this.value / 1}, 1); _range_compute_arr (range); }; el_q.getElementsByClassName('pk_q')[0].onfocus = function ( e ) { if (this.hasAttribute ('data-open')) return ; var self = this; var parent = this.parentNode; var bar = document.createElement ('div'); bar.className = 'pk_pgeq_freq pk_q'; bar.innerHTML = '
'; bar.getElementsByClassName('pk_horiz')[0].oninput = function ( e ) { if (self.value != this.value) { self.value = this.value; self.onchange (); } }; parent.appendChild (bar); this.setAttribute('data-open', '1'); var down = function ( e ) { if ( !e.target.classList.contains ('pk_q') || (e.target.type === self.type && e.target !== self) ) { self.removeAttribute ('data-open'); parent.removeChild (bar); q.el.removeEventListener ('mousedown', down); } }; q.el.addEventListener ('mousedown', down, false); }; el.appendChild (el_q); // delete var el_del = d.createElement ('div'); el_del.className = 'pk_del'; el_del.innerHTML = 'DELETE'; el_del.getElementsByTagName('a')[0].onclick = function ( e ) { q.Remove (range); }; el.appendChild (el_del); // ---------------------- el_list.appendChild (el); return (el); } function _compare ( a, b ) { if (a.type === 'peaking' && b.type !== 'peaking') return -1; if (b.type === 'peaking' && a.type !== 'peaking') return 1; return 0; } // --- }; var ParagraphicModal = function ( app, custom_presets ) { app.fireEvent ('RequestSelect', 1); var filter_id = 'paragraphic_eq'; // ------- var PGEQ = new PK_FX_PGEQ (); var DrawBars = function (_, freq) { PGEQ.RenderBars (_, freq); }; var updateFilter = function () { if (!PGEQ) return ; var val = []; var ranges = PGEQ.ranges; for (var i = 0; i < ranges.length; ++i) { var range = ranges [ i ]; if (range._on) { val.push ({ 'type' : range.type, 'freq' : range.freq, 'val' : range.gain, 'q' : range.q }); } } return (val); }; var x = new PKAudioFXModal ({ id: filter_id, title: 'Paragraphic EQ', ondestroy: function ( q ) { app.stopListeningFor ('DidAudioProcess', DrawBars); app.ui.InteractionHandler.on = false; app.ui.KeyHandler.removeCallback (modal_esc_key); PGEQ = null; }, preview: function ( q ) { app.fireEvent ('RequestActionFX_PREVIEW_PARAMEQ', updateFilter ()); }, body: '', presets:[ {name:'Old Telephone',val:'1,highpass,0,5800,5.8,1,lowpass,0,7060,5'} ], custom_pres:custom_presets.Get (filter_id), onpreset: function ( val ) { var l = PGEQ.ranges.length; while (l-- > 0) { PGEQ.Remove (PGEQ.ranges[l]); } var canvas = PGEQ.ui.canvas_eq; var cw = canvas.width; var ch = canvas.height; var list = val.split(','); var len = list.length; var els = (len / 5) >> 0; for (var j = 0; j < els; ++j) { var curr = []; var offset = j * 5; curr[0] = !!(list[ offset + 0 ] / 1); curr[1] = list[ offset + 1 ]; curr[2] = list[ offset + 2 ] / 1; curr[3] = list[ offset + 3 ] / 1; curr[4] = list[ offset + 4 ] / 1; var x = 0; var y = 0; if (curr[3] < 5000) { x = (curr[3] / 5000) * (cw / 2); } else { x = ((cw / 2) + (((curr[3] - 5000) / 15000) * (cw / 2))).toFixed(1)/1; } if (curr[1] === 'peaking') y = ((1.0 - (((curr[2]/1) + max_db_val) / (max_db_val * 2))) * ch).toFixed(1)/1; else y = (ch / 2).toFixed(1)/1; // (type, is_on, freq, gain, qval, coords_x, coords_y) PGEQ.Add (curr[1], !!curr[0], curr[3]/1, curr[2]/1, curr[4]/1, x, y); } }, buttons: [{ title:'Apply EQ', clss:'pk_modal_a_accpt', callback: function( q ) { app.fireEvent ('RequestActionFX_PARAMEQ', updateFilter ()); q.Destroy (); } }], setup:function( q ) { PGEQ.Init ( q.el_body ); PGEQ.Callback = function () { app.fireEvent ('RequestActionFX_UPDATE_PREVIEW', updateFilter ()); }; app.listenFor ('DidAudioProcess', DrawBars); app.fireEvent ('RequestPause'); app.ui.InteractionHandler.checkAndSet (modal_name); app.ui.KeyHandler.addCallback (modal_esc_key, function ( e ) { if (!app.ui.InteractionHandler.check (modal_name)) return ; q.Destroy (); }, [27]); } }, app); x.Show (); }; PKAudioEditor._deps.FxEQ = ParagraphicModal; /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// function getOfflineAudioContext (channels, sampleRate, duration) { return new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(channels, duration, sampleRate); }; function _normalize_array (data) { var new_array = []; for (var i = 0; i < data.length; ++i) { new_array.push ( Math.abs (Math.round ((data[i + 1] - data[i]) * 1000)) ); } return (new_array); }; function _normalize_array2 (data) { var new_array = []; for (var i = 0; i < data.length; ++i) { new_array.push ( Math.round (Math.abs (data[i] * 1000)) ); } return (new_array); }; function _group_rhythm ( data, diff_arr ) { if (diff_arr.length <= 1) return ; var peak_median = 0; for (var i = 0; i < data.length; ++i) { peak_median += data[i]; } peak_median /= diff_arr.length; peak_median -= peak_median * 0.2; var diff_median = 0; for (var i = 0; i < diff_arr.length; ++i) { diff_median += diff_arr[i]; } diff_median /= diff_arr.length; if (diff_median > 1) diff_median -= (diff_median * 0.2); var existing = 0; for (var i = 0; i < diff_arr.length; ++i) { if (diff_arr[i] <= diff_median) continue; ++existing; } // console.log (" DIFF MEDIAN IS ", diff_median, " and total beats: ", existing, " out of: ", diff_arr.length); // clean-up the drums array - based on the median. // console.log ( JSON.stringify( data ) ); // console.log ("----------"); for (var i = 0, j = 0; i < data.length; ++i) { if (data[i] !== 0) { if (diff_arr[j] && diff_arr[j] < diff_median) { if (data[i] > peak_median && diff_arr[j] > (diff_median * 0.6)){ //console.log ( 'ZEROEDC NOOOOT ', i, ' ', data[i], ' with median ', peak_median , ' but diff was ', diff_arr[j], ' yet. ', diff_median ); } else { //console.log ( 'ZEROEDC ', i, ' ', data[i], ' with median ', peak_median , ' but diff was ', diff_arr[j], ' yet. ', diff_median ); data[i] = 0; } } ++j; } // ---- } // console.log( data ); window.final_arr = data; // console.log ( JSON.stringify( data ) ); // now count distance between peaks var distances = {}; var unique_distances = []; var first_found = 0; var is_first = true; for (var i = 0; i < data.length - 1; ++i) // #### do not litter the last data { if (data[i] === 0) { ++first_found; continue; } //if (first_found < 1 && !is_first) { // continue; //} if (is_first) { is_first = false; } first_found = 0; var own = []; unique_distances.push (own); // console.log ('----------------------------------'); // console.log ('COMPUTING DISTANCE OF ' + i + ' value ' + data[i] ); var interval = 0; var total = 12; var last_found = 0; for (var j = i + 1; j < 1000; ++j) { if (data[j] === 0) { ++interval; ++last_found; continue; } else if (!data[j]) { break; } if (last_found < 0) { continue; } last_found = 0; if (--total === 0) break; own.push (interval); // if it exists, immediately reach out for the next one. if (!distances[interval]) distances[interval] = 0; distances[interval] += 1; // console.log ('distance with index ' + j + ' value ' + data[j] + ' is ' + interval ); } // break; } console.log(unique_distances); // grab only the big peaks. function getmax (a) { var m = -Infinity, i = 0, n = a.length; for (; i != n; ++i) { if (a[i] > m) { m = a[i]; } } return m; } function getmin (a) { var m = Infinity, i = 0, n = a.length; for (; i != n; ++i) { if (a[i] !== 0 && a[i] < m) { m = a[i]; } } return m; } var max = getmax (data); var min = getmin (data); var count = 0; var threshold = Math.round ((max - min) * 0.3); var velocities = []; for (var i = 0; i < data.length; ++i) { if (data[i] === 0) continue; if (data[i] >= (max - threshold)) { velocities.push (3); } else if (data[i] >= (max - (threshold * 2))) { velocities.push (2); } else if (data[i] >= (max - (threshold * 3))) { velocities.push (1); } else { velocities.push (0); } } return ([distances, velocities]); }; var TempoToolsModal = function ( app ) { app.fireEvent ('RequestSelect', 1); var filter_id = 'tempo_tools'; var act_index = 1; var act_tool = null; // ------ var TempoMetro = function ( app, modal ) { var q = this; q.app = app; var bpm = 120; var tick = null; var count = 0; var time = ((60.0 / bpm) * 1000) >> 0; var audioContext = null; // new AudioContext(); var osc = null; var amp = null; var ready = false; var volume = 0.5; var accentuate = true; var DidStopPlay = null; var DidPlay = null; var MetronomeAct = null; var MetronomeInAct = null; q.Init = function ( container ) { var q = this; q.el = container; _make_ui ( q ); _make_evs ( q ); }; q.Destroy = function () { q.app.stopListeningFor ('DidStopPlay', DidStopPlay); q.app.stopListeningFor ('DidPlay', DidPlay); q.app.stopListeningFor ('DidStartMetro', MetronomeAct); q.app.stopListeningFor ('DidStopMetro', MetronomeInAct); DidStopPlay = null; DidPlay = null; MetronomeAct = null; MetronomeInAct = null; if (ready) { if (tick) { clearTimeout (tick); tick = null; } if (audioContext) { var now = audioContext.currentTime; osc.stop (now); amp.disconnect (); osc.disconnect (); audioContext = null; ready = false; } } if (q.body) { q.body.parentNode.removeChild ( q.body ); q.body = null; } q.app = null; }; function _make_ui ( q ) { var el_drawer = d.createElement ('div'); el_drawer.className = 'pk_row'; el_drawer.innerHTML = '
'+ ''+ ''+ '120'+ '
'+ '
'+ ''+ ''+ '50%'+ '
'+ '
'+ ''+ '
' + '
'+ 'Metronome'+ 'Play Track'+ 'Play Both'+ '
'; q.body = el_drawer; q.el.appendChild ( el_drawer ); }; function _make_evs ( q ) { var range = q.body.getElementsByClassName('pk_horiz')[0]; var span = q.body.getElementsByClassName('pk_val')[0]; var range2 = q.body.getElementsByClassName('pk_horiz')[1]; var span2 = q.body.getElementsByClassName('pk_val')[1]; var checkbox = q.body.getElementsByClassName('pk_check')[0]; range.oninput = function() { bpm = (range.value/1); span.innerHTML = bpm; time = ((60.0 / bpm) * 1000) >> 0; }; range2.oninput = function() { var val = (range2.value/1); volume = val; span2.innerHTML = ((val*100) >> 0) + '%'; }; checkbox.oninput = function() { accentuate = checkbox.checked; }; var metronome_btn = q.body.getElementsByClassName ('pk_modal_a_bottom')[0]; var play_btn = q.body.getElementsByClassName ('pk_modal_a_bottom')[1]; var both_btn = q.body.getElementsByClassName ('pk_modal_a_bottom')[2]; metronome_btn.onclick = function () { if (tick) { clearTimeout (tick); tick = null; count = 0; q.app.fireEvent ('DidStopMetro'); return ; } var play = function () { tick = setTimeout (function() { if (!tick) return ; if (++count % 4 === 0) _metronome (1); else _metronome (0); play (); }, time); }; count = 0; if (!ready) _prepare (); q.app.fireEvent ('DidStartMetro'); _metronome (1); play (); }; MetronomeInAct = function() { metronome_btn.classList.remove ('pk_act'); }; MetronomeAct = function() { metronome_btn.classList.add ('pk_act'); }; q.app.listenFor ('DidStartMetro', MetronomeAct); q.app.listenFor ('DidStopMetro', MetronomeInAct); play_btn.onclick = function () { if (PKAudioEditor.engine.wavesurfer.isPlaying()) { q.app.fireEvent ('RequestStop'); } else { q.app.fireEvent ('RequestPlay'); } }; if (!PKAudioEditor.engine.wavesurfer.isReady) { play_btn.className += ' pk_inact'; both_btn.className += ' pk_inact'; } if (PKAudioEditor.engine.wavesurfer.isPlaying()) { play_btn.className += ' pk_act'; } DidStopPlay = function() { play_btn.classList.remove ('pk_act'); play_btn.innerText = 'Play Track'; }; DidPlay = function() { play_btn.classList.add ('pk_act'); play_btn.innerText = 'Stop Track'; }; q.app.listenFor ('DidStopPlay', DidStopPlay); q.app.listenFor ('DidPlay', DidPlay); both_btn.onclick = function () { if (tick) metronome_btn.onclick (); if (PKAudioEditor.engine.wavesurfer.isPlaying()) play_btn.onclick (); setTimeout(function() { if (!tick && !PKAudioEditor.engine.wavesurfer.isPlaying()) { play_btn.onclick (); setTimeout(function(){ metronome_btn.onclick (); },0); } },66); }; }; function _metronome ( type ) { if (type === 1 && accentuate) { osc.frequency.value = 880.0; } else { osc.frequency.value = 440.0; } amp.gain.setValueAtTime (amp.gain.value, audioContext.currentTime); amp.gain.linearRampToValueAtTime (volume, audioContext.currentTime + 0.01); amp.gain.linearRampToValueAtTime (0.0, audioContext.currentTime + 0.12); }; function _prepare () { audioContext = new (window.AudioContext || window.webkitAudioContext)(); osc = audioContext.createOscillator (); amp = audioContext.createGain(); amp.gain.value = 0; osc.connect (amp); amp.connect (audioContext.destination); osc.start (0); ready = true; // osc.stop( time + 0.05 ); }; }; var TempoTap = function ( app, modal ) { var q = this; q.app = app; var DidStopPlay = null; var DidPlay = null; var DidSetLoop = null; var DidAudioProcess = null; q.Init = function ( container ) { var q = this; q.el = container; _make_ui ( q ); _make_evs ( q ); }; q.Destroy = function () { q.app.stopListeningFor ('DidStopPlay', DidStopPlay); q.app.stopListeningFor ('DidPlay', DidPlay); q.app.stopListeningFor ('DidSetLoop', DidSetLoop); q.app.stopListeningFor ('DidAudioProcess', DidAudioProcess); DidStopPlay = null; DidPlay = null; DidSetLoop = null; DidAudioProcess = null; q.app.ui.KeyHandler.removeCallback ('tmpTap'); if (q.body) { q.body.parentNode.removeChild ( q.body ); q.body = null; } q.app = null; }; function _make_ui ( q ) { var el_drawer = d.createElement ('div'); el_drawer.className = 'pk_row'; // Estimate tempo for selected area button el_drawer.innerHTML = '
' + 'Average BPM'+ ''+ '
'+ '
'+ 'Nearest BPM'+ ''+ '
'+ '
'+ 'Timing Taps'+ ''+ 'Reset'+ 'Play Track'+ 'Loop'+ '
'+ '
'+ 'CLEARED...'+ 'STAND BY...'+ '
'+ '
'+ ''+ ''+ '
'+ '
'+ ''+ 'Tap in this area, or hit [SPACE] rhythmically, to measure BPM.'+ ''+ '
'; q.body = el_drawer; q.el.appendChild ( el_drawer ); }; function _make_evs ( q ) { var tap_graph = q.body.querySelectorAll('#pk_tmp_tap')[0]; var tap_area = q.body.querySelectorAll('#pk_tmp_tap3')[0]; var reset_btn = q.body.getElementsByClassName ('pk_modal_a_bottom')[0]; var play_btn = q.body.getElementsByClassName ('pk_modal_a_bottom')[1]; var loop_btn = q.body.getElementsByClassName ('pk_modal_a_bottom')[2]; var canvas = q.body.getElementsByTagName('canvas')[0]; var ctx = canvas.getContext('2d', {alpha:false,antialias:false}); var tempCanvas = document.createElement('canvas'); tempCanvas.width = 500 * 2; tempCanvas.height = 100 * 2; var tempCtx = tempCanvas.getContext ('2d', {alpha:false,antialias:false}); ctx.imageSmoothingEnabled = true; tempCtx.imageSmoothingEnabled = true; var value_els = q.body.getElementsByClassName ('pk_val'); var tap_msg = tap_graph.getElementsByClassName ('pk_obj2'); var tap_msg2 = tap_area.getElementsByTagName('span')[0]; var bpm_el = value_els[ 0 ]; var bpm_el_round = value_els[ 1 ]; var bpm_el_count = value_els[ 2 ]; var reset_wait = 3000; var time_msec = 0; var time_msec_prev = 0; var time_msec_first = 0; var count = 0; var bpm = 0; var steps_count = 0; var first = true; var is_playing = false; var _reset_count = function ( force ) { if (first) { tap_msg[1].style.opacity = '0'; } count = 0; steps_count = 0; first = true; setTimeout(function() { if (!first) return ; tap_msg[0].style.opacity = '0.5'; if (!force) { reset_btn.className += ' pk_act'; setTimeout(function() { reset_btn.classList.remove ('pk_act'); },140); } setTimeout(function() { if (first) { tap_msg[0].style.opacity = '0'; tap_msg[1].style.opacity = '0.5'; } else { tap_msg[0].style.opacity = '0'; tap_msg[1].style.opacity = '0'; } }, force ? 490 : 874); }, (force ? 0 : 150)); if (force) { bpm_el.value = '-'; bpm_el_round.value = '-'; bpm_el_count.value = '-'; var els = tap_graph.parentNode.getElementsByClassName('pk_obj'); var l = els.length; while (l-- > 0) { if (els[l]) { els[l].parentNode.removeChild (els[l]); } } } }; reset_btn.onclick = function () { _reset_count (true); }; play_btn.onclick = function () { if (PKAudioEditor.engine.wavesurfer.isPlaying()) { q.app.fireEvent ('RequestStop'); } else { q.app.fireEvent ('RequestPlay'); } }; if (!PKAudioEditor.engine.wavesurfer.isReady) { play_btn.className += ' pk_inact'; loop_btn.className += ' pk_inact'; } if (PKAudioEditor.engine.wavesurfer.isPlaying()) { play_btn.className += ' pk_act'; } DidStopPlay = function() { is_playing = false; play_btn.classList.remove ('pk_act'); play_btn.innerText = 'Play Track'; }; DidPlay = function() { is_playing = true; play_btn.classList.add ('pk_act'); play_btn.innerText = 'Stop Track'; }; q.app.listenFor ('DidStopPlay', DidStopPlay); q.app.listenFor ('DidPlay', DidPlay); var old_left_time = -999999; var old_right_time = -999999; var peaks = []; var skipp = false; var remaining = 0; DidAudioProcess = function() { //if (skipp) { // skipp = false; // return ; //} //skipp = true; var wv = PKAudioEditor.engine.wavesurfer; var buffer = wv.backend.buffer; var chan_data = buffer.getChannelData ( 0 ); var sample_rate = buffer.sampleRate; var curr_time = wv.getCurrentTime (); var width = 500; var height = 100; var half_height = (height / 2) * 2; var new_width = width; var cached_index = 0; var pixels = 0; var raw_pixels = 0; var limit = 3; var left_time = curr_time - (limit/2); var right_time = curr_time + (limit/2); var quick_render = false; var start_offset = (left_time * sample_rate) >> 0; var end_offset = ((left_time + limit) * sample_rate) >> 0; var length = end_offset - start_offset; var mod = (length / width) >> 0; if (left_time < old_right_time) { // find pixels var diff = right_time - old_right_time; // pixels = Math.round ( (diff / limit) * width); raw_pixels = ( (diff / limit) * width); pixels = Math.round ( raw_pixels ); raw_pixels = ((raw_pixels*1000) >> 0)/1000; if (pixels >= 0) { if (pixels === 0) return ; new_width = pixels; start_offset = (old_right_time * sample_rate) >> 0; end_offset = (right_time * sample_rate) >> 0; length = end_offset - start_offset; mod = (length / pixels) >> 0; peaks = peaks.slice (pixels * 2); cached_index = width - pixels; quick_render = true; } } old_right_time = right_time; 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 + cached_index)] = max; peaks[2 * (i + cached_index) + 1 ] = min; } if (quick_render) { // var imgdata = ctx.getImageData(0, 0, width, height); // tempCtx.putImageData (imgdata, 0, 0); tempCtx.drawImage (canvas, 0, 0); //, width, height, 0, 0, width, height); } ctx.fillStyle = "#000"; // ctx.clearRect( 0, 0, width, height ); ctx.fillRect ( 0, 0, width * 2, height * 2 ); ctx.fillStyle = '#99c2c6'; if (quick_render) { var forward = Math.round (raw_pixels * 2); remaining += forward - raw_pixels * 2; if (remaining > 1) { forward -= 1; remaining = 0; } // ctx.translate(-1.5, 0); ctx.translate (-forward, 0); ctx.drawImage (tempCanvas, 0, 0); //, width, height, 0, 0, width, height); ctx.setTransform (1, 0, 0, 1, 0, 0); // ctx.drawImage (tempCanvas, 0, 0, width, 100, -(raw_pixels.toFixed(1)/1), 0, width, 100); ctx.beginPath (); var peak = peaks[ (width - pixels - 2) * 2]; var _h = Math.round (peak * half_height); ctx.moveTo ( (width - pixels - 2) * 2, half_height - _h); for (var i = (width - pixels - 1); i < width; ++i) { peak = peaks[i * 2]; _h = Math.round (peak * half_height); ctx.lineTo ( i* 2, half_height - _h); } for (var i = width - 1; i >= (width - pixels - 1); --i) { var peak = peaks[ (i * 2) + 1]; var _h = Math.round (peak * half_height); ctx.lineTo ( i* 2, half_height - _h); } ctx.closePath(); ctx.fill(); } else { 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 * 2, 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 * 2, half_height - _h); } ctx.closePath(); ctx.fill(); } //console.log( peaks ); }; q.app.listenFor ('DidAudioProcess', DidAudioProcess); if (PKAudioEditor.engine.wavesurfer.regions.list[0]) { if (PKAudioEditor.engine.wavesurfer.regions.list[0].loop) loop_btn.className += ' pk_act'; } loop_btn.onclick = function() { q.app.fireEvent('RequestSetLoop'); }; DidSetLoop = function( val ) { val ? loop_btn.classList.add('pk_act') : loop_btn.classList.remove('pk_act'); }; q.app.listenFor('DidSetLoop', DidSetLoop); tap_graph.parentNode.addEventListener ('transitionend', function ( e ) { if (!tap_graph) return ; var el = e.target; if (el.tagName !== 'DIV') return ; el.parentNode.removeChild ( el ); --steps_count; if (steps_count === 0) { if (is_playing) setTimeout(function() { if (steps_count === 0) _reset_count (); },1100); else _reset_count (); } }); tap_area.onclick = function ( ev ) { if (ev) { ev.preventDefault (); ev.stopPropagation (); } if (first) { first = false; tap_msg[1].style.opacity = '0'; } time_msec = Date.now (); if ((time_msec - time_msec_prev) > reset_wait) { count = 0; } if (count === 0) { time_msec_first = time_msec; count = 1; bpm_el.value = 'First Beat'; bpm_el_round.value = 'First Beat'; bpm_el_count.value = count; } else { bpm = 60000 * count / (time_msec - time_msec_first); ++count; bpm_el.value = Math.round (bpm * 100) / 100; bpm_el_round.value = Math.round (bpm); bpm_el_count.value = count; } var step = document.createElement ('div'); step.className = 'pk_obj'; if (is_playing) { canvas.parentNode.appendChild (step); } else { tap_graph.appendChild (step); } ++steps_count; tap_area.classList.add ('pk_act'); requestAnimationFrame(function() { step.style.transform = 'translate3d(-10%,0,0)'; setTimeout(function() { tap_area.classList.remove ('pk_act'); },56); }); time_msec_prev = time_msec; }; app.ui.KeyHandler.addCallback ('tmpTap', function ( e, o, ev ) { if (!app.ui.InteractionHandler.check (modal_name)) return ; ev.preventDefault (); ev.stopPropagation (); tap_area.onclick (null); }, [32]); // --- }; }; // events var TempoEstimation = function ( app, modal ) { var q = this; q.app = app; q.Init = function ( container ) { var q = this; q.el = container; _make_ui ( q ); _make_evs ( q ); }; q.Destroy = function () { if (q.body) { q.body.parentNode.removeChild ( q.body ); q.body = null; } q.app = null; }; q.Est = function ( selection ) { var q = this; var wavesurfer = q.app.engine.wavesurfer; var buffer = wavesurfer.backend.buffer; var starting_time = 20.375; var ending_time = wavesurfer.getDuration (); var sample_rate = buffer.sampleRate; var look_ahead = 10 * sample_rate; var offset_rate = starting_time * sample_rate; var duration_rate = ending_time * sample_rate; var dist_rhythm = {}; // now run offline var audio_ctx = getOfflineAudioContext ( 1, buffer.sampleRate, buffer.length ); var source = audio_ctx.createBufferSource (); source.buffer = buffer; var filter = audio_ctx.createBiquadFilter (); filter.type = 'highpass'; filter.frequency.value = 50; filter.Q.value = 1.1; source.connect (filter); var filter2 = audio_ctx.createBiquadFilter (); filter2.type = 'lowpass'; filter2.frequency.value = 140; filter2.Q.value = 2.5; filter.connect (filter2); filter2.connect (audio_ctx.destination); source.start (0); var offline_callback = function( rendered_buffer ) { _pass ( rendered_buffer, offset_rate, duration_rate ); }; var _pass = function ( rendered_buffer, offset, duration ) { var chan_data = rendered_buffer.getChannelData ( 0 ); var new_arr = []; var diff_arr = []; var currval = 0; var prev_val = 0; var bottom = 100000; var top = -100000; var found_pick = false; var going_up = false; var peak_dist = 0; var peak_prev = 0; var next_offset = offset + look_ahead; var trimmed_arr = []; var modulus_coefficient = Math.round (look_ahead / 200); var plus_one = look_ahead + modulus_coefficient; for (var i = 0; i < plus_one; ++i) { if (i % modulus_coefficient === 0) { // look into 50 neighboring entries for higher values. var val_clean = chan_data[ offset + i ]; var val = Math.abs (val_clean); //console.log( "was ", val_clean ); var tmp_val = 0; for (var uu = 1; uu < 50; ++uu) { tmp_val = Math.abs (chan_data[ offset + i - uu ]); if (tmp_val > val) { val_clean = chan_data[ offset + i - uu ]; val = Math.abs (val_clean); } } for (var uu = 1; uu < 50; ++uu) { tmp_val = Math.abs (chan_data[ offset + i + uu ]); if (tmp_val > val) { val_clean = chan_data[ offset + i + uu ]; val = Math.abs (val_clean); } } //console.log( "added ", val_clean ); //console.log("-----"); trimmed_arr.push ( val_clean ); } } trimmed_arr = _normalize_array2 (trimmed_arr); trimmed_arr.pop (); // ------------ prev_val = trimmed_arr[0]; for (var j = 1; j < trimmed_arr.length; ++j) { currval = trimmed_arr[j]; if (currval > prev_val) { if (!going_up) { if (bottom > prev_val) { bottom = prev_val; top = -100000; } } going_up = true; } else if (currval < prev_val ) { if (going_up) { // console.log (":: peak: ", prev_val.toFixed(2)/1, " bottom: ", bottom.toFixed(2)/1, " diff: ", Math.abs(prev_val-bottom).toFixed(2)/1 ); found_pick = true; if (peak_dist < 3 && Math.abs (new_arr[peak_prev] - prev_val) < 150) { // debugger; if (prev_val > new_arr[peak_prev]) { new_arr[peak_prev] = 0; diff_arr.pop (); } else { found_pick = false; } } if (found_pick) { diff_arr.push (Math.abs (prev_val - bottom)); peak_dist = 0; new_arr.push ( prev_val ); peak_prev = new_arr.length - 1; if (prev_val > top) { top = prev_val; bottom = 100000; } } // ----- } going_up = false; } prev_val = currval; if (!found_pick) { new_arr.push ( 0 ); //console.log( "ZEROED ", new_arr.length - 1 ); ++peak_dist; } else { found_pick = false; } } // console.log( trimmed_arr ); // console.log( new_arr ); // window.trimmed_arr = trimmed_arr; // window.new_arr = new_arr; // window.chan = chan_data; // ---- var ret = _group_rhythm ( new_arr, diff_arr ); if (!ret) { console.log ("something weird happened, error 244"); return ; } var distances = ret[0]; // console.log( diff_arr ); // console.log( ret[1] ); for (var k in distances) { if (!dist_rhythm[ k ]) dist_rhythm[ k ] = 0; dist_rhythm[ k ] += distances[ k ]; } console.log ( distances ); // console.log( ' ---------------- ' ); // console.log ('--------- END OF PASS ------- ', offset, ' / ', duration); if (next_offset + look_ahead >= duration) { // Done... console.log ( dist_rhythm ); } else { // _pass ( rendered_buffer, next_offset, duration ); } // ---- }; 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 ); }; }; function _make_ui ( q ) { var el_drawer = d.createElement ('div'); el_drawer.className = 'pk_row'; // Estimate tempo for selected area button el_drawer.innerHTML = '
' + ''+ ''+ ''+ '
' + '
' + 'Estimate'+ '
'; q.body = el_drawer; q.el.appendChild ( el_drawer ); }; function _make_evs ( q ) { var btn_est = q.body.getElementsByTagName ('a')[0]; if (!btn_est) return ; btn_est.onclick = function () { q.Est && q.Est (1); // q.app && q.app.fireEvent ('ReqEst', 1); }; }; }; var x = new PKAudioFXModal ({ id: filter_id, title: 'Tempo & Rhythm Tools', ondestroy: function ( q ) { app.ui.InteractionHandler.on = false; app.ui.KeyHandler.removeCallback (modal_esc_key); act_tool.Destroy (); act_tool = null; app.fireEvent ('RequestStop'); }, body: '
' + 'Tempo Estimation' + 'Tempo Tap' + 'Metronome
', // buttons: [{ // title:'Apply EQ', // clss:'pk_modal_a_accpt', // callback: function( q ) { // q.Destroy (); // } // }], setup:function( q ) { var toplinks = q.el_body.getElementsByClassName('pk_tbsa'); var destroy = function () { if (act_tool) { act_tool.Destroy (); act_tool = null; toplinks[act_index].classList.remove('pk_act'); } }; var activate = function () { // get the active state if (act_index === 0) { // toplinks[0].className += ' pk_act'; // act_tool = new TempoEstimation ( app, q ); return ; } else if (act_index === 1) { toplinks[1].className += ' pk_act'; act_tool = new TempoTap ( app, q ); } else if (act_index === 2) { toplinks[2].className += ' pk_act'; act_tool = new TempoMetro ( app, q ); } act_tool && act_tool.Init ( q.el_body ); }; //toplinks[0].onclick = function() { // destroy (); // act_index = 0; // activate (); //}; toplinks[1].onclick = function() { if (act_index === 1) return ; destroy (); act_index = 1; activate (); }; toplinks[2].onclick = function() { if (act_index === 2) return ; destroy (); act_index = 2; activate (); }; activate (); // --- app.fireEvent ('RequestPause'); app.ui.InteractionHandler.checkAndSet (modal_name); app.ui.KeyHandler.addCallback (modal_esc_key, function ( e ) { if (!app.ui.InteractionHandler.check (modal_name)) return ; q.Destroy (); }, [27]); } }, app); x.Show (); // ------ }; PKAudioEditor._deps.FxTMP = TempoToolsModal; /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// var RecModal = function ( app ) { var filter_id = 'rec_tools'; var audio_stream = null; var audio_context = null; var script_processor = null; var media_stream_source = null; var temp_buffers = [] var newbuff = null; var sample_rate = 44100; var buffer_size = 2048; // * 2 ? var channel_num = 1; var channel_num_out = 1; var stop_audio = function () { if (!audio_stream) return ; audio_stream.getTracks().forEach(function (stream) { stream.stop (); }); if (script_processor) { script_processor.onaudioprocess = null; } media_stream_source && media_stream_source.disconnect (); script_processor && script_processor.disconnect (); media_stream_source = null; audio_stream = null; audio_context = null; }; var x = new PKAudioFXModal ({ id: filter_id, title: 'New Recording', ondestroy: function ( q ) { // destroy audio... stop_audio (); temp_buffers = []; newbuff = null; app.ui.InteractionHandler.on = false; app.ui.KeyHandler.removeCallback (modal_esc_key); app.fireEvent ('RequestStop'); }, body: '
' + '
' + '' + '' + '
' + '
' + '
' + '
' + '
' + '0.0
' + '
' + '
'+ '
' + '
' + 'START RECORDING' + 'PAUSE' + '
' + '
' + '' + '' + '
' + '
', // buttons: [{ // title:'Apply EQ', // clss:'pk_modal_a_accpt', // callback: function( q ) { // q.Destroy (); // } // }], setup:function( q ) { var is_ready = false; var is_active = false; var is_paused = false; var has_recorded = false; var mainbtns = q.el_body.getElementsByClassName('pk_tbsa'); var btn_start = mainbtns[0]; var btn_pause = mainbtns[1]; var btn_open = mainbtns[2]; var btn_add = mainbtns[3]; var time_span = q.el_body.getElementsByTagName('span')[0]; var devices_sel = q.el_body.getElementsByTagName('select')[0]; var devices = []; var volcanvas = q.el_body.getElementsByTagName('canvas')[0]; var volctx = volcanvas.getContext('2d', {alpha:false,antialias:false}); var freqcanvas = q.el_body.getElementsByTagName('canvas')[1]; var freqctx = freqcanvas.getContext('2d', {alpha:false,antialias:false}); var tempCanvas = document.createElement('canvas'); tempCanvas.width = 500 * 2; tempCanvas.height = 100 * 2; var tempCtx = tempCanvas.getContext ('2d', {alpha:false,antialias:false}); var first_skip = 12; var curr_offset = 0; var temp_buffer_index = -1; var volume = 0; var currtime = 0; var has_devices = false; var old_left_time = -999999; var old_right_time = -999999; var peaks = []; var skipp = false; var remaining = 0; var debounce = false; temp_buffers = []; newbuff = null; var draw_volume = function () { volctx.fillStyle = "#000"; volctx.fillRect(0,0,200,40); if (!is_active) { return ; } volctx.fillStyle = "green"; volctx.fillRect(0, 0, volume*200*1.67, 40); time_span.innerText = ((currtime * 10) >> 0) / 10; window.requestAnimationFrame( draw_volume ); }; var fetchBufferFunction = function (ev) { if (first_skip > 0) { --first_skip; return ; } if (is_paused) { return ; } curr_offset += ev.inputBuffer.duration * sample_rate; var float_array = ev.inputBuffer.getChannelData (0).slice (0); temp_buffers[ ++temp_buffer_index ] = float_array; var sum = 0; var x; for (var i = 0; i < buffer_size; i += 2) { x = float_array[i]; sum += x * x; } var rms = Math.sqrt(sum / (buffer_size / 2) ); volume = Math.max(rms, volume * 0.9); var curr_time = (temp_buffer_index * buffer_size) / sample_rate; currtime = curr_time; var width = 500; var height = 100; var half_height = (height / 2) * 2; var new_width = width; var cached_index = 0; var pixels = 0; var raw_pixels = 0; var limit = 3; var left_time = curr_time - limit; var right_time = curr_time; // + (limit/2); var quick_render = false; var start_offset = (left_time * sample_rate) >> 0; var end_offset = ((left_time + limit) * sample_rate) >> 0; var length = end_offset - start_offset; var mod = (length / width) >> 0; if (left_time < old_right_time) { // find pixels var diff = right_time - old_right_time; // pixels = Math.round ( (diff / limit) * width); raw_pixels = ( (diff / limit) * width); pixels = Math.round ( raw_pixels ); raw_pixels = ((raw_pixels*1000) >> 0)/1000; if (pixels >= 0) { if (pixels === 0) return ; new_width = pixels; start_offset = (old_right_time * sample_rate) >> 0; end_offset = (right_time * sample_rate) >> 0; length = end_offset - start_offset; mod = (length / pixels) >> 0; peaks = peaks.slice (pixels * 2); cached_index = width - pixels; quick_render = true; } } old_right_time = right_time; 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) { var temp = new_offset + j; var temp2 = (temp/2048) >> 0; var temp3 = temp % 2048; if (!temp_buffers[temp2]) continue; if ( temp_buffers[temp2][ temp3 ] > max ) { max = temp_buffers[temp2][ temp3 ]; } else if ( temp_buffers[temp2][ temp3 ] < min ) { min = temp_buffers[temp2][ temp3 ]; } } } peaks[2 * (i + cached_index)] = max; peaks[2 * (i + cached_index) + 1 ] = min; } if (quick_render) { // var imgdata = ctx.getImageData(0, 0, width, height); // tempCtx.putImageData (imgdata, 0, 0); tempCtx.drawImage (freqcanvas, 0, 0); //, width, height, 0, 0, width, height); } freqctx.fillStyle = "#000"; // freqctx.clearRect( 0, 0, width, height ); freqctx.fillRect ( 0, 0, width * 2, height * 2 ); freqctx.fillStyle = '#99c2c6'; if (quick_render) { var forward = Math.round (raw_pixels * 2); remaining += forward - raw_pixels * 2; if (remaining > 1) { forward -= 1; remaining = 0; } // freqctx.translate(-1.5, 0); freqctx.translate (-forward, 0); freqctx.drawImage (tempCanvas, 0, 0); //, width, height, 0, 0, width, height); freqctx.setTransform (1, 0, 0, 1, 0, 0); // freqctx.drawImage (tempCanvas, 0, 0, width, 100, -(raw_pixels.toFixed(1)/1), 0, width, 100); freqctx.beginPath (); var peak = peaks[ (width - pixels - 2) * 2]; var _h = Math.round (peak * half_height); freqctx.moveTo ( (width - pixels - 2) * 2, half_height - _h); for (var i = (width - pixels - 1); i < width; ++i) { peak = peaks[i * 2]; _h = Math.round (peak * half_height); freqctx.lineTo ( i* 2, half_height - _h); } for (var i = width - 1; i >= (width - pixels - 1); --i) { var peak = peaks[ (i * 2) + 1]; var _h = Math.round (peak * half_height); freqctx.lineTo ( i* 2, half_height - _h); } freqctx.closePath(); freqctx.fill(); } else { freqctx.beginPath (); freqctx.moveTo ( 0, half_height ); for (var i = 0; i < width; ++i) { var peak = peaks[i * 2]; var _h = Math.round (peak * half_height); freqctx.lineTo ( i * 2, half_height - _h); } for (var i = width - 1; i >= 0; --i) { var peak = peaks[ (i * 2) + 1]; var _h = Math.round (peak * half_height); freqctx.lineTo ( i * 2, half_height - _h); } freqctx.closePath(); freqctx.fill(); } }; navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(function( _stream ) { _stream.getTracks().forEach(function (stream) { stream.stop (); }); enumerate (); }).catch(function(error) { alert("no microphone permissions found!"); }); var enumerate = function () { if (navigator.mediaDevices.enumerateDevices) { navigator.mediaDevices.enumerateDevices().then((devices) => { devices = devices.filter((d) => d.kind === 'audioinput'); has_devices = true; var len = devices.length; for (var i = 0; i < len; ++i) { var el = document.createElement('option'); el.value = devices[i].deviceId; el.innerText = devices[i].label; devices_sel.appendChild (el); } is_ready = true; btn_start.classList.remove ('pk_inact'); }); } else { devices_sel.parentNode.style.display = 'none'; has_devices = false; is_ready = true; btn_start.classList.remove ('pk_inact'); } }; var stop = function () { stop_audio (); is_active = false; is_paused = false; first_skip = 10; ++temp_buffer_index; var k = -1; newbuff = new Float32Array (temp_buffer_index * buffer_size); for (var i = 0; i < temp_buffer_index; ++i) { for (var j = 0; j < buffer_size; ++j) { newbuff[++k] = temp_buffers[i][j]; } } temp_buffer_index = -1; temp_buffers = []; // ------ btn_open.style.display = 'block'; // check to see if we are ready if (app.engine.is_ready) { btn_add.style.display = 'block'; } has_recorded = true; }; // --- btn_start.onclick = function () { if (!is_ready) return ; if (debounce) { return ; } debounce = true; setTimeout(function() { debounce = false; }, 260); // check if recording exists - ask for confirmation if (has_recorded) { if (!window.confirm("Are you sure? This will discard the current recording.")) { return ; } } if (is_active) { stop (); btn_pause.classList.add ('pk_inact'); btn_start.innerText = 'START RECORDING'; btn_start.style.boxShadow = 'none'; return ; } temp_buffer_index = -1; temp_buffers = []; newbuff = null; volume = 0; btn_open.style.display = 'none'; btn_add.style.display = 'none'; audio_context = new (window.AudioContext || window.webkitAudioContext)(); sample_rate = audio_context.sampleRate; var audio_val = true; if (has_devices) { audio_val = {deviceId: devices_sel.value}; // devices_sel.options[devices_sel.selectedIndex].value; } navigator.mediaDevices.getUserMedia({ audio: audio_val }).then(function( stream ) { audio_stream = stream; media_stream_source = audio_context.createMediaStreamSource ( stream ); script_processor = audio_context.createScriptProcessor ( buffer_size, channel_num, channel_num_out ); media_stream_source.connect ( script_processor ); script_processor.connect ( audio_context.destination ); is_active = true; btn_pause.classList.remove ('pk_inact'); btn_start.innerText = 'FINISH RECORDING'; btn_start.style.boxShadow = '#992222 0px 0px 6px inset'; script_processor.onaudioprocess = fetchBufferFunction; draw_volume (); }).catch(function(error) { }); }; btn_pause.onclick = function () { if (!is_ready) return ; if (!is_active) return ; is_paused = !is_paused; btn_pause.innerText = is_paused ? 'UN-PAUSE' : 'PAUSE'; }; btn_open.onclick = function () { if (debounce) { return ; } debounce = true; setTimeout(function() { debounce = false; }, 150); app.engine.wavesurfer.backend._add = 0; app.engine.LoadDB ({ samplerate: sample_rate, data: [ newbuff.buffer ] }); // ---- q.Destroy (); }; btn_add.onclick = function () { if (debounce) { return ; } debounce = true; setTimeout(function() { debounce = false; }, 150); app.engine.wavesurfer.backend._add = 1; app.engine.LoadDB ({ samplerate: sample_rate, data: [ newbuff.buffer ] }); // ---- q.Destroy (); }; // --- app.fireEvent ('RequestPause'); app.ui.InteractionHandler.checkAndSet (modal_name); app.ui.KeyHandler.addCallback (modal_esc_key, function ( e ) { if (!app.ui.InteractionHandler.check (modal_name)) return ; q.Destroy (); }, [27]); } }, app); x.Show (); }; PKAudioEditor._deps.FxREC = RecModal; })( window, document, PKAudioEditor );