* wip
This commit is contained in:
parent
60302fc1fe
commit
c28d8f3e7f
|
|
@ -161,7 +161,7 @@
|
|||
|
||||
/**
|
||||
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:
|
||||
// we need the id (guid) that the backend generated for the new track we just added
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@
|
|||
//= require react
|
||||
//= require react_ujs
|
||||
//= require react-init
|
||||
//= require react-components
|
||||
//= require web/signup_helper
|
||||
//= require web/signin_helper
|
||||
//= require web/signin
|
||||
//= require web/tracking
|
||||
//= require react-components
|
||||
//= require_directory .
|
||||
//= require_directory ./dialog
|
||||
//= require_directory ./wizard
|
||||
|
|
|
|||
|
|
@ -77,6 +77,9 @@
|
|||
}
|
||||
|
||||
if (type === 2) { // BACKEND_MIXER_CHANGE
|
||||
|
||||
context.MixerActions.mixersChanged(type, text)
|
||||
|
||||
if(context.JK.CurrentSessionModel)
|
||||
context.JK.CurrentSessionModel.onBackendMixerChanged(type, text)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
context.JK ||= {};
|
||||
broadcastActions = context.JK.Actions.Broadcast
|
||||
broadcastActions = @BroadcastActions
|
||||
|
||||
context.JK.ClientInit = class ClientInit
|
||||
constructor: () ->
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
var $draggingFaderHandle = null;
|
||||
var $draggingFader = null;
|
||||
var $floater = null;
|
||||
var draggingOrientation = null;
|
||||
|
||||
var logger = g.JK.logger;
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
e.stopPropagation();
|
||||
|
||||
var $fader = $(this);
|
||||
var floaterConvert = $fader.data('floaterConverter')
|
||||
var sessionModel = window.JK.CurrentSessionModel || null;
|
||||
|
||||
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 position = { top: e.pageY - offset.top, left: e.pageX - offset.left}
|
||||
|
||||
|
|
@ -53,6 +55,10 @@
|
|||
return false;
|
||||
}
|
||||
|
||||
if(floaterConvert) {
|
||||
window.JK.FaderHelpers.setFloaterValue($fader.find('.floater'), floaterConvert(faderPct))
|
||||
}
|
||||
|
||||
$fader.parent().triggerHandler('fader_change', {percentage: faderPct, dragging: false})
|
||||
|
||||
setHandlePosition($fader, faderPct);
|
||||
|
|
@ -61,9 +67,9 @@
|
|||
|
||||
function setHandlePosition($fader, value) {
|
||||
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);
|
||||
|
||||
// required because this method is entered directly when from a callback
|
||||
|
|
@ -81,7 +87,7 @@
|
|||
}
|
||||
|
||||
function faderValue($fader, e, offset) {
|
||||
var orientation = $fader.attr('orientation');
|
||||
var orientation = $fader.attr('data-orientation');
|
||||
var getPercentFunction = getVerticalFaderPercent;
|
||||
var relativePosition = offset.top;
|
||||
if (orientation && orientation == 'horizontal') {
|
||||
|
|
@ -92,7 +98,7 @@
|
|||
}
|
||||
|
||||
function getHandleCssAttribute($fader) {
|
||||
var orientation = $fader.attr('orientation');
|
||||
var orientation = $fader.attr('data-orientation');
|
||||
return (orientation === 'horizontal') ? 'left' : 'top';
|
||||
}
|
||||
|
||||
|
|
@ -134,12 +140,34 @@
|
|||
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})
|
||||
}
|
||||
|
||||
function onFaderDragStart(e, ui) {
|
||||
$draggingFaderHandle = $(this);
|
||||
$draggingFader = $draggingFaderHandle.closest('div[control="fader"]');
|
||||
$draggingFader = $draggingFaderHandle.closest('div[data-control="fader"]');
|
||||
$floater = $draggingFaderHandle.find('.floater')
|
||||
draggingOrientation = $draggingFader.attr('orientation');
|
||||
|
||||
var mediaControlsDisabled = $draggingFaderHandle.data('media-controls-disabled');
|
||||
|
|
@ -210,12 +238,12 @@
|
|||
|
||||
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-track-opener', selector.data('media-track-opener'))
|
||||
.data('showHelpAboutMediaMixers', selector.data('showHelpAboutMediaMixers'))
|
||||
|
||||
selector.find('div[control="fader-handle"]').draggable({
|
||||
selector.find('div[data-control="fader-handle"]').draggable({
|
||||
drag: onFaderDrag,
|
||||
start: onFaderDragStart,
|
||||
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) {
|
||||
|
||||
// deal with extremes better
|
||||
|
|
@ -263,27 +328,48 @@
|
|||
|
||||
// composite function resembling audio taper
|
||||
if (input <= 1) { return -80; }
|
||||
if (input <= 28) { return (2 * input - 80); }
|
||||
if (input <= 79) { return (0.5 * input - 38); }
|
||||
if (input < 99) { return (0.875 * input - 67.5); }
|
||||
if (input <= 28) { return Math.round((2 * input - 80)); } // -78 to -24 db
|
||||
if (input <= 79) { return Math.round((0.5 * input - 38)); } // -24 to 1.5 db
|
||||
if (input < 99) { return Math.round((0.875 * input - 67.5)); } // 1.625 - 19.125 db
|
||||
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);
|
||||
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) {
|
||||
draggingOrientation = $fader.attr('orientation');
|
||||
draggingOrientation = $fader.attr('data-orientation');
|
||||
setHandlePosition($fader, faderValue);
|
||||
draggingOrientation = null;
|
||||
},
|
||||
|
||||
setFloaterValue: function($floater, floaterValue) {
|
||||
$floater.text(floaterValue)
|
||||
},
|
||||
|
||||
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.FakeJamClient = function(app, p2pMessageFactory) {
|
||||
var ChannelGroupIds = context.JK.ChannelGroupIds
|
||||
var logger = context.JK.logger;
|
||||
logger.info("*** Fake JamClient instance initialized. ***");
|
||||
|
||||
|
|
@ -169,22 +170,22 @@
|
|||
function FTUEGetMusicInputs() {
|
||||
dbg('FTUEGetMusicInputs');
|
||||
return {
|
||||
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1":
|
||||
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
|
||||
"i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1":
|
||||
"Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
|
||||
};
|
||||
}
|
||||
function FTUEGetMusicOutputs() {
|
||||
dbg('FTUEGetMusicOutputs');
|
||||
return {
|
||||
"o~11~Multichannel (FW AP Multi)~0^o~11~Multichannel (FW AP Multi)~1":
|
||||
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
|
||||
"o~11~Multichannel (FWAPMulti)~0^o~11~Multichannel (FWAPMulti)~1":
|
||||
"Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
|
||||
};
|
||||
}
|
||||
function FTUEGetChatInputs() {
|
||||
dbg('FTUEGetChatInputs');
|
||||
return {
|
||||
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1":
|
||||
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
|
||||
"i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1":
|
||||
"Multichannel (FWAPMulti) - Channel 1/Multichannel (FWAPMulti) - Channel 2"
|
||||
};
|
||||
}
|
||||
function FTUEGetChannels() {
|
||||
|
|
@ -449,7 +450,7 @@
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -474,12 +475,22 @@
|
|||
}
|
||||
function SessionGetControlState(mixerIds, isMasterOrPersonal) {
|
||||
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 = [
|
||||
"FW AP Multi",
|
||||
"FW AP Multi",
|
||||
"FW AP Multi",
|
||||
"FW AP Multi",
|
||||
"FWAPMulti",
|
||||
"FWAPMulti",
|
||||
"FWAPMulti",
|
||||
"FWAPMulti",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
|
|
@ -533,6 +544,7 @@
|
|||
stereo: true,
|
||||
volume_left: -40,
|
||||
volume_right:-40,
|
||||
pan: 0,
|
||||
instrument_id:50, // see globals.js
|
||||
mode: isMasterOrPersonal,
|
||||
rid: mixerIds[i]
|
||||
|
|
@ -542,10 +554,10 @@
|
|||
}
|
||||
function SessionGetIDs() {
|
||||
return [
|
||||
"FW AP Multi_0_10000",
|
||||
"FW AP Multi_1_10100",
|
||||
"FW AP Multi_2_10200",
|
||||
"FW AP Multi_3_10500",
|
||||
"FWAPMulti_0_10000",
|
||||
"FWAPMulti_1_10100",
|
||||
"FWAPMulti_2_10200",
|
||||
"FWAPMulti_3_10500",
|
||||
"User@208.191.152.98#",
|
||||
"User@208.191.152.98_*"
|
||||
];
|
||||
|
|
@ -612,9 +624,9 @@
|
|||
|
||||
function doCallbacks() {
|
||||
var names = ["vu"];
|
||||
//var ids = ["FW AP Multi_2_10200", "FW AP Multi_0_10000"];
|
||||
var ids= ["i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~1",
|
||||
"i~11~MultiChannel (FW AP Multi)~0^i~11~Multichannel (FW AP Multi)~2"];
|
||||
//var ids = ["FWAPMulti_2_10200", "FWAPMulti_0_10000"];
|
||||
var ids= ["i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~1",
|
||||
"i~11~MultiChannel (FWAPMulti)~0^i~11~Multichannel (FWAPMulti)~2"];
|
||||
|
||||
var args = [];
|
||||
for (var i=0; i<ids.length; i++) {
|
||||
|
|
|
|||
|
|
@ -313,4 +313,25 @@
|
|||
context.JK.NAMED_MESSAGES = {
|
||||
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) {
|
||||
var url = '/api/auths/login';
|
||||
|
||||
|
|
@ -1854,6 +1862,7 @@
|
|||
this.addRecordingLike = addRecordingLike;
|
||||
this.addPlayablePlay = addPlayablePlay;
|
||||
this.getSession = getSession;
|
||||
this.deleteParticipant = deleteParticipant;
|
||||
this.getClientDownloads = getClientDownloads;
|
||||
this.createEmailInvitations = createEmailInvitations;
|
||||
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 ./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
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
||||
|
||||
|
||||
@SessionMyTracks = React.createClass({
|
||||
|
||||
render: () ->
|
||||
mixins: [Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
noTracksNotice = `<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>`
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
|
||||
`<div className="session-my-tracks">
|
||||
<h2>my live tracks</h2>
|
||||
<SessionTrackSettingsBtn />
|
||||
<div className="session-tracks-scroller">
|
||||
{noTracksNotice}
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
|
||||
tracks = []
|
||||
|
||||
if session.inSession()
|
||||
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
|
||||
|
||||
SessionActions = @SessionActions
|
||||
|
||||
@SessionScreen = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
|
@ -16,7 +18,7 @@ context = window
|
|||
<SessionResyncBtn />
|
||||
<SessionLeaveBtn />
|
||||
</div>
|
||||
<div className="tracks">
|
||||
<div className="tracks" id="new-tracks">
|
||||
<SessionMyTracks />
|
||||
<SessionOtherTracks />
|
||||
<SessionMediaTracks />
|
||||
|
|
@ -27,12 +29,14 @@ context = window
|
|||
componentDidMount: () ->
|
||||
@logger = context.JK.logger
|
||||
|
||||
beforeShow: () ->
|
||||
beforeShow: (data) =>
|
||||
@logger.debug("session beforeShow")
|
||||
|
||||
afterShow: () ->
|
||||
afterShow: (data) ->
|
||||
@logger.debug("session afterShow")
|
||||
|
||||
SessionActions.joinSession.trigger(data.id)
|
||||
|
||||
beforeHide: () ->
|
||||
@logger.debug("session beforeHide")
|
||||
|
||||
|
|
@ -44,7 +48,6 @@ context = window
|
|||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
@logger.debug("oh hai")
|
||||
screenBindings = {
|
||||
'beforeShow': @beforeShow,
|
||||
'afterShow': @afterShow,
|
||||
|
|
@ -54,4 +57,5 @@ context = window
|
|||
};
|
||||
|
||||
@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
|
||||
|
||||
logger = context.JK.logger
|
||||
|
||||
@SessionTrackSettingsBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
onConfigureSettings: (e) ->
|
||||
e.preventDefault();
|
||||
|
||||
@app.layout.showDialog('configure-tracks')
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
|
||||
render: () ->
|
||||
`<a className="session-track-settings">
|
||||
Settings
|
||||
</a>`
|
||||
`<div className="session-track-settings" onClick={this.onConfigureSettings}>
|
||||
<img src="/assets/content/icon_settings_lg.png" width="18" height="18" />
|
||||
<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({
|
||||
appInit: {}
|
||||
})
|
||||
|
||||
context.JK.Actions.App = @AppActions
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
context = window
|
||||
|
||||
BroadcastActions = Reflux.createActions({
|
||||
@BroadcastActions = Reflux.createActions({
|
||||
load: {asyncResult: true},
|
||||
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)
|
||||
}
|
||||
)
|
||||
|
||||
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,
|
||||
// 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,
|
||||
// update both sides, otherwise do nothing.
|
||||
|
|
@ -2193,8 +2193,8 @@
|
|||
// 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
|
||||
_updateVU(mixerId + "_vul", (leftValue + 80) / 100, leftClipping);
|
||||
_updateVU(mixerId + "_vur", (rightValue + 80) / 100, rightClipping);
|
||||
_updateVU(mixerId + "_vul", (leftValue + 80) / 80, leftClipping);
|
||||
_updateVU(mixerId + "_vur", (rightValue + 80) / 80, rightClipping);
|
||||
}
|
||||
else if(eventName === 'connection_status') {
|
||||
var mixerId = vuInfo[1];
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var ChannelGroupIds = context.JK.ChannelGroupIds
|
||||
|
||||
context.JK = context.JK || {};
|
||||
|
||||
// As these are helper functions, just have a single
|
||||
|
|
@ -20,7 +22,7 @@
|
|||
|
||||
var userTracks = context.JK.TrackHelpers.getUserTracks(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 {
|
||||
userTracks: userTracks,
|
||||
|
|
@ -51,7 +53,7 @@
|
|||
|
||||
// allTracks is the result of SessionGetAllControlState; as an optimization
|
||||
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 = []
|
||||
context._.each(mediaTracks, function(mediaTrack) {
|
||||
|
|
@ -80,7 +82,7 @@
|
|||
var localMusicTracks = [];
|
||||
var i;
|
||||
|
||||
localMusicTracks = context.JK.TrackHelpers.getTracks(jamClient, 2, allTracks);
|
||||
localMusicTracks = context.JK.TrackHelpers.getTracks(jamClient, ChannelGroupIds.AudioInputMusicGroup, allTracks);
|
||||
|
||||
var trackObjects = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -206,6 +206,70 @@
|
|||
})
|
||||
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)
|
||||
* @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;
|
||||
$fair: #cc9900;
|
||||
|
||||
$labelFontFamily: Arial, Helvetica, sans-serif;
|
||||
$labelFontSize: 12px;
|
||||
|
||||
@mixin labelFont {
|
||||
font-family: $labelFontFamily;
|
||||
font-size: $labelFontSize;
|
||||
}
|
||||
|
||||
@mixin border_box_sizing {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@
|
|||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.tracks {
|
||||
position:absolute;
|
||||
position: absolute;
|
||||
@include border_box_sizing;
|
||||
top: 71px;
|
||||
bottom: 0;
|
||||
|
|
@ -19,58 +20,326 @@
|
|||
|
||||
.session-my-tracks, .session-other-tracks, .session-media-tracks, .session-notifications {
|
||||
@include border_box_sizing;
|
||||
float:left;
|
||||
width:25%;
|
||||
float: left;
|
||||
width: 25%;
|
||||
border-right: 1px solid #4c4c4c;
|
||||
padding:15px;
|
||||
height:100%;
|
||||
padding: 15px;
|
||||
height: 100%;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.session-notifications {
|
||||
border-right-width:0;
|
||||
border-right-width: 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.in-session-controls {
|
||||
|
||||
width:100%;
|
||||
padding:11px 0px 11px 0px;
|
||||
background-color:#4c4c4c;
|
||||
min-height:20px;
|
||||
position:relative;
|
||||
min-width:690px;
|
||||
width: 100%;
|
||||
padding: 11px 0px 11px 0px;
|
||||
background-color: #4c4c4c;
|
||||
min-height: 20px;
|
||||
position: relative;
|
||||
min-width: 690px;
|
||||
|
||||
.label {
|
||||
float:left;
|
||||
font-size:12px;
|
||||
color:#ccc;
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
margin: 0px 0px 0px 4px;
|
||||
}
|
||||
|
||||
.block {
|
||||
float:left;
|
||||
float: left;
|
||||
margin: 6px 8px 0px 8px;
|
||||
}
|
||||
|
||||
a {
|
||||
img {
|
||||
margin-right:3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.session-tracks-scroller {
|
||||
position:relative;
|
||||
overflow-x:hidden;
|
||||
overflow-y:auto;
|
||||
width:100%;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height:125%;
|
||||
margin:0;
|
||||
line-height: 125%;
|
||||
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 -->
|
||||
<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="handle" style="bottom:0%;" control="fader-handle">
|
||||
<div class="fader vertical" data-control="fader" data-fader-id="{{ faderId }}" data-orientation="vertical" style="height:{{height}}px;">
|
||||
<div class="handle" style="bottom:0%;" data-control="fader-handle">
|
||||
<%= image_tag "content/slider_gain_vertical.png", {:width => 28, :height => 11} %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
<!-- Horizontal Fader -->
|
||||
<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="handle" style="left:0%;" control="fader-handle">
|
||||
<div class="fader horizontal" data-control="fader" data-fader-id="{{ faderId }}" data-orientation="horizontal" style="width:{{width}}px;">
|
||||
<div class="handle" style="left:0%;" data-control="fader-handle">
|
||||
<%= image_tag "content/slider_volume.png", {:height => 17, :width => 8} %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@
|
|||
var sessionScreen = new JK.SessionScreen(JK.app);
|
||||
sessionScreen.initialize(localRecordingsDialog, recordingFinishedDialog, JK.FriendSelectorDialogInstance);
|
||||
|
||||
AppActions.appInit(JK.app)
|
||||
AppActions.appInit.trigger(JK.app)
|
||||
|
||||
var sessionSettingsDialog = new JK.SessionSettingsDialog(JK.app, sessionScreen);
|
||||
sessionSettingsDialog.initialize();
|
||||
|
|
|
|||
Loading…
Reference in New Issue