Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save joelkesler/960c5bffd5587ee285e1421448a62b2c to your computer and use it in GitHub Desktop.
Save joelkesler/960c5bffd5587ee285e1421448a62b2c to your computer and use it in GitHub Desktop.
Accent Multitrack Player - Let it Snow
<div id="jcmix"
data-title="Let It Snow"
data-arranger="Accent"
data-record="false"
>
<div class="track" data-url="https://www.dl.dropboxusercontent.com/s/53gftpxoayi5uzd/01%20Let%20It%20Snow%21%20-%20DRUMS.mp3?dl=0"
data-label="Drums"
data-label-mobile="Dr"
data-start-muted="false"></div>
<div class="track" data-url="https://www.dl.dropboxusercontent.com/s/wszvp6m45k5qr4t/02%20Let%20It%20Snow%21%20-%20BASS.mp3?dl=0"
data-label="Bass"
data-label-mobile="Bs"
data-start-muted="false"></div>
<div class="track" data-url="https://www.dl.dropboxusercontent.com/s/xfe7ze5dyxfl5lb/03%20Let%20It%20Snow%21%20-%20GUITAR.mp3?dl=0"
data-label="Guitar"
data-label-mobile="G"
data-start-muted="false"></div>
<div class="track" data-url="https://www.dl.dropboxusercontent.com/s/dcsiy6to5hpx12x/04%20Let%20It%20Snow%21%20-%20PIANO.mp3?dl=0"
data-label="Piano"
data-label-mobile="P"
data-start-muted="false"></div>
<div class="track" data-url="https://www.dl.dropboxusercontent.com/s/m5cmhzkcmfbrtoz/05%20Let%20It%20Snow%21%20-%20TROMBONES.mp3?dl=0"
data-label="Trombones"
data-label-mobile="Tbn"
data-start-muted="false"></div>
<div class="track"
data-url="https://www.dl.dropboxusercontent.com/s/xo17h1l0vk4bdpb/06%20Let%20It%20Snow%21%20-%20TRUMPETS.mp3?dl=0"
data-label="Trumpets"
data-label-mobile="Tpt"
data-start-muted="false"></div>
<div class="track" data-url="https://www.dl.dropboxusercontent.com/s/s0k76vt4j1uz6yw/07%20Let%20It%20Snow%21%20-%20SAXES.mp3?dl=0"
data-label="Saxes"
data-label-mobile="Sx"
data-start-muted="false"></div>
<div class="track" data-url="https://www.dl.dropboxusercontent.com/s/epska9pd3k010hs/08%20Let%20It%20Snow%21%20-%20ROOMS.mp3?dl=0"
data-label="Room Mics"
data-label-mobile="Rm"
data-start-muted="false"></div>
<div class="track" data-url="https://www.dl.dropboxusercontent.com/s/2t2mnqutnvqpch0/09%20Let%20It%20Snow%21%20-%20VOCALS.mp3?dl=0"
data-label="Vocals"
data-label-mobile="V"
data-start-muted="false"></div>
<div class="track" data-url="https://www.dl.dropboxusercontent.com/s/tmmg5cnk5nwvo6m/10%20Let%20It%20Snow%21%20-%20ARTURO.mp3?dl=0"
data-label="Arturo"
data-start-muted="false"></div>
</div>
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Recorder = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
"use strict";
module.exports = require("./recorder").Recorder;
},{"./recorder":2}],2:[function(require,module,exports){
'use strict';
var _createClass = (function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);
}
}return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
};
})();
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Recorder = undefined;
var _inlineWorker = require('inline-worker');
var _inlineWorker2 = _interopRequireDefault(_inlineWorker);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var Recorder = exports.Recorder = (function () {
function Recorder(source, cfg) {
var _this = this;
_classCallCheck(this, Recorder);
this.config = {
bufferLen: 4096,
numChannels: 2,
mimeType: 'audio/wav'
};
this.recording = false;
this.callbacks = {
getBuffer: [],
exportWAV: []
};
Object.assign(this.config, cfg);
this.context = source.context;
this.node = (this.context.createScriptProcessor || this.context.createJavaScriptNode).call(this.context, this.config.bufferLen, this.config.numChannels, this.config.numChannels);
this.node.onaudioprocess = function (e) {
if (!_this.recording) return;
var buffer = [];
for (var channel = 0; channel < _this.config.numChannels; channel++) {
buffer.push(e.inputBuffer.getChannelData(channel));
}
_this.worker.postMessage({
command: 'record',
buffer: buffer
});
};
source.connect(this.node);
this.node.connect(this.context.destination); //this should not be necessary
var self = {};
this.worker = new _inlineWorker2.default(function () {
var recLength = 0,
recBuffers = [],
sampleRate = undefined,
numChannels = undefined;
self.onmessage = function (e) {
switch (e.data.command) {
case 'init':
init(e.data.config);
break;
case 'record':
record(e.data.buffer);
break;
case 'exportWAV':
exportWAV(e.data.type);
break;
case 'getBuffer':
getBuffer();
break;
case 'clear':
clear();
break;
}
};
function init(config) {
sampleRate = config.sampleRate;
numChannels = config.numChannels;
initBuffers();
}
function record(inputBuffer) {
for (var channel = 0; channel < numChannels; channel++) {
recBuffers[channel].push(inputBuffer[channel]);
}
recLength += inputBuffer[0].length;
}
function exportWAV(type) {
var buffers = [];
for (var channel = 0; channel < numChannels; channel++) {
buffers.push(mergeBuffers(recBuffers[channel], recLength));
}
var interleaved = undefined;
if (numChannels === 2) {
interleaved = interleave(buffers[0], buffers[1]);
} else {
interleaved = buffers[0];
}
var dataview = encodeWAV(interleaved);
var audioBlob = new Blob([dataview], { type: type });
self.postMessage({ command: 'exportWAV', data: audioBlob });
}
function getBuffer() {
var buffers = [];
for (var channel = 0; channel < numChannels; channel++) {
buffers.push(mergeBuffers(recBuffers[channel], recLength));
}
self.postMessage({ command: 'getBuffer', data: buffers });
}
function clear() {
recLength = 0;
recBuffers = [];
initBuffers();
}
function initBuffers() {
for (var channel = 0; channel < numChannels; channel++) {
recBuffers[channel] = [];
}
}
function mergeBuffers(recBuffers, recLength) {
var result = new Float32Array(recLength);
var offset = 0;
for (var i = 0; i < recBuffers.length; i++) {
result.set(recBuffers[i], offset);
offset += recBuffers[i].length;
}
return result;
}
function interleave(inputL, inputR) {
var length = inputL.length + inputR.length;
var result = new Float32Array(length);
var index = 0,
inputIndex = 0;
while (index < length) {
result[index++] = inputL[inputIndex];
result[index++] = inputR[inputIndex];
inputIndex++;
}
return result;
}
function floatTo16BitPCM(output, offset, input) {
for (var i = 0; i < input.length; i++, offset += 2) {
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
}
function writeString(view, offset, string) {
for (var i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}
function encodeWAV(samples) {
var buffer = new ArrayBuffer(44 + samples.length * 2);
var view = new DataView(buffer);
/* RIFF identifier */
writeString(view, 0, 'RIFF');
/* RIFF chunk length */
view.setUint32(4, 36 + samples.length * 2, true);
/* RIFF type */
writeString(view, 8, 'WAVE');
/* format chunk identifier */
writeString(view, 12, 'fmt ');
/* format chunk length */
view.setUint32(16, 16, true);
/* sample format (raw) */
view.setUint16(20, 1, true);
/* channel count */
view.setUint16(22, numChannels, true);
/* sample rate */
view.setUint32(24, sampleRate, true);
/* byte rate (sample rate * block align) */
view.setUint32(28, sampleRate * 4, true);
/* block align (channel count * bytes per sample) */
view.setUint16(32, numChannels * 2, true);
/* bits per sample */
view.setUint16(34, 16, true);
/* data chunk identifier */
writeString(view, 36, 'data');
/* data chunk length */
view.setUint32(40, samples.length * 2, true);
floatTo16BitPCM(view, 44, samples);
return view;
}
}, self);
this.worker.postMessage({
command: 'init',
config: {
sampleRate: this.context.sampleRate,
numChannels: this.config.numChannels
}
});
this.worker.onmessage = function (e) {
var cb = _this.callbacks[e.data.command].pop();
if (typeof cb == 'function') {
cb(e.data.data);
}
};
}
_createClass(Recorder, [{
key: 'record',
value: function record() {
this.recording = true;
}
}, {
key: 'stop',
value: function stop() {
this.recording = false;
}
}, {
key: 'clear',
value: function clear() {
this.worker.postMessage({ command: 'clear' });
}
}, {
key: 'getBuffer',
value: function getBuffer(cb) {
cb = cb || this.config.callback;
if (!cb) throw new Error('Callback not set');
this.callbacks.getBuffer.push(cb);
this.worker.postMessage({ command: 'getBuffer' });
}
}, {
key: 'exportWAV',
value: function exportWAV(cb, mimeType) {
mimeType = mimeType || this.config.mimeType;
cb = cb || this.config.callback;
if (!cb) throw new Error('Callback not set');
this.callbacks.exportWAV.push(cb);
this.worker.postMessage({
command: 'exportWAV',
type: mimeType
});
}
}], [{
key: 'forceDownload',
value: function forceDownload(blob, filename) {
var url = (window.URL || window.webkitURL).createObjectURL(blob);
var link = window.document.createElement('a');
link.href = url;
link.download = filename || 'output.wav';
var click = document.createEvent("Event");
click.initEvent("click", true, true);
link.dispatchEvent(click);
}
}]);
return Recorder;
})();
exports.default = Recorder;
},{"inline-worker":3}],3:[function(require,module,exports){
"use strict";
module.exports = require("./inline-worker");
},{"./inline-worker":4}],4:[function(require,module,exports){
(function (global){
"use strict";
var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
var WORKER_ENABLED = !!(global === global.window && global.URL && global.Blob && global.Worker);
var InlineWorker = (function () {
function InlineWorker(func, self) {
var _this = this;
_classCallCheck(this, InlineWorker);
if (WORKER_ENABLED) {
var functionBody = func.toString().trim().match(/^function\s*\w*\s*\([\w\s,]*\)\s*{([\w\W]*?)}$/)[1];
var url = global.URL.createObjectURL(new global.Blob([functionBody], { type: "text/javascript" }));
return new global.Worker(url);
}
this.self = self;
this.self.postMessage = function (data) {
setTimeout(function () {
_this.onmessage({ data: data });
}, 0);
};
setTimeout(function () {
func.call(self);
}, 0);
}
_createClass(InlineWorker, {
postMessage: {
value: function postMessage(data) {
var _this = this;
setTimeout(function () {
_this.self.onmessage({ data: data });
}, 0);
}
}
});
return InlineWorker;
})();
module.exports = InlineWorker;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}]},{},[1])(1)
});
(function(e){if(typeof define==="function"&&define.amd){define(["jquery"],e)}else{e(jQuery)}})(function(e){"use strict";var t={},n=Math.max,r=Math.min;t.c={};t.c.d=e(document);t.c.t=function(e){return e.originalEvent.touches.length-1};t.o=function(){var n=this;this.o=null;this.$=null;this.i=null;this.g=null;this.v=null;this.cv=null;this.x=0;this.y=0;this.w=0;this.h=0;this.$c=null;this.c=null;this.t=0;this.isInit=false;this.fgColor=null;this.pColor=null;this.dH=null;this.cH=null;this.eH=null;this.rH=null;this.scale=1;this.relative=false;this.relativeWidth=false;this.relativeHeight=false;this.$div=null;this.run=function(){var t=function(e,t){var r;for(r in t){n.o[r]=t[r]}n._carve().init();n._configure()._draw()};if(this.$.data("kontroled"))return;this.$.data("kontroled",true);this.extend();this.o=e.extend({min:this.$.data("min")!==undefined?this.$.data("min"):0,max:this.$.data("max")!==undefined?this.$.data("max"):100,stopper:true,readOnly:this.$.data("readonly")||this.$.attr("readonly")==="readonly",cursor:this.$.data("cursor")===true&&30||this.$.data("cursor")||0,thickness:this.$.data("thickness")&&Math.max(Math.min(this.$.data("thickness"),1),.01)||.35,lineCap:this.$.data("linecap")||"butt",width:this.$.data("width")||200,height:this.$.data("height")||200,displayInput:this.$.data("displayinput")==null||this.$.data("displayinput"),displayPrevious:this.$.data("displayprevious"),fgColor:this.$.data("fgcolor")||"#87CEEB",inputColor:this.$.data("inputcolor"),font:this.$.data("font")||"Arial",fontWeight:this.$.data("font-weight")||"bold",inline:false,step:this.$.data("step")||1,rotation:this.$.data("rotation"),draw:null,change:null,cancel:null,release:null,format:function(e){return e},parse:function(e){return parseFloat(e)}},this.o);this.o.flip=this.o.rotation==="anticlockwise"||this.o.rotation==="acw";if(!this.o.inputColor){this.o.inputColor=this.o.fgColor}if(this.$.is("fieldset")){this.v={};this.i=this.$.find("input");this.i.each(function(t){var r=e(this);n.i[t]=r;n.v[t]=n.o.parse(r.val());r.bind("change blur",function(){var e={};e[t]=r.val();n.val(n._validate(e))})});this.$.find("legend").remove()}else{this.i=this.$;this.v=this.o.parse(this.$.val());this.v===""&&(this.v=this.o.min);this.$.bind("change blur",function(){n.val(n._validate(n.o.parse(n.$.val())))})}!this.o.displayInput&&this.$.hide();this.$c=e(document.createElement("canvas")).attr({width:this.o.width,height:this.o.height});this.$div=e('<div style="'+(this.o.inline?"display:inline;":"")+"width:"+this.o.width+"px;height:"+this.o.height+"px;"+'"></div>');this.$.wrap(this.$div).before(this.$c);this.$div=this.$.parent();if(typeof G_vmlCanvasManager!=="undefined"){G_vmlCanvasManager.initElement(this.$c[0])}this.c=this.$c[0].getContext?this.$c[0].getContext("2d"):null;if(!this.c){throw{name:"CanvasNotSupportedException",message:"Canvas not supported. Please use excanvas on IE8.0.",toString:function(){return this.name+": "+this.message}}}this.scale=(window.devicePixelRatio||1)/(this.c.webkitBackingStorePixelRatio||this.c.mozBackingStorePixelRatio||this.c.msBackingStorePixelRatio||this.c.oBackingStorePixelRatio||this.c.backingStorePixelRatio||1);this.relativeWidth=this.o.width%1!==0&&this.o.width.indexOf("%");this.relativeHeight=this.o.height%1!==0&&this.o.height.indexOf("%");this.relative=this.relativeWidth||this.relativeHeight;this._carve();if(this.v instanceof Object){this.cv={};this.copy(this.v,this.cv)}else{this.cv=this.v}this.$.bind("configure",t).parent().bind("configure",t);this._listen()._configure()._xy().init();this.isInit=true;this.$.val(this.o.format(this.v));this._draw();return this};this._carve=function(){if(this.relative){var e=this.relativeWidth?this.$div.parent().width()*parseInt(this.o.width)/100:this.$div.parent().width(),t=this.relativeHeight?this.$div.parent().height()*parseInt(this.o.height)/100:this.$div.parent().height();this.w=this.h=Math.min(e,t)}else{this.w=this.o.width;this.h=this.o.height}this.$div.css({width:this.w+"px",height:this.h+"px"});this.$c.attr({width:this.w,height:this.h});if(this.scale!==1){this.$c[0].width=this.$c[0].width*this.scale;this.$c[0].height=this.$c[0].height*this.scale;this.$c.width(this.w);this.$c.height(this.h)}return this};this._draw=function(){var e=true;n.g=n.c;n.clear();n.dH&&(e=n.dH());e!==false&&n.draw()};this._touch=function(e){var r=function(e){var t=n.xy2val(e.originalEvent.touches[n.t].pageX,e.originalEvent.touches[n.t].pageY);if(t==n.cv)return;if(n.cH&&n.cH(t)===false)return;n.change(n._validate(t));n._draw()};this.t=t.c.t(e);r(e);t.c.d.bind("touchmove.k",r).bind("touchend.k",function(){t.c.d.unbind("touchmove.k touchend.k");n.val(n.cv)});return this};this._mouse=function(e){var r=function(e){var t=n.xy2val(e.pageX,e.pageY);if(t==n.cv)return;if(n.cH&&n.cH(t)===false)return;n.change(n._validate(t));n._draw()};r(e);t.c.d.bind("mousemove.k",r).bind("keyup.k",function(e){if(e.keyCode===27){t.c.d.unbind("mouseup.k mousemove.k keyup.k");if(n.eH&&n.eH()===false)return;n.cancel()}}).bind("mouseup.k",function(e){t.c.d.unbind("mousemove.k mouseup.k keyup.k");n.val(n.cv)});return this};this._xy=function(){var e=this.$c.offset();this.x=e.left;this.y=e.top;return this};this._listen=function(){if(!this.o.readOnly){this.$c.bind("mousedown",function(e){e.preventDefault();n._xy()._mouse(e)}).bind("touchstart",function(e){e.preventDefault();n._xy()._touch(e)});this.listen()}else{this.$.attr("readonly","readonly")}if(this.relative){e(window).resize(function(){n._carve().init();n._draw()})}return this};this._configure=function(){if(this.o.draw)this.dH=this.o.draw;if(this.o.change)this.cH=this.o.change;if(this.o.cancel)this.eH=this.o.cancel;if(this.o.release)this.rH=this.o.release;if(this.o.displayPrevious){this.pColor=this.h2rgba(this.o.fgColor,"0.4");this.fgColor=this.h2rgba(this.o.fgColor,"0.6")}else{this.fgColor=this.o.fgColor}return this};this._clear=function(){this.$c[0].width=this.$c[0].width};this._validate=function(e){var t=~~((e<0?-.5:.5)+e/this.o.step)*this.o.step;return Math.round(t*100)/100};this.listen=function(){};this.extend=function(){};this.init=function(){};this.change=function(e){};this.val=function(e){};this.xy2val=function(e,t){};this.draw=function(){};this.clear=function(){this._clear()};this.h2rgba=function(e,t){var n;e=e.substring(1,7);n=[parseInt(e.substring(0,2),16),parseInt(e.substring(2,4),16),parseInt(e.substring(4,6),16)];return"rgba("+n[0]+","+n[1]+","+n[2]+","+t+")"};this.copy=function(e,t){for(var n in e){t[n]=e[n]}}};t.Dial=function(){t.o.call(this);this.startAngle=null;this.xy=null;this.radius=null;this.lineWidth=null;this.cursorExt=null;this.w2=null;this.PI2=2*Math.PI;this.extend=function(){this.o=e.extend({bgColor:this.$.data("bgcolor")||"#EEEEEE",angleOffset:this.$.data("angleoffset")||0,angleArc:this.$.data("anglearc")||360,inline:true},this.o)};this.val=function(e,t){if(null!=e){e=this.o.parse(e);if(t!==false&&e!=this.v&&this.rH&&this.rH(e)===false){return}this.cv=this.o.stopper?n(r(e,this.o.max),this.o.min):e;this.v=this.cv;this.$.val(this.o.format(this.v));this._draw()}else{return this.v}};this.xy2val=function(e,t){var i,s;i=Math.atan2(e-(this.x+this.w2),-(t-this.y-this.w2))-this.angleOffset;if(this.o.flip){i=this.angleArc-i-this.PI2}if(this.angleArc!=this.PI2&&i<0&&i>-.5){i=0}else if(i<0){i+=this.PI2}s=i*(this.o.max-this.o.min)/this.angleArc+this.o.min;this.o.stopper&&(s=n(r(s,this.o.max),this.o.min));return s};this.listen=function(){var t=this,i,s,o=function(e){e.preventDefault();var o=e.originalEvent,u=o.detail||o.wheelDeltaX,a=o.detail||o.wheelDeltaY,f=t._validate(t.o.parse(t.$.val()))+(u>0||a>0?t.o.step:u<0||a<0?-t.o.step:0);f=n(r(f,t.o.max),t.o.min);t.val(f,false);if(t.rH){clearTimeout(i);i=setTimeout(function(){t.rH(f);i=null},100);if(!s){s=setTimeout(function(){if(i)t.rH(f);s=null},200)}}},u,a,f=1,l={37:-t.o.step,38:t.o.step,39:t.o.step,40:-t.o.step};this.$.bind("keydown",function(i){var s=i.keyCode;if(s>=96&&s<=105){s=i.keyCode=s-48}u=parseInt(String.fromCharCode(s));if(isNaN(u)){s!==13&&s!==8&&s!==9&&s!==189&&(s!==190||t.$.val().match(/\./))&&i.preventDefault();if(e.inArray(s,[37,38,39,40])>-1){i.preventDefault();var o=t.o.parse(t.$.val())+l[s]*f;t.o.stopper&&(o=n(r(o,t.o.max),t.o.min));t.change(t._validate(o));t._draw();a=window.setTimeout(function(){f*=2},30)}}}).bind("keyup",function(e){if(isNaN(u)){if(a){window.clearTimeout(a);a=null;f=1;t.val(t.$.val())}}else{t.$.val()>t.o.max&&t.$.val(t.o.max)||t.$.val()<t.o.min&&t.$.val(t.o.min)}});this.$c.bind("mousewheel DOMMouseScroll",o);this.$.bind("mousewheel DOMMouseScroll",o)};this.init=function(){if(this.v<this.o.min||this.v>this.o.max){this.v=this.o.min}this.$.val(this.v);this.w2=this.w/2;this.cursorExt=this.o.cursor/100;this.xy=this.w2*this.scale;this.lineWidth=this.xy*this.o.thickness;this.lineCap=this.o.lineCap;this.radius=this.xy-this.lineWidth/2;this.o.angleOffset&&(this.o.angleOffset=isNaN(this.o.angleOffset)?0:this.o.angleOffset);this.o.angleArc&&(this.o.angleArc=isNaN(this.o.angleArc)?this.PI2:this.o.angleArc);this.angleOffset=this.o.angleOffset*Math.PI/180;this.angleArc=this.o.angleArc*Math.PI/180;this.startAngle=1.5*Math.PI+this.angleOffset;this.endAngle=1.5*Math.PI+this.angleOffset+this.angleArc;var e=n(String(Math.abs(this.o.max)).length,String(Math.abs(this.o.min)).length,2)+2;this.o.displayInput&&this.i.css({width:(this.w/2+4>>0)+"px",height:(this.w/3>>0)+"px",position:"absolute","vertical-align":"middle","margin-top":(this.w/3>>0)+"px","margin-left":"-"+(this.w*3/4+2>>0)+"px",border:0,background:"none",font:this.o.fontWeight+" "+(this.w/e>>0)+"px "+this.o.font,"text-align":"center",color:this.o.inputColor||this.o.fgColor,padding:"0px","-webkit-appearance":"none"})||this.i.css({width:"0px",visibility:"hidden"})};this.change=function(e){this.cv=e;this.$.val(this.o.format(e))};this.angle=function(e){return(e-this.o.min)*this.angleArc/(this.o.max-this.o.min)};this.arc=function(e){var t,n;e=this.angle(e);if(this.o.flip){t=this.endAngle+1e-5;n=t-e-1e-5}else{t=this.startAngle-1e-5;n=t+e+1e-5}this.o.cursor&&(t=n-this.cursorExt)&&(n=n+this.cursorExt);return{s:t,e:n,d:this.o.flip&&!this.o.cursor}};this.draw=function(){var e=this.g,t=this.arc(this.cv),n,r=1;e.lineWidth=this.lineWidth;e.lineCap=this.lineCap;if(this.o.bgColor!=="none"){e.beginPath();e.strokeStyle=this.o.bgColor;e.arc(this.xy,this.xy,this.radius,this.endAngle-1e-5,this.startAngle+1e-5,true);e.stroke()}if(this.o.displayPrevious){n=this.arc(this.v);e.beginPath();e.strokeStyle=this.pColor;e.arc(this.xy,this.xy,this.radius,n.s,n.e,n.d);e.stroke();r=this.cv==this.v}e.beginPath();e.strokeStyle=r?this.o.fgColor:this.fgColor;e.arc(this.xy,this.xy,this.radius,t.s,t.e,t.d);e.stroke()};this.cancel=function(){this.val(this.v)}};e.fn.dial=e.fn.knob=function(n){return this.each(function(){var r=new t.Dial;r.o=n;r.$=e(this);r.run()}).parent()}})
//v1.0.3
//31/05/2016
var Jcmix = function() {
//DOM
this.JCmix = $('#jcmix');
this.DOMbody = $('body');
this.DOMhead = $('head');
this.DOMdocument = $(document);
this.playButton; // DOM for transport play button
this.startButton; // DOM for transport back to beginning button
this.resetButton; // DOM for transport reset volume/panning button
this.recordButton; // DOM for transport record button
this.toggleRecordingsButton;
this.clearRecordingsButton;
this.recordinglist;
this.channelStrip; // DOM for channel strip
this.timer; // DOM for timer
this.timerProgress; // DOM for progres time of track
this.timerTotal; // DOM for total time of track
this.transport // DOM for transport pannel
this.timeline; // DOM for transport timeline
this.progressbar; // DOM for transport timeline progress bar
this.percentLoaded; // the ammount loaded (initial load)
this.loader; // the initial loader
this.mixer; // DOM for mixer
////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////
// LITERALS
this.meterHeight = 400; // height of meter in pixels (you also need ot change height var in scss)
this.meterWidth = 10; // height of meter in pixels
this.stylesheetURL = "assets/css/jcmix.min.css"; // Location of stylesheet
// VARS
this.recording = false; // toggle recording
this.allowRecord = false; // toggle recording
this.title;
this.arranger;
this.spotify;
this.rec = false; // recordin gelement
this.loop = 0; // count loops
this.currentLoopProgress = 0; // progress through each loop in milliseconds
this.totalLength = 0; // total length of track
this.timelineClickPaused = false // only allow timeline to be updated once every few seconds
this.replay = false;
this.replayFrom = false;
this.fingerPosition = 0;
this.loading = true; // the app is loading
this.ajaxURL = false;
this.loading = 0;
this.playing = []; // whuich are currently playing
this.context;
this.bufferLoader;
this.javascriptNode = [];
this.leftAnalysers = [];
this.rightAnalysers = [];
this.splitters = [];
this.gainNodes = []; // the gain nodes themselves
this.pannerNodes = []; // the gain nodes themselves
this.gainValues = []; // gain value to resume to after unmute
this.muted = []; // bool if track is muted
this.soloed = []; // bool if track is soloed
this.timelinedrag = false; // don't update timeline if dragging progress bar
this.masterGainNodes;
this.initialMutes = {}; // map of track index to initial mute value
this.initialPans = {}; // map of track index to initial pan value
this.initialVols = {}; // map of track index to initial volume value
this.urlList = []; // The list of tracks to be loaded
this.bufferList = [];
this.loadCount = 0; // the number of tracks loaded
this.startedAt; // When did the track start playing
this.paused = true; // is the track paused?
// ARRAYS
this.audios = [];
this.buffer = [];
this.bouncerleft = [];
this.bouncerright = [];
};
Jcmix.prototype = {
/**
* INIT
* Initialization
*
**/
init: function(){
this.setTrackData();
this.addStylesheet();
this.createLoader();
this.buildTracks(); // get user tracks from frontend
this.bindMouseandTouch();
},
getAllUrlParams: function(url) {
// get query string from url (optional) or window
var queryString = url ? url.split('?')[1] : window.location.search.slice(1);
// we'll store the parameters here
var obj = {};
// if query string exists
if (queryString) {
// stuff after # is not part of query string, so get rid of it
queryString = queryString.split('#')[0];
// split our query string into its component parts
var arr = queryString.split('&');
for (var i=0; i<arr.length; i++) {
// separate the keys and the values
var a = arr[i].split('=');
// in case params look like: list[]=thing1&list[]=thing2
var paramNum = undefined;
var paramName = a[0].replace(/\[\d*\]/, function(v) {
paramNum = v.slice(1,-1);
return '';
});
// set parameter value (use 'true' if empty)
var paramValue = typeof(a[1])==='undefined' ? true : a[1];
// (optional) keep case consistent
paramName = paramName.toLowerCase();
paramValue = paramValue.toLowerCase();
// if parameter name already exists
if (obj[paramName]) {
// convert value to array (if still string)
if (typeof obj[paramName] === 'string') {
obj[paramName] = [obj[paramName]];
}
// if no array index number specified...
if (typeof paramNum === 'undefined') {
// put the value on the end of the array
obj[paramName].push(paramValue);
}
// if array index number specified...
else {
// put the value at that index number
obj[paramName][paramNum] = paramValue;
}
}
// if param name doesn't exist yet, set it
else {
obj[paramName] = paramValue;
}
}
}
return obj;
},
/**
* BUILD TRACKS
* build the tracks from the frontend
*
**/
buildTracks: function(){
var that = this;
var count = 0;
this.JCmix.children('.track').each(function(){
count++;
that.urlList.push($(this).attr('data-url'));
if (count == that.JCmix.children('.track').length) {
that.setupAudioContext();
}
});
},
/**
* Build track divs for the selected song and append them to the parent jcmix div
**/
setTrackData: function() {
// this.JCmix.attr('data-record', 'false');
// this.JCmix.attr('data-arranger', 'ARR. BY ANTHONY PULIDO');
// this.JCmix.append(
// ``
// );
},
/**
* SETUP AUDIO CONTEXT
* setup the audio context
*
**/
setupAudioContext: function(){
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
if(window.AudioContext){
this.context = new AudioContext();
this.load();
}else{
var head = document.getElementsByTagName('head')[0];
var nosupport = document.createElement("div");
nosupport.id = "nosupport";
nosupport.innerHTML = "You are using a browser that does not support HTML5 Audio. <br><a href='https://www.google.com/chrome/' target='_blank'>Click here to download Google Chrome, and experience a better web.</a><br><br><a href='#' onclick='closeSupport()'>Close This Message</a>";
document.body.appendChild(nosupport);
}
},
/**
* ADD STYLESHEET
* add in the style sheet to the header
*
**/
addStylesheet: function(){
/*var stylesheet = document.createElement('link');
stylesheet.rel = "stylesheet";
stylesheet.type= "text/css";
stylesheet.href= this.stylesheetURL;
this.DOMhead.append(stylesheet);*/
},
/**
* CREATE LOADER
* create the loader div
*
**/
createLoader: function(){
var loader = "<div class=\"loader\">";
loader += "<div class=\"loader-inner ball-clip-rotate-multiple\">";
loader += "<div></div>";
loader += "<div></div>";
loader += "</div>";
loader += "<p>Loading... <span>0</span>%</p>";
loader += "</div>";
this.JCmix.append(loader); // Aoppend to body
this.percentLoaded = $('.loader p span'); // create the DOM element
this.loader = $('.loader');
},
/**
* HIDE LOADER
* Hide the loading screen once loaded
*
**/
hideLoader: function(){
var that = this;
this.loader.hide()
that.resizeLabels();
that.mixer.fadeTo('slow', 1);
},
/**
* RESIZE LABELS
* resize labels so that they all the same
*
**/
resizeLabels: function(){
var height = 0;
$('.track-label').each(function(){
if($(this).height() > height)
$('.track-label').css('height', $(this).height());
});
},
/**
* BUILD FRONTEND
* Build frontend elements after loading
*
**/
buildFrontEnd: function(){
this.createMixer();
this.createTransport();
this.setMaxWidth();
},
/**
* ADD INFO TEXT
* add ino text to bottom
*
**/
setMaxWidth: function() {
var sqsCodeBlock = '.sqs-block.code-block.sqs-block-code';
var maxWidth = '1000px';
if (this.urlList.length > 8) {
maxWidth = '1100px';
}
$(sqsCodeBlock).css('max-width', maxWidth);
},
/**
* CREATE TRANSPORT
* create transport element
*
**/
createTransport: function(){
if(this.JCmix.attr('data-record') == 'true')
this.allowRecord = true;
var transport = "<div id=\"transport\">";
transport += "<a type=\"button\" id=\"play\">";
transport += "<a type=\"button\" id=\"start\">";
if(this.allowRecord)
transport += "<a type=\"button\" id=\"record\">";
transport += "<a type=\"button\" id=\"reset\"></a>";
transport += "</div>";
this.mixer.append(transport); // Aoppend to body
this.transport = $('#transport');
this.playButton = $('#play'); // create the DOM element
this.startButton = $('#start');
this.resetButton = $('#reset');
var that = this;
// TRANSPORT CLICK HANDLERS
this.playButton.on('click',function(e) {
e.preventDefault();
if(this.dataset.state == "pause"){
this.dataset.state = "play";
$(this).removeClass('active');
}
else{
this.dataset.state = "pause";
$(this).addClass('active');
}
that.togglePlay(this.dataset.state);
});
window.addEventListener("keypress", function (e) {
var SPACEBAR = 32;
if (e.which === SPACEBAR) {
e.preventDefault();
that.playButton.trigger('click');
}
});
this.resetButton.on('click',function(e) {
e.preventDefault();
$('div[data-channel]').each(function(channelIndex) {
that.resetMute(channelIndex);
that.resetSolo(channelIndex);
that.resetPan(channelIndex);
that.resetVol(channelIndex);
});
});
if(this.allowRecord){ // only do this if allowing recording
this.transport.addClass('recording');
this.mixer.append("<div id=\"recordListButtons\"><a type=\"button\" id=\"toggleRecordings\" >Hide Recordings<a><a type=\"button\" id=\"clearRecordings\" >Clear Recordings<a></div>"); // Aoppend to body
this.mixer.append("<ul id=\"recordinglist\"></ul>"); // Aoppend to body
this.recordButton = $('#record');
this.recordinglist = $('#recordinglist');
this.toggleRecordingsButton = $('#toggleRecordings');
this.clearRecordingsButton = $('#clearRecordings');
this.recordButton.on('click',function(e) {
if(!that.paused) //can't toggle if currenty playing
return false;
e.preventDefault();
if(this.dataset.state == 'record'){
this.dataset.state = 'non';
$(this).removeClass('active');
that.recording = false;
}
else{
this.dataset.state = 'record';
$(this).addClass('active');
that.recording = true;
}
});
this.toggleRecordingsButton.on('click',function(e) {
if($(this).html() == 'Hide Recordings'){
$(this).html('Show Recordings');
that.recordinglist.slideUp();
}
else{
$(this).html('Hide Recordings');
that.recordinglist.slideDown();
}
});
this.clearRecordingsButton.on('click',function(e) {
that.recordinglist.html('');
});
}
this.startButton.on('click', function(){
that.rewindToBeginning();
});
},
/**
* CREATE MIXER
* create mixer element
*
**/
createMixer: function(){
this.title = this.JCmix.attr('data-title');
this.arranger = this.JCmix.attr('data-arranger');
this.spotify = this.JCmix.attr('data-spotify');
var mixer = "<div id=\"mixer\">";
// mixer += "<iframe src=\"" + this.spotify + "\" width=\"80%\" height=\"80\" style=\"margin:0 10% 1\" frameborder=\"1\" allowtransparency=\"true\" allow=\"encrypted-media\"></iframe>";
mixer += "<h2 class=\"mixer-title\">"+this.title+"</h2>";
mixer += "<h4>"+this.arranger+"</h4>";
mixer += "<div id=\"channelstrip\"></div>";
mixer += "<div class=\"paddingholder\">";
mixer += "<div id=\"timeline\">";
mixer += "<div id=\"progress\" ></div>";
mixer += "</div>";
mixer += "</div>";
mixer += "<div class=\"paddingholder\">";
mixer += "<div id=\"timer\">";
mixer += "<span class=\"progress\">00:00:000</span>";
mixer += "<span> / </span> ";
mixer += "<span class=\"total\"></span>";
mixer += "</div>";
mixer += "</div>";
mixer += "</div>";
this.JCmix.append(mixer); // Aoppend to body
// create the DOM elements
this.mixer = $('#mixer');
this.channelStrip = $('#channelstrip');
this.timer = $('#timer');
this.timerProgress = $('#timer .progress');
this.timerTotal = $('#timer .total');
this.timeline = $('#timeline');
this.progressbar = $('#progress');
this.setTransportEventHandlers();
},
/**
* SET TRANSPORT EVENT HANDLERS
* set event handlers for dragging the transport bar
*
**/
setTransportEventHandlers: function(){
var that = this;
// timeline dragging event handlers
this.DOMdocument.on(TouchMouseEvent.DOWN,function(e){
if( e.target.id=="timeline" || e.target.id=="progress"){
that.timelinedrag = true;
that.fingerPosition = e.pageX-parseFloat(that.JCmix.offset().left); // distance from side of timeline to page
$(this).on(TouchMouseEvent.MOVE,function(e){
that.fingerPosition = e.pageX-parseFloat(that.JCmix.offset().left); // distance from side of timeline to page
that.timelineClick(that.fingerPosition, false);
});
that.DOMdocument.on(TouchMouseEvent.UP, function(e){
$(this).unbind(TouchMouseEvent.MOVE);
$(this).unbind(TouchMouseEvent.UP);
that.timelinedrag = false;
//if( e.target.id=="timeline" || e.target.id=="progress"){
if(e.pageX !== null)
distance = e.pageX-parseFloat(that.JCmix.offset().left);
that.timelineClick(that.fingerPosition, true);
//}
});
}
});
// on slider changes
this.DOMbody.on('change', 'input[type=range][data-slider]', function() {
that.sliderChange($(this).attr('data-slider'), $(this).val());
});
// on mute changes
this.DOMbody.on('change', 'input[data-mute]', function() {
that.muteChange($(this).attr('data-mute'), $(this).is(":checked"));
});
// on solo changes
this.DOMbody.on('change', 'input[data-solo]', function() {
that.soloChange($(this).attr('data-solo'), $(this).is(":checked"));
});
// on panner changes
/*this.DOMbody.on('change', 'input[data-panner]', function() {
that.panChange($(this).attr('data-panner'), $(this).val());
});*/
},
getHighestVolume: function(array){
return Math.max.apply( Math, array );
},
getAverageVolume: function(array) {
var values = 0;
var average;
var length = array.length;
// get all the frequency amplitudes
for (var i = 0; i < length; i++) {
values += array[i];
}
average = values / length;
return average;
},
/**
* UPDATE LOADING PERCENT
* Update the percent loaded
*
**/
updateLoadingPercent: function(){
var percent = (100 / this.urlList.length) * this.loadCount;
this.percentLoaded.html(Math.round(percent));
},
/**
* LOAD BUFFER
* Get the audio files
*
**/
loadBuffer: function(url, index) {
// Load buffer asynchronously
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
var loader = this;
request.onload = function() {
// Asynchronously decode the audio file data in request.response
loader.context.decodeAudioData(
request.response,
function(buffer) {
if (!buffer) {
// alert('error decoding file data: ' + url);
console.error('error decoding file data: ' + url);
return;
}
loader.bufferList[index] = buffer;
if (++loader.loadCount == loader.urlList.length) {
loader.finishedLoading(loader.bufferList);
}
loader.updateLoadingPercent();
},
function(error) {
console.error('decodeAudioData error', error);
}
);
}
request.onerror = function() {
// alert('Failed to load audio. Ensure that your audio files are hosted on the same server as your website, or that you allow cross origin resource sharing (CORS) if you are hosting them on a different server.');
}
request.send();
},
/**
* LOAD
* Ready the audio
*
**/
load: function() {
for (var i = 0; i < this.urlList.length; ++i)
this.loadBuffer(this.urlList[i], i);
},
/**
* SET DEFAULT VALUES
* Set values as defined by user
*
**/
setDefaultValues: function(){
var that = this;
var count = 0;
this.JCmix.children('.track').each(function(){
// change mute attribute
var mute = $(this).attr('data-start-muted');
if(mute == 'true') {
that.initialMutes[count] = true;
$('[data-mute='+count+']').prop('checked', true).trigger('change');
} else {
that.initialMutes[count] = false;
}
// change initial volume attribute
var initialVol = $(this).attr('data-initial-volume');
that.initialVols[count] = initialVol;
$('[data-slider='+count+']').val(initialVol).trigger('change');
// change initial pan attribute
var initialPan = $(this).attr('data-initial-pan');
that.initialPans[count] = initialPan;
$('[data-panner='+count+']').val(initialPan).trigger('change');
var label = $(this).attr('data-label');
$('[data-label='+count+']').html(label);
var mobileLabel = $(this).attr('data-label-mobile');
$('[data-label-mobile='+count+']').html(mobileLabel);
count++;
// we've done with the default divs now, so we can delete them
if(count == that.JCmix.children('.track').length)
that.deleteDefaultDivs();
$('[data-label='+count+']').html('Master');
$('[data-label-mobile='+count+']').html('M');
});
},
/**
* DELETE DEFAULT DIVS
* Delete use setting divs.
*
**/
deleteDefaultDivs: function(){
this.JCmix.children('.track').each(function(){
$(this).remove();
});
},
/**
* FINISHED LOADING
* Buffer the audio
*
**/
finishedLoading: function (bufferList) {
this.buildFrontEnd();
this.buffer = [];
for (var i = 0; i < this.urlList.length; i++){
this.buffer.push(bufferList[i]);
}
this.createElements(); // remove for autoplay;
this.setDefaultValues();
this.setDuration(); // set duration of piece
this.hideLoader();
},
/**
* Build a new meter
* Buffer the audio
*
**/
createMeter: function(i)
{
// Create left analyser
var analyser = this.context.createAnalyser();
this.leftAnalysers.push(analyser);
this.leftAnalysers[i].smoothingTimeConstant = 0.3;
this.leftAnalysers[i].fftSize = 1024;
// Create right analyser
var analyser2 = this.context.createAnalyser();
this.rightAnalysers.push(analyser2);
this.rightAnalysers[i].smoothingTimeConstant = 0.0;
this.rightAnalysers[i].fftSize = 1024;
// setup a javascript node
// connect to destination, else it isn't called
// create [a buffer source node
var splitter = this.context.createChannelSplitter();
this.splitters.push(splitter);
// connect the source to the analyser and the splitter
if(i < this.pannerNodes.length-1) // if this is a regular channels trip
this.pannerNodes[i].connect(this.splitters[i]);
else // master channel doesn't use a panner
this.gainNodes[i].connect(this.splitters[i]);
// connect one of the outputs from the splitter to
// the analyser
this.splitters[i].connect(this.leftAnalysers[i],0,0);
this.splitters[i].connect(this.rightAnalysers[i],1,0);
// connect the splitter to the javascriptnode
// we use the javascript node to draw at a
// specific interval.
this.leftAnalysers[i].connect(this.javascriptNode[i]);
this.javascriptNode[i].connect(this.context.destination);
if(i < this.pannerNodes.length-1){ // if this is a regular channels trip
this.pannerNodes[i].connect(this.gainNodes[this.gainNodes.length-1]);
}
else{ // this is the master channel strip
this.gainNodes[i].connect(this.context.destination);
this.rec = new Recorder(this.gainNodes[i]);
}
this.drawCanvas(this.leftAnalysers[i], this.rightAnalysers[i], this, i);
},
/*
* SLIDER CHANGE
* Event for when a slider changes
*/
sliderChange: function(audio_id,value){
if (!this.muted[audio_id] && (this.soloed[audio_id] || this.getSoloedTracks().length === 0)) {
this.gainNodes[audio_id].gain.value = value;
}
},
/*
* MUTE CHANGE
* Event when mute changes
*/
muteChange: function(audio_id,value){
if (value) {
this.gainNodes[audio_id].gain.value = 0; // mute the gain node
this.muted[audio_id] = true;
} else {
this.muted[audio_id] = false;
if (this.soloed[audio_id] || this.getSoloedTracks().length === 0) {
this.gainNodes[audio_id].gain.value = $('input[type=range][data-slider="'+audio_id+'"]').val(); // restore previous gain value
}
}
},
soloChange: function(audio_id,value){
if (value) {
$('[data-solo='+audio_id+']').parent().addClass('soloed');
$('input[data-solo="'+audio_id+'"]').prop('checked', true);
$('input[data-mobile-solo="'+audio_id+'"]').prop('checked', true);
this.soloed[audio_id] = true;
} else {
$('[data-solo='+audio_id+']').parent().removeClass('soloed');
$('input[data-solo="'+audio_id+'"]').prop('checked', false);
$('input[data-mobile-solo="'+audio_id+'"]').prop('checked', false);
this.soloed[audio_id] = false;
}
var soloedTracks = this.getSoloedTracks();
for (var track = 0; track < this.urlList.length; track++) {
if (soloedTracks.length > 0) {
if (!this.soloed[track]) {
this.gainNodes[track].gain.value = 0; // mute the gain node
} else if (!this.muted[track]) {
this.gainNodes[track].gain.value = $('input[type=range][data-slider="'+track+'"]').val(); // restore gain value
}
} else {
if (!this.muted[track]) {
this.gainNodes[track].gain.value = $('input[type=range][data-slider="'+track+'"]').val(); // restore gain value
}
}
}
},
getSoloedTracks: function() {
var soloedTracks = [];
for (var track = 0; track < this.soloed.length; track++) {
if (this.soloed[track]) {
soloedTracks.push(track);
}
}
return soloedTracks;
},
/* PAD
* pad string with leading zeros
*/
pad: function(str, max) {
str = str.toString();
return str.length < max ? this.pad("0" + str, max) : str;
},
/*
* FORMAT TIME
* Convert milliseconds to readable format
*/
formatTime: function(millis){
var hours = Math.floor(millis / 36e5),
mins = Math.floor((millis % 36e5) / 6e4),
secs = Math.floor((millis % 6e4) / 1000);
mill = Math.floor(millis % 1000);
var returns = "<span>"+this.pad(mins,2)+"</span>:<span>"+this.pad(secs,2)+"</span>:<span>"+this.pad(mill, 2).substring(2, 0);+"</span>";
return returns;
},
/*
* COUNT LOOP
* count the number of loops
*/
countLoops: function(current){
this.loops = Math.floor(current/this.totalLength);
this.currentLoopProgress= (Date.now() - this.startedAt)-(this.loops*(this.buffer[0].duration*1000));
},
/*
* UPDATE TIMER
* Update time to current starte
*/
updateTimer: function(){
var current = this.formatTime(this.currentLoopProgress);
var totalLength = this.formatTime(this.totalLength);
this.timerProgress.html(current);
this.timerTotal.html(totalLength);
this.updateTimeline();
},
/*
* Update position of progress bar
*/
updateTimeline: function(){
if(this.timelinedrag)
return false;
var current = Date.now() - this.startedAt;
this.countLoops(current); // work out number of times looped
var percent = (100/this.totalLength)*this.currentLoopProgress;
var left = (this.timeline.width()/100) * percent;
this.progressbar.css('left',left);
},
/*
* DRAW CANVAS
* Create the listener which draws the sound bar onto the canvas
*/
drawCanvas: function(leftAnalyser, rightAnalyser, ctx, i)
{
var canvas = $('#c'+i).get()[0].getContext("2d");
ctx.bouncerleft.push({ "average":0 , "opacity":1 });
ctx.bouncerright.push({ "average":0 , "opacity":1 });
gradient = canvas.createLinearGradient(0,0,0,400);
gradient.addColorStop(1,'#d89f36');
// gradient.addColorStop(1,'#31e2fc');
// gradient.addColorStop(0.75,'#38fedd');
// gradient.addColorStop(0.25,'#38fedd');
// gradient.addColorStop(0,'#31e0fc');
// when the javascript node is called
// we use information from the analyzer node
// to draw the volume
ctx.javascriptNode[i].onaudioprocess = function(event) {
ctx.updateTimer();
// get the average for the first channel
var array = new Uint8Array(leftAnalyser.frequencyBinCount);
leftAnalyser.getByteFrequencyData(array);
var average = ctx.getAverageVolume(array);
// get the average for the second channel
var array2 = new Uint8Array(rightAnalyser.frequencyBinCount);
rightAnalyser.getByteFrequencyData(array2);
var average2 = ctx.getAverageVolume(array2);
// bouncers left
if(average > ctx.bouncerleft[i].average){
ctx.bouncerleft[i].average = average;
ctx.bouncerleft[i].opacity = 1;
}
else{
if(ctx.bouncerleft[i].opacity > 0.1) // fade out
ctx.bouncerleft[i].opacity = ctx.bouncerleft[i].opacity -0.1;
else
ctx.bouncerleft[i].opacity = 0;
ctx.bouncerleft[i].average--; // make it fall
}
// bouncers right
if(average2 > ctx.bouncerright[i].average){
ctx.bouncerright[i].opacity = 1;
ctx.bouncerright[i].average = average2;
}
else{
if(ctx.bouncerright[i].opacity > 0.1)// fade out
ctx.bouncerright[i].opacity = ctx.bouncerright[i].opacity -0.1;
else
ctx.bouncerright[i].opacity = 0;
ctx.bouncerright[i].average--;// make it fall
}
// clear the current state
canvas.clearRect(0, 0, 60, ctx.meterHeight);
if (i < ($('canvas.meter').length - 1)) {
canvas.fillStyle="#222e44";
} else {
canvas.fillStyle="transparent";
}
// canvas.fillStyle="#15181b";
// create background to meters
canvas.fillRect(0,0,ctx.meterWidth,ctx.meterHeight+200);
canvas.fillRect(ctx.meterWidth+5,0,ctx.meterWidth,ctx.meterHeight+200);
// set the fill style
canvas.fillStyle=gradient;
// create the delayed max meter bar
if(average > 0)
canvas.fillRect(0,ctx.meterHeight-(ctx.bouncerleft[i].average*(ctx.meterHeight/100))-2,ctx.meterWidth,ctx.bouncerleft[i].opacity);
if(average2 > 0)
canvas.fillRect(ctx.meterWidth+5,ctx.meterHeight-(ctx.bouncerright[i].average*(ctx.meterHeight/100))-2,ctx.meterWidth,ctx.bouncerright[i].opacity);
// create the meters (ctx.meterHeight/100) is 1% of the meter height
canvas.fillRect(0,ctx.meterHeight-(average*(ctx.meterHeight/100)),ctx.meterWidth,ctx.meterHeight+200);
canvas.fillRect(ctx.meterWidth+5,ctx.meterHeight-(average2*(ctx.meterHeight/100)),ctx.meterWidth,ctx.meterHeight+200);
if(parseFloat(Date.now() - ctx.startedAt)+50 >= parseFloat(ctx.totalLength)){ // disable looping for now
$('#play').removeClass('active');
ctx.stop();
ctx.rewindToBeginning();
return false;
}
}
},
panChange: function(audio_id,value) {
var xDeg = parseInt(value);
var zDeg = xDeg + 90;
if (zDeg > 90) {
zDeg = 180 - zDeg;
}
var x = Math.sin(xDeg * (Math.PI / 180));
var z = Math.sin(zDeg * (Math.PI / 180));
this.pannerNodes[audio_id].setPosition(x, 0, z);
},
resetMute: function(audio_id) {
var that = this;
$('[data-mute='+audio_id+']').prop('checked', that.initialMutes[audio_id]).trigger('change');
},
resetSolo: function(audio_id) {
var that = this;
$('[data-solo='+audio_id+']').prop('checked', false).trigger('change');
},
resetPan: function(audio_id) {
var that = this;
$('input[data-panner="'+audio_id+'"]').val(that.initialPans[audio_id]);
$('[data-panner='+audio_id+']').trigger('change');
},
resetVol: function(audio_id) {
var that = this;
$('input[data-slider="'+audio_id+'"]').val(that.initialVols[audio_id]);
$('[data-slider='+audio_id+']').trigger('change');
},
/**
* CREATE GAIN NODES
* Create a gain node for index
*
**/
createGainNode: function(i, master){
// Create a gain node.
var gainnode = this.context.createGain();
var pannernode = this.context.createPanner();
pannernode.panningModel = "equalpower";
this.gainValues.push(1);
this.muted.push(false);
this.soloed.push(false);
this.gainNodes.push(gainnode);
this.pannerNodes.push(pannernode);
// Connect the source to the gain node.
if(!master){
// this.audios[i].connect(this.pannerNodes[i]);
// this.pannerNodes[i].connect(this.gainNodes[i]);
this.audios[i].connect(this.gainNodes[i]);
this.gainNodes[i].connect(this.pannerNodes[i]);
}
// this.panChange(i,0); // set pan to 0
this.DOMbody.find("[data-panner="+i+"]").trigger('change');
$("[data-slider="+i+"]").trigger('change'); // set gain note to value of visual slider
$("[data-mute="+i+"]").trigger('change'); // set gain note to value of mute
},
/**
* RESERT ARRAYS
* Reset arrays to defaults
*
**/
resetArrays: function(){
this.bouncerleft = [];
this.bouncerright = [];
this.leftAnalysers = [];
this.rightAnalysers = [];
this.splitters = [];
this.audios = [];
this.gainNodes = [];
this.pannerNodes = [];
this.gainValues = [];
this.javascriptNode = [];
this.muted = [];
this.soloed = [];
this.loop = 0; // reset loops loops
},
/**
* Load visuals and audio nodes
*
**/
createElements: function(){
this.resetArrays();
// Create two sources and play them both together.
for (var i = 0; i < this.urlList.length; i++){
var source1 = this.context.createBufferSource();
source1.buffer = this.buffer[i];
source1.loop = false; // flase to stop looping
this.audios.push(source1);
this.audios[i].muted = true;
// create a new javascript node ready for the meter
javascriptNode = this.context.createScriptProcessor(2048, 1, 1);
// create a gain controller
this.createGainNode(i, false);
// Connect the gain node to the destination.
this.javascriptNode.push(javascriptNode);
// create a meter visualisation
var channel = this.createChannel(i);
// create a new canvas
var canvas = this.createCanvas(i, channel, false);
// create Sliders
this.createSlider(i, channel);
this.createMute(i, channel);
this.createPanner(i, channel);
this.createLabel(i, channel, false);
}
// Create master elements
javascriptNode = this.context.createScriptProcessor(2048, 1, 1);
this.createGainNode(i, true);
this.javascriptNode.push(javascriptNode);
var channel = this.createChannel(i);
// create a new canvas
var canvas = this.createCanvas(i, channel, true);
// create Sliders
this.createSlider(i, channel);
this.createMute(i, channel);
this.createLabel(i, channel, true);
// init soloed tracks once all gain nodes and label elements have been created
for (var i = 0; i < this.urlList.length; i++){
$("[data-solo="+i+"]").trigger('change');
}
},
/**
* SET DURATION
* set dutaion of timeline to the length of the ongest buffer
*
**/
setDuration: function(){
for (var i = 0; i < this.audios.length; i++){
if((this.buffer[i].duration * 1000) > this.totalLength)
this.totalLength = this.buffer[i].duration * 1000; // tottal length of buffer in milliseconds
}
this.updateTimer(); // update visual timer
},
/**
* PLAY
* Play the audio
*
**/
play: function(){
this.paused = false;
this.createElements();
this.replay = false;
this.playing = [];
for (var i = 0; i < this.audios.length; i++){
this.createMeter(i);
// start from paused time
if (this.currentLoopProgress) {
this.startedAt = Date.now() - this.currentLoopProgress;
var startFrom = this.currentLoopProgress / 1000;
if(this.buffer[i].duration > startFrom){
this.playing.push(i);
this.audios[i].start(0, this.currentLoopProgress / 1000);
}
}
// start from beginning
else {
this.playing.push(i);
this.startedAt = Date.now();
this.audios[i].start(0);
}
}
this.createMeter(i); // create master meter
//on end of source, disconnect everything
var that = this;
$(this.audios).each(function(index){
this.onended = function() {
that.onended(index);
}
});
if(this.recording)
this.startRecording();
},
/**
* START RECORDING
* Start recording the audio stream
*
**/
startRecording: function(){
this.rec.clear();
this.rec.record();
},
onended: function(index){
if(this.playing.indexOf(index) > -1){
// disconnect everything
this.javascriptNode[index].disconnect();
this.audios[index].disconnect();
this.gainNodes[index].disconnect();
this.pannerNodes[index].disconnect();
this.javascriptNode[index].disconnect();
this.leftAnalysers[index].disconnect();
this.rightAnalysers[index].disconnect();
this.splitters[index].disconnect();
var number = this.playing.indexOf(index);
this.playing.splice(number, 1);
this.clearCanvas(index);
}
// disconnect everything
if(this.playing.length == 0 && this.paused){
for (var i = 0; i < this.gainNodes.length; i++){
this.gainNodes[i].disconnect();
this.pannerNodes[i].disconnect();
this.clearCanvas(i);
}
for (var i = 0; i < this.javascriptNode.length; i++){
this.javascriptNode[i].disconnect();
}
for (var i = 0; i < this.leftAnalysers.length; i++){
this.leftAnalysers[i].disconnect();
}
for (var i = 0; i < this.rightAnalysers.length; i++){
this.rightAnalysers[i].disconnect();
}
for (var i = 0; i < this.splitters.length; i++){
this.splitters[i].disconnect();
}
/* this.gainNodes[this.gainNodes.length-1].disconnect();
this.javascriptNode[this.gainNodes.length-1].disconnect();
this.leftAnalysers[this.gainNodes.length-1].disconnect();
this.rightAnalysers[this.gainNodes.length-1].disconnect();
this.splitters[this.gainNodes.length-1].disconnect();*/
if(this.replay){ // replay if this is a timeline skip rather than a full on stop
var percent = (100/this.totalLength)*this.replayFrom;
var left = (this.timeline.width()/100) * percent;
this.progressbar.css('left',left);
this.currentLoopProgress = this.replayFrom;
this.play();
}
}
return true;
},
/**
* Clear Canvas
* clear the canvas from index
*
**/
clearCanvas: function(index){
var canvas = $('#c'+index).get()[0].getContext("2d");
// clear canvas
canvas.clearRect(0, 0, 60, 400);
//rebuild background
if (index < ($('canvas.meter').length - 1)) {
canvas.fillStyle="#222e44";
} else {
canvas.fillStyle="transparent";
}
//canvas.fillStyle="#15181b";
canvas.fillRect(0,0,this.meterWidth,this.meterHeight+200);
canvas.fillRect(this.meterWidth+5,0,this.meterWidth,this.meterHeight+200);
},
/**
* PAUSE
* Pause the audio
*
**/
stop: function(){
if(this.recording && !this.paused){
this.rec.stop();
this.toggleRecordingsButton.show();
this.clearRecordingsButton.show();
this.rec.exportWAV(function(blob) {
var url = URL.createObjectURL(blob);
var li = document.createElement('li');
var au = document.createElement('audio');
var hf = document.createElement('a');
var src = document.createElement('source');
au.controls = true;
src.src = url;
src.type = "audio/wav";
au.appendChild(src);
hf.href = url;
hf.download = that.title+' ' +new Date().toISOString() + '.wav';
hf.innerHTML = hf.download;
li.appendChild(au);
li.appendChild(hf);
that.recordinglist.append(li);
});
}
this.paused = true;
var that = this;
for (var i = 0; i < this.audios.length; i++){
if(this.playing.indexOf(i) > -1)
this.audios[i].stop(0);
this.currentLoopProgress = Date.now() - this.startedAt;
}
},
/**
* Toggle the play state
* play mix
*
**/
togglePlay: function(state){
if(state == 'pause')
this.play();
else
this.stop();
},
/*
* CREATE CHANNEL
* CREATE CHANNEL STRIP
*/
createChannel: function(i){
if($('div[data-channel="'+i+'"]').length == 0){
var newdiv = document.createElement('div');
newdiv.dataset.channel = i;
this.channelStrip.append(newdiv);
}
return $('div[data-channel="'+i+'"]');
},
/*
* CREATE MUTE
* CREATE MUTE BUTTON
*/
createMute: function(i, channel){
var that = this;
if ($('input[data-mute="'+i+'"]').length == 0){
var mute = document.createElement('div');
mute.className = 'mute-button';
mute.innerHTML = "<label><input data-mute='"+i+"' type='checkbox' value='1' ><span>M</span></label>";
$(channel).append(mute);
// if there is no initial mute value (i.e. master track), set it to false
if (!that.initialMutes[i]) {
that.initialMutes[i] = false;
}
}
return $('input[data-mute="'+i+'"]');
},
/*
* CREATE PANNER
* CREATE PANNER BUTTON
*/
createPanner: function(i, channel){
var that = this;
if ($('input[data-panner="'+i+'"]').length == 0){
var panner = document.createElement('div');
panner.className = 'panner-range';
panner.innerHTML = "<input data-panner='"+i+"' value='0' class='dial' type='text'>";
$(channel).append(panner);
$("[data-panner='"+i+"']").knob({
'min':-90,
'max':90,
'width' : 30,
'height' : 30,
'fgColor' : '#FFF',
'angleOffset': '-125',
'bgColor' : '#a4263b',
'angleArc': '250',
'skin' :'tron',
'thickness':'.2',
'displayPrevious' : true,
'cursor':'30',
'font': 'futura-pt',
'inputColor': '#292929',
'draw' : function () { that.panChange(i,this.v); }
});
// reset to initialPan upon alt/ctrl + click on panner input
$('input[data-panner="'+i+'"]').click(function(event) {
if (event.altKey || event.ctrlKey || event.metaKey) {
event.preventDefault();
that.resetPan(i);
}
});
// reset to initialPan upon alt/ctrl + click on panner wheel
$('input[data-panner="'+i+'"]').parent().parent().click(function(event) {
if (event.altKey || event.ctrlKey || event.metaKey) {
event.preventDefault();
that.resetPan(i);
}
});
}
return $('input[data-panner="'+i+'"]');
},
/*
* CREATE LABEL
* CREATE Label for track
*/
createLabel: function(i, channel, isMaster){
var that = this;
if ($('[data-label="'+i+'"]').length == 0){
var label = document.createElement('div');
label.className = 'track-label';
label.innerHTML = "<label data-label='"+i+"'></label>";
label.innerHTML += "<label class=\"label-mobile\" data-label-mobile='"+i+"'></label>";
if (!isMaster) {
label.innerHTML += "<input data-solo='"+i+"' type='checkbox' value='0' >";
$(channel).append(label);
$('[data-label="'+i+'"]').parent().click(function(event) {
event.preventDefault();
$('input[data-solo="'+i+'"]').prop('checked', !$('input[data-solo="'+i+'"]').is(':checked')).trigger('change');
});
var mobileSolo = document.createElement('div');
mobileSolo.className = 'mobile-solo-button';
mobileSolo.innerHTML = "<label><input data-mobile-solo='"+i+"' type='checkbox' value='0' ><span data-solo-span='"+i+"'>S</span></label>";
$(channel).append(mobileSolo);
$('[data-solo-span="'+i+'"]').click(function(event) {
event.preventDefault();
$('input[data-solo="'+i+'"]').prop('checked', !$('input[data-solo="'+i+'"]').is(':checked')).trigger('change');
});
} else {
$(channel).append(label);
}
}
return $('[data-label="'+i+'"]');
},
/*
* CREATE SLIDER
* CREATE VOLUME SLIDER
*/
createSlider: function(i, channel){
var that = this;
if ($('input[data-slider="'+i+'"]').length == 0){
var slider = document.createElement('div');
slider.className = 'slider';
var input = document.createElement("input");
input.type = "range";
input.dataset.slider = i;
input.min = 0;
input.max = 3;
input.step = 0.01;
input.value = 2;
// if there is no initial volume (i.e. master track), set it to the current value
if (!that.initialVols[i]) {
that.initialVols[i] = input.value;
}
input.addEventListener('input', function() {
this.setAttribute('value', this.value);
$(this).trigger('change');
});
$(slider).append(input);
$(channel).append(slider);
// reset to initialVol upon alt/ctrl + click on gain slider
$('input[data-slider="'+i+'"]').click(function(event) {
if (event.altKey || event.ctrlKey || event.metaKey) {
event.preventDefault();
that.resetVol(i);
}
});
}
return $('input[data-slider="'+i+'"]');
},
/*
* CREATE CANVAS
* Create a new canvas element for channel
*/
createCanvas: function(i, channel, isMaster){
if ($('#c'+i).length == 0){
var mycanvas = document.createElement("canvas");
mycanvas.id = "c"+i;
mycanvas.className = "meter";
mycanvas.width = (this.meterWidth*2)+5;
mycanvas.height = this.meterHeight;
channel.append(mycanvas);
// create background to meters
var canvas = $('#c'+i).get()[0].getContext("2d");
if (isMaster) {
canvas.fillStyle="transparent";
} else {
canvas.fillStyle="#222e44";
}
//canvas.fillStyle="#15181b";
canvas.fillRect(0,0,this.meterWidth,this.meterHeight+200);
canvas.fillRect(this.meterWidth+5,0,this.meterWidth,this.meterHeight+200);
}
return $('#c'+i).get()[0].getContext("2d");
},
/*
* REWIND TO BEGINNING
* Rewind track to beginning
*/
rewindToBeginning: function(){
if(this.paused){
this.playButton.attr('data-state', 'play');
}
this.progressbar.css('left',0);
this.currentLoopProgress = 0;
this.replayFrom = 0;
if(!this.paused){
this.replay = true;
this.replayFrom = 0;
this.stop();
}
},
/*
* TIMELINE CLICK
* start playign from where the user clicked the timeline
*/
timelineClick: function(position, audio){
var paused = this.paused;
var percent = (100/this.timeline.width()) * position;
if(percent < 1 || percent > 99) // only if mouse inside box
return false;
var pausedAt = (this.totalLength/100) * percent;
this.currentLoopProgress = pausedAt;
// reposition progress bar
var percent = (100/this.totalLength)*pausedAt;
var left = (this.timeline.width()/100) * percent;
this.progressbar.css('left',left);
var that = this;
if(!paused && audio){
this.replay = true;
this.replayFrom = pausedAt;
this.stop();
}
},
/*
* Bind mouse clciks and screen touches
*/
bindMouseandTouch: function(){
/* == GLOBAL DECLERATIONS == */
TouchMouseEvent = {
DOWN: "touchmousedown",
UP: "touchmouseup",
MOVE: "touchmousemove"
}
/* == EVENT LISTENERS == */
var onMouseEvent = function(event) {
var type;
switch (event.type) {
case "mousedown": type = TouchMouseEvent.DOWN; break;
case "mouseup": type = TouchMouseEvent.UP; break;
case "mousemove": type = TouchMouseEvent.MOVE; break;
default:
return;
}
var touchMouseEvent = normalizeEvent(type, event, event.pageX, event.pageY);
$(event.target).trigger(touchMouseEvent);
}
var onTouchEvent = function(event) {
var type;
switch (event.type) {
case "touchstart": type = TouchMouseEvent.DOWN; break;
case "touchend": type = TouchMouseEvent.UP; break;
case "touchmove": type = TouchMouseEvent.MOVE; break;
default:
return;
}
var touch = event.originalEvent.touches[0];
var touchMouseEvent;
if (type == TouchMouseEvent.UP)
touchMouseEvent = normalizeEvent(type, event, null, null);
else
touchMouseEvent = normalizeEvent(type, event, touch.pageX, touch.pageY);
$(event.target).trigger(touchMouseEvent);
}
/* == NORMALIZE == */
var normalizeEvent = function(type, original, x, y) {
return $.Event(type, {
pageX: x,
pageY: y,
originalEvent: original
});
}
/* == LISTEN TO ORIGINAL EVENT == */
var jQueryDocument = $(document);
if ("ontouchstart" in window) {
jQueryDocument.on("touchstart", onTouchEvent);
jQueryDocument.on("touchmove", onTouchEvent);
jQueryDocument.on("touchend", onTouchEvent);
} else {
jQueryDocument.on("mousedown", onMouseEvent);
jQueryDocument.on("mouseup", onMouseEvent);
jQueryDocument.on("mousemove", onMouseEvent);
}
}
}; // End Class
// check browser version
function get_browser_info(){
var ua=navigator.userAgent,tem,M=ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if(/trident/i.test(M[1])){
tem=/\brv[ :]+(\d+)/g.exec(ua) || [];
return {name:'IE',version:(tem[1]||'')};
}
if(M[1]==='Chrome'){
tem=ua.match(/\bOPR\/(\d+)/)
if(tem!=null) {return {name:'Opera', version:tem[1]};}
}
M=M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
if((tem=ua.match(/version\/(\d+)/i))!=null) {M.splice(1,1,tem[1]);}
return {
name: M[0],
version: M[1]
};
}
function closeSupport(){
$("#nosupport").remove();
}
// Run the mixer
function run(){
var jcmix = new Jcmix();
jcmix.init();
$('#panner').on('change', function(event){
jcmix.pan(this);
});
}
$( window ).on("load", function() {
run();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
#jcmix {
margin: 0 auto;
/*
input[type='range']:nth-of-type(1)::-webkit-slider-runnable-track:before, input[type='range']:nth-of-type(1) /deep/ #track:before {
content: "320Hz";
}
input[type='range']:nth-of-type(2)::-webkit-slider-runnable-track:before, input[type='range']:nth-of-type(2) /deep/ #track:before {
content: "600Hz";
}
input[type='range']:nth-of-type(3)::-webkit-slider-runnable-track:before, input[type='range']:nth-of-type(3) /deep/ #track:before {
content: "1KHz";
}
input[type='range']:nth-of-type(4)::-webkit-slider-runnable-track:before, input[type='range']:nth-of-type(4) /deep/ #track:before {
content: "3KHz";
}
input[type='range']:nth-of-type(5)::-webkit-slider-runnable-track:before, input[type='range']:nth-of-type(5) /deep/ #track:before {
content: "6KHz";
}
input[type='range']:nth-of-type(6)::-webkit-slider-runnable-track:before, input[type='range']:nth-of-type(6) /deep/ #track:before {
content: "12KHz";
}*/
/*
input[type='range']:nth-of-type(1)::-webkit-slider-runnable-track:after, input[type='range']:nth-of-type(1) /deep/ #track:after, input[type='range']:nth-of-type(6)::-webkit-slider-runnable-track:after, input[type='range']:nth-of-type(6) /deep/ #track:after {
content: '+18 0 -18';
}*/ }
#jcmix input {
cursor: pointer;
}
#jcmix input[type='range'] {
align-self: center;
margin: -0.75em 0;
border: solid 1.5em transparent;
padding: 0;
width: 400px;
height: 0em;
background: repeating-linear-gradient(90deg, #3b3e41, #3b3e41 0.0625em, transparent 0.0625em, transparent 2em) no-repeat 50% 0.4375em border-box, repeating-linear-gradient(90deg, #3b3e41, #3b3e41 0.0625em, transparent 0.0625em, transparent 2em) no-repeat 50% 2em border-box;
background-size: 390px 0.625em, 390px 0.625em, 100% 2.25em;
font-size: 1em;
cursor: pointer; }
@media (max-width: 935px) {
#jcmix input[type='range'] {
background: repeating-linear-gradient(90deg, #3b3e41, #3b3e41 0.0625em, transparent 0.0625em, transparent 2em) no-repeat 50% 1.8em border-box, repeating-linear-gradient(90deg, #3b3e41, #3b3e41 0.0625em, transparent 0.0625em, transparent 2em) no-repeat 50% 1em border-box;
background-size: 390px 0.225em, 390px 0.225em, 100% 2.25em; } }
#jcmix input[type='range'], #jcmix input[type='range']::-webkit-slider-runnable-track, #jcmix input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none; }
#jcmix input[type='range']::-webkit-slider-runnable-track {
position: relative;
width: 13em;
height: 0.1em;
border-radius: .1875em;
background: #15181b; }
#jcmix input[type='range']::-moz-range-track {
width: 390px;
height: 0.1em;
border-radius: .1875em;
background: #15181b; }
#jcmix input[type='range']::-ms-track {
border: none;
width: 13em;
height: 0.1em;
border-radius: .1875em;
color: transparent;
background: #15181b; }
#jcmix input[type='range']::-ms-fill-lower {
display: none; }
#jcmix input[type='range']::-webkit-slider-thumb {
font-size: 0.7em;
margin-top: -0.90em;
border: none;
width: 4em;
height: 2em;
border-radius: .5em;
box-shadow: -.125em 0 .25em #928886, inset -1px 0 1px #fff;
background: radial-gradient(#ebe1e0 10%, rgba(235, 225, 224, 0.2) 10%, rgba(235, 225, 224, 0) 72%) no-repeat 50% 50%, radial-gradient(at 100% 50%, #e9dfde, #eae1de 71%, transparent 71%) no-repeat 2.5em 50%, linear-gradient(90deg, #e9dfde, #d0c8c6) no-repeat 100% 50%, radial-gradient(at 0 50%, #d0c6c5, #c6baba 71%, transparent 71%) no-repeat 0.75em 50%, linear-gradient(90deg, #e3d9d8, #d0c6c5) no-repeat 0 50%, linear-gradient(#cdc0c0, #fcf5ef, #fcf5ef, #cdc0c0);
background-size: 0.825em 100%; }
#jcmix input[type='range']::-moz-range-thumb {
font-size: 0.7em;
border: none;
width: 4em;
height: 2em;
border-radius: .5em;
box-shadow: -.125em 0 .25em #928886, inset -1px 0 1px #fff;
background: radial-gradient(#ebe1e0 10%, rgba(235, 225, 224, 0.2) 10%, rgba(235, 225, 224, 0) 72%) no-repeat 50% 50%, radial-gradient(at 100% 50%, #e9dfde, #eae1de 71%, transparent 71%) no-repeat 2.5em 50%, linear-gradient(90deg, #e9dfde, #d0c8c6) no-repeat 100% 50%, radial-gradient(at 0 50%, #d0c6c5, #c6baba 71%, transparent 71%) no-repeat 0.75em 50%, linear-gradient(90deg, #e3d9d8, #d0c6c5) no-repeat 0 50%, linear-gradient(#cdc0c0, #fcf5ef, #fcf5ef, #cdc0c0);
background-size: 0.825em 100%; }
#jcmix input[type='range']::-ms-thumb {
font-size: 0.7em;
border: none;
width: 4em;
height: 2em;
border-radius: .5em;
box-shadow: -.125em 0 .25em #928886, inset -1px 0 1px #fff;
background: radial-gradient(#ebe1e0 10%, rgba(235, 225, 224, 0.2) 10%, rgba(235, 225, 224, 0) 72%) no-repeat 50% 50%, radial-gradient(at 100% 50%, #e9dfde, #eae1de 71%, transparent 71%) no-repeat 2.5em 50%, linear-gradient(90deg, #e9dfde, #d0c8c6) no-repeat 100% 50%, radial-gradient(at 0 50%, #d0c6c5, #c6baba 71%, transparent 71%) no-repeat 0.75em 50%, linear-gradient(90deg, #e3d9d8, #d0c6c5) no-repeat 0 50%, linear-gradient(#cdc0c0, #fcf5ef, #fcf5ef, #cdc0c0);
background-size: 0.825em 100%; }
#jcmix input[type='range']::-webkit-slider-runnable-track:before, #jcmix input[type='range']::-webkit-slider-runnable-track:after, #jcmix input[type='range'] /deep/ #track:before, #jcmix input[type='range'] /deep/ #track:after {
position: absolute;
font: 0.75em/8em trebuchet ms, arial, sans-serif; }
#jcmix input[type='range']::-webkit-slider-runnable-track:before, #jcmix input[type='range'] /deep/ #track:before {
top: 50%;
right: 100%;
transform: translate(50%, -50%) rotate(90deg) translate(0, 32%); }
#jcmix input[type='range']::-webkit-slider-runnable-track:after, #jcmix input[type='range'] /deep/ #track:after {
left: 50%;
width: 3em;
word-spacing: 1em; }
#jcmix input[type='range']:nth-of-type(1)::-webkit-slider-runnable-track:after, #jcmix input[type='range']:nth-of-type(1) /deep/ #track:after {
bottom: 100%;
transform: translate(-50%, 50%) rotate(90deg) translate(-4.375em);
text-align: right; }
#jcmix input[type='range']:nth-of-type(6)::-webkit-slider-runnable-track:after, #jcmix input[type='range']:nth-of-type(6) /deep/ #track:after {
top: 100%;
transform: translate(-50%, -50%) rotate(90deg) translate(4.375em); }
#jcmix input[type='range']:focus {
outline: none; }
#jcmix input[type='range']:focus::-webkit-slider-runnable-track {
background: #15181b; }
#jcmix input[type='range']:focus::-moz-range-track {
background: #15181b; }
#jcmix input[type='range']:focus::-ms-track {
background: #15181b; }
#jcmix .mute-button label input {
display: none; }
#jcmix .mute-button {
margin: 4px;
border-radius: 4px;
overflow: auto;
float: left; }
#jcmix .mute-button label {
cursor: pointer;
float: left; }
#jcmix .mute-button label span {
background-color: #FFFFFF;
text-align: center;
font-weight: bold;
padding: 3px;
display: block;
border-radius: 4px; }
#jcmix .mute-button label input {
position: absolute;
top: -20px; }
#jcmix .mute-button input:checked + span {
background-color: #a4263b;
color: #FFFFFF; }
#jcmix .mobile-solo-button label input {
display: none; }
#jcmix .mobile-solo-button {
margin: 4px;
border-radius: 4px;
overflow: auto;
float: left; }
#jcmix .mobile-solo-button label {
float: left; }
#jcmix .mobile-solo-button label span {
background-color: #FFFFFF;
text-align: center;
font-weight: bold;
padding: 3px;
display: block;
border-radius: 4px; }
#jcmix .mobile-solo-button label input {
position: absolute;
top: -20px; }
#jcmix .mobile-solo-button input:checked + span {
background-color: #d89f36;
color: #FFFFFF; }
/**
*
* All animations must live in their own file
* in the animations directory and be included
* here.
*
*/
/**
* Styles shared by multiple animations
*/
/**
* Dots
*/
@keyframes rotate {
0% {
-webkit-transform: rotate(0deg) scale(1);
transform: rotate(0deg) scale(1); }
50% {
-webkit-transform: rotate(180deg) scale(0.6);
transform: rotate(180deg) scale(0.6); }
100% {
-webkit-transform: rotate(360deg) scale(1);
transform: rotate(360deg) scale(1); } }
#jcmix .ball-clip-rotate-multiple {
position: relative; }
#jcmix .ball-clip-rotate-multiple > div {
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
position: absolute;
left: -20px;
top: -20px;
border: 2px solid #1d7a9c;
border-bottom-color: transparent;
border-top-color: transparent;
border-radius: 100%;
height: 35px;
width: 35px;
-webkit-animation: rotate 1s 0s ease-in-out infinite;
animation: rotate 1s 0s ease-in-out infinite; }
#jcmix .ball-clip-rotate-multiple > div:last-child {
display: inline-block;
top: -10px;
left: -10px;
width: 15px;
height: 15px;
-webkit-animation-duration: 0.5s;
animation-duration: 0.5s;
border-color: #00a7cc transparent #00a7cc transparent;
-webkit-animation-direction: reverse;
animation-direction: reverse; }
@-webkit-keyframes ball-scale-ripple {
0% {
-webkit-transform: scale(0.1);
transform: scale(0.1);
opacity: 1; }
70% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 0.7; }
100% {
opacity: 0.0; } }
@keyframes ball-scale-ripple {
0% {
-webkit-transform: scale(0.1);
transform: scale(0.1);
opacity: 1; }
70% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 0.7; }
100% {
opacity: 0.0; } }
#jcmix #transport {
overflow: auto; }
#jcmix #play {
display: block;
width: 0;
float: left;
height: 0;
border-top: 20px solid transparent !important;
border-bottom: 20px solid transparent !important;
border-left: 24px solid #222e44 !important;
margin: 40px auto 30px auto;
position: relative;
z-index: 1;
transition: all 0.1s;
-webkit-transition: all 0.1s;
-moz-transition: all 0.1s;
left: 100px; }
#jcmix #play:before {
content: '';
position: absolute;
top: -30px;
left: -46px;
bottom: -30px;
right: -14px;
border-radius: 50%;
border: 3px solid #222e44 !important;
z-index: 2;
transition: all 0.1s;
-webkit-transition: all 0.1s;
-moz-transition: all 0.1s; }
#jcmix #play:after {
content: '';
opacity: 0;
transition: opacity 0.2s;
-webkit-transition: opacity 0.2s;
-moz-transition: opacity 0.2s; }
#jcmix #play:hover:before, #jcmix #play:focus:before {
transform: scale(1.1);
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1); }
#jcmix #play.active {
border-color: transparent !important; }
#jcmix #play.active:after {
content: '';
opacity: 1;
width: 20px;
height: 32px;
background: #222e44;
position: absolute;
right: 2px;
top: -16px;
border-left: 8px solid #222e44 !important;
box-shadow: inset 12px 0 0 0 #FFFFFF; }
#jcmix #start {
display: block;
float: left;
width: 0;
height: 0;
border-top: 18px solid transparent !important;
border-bottom: 18px solid transparent !important;
border-right: 21.6px solid #222e44 !important;
margin: 42px auto 18px auto;
position: relative;
z-index: 1;
transition: all 0.1s;
-webkit-transition: all 0.1s;
-moz-transition: all 0.1s; }
#jcmix #start:before {
content: '';
position: absolute;
top: -30px;
left: -18px;
bottom: -30px;
right: -42px;
border-radius: 50%;
border: 3px solid #222e44;
z-index: 2;
transition: all 0.1s;
-webkit-transition: all 0.1s;
-moz-transition: all 0.1s; }
#jcmix #start:after {
content: "";
display: block;
width: 5px;
height: 20px;
background: #222e44;
margin-top: -10px;
margin-left: -4px; }
#jcmix #start:hover:before, #jcmix #start:focus:before {
transform: scale(1.1);
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1); }
#jcmix #reset {
display: block;
width: 0;
float: left;
height: 0;
border: 10px solid transparent;
border-radius: 75%;
margin: 50px auto 30px auto;
position: relative;
z-index: 1;
transition: all 0.1s;
-webkit-transition: all 0.1s;
-moz-transition: all 0.1s;
left: 125px; }
#jcmix #reset:before {
content: '';
background-image: url('https://static1.squarespace.com/static/5932eaebf7e0ab84d67576df/t/5b00ca8ef950b7644aca672c/1526778510692/mixer_reset.png');
background-size: 100%;
background-position: center;
background-repeat: no-repeat;
position: absolute;
top: -30px;
left: -30px;
bottom: -30px;
right: -30px;
z-index: 2;
transition: all 0.1s;
-webkit-transition: all 0.1s;
-moz-transition: all 0.1s; }
#jcmix #reset:after {
content: '';
opacity: 0;
transition: opacity 0.2s;
-webkit-transition: opacity 0.2s;
-moz-transition: opacity 0.2s; }
#jcmix #reset:hover:before, #jcmix #reset:focus:before {
transform: scale(1.1);
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1); }
#jcmix #record {
display: block;
width: 0;
float: left;
height: 0;
border: 10px solid #222e44;
border-radius: 75%;
margin: 50px auto 30px auto;
position: relative;
z-index: 1;
transition: all 0.1s;
-webkit-transition: all 0.1s;
-moz-transition: all 0.1s;
left: 125px; }
#jcmix #record:before {
content: '';
position: absolute;
top: -30px;
left: -30px;
bottom: -30px;
right: -30px;
border-radius: 50%;
border: 2px solid #222e44;
z-index: 2;
transition: all 0.1s;
-webkit-transition: all 0.1s;
-moz-transition: all 0.1s; }
#jcmix #record:after {
content: '';
opacity: 0;
transition: opacity 0.2s;
-webkit-transition: opacity 0.2s;
-moz-transition: opacity 0.2s; }
#jcmix #record:hover:before, #jcmix #record:focus:before {
transform: scale(1.1);
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1); }
#jcmix #record.active {
border-color: red; }
#jcmix #recordinglist {
text-align: center;
list-style-type: none;
padding: 0;
overflow: auto; }
#jcmix #recordinglist li {
overflow: auto; }
#jcmix #recordinglist a, #jcmix #recordinglist audio {
clear: both;
float: left;
text-align: center;
width: 100%; }
#jcmix #recordinglist audio {
height: 50px; }
#jcmix #recordinglist a {
color: white;
background: #2f5379;
font-size: 10px; }
#jcmix #recordListButtons {
width: 100%;
text-align: center; }
#jcmix #recordListButtons #toggleRecordings, #jcmix #recordListButtons #clearRecordings {
display: none;
cursor: pointer;
padding: 5px;
margin: 5px;
background: #2f5379;
color: white;
font-size: 10px; }
#jcmix #recordListButtons #clearRecordings {
color: red; }
#jcmix .panner-range {
cursor: pointer;
position: absolute;
top: 8px;
left: 50px;
height: 30px; }
@media (max-width: 790px) {
#jcmix .panner-range {
left: 8px; } }
#jcmix .panner-range .dial {
cursor: text; }
/** a, a:link, a:hover, a:visited {
color: #7f7f7f; } **/
#nosupport {
font-family: "futura-pt"
background: red;
position: absolute;
top: 0;
left: 0;
right: 0;
-webkit-transition: all .25s ease;
-moz-transition: all .25s ease;
-ms-transition: all .25s ease;
-o-transition: all .25s ease;
transition: all .25s ease;
color: #FFF;
text-align: center;
padding: 30px;
z-index: 10; }
#nosupport a, #nosupport a:link, #nosupport a:hover, #nosupport a:visited {
color: #FFF; }
#jcmix {
/** max-width: 766px; **/
width: 100%;
min-width: 260px;
margin: 0 auto;
margin-top: 2rem;
height: 600px;
height: 400;
font-family: "futura-pt" }
#jcmix h1 {
text-align: center;
font-weight: 100; }
#jcmix h2 {
text-align: center; }
#jcmix h3 {
text-align: center; }
#jcmix #info {
display: none !important; }
@media (max-width: 935px) {
#jcmix {
max-width: 637px; } }
@media (max-width: 790px) {
#jcmix {
max-width: 360px; } }
#jcmix #mixer {
background: transparent !important; }
#jcmix a, #jcmix a:link, #jcmix a:hover, #jcmix a:visited {
color: #FFF; }
#jcmix * {
-webkit-touch-callout: none;
-webkit-user-select: none;
/* Disable selection/copy in UIWebView */ }
#jcmix h1 {
color: #FFF;
text-align: center; }
#jcmix #info {
display: block;
float: left;
margin-top: 10px;
color: #7f7f7f;
font-size: 10px;
width: 100%;
text-align: center; }
#jcmix .loader {
width: 100%;
height: 100%;
position: relative; }
#jcmix .loader .loader-inner {
position: absolute;
left: 50%;
top: 50%;
margin-top: -50px; }
#jcmix .loader p {
color: #222e44;
text-align: center;
width: 100%;
font-size: 0.8em;
position: relative;
top: 50%; }
#jcmix #timer {
/** font-family: "Share Tech Mono"; **/
font-family: "futura-pt";
color: #222e44;
font-size: 20px;
padding: 10px;
overflow: auto;
position: relative;
display: block;
clear: both;
background: transparent;
text-align: center; }
#jcmix #timer span {
display: inline-block;
text-align: left; }
#jcmix #timer span span {
width: 32.6px; }
#jcmix #timer .progress {
width: auto; }
#jcmix #timer .total {
margin-left: 5px;
width: auto; }
#jcmix canvas.meter {
display: block;
float: left;
margin-right: 50px;
margin-left: 10px;
margin-top: 8px; }
@media (max-width: 790px) {
#jcmix canvas.meter {
margin-right: 2px;
margin-top: 0; } }
#jcmix #transport {
clear: both;
display: block;
text-align: center;
/*width: 150px;*/
width: 226px;
height: 150px;
cursor: pointer;
position: relative;
margin: -20px auto 0 auto; }
#jcmix #transport.recording {
width: 215px; }
#jcmix #mixer {
background: #16191c;
position: relative;
overflow: auto;
display: block;
float: left;
opacity: 0;
margin-left: 14px !important;
z-index: 1; }
#jcmix #mixer #channelstrip {
position: relative;
overflow: hidden !important;
display: block;
float: left;
width: 100%; }
#jcmix #mixer #channelstrip div[data-channel] {
position: relative;
float: left;
display: block;
background: rgba(124, 124, 124, 0.2);
height: 100%;
margin: 5px;
padding: 5px;
padding-top: 50px; }
@media (min-width: 790px) {
#jcmix #mixer #channelstrip div[data-channel] {
-webkit-box-shadow: inset 0px 0px 5px 0px rgba(26, 27, 28, 0.75);
-moz-box-shadow: inset 0px 0px 5px 0px rgba(26, 27, 28, 0.75);
box-shadow: inset 0px 0px 5px 0px rgba(26, 27, 28, 0.75); } }
@media (max-width: 935px) {
#jcmix #mixer #channelstrip div[data-channel] {
margin: 0px;
padding: 0px;
padding-top: 50px; } }
#jcmix #mixer #channelstrip div[data-channel]:last-child {
background: #292929 !important; }
@media (max-width: 790px) {
#jcmix #mixer #channelstrip div[data-channel] {
background: none !important;
margin-bottom: 30px; }
#jcmix #mixer #channelstrip div[data-channel]:last-child {
background: none !important; } }
#jcmix #mixer #channelstrip div[data-channel]:last-child .track-label {
color: #FFFFFF !important;
cursor: default !important; }
#jcmix #mixer #channelstrip div[data-channel]:last-child .track-label label {
cursor: default !important; }
#jcmix #mixer #channelstrip div[data-channel] .slider {
display: block;
float: left;
-ms-transform: rotate(270deg);
/* IE 9 */
-webkit-transform: rotate(270deg);
/* Chrome, Safari, Opera */
transform: rotate(270deg);
position: absolute;
top: 221px;
left: -150px;
margin-top: 8px; }
@media (max-width: 935px) {
#jcmix #mixer #channelstrip div[data-channel] .slider {
left: -160px; } }
@media (max-width: 790px) {
#jcmix #mixer #channelstrip div[data-channel] .slider {
z-index: 1;
left: -193px;
margin-top: 0; } }
#jcmix #mixer #channelstrip div[data-channel] .track-label {
margin-top: 18px;
overflow: auto;
clear: both;
float: left;
background: #292929;
width: 85px;
color: #FFF;
text-align: center;
font-size: 13px;
padding-top: 5px;
padding-bottom: 5px;
cursor: pointer;
z-index: 1; }
#jcmix #mixer #channelstrip div[data-channel] .track-label input {
display: none; }
#jcmix #mixer #channelstrip div[data-channel] .track-label label {
cursor: pointer; }
#jcmix #mixer #channelstrip div[data-channel] .track-label label.label-mobile {
display: none; }
#jcmix #mixer #channelstrip div[data-channel] .track-label.soloed {
background: #d89f36;
color: #FFFFFF; }
@media (max-width: 790px) {
#jcmix #mixer #channelstrip div[data-channel] .track-label {
width: auto; }
#jcmix #mixer #channelstrip div[data-channel] .track-label label {
display: none; }
#jcmix #mixer #channelstrip div[data-channel] .track-label label.label-mobile {
display: block;
position: absolute;
left: 18px;
bottom: 58px;
font-size: 10px;
text-align: center;
background: none;
color: #000000;
cursor: default; } }
#jcmix .mute-button {
position: absolute;
left: 11px;
top: 5px;
font-size: 10px; }
#jcmix .mute-button span {
width: 18px;
text-align: center; }
#jcmix .mobile-solo-button {
display: none;
position: absolute;
left: 11px;
top: 5px;
font-size: 10px; }
#jcmix .mobile-solo-button span {
width: 18px;
text-align: center;
cursor: pointer; }
@media (max-width: 790px) {
#jcmix canvas.meter {
margin-bottom: 60px; }
#jcmix .track-label {
margin-top: 10px; }
#jcmix .mute-button {
left: 5px;
top: auto;
bottom: 15px;
z-index: 1; }
#jcmix .mute-button label span {
border: 1px solid #a4263b; }
#jcmix .mobile-solo-button {
display: block;
left: 5px;
top: auto;
bottom: -30px; }
#jcmix .mobile-solo-button label span {
border: 1px solid #d89f36; } }
#jcmix .paddingholder {
padding: 5px;
display: block;
clear: both;
position: relative; }
#jcmix #timeline {
background: #222e44;
height: 30px;
width: 100%;
cursor: pointer;
border-radius: 5px;
position: relative;
display: block;
clear: both; }
#jcmix #timeline #progress {
width: 4px;
height: 100%;
background: #d89f36;
border-radius: 5px;
position: absolute;
left: 0; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment