* wip
This commit is contained in:
parent
60302fc1fe
commit
c28d8f3e7f
|
|
@ -161,7 +161,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
var inputTracks = context.JK.TrackHelpers.getTracks(context.jamClient, 2);
|
var inputTracks = context.JK.TrackHelpers.getTracks(context.jamClient, 4);
|
||||||
|
|
||||||
// this is some ugly logic coming up, here's why:
|
// this is some ugly logic coming up, here's why:
|
||||||
// we need the id (guid) that the backend generated for the new track we just added
|
// we need the id (guid) that the backend generated for the new track we just added
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,11 @@
|
||||||
//= require react
|
//= require react
|
||||||
//= require react_ujs
|
//= require react_ujs
|
||||||
//= require react-init
|
//= require react-init
|
||||||
//= require react-components
|
|
||||||
//= require web/signup_helper
|
//= require web/signup_helper
|
||||||
//= require web/signin_helper
|
//= require web/signin_helper
|
||||||
//= require web/signin
|
//= require web/signin
|
||||||
//= require web/tracking
|
//= require web/tracking
|
||||||
|
//= require react-components
|
||||||
//= require_directory .
|
//= require_directory .
|
||||||
//= require_directory ./dialog
|
//= require_directory ./dialog
|
||||||
//= require_directory ./wizard
|
//= require_directory ./wizard
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 2) { // BACKEND_MIXER_CHANGE
|
if (type === 2) { // BACKEND_MIXER_CHANGE
|
||||||
|
|
||||||
|
context.MixerActions.mixersChanged(type, text)
|
||||||
|
|
||||||
if(context.JK.CurrentSessionModel)
|
if(context.JK.CurrentSessionModel)
|
||||||
context.JK.CurrentSessionModel.onBackendMixerChanged(type, text)
|
context.JK.CurrentSessionModel.onBackendMixerChanged(type, text)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
$ = jQuery
|
$ = jQuery
|
||||||
context = window
|
context = window
|
||||||
context.JK ||= {};
|
context.JK ||= {};
|
||||||
broadcastActions = context.JK.Actions.Broadcast
|
broadcastActions = @BroadcastActions
|
||||||
|
|
||||||
context.JK.ClientInit = class ClientInit
|
context.JK.ClientInit = class ClientInit
|
||||||
constructor: () ->
|
constructor: () ->
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
var $draggingFaderHandle = null;
|
var $draggingFaderHandle = null;
|
||||||
var $draggingFader = null;
|
var $draggingFader = null;
|
||||||
|
var $floater = null;
|
||||||
var draggingOrientation = null;
|
var draggingOrientation = null;
|
||||||
|
|
||||||
var logger = g.JK.logger;
|
var logger = g.JK.logger;
|
||||||
|
|
@ -20,6 +21,7 @@
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
var $fader = $(this);
|
var $fader = $(this);
|
||||||
|
var floaterConvert = $fader.data('floaterConverter')
|
||||||
var sessionModel = window.JK.CurrentSessionModel || null;
|
var sessionModel = window.JK.CurrentSessionModel || null;
|
||||||
|
|
||||||
var mediaControlsDisabled = $fader.data('media-controls-disabled');
|
var mediaControlsDisabled = $fader.data('media-controls-disabled');
|
||||||
|
|
@ -43,7 +45,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draggingOrientation = $fader.attr('orientation');
|
draggingOrientation = $fader.attr('data-orientation');
|
||||||
var offset = $fader.offset();
|
var offset = $fader.offset();
|
||||||
var position = { top: e.pageY - offset.top, left: e.pageX - offset.left}
|
var position = { top: e.pageY - offset.top, left: e.pageX - offset.left}
|
||||||
|
|
||||||
|
|
@ -53,6 +55,10 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(floaterConvert) {
|
||||||
|
window.JK.FaderHelpers.setFloaterValue($fader.find('.floater'), floaterConvert(faderPct))
|
||||||
|
}
|
||||||
|
|
||||||
$fader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false})
|
$fader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false})
|
||||||
|
|
||||||
setHandlePosition($fader, faderPct);
|
setHandlePosition($fader, faderPct);
|
||||||
|
|
@ -61,9 +67,9 @@
|
||||||
|
|
||||||
function setHandlePosition($fader, value) {
|
function setHandlePosition($fader, value) {
|
||||||
var ratio, position;
|
var ratio, position;
|
||||||
var $handle = $fader.find('div[control="fader-handle"]');
|
var $handle = $fader.find('div[data-control="fader-handle"]');
|
||||||
|
|
||||||
var orientation = $fader.attr('orientation');
|
var orientation = $fader.attr('data-orientation');
|
||||||
var handleCssAttribute = getHandleCssAttribute($fader);
|
var handleCssAttribute = getHandleCssAttribute($fader);
|
||||||
|
|
||||||
// required because this method is entered directly when from a callback
|
// required because this method is entered directly when from a callback
|
||||||
|
|
@ -81,7 +87,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function faderValue($fader, e, offset) {
|
function faderValue($fader, e, offset) {
|
||||||
var orientation = $fader.attr('orientation');
|
var orientation = $fader.attr('data-orientation');
|
||||||
var getPercentFunction = getVerticalFaderPercent;
|
var getPercentFunction = getVerticalFaderPercent;
|
||||||
var relativePosition = offset.top;
|
var relativePosition = offset.top;
|
||||||
if (orientation && orientation == 'horizontal') {
|
if (orientation && orientation == 'horizontal') {
|
||||||
|
|
@ -92,7 +98,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHandleCssAttribute($fader) {
|
function getHandleCssAttribute($fader) {
|
||||||
var orientation = $fader.attr('orientation');
|
var orientation = $fader.attr('data-orientation');
|
||||||
return (orientation === 'horizontal') ? 'left' : 'top';
|
return (orientation === 'horizontal') ? 'left' : 'top';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,12 +140,34 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simple snap feature to stick to the mid point
|
||||||
|
if(faderPct > 46 && faderPct < 54 && $draggingFader.data('snap')) {
|
||||||
|
faderPct = 50
|
||||||
|
var orientation = $draggingFader.attr('data-orientation');
|
||||||
|
if(orientation == 'horizontal') {
|
||||||
|
var width = $draggingFader.width()
|
||||||
|
var left = width / 2
|
||||||
|
ui.position.left = left
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var height = $draggingFader.height()
|
||||||
|
var top = height / 2
|
||||||
|
ui.position.top = top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var floaterConvert = $draggingFaderHandle.data('floaterConverter')
|
||||||
|
|
||||||
|
if(floaterConvert && $floater) {
|
||||||
|
window.JK.FaderHelpers.setFloaterValue($floater, floaterConvert(faderPct))
|
||||||
|
}
|
||||||
$draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: true})
|
$draggingFader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFaderDragStart(e, ui) {
|
function onFaderDragStart(e, ui) {
|
||||||
$draggingFaderHandle = $(this);
|
$draggingFaderHandle = $(this);
|
||||||
$draggingFader = $draggingFaderHandle.closest('div[control="fader"]');
|
$draggingFader = $draggingFaderHandle.closest('div[data-control="fader"]');
|
||||||
|
$floater = $draggingFaderHandle.find('.floater')
|
||||||
draggingOrientation = $draggingFader.attr('orientation');
|
draggingOrientation = $draggingFader.attr('orientation');
|
||||||
|
|
||||||
var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled');
|
var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled');
|
||||||
|
|
@ -210,12 +238,12 @@
|
||||||
|
|
||||||
selector.html(g._.template(templateSource, options));
|
selector.html(g._.template(templateSource, options));
|
||||||
|
|
||||||
selector.find('div[control="fader"]')
|
selector.find('div[data-control="fader"]')
|
||||||
.data('media-controls-disabled', selector.data('media-controls-disabled'))
|
.data('media-controls-disabled', selector.data('media-controls-disabled'))
|
||||||
.data('media-track-opener', selector.data('media-track-opener'))
|
.data('media-track-opener', selector.data('media-track-opener'))
|
||||||
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
|
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
|
||||||
|
|
||||||
selector.find('div[control="fader-handle"]').draggable({
|
selector.find('div[data-control="fader-handle"]').draggable({
|
||||||
drag: onFaderDrag,
|
drag: onFaderDrag,
|
||||||
start: onFaderDragStart,
|
start: onFaderDragStart,
|
||||||
stop: onFaderDragStop,
|
stop: onFaderDragStop,
|
||||||
|
|
@ -233,6 +261,43 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderFader2: function (selector, userOptions, floaterConverter) {
|
||||||
|
selector = $(selector);
|
||||||
|
if (userOptions === undefined) {
|
||||||
|
throw ("renderFader: userOptions is required");
|
||||||
|
}
|
||||||
|
var renderDefaults = {
|
||||||
|
faderType: "vertical"
|
||||||
|
};
|
||||||
|
var options = $.extend({}, renderDefaults, userOptions);
|
||||||
|
|
||||||
|
selector.find('div[data-control="fader"]')
|
||||||
|
.data('media-controls-disabled', selector.data('media-controls-disabled'))
|
||||||
|
.data('media-track-opener', selector.data('media-track-opener'))
|
||||||
|
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
|
||||||
|
.data('floaterConverter', floaterConverter)
|
||||||
|
.data('snap', userOptions.snap)
|
||||||
|
|
||||||
|
selector.find('div[data-control="fader-handle"]').draggable({
|
||||||
|
drag: onFaderDrag,
|
||||||
|
start: onFaderDragStart,
|
||||||
|
stop: onFaderDragStop,
|
||||||
|
containment: "parent",
|
||||||
|
axis: options.faderType === 'horizontal' ? 'x' : 'y'
|
||||||
|
}).data('media-controls-disabled', selector.data('media-controls-disabled'))
|
||||||
|
.data('media-track-opener', selector.data('media-track-opener'))
|
||||||
|
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
|
||||||
|
.data('floaterConverter', floaterConverter)
|
||||||
|
.data('snap', userOptions.snap)
|
||||||
|
|
||||||
|
// Embed any custom styles, applied to the .fader below selector
|
||||||
|
if ("style" in options) {
|
||||||
|
for (var key in options.style) {
|
||||||
|
selector.find(' .fader').css(key, options.style[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
convertLinearToDb: function (input) {
|
convertLinearToDb: function (input) {
|
||||||
|
|
||||||
// deal with extremes better
|
// deal with extremes better
|
||||||
|
|
@ -263,27 +328,48 @@
|
||||||
|
|
||||||
// composite function resembling audio taper
|
// composite function resembling audio taper
|
||||||
if (input <= 1) { return -80; }
|
if (input <= 1) { return -80; }
|
||||||
if (input <= 28) { return (2 * input - 80); }
|
if (input <= 28) { return Math.round((2 * input - 80)); } // -78 to -24 db
|
||||||
if (input <= 79) { return (0.5 * input - 38); }
|
if (input <= 79) { return Math.round((0.5 * input - 38)); } // -24 to 1.5 db
|
||||||
if (input < 99) { return (0.875 * input - 67.5); }
|
if (input < 99) { return Math.round((0.875 * input - 67.5)); } // 1.625 - 19.125 db
|
||||||
if (input >= 99) { return 20; }
|
if (input >= 99) { return 20; }
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
convertAudioTaperToPercent: function(db) {
|
||||||
|
if(db <= -78) { return 0}
|
||||||
|
if(db <= -24) { return (db + 80) / 2 }
|
||||||
|
if(db <= 1.5) { return (db + 38) / .5 }
|
||||||
|
if(db <= 19.125) { return (db + 67.5) / 0.875 }
|
||||||
|
return 100;
|
||||||
|
},
|
||||||
|
|
||||||
setFaderValue: function (faderId, faderValue) {
|
|
||||||
var $fader = $('[fader-id="' + faderId + '"]');
|
setFaderValue: function (faderId, faderValue, floaterValue) {
|
||||||
|
var $fader = $('[data-fader-id="' + faderId + '"]');
|
||||||
this.setHandlePosition($fader, faderValue);
|
this.setHandlePosition($fader, faderValue);
|
||||||
|
if(floaterValue !== undefined) {
|
||||||
|
var $floater = $fader.find('.floater')
|
||||||
|
this.setFloaterValue($floater, floaterValue)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showFader: function(faderId) {
|
||||||
|
var $fader = $('[data-fader-id="' + faderId + '"]');
|
||||||
|
$fader.find('div[data-control="fader-handle"]').show()
|
||||||
},
|
},
|
||||||
|
|
||||||
setHandlePosition: function ($fader, faderValue) {
|
setHandlePosition: function ($fader, faderValue) {
|
||||||
draggingOrientation = $fader.attr('orientation');
|
draggingOrientation = $fader.attr('data-orientation');
|
||||||
setHandlePosition($fader, faderValue);
|
setHandlePosition($fader, faderValue);
|
||||||
draggingOrientation = null;
|
draggingOrientation = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setFloaterValue: function($floater, floaterValue) {
|
||||||
|
$floater.text(floaterValue)
|
||||||
|
},
|
||||||
|
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
$('body').on('click', 'div[control="fader"]', faderClick);
|
$('body').on('click', 'div[data-control="fader"]', faderClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
context.JK = context.JK || {};
|
context.JK = context.JK || {};
|
||||||
context.JK.FakeJamClient = function(app, p2pMessageFactory) {
|
context.JK.FakeJamClient = function(app, p2pMessageFactory) {
|
||||||
|
var ChannelGroupIds = context.JK.ChannelGroupIds
|
||||||
var logger = context.JK.logger;
|
var logger = context.JK.logger;
|
||||||
logger.info("*** Fake JamClient instance initialized. ***");
|
logger.info("*** Fake JamClient instance initialized. ***");
|
||||||
|
|
||||||
|
|
@ -169,22 +170,22 @@
|
||||||
function FTUEGetMusicInputs() {
|
function FTUEGetMusicInputs() {
|
||||||
dbg('FTUEGetMusicInputs');
|
dbg('FTUEGetMusicInputs');
|
||||||
return {
|
return {
|
||||||
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1":
|
"i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1":
|
||||||
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
|
"Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function FTUEGetMusicOutputs() {
|
function FTUEGetMusicOutputs() {
|
||||||
dbg('FTUEGetMusicOutputs');
|
dbg('FTUEGetMusicOutputs');
|
||||||
return {
|
return {
|
||||||
"o~11~Multichannel (FW AP Multi)~0^o~11~Multichannel (FW AP Multi)~1":
|
"o~11~Multichannel (FWAPMulti)~0^o~11~Multichannel (FWAPMulti)~1":
|
||||||
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
|
"Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function FTUEGetChatInputs() {
|
function FTUEGetChatInputs() {
|
||||||
dbg('FTUEGetChatInputs');
|
dbg('FTUEGetChatInputs');
|
||||||
return {
|
return {
|
||||||
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1":
|
"i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1":
|
||||||
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
|
"Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function FTUEGetChannels() {
|
function FTUEGetChannels() {
|
||||||
|
|
@ -449,7 +450,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetASIODevices() {
|
function GetASIODevices() {
|
||||||
var response =[{"device_id":0,"device_name":"Realtek High Definition Audio","device_type": 0,"interfaces":[{"interface_id":0,"interface_name":"Realtek HDA SPDIF Out","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":1,"interface_name":"Realtek HD Audio rear output","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":2,"interface_name":"Realtek HD Audio Mic input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":3,"interface_name":"Realtek HD Audio Line input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":4,"interface_name":"Realtek HD Digital input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"}]},{"interface_id":5,"interface_name":"Realtek HD Audio Stereo input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]}],"wavert_supported":false},{"device_id":1,"device_name":"M-Audio FW Audiophile","device_type": 1,"interfaces":[{"interface_id":0,"interface_name":"FW AP Multi","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":1,"interface_name":"FW AP 1/2","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":2,"interface_name":"FW AP SPDIF","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":3,"interface_name":"FW AP 3/4","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":2,"device_name":"Virtual Audio Cable","device_type": 2,"interfaces":[{"interface_id":0,"interface_name":"Virtual Cable 2","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]},{"interface_id":1,"interface_name":"Virtual Cable 1","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":3,"device_name":"WebCamDV WDM Audio Capture","device_type": 3,"interfaces":[{"interface_id":0,"interface_name":"WebCamDV Audio","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"},{"is_input":false,"pin_id":1,"pin_name":"Volume Control"}]}],"wavert_supported":false}];
|
var response =[{"device_id":0,"device_name":"Realtek High Definition Audio","device_type": 0,"interfaces":[{"interface_id":0,"interface_name":"Realtek HDA SPDIF Out","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":1,"interface_name":"Realtek HD Audio rear output","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":2,"interface_name":"Realtek HD Audio Mic input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":3,"interface_name":"Realtek HD Audio Line input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":4,"interface_name":"Realtek HD Digital input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"}]},{"interface_id":5,"interface_name":"Realtek HD Audio Stereo input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]}],"wavert_supported":false},{"device_id":1,"device_name":"M-Audio FW Audiophile","device_type": 1,"interfaces":[{"interface_id":0,"interface_name":"FWAPMulti","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":1,"interface_name":"FW AP 1/2","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":2,"interface_name":"FW AP SPDIF","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":3,"interface_name":"FW AP 3/4","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":2,"device_name":"Virtual Audio Cable","device_type": 2,"interfaces":[{"interface_id":0,"interface_name":"Virtual Cable 2","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]},{"interface_id":1,"interface_name":"Virtual Cable 1","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":3,"device_name":"WebCamDV WDM Audio Capture","device_type": 3,"interfaces":[{"interface_id":0,"interface_name":"WebCamDV Audio","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"},{"is_input":false,"pin_id":1,"pin_name":"Volume Control"}]}],"wavert_supported":false}];
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -474,12 +475,22 @@
|
||||||
}
|
}
|
||||||
function SessionGetControlState(mixerIds, isMasterOrPersonal) {
|
function SessionGetControlState(mixerIds, isMasterOrPersonal) {
|
||||||
dbg("SessionGetControlState");
|
dbg("SessionGetControlState");
|
||||||
var groups = [0, 1, 2, 3, 3, 7, 8, 10, 11, 12];
|
var groups =
|
||||||
|
[ChannelGroupIds.MasterGroup,
|
||||||
|
ChannelGroupIds.MonitorGroup,
|
||||||
|
ChannelGroupIds.AudioInputMusicGroup,
|
||||||
|
ChannelGroupIds.AudioInputChatGroup,
|
||||||
|
ChannelGroupIds.AudioInputChatGroup,
|
||||||
|
ChannelGroupIds.UserMusicInputGroup,
|
||||||
|
ChannelGroupIds.UserChatInputGroup,
|
||||||
|
ChannelGroupIds.PeerMediaTrackGroup,
|
||||||
|
ChannelGroupIds.JamTrackGroup,
|
||||||
|
ChannelGroupIds.MetronomeGroup];
|
||||||
var names = [
|
var names = [
|
||||||
"FW AP Multi",
|
"FWAPMulti",
|
||||||
"FW AP Multi",
|
"FWAPMulti",
|
||||||
"FW AP Multi",
|
"FWAPMulti",
|
||||||
"FW AP Multi",
|
"FWAPMulti",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
|
|
@ -533,6 +544,7 @@
|
||||||
stereo: true,
|
stereo: true,
|
||||||
volume_left: -40,
|
volume_left: -40,
|
||||||
volume_right:-40,
|
volume_right:-40,
|
||||||
|
pan: 0,
|
||||||
instrument_id:50, // see globals.js
|
instrument_id:50, // see globals.js
|
||||||
mode: isMasterOrPersonal,
|
mode: isMasterOrPersonal,
|
||||||
rid: mixerIds[i]
|
rid: mixerIds[i]
|
||||||
|
|
@ -542,10 +554,10 @@
|
||||||
}
|
}
|
||||||
function SessionGetIDs() {
|
function SessionGetIDs() {
|
||||||
return [
|
return [
|
||||||
"FW AP Multi_0_10000",
|
"FWAPMulti_0_10000",
|
||||||
"FW AP Multi_1_10100",
|
"FWAPMulti_1_10100",
|
||||||
"FW AP Multi_2_10200",
|
"FWAPMulti_2_10200",
|
||||||
"FW AP Multi_3_10500",
|
"FWAPMulti_3_10500",
|
||||||
"User@208.191.152.98#",
|
"User@208.191.152.98#",
|
||||||
"User@208.191.152.98_*"
|
"User@208.191.152.98_*"
|
||||||
];
|
];
|
||||||
|
|
@ -612,9 +624,9 @@
|
||||||
|
|
||||||
function doCallbacks() {
|
function doCallbacks() {
|
||||||
var names = ["vu"];
|
var names = ["vu"];
|
||||||
//var ids = ["FW AP Multi_2_10200", "FW AP Multi_0_10000"];
|
//var ids = ["FWAPMulti_2_10200", "FWAPMulti_0_10000"];
|
||||||
var ids= ["i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1",
|
var ids= ["i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1",
|
||||||
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~2"];
|
"i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~2"];
|
||||||
|
|
||||||
var args = [];
|
var args = [];
|
||||||
for (var i=0; i<ids.length; i++) {
|
for (var i=0; i<ids.length; i++) {
|
||||||
|
|
|
||||||
|
|
@ -313,4 +313,25 @@
|
||||||
context.JK.NAMED_MESSAGES = {
|
context.JK.NAMED_MESSAGES = {
|
||||||
MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix'
|
MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix'
|
||||||
}
|
}
|
||||||
})(window,jQuery);
|
|
||||||
|
context.JK.ChannelGroupIds = {
|
||||||
|
"MasterGroup": 0,
|
||||||
|
"MonitorGroup": 1,
|
||||||
|
"MasterCatGroup" : 2,
|
||||||
|
"MonitorCatGroup" : 3,
|
||||||
|
"AudioInputMusicGroup": 4,
|
||||||
|
"AudioInputChatGroup": 5,
|
||||||
|
"MediaTrackGroup": 6,
|
||||||
|
"StreamOutMusicGroup": 7,
|
||||||
|
"StreamOutChatGroup": 8,
|
||||||
|
"StreamOutMediaGroup" : 9,
|
||||||
|
"UserMusicInputGroup": 10,
|
||||||
|
"UserChatInputGroup": 11,
|
||||||
|
"UserMediaInputGroup": 12,
|
||||||
|
"PeerAudioInputMusicGroup": 13,
|
||||||
|
"PeerMediaTrackGroup": 14,
|
||||||
|
"JamTrackGroup": 15,
|
||||||
|
"MetronomeGroup": 16
|
||||||
|
};
|
||||||
|
|
||||||
|
})(window,jQuery);
|
||||||
|
|
@ -478,6 +478,14 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteParticipant(clientId) {
|
||||||
|
var url = "/api/participants/" + lientId;
|
||||||
|
return $.ajax({
|
||||||
|
type: "DELETE",
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function login(options) {
|
function login(options) {
|
||||||
var url = '/api/auths/login';
|
var url = '/api/auths/login';
|
||||||
|
|
||||||
|
|
@ -1854,6 +1862,7 @@
|
||||||
this.addRecordingLike = addRecordingLike;
|
this.addRecordingLike = addRecordingLike;
|
||||||
this.addPlayablePlay = addPlayablePlay;
|
this.addPlayablePlay = addPlayablePlay;
|
||||||
this.getSession = getSession;
|
this.getSession = getSession;
|
||||||
|
this.deleteParticipant = deleteParticipant;
|
||||||
this.getClientDownloads = getClientDownloads;
|
this.getClientDownloads = getClientDownloads;
|
||||||
this.createEmailInvitations = createEmailInvitations;
|
this.createEmailInvitations = createEmailInvitations;
|
||||||
this.createMusicianInvite = createMusicianInvite;
|
this.createMusicianInvite = createMusicianInvite;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
context = window
|
||||||
|
$ = jQuery
|
||||||
|
|
||||||
|
panHelper = class PanHelper
|
||||||
|
|
||||||
|
###
|
||||||
|
Convert the pan value that comes from a backend mixer
|
||||||
|
to a 0-100 % usable by a draggable panner element
|
||||||
|
###
|
||||||
|
convertPanToPercent: (mixerPan) ->
|
||||||
|
value = (((mixerPan + 90) / 90) * 100) / 2
|
||||||
|
|
||||||
|
if value < 0
|
||||||
|
0
|
||||||
|
else if value > 100
|
||||||
|
100
|
||||||
|
else
|
||||||
|
value
|
||||||
|
|
||||||
|
###
|
||||||
|
Convert the % value of a draggable panner element
|
||||||
|
to a mixer-ready pan value
|
||||||
|
###
|
||||||
|
convertPercentToPan: (percent) ->
|
||||||
|
value = 2 * percent / 100 * 90 - 90
|
||||||
|
|
||||||
|
if value < -90
|
||||||
|
-90
|
||||||
|
else if value > 90
|
||||||
|
90
|
||||||
|
else
|
||||||
|
Math.round(value)
|
||||||
|
|
||||||
|
context.JK.PanHelpers = new panHelper()
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
//= require_directory ./react-components/helpers
|
||||||
//= require_directory ./react-components/actions
|
//= require_directory ./react-components/actions
|
||||||
|
//= require ./react-components/stores/AppStore
|
||||||
|
//= require ./react-components/stores/SessionStore
|
||||||
|
//= require ./react-components/stores/MixerStore
|
||||||
|
//= require ./react-components/stores/SessionMyTracksStore
|
||||||
//= require_directory ./react-components/stores
|
//= require_directory ./react-components/stores
|
||||||
//= require_directory ./react-components
|
//= require_directory ./react-components
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
MixerActions = @MixerActions
|
||||||
|
|
||||||
|
@SessionMyTrack = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged")]
|
||||||
|
|
||||||
|
onInputsChanged: (sessionMixers) ->
|
||||||
|
|
||||||
|
mixers = sessionMixers.mixers
|
||||||
|
newMixers = mixers.findMixerForTrack.apply(mixers, this.props.mixerFinder)
|
||||||
|
|
||||||
|
this.setState({mixers: newMixers})
|
||||||
|
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{mixers: this.props.mixers}
|
||||||
|
|
||||||
|
handleMute: (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
muting = $(e.currentTarget).is('.enabled')
|
||||||
|
|
||||||
|
MixerActions.mute([this.state.mixers.mixer, this.state.mixers.oppositeMixer], muting)
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
|
||||||
|
muteMixer = this.state.mixers.muteMixer
|
||||||
|
vuMixer = this.state.mixers.vuMixer
|
||||||
|
|
||||||
|
classes = React.addons.classSet({
|
||||||
|
'track-icon-mute': true
|
||||||
|
'enabled' : !muteMixer.mute
|
||||||
|
'muted' : muteMixer.mute
|
||||||
|
})
|
||||||
|
|
||||||
|
`<div className="session-track my-track">
|
||||||
|
<div className="name">{this.props.name}</div>
|
||||||
|
<div className="track-avatar"><img src={this.props.photoUrl}/></div>
|
||||||
|
<div className="track-instrument"><img height="45" src={this.props.instrumentIcon} width="45" /></div>
|
||||||
|
<div className="track-controls">
|
||||||
|
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" mixers={this.state.mixers} />
|
||||||
|
<div className="track-buttons">
|
||||||
|
<div className={classes} data-control="mute" data-mixer-id={muteMixer.id} onClick={this.handleMute}/>
|
||||||
|
<div className="track-icon-pan"/>
|
||||||
|
<div className="track-icon-equalizer" />
|
||||||
|
</div>
|
||||||
|
<br className="clearall"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<br className="clearall"/>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
componentDidMount: () ->
|
||||||
|
$root = jQuery(this.getDOMNode())
|
||||||
|
$mute = $root.find('.track-icon-mute')
|
||||||
|
$pan = $root.find('.track-icon-pan')
|
||||||
|
|
||||||
|
context.JK.interactReactBubble(
|
||||||
|
$mute,
|
||||||
|
'SessionTrackVolumeHover',
|
||||||
|
{mixers:this.state.mixers, mixerFinder: this.props.mixerFinder},
|
||||||
|
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||||
|
|
||||||
|
context.JK.interactReactBubble(
|
||||||
|
$pan,
|
||||||
|
'SessionTrackPanHover',
|
||||||
|
{mixers:this.state.mixers, mixerFinder: this.props.mixerFinder},
|
||||||
|
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -1,23 +1,75 @@
|
||||||
context = window
|
context = window
|
||||||
|
|
||||||
|
|
||||||
|
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
||||||
|
|
||||||
|
|
||||||
@SessionMyTracks = React.createClass({
|
@SessionMyTracks = React.createClass({
|
||||||
|
|
||||||
render: () ->
|
mixins: [Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||||
|
|
||||||
noTracksNotice = `<div className="session-mytracks-notracks">
|
onInputsChanged: (sessionMixers) ->
|
||||||
<p className="notice">
|
|
||||||
You have not set up any inputs for your instrument or vocals.
|
|
||||||
If you want to hear yourself play through the JamKazam app,
|
|
||||||
and let the app mix your live playing with JamTracks, or with other musicians in online sessions,
|
|
||||||
<a href="#" className="open-ftue-no-tracks">click here now.</a>
|
|
||||||
</p>
|
|
||||||
</div>`
|
|
||||||
|
|
||||||
`<div className="session-my-tracks">
|
session = sessionMixers.session
|
||||||
<h2>my live tracks</h2>
|
mixers = sessionMixers.mixers
|
||||||
<SessionTrackSettingsBtn />
|
|
||||||
<div className="session-tracks-scroller">
|
tracks = []
|
||||||
{noTracksNotice}
|
|
||||||
</div>
|
if session.inSession()
|
||||||
</div>`
|
participant = session.getParticipant(@app.clientId)
|
||||||
})
|
|
||||||
|
name = participant.user.name;
|
||||||
|
|
||||||
|
for track in participant.tracks
|
||||||
|
# try to find mixer info for this track
|
||||||
|
mixerFinder = [participant.client_id, track, true]
|
||||||
|
mixerData = mixers.findMixerForTrack(participant.client_id, track, true)
|
||||||
|
mixerData.mixerFinder = mixerFinder # so that other callers can re-find their mixer data
|
||||||
|
|
||||||
|
# todo: sessionModel.setAudioEstablished
|
||||||
|
|
||||||
|
instrumentIcon = context.JK.getInstrumentIcon45(track.instrument_id);
|
||||||
|
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url);
|
||||||
|
|
||||||
|
tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, name: name, instrumentIcon: instrumentIcon, photoUrl: photoUrl})
|
||||||
|
|
||||||
|
# TODO: also deal with chat
|
||||||
|
|
||||||
|
this.setState(tracks: tracks, session:session)
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
|
||||||
|
content = null
|
||||||
|
tracks = []
|
||||||
|
|
||||||
|
if this.state.tracks.length > 0
|
||||||
|
for track in this.state.tracks
|
||||||
|
tracks.push(`<SessionMyTrack key={track.track.client_track_id} {...track} />`)
|
||||||
|
|
||||||
|
else if this.state.session? && this.state.session.inSession()
|
||||||
|
content = `<div className="session-mytracks-notracks">
|
||||||
|
<p className="notice">
|
||||||
|
You have not set up any inputs for your instrument or vocals.
|
||||||
|
If you want to hear yourself play through the JamKazam app,
|
||||||
|
and let the app mix your live playing with JamTracks, or with other musicians in online sessions,
|
||||||
|
<a href="#" className="open-ftue-no-tracks">click here now.</a>
|
||||||
|
</p>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
`<div className="session-my-tracks">
|
||||||
|
<h2>my live tracks</h2>
|
||||||
|
<SessionTrackSettingsBtn />
|
||||||
|
<div className="session-tracks-scroller">
|
||||||
|
{content}
|
||||||
|
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
|
||||||
|
{tracks}
|
||||||
|
</ReactCSSTransitionGroup>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
getInitialState:() ->
|
||||||
|
{tracks:[], session: null}
|
||||||
|
|
||||||
|
onAppInit: (app) ->
|
||||||
|
@app = app
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
context = window
|
context = window
|
||||||
|
|
||||||
|
SessionActions = @SessionActions
|
||||||
|
|
||||||
@SessionScreen = React.createClass({
|
@SessionScreen = React.createClass({
|
||||||
|
|
||||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||||
|
|
@ -16,7 +18,7 @@ context = window
|
||||||
<SessionResyncBtn />
|
<SessionResyncBtn />
|
||||||
<SessionLeaveBtn />
|
<SessionLeaveBtn />
|
||||||
</div>
|
</div>
|
||||||
<div className="tracks">
|
<div className="tracks" id="new-tracks">
|
||||||
<SessionMyTracks />
|
<SessionMyTracks />
|
||||||
<SessionOtherTracks />
|
<SessionOtherTracks />
|
||||||
<SessionMediaTracks />
|
<SessionMediaTracks />
|
||||||
|
|
@ -27,12 +29,14 @@ context = window
|
||||||
componentDidMount: () ->
|
componentDidMount: () ->
|
||||||
@logger = context.JK.logger
|
@logger = context.JK.logger
|
||||||
|
|
||||||
beforeShow: () ->
|
beforeShow: (data) =>
|
||||||
@logger.debug("session beforeShow")
|
@logger.debug("session beforeShow")
|
||||||
|
|
||||||
afterShow: () ->
|
afterShow: (data) ->
|
||||||
@logger.debug("session afterShow")
|
@logger.debug("session afterShow")
|
||||||
|
|
||||||
|
SessionActions.joinSession.trigger(data.id)
|
||||||
|
|
||||||
beforeHide: () ->
|
beforeHide: () ->
|
||||||
@logger.debug("session beforeHide")
|
@logger.debug("session beforeHide")
|
||||||
|
|
||||||
|
|
@ -44,7 +48,6 @@ context = window
|
||||||
|
|
||||||
onAppInit: (@app) ->
|
onAppInit: (@app) ->
|
||||||
|
|
||||||
@logger.debug("oh hai")
|
|
||||||
screenBindings = {
|
screenBindings = {
|
||||||
'beforeShow': @beforeShow,
|
'beforeShow': @beforeShow,
|
||||||
'afterShow': @afterShow,
|
'afterShow': @afterShow,
|
||||||
|
|
@ -54,4 +57,5 @@ context = window
|
||||||
};
|
};
|
||||||
|
|
||||||
@app.bindScreen('session2', screenBindings);
|
@app.bindScreen('session2', screenBindings);
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
context = window
|
||||||
|
logger = context.JK.logger
|
||||||
|
|
||||||
|
@SessionTrackGain = React.createClass({
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{
|
||||||
|
mixers: this.props.mixers,
|
||||||
|
behaviors: this.props.behaviors || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
faderChanged: (e, data) ->
|
||||||
|
$target = $(this)
|
||||||
|
groupId = $target.data('groupId')
|
||||||
|
mixerIds = [this.state.mixers.mixer.id]
|
||||||
|
|
||||||
|
MixerActions.faderChanged(data, mixerIds, groupId)
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
`<div className="track-gain">
|
||||||
|
<div className="fader vertical" data-control="fader" data-fader-id={this.state.mixers.mixer.id} data-orientation="vertical">
|
||||||
|
<div className="handle" data-control="fader-handle">
|
||||||
|
<img src="/assets/content/slider_gain_vertical.png" width="28" height="11"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
componentDidMount: () ->
|
||||||
|
$root = jQuery(this.getDOMNode())
|
||||||
|
if !$root.is('.track-gain')
|
||||||
|
logger.error("unknown root node")
|
||||||
|
|
||||||
|
$fader = $root.attr('data-mixer-id', this.state.mixers.mixer.id).data('groupId', this.state.mixers.mixer.groupId).data('mixer', this.state.mixers.mixer).data('opposite-mixer', this.state.mixers.oppositeMixer)
|
||||||
|
|
||||||
|
if this.state.behaviors.mediaControlsDisabled
|
||||||
|
$fader.data('media-controls-disabled', true).data('media-track-opener', this.state.behaviors.mediaTrackOpener) # this we be applied later to the fader handle $element
|
||||||
|
|
||||||
|
$fader.data('showHelpAboutMediaMixers', this.state.behaviors.showHelpAboutMediaMixers)
|
||||||
|
|
||||||
|
context.JK.FaderHelpers.renderFader2($fader, {faderType: 'vertical'});
|
||||||
|
|
||||||
|
# Initialize gain position
|
||||||
|
MixerActions.initGain(this.state.mixers.mixer)
|
||||||
|
|
||||||
|
# watch for fader change events
|
||||||
|
$fader.on('fader_change', this.faderChanged);
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
context = window
|
||||||
|
logger = context.JK.logger
|
||||||
|
|
||||||
|
@SessionTrackPan = React.createClass({
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{
|
||||||
|
mixers: this.props.mixers,
|
||||||
|
behaviors: this.props.behaviors || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
panChanged: (e, data) ->
|
||||||
|
$target = $(this)
|
||||||
|
groupId = $target.data('groupId')
|
||||||
|
mixerIds = [this.state.mixers.mixer.id]
|
||||||
|
|
||||||
|
MixerActions.panChanged(data, mixerIds, groupId)
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
`<div className="track-pan">
|
||||||
|
<div className="left-label">Left</div>
|
||||||
|
<div className="right-label">Right</div>
|
||||||
|
<div className="fader horizontal" data-control="fader" data-fader-id={this.state.mixers.mixer.id} data-orientation="horizontal">
|
||||||
|
<div className="handle" data-control="fader-handle">
|
||||||
|
<div className="floater"></div>
|
||||||
|
<img src="/assets/content/slider_gain_horiz.png" width="11" height="28"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
componentDidMount: () ->
|
||||||
|
$root = jQuery(this.getDOMNode())
|
||||||
|
if !$root.is('.track-pan')
|
||||||
|
logger.error("unknown root node")
|
||||||
|
|
||||||
|
$fader = $root.attr('data-mixer-id', this.state.mixers.mixer.id).data('groupId', this.state.mixers.mixer.groupId).data('mixer', this.state.mixers.mixer).data('opposite-mixer', this.state.mixers.oppositeMixer)
|
||||||
|
|
||||||
|
if this.state.behaviors.mediaControlsDisabled
|
||||||
|
$fader.data('media-controls-disabled', true).data('media-track-opener', this.state.behaviors.mediaTrackOpener) # this we be applied later to the fader handle $element
|
||||||
|
|
||||||
|
$fader.data('showHelpAboutMediaMixers', this.state.behaviors.showHelpAboutMediaMixers)
|
||||||
|
|
||||||
|
context.JK.FaderHelpers.renderFader2($fader, {faderType: 'horizontal', snap:true}, context.JK.PanHelpers.convertPercentToPan)
|
||||||
|
|
||||||
|
# Initialize gain position
|
||||||
|
MixerActions.initPan(this.state.mixers.mixer)
|
||||||
|
|
||||||
|
# watch for fader change events
|
||||||
|
$fader.on('fader_change', this.panChanged)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
MixerActions = @MixerActions
|
||||||
|
|
||||||
|
@SessionTrackPanHover = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Reflux.listenTo(@SessionMyTracksStore, "onInputsChanged")]
|
||||||
|
|
||||||
|
onInputsChanged: (sessionMixers) ->
|
||||||
|
mixers = sessionMixers.mixers
|
||||||
|
newMixers = mixers.findMixerForTrack.apply(mixers, this.props.mixerFinder)
|
||||||
|
|
||||||
|
this.setState({mixers: newMixers})
|
||||||
|
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{mixers: this.props.mixers}
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`<div className="track-pan-hover">
|
||||||
|
<div className="textual-help">
|
||||||
|
<p>
|
||||||
|
Use this slider to pan the audio of this track left or right in your personal mix.
|
||||||
|
This will not pan audio for other musicians in the session.
|
||||||
|
To pan audio in the master mix for recordings and broadcasts, use the Mixer button in the toolbar.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="session-pan">
|
||||||
|
<SessionTrackPan mixers={this.state.mixers} />
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -1,9 +1,22 @@
|
||||||
context = window
|
context = window
|
||||||
|
|
||||||
|
logger = context.JK.logger
|
||||||
|
|
||||||
@SessionTrackSettingsBtn = React.createClass({
|
@SessionTrackSettingsBtn = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||||
|
|
||||||
|
onConfigureSettings: (e) ->
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
@app.layout.showDialog('configure-tracks')
|
||||||
|
|
||||||
|
onAppInit: (app) ->
|
||||||
|
@app = app
|
||||||
|
|
||||||
render: () ->
|
render: () ->
|
||||||
`<a className="session-track-settings">
|
`<div className="session-track-settings" onClick={this.onConfigureSettings}>
|
||||||
Settings
|
<img src="/assets/content/icon_settings_lg.png" width="18" height="18" />
|
||||||
</a>`
|
<span>Settings</span>
|
||||||
|
</div>`
|
||||||
})
|
})
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
@SessionTrackVU = React.createClass({
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{mixers: this.props.mixers}
|
||||||
|
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
lights = []
|
||||||
|
redSwitch = Math.round(this.props.lightCount * 0.66);
|
||||||
|
lightClass = 'vu-red-off'
|
||||||
|
|
||||||
|
if this.props.orientation == 'horizontal'
|
||||||
|
|
||||||
|
for i in [0..this.props.lightCount-1]
|
||||||
|
lightClass = if i >= redSwitch then 'vu-red-off' else 'vu-green-off'
|
||||||
|
|
||||||
|
lightClasses = React.addons.classSet('vulight', 'vu' + i, lightClass)
|
||||||
|
|
||||||
|
lights.push(`<td width={this.props.lightWidth} height={this.props.lightHeight} className={lightClasses}></td>`)
|
||||||
|
|
||||||
|
tableClasses = React.addons.classSet('vu', 'horizontal', this.props.side + '-' + this.state.mixers.mixer.mixerId)
|
||||||
|
|
||||||
|
`<table className={tableClasses} data-light-count={this.props.lightCount}>
|
||||||
|
<tr>
|
||||||
|
{lights}
|
||||||
|
</tr>
|
||||||
|
</table>`
|
||||||
|
else
|
||||||
|
|
||||||
|
for i in [0..this.props.lightCount-1].reverse()
|
||||||
|
lightClass = if (i >= redSwitch) then "vu-red-off" else "vu-green-off"
|
||||||
|
|
||||||
|
lightClasses = React.addons.classSet('vulight', 'vu' + i, lightClass)
|
||||||
|
|
||||||
|
lights.push(`<tr><td width={this.props.lightWidth} height={this.props.lightHeight} className={lightClasses}></td></tr>`)
|
||||||
|
|
||||||
|
tableClasses = React.addons.classSet('vu', 'vertical', this.props.side + '-' + this.state.mixers.mixer.mixerId)
|
||||||
|
|
||||||
|
`<table className={tableClasses} data-light-count={this.props.lightCount}>
|
||||||
|
{lights}
|
||||||
|
</table>`
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
MixerActions = @MixerActions
|
||||||
|
|
||||||
|
@SessionTrackVolumeHover = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged")]
|
||||||
|
|
||||||
|
onInputsChanged: (sessionMixers) ->
|
||||||
|
|
||||||
|
mixers = sessionMixers.mixers
|
||||||
|
newMixers = mixers.findMixerForTrack.apply(mixers, this.props.mixerFinder)
|
||||||
|
|
||||||
|
this.setState({mixers: newMixers})
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{mixers: this.props.mixers}
|
||||||
|
|
||||||
|
handleMute: (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
muting = $(e.currentTarget).is('.enabled')
|
||||||
|
|
||||||
|
MixerActions.mute([this.state.mixers.mixer, this.state.mixers.oppositeMixer], muting)
|
||||||
|
|
||||||
|
handleMuteCheckbox: (e) ->
|
||||||
|
muting = $(e.target).is(':checked')
|
||||||
|
|
||||||
|
MixerActions.mute([this.state.mixers.mixer, this.state.mixers.oppositeMixer], muting)
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
|
||||||
|
muteMixer = this.state.mixers.muteMixer
|
||||||
|
|
||||||
|
classes = React.addons.classSet({
|
||||||
|
'track-icon-mute': true
|
||||||
|
'enabled' : !muteMixer.mute
|
||||||
|
'muted' : muteMixer.mute
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
`<div className="track-volume-hover">
|
||||||
|
<div className="session-track track">
|
||||||
|
<div className="track-vu-left">
|
||||||
|
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vul" mixers={this.state.mixers} />
|
||||||
|
</div>
|
||||||
|
<div className="track-vu-right">
|
||||||
|
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vur" mixers={this.state.mixers} />
|
||||||
|
</div>
|
||||||
|
<div className="track-label">
|
||||||
|
<div>Volume</div>
|
||||||
|
<div>{this.state.mixers.mixer.volume_left}dB</div>
|
||||||
|
</div>
|
||||||
|
<SessionTrackGain mixers={this.state.mixers} />
|
||||||
|
<div className={classes} data-control="mute" data-mixer-id={this.state.mixers.muteMixer.id} onClick={this.handleMute}/>
|
||||||
|
|
||||||
|
<input type="checkbox" name="mute"/>
|
||||||
|
<label for="mute">Mute</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="textual-help">
|
||||||
|
<p>Use this slider to control the volume of this track in your personal mix.</p>
|
||||||
|
<p>This will not affect the volume of this track for other musicians in the session.</p>
|
||||||
|
<p>To adjust master levels for all musicians for recordings and broadcasts, use Mixer button in the toolbar.</p>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
componentDidMount: () ->
|
||||||
|
$root = jQuery(this.getDOMNode())
|
||||||
|
|
||||||
|
# initialize icheck
|
||||||
|
$checkbox = $root.find('input')
|
||||||
|
context.JK.checkbox($checkbox)
|
||||||
|
$checkbox.on('ifChanged', this.handleMuteCheckbox);
|
||||||
|
|
||||||
|
if this.state.mixers.muteMixer.mute
|
||||||
|
$checkbox.iCheck('check').attr('checked', true)
|
||||||
|
else
|
||||||
|
$checkbox.iCheck('uncheck').attr('checked', false)
|
||||||
|
|
||||||
|
componentWillUpdate: (nextProps, nextState) ->
|
||||||
|
$root = jQuery(this.getDOMNode())
|
||||||
|
|
||||||
|
# re-initialize icheck
|
||||||
|
$checkbox = $root.find('input')
|
||||||
|
|
||||||
|
if nextState.mixers.muteMixer.mute
|
||||||
|
$checkbox.iCheck('check').attr('checked', true)
|
||||||
|
else
|
||||||
|
$checkbox.iCheck('uncheck').attr('checked', false)
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
@TestComponent = React.createClass({
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{something: 1}
|
||||||
|
|
||||||
|
tick: () ->
|
||||||
|
console.log("tick")
|
||||||
|
this.setState({something: this.state.something + 1})
|
||||||
|
|
||||||
|
componentDidMount: () ->
|
||||||
|
console.log("here")
|
||||||
|
setInterval(@tick, 1000)
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
console.log("render")
|
||||||
|
`<div>{this.state.something}</div>`
|
||||||
|
})
|
||||||
|
|
@ -3,5 +3,3 @@ context = window
|
||||||
@AppActions = Reflux.createActions({
|
@AppActions = Reflux.createActions({
|
||||||
appInit: {}
|
appInit: {}
|
||||||
})
|
})
|
||||||
|
|
||||||
context.JK.Actions.App = @AppActions
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
context = window
|
context = window
|
||||||
|
|
||||||
BroadcastActions = Reflux.createActions({
|
@BroadcastActions = Reflux.createActions({
|
||||||
load: {asyncResult: true},
|
load: {asyncResult: true},
|
||||||
hide: {}
|
hide: {}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
@MixerActions = Reflux.createActions({
|
||||||
|
mute: {}
|
||||||
|
faderChanged: {}
|
||||||
|
initGain: {}
|
||||||
|
panChanged: {}
|
||||||
|
initPan: {}
|
||||||
|
mixersChanged: {}
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
@SessionActions = Reflux.createActions({
|
||||||
|
joinSession: {}
|
||||||
|
leaveSession: {}
|
||||||
|
resyncServer: {asyncResult: true}
|
||||||
|
myTracksChanged: {}
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
@SessionMyTracksActions = Reflux.createActions({
|
||||||
|
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,461 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
ChannelGroupIds = context.JK.ChannelGroupIds
|
||||||
|
MIX_MODES = context.JK.MIX_MODES;
|
||||||
|
|
||||||
|
|
||||||
|
@MixerHelper = class MixerHelper
|
||||||
|
|
||||||
|
constructor: (@session, @masterMixers, @personalMixers, @mixMode) ->
|
||||||
|
@mixersByResourceId = {}
|
||||||
|
@mixersByTrackId = {}
|
||||||
|
@allMixers = {}
|
||||||
|
@currentMixerRangeMin = null
|
||||||
|
@currentMixerRangeMax = null
|
||||||
|
@mediaTrackGroups = [ChannelGroupIds.MediaTrackGroup, ChannelGroupIds.JamTrackGroup,
|
||||||
|
ChannelGroupIds.MetronomeGroup]
|
||||||
|
@muteBothMasterAndPersonalGroups = [ChannelGroupIds.AudioInputMusicGroup, ChannelGroupIds.MediaTrackGroup,
|
||||||
|
ChannelGroupIds.JamTrackGroup, ChannelGroupIds.MetronomeGroup]
|
||||||
|
|
||||||
|
|
||||||
|
@organize()
|
||||||
|
|
||||||
|
updateMixers: (type, text, @masterMixers, @personalMixers) ->
|
||||||
|
|
||||||
|
@organize()
|
||||||
|
|
||||||
|
if @session.inSession() && text == 'RebuildAudioIoControl'
|
||||||
|
SessionActions.myTracksChanged.trigger()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
organize: () ->
|
||||||
|
for masterMixer, i in @masterMixers
|
||||||
|
@allMixers['M' + masterMixer.id] = masterMixer; # populate allMixers by mixer.id
|
||||||
|
|
||||||
|
# populate mixer pair
|
||||||
|
mixerPair = {}
|
||||||
|
@mixersByResourceId[masterMixer.rid] = mixerPair
|
||||||
|
@mixersByTrackId[masterMixer.id] = mixerPair
|
||||||
|
mixerPair.master = masterMixer;
|
||||||
|
|
||||||
|
for personalMixer, i in @personalMixers
|
||||||
|
|
||||||
|
@allMixers['P' + personalMixer.id] = personalMixer
|
||||||
|
|
||||||
|
# populate other side of mixer pair
|
||||||
|
|
||||||
|
mixerPair = @mixersByResourceId[personalMixer.rid]
|
||||||
|
unless mixerPair
|
||||||
|
if personalMixer.group_id != ChannelGroupIds.MonitorGroup
|
||||||
|
logger.warn("there is no master version of ", personalMixer)
|
||||||
|
|
||||||
|
mixerPair = {}
|
||||||
|
@mixersByResourceId[personalMixer.rid] = mixerPair
|
||||||
|
|
||||||
|
@mixersByTrackId[personalMixer.id] = mixerPair;
|
||||||
|
mixerPair.personal = personalMixer;
|
||||||
|
|
||||||
|
@groupTypes()
|
||||||
|
|
||||||
|
groupTypes: () ->
|
||||||
|
localMediaMixers = @mixersForGroupIds(@mediaTrackGroups, MIX_MODES.MASTER)
|
||||||
|
peerLocalMediaMixers = @mixersForGroupId(ChannelGroupIds.PeerMediaTrackGroup, MIX_MODES.MASTER)
|
||||||
|
|
||||||
|
logger.debug("localMediaMixers", localMediaMixers)
|
||||||
|
#logger.debug("peerLocalMediaMixers", peerLocalMediaMixers)
|
||||||
|
|
||||||
|
# get the server data regarding various media tracks
|
||||||
|
recordedBackingTracks = @session.recordedBackingTracks()
|
||||||
|
backingTracks = @session.backingTracks()
|
||||||
|
recordedJamTracks = @session.recordedJamTracks()
|
||||||
|
jamTracks = @session.jamTracks()
|
||||||
|
|
||||||
|
###
|
||||||
|
with mixer info, we use these to decide what kind of tracks are open in the backend
|
||||||
|
|
||||||
|
each mixer has a media_type field, which describes the type of media track it is.
|
||||||
|
* JamTrack
|
||||||
|
* BackingTrack
|
||||||
|
* RecordingTrack
|
||||||
|
* MetronomeTrack
|
||||||
|
* "" - adhoc track (not supported visually)
|
||||||
|
|
||||||
|
it is supposed to be the case that there are only one type of track open at a time, however, that's a business policy/logic
|
||||||
|
constraint; and may be buggy. **So, we should render whatever we have, so that it's obvious what's really going on.**
|
||||||
|
|
||||||
|
so, let's group up all mixers by type, and then ask them to be rendered
|
||||||
|
###
|
||||||
|
|
||||||
|
recordingTrackMixers = []
|
||||||
|
backingTrackMixers = []
|
||||||
|
jamTrackMixers = []
|
||||||
|
metronomeTrackMixers = []
|
||||||
|
adhocTrackMixers = []
|
||||||
|
|
||||||
|
groupByType = (mixers, isLocalMixer) ->
|
||||||
|
for mixer in mixers
|
||||||
|
mediaType = mixer.media_type
|
||||||
|
groupId = mixer.group_id
|
||||||
|
|
||||||
|
if mediaType == 'MetronomeTrack' || groupId == ChannelGroupIds.MetronomeGroup
|
||||||
|
# Metronomes come across with a blank media type, so check group_id:
|
||||||
|
metronomeTrackMixers.push(mixer)
|
||||||
|
else if mediaType == null || mediaType == "" || mediaType == 'RecordingTrack'
|
||||||
|
# additional check; if we can match an id in backing tracks or recorded backing track,
|
||||||
|
# we need to remove it from the recorded track set, but move it to the backing track set
|
||||||
|
|
||||||
|
isJamTrack = false;
|
||||||
|
|
||||||
|
if jamTracks
|
||||||
|
# check if the ID matches that of an open jam track
|
||||||
|
for jamTrack in jamTracks
|
||||||
|
if mixer.id == jamTrack.id
|
||||||
|
isJamTrack = true;
|
||||||
|
break
|
||||||
|
|
||||||
|
if !isJamTrack && recordedJamTracks
|
||||||
|
# then check if the ID matches that of a open, recorded jam track
|
||||||
|
for recordedJamTrack in recordedJamTracks
|
||||||
|
if mixer.id == recordedJamTrack.id
|
||||||
|
isJamTrack = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if isJamTrack
|
||||||
|
jamTrackMixers.push(mixer)
|
||||||
|
else
|
||||||
|
isBackingTrack = false
|
||||||
|
if recordedBackingTracks
|
||||||
|
for recordedBackingTrack in recordedBackingTracks
|
||||||
|
if mixer.id == 'L' + recordedBackingTrack.client_track_id
|
||||||
|
isBackingTrack = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if backingTracks
|
||||||
|
for backingTrack in backingTracks
|
||||||
|
if mixer.id == 'L' + backingTrack.client_track_id
|
||||||
|
isBackingTrack = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if isBackingTrack
|
||||||
|
backingTrackMixers.push(mixer)
|
||||||
|
else
|
||||||
|
# couldn't resolve this as a JamTrack or Backing track, must be a normal recorded file
|
||||||
|
recordingTrackMixers.push(mixer)
|
||||||
|
|
||||||
|
else if mediaType == 'PeerMediaTrack' || mediaType == 'BackingTrack'
|
||||||
|
backingTrackMixers.push(mixer)
|
||||||
|
else if mediaType == 'JamTrack'
|
||||||
|
jamTrackMixers.push(mixer);
|
||||||
|
else if mediaType == null || mediaType == "" || mediaType == 'RecordingTrack'
|
||||||
|
# mediaType == null is for backwards compat with older clients. Can be removed soon
|
||||||
|
recordingTrackMixers.push(mixer)
|
||||||
|
else
|
||||||
|
logger.warn("Unknown track type: " + mediaType)
|
||||||
|
adhocTrackMixers.push(mixer)
|
||||||
|
|
||||||
|
groupByType(localMediaMixers, true);
|
||||||
|
groupByType(peerLocalMediaMixers, false);
|
||||||
|
|
||||||
|
###
|
||||||
|
if recordingTrackMixers.length > 0
|
||||||
|
renderRecordingTracks(recordingTrackMixers)
|
||||||
|
|
||||||
|
if backingTrackMixers.length > 0
|
||||||
|
renderBackingTracks(backingTrackMixers)
|
||||||
|
|
||||||
|
if jamTrackMixers.length > 0
|
||||||
|
renderJamTracks(jamTrackMixers);
|
||||||
|
|
||||||
|
if metronomeTrackMixers.length > 0 && @session.jamTracks() == null && @session.recordedJamTracks() == null
|
||||||
|
renderMetronomeTracks(metronomeTrackMixers);
|
||||||
|
|
||||||
|
checkMetronomeTransition();
|
||||||
|
###
|
||||||
|
|
||||||
|
if adhocTrackMixers.length > 0
|
||||||
|
logger.warn("some tracks are open that we don't know how to show")
|
||||||
|
|
||||||
|
mixersForGroupIds: (groupIds, mixMode) ->
|
||||||
|
foundMixers = []
|
||||||
|
mixers = if mixMode == MIX_MODES.MASTER then @masterMixers else @personalMixers;
|
||||||
|
|
||||||
|
for mixer in mixers
|
||||||
|
groupIdLen = groupIds.length
|
||||||
|
for i in groupIdLen
|
||||||
|
if mixer.group_id == groupIds[i]
|
||||||
|
foundMixers.push(mixer)
|
||||||
|
|
||||||
|
foundMixers
|
||||||
|
|
||||||
|
mixersForGroupId: (groupId, mixMode) ->
|
||||||
|
foundMixers = [];
|
||||||
|
mixers = if mixMode == MIX_MODES.MASTER then @masterMixers else @personalMixers;
|
||||||
|
for mixer in mixers
|
||||||
|
if mixer.group_id == groupId
|
||||||
|
foundMixers.push(mixer)
|
||||||
|
|
||||||
|
foundMixers
|
||||||
|
|
||||||
|
getMixer: (mixerId, mode) ->
|
||||||
|
mode = @mixMode unless mode?
|
||||||
|
|
||||||
|
@allMixers[(if mode then 'M' else 'P') + mixerId]
|
||||||
|
|
||||||
|
getMixerByTrackId: (trackId, mode) ->
|
||||||
|
mixerPair = @mixersByTrackId[trackId]
|
||||||
|
|
||||||
|
return null unless mixerPair
|
||||||
|
|
||||||
|
if mode == undefined
|
||||||
|
return mixerPair
|
||||||
|
|
||||||
|
else
|
||||||
|
if mode == MIX_MODES.MASTER
|
||||||
|
return mixerPair.master
|
||||||
|
else
|
||||||
|
return mixerPair.personal
|
||||||
|
|
||||||
|
|
||||||
|
groupedMixersForClientId: (clientId, groupIds, usedMixers, mixMode) ->
|
||||||
|
foundMixers = {};
|
||||||
|
mixers = if mixMode == MIX_MODES.MASTER then @masterMixers else @personalMixers;
|
||||||
|
|
||||||
|
for mixer in mixers
|
||||||
|
if mixer.client_id == clientId
|
||||||
|
for groupId in groupIds
|
||||||
|
if mixer.group_id == groupId
|
||||||
|
if (mixer.groupId != ChannelGroupIds.UserMusicInputGroup) && !(mixer.id in usedMixers)
|
||||||
|
mixers = foundMixers[mixer.group_id]
|
||||||
|
if !mixers
|
||||||
|
mixers = []
|
||||||
|
foundMixers[mixer.group_id] = mixers
|
||||||
|
mixers.push(mixer)
|
||||||
|
|
||||||
|
foundMixers
|
||||||
|
|
||||||
|
findMixerForTrack: (client_id, track, myTrack) ->
|
||||||
|
mixer = null # what is the best mixer for this track/client ID?
|
||||||
|
oppositeMixer = null # what is the corresponding mixer in the opposite mode?
|
||||||
|
vuMixer = null
|
||||||
|
muteMixer = null
|
||||||
|
|
||||||
|
if myTrack
|
||||||
|
# when it's your track, look it up by the backend resource ID
|
||||||
|
mixer = @getMixerByTrackId(track.client_track_id, @mixMode)
|
||||||
|
vuMixer = mixer
|
||||||
|
muteMixer = mixer
|
||||||
|
|
||||||
|
# sanity checks
|
||||||
|
if mixer && mixer.group_id != ChannelGroupIds.AudioInputMusicGroup
|
||||||
|
logger.error("found local mixer that was not of groupID: AudioInputMusicGroup", mixer)
|
||||||
|
|
||||||
|
if mixer
|
||||||
|
# find the matching AudioInputMusicGroup for the opposite mode
|
||||||
|
oppositeMixer = @getMixerByTrackId(track.client_track_id, !@mixMode)
|
||||||
|
|
||||||
|
if @mixMode == MIX_MODES.PERSONAL
|
||||||
|
muteMixer = oppositeMixer; # make the master mixer the mute mixer
|
||||||
|
|
||||||
|
# sanity checks
|
||||||
|
if !oppositeMixer
|
||||||
|
logger.error("unable to find opposite mixer for local mixer", mixer)
|
||||||
|
else if oppositeMixer.group_id != ChannelGroupIds.AudioInputMusicGroup
|
||||||
|
logger.error("found local mixer in opposite mode that was not of groupID: AudioInputMusicGroup", mixer, oppositeMixer)
|
||||||
|
else
|
||||||
|
logger.debug("local track is not present: ", track, mixer)
|
||||||
|
else
|
||||||
|
switch @mixMode
|
||||||
|
when MIX_MODES.MASTER
|
||||||
|
|
||||||
|
# when it's a remote track and in master mode, we should find the PeerAudioInputMusicGroup
|
||||||
|
mixer = @getMixerByTrackId(track.client_track_id, MIX_MODES.MASTER)
|
||||||
|
|
||||||
|
# sanity check
|
||||||
|
if mixer && mixer.group_id != ChannelGroupIds.PeerAudioInputMusicGroup
|
||||||
|
logger.error("found remote mixer that was not of groupID: PeerAudioInputMusicGroup", mixer)
|
||||||
|
|
||||||
|
vuMixer = mixer
|
||||||
|
muteMixer = mixer
|
||||||
|
|
||||||
|
if mixer
|
||||||
|
# we should be able to find a UserMusicInputGroup for this clientId in personal mode
|
||||||
|
oppositeMixers = @groupedMixersForClientId(client_id, [ ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL)
|
||||||
|
if oppositeMixers[ChannelGroupIds.UserMusicInputGroup]
|
||||||
|
oppositeMixer = oppositeMixers[ChannelGroupIds.UserMusicInputGroup][0]
|
||||||
|
|
||||||
|
if !oppositeMixer
|
||||||
|
logger.error("unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer", mixer )
|
||||||
|
|
||||||
|
when MIX_MODES.PERSONAL
|
||||||
|
mixers = @groupedMixersForClientId(client_id, [ ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL)
|
||||||
|
if mixers[ChannelGroupIds.UserMusicInputGroup]
|
||||||
|
mixer = mixers[ChannelGroupIds.UserMusicInputGroup][0]
|
||||||
|
|
||||||
|
vuMixer = mixer
|
||||||
|
muteMixer = mixer
|
||||||
|
|
||||||
|
if mixer
|
||||||
|
# now grab the PeerAudioInputMusicGroup in master mode to satisfy the 'opposite' mixer
|
||||||
|
oppositeMixer = @getMixerByTrackId(track.client_track_id, MIX_MODES.MASTER)
|
||||||
|
if !oppositeMixer
|
||||||
|
logger.debug("unable to find a PeerAudioInputMusicGroup master mixer matching a UserMusicInput", track.client_track_id, @mixersByTrackId)
|
||||||
|
else if oppositeMixer.group_id != ChannelGroupIds.PeerAudioInputMusicGroup
|
||||||
|
logger.error("found remote mixer that was not of groupID: PeerAudioInputMusicGroup", mixer)
|
||||||
|
|
||||||
|
vuMixer = oppositeMixer; # for personal mode, use the PeerAudioInputMusicGroup's VUs
|
||||||
|
|
||||||
|
{
|
||||||
|
mixer: mixer,
|
||||||
|
oppositeMixer: oppositeMixer,
|
||||||
|
vuMixer: vuMixer,
|
||||||
|
muteMixer: muteMixer
|
||||||
|
}
|
||||||
|
|
||||||
|
mute: (mixerId, mode, muting) ->
|
||||||
|
|
||||||
|
mode = @mixMode unless mode?
|
||||||
|
|
||||||
|
@fillTrackVolumeObject(mixerId, mode)
|
||||||
|
|
||||||
|
context.trackVolumeObject.mute = muting
|
||||||
|
|
||||||
|
context.jamClient.SessionSetControlState(mixerId, mode)
|
||||||
|
|
||||||
|
# keep state of mixer in sync with backend
|
||||||
|
mixer = @getMixer(mixerId, mode)
|
||||||
|
mixer.mute = muting
|
||||||
|
|
||||||
|
faderChanged: (data, mixerIds, groupId) ->
|
||||||
|
# media tracks are the only controls that sometimes set two mixers right now
|
||||||
|
hasMasterAndPersonalControls = mixerIds.length == 2
|
||||||
|
|
||||||
|
for mixerId, i in mixerIds
|
||||||
|
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
|
||||||
|
mode = undefined
|
||||||
|
if hasMasterAndPersonalControls
|
||||||
|
mode = if i == 0 then MIX_MODES.MASTER else MIX_MODES.PERSONAL
|
||||||
|
|
||||||
|
mixer = @fillTrackVolumeObject(mixerId, mode, broadcast)
|
||||||
|
|
||||||
|
@setMixerVolume(mixer, data.percentage)
|
||||||
|
|
||||||
|
# keep state of mixer in sync with backend
|
||||||
|
mixer = @getMixer(mixer.id, mixer.mode)
|
||||||
|
mixer.volume_left = context.trackVolumeObject.volL
|
||||||
|
|
||||||
|
if groupId == ChannelGroupIds.UserMusicInputGroup
|
||||||
|
# there may be other mixers with this same ID in the case of a Peer Music Stream, so update them as well
|
||||||
|
context.JK.FaderHelpers.setFaderValue(mixerId, data.percentage)
|
||||||
|
|
||||||
|
initGain: (mixer) ->
|
||||||
|
gainPercent = context.JK.FaderHelpers.convertAudioTaperToPercent(mixer.volume_left)
|
||||||
|
context.JK.FaderHelpers.setFaderValue(mixer.id, gainPercent)
|
||||||
|
context.JK.FaderHelpers.showFader(mixer.id)
|
||||||
|
|
||||||
|
panChanged: (data, mixerIds, groupId) ->
|
||||||
|
# media tracks are the only controls that sometimes set two mixers right now
|
||||||
|
for mixerId, i in mixerIds
|
||||||
|
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
|
||||||
|
mode = undefined
|
||||||
|
mixer = @fillTrackVolumeObject(mixerId, mode, broadcast)
|
||||||
|
|
||||||
|
@setMixerPan(mixer, data.percentage)
|
||||||
|
|
||||||
|
# keep state of mixer in sync with backend
|
||||||
|
mixer = @getMixer(mixer.id, mixer.mode)
|
||||||
|
mixer.pan = context.trackVolumeObject.pan
|
||||||
|
|
||||||
|
initPan: (mixer) ->
|
||||||
|
panPercent= context.JK.PanHelpers.convertPanToPercent(mixer.pan)
|
||||||
|
context.JK.FaderHelpers.setFaderValue(mixer.id, panPercent, mixer.pan)
|
||||||
|
context.JK.FaderHelpers.showFader(mixer.id)
|
||||||
|
|
||||||
|
setMixerPan: (mixer, panPercent) ->
|
||||||
|
|
||||||
|
context.trackVolumeObject.pan = context.JK.PanHelpers.convertPercentToPan(panPercent);
|
||||||
|
context.jamClient.SessionSetControlState(mixer.id, mixer.mode);
|
||||||
|
|
||||||
|
setMixerVolume: (mixer, volumePercent) ->
|
||||||
|
###
|
||||||
|
// The context.trackVolumeObject has been filled with the mixer values
|
||||||
|
// that go with mixerId, and the range of that mixer
|
||||||
|
// has been set in currentMixerRangeMin-Max.
|
||||||
|
// All that needs doing is to translate the incoming percent
|
||||||
|
// into the real value ont the sliders range. Set Left/Right
|
||||||
|
// volumes on trackVolumeObject, and call SetControlState to stick.
|
||||||
|
###
|
||||||
|
|
||||||
|
context.trackVolumeObject.volL = context.JK.FaderHelpers.convertPercentToAudioTaper(volumePercent);
|
||||||
|
context.trackVolumeObject.volR = context.JK.FaderHelpers.convertPercentToAudioTaper(volumePercent);
|
||||||
|
|
||||||
|
context.jamClient.SessionSetControlState(mixer.id, mixer.mode);
|
||||||
|
|
||||||
|
percentFromMixerValue: (min, max, value) ->
|
||||||
|
try
|
||||||
|
range = Math.abs(max - min)
|
||||||
|
magnitude = value - min
|
||||||
|
percent = Math.round(100*(magnitude/range))
|
||||||
|
percent
|
||||||
|
catch err
|
||||||
|
0
|
||||||
|
|
||||||
|
|
||||||
|
percentToMixerValue:(min, max, percent) ->
|
||||||
|
range = Math.abs(max - min);
|
||||||
|
multiplier = percent/100; # Change 85 into 0.85
|
||||||
|
value = min + (multiplier * range);
|
||||||
|
|
||||||
|
# Protect against percents < 0 and > 100
|
||||||
|
if value < min
|
||||||
|
value = min;
|
||||||
|
|
||||||
|
if value > max
|
||||||
|
value = max;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
|
||||||
|
fillTrackVolumeObject: (mixerId, mode, broadcast) ->
|
||||||
|
_broadcast = true
|
||||||
|
if broadcast?
|
||||||
|
_broadcast = broadcast
|
||||||
|
|
||||||
|
mixer = @getMixer(mixerId, mode)
|
||||||
|
context.trackVolumeObject.clientID = mixer.client_id
|
||||||
|
context.trackVolumeObject.broadcast = _broadcast
|
||||||
|
context.trackVolumeObject.master = mixer.master
|
||||||
|
context.trackVolumeObject.monitor = mixer.monitor
|
||||||
|
context.trackVolumeObject.mute = mixer.mute
|
||||||
|
context.trackVolumeObject.name = mixer.name
|
||||||
|
context.trackVolumeObject.record = mixer.record
|
||||||
|
context.trackVolumeObject.volL = mixer.volume_left
|
||||||
|
context.trackVolumeObject.pan = mixer.pan
|
||||||
|
|
||||||
|
# today we treat all tracks as mono, but this is required to make a stereo track happy
|
||||||
|
# context.trackVolumeObject.volR = mixer.volume_right;
|
||||||
|
context.trackVolumeObject.volR = mixer.volume_left;
|
||||||
|
|
||||||
|
context.trackVolumeObject.loop = mixer.loop;
|
||||||
|
# trackVolumeObject doesn't have a place for range min/max
|
||||||
|
@currentMixerRangeMin = mixer.range_low;
|
||||||
|
@currentMixerRangeMax = mixer.range_high;
|
||||||
|
mixer
|
||||||
|
|
||||||
|
updateVU: (mixerId, value, isClipping) ->
|
||||||
|
selector = null
|
||||||
|
pureMixerId = mixerId.replace("_vul", "")
|
||||||
|
pureMixerId = pureMixerId.replace("_vur", "")
|
||||||
|
mixer = @getMixer(pureMixerId, @mixMode)
|
||||||
|
unless mixer
|
||||||
|
# try again, in the opposite mode (awful that this is necessary)
|
||||||
|
mixer = @getMixer(pureMixerId, !@mixMode)
|
||||||
|
|
||||||
|
if mixer
|
||||||
|
if mixer.stereo # // stereo track
|
||||||
|
context.JK.VuHelpers.updateVU2('vul', mixer, value)
|
||||||
|
else
|
||||||
|
if mixerId.substr(-4) == "_vul"
|
||||||
|
# Do the left
|
||||||
|
context.JK.VuHelpers.updateVU2('vul', mixer, value)
|
||||||
|
# Do the right
|
||||||
|
context.JK.VuHelpers.updateVU2('vur', mixer, value)
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
@SessionHelper = class SessionHelper
|
||||||
|
|
||||||
|
constructor: (app, session) ->
|
||||||
|
@app = app
|
||||||
|
@session = session
|
||||||
|
|
||||||
|
inSession: () ->
|
||||||
|
@session?
|
||||||
|
|
||||||
|
participants: () ->
|
||||||
|
if @session
|
||||||
|
return @session.participants
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
|
# if any participant has the metronome open, then we say this session has the metronome open
|
||||||
|
isMetronomeOpen: () ->
|
||||||
|
metronomeOpen = false;
|
||||||
|
for participant in @participants()
|
||||||
|
if participant.metronome_open
|
||||||
|
metronomeOpen = true
|
||||||
|
break
|
||||||
|
|
||||||
|
metronomeOpen
|
||||||
|
|
||||||
|
isPlayingRecording: () ->
|
||||||
|
# this is the server's state; there is no guarantee that the local tracks
|
||||||
|
# requested from the backend will have corresponding track information
|
||||||
|
return !!(@session && @session.claimed_recording);
|
||||||
|
|
||||||
|
recordedTracks: () ->
|
||||||
|
if @session && @session.claimed_recording
|
||||||
|
@session.claimed_recording.recording.recorded_tracks
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
|
recordedBackingTracks: () ->
|
||||||
|
if @session && @session.claimed_recording
|
||||||
|
@session.claimed_recording.recording.recorded_backing_tracks
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
|
backingTracks: () ->
|
||||||
|
backingTracks = []
|
||||||
|
# this may be wrong if we loosen the idea that only one person can have a backing track open.
|
||||||
|
# but for now, the 1st person we find with a backing track open is all there is to find...
|
||||||
|
for participant in @participants()
|
||||||
|
if participant.backing_tracks.length > 0
|
||||||
|
backingTracks = participant.backing_tracks
|
||||||
|
break
|
||||||
|
|
||||||
|
backingTracks
|
||||||
|
|
||||||
|
jamTracks: () ->
|
||||||
|
if @session && @session.jam_track
|
||||||
|
@session.jam_track.tracks.filter((track)->
|
||||||
|
track.track_type == 'Track'
|
||||||
|
)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
|
recordedJamTracks:() ->
|
||||||
|
if @session && @session.claimed_recording
|
||||||
|
@session.claimed_recording.recording.recorded_jam_track_tracks
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
|
|
||||||
|
getParticipant: (clientId) ->
|
||||||
|
found = null
|
||||||
|
for participant in @participants()
|
||||||
|
if participant.client_id == clientId
|
||||||
|
found = participant
|
||||||
|
break
|
||||||
|
|
||||||
|
logger.warn('unable to find participant with clientId: ' + clientId) unless found
|
||||||
|
found
|
||||||
|
|
||||||
|
|
@ -10,6 +10,3 @@ logger = context.JK.logger
|
||||||
@trigger(app)
|
@trigger(app)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
context.JK.Stores.App = @AppStore
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
context = window
|
||||||
|
logger = context.JK.logger
|
||||||
|
MIX_MODES = context.JK.MIX_MODES;
|
||||||
|
|
||||||
|
@MixerStore = Reflux.createStore(
|
||||||
|
{
|
||||||
|
init: ->
|
||||||
|
# Register with the app store to get @app
|
||||||
|
this.listenTo(context.AppStore, this.onAppInit);
|
||||||
|
this.listenTo(context.SessionStore, this.onSessionChange)
|
||||||
|
this.listenTo(context.MixerActions.mute, this.onMute)
|
||||||
|
this.listenTo(context.MixerActions.faderChanged, this.onFaderChanged)
|
||||||
|
this.listenTo(context.MixerActions.initGain, this.onInitGain)
|
||||||
|
this.listenTo(context.MixerActions.initPan, this.onInitPan)
|
||||||
|
this.listenTo(context.MixerActions.panChanged, this.onPanChanged)
|
||||||
|
this.listenTo(context.MixerActions.mixersChanged, this.onMixersChanged)
|
||||||
|
|
||||||
|
context.JK.HandleVolumeChangeCallback2 = @handleVolumeChangeCallback
|
||||||
|
context.JK.HandleMetronomeCallback2 = @handleMetronomeCallback
|
||||||
|
context.JK.HandleBridgeCallback2 = @handleBridgeCallback
|
||||||
|
context.JK.HandleBackingTrackSelectedCallback2 = @handleBackingTrackSelectedCallback
|
||||||
|
|
||||||
|
handleVolumeChangeCallback: () ->
|
||||||
|
logger.debug("volume change")
|
||||||
|
|
||||||
|
handleMetronomeCallback: () ->
|
||||||
|
logger.debug("metronome callback")
|
||||||
|
|
||||||
|
handleBridgeCallback: (vuData) ->
|
||||||
|
|
||||||
|
eventName = null
|
||||||
|
mixerId = null
|
||||||
|
value = null
|
||||||
|
vuInfo = null
|
||||||
|
|
||||||
|
for vuInfo in vuData
|
||||||
|
eventName = vuInfo[0];
|
||||||
|
vuVal = 0.0;
|
||||||
|
if eventName == "vu"
|
||||||
|
mixerId = vuInfo[1];
|
||||||
|
leftValue = vuInfo[2];
|
||||||
|
leftClipping = vuInfo[3];
|
||||||
|
rightValue = vuInfo[4];
|
||||||
|
rightClipping = vuInfo[5];
|
||||||
|
# TODO - no guarantee range will be -80 to 20. Get from the
|
||||||
|
# GetControlState for this mixer which returns min/max
|
||||||
|
# value is a DB value from -80 to 20. Convert to float from 0.0-1.0
|
||||||
|
|
||||||
|
@mixers.updateVU(mixerId + "_vul", (leftValue + 80) / 80, leftClipping)
|
||||||
|
@mixers.updateVU(mixerId + "_vur", (rightValue + 80) / 80, rightClipping)
|
||||||
|
|
||||||
|
|
||||||
|
handleBackingTrackSelectedCallback: () ->
|
||||||
|
logger.debug("backing track selected")
|
||||||
|
|
||||||
|
onAppInit: (@app) ->
|
||||||
|
@gearUtils = context.JK.GearUtilsInstance
|
||||||
|
@sessionUtils = context.JK.SessionUtils
|
||||||
|
|
||||||
|
onSessionChange: (session) ->
|
||||||
|
|
||||||
|
@session = session
|
||||||
|
|
||||||
|
masterMixers = context.jamClient.SessionGetAllControlState(true);
|
||||||
|
personalMixers = context.jamClient.SessionGetAllControlState(false);
|
||||||
|
|
||||||
|
# TODO: grab correct mix mode , line 870 sessionModel.js
|
||||||
|
@mixers = new context.MixerHelper(session, masterMixers, personalMixers, MIX_MODES.PERSONAL)
|
||||||
|
|
||||||
|
this.trigger({session: @session, mixers: @mixers})
|
||||||
|
|
||||||
|
|
||||||
|
onMute: (mixers, muting) ->
|
||||||
|
|
||||||
|
for mixer in mixers
|
||||||
|
@mixers.mute(mixer.id, mixer.mode, muting);
|
||||||
|
|
||||||
|
# simulate a state change to cause a UI redraw
|
||||||
|
this.trigger({session: @session, mixers: @mixers})
|
||||||
|
|
||||||
|
onFaderChanged: (data, mixerIds, groupId) ->
|
||||||
|
|
||||||
|
@mixers.faderChanged(data, mixerIds, groupId)
|
||||||
|
|
||||||
|
this.trigger({session: @session, mixers: @mixers})
|
||||||
|
|
||||||
|
onPanChanged: (data, mixerIds, groupId) ->
|
||||||
|
@mixers.panChanged(data, mixerIds, groupId)
|
||||||
|
|
||||||
|
this.trigger({session: @session, mixers: @mixers})
|
||||||
|
|
||||||
|
onInitGain: (mixer) ->
|
||||||
|
@mixers.initGain(mixer)
|
||||||
|
|
||||||
|
onInitPan: (mixer) ->
|
||||||
|
@mixers.initPan(mixer)
|
||||||
|
|
||||||
|
onMixersChanged: (type, text) ->
|
||||||
|
|
||||||
|
if @mixers
|
||||||
|
masterMixers = context.jamClient.SessionGetAllControlState(true);
|
||||||
|
personalMixers = context.jamClient.SessionGetAllControlState(false);
|
||||||
|
|
||||||
|
# TODO: grab correct mix mode , line 870 sessionModel.js
|
||||||
|
@mixers.updateMixers(type, text, masterMixers, personalMixers)
|
||||||
|
|
||||||
|
this.trigger({session: @session, mixers: @mixers})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
$ = jQuery
|
||||||
|
context = window
|
||||||
|
logger = context.JK.logger
|
||||||
|
|
||||||
|
@SessionMyTracksStore = Reflux.createStore(
|
||||||
|
{
|
||||||
|
|
||||||
|
init: ->
|
||||||
|
# Register with the app store to get @app
|
||||||
|
this.listenTo(context.AppStore, this.onAppInit);
|
||||||
|
this.listenTo(context.MixerStore, this.onSessionMixerChange)
|
||||||
|
|
||||||
|
onAppInit: (@app) ->
|
||||||
|
@gearUtils = context.JK.GearUtilsInstance
|
||||||
|
@sessionUtils = context.JK.SessionUtils
|
||||||
|
|
||||||
|
onSessionMixerChange: (sessionMixers) ->
|
||||||
|
|
||||||
|
this.trigger(sessionMixers)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,423 @@
|
||||||
|
$ = jQuery
|
||||||
|
context = window
|
||||||
|
logger = context.JK.logger
|
||||||
|
rest = context.JK.Rest()
|
||||||
|
EVENTS = context.JK.EVENTS;
|
||||||
|
|
||||||
|
SessionActions = @SessionActions
|
||||||
|
|
||||||
|
@SessionStore = Reflux.createStore(
|
||||||
|
{
|
||||||
|
listenables: SessionActions
|
||||||
|
|
||||||
|
userTracks: null # comes from the backend
|
||||||
|
currentSessionId: null
|
||||||
|
currentSession: null
|
||||||
|
currentOrLastSession: null
|
||||||
|
startTime: null
|
||||||
|
currentParticipants: {}
|
||||||
|
participantsEverSeen: {}
|
||||||
|
users: {} # // User info for session participants
|
||||||
|
requestingSessionRefresh: false
|
||||||
|
pendingSessionRefresh: false
|
||||||
|
sessionPageEnterTimeout: null
|
||||||
|
sessionPageEnterDeferred: null
|
||||||
|
gearUtils: null
|
||||||
|
sessionUtils: null
|
||||||
|
|
||||||
|
|
||||||
|
init: ->
|
||||||
|
# Register with the app store to get @app
|
||||||
|
this.listenTo(context.AppStore, this.onAppInit);
|
||||||
|
|
||||||
|
onAppInit: (@app) ->
|
||||||
|
@gearUtils = context.JK.GearUtilsInstance
|
||||||
|
@sessionUtils = context.JK.SessionUtils
|
||||||
|
|
||||||
|
|
||||||
|
onJoinSession: (sessionId) ->
|
||||||
|
|
||||||
|
# initialize webcamViewer
|
||||||
|
if gon.global.video_available && gon.global.video_available != "none"
|
||||||
|
webcamViewer.beforeShow()
|
||||||
|
|
||||||
|
# double-check that we are connected to the server via websocket
|
||||||
|
|
||||||
|
return unless @ensureConnected()
|
||||||
|
|
||||||
|
# update the session data to be empty
|
||||||
|
@updateCurrentSession(null)
|
||||||
|
|
||||||
|
# start setting data for this new session
|
||||||
|
@currentSessionId = sessionId
|
||||||
|
@startTime = new Date().getTime()
|
||||||
|
|
||||||
|
# let's find out the public/private nature of this session,
|
||||||
|
# so that we can decide whether we need to validate the audio profile more aggressively
|
||||||
|
rest.getSessionHistory(@currentSessionId)
|
||||||
|
.done((musicSession)=>
|
||||||
|
musicianAccessOnJoin = musicSession.musician_access
|
||||||
|
|
||||||
|
shouldVerifyNetwork = musicSession.musician_access;
|
||||||
|
|
||||||
|
@gearUtils.guardAgainstInvalidConfiguration(@app, shouldVerifyNetwork).fail(() =>
|
||||||
|
SessionActions.leaveSession.trigger({location: '/client#/home'})
|
||||||
|
).done(() =>
|
||||||
|
result = @sessionUtils.SessionPageEnter();
|
||||||
|
|
||||||
|
@gearUtils.guardAgainstActiveProfileMissing(@app, result)
|
||||||
|
.fail((data) =>
|
||||||
|
leaveBehavior = {}
|
||||||
|
|
||||||
|
if data && data.reason == 'handled'
|
||||||
|
if data.nav == 'BACK'
|
||||||
|
leaveBehavior.location = -1
|
||||||
|
else
|
||||||
|
leaveBehavior.location = data.nav
|
||||||
|
else
|
||||||
|
leaveBehavior.location = '/client#/home';
|
||||||
|
|
||||||
|
SessionActions.leaveSession.trigger(leaveBehavior)
|
||||||
|
).done(() =>
|
||||||
|
@waitForSessionPageEnterDone()
|
||||||
|
.done((userTracks) =>
|
||||||
|
@userTracks = userTracks
|
||||||
|
|
||||||
|
@ensureAppropriateProfile(musicianAccessOnJoin)
|
||||||
|
.done(() =>
|
||||||
|
logger.debug("user has passed all session guards")
|
||||||
|
@joinSession()
|
||||||
|
)
|
||||||
|
.fail((result) =>
|
||||||
|
unless result.controlled_location
|
||||||
|
SessionActions.leaveSession.trigger({location: "/client#/home"})
|
||||||
|
)
|
||||||
|
).fail((data) =>
|
||||||
|
if data == "timeout"
|
||||||
|
context.JK.alertSupportedNeeded('The audio system has not reported your configured tracks in a timely fashion.')
|
||||||
|
else if data == 'session_over'
|
||||||
|
# do nothing; session ended before we got the user track info. just bail
|
||||||
|
logger.debug("session is over; bailing")
|
||||||
|
else
|
||||||
|
context.JK.alertSupportedNeeded('Unable to determine configured tracks due to reason: ' + data)
|
||||||
|
|
||||||
|
SessionActions.leaveSession.trigger({location: '/client#/home'})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.fail(() =>
|
||||||
|
logger.error("unable to fetch session history")
|
||||||
|
)
|
||||||
|
|
||||||
|
waitForSessionPageEnterDone: () ->
|
||||||
|
@sessionPageEnterDeferred = $.Deferred()
|
||||||
|
|
||||||
|
# see if we already have tracks; if so, we need to run with these
|
||||||
|
inputTracks = context.JK.TrackHelpers.getUserTracks(context.jamClient)
|
||||||
|
|
||||||
|
logger.debug("isNoInputProfile", @gearUtils.isNoInputProfile())
|
||||||
|
if inputTracks.length > 0 || @gearUtils.isNoInputProfile()
|
||||||
|
logger.debug("on page enter, tracks are already available")
|
||||||
|
@sessionPageEnterDeferred.resolve(inputTracks)
|
||||||
|
deferred = @sessionPageEnterDeferred
|
||||||
|
@sessionPageEnterDeferred = null
|
||||||
|
return deferred
|
||||||
|
|
||||||
|
@sessionPageEnterTimeout = setTimeout(()=>
|
||||||
|
if @sessionPageEnterTimeout
|
||||||
|
if @sessionPageEnterDeferred
|
||||||
|
@sessionPageEnterDeferred.reject('timeout')
|
||||||
|
@sessionPageEnterDeferred = null
|
||||||
|
@sessionPageEnterTimeout = null
|
||||||
|
, 5000)
|
||||||
|
|
||||||
|
@sessionPageEnterDeferred
|
||||||
|
|
||||||
|
ensureAppropriateProfile: (musicianAccess) ->
|
||||||
|
deferred = new $.Deferred();
|
||||||
|
if musicianAccess
|
||||||
|
deferred = context.JK.guardAgainstSinglePlayerProfile(@app)
|
||||||
|
else
|
||||||
|
deferred.resolve();
|
||||||
|
deferred
|
||||||
|
|
||||||
|
joinSession: () ->
|
||||||
|
context.jamClient.SessionRegisterCallback("JK.HandleBridgeCallback2");
|
||||||
|
#context.jamClient.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted");
|
||||||
|
context.jamClient.SessionSetConnectionStatusRefreshRate(1000);
|
||||||
|
#context.JK.HelpBubbleHelper.jamtrackGuideSession($screen.find('li.open-a-jamtrack'), $screen)
|
||||||
|
|
||||||
|
# subscribe to events from the recording model
|
||||||
|
@recordingRegistration()
|
||||||
|
|
||||||
|
# tell the server we want to join
|
||||||
|
|
||||||
|
rest.joinSession({
|
||||||
|
client_id: @app.clientId,
|
||||||
|
ip_address: context.JK.JamServer.publicIP,
|
||||||
|
as_musician: true,
|
||||||
|
tracks: @userTracks,
|
||||||
|
session_id: @currentSessionId,
|
||||||
|
audio_latency: context.jamClient.FTUEGetExpectedLatency().latency
|
||||||
|
})
|
||||||
|
.done((response) =>
|
||||||
|
unless @inSession()
|
||||||
|
# the user has left the session before they got joined. We need to issue a leave again to the server to make sure they are out
|
||||||
|
logger.debug("user left before fully joined to session. telling server again that they have left")
|
||||||
|
@leaveSessionRest(response.id)
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug("calling jamClient.JoinSession");
|
||||||
|
# on temporary disconnect scenarios, a user may already be in a session when they enter this path
|
||||||
|
# so we avoid double counting
|
||||||
|
unless @alreadyInSession()
|
||||||
|
if response.music_session.participant_count == 1
|
||||||
|
context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.create);
|
||||||
|
else
|
||||||
|
context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.join);
|
||||||
|
|
||||||
|
#recordingModel.reset();
|
||||||
|
|
||||||
|
context.jamClient.JoinSession({sessionID: response.id});
|
||||||
|
|
||||||
|
@refreshCurrentSession(true);
|
||||||
|
|
||||||
|
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SESSION_JOIN, @trackChanges);
|
||||||
|
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SESSION_DEPART, @trackChanges);
|
||||||
|
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.TRACKS_CHANGED, @trackChanges);
|
||||||
|
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.HEARTBEAT_ACK, @trackChanges);
|
||||||
|
|
||||||
|
$(document).trigger(EVENTS.SESSION_STARTED, {session: {id: @currentSessionId}}) if document
|
||||||
|
|
||||||
|
@handleAutoOpenJamTrack()
|
||||||
|
)
|
||||||
|
.fail((xhr) =>
|
||||||
|
@updateCurrentSession(null)
|
||||||
|
|
||||||
|
if xhr.status == 404
|
||||||
|
# we tried to join the session, but it's already gone. kick user back to join session screen
|
||||||
|
leaveBehavior =
|
||||||
|
location: "/client#/findSession"
|
||||||
|
notify:
|
||||||
|
title: "Unable to Join Session",
|
||||||
|
text: " The session you attempted to join is over."
|
||||||
|
SessionActions.leaveSession.trigger(leaveBehavior)
|
||||||
|
else if xhr.status == 422
|
||||||
|
response = JSON.parse(xhr.responseText);
|
||||||
|
if response["errors"] && response["errors"]["tracks"] && response["errors"]["tracks"][0] == "Please select at least one track"
|
||||||
|
@app.notifyAlert("No Inputs Configured", $('<span>You will need to reconfigure your audio device.</span>'))
|
||||||
|
|
||||||
|
else if response["errors"] && response["errors"]["music_session"] && response["errors"]["music_session"][0] == ["is currently recording"]
|
||||||
|
|
||||||
|
leaveBehavior =
|
||||||
|
location: "/client#/findSession"
|
||||||
|
notify:
|
||||||
|
title: "Unable to Join Session"
|
||||||
|
text: "The session is currently recording."
|
||||||
|
SessionActions.leaveSession.trigger(leaveBehavior)
|
||||||
|
else
|
||||||
|
@app.notifyServerError(xhr, 'Unable to Join Session');
|
||||||
|
else
|
||||||
|
@app.notifyServerError(xhr, 'Unable to Join Session');
|
||||||
|
)
|
||||||
|
|
||||||
|
trackChanges: (header, payload) ->
|
||||||
|
if @currentTrackChanges < payload.track_changes_counter
|
||||||
|
# we don't have the latest info. try and go get it
|
||||||
|
logger.debug("track_changes_counter = stale. refreshing...")
|
||||||
|
@refreshCurrentSession();
|
||||||
|
|
||||||
|
else
|
||||||
|
if header.type != 'HEARTBEAT_ACK'
|
||||||
|
# don't log if HEARTBEAT_ACK, or you will see this log all the time
|
||||||
|
logger.info("track_changes_counter = fresh. skipping refresh...", header, payload)
|
||||||
|
|
||||||
|
handleAutoOpenJamTrack: () ->
|
||||||
|
jamTrack = @sessionUtils.grabAutoOpenJamTrack();
|
||||||
|
if jamTrack
|
||||||
|
# give the session to settle just a little (call a timeout of 1 second)
|
||||||
|
setTimeout(()=>
|
||||||
|
# tell the server we are about to open a jamtrack
|
||||||
|
rest.openJamTrack({id: context.JK.CurrentSessionModel.id(), jam_track_id: jamTrack.id})
|
||||||
|
.done((response) =>
|
||||||
|
logger.debug("jamtrack opened")
|
||||||
|
# now actually load the jamtrack
|
||||||
|
# TODO
|
||||||
|
# context.JK.CurrentSessionModel.updateSession(response);
|
||||||
|
# loadJamTrack(jamTrack);
|
||||||
|
)
|
||||||
|
.fail((jqXHR) =>
|
||||||
|
@app.notifyServerError(jqXHR, "Unable to Open JamTrack For Playback")
|
||||||
|
)
|
||||||
|
, 1000)
|
||||||
|
|
||||||
|
inSession: () ->
|
||||||
|
!!@currentSessionId
|
||||||
|
|
||||||
|
alreadyInSession: () ->
|
||||||
|
inSession = false
|
||||||
|
for participant in @participants()
|
||||||
|
if participant.user.id == context.JK.currentUserId
|
||||||
|
inSession = true
|
||||||
|
break
|
||||||
|
|
||||||
|
participants: () ->
|
||||||
|
if @currentSession
|
||||||
|
@currentSession.participants;
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
|
refreshCurrentSession: (force) ->
|
||||||
|
logger.debug("refreshCurrentSession(force=true)") if force
|
||||||
|
|
||||||
|
@refreshCurrentSessionRest(force)
|
||||||
|
|
||||||
|
refreshCurrentSessionRest: (force) ->
|
||||||
|
unless @inSession()
|
||||||
|
logger.debug("refreshCurrentSession skipped: ")
|
||||||
|
return
|
||||||
|
|
||||||
|
if @requestingSessionRefresh
|
||||||
|
# if someone asks for a refresh while one is going on, we ask for another to queue up
|
||||||
|
logger.debug("queueing refresh")
|
||||||
|
@pendingSessionRefresh = true;
|
||||||
|
else
|
||||||
|
@requestingSessionRefresh = true
|
||||||
|
rest.getSession(@currentSessionId)
|
||||||
|
.done((response) =>
|
||||||
|
@updateSessionInfo(response, force)
|
||||||
|
)
|
||||||
|
.fail((jqXHR) =>
|
||||||
|
if jqXHR.status != 404
|
||||||
|
@app.notifyServerError(jqXHR, "Unable to refresh session data")
|
||||||
|
else
|
||||||
|
logger.debug("refreshCurrentSessionRest: could not refresh data for session because it's gone")
|
||||||
|
)
|
||||||
|
.always(() =>
|
||||||
|
@requestingSessionRefresh = false
|
||||||
|
if @pendingSessionRefresh
|
||||||
|
# and when the request is done, if we have a pending, fire it off again
|
||||||
|
pendingSessionRefresh = false
|
||||||
|
@refreshCurrentSessionRest(force)
|
||||||
|
)
|
||||||
|
|
||||||
|
updateSessionInfo: (session, force) ->
|
||||||
|
if force == true || @currentTrackChanges < session.track_changes_counter
|
||||||
|
logger.debug("updating current track changes from %o to %o", @currentTrackChanges, session.track_changes_counter)
|
||||||
|
@currentTrackChanges = session.track_changes_counter;
|
||||||
|
@sendClientParticipantChanges(@currentSession, session);
|
||||||
|
@updateCurrentSession(session);
|
||||||
|
#if(callback != null) {
|
||||||
|
# callback();
|
||||||
|
#}
|
||||||
|
else
|
||||||
|
logger.info("ignoring refresh because we already have current: " + @currentTrackChanges + ", seen: " + session.track_changes_counter);
|
||||||
|
|
||||||
|
|
||||||
|
leaveSessionRest: () ->
|
||||||
|
rest.deleteParticipant(@app.clientId);
|
||||||
|
|
||||||
|
sendClientParticipantChanges: (oldSession, newSession) ->
|
||||||
|
joins = []
|
||||||
|
leaves = []
|
||||||
|
leaveJoins = []; # Will hold JamClientParticipants
|
||||||
|
|
||||||
|
oldParticipants = []; # will be set to session.participants if session
|
||||||
|
oldParticipantIds = {};
|
||||||
|
newParticipants = [];
|
||||||
|
newParticipantIds = {};
|
||||||
|
|
||||||
|
if oldSession && oldSession.participants
|
||||||
|
for oldParticipant in oldSession.participants
|
||||||
|
oldParticipantIds[oldParticipant.client_id] = oldParticipant
|
||||||
|
|
||||||
|
if newSession && newSession.participants
|
||||||
|
for newParticipant in newSession.participants
|
||||||
|
newParticipantIds[newParticipant.client_id] = newParticipant
|
||||||
|
|
||||||
|
for client_id, participant of newParticipantIds
|
||||||
|
# grow the 'all participants seen' list
|
||||||
|
unless (client_id in @participantsEverSeen)
|
||||||
|
@participantsEverSeen[client_id] = participant;
|
||||||
|
|
||||||
|
|
||||||
|
if client_id in oldParticipantIds
|
||||||
|
# if the participant is here now, and here before, there is still a chance we missed a
|
||||||
|
# very fast leave/join. So check if joined_session_at is different
|
||||||
|
if oldParticipantIds[client_id].joined_session_at != participant.joined_session_at
|
||||||
|
leaveJoins.push(participant)
|
||||||
|
else
|
||||||
|
# new participant id that's not in old participant ids: Join
|
||||||
|
joins.push(participant);
|
||||||
|
|
||||||
|
for client_id, participant of oldParticipantIds
|
||||||
|
unless (client_id in newParticipantIds)
|
||||||
|
# old participant id that's not in new participant ids: Leave
|
||||||
|
leaves.push(participant);
|
||||||
|
|
||||||
|
for i, v of joins
|
||||||
|
if v.client_id != @app.clientId
|
||||||
|
@participantJoined(newSession, v)
|
||||||
|
|
||||||
|
for i,v of leaves
|
||||||
|
if v.client_id != @app.clientId
|
||||||
|
@participantLeft(newSession, v)
|
||||||
|
|
||||||
|
for i,v of leaveJoins
|
||||||
|
if v.client_id != @app.clientId
|
||||||
|
logger.debug("participant had a rapid leave/join")
|
||||||
|
@participantLeft(newSession, v)
|
||||||
|
@participantJoined(newSession, v)
|
||||||
|
|
||||||
|
participantJoined: (newSession, participant) ->
|
||||||
|
logger.debug("jamClient.ParticipantJoined", participant.client_id)
|
||||||
|
context.jamClient.ParticipantJoined(newSession, @toJamClientParticipant(participant));
|
||||||
|
@currentParticipants[participant.client_id] = {server: participant, client: {audio_established: null}}
|
||||||
|
|
||||||
|
participantLeft: (newSession, participant) ->
|
||||||
|
logger.debug("jamClient.ParticipantLeft", participant.client_id)
|
||||||
|
context.jamClient.ParticipantLeft(newSession, @toJamClientParticipant(participant));
|
||||||
|
delete @currentParticipants[participant.client_id]
|
||||||
|
|
||||||
|
toJamClientParticipant: (participant) ->
|
||||||
|
{
|
||||||
|
userID: "",
|
||||||
|
clientID: participant.client_id,
|
||||||
|
tcpPort: 0,
|
||||||
|
udpPort: 0,
|
||||||
|
localIPAddress: participant.ip_address, # ?
|
||||||
|
globalIPAddress: participant.ip_address, # ?
|
||||||
|
latency: 0,
|
||||||
|
natType: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
recordingRegistration: () ->
|
||||||
|
logger.debug("recording registration not hooked up yet")
|
||||||
|
|
||||||
|
updateCurrentSession: (sessionData) ->
|
||||||
|
if sessionData != null
|
||||||
|
@currentOrLastSession = sessionData
|
||||||
|
|
||||||
|
@currentSession = sessionData
|
||||||
|
|
||||||
|
console.log("SESSION CHANGED", sessionData)
|
||||||
|
|
||||||
|
this.trigger(new context.SessionHelper(@app, @currentSession))
|
||||||
|
|
||||||
|
ensureConnected: () ->
|
||||||
|
unless context.JK.JamServer.connected
|
||||||
|
leaveBehavior =
|
||||||
|
location: '/client#/home'
|
||||||
|
notify:
|
||||||
|
title: "Not Connected"
|
||||||
|
text: 'To create or join a session, you must be connected to the server.'
|
||||||
|
|
||||||
|
SessionActions.leaveSession.trigger(leaveBehavior)
|
||||||
|
|
||||||
|
context.JK.JamServer.connected
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
@ -1957,7 +1957,7 @@
|
||||||
|
|
||||||
// Given a mixerID and a value between 0.0-1.0,
|
// Given a mixerID and a value between 0.0-1.0,
|
||||||
// light up the proper VU lights.
|
// light up the proper VU lights.
|
||||||
function _updateVU(mixerId, value, isClipping) {
|
function _updateVU(mixerId, value, isClipping) {
|
||||||
|
|
||||||
// Special-case for mono tracks. If mono, and it's a _vul id,
|
// Special-case for mono tracks. If mono, and it's a _vul id,
|
||||||
// update both sides, otherwise do nothing.
|
// update both sides, otherwise do nothing.
|
||||||
|
|
@ -2193,8 +2193,8 @@
|
||||||
// TODO - no guarantee range will be -80 to 20. Get from the
|
// TODO - no guarantee range will be -80 to 20. Get from the
|
||||||
// GetControlState for this mixer which returns min/max
|
// GetControlState for this mixer which returns min/max
|
||||||
// value is a DB value from -80 to 20. Convert to float from 0.0-1.0
|
// value is a DB value from -80 to 20. Convert to float from 0.0-1.0
|
||||||
_updateVU(mixerId + "_vul", (leftValue + 80) / 100, leftClipping);
|
_updateVU(mixerId + "_vul", (leftValue + 80) / 80, leftClipping);
|
||||||
_updateVU(mixerId + "_vur", (rightValue + 80) / 100, rightClipping);
|
_updateVU(mixerId + "_vur", (rightValue + 80) / 80, rightClipping);
|
||||||
}
|
}
|
||||||
else if(eventName === 'connection_status') {
|
else if(eventName === 'connection_status') {
|
||||||
var mixerId = vuInfo[1];
|
var mixerId = vuInfo[1];
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var ChannelGroupIds = context.JK.ChannelGroupIds
|
||||||
|
|
||||||
context.JK = context.JK || {};
|
context.JK = context.JK || {};
|
||||||
|
|
||||||
// As these are helper functions, just have a single
|
// As these are helper functions, just have a single
|
||||||
|
|
@ -20,7 +22,7 @@
|
||||||
|
|
||||||
var userTracks = context.JK.TrackHelpers.getUserTracks(jamClient, allTracks);
|
var userTracks = context.JK.TrackHelpers.getUserTracks(jamClient, allTracks);
|
||||||
var backingTracks = context.JK.TrackHelpers.getBackingTracks(jamClient, allTracks);
|
var backingTracks = context.JK.TrackHelpers.getBackingTracks(jamClient, allTracks);
|
||||||
var metronomeTracks = context.JK.TrackHelpers.getTracks(jamClient, 12);
|
var metronomeTracks = context.JK.TrackHelpers.getTracks(jamClient, ChannelGroupIds.MetronomeGroup);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userTracks: userTracks,
|
userTracks: userTracks,
|
||||||
|
|
@ -51,7 +53,7 @@
|
||||||
|
|
||||||
// allTracks is the result of SessionGetAllControlState; as an optimization
|
// allTracks is the result of SessionGetAllControlState; as an optimization
|
||||||
getBackingTracks: function(jamClient, allTracks) {
|
getBackingTracks: function(jamClient, allTracks) {
|
||||||
var mediaTracks = context.JK.TrackHelpers.getTracks(jamClient, 4, allTracks);
|
var mediaTracks = context.JK.TrackHelpers.getTracks(jamClient, ChannelGroupIds.MediaTrackGroup, allTracks);
|
||||||
|
|
||||||
var backingTracks = []
|
var backingTracks = []
|
||||||
context._.each(mediaTracks, function(mediaTrack) {
|
context._.each(mediaTracks, function(mediaTrack) {
|
||||||
|
|
@ -80,7 +82,7 @@
|
||||||
var localMusicTracks = [];
|
var localMusicTracks = [];
|
||||||
var i;
|
var i;
|
||||||
|
|
||||||
localMusicTracks = context.JK.TrackHelpers.getTracks(jamClient, 2, allTracks);
|
localMusicTracks = context.JK.TrackHelpers.getTracks(jamClient, ChannelGroupIds.AudioInputMusicGroup, allTracks);
|
||||||
|
|
||||||
var trackObjects = [];
|
var trackObjects = [];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,70 @@
|
||||||
})
|
})
|
||||||
return $element;
|
return $element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates a hover element that does not dissappear when the user mouses over the hover.
|
||||||
|
*
|
||||||
|
* @param $element
|
||||||
|
* @param text
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
context.JK.interactReactBubble = function($element, reactElementName, reactProps, options) {
|
||||||
|
|
||||||
|
if(!options) options = {};
|
||||||
|
|
||||||
|
function waitForBubbleHover($bubble) {
|
||||||
|
$bubble.hoverIntent({
|
||||||
|
over: function() {
|
||||||
|
if(timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
out: function() {
|
||||||
|
//$element.btOff();
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeout = null;
|
||||||
|
|
||||||
|
|
||||||
|
options.trigger = 'none'
|
||||||
|
options.clickAnywhereToClose = true
|
||||||
|
options.preShow = function(container) {
|
||||||
|
var reactElement = context[reactElementName]
|
||||||
|
if(!reactElementName) {
|
||||||
|
throw "unknown react element" + reactElementName
|
||||||
|
}
|
||||||
|
var element = React.createElement(reactElement, reactProps);
|
||||||
|
|
||||||
|
var $container = $(container)
|
||||||
|
|
||||||
|
React.render(element, $container.find('.react-holder').get(0))
|
||||||
|
}
|
||||||
|
options.postShow = function(container) {
|
||||||
|
|
||||||
|
if(timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
}
|
||||||
|
waitForBubbleHover($(container))
|
||||||
|
timeout = setTimeout(function() {/**$element.btOff()*/}, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
$element.hoverIntent({
|
||||||
|
over: function() {
|
||||||
|
$element.btOn();
|
||||||
|
},
|
||||||
|
out: function() {
|
||||||
|
|
||||||
|
}});
|
||||||
|
|
||||||
|
options.cssStyles = {}
|
||||||
|
options.padding = 0;
|
||||||
|
context.JK.hoverBubble($element, '<div class="react-holder ' + reactElementName + '"></div>', options)
|
||||||
|
return $element;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associates a bubble on hover (by default) with the specified $element, using jquery.bt.js (BeautyTips)
|
* Associates a bubble on hover (by default) with the specified $element, using jquery.bt.js (BeautyTips)
|
||||||
* @param $element The element that should show the bubble when hovered
|
* @param $element The element that should show the bubble when hovered
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,53 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a selector representing a container for a VU meter and
|
||||||
|
* a value between 0.0 and 1.0, light the appropriate lights.
|
||||||
|
*/
|
||||||
|
updateVU2: function (side, mixer, value) {
|
||||||
|
// There are 13 VU lights. Figure out how many to
|
||||||
|
// light based on the incoming value.
|
||||||
|
|
||||||
|
var $selector = $('.' + side + '-' + mixer.id)
|
||||||
|
|
||||||
|
$selector.each(function() {
|
||||||
|
var $table = $(this)
|
||||||
|
var horizontal = $table.is('.horizontal')
|
||||||
|
|
||||||
|
var lightCount = Number($table.attr('data-light-count'))
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
var state = 'on';
|
||||||
|
var lights = Math.round(value * lightCount);
|
||||||
|
var redSwitch = Math.round(lightCount * 0.6666667);
|
||||||
|
|
||||||
|
var $light = null;
|
||||||
|
var colorClass = 'vu-green-';
|
||||||
|
var thisLightSelector = null;
|
||||||
|
|
||||||
|
// Remove all light classes from all lights
|
||||||
|
var allLightsSelector = $table.find('td');
|
||||||
|
$(allLightsSelector).removeClass('vu-green-off vu-green-on vu-red-off vu-red-on');
|
||||||
|
|
||||||
|
// Set the lights
|
||||||
|
for (i = 0; i < lightCount; i++) {
|
||||||
|
colorClass = 'vu-green-';
|
||||||
|
state = 'on';
|
||||||
|
if (i >= redSwitch) {
|
||||||
|
colorClass = 'vu-red-';
|
||||||
|
}
|
||||||
|
if (i >= lights) {
|
||||||
|
state = 'off';
|
||||||
|
}
|
||||||
|
|
||||||
|
var lightIndex = horizontal ? i : lightCount - i - 1;
|
||||||
|
allLightsSelector.eq(lightIndex).addClass(colorClass + state);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,14 @@ $poor: #980006;
|
||||||
$error: #980006;
|
$error: #980006;
|
||||||
$fair: #cc9900;
|
$fair: #cc9900;
|
||||||
|
|
||||||
|
$labelFontFamily: Arial, Helvetica, sans-serif;
|
||||||
|
$labelFontSize: 12px;
|
||||||
|
|
||||||
|
@mixin labelFont {
|
||||||
|
font-family: $labelFontFamily;
|
||||||
|
font-size: $labelFontSize;
|
||||||
|
}
|
||||||
|
|
||||||
@mixin border_box_sizing {
|
@mixin border_box_sizing {
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tracks {
|
.tracks {
|
||||||
position:absolute;
|
position: absolute;
|
||||||
@include border_box_sizing;
|
@include border_box_sizing;
|
||||||
top: 71px;
|
top: 71px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
@ -19,58 +20,326 @@
|
||||||
|
|
||||||
.session-my-tracks, .session-other-tracks, .session-media-tracks, .session-notifications {
|
.session-my-tracks, .session-other-tracks, .session-media-tracks, .session-notifications {
|
||||||
@include border_box_sizing;
|
@include border_box_sizing;
|
||||||
float:left;
|
float: left;
|
||||||
width:25%;
|
width: 25%;
|
||||||
border-right: 1px solid #4c4c4c;
|
border-right: 1px solid #4c4c4c;
|
||||||
padding:15px;
|
padding: 15px;
|
||||||
height:100%;
|
height: 100%;
|
||||||
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-notifications {
|
.session-notifications {
|
||||||
border-right-width:0;
|
border-right-width: 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.in-session-controls {
|
.in-session-controls {
|
||||||
|
|
||||||
width:100%;
|
width: 100%;
|
||||||
padding:11px 0px 11px 0px;
|
padding: 11px 0px 11px 0px;
|
||||||
background-color:#4c4c4c;
|
background-color: #4c4c4c;
|
||||||
min-height:20px;
|
min-height: 20px;
|
||||||
position:relative;
|
position: relative;
|
||||||
min-width:690px;
|
min-width: 690px;
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
float:left;
|
float: left;
|
||||||
font-size:12px;
|
font-size: 12px;
|
||||||
color:#ccc;
|
color: #ccc;
|
||||||
margin: 0px 0px 0px 4px;
|
margin: 0px 0px 0px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.block {
|
.block {
|
||||||
float:left;
|
float: left;
|
||||||
margin: 6px 8px 0px 8px;
|
margin: 6px 8px 0px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
img {
|
img {
|
||||||
margin-right:3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.session-tracks-scroller {
|
.session-tracks-scroller {
|
||||||
position:relative;
|
position: relative;
|
||||||
overflow-x:hidden;
|
overflow-x: hidden;
|
||||||
overflow-y:auto;
|
overflow-y: auto;
|
||||||
width:100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
line-height:125%;
|
line-height: 125%;
|
||||||
margin:0;
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-my-tracks {
|
||||||
|
.session-track {
|
||||||
|
float:left;
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 6px 6px 6px 10px;
|
||||||
|
color: $ColorTextTypical;
|
||||||
|
background-color: #242323;
|
||||||
|
border-radius: 6px;
|
||||||
|
min-height: 76px;
|
||||||
|
max-width: 210px;
|
||||||
|
@include border_box_sizing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.react-holder {
|
||||||
|
&.SessionTrackVolumeHover {
|
||||||
|
height:331px;
|
||||||
|
width:235px;
|
||||||
|
|
||||||
|
.session-track {
|
||||||
|
float:left;
|
||||||
|
background-color: #242323;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
height: 300px;
|
||||||
|
margin-right: 14px;
|
||||||
|
position: relative;
|
||||||
|
width: 70px;
|
||||||
|
margin-top:19px;
|
||||||
|
margin-left:24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-icon-mute {
|
||||||
|
float:none;
|
||||||
|
position: absolute;
|
||||||
|
top: 246px;
|
||||||
|
left: 29px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-gain {
|
||||||
|
position:absolute;
|
||||||
|
width:28px;
|
||||||
|
height:209px;
|
||||||
|
top:32px;
|
||||||
|
left:23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fader {
|
||||||
|
height:209px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.handle {
|
||||||
|
bottom:0%;
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textual-help {
|
||||||
|
float:left;
|
||||||
|
width:100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size:12px;
|
||||||
|
padding:0;
|
||||||
|
margin:16px 0 0;
|
||||||
|
line-height:125%;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
margin-top:19px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icheckbox_minimal {
|
||||||
|
position:absolute;
|
||||||
|
top: 271px;
|
||||||
|
left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
position:absolute;
|
||||||
|
top: 271px;
|
||||||
|
left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
@include labelFont;
|
||||||
|
position:absolute;
|
||||||
|
top:273px;
|
||||||
|
left:34px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.SessionTrackPanHover {
|
||||||
|
width:331px;
|
||||||
|
height:197px;
|
||||||
|
padding:15px;
|
||||||
|
@include border_box_sizing;
|
||||||
|
|
||||||
|
.session-pan {
|
||||||
|
.textual-help {
|
||||||
|
float:left;
|
||||||
|
width:100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size:12px;
|
||||||
|
padding:0;
|
||||||
|
line-height:125%;
|
||||||
|
}
|
||||||
|
.track-pan {
|
||||||
|
background-color: #242323;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
height: 70px;
|
||||||
|
position: relative;
|
||||||
|
width: 300px;
|
||||||
|
margin-top:15px;
|
||||||
|
}
|
||||||
|
.fader {
|
||||||
|
position:absolute;
|
||||||
|
width:205px;
|
||||||
|
height:24px;
|
||||||
|
top:34px;
|
||||||
|
left:44px;
|
||||||
|
background-image: url('/assets/content/bkg_slider_gain_horiz_24.png');
|
||||||
|
}
|
||||||
|
.handle {
|
||||||
|
display:none;
|
||||||
|
|
||||||
|
img {
|
||||||
|
position:absolute;
|
||||||
|
left:-5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.left-label {
|
||||||
|
@include labelFont;
|
||||||
|
position:absolute;
|
||||||
|
left:13px;
|
||||||
|
top:40px;
|
||||||
|
}
|
||||||
|
.right-label {
|
||||||
|
@include labelFont;
|
||||||
|
position:absolute;
|
||||||
|
right:12px;
|
||||||
|
top:40px;
|
||||||
|
}
|
||||||
|
.floater {
|
||||||
|
width:20px;
|
||||||
|
text-align:center;
|
||||||
|
top:-22px;
|
||||||
|
left:-8px;
|
||||||
|
@include labelFont;
|
||||||
|
position:absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.session-track {
|
||||||
|
|
||||||
|
.name {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
@include labelFont;
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-avatar {
|
||||||
|
float: left;
|
||||||
|
padding: 1px;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
background-color: #ed3618;
|
||||||
|
-webkit-border-radius: 22px;
|
||||||
|
-moz-border-radius: 22px;
|
||||||
|
border-radius: 22px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
-webkit-border-radius: 22px;
|
||||||
|
-moz-border-radius: 22px;
|
||||||
|
border-radius: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-instrument {
|
||||||
|
float: left;
|
||||||
|
padding: 1px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.vu {
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
td {
|
||||||
|
border: 3px solid #242323;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-controls {
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-left: 10px;
|
||||||
|
float:left
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-buttons {
|
||||||
|
margin-top:22px;
|
||||||
|
padding:0 0 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-icon-mute {
|
||||||
|
float:left;
|
||||||
|
position:relative;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-icon-pan {
|
||||||
|
float:left;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-image:url('/assets/content/icon_pan.png');
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
text-align: center;
|
||||||
|
margin-left:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-icon-equalizer {
|
||||||
|
float:left;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-image:url('/assets/content/icon_equalizer.png');
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
text-align: center;
|
||||||
|
margin-left:7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-track-list-enter {
|
||||||
|
opacity: 0.01;
|
||||||
|
transition: opacity .5s ease-in;
|
||||||
|
|
||||||
|
&.session-track-list-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-track-list-leave {
|
||||||
|
opacity:1;
|
||||||
|
transition: opacity .5s ease-in;
|
||||||
|
|
||||||
|
&.session-track-list-leave-active {
|
||||||
|
opacity: 0.01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-track-settings {
|
||||||
|
height:18px;
|
||||||
|
cursor:pointer;
|
||||||
|
|
||||||
|
span {
|
||||||
|
top: -4px;
|
||||||
|
position: relative;
|
||||||
|
left:3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
<!-- Vertical Fader -->
|
<!-- Vertical Fader -->
|
||||||
<script type="text/template" id="template-fader-v">
|
<script type="text/template" id="template-fader-v">
|
||||||
<div class="fader vertical" control="fader" fader-id="{{ faderId }}" orientation="vertical" style="height:{{height}}px;">
|
<div class="fader vertical" data-control="fader" data-fader-id="{{ faderId }}" data-orientation="vertical" style="height:{{height}}px;">
|
||||||
<div class="handle" style="bottom:0%;" control="fader-handle">
|
<div class="handle" style="bottom:0%;" data-control="fader-handle">
|
||||||
<%= image_tag "content/slider_gain_vertical.png", {:width => 28, :height => 11} %>
|
<%= image_tag "content/slider_gain_vertical.png", {:width => 28, :height => 11} %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
|
|
||||||
<!-- Horizontal Fader -->
|
<!-- Horizontal Fader -->
|
||||||
<script type="text/template" id="template-fader-h">
|
<script type="text/template" id="template-fader-h">
|
||||||
<div class="fader horizontal" control="fader" fader-id="{{ faderId }}" orientation="horizontal" style="width:{{width}}px;">
|
<div class="fader horizontal" data-control="fader" data-fader-id="{{ faderId }}" data-orientation="horizontal" style="width:{{width}}px;">
|
||||||
<div class="handle" style="left:0%;" control="fader-handle">
|
<div class="handle" style="left:0%;" data-control="fader-handle">
|
||||||
<%= image_tag "content/slider_volume.png", {:height => 17, :width => 8} %>
|
<%= image_tag "content/slider_volume.png", {:height => 17, :width => 8} %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,7 @@
|
||||||
var sessionScreen = new JK.SessionScreen(JK.app);
|
var sessionScreen = new JK.SessionScreen(JK.app);
|
||||||
sessionScreen.initialize(localRecordingsDialog, recordingFinishedDialog, JK.FriendSelectorDialogInstance);
|
sessionScreen.initialize(localRecordingsDialog, recordingFinishedDialog, JK.FriendSelectorDialogInstance);
|
||||||
|
|
||||||
AppActions.appInit(JK.app)
|
AppActions.appInit.trigger(JK.app)
|
||||||
|
|
||||||
var sessionSettingsDialog = new JK.SessionSettingsDialog(JK.app, sessionScreen);
|
var sessionSettingsDialog = new JK.SessionSettingsDialog(JK.app, sessionScreen);
|
||||||
sessionSettingsDialog.initialize();
|
sessionSettingsDialog.initialize();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue