Compare commits
13 Commits
develop
...
build-3001
| Author | SHA1 | Date |
|---|---|---|
|
|
49caddd79f | |
|
|
c96a19d1b4 | |
|
|
6f9d6c2fb5 | |
|
|
3130ab31a2 | |
|
|
512e89074b | |
|
|
7e6e79ccca | |
|
|
bc0add9aa0 | |
|
|
4be1117a02 | |
|
|
910b052204 | |
|
|
7a803bb682 | |
|
|
a769dd37bf | |
|
|
c28d8f3e7f | |
|
|
60302fc1fe |
|
|
@ -94,6 +94,7 @@ gem 'react-rails', '~> 1.0'
|
|||
|
||||
source 'https://rails-assets.org' do
|
||||
gem 'rails-assets-reflux'
|
||||
gem 'rails-assets-classnames'
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 263 B |
Binary file not shown.
|
After Width: | Height: | Size: 296 B |
Binary file not shown.
|
After Width: | Height: | Size: 1010 B |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -198,50 +198,56 @@
|
|||
|
||||
function loggedIn(header, payload) {
|
||||
|
||||
server.signedIn = true;
|
||||
server.clientID = payload.client_id;
|
||||
server.publicIP = payload.public_ip;
|
||||
// reason for setTimeout:
|
||||
// loggedIn causes an absolute ton of initialization to happen, and errors sometimes happen
|
||||
// but because loggedIn(header,payload) is a callback from a websocket, the browser doesn't show a stack trace...
|
||||
|
||||
if (context.jamClient !== undefined) {
|
||||
context.jamClient.connected = true;
|
||||
context.jamClient.clientID = server.clientID;
|
||||
}
|
||||
setTimeout(function() {
|
||||
server.signedIn = true;
|
||||
server.clientID = payload.client_id;
|
||||
server.publicIP = payload.public_ip;
|
||||
|
||||
clearConnectTimeout();
|
||||
if (context.jamClient !== undefined) {
|
||||
context.jamClient.connected = true;
|
||||
context.jamClient.clientID = server.clientID;
|
||||
}
|
||||
|
||||
heartbeatStateReset();
|
||||
clearConnectTimeout();
|
||||
|
||||
app.clientId = payload.client_id;
|
||||
heartbeatStateReset();
|
||||
|
||||
if(isClientMode()) {
|
||||
// tell the backend that we have logged in
|
||||
context.jamClient.OnLoggedIn(payload.user_id, payload.token); // ACTS AS CONTINUATION
|
||||
$.cookie('client_id', payload.client_id);
|
||||
}
|
||||
app.clientId = payload.client_id;
|
||||
|
||||
// this has to be after context.jamclient.OnLoggedIn, because it hangs in scenarios
|
||||
// where there is no device on startup for the current profile.
|
||||
// So, in that case, it's possible that a reconnect loop will attempt, but we *do not want*
|
||||
// it to go through unless we've passed through .OnLoggedIn
|
||||
server.connected = true;
|
||||
server.reconnecting = false;
|
||||
server.connecting = false;
|
||||
initialConnectAttempt = false;
|
||||
if (isClientMode()) {
|
||||
// tell the backend that we have logged in
|
||||
context.jamClient.OnLoggedIn(payload.user_id, payload.token); // ACTS AS CONTINUATION
|
||||
$.cookie('client_id', payload.client_id);
|
||||
}
|
||||
|
||||
heartbeatMS = payload.heartbeat_interval * 1000;
|
||||
connection_expire_time = payload.connection_expire_time * 1000;
|
||||
logger.info("loggedIn(): clientId=" + app.clientId + " heartbeat=" + payload.heartbeat_interval + "s expire_time=" + payload.connection_expire_time + 's');
|
||||
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
|
||||
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
|
||||
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
|
||||
connectDeferred.resolve();
|
||||
$self.triggerHandler(EVENTS.CONNECTION_UP)
|
||||
// this has to be after context.jamclient.OnLoggedIn, because it hangs in scenarios
|
||||
// where there is no device on startup for the current profile.
|
||||
// So, in that case, it's possible that a reconnect loop will attempt, but we *do not want*
|
||||
// it to go through unless we've passed through .OnLoggedIn
|
||||
server.connected = true;
|
||||
server.reconnecting = false;
|
||||
server.connecting = false;
|
||||
initialConnectAttempt = false;
|
||||
|
||||
activeElementEvent('afterConnect', payload);
|
||||
heartbeatMS = payload.heartbeat_interval * 1000;
|
||||
connection_expire_time = payload.connection_expire_time * 1000;
|
||||
logger.info("loggedIn(): clientId=" + app.clientId + " heartbeat=" + payload.heartbeat_interval + "s expire_time=" + payload.connection_expire_time + 's');
|
||||
heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS);
|
||||
heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000);
|
||||
lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat
|
||||
connectDeferred.resolve();
|
||||
$self.triggerHandler(EVENTS.CONNECTION_UP)
|
||||
|
||||
if(payload.client_update && context.JK.ClientUpdateInstance) {
|
||||
context.JK.ClientUpdateInstance.runCheck(payload.client_update.product, payload.client_update.version, payload.client_update.uri, payload.client_update.size)
|
||||
}
|
||||
activeElementEvent('afterConnect', payload);
|
||||
|
||||
if (payload.client_update && context.JK.ClientUpdateInstance) {
|
||||
context.JK.ClientUpdateInstance.runCheck(payload.client_update.product, payload.client_update.version, payload.client_update.uri, payload.client_update.size)
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function heartbeatAck(header, payload) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
//= require jquery.exists
|
||||
//= require jquery.payment
|
||||
//= require jquery.visible
|
||||
//= require classnames
|
||||
//= require reflux
|
||||
//= require howler.core.js
|
||||
//= require jstz
|
||||
|
|
@ -54,11 +55,12 @@
|
|||
//= 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 webcam_viewer
|
||||
//= require react-components
|
||||
//= require_directory .
|
||||
//= require_directory ./dialog
|
||||
//= require_directory ./wizard
|
||||
|
|
|
|||
|
|
@ -37,20 +37,16 @@
|
|||
}
|
||||
|
||||
function onGenericEvent(type, text) {
|
||||
context.setTimeout(function() {
|
||||
var alert = ALERT_TYPES[type];
|
||||
|
||||
if(alert && alert.title) {
|
||||
app.notify({
|
||||
"title": ALERT_TYPES[type].title,
|
||||
"text": text,
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
});
|
||||
}
|
||||
else {
|
||||
logger.debug("Unhandled Backend Event type %o, data %o", type, text)
|
||||
}
|
||||
}, 1);
|
||||
var alert = ALERT_TYPES[type];
|
||||
|
||||
if(alert && alert.title) {
|
||||
context.NotificationActions.backendNotification({msg: alert.title, detail: alert.message, backend_detail:text, help: alert.help})
|
||||
}
|
||||
else {
|
||||
logger.debug("Unhandled Backend Event type %o, data %o", type, text)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function alertCallback(type, text) {
|
||||
|
|
@ -77,8 +73,11 @@
|
|||
}
|
||||
|
||||
if (type === 2) { // BACKEND_MIXER_CHANGE
|
||||
if(context.JK.CurrentSessionModel)
|
||||
context.JK.CurrentSessionModel.onBackendMixerChanged(type, text)
|
||||
|
||||
context.MixerActions.mixersChanged(type, text)
|
||||
|
||||
//if(context.JK.CurrentSessionModel)
|
||||
// context.JK.CurrentSessionModel.onBackendMixerChanged(type, text)
|
||||
}
|
||||
else if (type === ALERT_NAMES.NO_VALID_AUDIO_CONFIG) { // NO_VALID_AUDIO_CONFIG
|
||||
if(context.JK.GearUtilsInstance && context.JK.GearUtilsInstance.isRestartingAudio()) {
|
||||
|
|
@ -101,28 +100,36 @@
|
|||
onStunEvent();
|
||||
}
|
||||
else if (type === 26) { // DEAD_USER_REMOVE_EVENT
|
||||
if(context.JK.CurrentSessionModel)
|
||||
context.JK.CurrentSessionModel.onDeadUserRemove(type, text);
|
||||
MixerActions.deadUserRemove(text);
|
||||
//if(context.JK.CurrentSessionModel)
|
||||
// context.JK.CurrentSessionModel.onDeadUserRemove(type, text);
|
||||
}
|
||||
else if (type === 27) { // WINDOW_CLOSE_BACKGROUND_MODE
|
||||
if(context.JK.CurrentSessionModel)
|
||||
context.JK.CurrentSessionModel.onWindowBackgrounded(type, text);
|
||||
|
||||
SessionActions.windowBackgrounded()
|
||||
|
||||
//if(context.JK.CurrentSessionModel)
|
||||
// context.JK.CurrentSessionModel.onWindowBackgrounded(type, text);
|
||||
}
|
||||
else if(type === ALERT_NAMES.SESSION_LIVEBROADCAST_FAIL) {
|
||||
if(context.JK.CurrentSessionModel)
|
||||
context.JK.CurrentSessionModel.onBroadcastFailure(type, text);
|
||||
SessionActions.broadcastFailure(text)
|
||||
//if(context.JK.CurrentSessionModel)
|
||||
// context.JK.CurrentSessionModel.onBroadcastFailure(type, text);
|
||||
}
|
||||
else if(type === ALERT_NAMES.SESSION_LIVEBROADCAST_ACTIVE) {
|
||||
if(context.JK.CurrentSessionModel)
|
||||
context.JK.CurrentSessionModel.onBroadcastSuccess(type, text);
|
||||
SessionActions.broadcastSuccess(text)
|
||||
//if(context.JK.CurrentSessionModel)
|
||||
// context.JK.CurrentSessionModel.onBroadcastSuccess(type, text);
|
||||
}
|
||||
else if(type === ALERT_NAMES.SESSION_LIVEBROADCAST_STOPPED) {
|
||||
if(context.JK.CurrentSessionModel)
|
||||
context.JK.CurrentSessionModel.onBroadcastStopped(type, text);
|
||||
SessionActions.broadcastStopped(text)
|
||||
//if(context.JK.CurrentSessionModel)
|
||||
//context.JK.CurrentSessionModel.onBroadcastStopped(type, text);
|
||||
}
|
||||
else if(type === ALERT_NAMES.RECORD_PLAYBACK_STATE) {
|
||||
if(context.JK.CurrentSessionModel)
|
||||
context.JK.CurrentSessionModel.onPlaybackStateChange(type, text);
|
||||
//if(context.JK.CurrentSessionModel)
|
||||
// context.JK.CurrentSessionModel.onPlaybackStateChange(type, text);
|
||||
context.MediaPlaybackActions.playbackStateChange(text);
|
||||
}
|
||||
else if((!context.JK.CurrentSessionModel || !context.JK.CurrentSessionModel.inSession()) &&
|
||||
(ALERT_NAMES.INPUT_IO_RATE == type || ALERT_NAMES.INPUT_IO_JTR == type || ALERT_NAMES.OUTPUT_IO_RATE == type || ALERT_NAMES.OUTPUT_IO_JTR== type)) {
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@
|
|||
updateUri = uri;
|
||||
updateSize = size;
|
||||
|
||||
if(context.JK.CurrentSessionModel && context.JK.CurrentSessionModel.inSession()) {
|
||||
if(context.SessionStore.inSession()) {
|
||||
logger.debug("deferring client update because in session")
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
context.JK ||= {};
|
||||
broadcastActions = context.JK.Actions.Broadcast
|
||||
broadcastActions = @BroadcastActions
|
||||
|
||||
context.JK.ClientInit = class ClientInit
|
||||
constructor: () ->
|
||||
|
|
@ -21,7 +21,10 @@ context.JK.ClientInit = class ClientInit
|
|||
this.watchBroadcast()
|
||||
|
||||
checkBroadcast: () =>
|
||||
broadcastActions.load.triggerPromise()
|
||||
broadcastActions.load.triggerPromise().catch(() ->
|
||||
false
|
||||
)
|
||||
|
||||
|
||||
watchBroadcast: () =>
|
||||
if context.JK.currentUserId
|
||||
|
|
|
|||
|
|
@ -120,11 +120,11 @@
|
|||
openingRecording = true;
|
||||
|
||||
// tell the server we are about to start a recording
|
||||
rest.startPlayClaimedRecording({id: context.JK.CurrentSessionModel.id(), claimed_recording_id: claimedRecording.id})
|
||||
rest.startPlayClaimedRecording({id: context.SessionStore.id(), claimed_recording_id: claimedRecording.id})
|
||||
.done(function(response) {
|
||||
|
||||
// update session info
|
||||
context.JK.CurrentSessionModel.updateSession(response);
|
||||
context.SessionActions.updateSession.trigger(response);
|
||||
|
||||
var recordingId = $(this).attr('data-recording-id');
|
||||
var openRecordingResult = context.jamClient.OpenRecording(claimedRecording.recording);
|
||||
|
|
@ -138,7 +138,7 @@
|
|||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
});
|
||||
|
||||
rest.stopPlayClaimedRecording({id: context.JK.CurrentSessionModel.id(), claimed_recording_id: claimedRecording.id})
|
||||
rest.stopPlayClaimedRecording({id: context.SessionStore.id(), claimed_recording_id: claimedRecording.id})
|
||||
.fail(function(jqXHR) {
|
||||
app.notify({
|
||||
"title": "Couldn't Stop Recording Playback",
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@
|
|||
var backingTrack = $(this).data('server-model');
|
||||
|
||||
// tell the server we are about to open a backing track:
|
||||
rest.openBackingTrack({id: context.JK.CurrentSessionModel.id(), backing_track_path: backingTrack.name})
|
||||
rest.openBackingTrack({id: context.SessionStore.id(), backing_track_path: backingTrack.name})
|
||||
.done(function(response) {
|
||||
var result = context.jamClient.SessionOpenBackingTrackFile(backingTrack.name, false);
|
||||
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
// else {
|
||||
// logger.error("unable to open backing track")
|
||||
// }
|
||||
context.JK.CurrentSessionModel.refreshCurrentSession(true);
|
||||
context.SessionActions.syncWithServer()
|
||||
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
|
|
|
|||
|
|
@ -86,10 +86,10 @@
|
|||
var jamTrack = $(this).data('server-model');
|
||||
|
||||
// tell the server we are about to open a jamtrack
|
||||
rest.openJamTrack({id: context.JK.CurrentSessionModel.id(), jam_track_id: jamTrack.id})
|
||||
rest.openJamTrack({id: context.SessionStore.id(), jam_track_id: jamTrack.id})
|
||||
.done(function(response) {
|
||||
$dialog.data('result', {success:true, jamTrack: jamTrack})
|
||||
context.JK.CurrentSessionModel.updateSession(response);
|
||||
context.SessionActions.updateSession.trigger(response);
|
||||
app.layout.closeDialog('open-jam-track-dialog');
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@
|
|||
function events() {
|
||||
$('#btn-rate-session-cancel', $scopeSelector).click(function(evt) {
|
||||
closeDialog();
|
||||
return false;
|
||||
});
|
||||
$('#btn-rate-session-up', $scopeSelector).click(function(evt) {
|
||||
if ($(this).hasClass('selected')) {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
});
|
||||
}
|
||||
else if (localResult.aggregate_state == 'PARTIALLY_MISSING') {
|
||||
logger.error("unable to open recording due to some missing tracks: %o", localResults);
|
||||
logger.error("unable to open recording due to some missing tracks: %o", recording, localResults);
|
||||
app.notify({
|
||||
title: "Unable to Open Recording for Playback",
|
||||
text: "Some of your tracks associated with the recording are missing. This is a bug in the application.",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
context.JK ||= {}
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
context.JK.SessionMasterMixDialog = class SessionMasterMixDialog
|
||||
constructor: (@app) ->
|
||||
@rest = context.JK.Rest()
|
||||
@logger = context.JK.logger
|
||||
@screen = null
|
||||
@dialogId = 'session-master-mix-dialog'
|
||||
@dialog = null
|
||||
@closeBtn = null
|
||||
|
||||
initialize:() =>
|
||||
dialogBindings =
|
||||
'beforeShow' : @beforeShow
|
||||
'afterShow' : @afterShow
|
||||
'afterHide' : @afterHide
|
||||
|
||||
|
||||
@dialog = $('[layout-id="' + @dialogId + '"]')
|
||||
@app.bindDialog(@dialogId, dialogBindings)
|
||||
@content = @dialog.find(".dialog-inner")
|
||||
|
||||
beforeShow:() =>
|
||||
@logger.debug("session-master-mix-dlg: beforeShow")
|
||||
context.jamClient.SetMixerMode(MIX_MODES.MASTER)
|
||||
|
||||
afterShow:() =>
|
||||
@logger.debug("session-master-mix-dlg: afterShow")
|
||||
|
||||
afterHide:() =>
|
||||
context.jamClient.SetMixerMode(MIX_MODES.PERSONAL)
|
||||
|
||||
|
||||
|
|
@ -6,11 +6,12 @@
|
|||
var gearUtils = context.JK.GearUtilsInstance;
|
||||
var $dialog;
|
||||
var $screen = $('#session-settings');
|
||||
var $selectedFilenames = $screen.find('#selected-filenames');
|
||||
var $uploadSpinner = $screen.find('.upload-spinner');
|
||||
var $selectedFilenames = $('#settings-selected-filenames');
|
||||
//var $selectedFilenames = $screen.find('#selected-filenames');
|
||||
var $uploadSpinner = $screen.find('.spinner-small');
|
||||
//var $selectedFilenames = $('#settings-selected-filenames');
|
||||
var $inputFiles = $screen.find('#session-select-files');
|
||||
var $btnSelectFiles = $screen.find('.btn-select-files');
|
||||
var $inputBox = $screen.find('.inputbox')
|
||||
var rest = new JK.Rest();
|
||||
var sessionId;
|
||||
|
||||
|
|
@ -21,7 +22,7 @@
|
|||
context.JK.GenreSelectorHelper.render('#session-settings-genre');
|
||||
$dialog = $('[layout-id="session-settings"]');
|
||||
|
||||
var currentSession = sessionScreen.getCurrentSession();
|
||||
var currentSession = context.SessionStore.currentSession;
|
||||
sessionId = currentSession.id;
|
||||
|
||||
// id
|
||||
|
|
@ -65,13 +66,21 @@
|
|||
$('#session-settings-fan-access').val('listen-chat-band');
|
||||
}
|
||||
|
||||
// notation files
|
||||
/**
|
||||
// notation files in the account screen. ugh.
|
||||
$selectedFilenames.empty();
|
||||
for (var i=0; i < currentSession.music_notations.length; i++) {
|
||||
var notation = currentSession.music_notations[i];
|
||||
$selectedFilenames.append('<a href="' + notation.file_url + '" rel="external">' + notation.file_name + '</a> ');
|
||||
}*/
|
||||
|
||||
$inputBox.empty();
|
||||
for (var i=0; i < currentSession.music_notations.length; i++) {
|
||||
var notation = currentSession.music_notations[i];
|
||||
addNotation(notation)
|
||||
}
|
||||
|
||||
|
||||
context.JK.dropdown($('#session-settings-language'));
|
||||
context.JK.dropdown($('#session-settings-musician-access'));
|
||||
context.JK.dropdown($('#session-settings-fan-access'));
|
||||
|
|
@ -81,6 +90,29 @@
|
|||
$('#session-settings-fan-access').easyDropDown(easyDropDownState)
|
||||
}
|
||||
|
||||
function addNotation(notation) {
|
||||
|
||||
var $notation = $('<div class="notation-entry"><div>' + notation.file_name + '</div><a href="#" data-id="' + notation.id + '">X</a></div>')
|
||||
$notation.find('a').on('click', function(e) {
|
||||
|
||||
if($(this).attr('data-deleting')) {
|
||||
// ignore duplicate delete attempts
|
||||
return false;
|
||||
}
|
||||
|
||||
$(this).attr('data-deleting', true)
|
||||
var $notationEntry = $(this).closest('.notation-entry').find('div').text('deleting...')
|
||||
|
||||
rest.deleteMusicNotation({id: notation.id})
|
||||
.done(function() {
|
||||
$notation.remove()
|
||||
})
|
||||
.fail(app.ajaxError)
|
||||
return false;
|
||||
})
|
||||
$inputBox.append($notation);
|
||||
}
|
||||
|
||||
function saveSettings(evt) {
|
||||
|
||||
var data = {};
|
||||
|
|
@ -111,16 +143,14 @@
|
|||
data.fan_access = false;
|
||||
data.fan_chat = false;
|
||||
}
|
||||
else if (fanAccess == 'listen-chat-each') {
|
||||
data.fan_access = true;
|
||||
data.fan_chat = false;
|
||||
}
|
||||
else if (fanAccess == 'listen-chat-band') {
|
||||
else if (fanAccess == 'listen-chat') {
|
||||
data.fan_access = true;
|
||||
data.fan_chat = true;
|
||||
}
|
||||
|
||||
rest.updateSession($('#session-settings-id').val(), data).done(settingsSaved);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function uploadNotations(notations) {
|
||||
|
|
@ -177,7 +207,7 @@
|
|||
}
|
||||
})
|
||||
.always(function() {
|
||||
$btnSelectFiles.text('SELECT FILES...').data('uploading', null)
|
||||
$btnSelectFiles.text('ADD FILES...').data('uploading', null)
|
||||
$uploadSpinner.hide();
|
||||
});
|
||||
}
|
||||
|
|
@ -203,10 +233,9 @@
|
|||
else {
|
||||
// upload as soon as user picks their files.
|
||||
uploadNotations($inputFiles.get(0).files)
|
||||
.done(function() {
|
||||
context._.each(fileNames, function(fileName) {
|
||||
var $text = $('<span>').text(fileName);
|
||||
$selectedFilenames.append($text);
|
||||
.done(function(response) {
|
||||
context._.each(response, function(notation) {
|
||||
addNotation(notation)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
@ -225,13 +254,13 @@
|
|||
|
||||
function settingsSaved(response) {
|
||||
// No response returned from this call. 204.
|
||||
sessionScreen.refreshCurrentSession(true);
|
||||
context.SessionActions.syncWithServer()
|
||||
app.layout.closeDialog('session-settings');
|
||||
}
|
||||
|
||||
function events() {
|
||||
$('#session-settings-dialog-submit').on('click', saveSettings);
|
||||
|
||||
$('#session-settings-dialog').on('submit', saveSettings)
|
||||
$inputFiles.on('change', changeSelectedFiles);
|
||||
$btnSelectFiles.on('click', toggleSelectFiles);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@
|
|||
|
||||
function onStartRecording(from, payload) {
|
||||
logger.debug("received start recording request from " + from);
|
||||
if(context.JK.CurrentSessionModel.recordingModel.isRecording()) {
|
||||
if(context.SessionStore.isRecording()) {
|
||||
// reject the request to start the recording
|
||||
context.JK.JamServer.sendP2PMessage(from, JSON.stringify(p2pMessageFactory.startRecordingAck(payload.recordingId, false, "already-recording", null)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,23 +123,23 @@
|
|||
0: {"title": "", "message": ""}, // NO_EVENT,
|
||||
1: {"title": "", "message": ""}, // BACKEND_ERROR: generic error - eg P2P message error
|
||||
2: {"title": "", "message": ""}, // BACKEND_MIXER_CHANGE, - event that controls have been regenerated
|
||||
3: {"title": "High Packet Jitter", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // PACKET_JTR,
|
||||
4: {"title": "High Packet Loss", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // PACKET_LOSS
|
||||
5: {"title": "High Packet Late", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // PACKET_LATE,
|
||||
6: {"title": "Large Jitter Queue", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // JTR_QUEUE_DEPTH,
|
||||
7: {"title": "High Network Jitter", "message": "Your network connection is currently experiencing network jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // NETWORK_JTR,
|
||||
8: {"title": "High Session Latency", "message": "The latency of your audio device combined with your Internet connection has become high enough to impact your session quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // NETWORK_PING,
|
||||
9: {"title": "Bandwidth Throttled", "message": "The available bandwidth on your network has diminished, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // BITRATE_THROTTLE_WARN,
|
||||
10:{"title": "Low Bandwidth", "message": "The available bandwidth on your network has become too low, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // BANDWIDTH_LOW
|
||||
3: {"title": "High Packet Jitter", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // PACKET_JTR,
|
||||
4: {"title": "High Packet Loss", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // PACKET_LOSS
|
||||
5: {"title": "High Packet Late", "message": "Your network connection is currently experiencing packet loss at a rate that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // PACKET_LATE,
|
||||
6: {"title": "Large Jitter Queue", "message": "Your network connection is currently experiencing packet jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // JTR_QUEUE_DEPTH,
|
||||
7: {"title": "High Network Jitter", "message": "Your network connection is currently experiencing network jitter at a level that is too high to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // NETWORK_JTR,
|
||||
8: {"title": "High Session Latency", "message": "The latency of your audio device combined with your Internet connection has become high enough to impact your session quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // NETWORK_PING,
|
||||
9: {"title": "Bandwidth Throttled", "message": "The available bandwidth on your network has diminished, and this may impact your audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // BITRATE_THROTTLE_WARN,
|
||||
10:{"title": "Low Bandwidth", "message": "The available bandwidth on your network has become too low, and this may impact your audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // BANDWIDTH_LOW
|
||||
|
||||
// IO related events
|
||||
11:{"title": "Variable Input Rate", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // INPUT_IO_RATE
|
||||
12:{"title": "High Input Jitter", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // INPUT_IO_JTR,
|
||||
13:{"title": "Variable Output Rate", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // OUTPUT_IO_RATE
|
||||
14:{"title": "High Output Jitter", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // OUTPUT_IO_JTR,
|
||||
11:{"title": "Variable Input Rate", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems" }, // INPUT_IO_RATE
|
||||
12:{"title": "High Input Jitter", "message": "The input rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // INPUT_IO_JTR,
|
||||
13:{"title": "Variable Output Rate", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // OUTPUT_IO_RATE
|
||||
14:{"title": "High Output Jitter", "message": "The output rate of your audio device is varying too much to deliver good audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // OUTPUT_IO_JTR,
|
||||
|
||||
// CPU load related
|
||||
15: { "title": "CPU Utilization High", "message": "The CPU of your computer is unable to keep up with the current processing load, and this may impact your audio quality. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>." }, // CPU_LOAD
|
||||
15: { "title": "CPU Utilization High", "message": "The CPU of your computer is unable to keep up with the current processing load, and this may impact your audio quality. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // CPU_LOAD
|
||||
16: {"title": "Decode Violations", "message": ""}, // DECODE_VIOLATIONS,
|
||||
17: {"title": "", "message": ""}, // LAST_THRESHOLD
|
||||
18: {"title": "Wifi Alert", "message": ""}, // WIFI_NETWORK_ALERT, //user or peer is using wifi
|
||||
|
|
@ -162,10 +162,10 @@
|
|||
33: {"title": "Client No Longer Pinned", "message": "This client is no longer designated as the source of the broadcast."}, // SESSION_LIVEBROADCAST_UNPINNED, //node unpinned by user
|
||||
|
||||
34: {"title": "", "message": ""}, // BACKEND_STATUS_MSG, //status/informational message
|
||||
35: {"title": "LAN Unpredictable", "message": "Your local network is adding considerable variance to transmit times. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // LOCAL_NETWORK_VARIANCE_HIGH,//the ping time via a hairpin for the user network is unnaturally high or variable.
|
||||
35: {"title": "LAN Unpredictable", "message": "Your local network is adding considerable variance to transmit times. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // LOCAL_NETWORK_VARIANCE_HIGH,//the ping time via a hairpin for the user network is unnaturally high or variable.
|
||||
|
||||
//indicates problem with user computer stack or network itself (wifi, antivirus etc)
|
||||
36: {"title": "LAN High Latency", "message": "Your local network is adding considerable latency. For troubleshooting tips, <a href='https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems'>click here</a>."}, // LOCAL_NETWORK_LATENCY_HIGH,
|
||||
36: {"title": "LAN High Latency", "message": "Your local network is adding considerable latency. For troubleshooting tips, click the '?'.", help: "https://jamkazam.desk.com/customer/portal/articles/1288778-troubleshoot-session-quality-problems"}, // LOCAL_NETWORK_LATENCY_HIGH,
|
||||
37: {"title": "", "message": ""}, // RECORDING_CLOSE, //update and remove tracks from front-end
|
||||
38: {"title": "No Audio Sent", "message": ""}, // PEER_REPORTS_NO_AUDIO_RECV, //update and remove tracks from front-end
|
||||
39: {"title": "", "message": ""}, // SHOW_PREFERENCES, //show preferences dialog
|
||||
|
|
@ -313,4 +313,35 @@
|
|||
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
|
||||
};
|
||||
|
||||
context.JK.CategoryGroupIds = {
|
||||
"AudioInputMusic" : "AudioInputMusic",
|
||||
"AudioInputChat" : "AudioInputChat",
|
||||
"UserMusic" : "UserMusic",
|
||||
"UserChat" : "UserChat",
|
||||
"UserMedia" : "UserMedia",
|
||||
"MediaTrack" : "MediaTrack",
|
||||
"Metronome" : "Metronome"
|
||||
}
|
||||
|
||||
})(window,jQuery);
|
||||
|
|
@ -95,6 +95,14 @@
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
function deleteMusicNotation(options) {
|
||||
return $.ajax({
|
||||
type: "DELETE",
|
||||
url: "/api/music_notations/" +options.id
|
||||
});
|
||||
}
|
||||
|
||||
function legacyJoinSession(options) {
|
||||
var sessionId = options["session_id"];
|
||||
delete options["session_id"];
|
||||
|
|
@ -478,6 +486,14 @@
|
|||
});
|
||||
}
|
||||
|
||||
function deleteParticipant(clientId) {
|
||||
var url = "/api/participants/" + clientId;
|
||||
return $.ajax({
|
||||
type: "DELETE",
|
||||
url: url
|
||||
});
|
||||
}
|
||||
|
||||
function login(options) {
|
||||
var url = '/api/auths/login';
|
||||
|
||||
|
|
@ -1800,6 +1816,7 @@
|
|||
this.createScheduledSession = createScheduledSession;
|
||||
this.uploadMusicNotations = uploadMusicNotations;
|
||||
this.getMusicNotation = getMusicNotation;
|
||||
this.deleteMusicNotation = deleteMusicNotation;
|
||||
this.getBroadcastNotification = getBroadcastNotification;
|
||||
this.quietBroadcastNotification = quietBroadcastNotification;
|
||||
this.legacyJoinSession = legacyJoinSession;
|
||||
|
|
@ -1854,6 +1871,7 @@
|
|||
this.addRecordingLike = addRecordingLike;
|
||||
this.addPlayablePlay = addPlayablePlay;
|
||||
this.getSession = getSession;
|
||||
this.deleteParticipant = deleteParticipant;
|
||||
this.getClientDownloads = getClientDownloads;
|
||||
this.createEmailInvitations = createEmailInvitations;
|
||||
this.createMusicianInvite = createMusicianInvite;
|
||||
|
|
|
|||
|
|
@ -308,7 +308,6 @@ context.JK.JamTrackScreen=class JamTrackScreen
|
|||
rest.addJamtrackToShoppingCart(params).done((response) =>
|
||||
if(isFree)
|
||||
if context.JK.currentUserId?
|
||||
alert("TODO")
|
||||
context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices
|
||||
context.location = '/client#/redeemComplete'
|
||||
else
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
$.fn.metronomePlaybackMode = function(options) {
|
||||
|
||||
options = options || {mode: 'self'}
|
||||
options = $.extend(false, {mode: 'self', positions: ['top']}, options);
|
||||
|
||||
return this.each(function(index) {
|
||||
|
||||
|
|
@ -78,8 +78,8 @@
|
|||
spikeLength:0,
|
||||
width:180,
|
||||
closeWhenOthersOpen: true,
|
||||
offsetParent: $parent.offsetParent(),
|
||||
positions:['top'],
|
||||
offsetParent: options.offsetParent || $parent.offsetParent(),
|
||||
positions: options.positions,
|
||||
preShow: function() {
|
||||
$parent.find('.down-arrow').removeClass('down-arrow').addClass('up-arrow')
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
//= require bugsnag
|
||||
//= require bind-polyfill
|
||||
//= require jquery
|
||||
//= require jquery.monkeypatch
|
||||
//= require jquery_ujs
|
||||
//= require jquery.ui.draggable
|
||||
//= require jquery.ui.droppable
|
||||
//= require jquery.bt
|
||||
//= require jquery.icheck
|
||||
//= require jquery.easydropdown
|
||||
//= require jquery.metronomePlaybackMode
|
||||
//= require classnames
|
||||
//= require reflux
|
||||
//= require AAC_underscore
|
||||
//= require AAA_Log
|
||||
//= require globals
|
||||
//= require jam_rest
|
||||
//= require ga
|
||||
//= require utils
|
||||
//= require playbackControls
|
||||
//= require webcam_viewer
|
||||
//= require react
|
||||
//= require react_ujs
|
||||
//= require react-init
|
||||
//= require react-components
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
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)
|
||||
|
||||
convertPercentToPanForDisplay: (percent) ->
|
||||
Math.abs(context.JK.PanHelpers.convertPercentToPan(percent))
|
||||
|
||||
context.JK.PanHelpers = new panHelper()
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* Playback widget (play, pause , etc)
|
||||
*/
|
||||
|
||||
(function(context, $) {
|
||||
|
||||
"use strict";
|
||||
|
|
@ -18,7 +19,7 @@
|
|||
context.JK = context.JK || {};
|
||||
context.JK.PlaybackControls = function($parentElement, options){
|
||||
|
||||
options = $.extend(false, {playmodeControlsVisible:false}, options);
|
||||
options = $.extend(false, {playmodeControlsVisible:false, mediaActions:null}, options);
|
||||
|
||||
var logger = context.JK.logger;
|
||||
if($parentElement.length == 0) {
|
||||
|
|
@ -68,23 +69,42 @@
|
|||
if(endReached) {
|
||||
update(0, playbackDurationMs, playbackPlaying);
|
||||
}
|
||||
$self.triggerHandler('play', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode});
|
||||
|
||||
if(options.mediaActions) {
|
||||
options.mediaActions.mediaStartPlay({playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode})
|
||||
}
|
||||
else {
|
||||
$self.triggerHandler('play', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode});
|
||||
|
||||
}
|
||||
|
||||
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
|
||||
var sessionModel = context.JK.CurrentSessionModel || null;
|
||||
context.JK.GA.trackJamTrackPlaySession(sessionModel.id(), true)
|
||||
context.JK.GA.trackJamTrackPlaySession(context.SessionStore.id(), true)
|
||||
}
|
||||
}
|
||||
|
||||
function stopPlay(endReached) {
|
||||
logger.debug("STOP PLAY CLICKED")
|
||||
updateIsPlaying(false);
|
||||
$self.triggerHandler('stop', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
|
||||
|
||||
if(options.mediaActions) {
|
||||
logger.debug("mediaStopPlay", endReached)
|
||||
options.mediaActions.mediaStopPlay({playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached})
|
||||
}
|
||||
else {
|
||||
$self.triggerHandler('stop', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
|
||||
}
|
||||
}
|
||||
|
||||
function pausePlay(endReached) {
|
||||
updateIsPlaying(false);
|
||||
$self.triggerHandler('pause', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
|
||||
|
||||
if(options.mediaActions) {
|
||||
options.mediaActions.mediaPausePlay({playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached})
|
||||
}
|
||||
else {
|
||||
$self.triggerHandler('pause', {playbackMode: playbackMode, playbackMonitorMode: playbackMonitorMode, endReached : endReached});
|
||||
}
|
||||
}
|
||||
|
||||
function updateOffsetBasedOnPosition(offsetLeft) {
|
||||
|
|
@ -93,8 +113,13 @@
|
|||
playbackPositionMs = parseInt((offsetLeft / sliderBarWidth) * playbackDurationMs);
|
||||
updateCurrentTimeText(playbackPositionMs);
|
||||
if(canUpdateBackend) {
|
||||
if(options.mediaActions) {
|
||||
options.mediaActions.mediaChangePosition({positionMs: playbackPositionMs, playbackMonitorMode: playbackMonitorMode})
|
||||
}
|
||||
else {
|
||||
$self.triggerHandler('change-position', {positionMs: playbackPositionMs, playbackMonitorMode: playbackMonitorMode});
|
||||
canUpdateBackend = false;
|
||||
}
|
||||
canUpdateBackend = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,34 +152,17 @@
|
|||
}
|
||||
|
||||
$playButton.on('click', function(e) {
|
||||
var sessionModel = context.JK.CurrentSessionModel || null;
|
||||
//if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
|
||||
// context.JK.prodBubble($fader, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $playButton})
|
||||
// return false;
|
||||
//}
|
||||
|
||||
console.log("CLICKED PLAY")
|
||||
startPlay();
|
||||
return false;
|
||||
});
|
||||
|
||||
$pauseButton.on('click', function(e) {
|
||||
var sessionModel = context.JK.CurrentSessionModel || null;
|
||||
//if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
|
||||
// context.JK.prodBubble($pauseButton, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $pauseButton})
|
||||
// return false;
|
||||
//}
|
||||
|
||||
pausePlay();
|
||||
return false;
|
||||
});
|
||||
|
||||
$stopButton.on('click', function(e) {
|
||||
var sessionModel = context.JK.CurrentSessionModel || null;
|
||||
//if(sessionModel && sessionModel.areControlsLockedForJamTrackRecording() && $parentElement.closest('.session-track').data('track_data').type == 'jam_track') {
|
||||
// context.JK.prodBubble($pauseButton, 'jamtrack-controls-disabled', {}, {positions:['top'], offsetParent: $pauseButton})
|
||||
// return false;
|
||||
//}
|
||||
|
||||
stopPlay();
|
||||
return false;
|
||||
});
|
||||
|
|
@ -211,53 +219,61 @@
|
|||
throw "unknown playbackMonitorMode: " + playbackMonitorMode;
|
||||
}
|
||||
}
|
||||
|
||||
function executeMonitor(positionMs, durationMs, isPlaying) {
|
||||
|
||||
if(positionMs < 0) {
|
||||
// bug in backend?
|
||||
positionMs = 0;
|
||||
}
|
||||
|
||||
if(positionMs > 0) {
|
||||
seenActivity = true;
|
||||
}
|
||||
|
||||
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.METRONOME) {
|
||||
updateIsPlaying(isPlaying);
|
||||
}
|
||||
else {
|
||||
update(positionMs, durationMs, isPlaying);
|
||||
}
|
||||
|
||||
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
|
||||
|
||||
if(playbackPlaying) {
|
||||
$jamTrackGetReady.attr('data-current-time', positionMs)
|
||||
}
|
||||
else {
|
||||
// this is so the jamtrack 'Get Ready!' stays hidden when it's not playing
|
||||
$jamTrackGetReady.attr('data-current-time', -1)
|
||||
}
|
||||
}
|
||||
|
||||
monitorPlaybackTimeout = setTimeout(monitorRecordingPlayback, 500);
|
||||
}
|
||||
|
||||
function monitorRecordingPlayback() {
|
||||
if(!monitoring) {
|
||||
return;
|
||||
}
|
||||
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
|
||||
var positionMs = context.jamClient.SessionCurrrentJamTrackPlayPosMs();
|
||||
var duration = context.jamClient.SessionGetJamTracksPlayDurationMs();
|
||||
var durationMs = duration.media_len;
|
||||
var start = duration.start; // needed to understand start offset, and prevent slider from moving in tapins
|
||||
if(options.mediaActions) {
|
||||
options.mediaActions.positionUpdate(playbackMonitorMode)
|
||||
}
|
||||
else {
|
||||
var positionMs = context.jamClient.SessionCurrrentPlayPosMs();
|
||||
var durationMs = context.jamClient.SessionGetTracksPlayDurationMs();
|
||||
}
|
||||
|
||||
var isPlaying = context.jamClient.isSessionTrackPlaying();
|
||||
|
||||
if(positionMs < 0) {
|
||||
// bug in backend?
|
||||
positionMs = 0;
|
||||
}
|
||||
|
||||
if(positionMs > 0) {
|
||||
seenActivity = true;
|
||||
}
|
||||
|
||||
|
||||
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.METRONOME) {
|
||||
updateIsPlaying(isPlaying);
|
||||
}
|
||||
else {
|
||||
update(positionMs, durationMs, isPlaying);
|
||||
}
|
||||
|
||||
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
|
||||
|
||||
if(playbackPlaying) {
|
||||
$jamTrackGetReady.attr('data-current-time', positionMs)
|
||||
if(playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK) {
|
||||
var positionMs = context.jamClient.SessionCurrrentJamTrackPlayPosMs();
|
||||
var duration = context.jamClient.SessionGetJamTracksPlayDurationMs();
|
||||
var durationMs = duration.media_len;
|
||||
}
|
||||
else {
|
||||
// this is so the jamtrack 'Get Ready!' stays hidden when it's not playing
|
||||
$jamTrackGetReady.attr('data-current-time', -1)
|
||||
var positionMs = context.jamClient.SessionCurrrentPlayPosMs();
|
||||
var durationMs = context.jamClient.SessionGetTracksPlayDurationMs();
|
||||
}
|
||||
|
||||
}
|
||||
var isPlaying = context.jamClient.isSessionTrackPlaying();
|
||||
|
||||
monitorPlaybackTimeout = setTimeout(monitorRecordingPlayback, 500);
|
||||
executeMonitor(positionMs, durationMs, isPlaying)
|
||||
}
|
||||
}
|
||||
|
||||
function update(currentTimeMs, durationTimeMs, isPlaying, offsetStart) {
|
||||
|
|
@ -304,7 +320,11 @@
|
|||
}
|
||||
|
||||
function updateCurrentTimeText(timeMs) {
|
||||
$currentTime.text(context.JK.prettyPrintSeconds(parseInt(timeMs / 1000)));
|
||||
var time = context.JK.prettyPrintSeconds(parseInt(timeMs / 1000))
|
||||
$currentTime.text(time);
|
||||
if(options.mediaActions) {
|
||||
options.mediaActions.currentTimeChanged(time)
|
||||
}
|
||||
}
|
||||
|
||||
function updateSliderPosition(timeMs) {
|
||||
|
|
@ -362,6 +382,12 @@
|
|||
}
|
||||
|
||||
function startMonitor(_playbackMonitorMode) {
|
||||
logger.debug("startMonitor: " + _playbackMonitorMode)
|
||||
|
||||
if(monitoring && _playbackMonitorMode == playbackMonitorMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
monitoring = true;
|
||||
// resets everything to zero
|
||||
init();
|
||||
|
|
@ -376,6 +402,11 @@
|
|||
logger.debug("playbackControl.startMonitor " + playbackMonitorMode + "")
|
||||
|
||||
styleControls();
|
||||
|
||||
if(monitorPlaybackTimeout != null) {
|
||||
clearTimeout(monitorPlaybackTimeout);
|
||||
monitorPlaybackTimeout = null;
|
||||
}
|
||||
monitorRecordingPlayback();
|
||||
}
|
||||
|
||||
|
|
@ -407,6 +438,7 @@
|
|||
this.setPlaybackMode = setPlaybackMode;
|
||||
this.startMonitor = startMonitor;
|
||||
this.stopMonitor = stopMonitor;
|
||||
this.executeMonitor = executeMonitor;
|
||||
this.onPlayStopEvent = onPlayStopEvent;
|
||||
this.onPlayStartEvent = onPlayStartEvent;
|
||||
this.onPlayPauseEvent = onPlayPauseEvent;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,14 @@
|
|||
//= require ./react-components/actions/BroadcastActions
|
||||
//= require ./react-components/stores/BroadcastStore
|
||||
//= require_directory ./react-components/helpers
|
||||
//= require_directory ./react-components/actions
|
||||
//= require ./react-components/stores/AppStore
|
||||
//= require ./react-components/stores/RecordingStore
|
||||
//= require ./react-components/stores/SessionStore
|
||||
//= require ./react-components/stores/MixerStore
|
||||
//= require ./react-components/stores/SessionNotificationStore
|
||||
//= require ./react-components/stores/MediaPlaybackStore
|
||||
//= require ./react-components/stores/SessionMyTracksStore
|
||||
//= require ./react-components/stores/SessionOtherTracksStore
|
||||
//= require ./react-components/stores/SessionMediaTracksStore
|
||||
//= require_directory ./react-components/stores
|
||||
//= require_directory ./react-components/mixins
|
||||
//= require_directory ./react-components
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
context = window
|
||||
PLAYBACK_MONITOR_MODE = context.JK.PLAYBACK_MONITOR_MODE
|
||||
EVENTS = context.JK.EVENTS
|
||||
logger = context.JK.logger
|
||||
|
||||
mixins = []
|
||||
|
||||
# this check ensures we attempt to listen if this component is created in a popup
|
||||
reactContext = if window.opener? then window.opener else window
|
||||
|
||||
MixerStore = reactContext.MixerStore
|
||||
MixerActions = reactContext.MixerActions
|
||||
MediaPlaybackStore = reactContext.MediaPlaybackStore
|
||||
SessionActions = reactContext.SessionActions
|
||||
MediaPlaybackActions = reactContext.MediaPlaybackActions
|
||||
|
||||
mixins.push(Reflux.listenTo(MixerStore,"onInputsChanged"))
|
||||
mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged'))
|
||||
|
||||
|
||||
@MediaControls = React.createClass({
|
||||
|
||||
mixins: mixins
|
||||
tempos : [ 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 63, 66, 69, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 126, 132, 138, 144, 152, 160, 168, 176, 184, 192, 200, 208 ]
|
||||
|
||||
onMediaStateChanged: (changes) ->
|
||||
if changes.playbackStateChanged
|
||||
if @state.controls?
|
||||
if changes.playbackState == 'play_start'
|
||||
@state.controls.onPlayStartEvent()
|
||||
else if changes.playbackState == 'play_stop'
|
||||
@state.controls.onPlayStopEvent()
|
||||
else if changes.playbackState == 'play_pause'
|
||||
@state.controls.onPlayPauseEvent();
|
||||
else if changes.positionUpdateChanged
|
||||
if @state.controls?
|
||||
@state.controls.executeMonitor(changes.positionMs, changes.durationMs, changes.isPlaying)
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
|
||||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
|
||||
if @state.controls?
|
||||
mediaSummary = mixers.mediaSummary
|
||||
metro = mixers.metro
|
||||
|
||||
@monitorControls(@state.controls, mediaSummary)
|
||||
@setState({mediaSummary: mediaSummary, metro: metro})
|
||||
|
||||
@updateMetronomeDetails(metro, @state.initializedMetronomeControls)
|
||||
|
||||
updateMetronomeDetails: (metro, initializedMetronomeControls) ->
|
||||
logger.debug("MediaControls: setting tempo/sound/cricket", metro)
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$root.find("select.metro-tempo").val(metro.tempo)
|
||||
$root.find("select.metro-sound").val(metro.sound)
|
||||
|
||||
if initializedMetronomeControls
|
||||
mode = if metro.cricket then 'cricket' else 'self'
|
||||
logger.debug("settingcricket", mode)
|
||||
$root.find('#metronome-playback-select').metronomeSetPlaybackMode(mode)
|
||||
|
||||
monitorControls: (controls, mediaSummary) ->
|
||||
|
||||
if mediaSummary.mediaOpen
|
||||
if mediaSummary.jamTrackOpen
|
||||
controls.startMonitor(PLAYBACK_MONITOR_MODE.JAMTRACK)
|
||||
else if mediaSummary.backingTrackOpen
|
||||
controls.startMonitor(PLAYBACK_MONITOR_MODE.MEDIA_FILE)
|
||||
else if mediaSummary.metronomeOpen
|
||||
controls.startMonitor(PLAYBACK_MONITOR_MODE.METRONOME)
|
||||
else if mediaSummary.recordingOpen
|
||||
controls.startMonitor(PLAYBACK_MONITOR_MODE.MEDIA_FILE)
|
||||
else
|
||||
logger.debug("unable to determine mediaOpen type", mediaSummary)
|
||||
controls.startMonitor(PLAYBACK_MONITOR_MODE.MEDIA_FILE)
|
||||
else
|
||||
controls.stopMonitor()
|
||||
|
||||
metronomePlaybackModeChanged: (e, data) ->
|
||||
|
||||
mode = data.playbackMode # will be either 'self' or 'cricket'
|
||||
|
||||
logger.debug("setting metronome playback mode: ", mode)
|
||||
isCricket = mode == 'cricket';
|
||||
SessionActions.metronomeCricketChange(isCricket)
|
||||
|
||||
|
||||
onMetronomeChanged: () ->
|
||||
|
||||
@setMetronomeFromForm()
|
||||
|
||||
setMetronomeFromForm: () ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
tempo = $root.find("select.metro-tempo:visible option:selected").val()
|
||||
sound = $root.find("select.metro-sound:visible option:selected").val()
|
||||
|
||||
t = parseInt(tempo)
|
||||
s = null
|
||||
if tempo == NaN || tempo == 0 || tempo == null
|
||||
t = 120
|
||||
|
||||
if sound == null || typeof(sound)=='undefined' || sound == ""
|
||||
s = "Beep"
|
||||
else
|
||||
s = sound
|
||||
|
||||
logger.debug("Setting tempo and sound:", t, s)
|
||||
MixerActions.metronomeChanged(t, s, 1, 0)
|
||||
|
||||
render: () ->
|
||||
|
||||
|
||||
tempo_options = []
|
||||
for tempo in @tempos
|
||||
tempo_options.push(`<option value={tempo}>{tempo}</option>`)
|
||||
|
||||
`<div className="media-controls has-mix">
|
||||
|
||||
<div className="jam-track-get-ready">
|
||||
<div className="spinner-small"></div>
|
||||
<span>Get Ready!</span>
|
||||
</div>
|
||||
|
||||
<div className="play-buttons">
|
||||
<a className="play-button" href="#">
|
||||
<img src="/assets/content/icon_playbutton.png" width="20" height="20" className="playbutton" />
|
||||
<img src="/assets/content/icon_pausebutton.png" width="20" height="20" className="pausebutton" />
|
||||
</a>
|
||||
<a className="stop-button" href="#">
|
||||
<img src="/assets/content/icon_stopbutton.png" width="20" height="20" className="stopbutton" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="metronome-playback-options">
|
||||
<span id="metronome-playback-select"></span>
|
||||
</div>
|
||||
|
||||
<div className="metronome-options">
|
||||
<div className="metronome-selects">
|
||||
<div class="metronome-field">
|
||||
<select className="metronome-select metro-sound" title="Metronome Sound" name="metronome_sound">
|
||||
<option value="Beep">Knock</option>
|
||||
<option value="Click">Tap</option>
|
||||
<option value="Snare">Snare</option>
|
||||
<option value="Kick">Kick</option>
|
||||
</select>
|
||||
<label htmlFor="metronome_sound">Sound:</label>
|
||||
</div>
|
||||
<div class="metronome-field">
|
||||
<select className="metronome-select metro-tempo" title="Metronome Tempo" name="metronome_tempo">
|
||||
{tempo_options}
|
||||
</select>
|
||||
<label htmlFor="metronome_tempo">Tempo:</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="recording-time start-time">0:00</div>
|
||||
<div className="recording-playback">
|
||||
<div className="recording-slider"><img src="/assets/content/slider_playcontrols.png" height="16" width="5" /></div>
|
||||
</div>
|
||||
<div className="recording-time duration-time">0:00</div>
|
||||
|
||||
<div className="recording-current">0:00</div>
|
||||
|
||||
<div className="playback-mode-buttons icheckbuttons">
|
||||
<input type="radio" name="playback-mode" defaultChecked="checked" value="preview-to-all" className="preview-to-all" /><label htmlFor="playback-mode-preview-all" className="radio-text">Preview to All</label>
|
||||
<input type="radio" name="playback-mode" value="preview-to-me" className="preview-to-me" /><label htmlFor="playback-mode-preview-me" className="radio-text">Preview Only to Me</label>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
|
||||
getInitialState: () ->
|
||||
{controls: null, mediaSummary: {}, initializedMetronomeControls: false}
|
||||
|
||||
tryPrepareMetronome: (metro) ->
|
||||
if @state.mediaSummary.metronomeOpen && !@state.initializedMetronomeControls
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$root.on("change", ".metronome-select", @onMetronomeChanged)
|
||||
$root.find('#metronome-playback-select').metronomePlaybackMode({positions:['bottom'], offsetParent:$('#minimal-container')}).on(EVENTS.METRONOME_PLAYBACK_MODE_SELECTED, @metronomePlaybackModeChanged)
|
||||
@updateMetronomeDetails(metro, true)
|
||||
@setState({initializedMetronomeControls: true})
|
||||
|
||||
|
||||
componentDidUpdate: (prevProps, prevState) ->
|
||||
@tryPrepareMetronome(@state.metro)
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
controls = context.JK.PlaybackControls($root, {mediaActions: MediaPlaybackActions})
|
||||
|
||||
mediaSummary = MixerStore.mixers.mediaSummary
|
||||
metro = MixerStore.mixers.metro
|
||||
|
||||
@monitorControls(controls, mediaSummary)
|
||||
|
||||
@tryPrepareMetronome(metro)
|
||||
|
||||
@setState({mediaSummary: mediaSummary, controls: controls, metro: metro})
|
||||
})
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
mixins = []
|
||||
|
||||
if window.opener?
|
||||
SessionActions = window.opener.SessionActions
|
||||
MediaPlaybackStore = window.opener.MediaPlaybackStore
|
||||
MixerActions = window.opener.MixerActions
|
||||
|
||||
mixins.push(Reflux.listenTo(MediaPlaybackStore, 'onMediaStateChanged'))
|
||||
|
||||
@PopupMediaControls = React.createClass({
|
||||
|
||||
mixins: mixins
|
||||
|
||||
onMediaStateChanged: (changes) ->
|
||||
if changes.currentTimeChanged && @root?
|
||||
@setState({time: changes.time})
|
||||
|
||||
showMetronome: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
SessionActions.showNativeMetronomeGui()
|
||||
|
||||
getInitialState: () ->
|
||||
{time: '0:00'}
|
||||
|
||||
close: () ->
|
||||
window.close()
|
||||
|
||||
render: () ->
|
||||
|
||||
closeLinkText = null
|
||||
header = null
|
||||
extraControls = null
|
||||
|
||||
# give the users options to close it
|
||||
if @props.mediaSummary.jamTrackOpen
|
||||
mediaType = "JamTrack"
|
||||
mediaName = @props.jamTracks[0].name
|
||||
closeLinkText = 'close JamTrack'
|
||||
header = `<h3>{mediaType}: {mediaName} ({this.state.time})</h3>`
|
||||
else if @props.mediaSummary.backingTrackOpen
|
||||
mediaType = "Audio File"
|
||||
mediaName = context.JK.getNameOfFile(@props.backingTracks[0].shortFilename)
|
||||
closeLinkText = 'close audio file'
|
||||
header = `<h3>{mediaType}: {mediaName} ({this.state.time})</h3>`
|
||||
extraControls =
|
||||
`<div>
|
||||
<div className="field">
|
||||
<input type="checkbox" name="loop" /><label htmlFor="loop">Loop audio file playback</label>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>`
|
||||
else if @props.mediaSummary.metronomeOpen
|
||||
mediaType = "Metronome"
|
||||
closeLinkText = 'close metronome'
|
||||
header = `<h3>Metronome</h3>`
|
||||
extraControls =
|
||||
`<div>
|
||||
<a className="display-metronome" onClick={this.showMetronome}>Display visual metronome</a>
|
||||
</div>`
|
||||
else if @props.mediaSummary.recordingOpen
|
||||
mediaType = "Recording"
|
||||
mediaName = @props.recordedTracks[0].recordingName
|
||||
closeLinkText = 'close recording'
|
||||
header = `<h3>{mediaType}: {mediaName} ({this.state.time})</h3>`
|
||||
else
|
||||
mediaType = ""
|
||||
|
||||
`<div className="media-controls-popup">
|
||||
{header}
|
||||
<MediaControls />
|
||||
{extraControls}
|
||||
<a className="close-link" onClick={this.close}>{closeLinkText}</a>
|
||||
</div>`
|
||||
|
||||
windowUnloaded: () ->
|
||||
SessionActions.closeMedia() unless window.DontAutoCloseMedia
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
$(window).unload(@windowUnloaded)
|
||||
|
||||
@root = jQuery(this.getDOMNode())
|
||||
|
||||
$loop = @root.find('input[name="loop"]')
|
||||
context.JK.checkbox($loop)
|
||||
|
||||
$loop.on('ifChecked', () =>
|
||||
logger.debug("@props", @props)
|
||||
# it doesn't matter if you do personal or master, because backend just syncs both
|
||||
MixerActions.loopChanged(@props.backingTracks[0].mixers.personal.mixer, true)
|
||||
)
|
||||
$loop.on('ifUnchecked', () =>
|
||||
# it doesn't matter if you do personal or master, because backend just syncs both
|
||||
MixerActions.loopChanged(@props.backingTracks[0].mixers.personal.mixer, false)
|
||||
)
|
||||
|
||||
@resizeWindow()
|
||||
|
||||
# this is necessary due to whatever the client's rendering behavior is.
|
||||
setTimeout(@resizeWindow, 300)
|
||||
|
||||
componentDidUpdate: () ->
|
||||
@resizeWindow()
|
||||
|
||||
resizeWindow: () =>
|
||||
$container = $('#minimal-container')
|
||||
width = $container.width()
|
||||
height = $container.height()
|
||||
|
||||
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
|
||||
#mysteryTopMargin = 20
|
||||
mysteryTopMargin = 0
|
||||
# deal with chrome in real browsers
|
||||
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
|
||||
|
||||
# handle native client chrome that the above outer-inner doesn't catch
|
||||
#if navigator.userAgent.indexOf('JamKazam') > -1
|
||||
|
||||
#offset += 25
|
||||
|
||||
window.resizeTo(width, height + offset)
|
||||
})
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
context = window
|
||||
|
||||
mixins = []
|
||||
|
||||
# this check ensures we attempt to listen if this component is created in a popup
|
||||
if window.opener
|
||||
mixins.push(Reflux.listenTo(window.opener.RecordingStore,"onRecordingStateChanged"))
|
||||
|
||||
@PopupRecordingStartStop = React.createClass({
|
||||
|
||||
mixins: mixins
|
||||
|
||||
onRecordingStateChanged: (recordingState) ->
|
||||
this.setState(isRecording: recordingState.isRecording, recordedOnce: this.state.recordedOnce || recordingState.isRecording)
|
||||
|
||||
startStopRecording: () ->
|
||||
if this.state.isRecording
|
||||
window.opener.RecordingActions.stopRecording()
|
||||
else
|
||||
window.opener.RecordingActions.startRecording()
|
||||
|
||||
onNoteShowHide: () ->
|
||||
this.setState(showNote: !this.state.showNote)
|
||||
|
||||
getInitialState: () ->
|
||||
{isRecording: window.ParentIsRecording, showNote: true, recordedOnce: false}
|
||||
|
||||
render: () ->
|
||||
|
||||
recordingVerb = if this.state.isRecording then 'Stop' else 'Start'
|
||||
|
||||
recordingBtnClasses = classNames({
|
||||
"currently-recording" : this.state.isRecording,
|
||||
"control" : true
|
||||
})
|
||||
|
||||
noteJSX = `<div className="important-note">
|
||||
<h5>
|
||||
Important Note
|
||||
</h5>
|
||||
<div className="contents">
|
||||
While playing in your session, you are listening to your own personal mix. This recording will use the master mix,
|
||||
which may sound very different. To hear and adjust your master mix settings, click the MIXER button in the session toolbar.
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
recordingJSX = `<div className="recording-options">
|
||||
<div className="field">
|
||||
<input type="radio" name="recording-input-type" id="recording-input-both" defaultChecked="checked" />
|
||||
<label htmlFor="recording-input-both">Record both video and audio</label>
|
||||
<div className="clearall"></div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<input type="radio" name="recording-input-type" id="recording-input-audio" />
|
||||
<label htmlFor="recording-input-audio">Record audio only</label>
|
||||
<div className="clearall"></div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
if this.state.showNote
|
||||
noteText = 'hide note'
|
||||
else
|
||||
noteText = 'show note'
|
||||
|
||||
noteShowHideJSX = `<a href="#" className="note-show-hide" onClick={this.onNoteShowHide}>{noteText}</a>`
|
||||
|
||||
note = null
|
||||
recordingOptions = null
|
||||
noteShowHide = null
|
||||
|
||||
if this.state.showNote && !this.state.isRecording && !this.state.recordedOnce
|
||||
# should we show the note itself? Only if not recording, too
|
||||
note = noteJSX
|
||||
|
||||
if !this.state.isRecording && !this.state.recordedOnce
|
||||
noteShowHide = noteShowHideJSX
|
||||
|
||||
if gon.global.video_available == "full"
|
||||
recordingOptions = recordingJSX
|
||||
|
||||
|
||||
`<div className="recording-start-stop">
|
||||
<div className="control-holder">
|
||||
<a className={recordingBtnClasses} onClick={this.startStopRecording}>
|
||||
<span className="helper" />
|
||||
<img src="/assets/content/recordbutton-off.png" width="20" height="20" />
|
||||
<span id="recording-status">{recordingVerb} Recording</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{recordingOptions}
|
||||
|
||||
{note}
|
||||
|
||||
{noteShowHide}
|
||||
|
||||
</div>`
|
||||
|
||||
windowUnloaded: () ->
|
||||
window.opener.RecordingActions.recordingControlsClosed()
|
||||
|
||||
componentDidMount: () ->
|
||||
$(window).unload(@windowUnloaded)
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
$recordingType = $root.find('input[type="radio"]')
|
||||
context.JK.checkbox($recordingType)
|
||||
|
||||
@resizeWindow()
|
||||
|
||||
# this is necessary due to whatever the client's rendering behavior is.
|
||||
setTimeout(@resizeWindow, 300)
|
||||
|
||||
componentDidUpdate: () ->
|
||||
@resizeWindow()
|
||||
|
||||
resizeWindow: () =>
|
||||
$container = $('#minimal-container')
|
||||
width = $container.width()
|
||||
height = $container.height()
|
||||
|
||||
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
|
||||
mysteryTopMargin = 20
|
||||
|
||||
# deal with chrome in real browsers
|
||||
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
|
||||
|
||||
# handle native client chrome that the above outer-inner doesn't catch
|
||||
#if navigator.userAgent.indexOf('JamKazam') > -1
|
||||
|
||||
#offset += 25
|
||||
|
||||
window.resizeTo(width, height + offset)
|
||||
})
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
@PopupWrapper = React.createClass({
|
||||
|
||||
getInitialState: () ->
|
||||
{ready: false}
|
||||
|
||||
render: () ->
|
||||
logger.debug("PopupProps", window.PopupProps)
|
||||
return React.createElement(window[this.props.component], window.PopupProps)
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionBackingTrack = React.createClass({
|
||||
|
||||
mixins: [@MasterPersonalMixersMixin]
|
||||
|
||||
propTypes: {
|
||||
mode: React.PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
mixer = @mixer()
|
||||
|
||||
unless mixer?
|
||||
logger.debug("ignoring mute because no media mixer")
|
||||
return
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
mixers = @mixers()
|
||||
muteMixer = mixers.mixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
componentClasses = classNames({
|
||||
"session-track" : true
|
||||
"backing-track" : true
|
||||
})
|
||||
|
||||
pan = if mixers.mixer? then mixers.mixer?.pan else 0
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
`<div className={componentClasses}>
|
||||
<div className="session-track-contents">
|
||||
<div className="name">{this.props.shortFilename}</div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:@mixers()}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@mixers()}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
context = window
|
||||
|
||||
@SessionChatMixer= React.createClass({
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
unless @props.mixers.mixer
|
||||
logger.debug("ignoring mute; no mixer")
|
||||
return
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([@props.mixers.mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
muteMixer = @props.mixers.muteMixer
|
||||
vuMixer = @props.mixers.vuMixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
pan = if @props.mixers.mixer? then @props.mixers.mixer.pan else 0
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
`<div className="session-track chat-mixer">
|
||||
<div className="disabled-track-overlay" />
|
||||
<div className="session-track-contents">
|
||||
<div className="name">Session Voice Chat Output</div>
|
||||
<div className="track-instrument"><img height="45" src="/assets/content/icon_instrument_voice45.png" width="45" /></div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={this.props.mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
context = window
|
||||
|
||||
@SessionInviteMusiciansBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
|
||||
@inviteMusiciansUtil = new JK.InviteMusiciansUtil(@app)
|
||||
@inviteMusiciansUtil.initialize(JK.FriendSelectorDialogInstance)
|
||||
|
||||
openInviteDialog : (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
friendInput = @inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', context.SessionStore.currentSessionId)
|
||||
@inviteMusiciansUtil.loadFriends()
|
||||
$(friendInput).show()
|
||||
@app.layout.showDialog('select-invites')
|
||||
|
||||
render: () ->
|
||||
`<a className="session-invite-musicians" onClick={this.openInviteDialog}>
|
||||
<img src="/assets/content/icon_add.png" width="19" height="19" />
|
||||
Invite Musicians
|
||||
</a>`
|
||||
})
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionJamTrack = React.createClass({
|
||||
|
||||
mixins: [@MasterPersonalMixersMixin]
|
||||
|
||||
propTypes: {
|
||||
mode: React.PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
mixer = @mixer()
|
||||
|
||||
unless mixer?
|
||||
logger.debug("ignoring mute because no media mixer")
|
||||
return
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
mixers = @mixers()
|
||||
muteMixer = mixers.mixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
componentClasses = classNames({
|
||||
"session-track" : true
|
||||
"jam-track" : true
|
||||
})
|
||||
|
||||
pan = if mixers.mixer? then mixers.mixer?.pan else 0
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
`<div className={componentClasses}>
|
||||
<div className="session-track-contents">
|
||||
<div className="name">{this.props.part}</div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
</div>
|
||||
<div className="track-instrument"><img height="24" src={this.props.instrumentIcon} width="24" /></div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:@mixers()}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@mixers()}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionJamTrackCategory = React.createClass({
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([this.props.mixers.mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
# today, all mixers are the same for a remote participant; so just grab the 1st
|
||||
mixers = @props.mixers
|
||||
|
||||
muteMixer = mixers.muteMixer
|
||||
vuMixer = mixers.vuMixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
componentClasses = classNames({
|
||||
"session-track" : true
|
||||
"jam-track-category" : true
|
||||
})
|
||||
|
||||
pan = mixers.mixer.pan
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
`<div className={componentClasses}>
|
||||
<div className="session-track-contents">
|
||||
<div className="jam-track-header">JamTrack:</div>
|
||||
<div className="name">{this.props.jamTrackName}</div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
context = window
|
||||
|
||||
@SessionLeaveBtn = React.createClass({
|
||||
|
||||
onLeave: (e) ->
|
||||
e.preventDefault()
|
||||
@rateSession()
|
||||
|
||||
SessionActions.leaveSession.trigger({location: '/client#/home'})
|
||||
|
||||
rateSession: () ->
|
||||
unless @rateSessionDialog?
|
||||
@rateSessionDialog = new context.JK.RateSessionDialog(context.JK.app);
|
||||
@rateSessionDialog.initialize();
|
||||
|
||||
@rateSessionDialog.showDialog();
|
||||
|
||||
render: () ->
|
||||
`<a className="session-leave button-grey right leave" onClick={this.onLeave}>
|
||||
X LEAVE
|
||||
</a>`
|
||||
})
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
context = window
|
||||
rest = context.JK.Rest()
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
@SessionMasterCategoryControls = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
mixers = sessionMixers.mixers
|
||||
inputGroupMixers = mixers.getAudioInputCategoryMixer(MIX_MODES.MASTER)
|
||||
chatGroupMixers = mixers.getChatCategoryMixer(MIX_MODES.MASTER)
|
||||
|
||||
@setState({inputGroupMixers: inputGroupMixers, chatGroupMixers: chatGroupMixers})
|
||||
|
||||
render: () ->
|
||||
|
||||
categoryControls = []
|
||||
|
||||
if @state.inputGroupMixers?
|
||||
input =
|
||||
mixers: @state.inputGroupMixers
|
||||
|
||||
categoryControls.push(`<SessionMusicMixer key={input.mixers.mixer.id} {...input} />`)
|
||||
|
||||
if @state.chatGroupMixers?
|
||||
input =
|
||||
mixers: @state.chatGroupMixers
|
||||
|
||||
categoryControls.push(`<SessionChatMixer key={input.mixers.mixer.id} {...input} />`)
|
||||
|
||||
|
||||
`<div className="session-category-controls">
|
||||
<h2>master output</h2>
|
||||
<div className="session-tracks-scroller">
|
||||
{categoryControls}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
|
||||
getInitialState:() ->
|
||||
{inputGroupMixers: null, chatGroupMixers: null}
|
||||
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
context = window
|
||||
rest = context.JK.Rest()
|
||||
SessionActions = @SessionActions
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
EVENTS = context.JK.EVENTS
|
||||
ChannelGroupIds = context.JK.ChannelGroupIds
|
||||
|
||||
@SessionMasterMediaTracks = React.createClass({
|
||||
|
||||
mixins: [@SessionMediaTracksMixin, Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
render: () ->
|
||||
|
||||
mediaTracks = []
|
||||
|
||||
if this.state.mediaSummary.mediaOpen
|
||||
|
||||
if this.state.mediaSummary.backingTrackOpen
|
||||
for backingTrack in @state.backingTracks
|
||||
backingTrack.mode = MIX_MODES.MASTER
|
||||
mediaTracks.push(`<SessionBackingTrack key={backingTrack.track.id} {...backingTrack} />`)
|
||||
else if this.state.mediaSummary.jamTrackOpen
|
||||
mediaTracks.push(`<SessionJamTrackCategory key="JamTrackCategory" jamTrackName={this.state.jamTrackName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.MASTER} />`)
|
||||
for jamTrack in @state.jamTracks
|
||||
jamTrack.mode = MIX_MODES.MASTER
|
||||
mediaTracks.push(`<SessionJamTrack key={jamTrack.id} {...jamTrack} />`)
|
||||
else if this.state.mediaSummary.recordingOpen
|
||||
mediaTracks.push(`<SessionRecordedCategory key="RecordedCategory" recordingName={this.state.recordingName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.MASTER} />`)
|
||||
for recordedTrack in @state.recordedTracks
|
||||
recordedTrack.mode = MIX_MODES.MASTER
|
||||
mediaTracks.push(`<SessionRecordedTrack key={recordedTrack.track.id} {...recordedTrack} />`)
|
||||
else if this.state.mediaSummary.metronomeOpen
|
||||
@state.metronome.mode = MIX_MODES.MASTER
|
||||
mediaTracks.push(`<SessionMetronome key={this.state.metronome.id} {...this.state.metronome} />`)
|
||||
|
||||
`<div className="session-media-tracks">
|
||||
<h2>recorded audio</h2>
|
||||
<div className="session-tracks-scroller">
|
||||
{mediaTracks}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
|
||||
getInitialState:() ->
|
||||
{mediaSummary:{mediaOpen: false}, isRecording: false, backingTracks: [], jamTracks: [], recordedTracks: [], metronome: null}
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
@SessionMasterMix = React.createClass({
|
||||
|
||||
render: () ->
|
||||
`<div id="master-tracks">
|
||||
<SessionMasterMyTracks mode={MIX_MODES.MASTER} />
|
||||
<SessionMasterOtherTracks mode={MIX_MODES.MASTER} />
|
||||
<SessionMasterMediaTracks mode={MIX_MODES.MASTER} />
|
||||
<SessionMasterCategoryControls mode={MIX_MODES.MASTER} />
|
||||
</div>`
|
||||
})
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
context = window
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
logger = context.JK.logger
|
||||
|
||||
@SessionMasterMyTracks = React.createClass({
|
||||
|
||||
mixins: [@SessionMyTracksMixin, Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
render: () ->
|
||||
|
||||
content = null
|
||||
tracks = []
|
||||
|
||||
if this.state.tracks.length > 0
|
||||
for track in this.state.tracks
|
||||
track.mode = MIX_MODES.MASTER
|
||||
tracks.push(`<SessionMyTrack key={track.track.client_track_id} {...track} />`)
|
||||
|
||||
if @state.chat
|
||||
@state.chat.mode = @props.mode
|
||||
tracks.push(`<SessionMyChat key="chat" {...this.state.chat} />`)
|
||||
|
||||
else if this.state.session? && this.state.session.inSession()
|
||||
logger.debug("no 'my inputs' for master mix")
|
||||
|
||||
`<div className="session-my-tracks">
|
||||
<h2>my live tracks</h2>
|
||||
<div className="session-tracks-scroller">
|
||||
{content}
|
||||
{tracks}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
getInitialState:() ->
|
||||
{tracks:[], session: null}
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionMasterOtherTrack = React.createClass({
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
unless @props.mixers.mixer?
|
||||
logger.debug("ignoring mute; no mixer")
|
||||
return
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([@props.mixers.mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
muteMixer = @props.mixers.muteMixer
|
||||
vuMixer = @props.mixers.vuMixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
pan = if @props.mixers.mixer? then @props.mixers.mixer.pan else 0
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
# <div className="track-icon-equalizer" />
|
||||
|
||||
`<div className="session-track my-track">
|
||||
<div className="disabled-track-overlay" />
|
||||
<div className="session-track-contents">
|
||||
<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="best" mixers={this.props.mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:this.props.mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:this.props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
# disable hover effects if there is no mixer
|
||||
if nextProps.mixers.mixer?
|
||||
$mute.off("click", false)
|
||||
$pan.off("click", false)
|
||||
else
|
||||
$mute.on("click", false)
|
||||
$pan.on("click", false)
|
||||
})
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
context = window
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
|
||||
@SessionMasterOtherTracks = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@SessionOtherTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
noAudioUsers = mixers.noAudioUsers
|
||||
|
||||
tracks = []
|
||||
|
||||
if session.inSession()
|
||||
|
||||
for participant in session.otherParticipants()
|
||||
|
||||
name = participant.user.name;
|
||||
|
||||
firstTrack = participant.tracks[0]
|
||||
|
||||
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url)
|
||||
|
||||
for track in participant.tracks
|
||||
|
||||
mixerData = mixers.findMixerForTrack(participant.client_id, track, false, @props.mode)
|
||||
|
||||
instrumentIcon = context.JK.getInstrumentIcon45(firstTrack.instrument_id)
|
||||
|
||||
trackState = {
|
||||
participant: participant,
|
||||
track: track,
|
||||
mixers: mixerData,
|
||||
name: name,
|
||||
instrumentIcon: instrumentIcon,
|
||||
photoUrl: photoUrl,
|
||||
hasMixer: mixerData.mixer? ,
|
||||
noAudio: noAudioUsers[participant.client_id]
|
||||
}
|
||||
|
||||
tracks.push(trackState)
|
||||
# todo: sessionModel.setAudioEstablished
|
||||
|
||||
this.setState(tracks: tracks, session: session)
|
||||
|
||||
render: () ->
|
||||
|
||||
tracks = []
|
||||
|
||||
for track in @state.tracks
|
||||
tracks.push(`<SessionMasterOtherTrack key={track.track.client_track_id} {...track} />`)
|
||||
|
||||
`<div className="session-other-tracks">
|
||||
<h2>other live tracks</h2>
|
||||
<div className="session-tracks-scroller">
|
||||
{tracks}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
getInitialState:() ->
|
||||
{tracks:[], session: null}
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -0,0 +1,309 @@
|
|||
context = window
|
||||
rest = context.JK.Rest()
|
||||
SessionActions = @SessionActions
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
EVENTS = context.JK.EVENTS
|
||||
ChannelGroupIds = context.JK.ChannelGroupIds
|
||||
|
||||
@SessionMediaTracks = React.createClass({
|
||||
|
||||
mixins: [@SessionMediaTracksMixin, Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
inputsChangedProcessed: (state) ->
|
||||
|
||||
if state.mediaSummary.mediaOpen
|
||||
if !@state.childWindow?
|
||||
logger.debug("OPENING CHILD WINDOW")
|
||||
childWindow = window.open("/popups/media-controls", 'Media Controls', 'scrollbars=yes,toolbar=no,status=no,height=155,width=350')
|
||||
childWindow.PopupProps = state
|
||||
state.childWindow = childWindow
|
||||
else
|
||||
if !state.metronomeFlickerTimeout? # if the metronomeFlickerTimeout is active, we don't consider closing the childWindow
|
||||
@checkCloseWindow()
|
||||
state.childWindow = null
|
||||
|
||||
checkCloseWindow: () ->
|
||||
if @state.childWindow?
|
||||
logger.debug("CLOSING CHILD WINDOW")
|
||||
@state.childWindow.DontAutoCloseMedia = true
|
||||
@state.childWindow.close()
|
||||
|
||||
|
||||
closeAudio: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
SessionActions.closeMedia()
|
||||
|
||||
cancelDownloadJamTrack: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
logger.debug("closing DownloadJamTrack widget")
|
||||
@state.downloadJamTrack.root.remove()
|
||||
@state.downloadJamTrack.destroy()
|
||||
|
||||
SessionActions.downloadingJamTrack(false)
|
||||
|
||||
@setState({downloadJamTrack: null})
|
||||
|
||||
openRecording: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
# just ignore the click if they are currently recording for now
|
||||
if @state.isRecording
|
||||
@app.notify({
|
||||
"title": "Currently Recording",
|
||||
"text": "You can't open a recording while creating a recording.",
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
})
|
||||
return
|
||||
|
||||
@app.layout.showDialog('localRecordings') unless @app.layout.isDialogShowing('localRecordings')
|
||||
|
||||
openBackingTrack: (e) ->
|
||||
e.preventDefault()
|
||||
if @state.backingTrackDialogOpen
|
||||
logger.debug("backing track dialog already open")
|
||||
return
|
||||
|
||||
|
||||
# just ignore the click if they are currently recording for now
|
||||
if @state.isRecording
|
||||
@app.notify({
|
||||
"title": "Currently Recording",
|
||||
"text": "You can't open a backing track while creating a recording.",
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
});
|
||||
return
|
||||
|
||||
@setState({backingTrackDialogOpen: true})
|
||||
context.jamClient.ShowSelectBackingTrackDialog("window.JK.HandleBackingTrackSelectedCallback2");
|
||||
|
||||
openMetronome: (e) ->
|
||||
|
||||
if @state.isRecording
|
||||
@app.notify({
|
||||
"title": "Currently Recording",
|
||||
"text": "You can't open a metronome while creating a recording.",
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
})
|
||||
return
|
||||
|
||||
SessionActions.openMetronome()
|
||||
|
||||
openJamTrack: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
if @state.isRecording
|
||||
@app.notify({
|
||||
"title": "Currently Recording",
|
||||
"text": "You can't open a jam track while creating a recording.",
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
})
|
||||
return
|
||||
|
||||
@app.layout.showDialog('open-jam-track-dialog').one(EVENTS.DIALOG_CLOSED, (e, data) =>
|
||||
# once the dialog is closed, see if the user has a jamtrack selected
|
||||
if !data.canceled && data.result.jamTrack
|
||||
@loadJamTrack(data.result.jamTrack)
|
||||
|
||||
else
|
||||
logger.debug("OpenJamTrack dialog closed with no selection; ignoring", data)
|
||||
)
|
||||
|
||||
loadJamTrack: (jamTrack) ->
|
||||
if @state.downloadJamTrack
|
||||
# if there was one showing before somehow, destroy it.
|
||||
logger.warn("destroying existing JamTrack")
|
||||
@state.downloadJamTrack.root.remove()
|
||||
@state.downloadJamTrack.destroy()
|
||||
#set to null
|
||||
|
||||
|
||||
downloadJamTrack = new context.JK.DownloadJamTrack(@app, jamTrack, 'large');
|
||||
|
||||
# the widget indicates when it gets to any transition; we can hide it once it reaches completion
|
||||
$(downloadJamTrack).on(EVENTS.JAMTRACK_DOWNLOADER_STATE_CHANGED, (e, data) =>
|
||||
if data.state == downloadJamTrack.states.synchronized
|
||||
logger.debug("jamtrack synchronized; hide widget and show tracks")
|
||||
downloadJamTrack.root.remove()
|
||||
downloadJamTrack.destroy()
|
||||
downloadJamTrack = null
|
||||
|
||||
this.setState({downloadJamTrack: null})
|
||||
|
||||
# XXX: test with this removed; it should be unnecessary
|
||||
context.jamClient.JamTrackStopPlay();
|
||||
|
||||
sampleRate = context.jamClient.GetSampleRate()
|
||||
sampleRateForFilename = if sampleRate == 48 then '48' else '44'
|
||||
fqId = jamTrack.id + '-' + sampleRateForFilename
|
||||
|
||||
if jamTrack.jmep
|
||||
logger.debug("setting jmep data")
|
||||
|
||||
context.jamClient.JamTrackLoadJmep(fqId, jamTrack.jmep)
|
||||
else
|
||||
logger.debug("no jmep data for jamtrack")
|
||||
|
||||
# JamTrackPlay means 'load'
|
||||
result = context.jamClient.JamTrackPlay(fqId);
|
||||
|
||||
SessionActions.downloadingJamTrack(false)
|
||||
|
||||
console.log("JamTrackPlay: result", )
|
||||
if !result
|
||||
@app.notify(
|
||||
{
|
||||
title: "JamTrack Can Not Open",
|
||||
text: "Unable to open your JamTrack. Please contact support@jamkazam.com"
|
||||
}
|
||||
, null, true)
|
||||
else
|
||||
participantCnt = context.SessionStore.participants().length
|
||||
rest.playJamTrack(jamTrack.id)
|
||||
.done(() =>
|
||||
@app.refreshUser();
|
||||
)
|
||||
|
||||
context.stats.write('web.jamtrack.open', {
|
||||
value: 1,
|
||||
session_size: participantCnt,
|
||||
user_id: context.JK.currentUserId,
|
||||
user_name: context.JK.currentUserName
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@setState({downloadJamTrack: downloadJamTrack})
|
||||
|
||||
render: () ->
|
||||
|
||||
scrollerClassData = {'session-tracks-scroller': true}
|
||||
mediaOptions = `<div className="media-options">
|
||||
<div className="open-media-file-header">
|
||||
<div className="vertical-helper" />
|
||||
<img src="/assets/content/icon_folder.png" width="22" height="20" />
|
||||
<span className="open-text">Open:</span>
|
||||
</div>
|
||||
<ul className="open-media-file-options">
|
||||
<li>
|
||||
<a className="open-recording" onClick={this.openRecording}>Recording</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="open-jamtrack" onClick={this.openJamTrack}>JamTrack</a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="open-backingtrack" onClick={this.openBackingTrack}>Audio File</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="use-metronome-header">
|
||||
<img src="/assets/content/icon_metronome.png" width="22" height="20" />
|
||||
<a className="open-metronome" onClick={this.openMetronome}>Use Metronome</a>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
contents = null
|
||||
mediaTracks = []
|
||||
|
||||
if this.state.downloadJamTrack?
|
||||
closeOptions =
|
||||
`<div>
|
||||
<a className="closeAudio" onClick={this.cancelDownloadJamTrack}>
|
||||
<img src="/assets/content/icon_close.png" width="18" height="20" />
|
||||
Close JamTrack
|
||||
</a>
|
||||
<div className="download-jamtrack-holder"></div>
|
||||
</div>`
|
||||
|
||||
contents = closeOptions
|
||||
|
||||
else if this.state.mediaSummary.mediaOpen
|
||||
|
||||
# give the users options to close it
|
||||
if this.state.mediaSummary.jamTrackOpen
|
||||
mediaType = "JamTrack"
|
||||
else if this.state.mediaSummary.backingTrackOpen
|
||||
mediaType = "Audio File"
|
||||
else if this.state.mediaSummary.metronomeOpen
|
||||
mediaType = "Metronome"
|
||||
else if this.state.mediaSummary.recordingOpen
|
||||
mediaType = "Recording"
|
||||
else
|
||||
mediaType = ""
|
||||
|
||||
closeOptions = `<a className="closeAudio" onClick={this.closeAudio}>
|
||||
<img src="/assets/content/icon_close.png" width="18" height="20" />
|
||||
Close {mediaType}
|
||||
</a>`
|
||||
|
||||
|
||||
if this.state.mediaSummary.backingTrackOpen
|
||||
for backingTrack in @state.backingTracks
|
||||
backingTrack.mode = MIX_MODES.PERSONAL
|
||||
mediaTracks.push(`<SessionBackingTrack key={backingTrack.track.id} {...backingTrack} />`)
|
||||
else if this.state.mediaSummary.jamTrackOpen
|
||||
mediaTracks.push(`<SessionJamTrackCategory key="JamTrackCategory" jamTrackName={this.state.jamTrackName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.PERSONAL} />`)
|
||||
for jamTrack in @state.jamTracks
|
||||
jamTrack.mode = MIX_MODES.PERSONAL
|
||||
mediaTracks.push(`<SessionJamTrack key={jamTrack.id} {...jamTrack} />`)
|
||||
else if this.state.mediaSummary.recordingOpen
|
||||
mediaTracks.push(`<SessionRecordedCategory key="RecordedCategory" recordingName={this.state.recordingName} mixers={this.state.mediaCategoryMixer} mode={MIX_MODES.PERSONAL} />`)
|
||||
for recordedTrack in @state.recordedTracks
|
||||
recordedTrack.mode = MIX_MODES.PERSONAL
|
||||
mediaTracks.push(`<SessionRecordedTrack key={recordedTrack.track.id} {...recordedTrack} />`)
|
||||
else if this.state.mediaSummary.metronomeOpen
|
||||
@state.metronome.mode = MIX_MODES.PERSONAL
|
||||
mediaTracks.push(`<SessionMetronome key={this.state.metronome.id} {...this.state.metronome} />`)
|
||||
|
||||
contents = closeOptions
|
||||
else
|
||||
|
||||
scrollerClassData['media-options-showing'] = true
|
||||
contents = mediaOptions
|
||||
|
||||
scrollerClasses = classNames(scrollerClassData)
|
||||
|
||||
`<div className="session-media-tracks">
|
||||
<h2>recorded audio</h2>
|
||||
{contents}
|
||||
<div className={scrollerClasses}>
|
||||
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
|
||||
{mediaTracks}
|
||||
</ReactCSSTransitionGroup>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
|
||||
getInitialState:() ->
|
||||
{mediaSummary:{mediaOpen: false}, isRecording: false, backingTracks: [], jamTracks: [], recordedTracks: [], metronome: null}
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
|
||||
handleBackingTrackSelectedCallback: (result) ->
|
||||
|
||||
@setState({backingTrackDialogOpen: false})
|
||||
|
||||
SessionActions.openBackingTrack(result)
|
||||
|
||||
componentDidMount: () ->
|
||||
context.JK.HandleBackingTrackSelectedCallback2 = @handleBackingTrackSelectedCallback
|
||||
|
||||
componentDidUpdate: () ->
|
||||
|
||||
if @state.downloadJamTrack?
|
||||
$holder = $(@getDOMNode()).find('.download-jamtrack-holder')
|
||||
|
||||
if $holder.find('.download-jamtrack').length == 0
|
||||
|
||||
SessionActions.downloadingJamTrack(true)
|
||||
$holder.append(@state.downloadJamTrack.root)
|
||||
|
||||
# kick off the download JamTrack process
|
||||
@state.downloadJamTrack.init()
|
||||
|
||||
@checkCloseWindow() if !@state.mediaSummary.mediaOpen && !@state.metronomeFlickerTimeout?
|
||||
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionMetronome = React.createClass({
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([this.props.mixers.mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
# today, all mixers are the same for a remote participant; so just grab the 1st
|
||||
mixers = @props.mixers
|
||||
|
||||
muteMixer = mixers.muteMixer
|
||||
vuMixer = mixers.vuMixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
componentClasses = classNames({
|
||||
"session-track" : true
|
||||
"metronome" : true
|
||||
})
|
||||
|
||||
pan = if mixers.mixer? then mixers.mixer?.pan else 0
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
`<div className={componentClasses}>
|
||||
<div className="session-track-contents">
|
||||
<div className="name">Metronome</div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className="track-instrument"><img height="24" src={this.props.instrumentIcon} width="24" /></div>
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
context = window
|
||||
|
||||
@SessionMixerBtn = React.createClass({
|
||||
|
||||
openDialog: (e) ->
|
||||
e.preventDefault()
|
||||
context.JK.app.layout.showDialog('session-master-mix-dialog')
|
||||
|
||||
render: () ->
|
||||
`<a className="session-mixer button-grey left" onClick={this.openDialog}>
|
||||
<img src="/assets/content/icon_settings_sm.png" align="texttop" height="12" width="12"/>
|
||||
MIXER
|
||||
</a>`
|
||||
})
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
context = window
|
||||
|
||||
@SessionMusicMixer= React.createClass({
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
unless @props.mixers.mixer
|
||||
logger.debug("ignoring mute; no mixer")
|
||||
return
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([@props.mixers.mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
muteMixer = @props.mixers.muteMixer
|
||||
vuMixer = @props.mixers.vuMixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
pan = if @props.mixers.mixer? then @props.mixers.mixer.pan else 0
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
`<div className="session-track music-mixer">
|
||||
<div className="disabled-track-overlay" />
|
||||
<div className="session-track-contents">
|
||||
<div className="name">Session Music Output</div>
|
||||
<div className="track-instrument"><img height="45" src="/assets/content/icon_instrument_voice45.png" width="45" /></div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={this.props.mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionMyChat = React.createClass({
|
||||
|
||||
mixins: [@MasterPersonalMixersMixin]
|
||||
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
mixers = @mixers()
|
||||
unless mixers.mixer
|
||||
logger.debug("ignoring mute; no mixer")
|
||||
return
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([mixers.mixer, mixers.oppositeMixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
mixers = @mixers()
|
||||
muteMixer = mixers.muteMixer
|
||||
vuMixer = mixers.vuMixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
|
||||
# <div className="track-icon-equalizer" />
|
||||
|
||||
`<div className="session-track my-track">
|
||||
<div className="disabled-track-overlay" />
|
||||
<div className="session-track-contents">
|
||||
<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='/assets/content/icon_instrument_chat45.png' width="45" /></div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:@mixers()}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
})
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionMyTrack = React.createClass({
|
||||
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
unless this.props.mixers.mixer
|
||||
logger.debug("ignoring mute; no mixer")
|
||||
return
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([this.props.mixers.mixer, this.props.mixers.oppositeMixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
muteMixer = this.props.mixers.muteMixer
|
||||
vuMixer = this.props.mixers.vuMixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
pan = if this.props.mixers.mixer? then this.props.mixers.mixer.pan else 0
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
# <div className="track-icon-equalizer" />
|
||||
|
||||
`<div className="session-track my-track">
|
||||
<div className="disabled-track-overlay" />
|
||||
<div className="session-track-contents">
|
||||
<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="best" mixers={this.props.mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
context.jamClient.SessionSetUserName(this.props.clientId, this.props.name)
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:this.props.mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:this.props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
# disable hover effects if there is no mixer
|
||||
if nextProps.mixers.mixer?
|
||||
$mute.off("click", false)
|
||||
$pan.off("click", false)
|
||||
else
|
||||
$mute.on("click", false)
|
||||
$pan.on("click", false)
|
||||
})
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
||||
|
||||
@SessionMyTracks = React.createClass({
|
||||
|
||||
mixins: [@SessionMyTracksMixin, Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
render: () ->
|
||||
|
||||
content = null
|
||||
tracks = []
|
||||
|
||||
if @state.tracks.length > 0
|
||||
for track in @state.tracks
|
||||
track.mode = MIX_MODES.PERSONAL
|
||||
tracks.push(`<SessionMyTrack key={track.track.client_track_id} {...track} />`)
|
||||
|
||||
if @state.chat
|
||||
@state.chat.mode = @props.mode
|
||||
tracks.push(`<SessionMyChat key="chat" {...this.state.chat} />`)
|
||||
|
||||
else if @state.session? && @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, chat:null}
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
context = window
|
||||
|
||||
@SessionNotification = React.createClass({
|
||||
|
||||
render: () ->
|
||||
|
||||
classes = classNames({
|
||||
'session-notification' : true
|
||||
'has-details' : @props.detail?
|
||||
})
|
||||
|
||||
help = `<a rel="external" href={this.props.help} className="notify-help">?</a>` if @props.help?
|
||||
|
||||
title = `<div className="msg">{this.props.title}{help}</div>`
|
||||
extra = `<div className="detail">{this.props.extra}</div>` if @props.extra?
|
||||
|
||||
`<div className={classes}>
|
||||
{title}
|
||||
{extra}
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
context.JK.popExternalLinks($root)
|
||||
|
||||
if @props.detail?
|
||||
context.JK.hoverBubble($root, @props.detail, {offsetParent:$root.closest('.top-parent'), positions: ['left', 'bottom']})
|
||||
})
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
context = window
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
NotificationActions = @NotificationActions
|
||||
|
||||
@SessionNotifications = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@SessionNotificationStore,"onNotificationsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
onNotificationsChanged: (notifications) ->
|
||||
@setState({notifications: notifications})
|
||||
|
||||
getInitialState: () ->
|
||||
{notifications: []}
|
||||
|
||||
clearNotifications: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
NotificationActions.clear()
|
||||
|
||||
render: () ->
|
||||
|
||||
notifications = []
|
||||
for notification in @state.notifications
|
||||
notifications.push(`<SessionNotification key={notification.id} {...notification} />`)
|
||||
|
||||
`<div className="session-notifications">
|
||||
<h2>notifications</h2>
|
||||
<a className="session-clear-notifications" onClick={this.clearNotifications}>
|
||||
<img src="/assets/content/icon_close.png" width="18" height="20" />
|
||||
Clear Notifications
|
||||
</a>
|
||||
<div className="session-tracks-scroller">
|
||||
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
|
||||
{notifications}
|
||||
</ReactCSSTransitionGroup>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionOtherTrack = React.createClass({
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
unless this.props.hasMixer
|
||||
logger.debug("ignoring mute; no mixer")
|
||||
return
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
mixers = if this.props.tracks.length > 0 then this.props.tracks[0].mixers else {}
|
||||
|
||||
MixerActions.mute([mixers.mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
# today, all mixers are the same for a remote participant; so just grab the 1st
|
||||
mixers = if this.props.tracks.length > 0 then this.props.tracks[0].mixers else {}
|
||||
|
||||
muteMixer = mixers.muteMixer
|
||||
vuMixer = mixers.vuMixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
componentClasses = classNames({
|
||||
"session-track" : true
|
||||
"my-track" : true
|
||||
"has-mixer" : this.props.hasMixer
|
||||
"no-mixer" : !this.props.hasMixer
|
||||
"has-audio" : this.props.noAudio != true
|
||||
"no-audio" : this.props.noAudio == true
|
||||
})
|
||||
|
||||
pan = if mixers.mixer? then mixers.mixer?.pan else 0
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
`<div className={componentClasses}>
|
||||
<div className="disabled-track-overlay" />
|
||||
<div className="session-track-contents">
|
||||
<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="best" mixers={mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
if this.props.participant.client_id?
|
||||
context.jamClient.SessionSetUserName(this.props.participant.client_id, this.props.name)
|
||||
else
|
||||
logger.error("no participant client ID")
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
mixers = if this.props.tracks.length > 0 then this.props.tracks[0].mixers else {}
|
||||
{mixers:mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
mixers = if this.props.tracks.length > 0 then this.props.tracks[0].mixers else {}
|
||||
{mixers:mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
unless this.props.hasMixer
|
||||
$mute.on("mouseenter", false)
|
||||
$mute.on("mouseleave", false)
|
||||
$pan.on("mouseentere", false)
|
||||
$pan.on("mouseleave", false)
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
# disable hover effects if there is no mixer
|
||||
if nextProps.hasMixer
|
||||
$mute.off("mouseenter", false)
|
||||
$mute.off("mouseleave", false)
|
||||
$pan.off("mouseenter", false)
|
||||
$pan.off("mouseleave", false)
|
||||
else
|
||||
$mute.on("mouseenter", false)
|
||||
$mute.on("mouseleave", false)
|
||||
$pan.on("mouseentere", false)
|
||||
$pan.on("mouseleave", false)
|
||||
})
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
context = window
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
|
||||
@SessionOtherTracks = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@SessionOtherTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
noAudioUsers = mixers.noAudioUsers
|
||||
|
||||
participants = []
|
||||
|
||||
if session.inSession()
|
||||
|
||||
for participant in session.otherParticipants()
|
||||
|
||||
tracks = []
|
||||
name = participant.user.name;
|
||||
|
||||
firstTrack = participant.tracks[0]
|
||||
hasMixer = false
|
||||
|
||||
for track in participant.tracks
|
||||
# try to find mixer info for this track
|
||||
mixerFinder = [participant.client_id, track, false] # so that other callers can re-find their mixer data
|
||||
|
||||
mixerData = mixers.findMixerForTrack(participant.client_id, track, false, @props.mode)
|
||||
if mixerData.mixer?
|
||||
hasMixer = true
|
||||
|
||||
tracks.push(track: track, mixers: mixerData, mixerFinder: mixerFinder)
|
||||
# todo: sessionModel.setAudioEstablished
|
||||
|
||||
instrumentIcon = context.JK.getInstrumentIcon45(firstTrack.instrument_id)
|
||||
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url)
|
||||
|
||||
|
||||
participantState = {
|
||||
participant: participant,
|
||||
tracks: tracks,
|
||||
name: name,
|
||||
instrumentIcon: instrumentIcon,
|
||||
photoUrl: photoUrl,
|
||||
hasMixer: hasMixer,
|
||||
noAudio: noAudioUsers[participant.client_id]
|
||||
}
|
||||
|
||||
participants.push(participantState)
|
||||
|
||||
this.setState(participants: participants, session: session)
|
||||
|
||||
render: () ->
|
||||
|
||||
content = null
|
||||
participants = []
|
||||
|
||||
noOthers = `<div className="when-empty">No other musicians are in your session.</div>`
|
||||
|
||||
if this.state.participants.length > 0
|
||||
for participant in this.state.participants
|
||||
participants.push(`<SessionOtherTrack key={participant.client_id} {...participant} />`)
|
||||
else if this.state.session? && this.state.session.inSession()
|
||||
content = noOthers
|
||||
|
||||
`<div className="session-other-tracks">
|
||||
<h2>other live tracks</h2>
|
||||
<SessionInviteMusiciansBtn />
|
||||
<div className="session-tracks-scroller">
|
||||
{content}
|
||||
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
|
||||
{participants}
|
||||
</ReactCSSTransitionGroup>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
getInitialState:() ->
|
||||
{participants:[], session: null}
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
context = window
|
||||
RecordingActions = @RecordingActions
|
||||
|
||||
@SessionRecordBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@MixerStore,"onSessionMixerChange")]
|
||||
|
||||
onSessionMixerChange: (sessionMixers) ->
|
||||
|
||||
this.setState({isRecording: sessionMixers.session.isRecording})
|
||||
|
||||
getInitialState: () ->
|
||||
{childWindow: null, isRecording: false}
|
||||
|
||||
openRecording: () ->
|
||||
|
||||
RecordingActions.openRecordingControls()
|
||||
|
||||
render: () ->
|
||||
`<a className="session-record button-grey left" onClick={this.openRecording}>
|
||||
<img src="/assets/content/icon_settings_sm.png" align="texttop" height="12" width="12"/>
|
||||
RECORD
|
||||
</a>`
|
||||
})
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionRecordedCategory = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
mode: React.PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([this.props.mixers.mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
# today, all mixers are the same for a remote participant; so just grab the 1st
|
||||
mixers = @props.mixers
|
||||
|
||||
muteMixer = mixers.muteMixer
|
||||
vuMixer = mixers.vuMixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
componentClasses = classNames({
|
||||
"session-track" : true
|
||||
"recorded-category" : true
|
||||
})
|
||||
|
||||
pan = mixers.mixer.pan
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
`<div className={componentClasses}>
|
||||
<div className="session-track-contents">
|
||||
<div className="name">{this.props.recordingName}</div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionRecordedTrack = React.createClass({
|
||||
|
||||
mixins: [@MasterPersonalMixersMixin]
|
||||
|
||||
propTypes: {
|
||||
mode: React.PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
mixer = @mixer()
|
||||
|
||||
unless mixer?
|
||||
logger.debug("ignoring mute because no media mixer")
|
||||
return
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([mixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
mixers = @mixers()
|
||||
muteMixer = mixers.mixer
|
||||
muteMixerId = muteMixer?.id
|
||||
|
||||
classes = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !muteMixer?.mute
|
||||
'muted' : muteMixer?.mute
|
||||
})
|
||||
|
||||
componentClasses = classNames({
|
||||
"session-track" : true
|
||||
"recorded-track" : true
|
||||
})
|
||||
|
||||
pan = if mixers.mixer? then mixers.mixer?.pan else 0
|
||||
|
||||
panStyle = {
|
||||
transform: "rotate(#{pan}deg)"
|
||||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
`<div className={componentClasses}>
|
||||
<div className="session-track-contents">
|
||||
<div className="name">{this.props.userName}</div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
</div>
|
||||
<div className="track-instrument"><img height="24" src={this.props.instrumentIcon} width="24" /></div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
'SessionTrackVolumeHover',
|
||||
() =>
|
||||
{mixers:@mixers()}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@mixers()}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
context = window
|
||||
|
||||
@SessionResyncBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
resync: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
SessionActions.audioResync()
|
||||
|
||||
render: () ->
|
||||
`<a className="session-resync button-grey resync left" onClick={this.resync}>
|
||||
<img src="/assets/content/icon_resync.png" align="texttop" height="12" width="12"/>
|
||||
RESYNC
|
||||
</a>`
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
SessionActions = @SessionActions
|
||||
|
||||
@SessionScreen = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit"), Reflux.listenTo(@SessionActions.allowLeaveSession, "onAllowLeaveSession")]
|
||||
|
||||
render: () ->
|
||||
`<div className="session-container">
|
||||
<div className="in-session-controls">
|
||||
<SessionSettingsBtn />
|
||||
<SessionVolumeSettingsBtn />
|
||||
<SessionShareBtn />
|
||||
<SessionRecordBtn />
|
||||
<SessionVideoBtn />
|
||||
<SessionMixerBtn />
|
||||
<SessionResyncBtn />
|
||||
<SessionLeaveBtn />
|
||||
</div>
|
||||
<div className="tracks" id="new-tracks">
|
||||
<SessionMyTracks mode={MIX_MODES.PERSONAL} />
|
||||
<SessionOtherTracks mode={MIX_MODES.PERSONAL} />
|
||||
<SessionMediaTracks mode={MIX_MODES.PERSONAL} />
|
||||
<SessionNotifications />
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
@logger = context.JK.logger
|
||||
|
||||
beforeShow: (data) ->
|
||||
@logger.debug("session beforeShow")
|
||||
@allowLeave = false
|
||||
|
||||
afterShow: (data) ->
|
||||
@logger.debug("session afterShow")
|
||||
|
||||
SessionActions.joinSession.trigger(data.id)
|
||||
|
||||
beforeHide: () ->
|
||||
context.JK.HelpBubbleHelper.clearJamTrackGuide();
|
||||
|
||||
beforeLeave: (data) ->
|
||||
@logger.debug("session beforeLeave", @allowLeave)
|
||||
|
||||
if @allowLeave
|
||||
return true
|
||||
else
|
||||
leaveSessionWarningDialog = new context.JK.LeaveSessionWarningDialog(context.JK.app,
|
||||
() =>
|
||||
@allowLeave = true
|
||||
context.location.hash = data.hash
|
||||
)
|
||||
|
||||
leaveSessionWarningDialog.initialize()
|
||||
@app.layout.showDialog('leave-session-warning')
|
||||
return false
|
||||
|
||||
beforeDisconnect: () ->
|
||||
@logger.debug("session beforeDisconnect")
|
||||
|
||||
onAllowLeaveSession: () ->
|
||||
@allowLeave = true
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
screenBindings = {
|
||||
'beforeShow': @beforeShow,
|
||||
'afterShow': @afterShow,
|
||||
'beforeHide': @beforeHide,
|
||||
'beforeLeave' : @beforeLeave,
|
||||
'beforeDisconnect' : @beforeDisconnect,
|
||||
};
|
||||
|
||||
@app.bindScreen('session', screenBindings);
|
||||
})
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionSelfVolumeHover = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged")]
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
|
||||
mixers = sessionMixers.mixers
|
||||
inputGroupMixers = mixers.getAudioInputCategoryMixer(MIX_MODES.PERSONAL)
|
||||
chatGroupMixers = mixers.getChatCategoryMixer( MIX_MODES.PERSONAL)
|
||||
|
||||
@setState({inputGroupMixers: inputGroupMixers, chatGroupMixers: chatGroupMixers})
|
||||
|
||||
getInitialState: () ->
|
||||
{inputGroupMixers: @props.inputGroupMixers, chatGroupMixers: @props.chatGroupMixers}
|
||||
|
||||
handleAudioInputMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([@state.inputGroupMixers.muteMixer], muting)
|
||||
|
||||
handleChatInputMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([@state.chatGroupMixers.muteMixer], muting)
|
||||
|
||||
handleAudioInputMuteCheckbox: (e) ->
|
||||
muting = $(e.target).is(':checked')
|
||||
|
||||
MixerActions.mute([@state.inputGroupMixers.muteMixer], muting)
|
||||
|
||||
handleChatMuteCheckbox: (e) ->
|
||||
muting = $(e.target).is(':checked')
|
||||
|
||||
MixerActions.mute([@state.chatGroupMixers.muteMixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
monitorMuteMixer = @state.inputGroupMixers.muteMixer
|
||||
monitorMuteMixerId = monitorMuteMixer?.id
|
||||
monitorVolumeLeft = @state.inputGroupMixers.mixer?.volume_left
|
||||
monitorMuteClasses = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !monitorMuteMixer?.mute
|
||||
'muted' : monitorMuteMixer?.mute
|
||||
})
|
||||
|
||||
chatMuteMixer = @state.chatGroupMixers.muteMixer
|
||||
chatMuteMixerId = chatMuteMixer?.id
|
||||
chatVolumeLeft = @state.chatGroupMixers.mixer?.volume_left
|
||||
chatMuteClasses = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !chatMuteMixer?.mute
|
||||
'muted' : chatMuteMixer?.mute
|
||||
})
|
||||
|
||||
`<div id="self-volume-hover" >
|
||||
<div className="monitor-mixer mixer-holder">
|
||||
<h3>Music</h3>
|
||||
<div className="session-track track">
|
||||
<div className="track-vu-left">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="best" mixers={this.state.inputGroupMixers} />
|
||||
</div>
|
||||
<div className="track-vu-right">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vur" mixers={this.state.inputGroupMixers} />
|
||||
</div>
|
||||
<div className="track-label">
|
||||
<div>Volume</div>
|
||||
<div>{monitorVolumeLeft}dB</div>
|
||||
</div>
|
||||
<SessionTrackGain mixers={this.state.inputGroupMixers} />
|
||||
<div className={monitorMuteClasses} data-control="mute" data-mixer-id={monitorMuteMixerId} onClick={this.handleAudioInputMute}/>
|
||||
|
||||
<input type="checkbox" name="mute"/>
|
||||
<label htmlFor="mute">Mute</label>
|
||||
</div>
|
||||
|
||||
<div className="textual-help">
|
||||
<p>Use this slider to control the volume of all the music in the session in your headphones or speakers.</p>
|
||||
<p>This will not affect the volume 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>
|
||||
|
||||
<div className="chat-mixer mixer-holder">
|
||||
<h3>Chat</h3>
|
||||
<div className="session-track track">
|
||||
<div className="track-vu-left">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="best" mixers={this.state.chatGroupMixers} />
|
||||
</div>
|
||||
<div className="track-vu-right">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vur" mixers={this.state.chatGroupMixers} />
|
||||
</div>
|
||||
<div className="track-label">
|
||||
<div>Volume</div>
|
||||
<div>{chatVolumeLeft}dB</div>
|
||||
</div>
|
||||
<SessionTrackGain mixers={this.state.chatGroupMixers} />
|
||||
<div className={chatMuteClasses} data-control="mute" data-mixer-id={chatMuteMixerId} onClick={this.handleChatMute}/>
|
||||
|
||||
<input type="checkbox" name="mute"/>
|
||||
<label htmlFor="mute">Mute</label>
|
||||
</div>
|
||||
|
||||
<div className="textual-help">
|
||||
<p>Use this slider to control the volume of all the voice chat in the session in your headphones or speakers.</p>
|
||||
<p>This will not affect the volume for other musicians in the session.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
# initialize icheck
|
||||
$chatMuteCheckbox = $root.find('.chat-mixer input')
|
||||
context.JK.checkbox($chatMuteCheckbox)
|
||||
$chatMuteCheckbox.on('ifChanged', @handleChatMuteCheckbox);
|
||||
|
||||
if @state.chatGroupMixers.muteMixer.mute
|
||||
$chatMuteCheckbox.iCheck('check').attr('checked', true)
|
||||
else
|
||||
$chatMuteCheckbox.iCheck('uncheck').attr('checked', false)
|
||||
|
||||
$audioInputMuteCheckbox = $root.find('.monitor-mixer input')
|
||||
context.JK.checkbox($audioInputMuteCheckbox)
|
||||
$audioInputMuteCheckbox.on('ifChanged', @handleAudioInputMuteCheckbox);
|
||||
|
||||
if @state.inputGroupMixers.muteMixer.mute
|
||||
$audioInputMuteCheckbox.iCheck('check').attr('checked', true)
|
||||
else
|
||||
$audioInputMuteCheckbox.iCheck('uncheck').attr('checked', false)
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
# re-initialize icheck
|
||||
$chatMuteCheckbox = $root.find('.chat-mixer input')
|
||||
|
||||
if nextState.chatGroupMixers.muteMixer?.mute
|
||||
$chatMuteCheckbox.iCheck('check').attr('checked', true)
|
||||
else
|
||||
$chatMuteCheckbox.iCheck('uncheck').attr('checked', false)
|
||||
|
||||
$audioInputMuteCheckbox = $root.find('.monitor-mixer input')
|
||||
|
||||
if nextState.inputGroupMixers.muteMixer?.mute
|
||||
$audioInputMuteCheckbox.iCheck('check').attr('checked', true)
|
||||
else
|
||||
$audioInputMuteCheckbox.iCheck('uncheck').attr('checked', false)
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
context = window
|
||||
|
||||
@SessionSettingsBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
openSettings: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@app.layout.showDialog('session-settings')
|
||||
|
||||
render: () ->
|
||||
`<a className="session-settings button-grey left" onClick={this.openSettings}>
|
||||
<img src="/assets/content/icon_settings_sm.png" align="texttop" height="12" width="12"/>
|
||||
SETTINGS
|
||||
</a>`
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
context = window
|
||||
|
||||
@SessionShareBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
onShare: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@app.layout.showDialog('share-dialog')
|
||||
|
||||
render: () ->
|
||||
`<a className="session-share button-grey left" onClick={this.onShare}>
|
||||
<img src="/assets/content/icon_share.png" align="texttop" height="12" width="12"/>
|
||||
SHARE
|
||||
</a>`
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
})
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
@SessionTrackGain = React.createClass({
|
||||
|
||||
getInitialState: () ->
|
||||
{
|
||||
mixers: @props.mixers,
|
||||
behaviors: @props.behaviors || {}
|
||||
}
|
||||
|
||||
faderChanged: (e, data) ->
|
||||
$target = $(this)
|
||||
groupId = $target.data('groupId')
|
||||
mixers = [@state.mixers.mixer]
|
||||
|
||||
MixerActions.faderChanged(data, mixers, groupId)
|
||||
|
||||
render: () ->
|
||||
mixerId = this.state.mixers?.mixer?.id
|
||||
|
||||
`<div className="track-gain">
|
||||
<div className="fader vertical" data-control="fader" data-fader-id={mixerId} 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', @state.mixers.mixer.id).data('groupId', @state.mixers.mixer.groupId).data('mixer', @state.mixers.mixer).data('opposite-mixer', @state.mixers.oppositeMixer)
|
||||
|
||||
if @state.behaviors.mediaControlsDisabled
|
||||
$fader.data('media-controls-disabled', true).data('media-track-opener', @state.behaviors.mediaTrackOpener) # this we be applied later to the fader handle $element
|
||||
|
||||
$fader.data('showHelpAboutMediaMixers', @state.behaviors.showHelpAboutMediaMixers)
|
||||
|
||||
context.JK.FaderHelpers.renderFader2($fader, {faderType: 'vertical'});
|
||||
|
||||
# Initialize gain position
|
||||
MixerActions.initGain(@state.mixers.mixer)
|
||||
|
||||
# watch for fader change events
|
||||
$fader.on('fader_change', @faderChanged);
|
||||
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
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')
|
||||
mixers = [@state.mixers.mixer]
|
||||
|
||||
MixerActions.panChanged(data, mixers, groupId)
|
||||
|
||||
render: () ->
|
||||
|
||||
mixerId = this.state.mixers?.mixer?.id
|
||||
|
||||
`<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={mixerId} 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.convertPercentToPanForDisplay)
|
||||
|
||||
# Initialize gain position
|
||||
MixerActions.initPan(this.state.mixers.mixer)
|
||||
|
||||
# watch for fader change events
|
||||
$fader.on('fader_change', this.panChanged)
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionTrackPanHover = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@SessionMyTracksStore, "onInputsChanged")]
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
mixers = sessionMixers.mixers
|
||||
newMixers = mixers.refreshMixer(@state.mixers)
|
||||
|
||||
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>`
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
# if the mixers go dead, whack our selves out of existence
|
||||
unless nextState.mixers?
|
||||
$container = $root.closest('.react-holder')
|
||||
$container.data('bt').btOff()
|
||||
return
|
||||
})
|
||||
|
|
@ -0,0 +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" onClick={this.onConfigureSettings}>
|
||||
<img src="/assets/content/icon_settings_lg.png" width="18" height="18" />
|
||||
<span>Settings</span>
|
||||
</a>`
|
||||
})
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
context = window
|
||||
ptrCount = 0
|
||||
|
||||
@SessionTrackVU = React.createClass({
|
||||
|
||||
|
||||
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 = classNames('vulight', 'vu' + i, lightClass)
|
||||
|
||||
lights.push(`<td key={i} width={this.props.lightWidth} height={this.props.lightHeight} className={lightClasses}></td>`)
|
||||
|
||||
tableClasses = classNames('vu', 'horizontal')
|
||||
|
||||
`<table className={tableClasses} data-light-count={this.props.lightCount}>
|
||||
<tbody>
|
||||
<tr>
|
||||
{lights}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`
|
||||
else
|
||||
|
||||
for i in [0..this.props.lightCount-1].reverse()
|
||||
lightClass = if (i >= redSwitch) then "vu-red-off" else "vu-green-off"
|
||||
|
||||
lightClasses = classNames('vulight', 'vu' + i, lightClass)
|
||||
|
||||
lights.push(`<tr key={i}><td width={this.props.lightWidth} height={this.props.lightHeight} className={lightClasses}></td></tr>`)
|
||||
|
||||
tableClasses = classNames('vu', 'vertical')
|
||||
|
||||
`<table className={tableClasses} data-light-count={this.props.lightCount}>
|
||||
<tbody>
|
||||
{lights}
|
||||
</tbody>
|
||||
</table>`
|
||||
|
||||
getInitialState: () ->
|
||||
{registered: null, ptr: @props.ptr || "STV#{ptrCount++}"}
|
||||
|
||||
registerVU: (mixer) ->
|
||||
|
||||
mixerChanged = false
|
||||
if @state.registered? && mixer?
|
||||
|
||||
# see if the mixer ID changed; if so, we need to unregister and re-register
|
||||
if @state.registered.mixer.id != mixer.id
|
||||
logger.debug("unregistering vu due to mixer change", @state.registered.mixer)
|
||||
context.JK.VuHelpers.unregisterVU(@state.registered.mixer, @state.registered.ptr)
|
||||
mixerChanged = true
|
||||
|
||||
if !mixerChanged && (@state.registered? || !mixer?)
|
||||
return
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
|
||||
if mixerChanged
|
||||
logger.debug("re-registering VU")
|
||||
else
|
||||
logger.debug("registered VU")
|
||||
|
||||
context.JK.VuHelpers.registerVU(@props.side, mixer, @state.ptr, @props.orientation == 'horizontal', @props.lightCount, $root.find('td'))
|
||||
|
||||
@setState(registered: {mixer: mixer, ptr: @state.ptr})
|
||||
|
||||
|
||||
componentWillReceiveProps: (nextProps) ->
|
||||
@registerVU(nextProps.mixers?.vuMixer)
|
||||
|
||||
componentDidMount: () ->
|
||||
@registerVU(@props.mixers?.vuMixer)
|
||||
|
||||
componentWillUnmount: () ->
|
||||
if @state.registered?
|
||||
logger.debug("unregistered VU")
|
||||
context.JK.VuHelpers.unregisterVU(@state.registered.mixer, @state.registered.ptr)
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
context = window
|
||||
ChannelGroupIds = context.JK.ChannelGroupIds
|
||||
MixerActions = @MixerActions
|
||||
ptrCount = 0
|
||||
|
||||
@SessionTrackVolumeHover = React.createClass({
|
||||
|
||||
btElement: null
|
||||
mixins: [Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged")]
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
|
||||
mixers = sessionMixers.mixers
|
||||
newMixers = mixers.refreshMixer(@state.mixers)
|
||||
|
||||
this.setState({mixers: newMixers})
|
||||
|
||||
getInitialState: () ->
|
||||
{mixers: this.props.mixers, ptr: "STVH#{ptrCount++}" }
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
if @state.mixers.mixer.group_id == ChannelGroupIds.AudioInputMusicGroup || @state.mixers.mixer.group_id == ChannelGroupIds.AudioInputChatGroup
|
||||
MixerActions.mute([this.state.mixers.mixer, this.state.mixers.oppositeMixer], muting)
|
||||
else
|
||||
MixerActions.mute([this.state.mixers.mixer], muting)
|
||||
|
||||
|
||||
handleMuteCheckbox: (e) ->
|
||||
muting = $(e.target).is(':checked')
|
||||
|
||||
if @state.mixers.mixer.group_id == ChannelGroupIds.AudioInputMusicGroup || @state.mixers.mixer.group_id == ChannelGroupIds.AudioInputChatGroup
|
||||
MixerActions.mute([this.state.mixers.mixer, this.state.mixers.oppositeMixer], muting)
|
||||
else
|
||||
MixerActions.mute([this.state.mixers.mixer], muting)
|
||||
|
||||
|
||||
render: () ->
|
||||
|
||||
muteMixer = this.state.mixers?.muteMixer
|
||||
|
||||
muteMixerId = muteMixer?.id
|
||||
volume_left = this.state.mixers?.mixer?.volume_left
|
||||
|
||||
classes = classNames({
|
||||
'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="left" mixers={this.state.mixers} ptr={this.state.ptr} />
|
||||
</div>
|
||||
<div className="track-vu-right">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="right" mixers={this.state.mixers} ptr={this.state.ptr} />
|
||||
</div>
|
||||
<div className="track-label">
|
||||
<div>Volume</div>
|
||||
<div>{volume_left}dB</div>
|
||||
</div>
|
||||
<SessionTrackGain mixers={this.state.mixers} />
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
|
||||
<input type="checkbox" name="mute"/>
|
||||
<label htmlFor="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 @state.mixers.muteMixer.mute
|
||||
$checkbox.iCheck('check').attr('checked', true)
|
||||
else
|
||||
$checkbox.iCheck('uncheck').attr('checked', false)
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
# if the mixers go dead, whack our selves out of existence
|
||||
unless nextState.mixers?
|
||||
$container = $root.closest('.react-holder')
|
||||
$container.data('bt').btOff()
|
||||
return
|
||||
|
||||
# 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,16 @@
|
|||
context = window
|
||||
SessionActions = @SessionActions
|
||||
|
||||
@SessionVideoBtn = React.createClass({
|
||||
|
||||
sessionWebCam: (e) ->
|
||||
e.preventDefault();
|
||||
|
||||
SessionActions.toggleSessionVideo()
|
||||
|
||||
render: () ->
|
||||
`<a className="session-share button-grey-toggle left" onClick={this.sessionWebCam}>
|
||||
<img src="/assets/content/icon_cam.png" align="texttop" height="12" width="12"/>
|
||||
VIDEO
|
||||
</a>`
|
||||
})
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
@SessionVolumeSettingsBtn = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@MixerStore,"onInputsChanged")]
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
this.setState(sessionMixers)
|
||||
|
||||
render: () ->
|
||||
`<a className="session-volume-settings button-grey left">
|
||||
<img src="/assets/content/icon_settings_sm.png" align="texttop" height="12" width="12"/>
|
||||
VOLUME
|
||||
</a>`
|
||||
|
||||
componentDidMount: () ->
|
||||
$root = $(this.getDOMNode())
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$root,
|
||||
'SessionSelfVolumeHover',
|
||||
() =>
|
||||
{inputGroupMixers: @state.mixers.getAudioInputCategoryMixer(MIX_MODES.PERSONAL), chatGroupMixers: @state.mixers.getChatCategoryMixer( MIX_MODES.PERSONAL)}
|
||||
,
|
||||
{width:470, positions:['right', 'bottom', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
})
|
||||
|
|
@ -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>`
|
||||
})
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
context = window
|
||||
|
||||
@AppActions = Reflux.createActions({
|
||||
appInit: {}
|
||||
})
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
context = window
|
||||
|
||||
BroadcastActions = Reflux.createActions({
|
||||
@BroadcastActions = Reflux.createActions({
|
||||
load: {asyncResult: true},
|
||||
hide: {}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
context = window
|
||||
|
||||
@MediaPlaybackActions = Reflux.createActions({
|
||||
playbackStateChange: {}
|
||||
positionUpdate:{}
|
||||
mediaStartPlay: {}
|
||||
mediaStopPlay: {}
|
||||
mediaPausePlay: {}
|
||||
mediaChangePosition: {}
|
||||
currentTimeChanged: {}
|
||||
})
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
context = window
|
||||
|
||||
@MixerActions = Reflux.createActions({
|
||||
mute: {}
|
||||
faderChanged: {}
|
||||
initGain: {}
|
||||
panChanged: {}
|
||||
initPan: {}
|
||||
mixersChanged: {}
|
||||
syncTracks: {}
|
||||
mixerModeChanged: {}
|
||||
loopChanged: {}
|
||||
openMetronome: {}
|
||||
metronomeChanged: {}
|
||||
deadUserRemove: {}
|
||||
})
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
context = window
|
||||
|
||||
@NotificationActions = Reflux.createActions({
|
||||
clear:{}
|
||||
backendNotification: {}
|
||||
frontendNotification: {}
|
||||
sessionEnded: {}
|
||||
})
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
context = window
|
||||
|
||||
@RecordingActions = Reflux.createActions({
|
||||
initModel: {}
|
||||
startRecording: {}
|
||||
stopRecording: {}
|
||||
startingRecording:{}
|
||||
startedRecording: {}
|
||||
stoppingRecording: {}
|
||||
stoppedRecording: {}
|
||||
abortedRecording: {}
|
||||
openRecordingControls: {}
|
||||
recordingControlsClosed: {}
|
||||
})
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
context = window
|
||||
|
||||
@SessionActions = Reflux.createActions({
|
||||
joinSession: {}
|
||||
leaveSession: {}
|
||||
mixersChanged: {}
|
||||
allowLeaveSession: {}
|
||||
syncWithServer: {}
|
||||
toggleSessionVideo : {}
|
||||
audioResync: {}
|
||||
openBackingTrack: {}
|
||||
closeMedia: {}
|
||||
updateSession: {}
|
||||
downloadingJamTrack : {}
|
||||
openMetronome: {}
|
||||
showNativeMetronomeGui: {}
|
||||
metronomeCricketChange: {}
|
||||
windowBackgrounded: {}
|
||||
broadcastFailure: {}
|
||||
broadcastSuccess: {}
|
||||
broadcastStopped: {}
|
||||
})
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
context = window
|
||||
|
||||
@SessionMyTracksActions = Reflux.createActions({
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,830 @@
|
|||
context = window
|
||||
|
||||
ChannelGroupIds = context.JK.ChannelGroupIds
|
||||
CategoryGroupIds = context.JK.CategoryGroupIds
|
||||
MIX_MODES = context.JK.MIX_MODES;
|
||||
|
||||
|
||||
@MixerHelper = class MixerHelper
|
||||
|
||||
constructor: (@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixMode) ->
|
||||
@mixMode = MIX_MODES.PERSONAL # TODO - remove mixMode from MixerHelper? Or at least stop using it in most functions
|
||||
@app = @session.app
|
||||
@mixersByResourceId = {}
|
||||
@mixersByTrackId = {}
|
||||
@allMixers = {}
|
||||
@currentMixerRangeMin = null
|
||||
@currentMixerRangeMax = null
|
||||
@mediaSummary = {}
|
||||
@mediaTrackGroups = [ChannelGroupIds.MediaTrackGroup, ChannelGroupIds.JamTrackGroup,
|
||||
ChannelGroupIds.MetronomeGroup]
|
||||
@muteBothMasterAndPersonalGroups = [ChannelGroupIds.AudioInputMusicGroup, ChannelGroupIds.MediaTrackGroup,
|
||||
ChannelGroupIds.JamTrackGroup, ChannelGroupIds.MetronomeGroup]
|
||||
@organize()
|
||||
|
||||
organize: () ->
|
||||
for masterMixer 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 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()
|
||||
@chatMixer = @resolveChatMixer()
|
||||
|
||||
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();
|
||||
###
|
||||
|
||||
@backingTracks = @resolveBackingTracks()
|
||||
@jamTracks = @resolveJamTracks()
|
||||
@recordedTracks = @resolveRecordedTracks()
|
||||
@metronome = @resolveMetronome()
|
||||
|
||||
|
||||
if @adhocTrackMixers.length > 0
|
||||
logger.warn("some tracks are open that we don't know how to show")
|
||||
|
||||
@mediaSummary =
|
||||
recordingOpen: @recordedTracks.length > 0
|
||||
jamTrackOpen: @jamTracks.length > 0
|
||||
backingTrackOpen: @backingTracks.length > 0
|
||||
metronomeOpen: @metronome?
|
||||
|
||||
# figure out if any media is open
|
||||
mediaOpenSummary = false
|
||||
for mediaType, mediaOpen of @mediaSummary
|
||||
mediaOpenSummary = true if mediaOpen
|
||||
|
||||
@mediaSummary.mediaOpen = mediaOpenSummary
|
||||
|
||||
# this method is pretty complicated because it forks on a key bit of state:
|
||||
# sessionModel.isPlayingRecording()
|
||||
# a backing track opened as part of a recording has a different behavior and presence on the server (recording.recorded_backing_tracks)
|
||||
# than a backing track opend ad-hoc (connection.backing_tracks)
|
||||
|
||||
resolveBackingTracks: () ->
|
||||
backingTracks = []
|
||||
|
||||
return backingTracks unless @backingTrackMixers.length > 0
|
||||
|
||||
# find both client and server representation of the backing track
|
||||
serverBackingTracks = []
|
||||
backingTrackMixers = @backingTrackMixers
|
||||
|
||||
if @session.isPlayingRecording()
|
||||
backingTrackMixers = context._.filter(backingTrackMixers, (mixer) -> return mixer.managed || !mixer.managed?)
|
||||
serverBackingTracks = @session.recordedBackingTracks()
|
||||
else
|
||||
serverBackingTracks = @session.backingTracks();
|
||||
backingTrackMixers = context._.filter(backingTrackMixers, (mixer) -> return !mixer.managed)
|
||||
if backingTrackMixers.length > 1
|
||||
logger.error("multiple, managed backing track mixers encountered", backingTrackMixers)
|
||||
@app.notify({
|
||||
title: "Multiple Backing Tracks Encountered",
|
||||
text: "Only one backing track can be open a time.",
|
||||
icon_url: "/assets/content/icon_alert_big.png"
|
||||
});
|
||||
return backingTracks;
|
||||
|
||||
# we don't render backing tracks unless we have server data to accompany
|
||||
if !serverBackingTracks? || serverBackingTracks.length == 0
|
||||
return backingTracks
|
||||
|
||||
noCorrespondingTracks = false
|
||||
for mixer in backingTrackMixers
|
||||
# find the track or tracks that correspond to the mixer
|
||||
correspondingTracks = []
|
||||
noCorrespondingTracks = false
|
||||
if @session.isPlayingRecording()
|
||||
for backingTrack in serverBackingTracks
|
||||
# occurs if this client is the one that opened the track, # occurs if this client is a remote participant
|
||||
if mixer.persisted_track_id == backingTrack.client_track_id || mixer.id == 'L' + backingTrack.client_track_id
|
||||
correspondingTracks.push(backingTrack)
|
||||
else
|
||||
# if this is just an open backing track, then we can assume that the 1st backingTrackMixer is ours
|
||||
correspondingTracks.push(serverBackingTracks[0])
|
||||
|
||||
if correspondingTracks.length == 0
|
||||
noCorrespondingTracks = true
|
||||
logger.debug("renderBackingTracks: could not map backing tracks")
|
||||
@app.notify({
|
||||
title: "Unable to Open Backing Track",
|
||||
text: "Could not correlate server and client tracks",
|
||||
icon_url: "/assets/content/icon_alert_big.png"
|
||||
});
|
||||
break
|
||||
|
||||
# now we have backing track and mixer in hand; we can render
|
||||
serverBackingTrack = correspondingTracks[0]
|
||||
|
||||
oppositeMixer = @getMixerByResourceId(mixer.rid, MIX_MODES.PERSONAL);
|
||||
|
||||
isOpener = mixer.group_id == ChannelGroupIds.MediaTrackGroup
|
||||
data =
|
||||
isOpener: isOpener
|
||||
shortFilename: context.JK.getNameOfFile(serverBackingTrack.filename)
|
||||
instrumentIcon: context.JK.getInstrumentIcon45(serverBackingTrack.instrument_id)
|
||||
photoUrl: "/assets/content/icon_recording.png"
|
||||
showLoop: isOpener && !@session.isPlayingRecording()
|
||||
track: serverBackingTrack
|
||||
mixers: @mediaMixers(mixer, isOpener)
|
||||
|
||||
backingTracks.push(data)
|
||||
|
||||
backingTracks
|
||||
|
||||
resolveJamTracks: () ->
|
||||
_jamTracks = []
|
||||
|
||||
return _jamTracks unless @jamTrackMixers.length > 0
|
||||
|
||||
|
||||
jamTrackMixers = @jamTrackMixers.slice();
|
||||
jamTracks = []
|
||||
jamTrackName = null;
|
||||
|
||||
if @session.isPlayingRecording()
|
||||
# only return managed mixers for recorded backing tracks
|
||||
jamTracks = @session.recordedJamTracks()
|
||||
jamTrackName = @session.recordedJamTrackName()
|
||||
else
|
||||
# only return un-managed (ad-hoc) mixers for normal backing tracks
|
||||
jamTracks = @session.jamTracks()
|
||||
jamTrackName = @session.jamTrackName()
|
||||
|
||||
# pluck the 1st mixer, and assume that all other mixers in this group are of the same type (between JamTrack vs Peer)
|
||||
# if it's a locally opened track (JamTrackGroup), then we can say this person is the opener
|
||||
isOpener = jamTrackMixers[0].group_id == ChannelGroupIds.JamTrackGroup;
|
||||
|
||||
if jamTracks
|
||||
noCorrespondingTracks = false
|
||||
for jamTrack in jamTracks
|
||||
mixer = null
|
||||
preMasteredClass = ""
|
||||
# find the track or tracks that correspond to the mixer
|
||||
correspondingTracks = []
|
||||
|
||||
for matchMixer in @jamTrackMixers
|
||||
if matchMixer.id == jamTrack.id
|
||||
correspondingTracks.push(jamTrack)
|
||||
mixer = matchMixer
|
||||
|
||||
if correspondingTracks.length == 0
|
||||
noCorrespondingTracks = true
|
||||
logger.error("could not correlate jam tracks", jamTrackMixers, jamTracks)
|
||||
@app.notify({
|
||||
title: "Unable to Open JamTrack",
|
||||
text: "Could not correlate server and client tracks",
|
||||
icon_url: "/assets/content/icon_alert_big.png"})
|
||||
return _jamTracks
|
||||
|
||||
#jamTracks = $.grep(jamTracks, (value) =>
|
||||
# $.inArray(value, correspondingTracks) < 0
|
||||
#)
|
||||
|
||||
# prune found mixers
|
||||
jamTrackMixers.splice(mixer);
|
||||
|
||||
oneOfTheTracks = correspondingTracks[0];
|
||||
instrumentIcon = context.JK.getInstrumentIcon24(oneOfTheTracks.instrument.id);
|
||||
|
||||
part = oneOfTheTracks.part
|
||||
part = '' unless name?
|
||||
|
||||
data =
|
||||
name: jamTrackName
|
||||
part: part
|
||||
isOpener: isOpener
|
||||
instrumentIcon: instrumentIcon
|
||||
track: oneOfTheTracks
|
||||
mixers: @mediaMixers(mixer, isOpener)
|
||||
|
||||
_jamTracks.push(data)
|
||||
|
||||
_jamTracks
|
||||
|
||||
resolveRecordedTracks: () ->
|
||||
recordedTracks = []
|
||||
|
||||
return recordedTracks unless @recordingTrackMixers.length > 0
|
||||
|
||||
serverRecordedTracks = @session.recordedTracks()
|
||||
|
||||
isOpener = @recordingTrackMixers[0].group_id == ChannelGroupIds.MediaTrackGroup
|
||||
|
||||
# using the server's info in conjuction with the client's, draw the recording tracks
|
||||
if serverRecordedTracks
|
||||
recordingName = @session.recordingName()
|
||||
noCorrespondingTracks = false
|
||||
for mixer in @recordingTrackMixers
|
||||
preMasteredClass = ""
|
||||
correspondingTracks = []
|
||||
for recordedTrack in serverRecordedTracks
|
||||
if mixer.id.indexOf("L") == 0
|
||||
if mixer.id.substring(1) == recordedTrack.client_track_id
|
||||
correspondingTracks.push(recordedTrack)
|
||||
else if mixer.id.indexOf("C") == 0
|
||||
if mixer.id.substring(1) == recordedTrack.client_id
|
||||
correspondingTracks.push(recordedTrack)
|
||||
preMasteredClass = "pre-mastered-track"
|
||||
else
|
||||
# this should not be possible
|
||||
alert("Invalid state: the recorded track had neither persisted_track_id or persisted_client_id")
|
||||
|
||||
if correspondingTracks.length == 0
|
||||
noCorrespondingTracks = true
|
||||
logger.debug("unable to correlate all recorded tracks", recordingMixers, serverRecordedTracks)
|
||||
@app.notify({
|
||||
title: "Unable to Open Recording",
|
||||
text: "Could not correlate server and client tracks",
|
||||
icon_url: "/assets/content/icon_alert_big.png"});
|
||||
return recordedTracks
|
||||
|
||||
serverRecordedTracks = $.grep(serverRecordedTracks,
|
||||
(value) =>
|
||||
$.inArray(value, correspondingTracks) < 0
|
||||
)
|
||||
|
||||
oneOfTheTracks = correspondingTracks[0]
|
||||
instrumentIcon = context.JK.getInstrumentIcon24(oneOfTheTracks.instrument_id)
|
||||
userName = oneOfTheTracks.user.name
|
||||
userName = oneOfTheTracks.user.first_name + ' ' + oneOfTheTracks.user.last_name unless userName?
|
||||
|
||||
data =
|
||||
recordingName: recordingName
|
||||
isOpener: isOpener
|
||||
userName: userName
|
||||
instrumentIcon: instrumentIcon
|
||||
track: oneOfTheTracks
|
||||
mixers: @mediaMixers(mixer, isOpener)
|
||||
|
||||
recordedTracks.push(data)
|
||||
|
||||
recordedTracks
|
||||
|
||||
resolveMetronome: () ->
|
||||
metronome = null
|
||||
|
||||
return metronome if @metronomeTrackMixers.length == 0
|
||||
|
||||
mixer = @metronomeTrackMixers[0]
|
||||
|
||||
instrumentIcon = "/assets/content/icon_metronome.png"
|
||||
|
||||
oppositeMixer = @getMixerByResourceId(mixer.rid, MIX_MODES.PERSONAL);
|
||||
|
||||
metronome =
|
||||
instrumentIcon: instrumentIcon
|
||||
mixers: {mixer: mixer, oppositeMixer: oppositeMixer, vuMixer: mixer, muteMixer: mixer}
|
||||
|
||||
metronome
|
||||
|
||||
resolveChatMixer: () ->
|
||||
masterChatMixers = @mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.MASTER);
|
||||
|
||||
return null if masterChatMixers.length == 0
|
||||
|
||||
personalChatMixers = @mixersForGroupId(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.PERSONAL);
|
||||
|
||||
if personalChatMixers.length == 0
|
||||
logger.warn("unable to find personal mixer for voice chat");
|
||||
return null
|
||||
|
||||
|
||||
masterChatMixer = masterChatMixers[0];
|
||||
personalChatMixer = personalChatMixers[0];
|
||||
|
||||
{
|
||||
master: {
|
||||
mixer: masterChatMixer
|
||||
muteMixer: masterChatMixer
|
||||
vuMixer: masterChatMixer
|
||||
oppositeMixer: personalChatMixer
|
||||
}
|
||||
personal: {
|
||||
mixer: personalChatMixer
|
||||
muteMixer: personalChatMixer
|
||||
vuMixer: personalChatMixer
|
||||
oppositeMixer: masterChatMixer
|
||||
}
|
||||
}
|
||||
|
||||
# supply the master mixer of a media track, and this function will harvest out the rest
|
||||
mediaMixers:(masterMixer, isOpener) ->
|
||||
personalMixer = if isOpener then @getMixerByResourceId(masterMixer.rid, MIX_MODES.PERSONAL) else null
|
||||
personalVuMixer = if isOpener then personalMixer else masterMixer
|
||||
{
|
||||
isOpener: isOpener
|
||||
|
||||
master: {
|
||||
mixer: masterMixer
|
||||
muteMixer: masterMixer
|
||||
vuMixer: masterMixer
|
||||
}
|
||||
personal: {
|
||||
mixer: personalMixer
|
||||
muteMixer: personalMixer
|
||||
vuMixer: personalVuMixer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mixersForGroupIds: (groupIds, mixMode) ->
|
||||
foundMixers = []
|
||||
mixers = if mixMode == MIX_MODES.MASTER then @masterMixers else @personalMixers;
|
||||
|
||||
for mixer in mixers
|
||||
for groupId in groupIds
|
||||
if mixer.group_id == groupId
|
||||
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
|
||||
unless mixer?
|
||||
logger.debug("empty mixer: ", mixers)
|
||||
continue
|
||||
|
||||
if mixer.client_id == clientId
|
||||
for groupId in groupIds
|
||||
if mixer.group_id == groupId
|
||||
if (mixer.groupId != ChannelGroupIds.UserMusicInputGroup) && !(mixer.id of usedMixers)
|
||||
mixers = foundMixers[mixer.group_id]
|
||||
if !mixers
|
||||
mixers = []
|
||||
foundMixers[mixer.group_id] = mixers
|
||||
mixers.push(mixer)
|
||||
|
||||
foundMixers
|
||||
|
||||
getMixerByResourceId:(resourceId, mode) ->
|
||||
mixerPair = @mixersByResourceId[resourceId];
|
||||
|
||||
return null if(!mixerPair)
|
||||
|
||||
if !mode?
|
||||
return mixerPair;
|
||||
else
|
||||
if mode == MIX_MODES.MASTER
|
||||
return mixerPair.master
|
||||
else
|
||||
return mixerPair.personal
|
||||
|
||||
|
||||
findMixerForTrack: (client_id, track, myTrack, mode = MIX_MODES.PERSONAL) ->
|
||||
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, mode)
|
||||
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, !mode)
|
||||
|
||||
if mode == 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, @allMixers)
|
||||
else
|
||||
switch mode
|
||||
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", client_id, track.client_track_id)
|
||||
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, mixers, groupId) ->
|
||||
for mixer in mixers
|
||||
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
|
||||
mixer = @fillTrackVolumeObject(mixer.id, mixer.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, mixers, groupId) ->
|
||||
# media tracks are the only controls that sometimes set two mixers right now
|
||||
for mixer in mixers
|
||||
broadcast = !(data.dragging) # If fader is still dragging, don't broadcast
|
||||
mixer = @fillTrackVolumeObject(mixer.id, mixer.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, Math.abs(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);
|
||||
|
||||
loopChanged: (mixer, shouldLoop) ->
|
||||
|
||||
@fillTrackVolumeObject(mixer.id, mixer.mode)
|
||||
context.trackVolumeObject.loop = shouldLoop
|
||||
context.jamClient.SessionSetControlState(mixer.id, mixer.mode)
|
||||
|
||||
# keep state of mixer in sync with backend
|
||||
mixer = @getMixer(mixer.id, mixer.mode)
|
||||
mixer.loop = context.trackVolumeObject.loop
|
||||
|
||||
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, mode, leftValue, leftClipping, rightValue, rightClipping) ->
|
||||
mixer = @getMixer(mixerId, mode)
|
||||
|
||||
if mixer?
|
||||
context.JK.VuHelpers.updateVU3(mixer, leftValue, leftClipping, rightValue, rightClipping)
|
||||
|
||||
###
|
||||
if mixer
|
||||
if mixer.stereo # // stereo track
|
||||
if mixerId.substr(-4) == "_vul"
|
||||
context.JK.VuHelpers.updateVU2('vul', mixer, value)
|
||||
else
|
||||
context.JK.VuHelpers.updateVU2('vur', 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)
|
||||
###
|
||||
getTrackInfo: () ->
|
||||
context.JK.TrackHelpers.getTrackInfo(context.jamClient, @masterMixers)
|
||||
|
||||
getGroupMixer: (categoryId, mode) ->
|
||||
groupId = if mode == MIX_MODES.MASTER then ChannelGroupIds.MasterCatGroup else ChannelGroupIds.MonitorCatGroup
|
||||
mixers = @mixersForGroupId(groupId, mode)
|
||||
|
||||
if mixers.length == 0
|
||||
logger.warn("could not find mixer with group ID: " + groupId + ', mode:' + mode)
|
||||
return null
|
||||
|
||||
found = null
|
||||
for mixer in mixers
|
||||
if mixer.name == categoryId
|
||||
found = mixer
|
||||
break
|
||||
|
||||
unless found?
|
||||
logger.warn("could not find mixer with categoryId: " + categoryId)
|
||||
return null
|
||||
else
|
||||
{
|
||||
mixer: found,
|
||||
muteMixer : found,
|
||||
vuMixer: found,
|
||||
oppositeMixer: found
|
||||
}
|
||||
|
||||
getAudioInputCategoryMixer: (mode) ->
|
||||
@getGroupMixer(CategoryGroupIds.AudioInputMusic, mode)
|
||||
|
||||
getChatCategoryMixer: (mode) ->
|
||||
@getGroupMixer(CategoryGroupIds.AudioInputChat, mode)
|
||||
|
||||
getMediaCategoryMixer: (mode) ->
|
||||
@getGroupMixer(CategoryGroupIds.MediaTrack, mode)
|
||||
|
||||
getUserMediaCategoryMixer: (mode) ->
|
||||
@getGroupMixer(CategoryGroupIds.UserMedia, mode)
|
||||
|
||||
|
||||
refreshMixer: (mixers) ->
|
||||
return null unless mixers? && mixers.mixer?
|
||||
|
||||
mixer = @getMixer(mixers.mixer.id, mixers.mixer.mode)
|
||||
|
||||
if mixer?
|
||||
oppositeMixer = if mixers.oppositeMixer then @getMixer(mixers.oppositeMixer.id, mixers.oppositeMixer.mode) else null
|
||||
{
|
||||
mixer: mixer
|
||||
vuMixer: @getMixer(mixers.vuMixer.id, mixers.vuMixer.mode)
|
||||
muteMixer: @getMixer(mixers.muteMixer.id, mixers.muteMixer.mode)
|
||||
oppositeMixer: oppositeMixer
|
||||
}
|
||||
else
|
||||
return null
|
||||
|
||||
|
||||
recordingName: () ->
|
||||
@session.recordingName()
|
||||
|
||||
jamTrackName: () ->
|
||||
@session.jamTrackName()
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
context = window
|
||||
|
||||
@SessionHelper = class SessionHelper
|
||||
|
||||
constructor: (app, session, participantsEverSeen, isRecording, downloadingJamTrack) ->
|
||||
@app = app
|
||||
@session = session
|
||||
@participantsEverSeen = participantsEverSeen
|
||||
@isRecording = isRecording
|
||||
@downloadingJamTrack = downloadingJamTrack
|
||||
|
||||
inSession: () ->
|
||||
@session?
|
||||
|
||||
participants: () ->
|
||||
if @session
|
||||
return @session.participants
|
||||
else
|
||||
[]
|
||||
|
||||
otherParticipants: () ->
|
||||
others = []
|
||||
for participant in @participants()
|
||||
myTrack = @app.clientId == participant.client_id
|
||||
|
||||
others.push(participant) unless myTrack
|
||||
others
|
||||
|
||||
# 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
|
||||
|
||||
backingTrack: () ->
|
||||
result = null
|
||||
if @session
|
||||
# TODO: objectize this for VRFS-2665, VRFS-2666, VRFS-2667, VRFS-2668
|
||||
result =
|
||||
path: @session.backing_track_path
|
||||
result
|
||||
|
||||
jamTracks: () ->
|
||||
if @session && @session.jam_track
|
||||
@session.jam_track.tracks.filter((track)->
|
||||
track.track_type == 'Track'
|
||||
)
|
||||
else
|
||||
null
|
||||
|
||||
jamTrackName: () ->
|
||||
@session?.jam_track?.name
|
||||
|
||||
recordedJamTracks:() ->
|
||||
if @session && @session.claimed_recording
|
||||
@session.claimed_recording.recording.recorded_jam_track_tracks
|
||||
else
|
||||
null
|
||||
|
||||
recordedJamTrackName: () ->
|
||||
jam_track = @session?.claimed_recording?.recording?.jam_track
|
||||
|
||||
if jam_track? then jam_track.name else null
|
||||
|
||||
recordingName: () ->
|
||||
@session?.claimed_recording?.name
|
||||
|
||||
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
|
||||
|
||||
id: () ->
|
||||
@session.id
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
@MasterPersonalMixersMixin = {
|
||||
|
||||
mixer: () ->
|
||||
if @props.mode == MIX_MODES.MASTER
|
||||
@props.mixers['master'].mixer
|
||||
else
|
||||
@props.mixers['personal'].mixer
|
||||
|
||||
mixers: () ->
|
||||
if @props.mode == MIX_MODES.MASTER
|
||||
@props.mixers['master']
|
||||
else
|
||||
@props.mixers['personal']
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
logger = context.JK.logger
|
||||
|
||||
@SessionMediaTracksMixin = {
|
||||
|
||||
metronomeTrulyGoneCheck: () ->
|
||||
|
||||
logger.debug("metronome is completely gone")
|
||||
@setState({metronomeFlickerTimeout: null})
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
|
||||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
|
||||
# the backend delete/adds the metronome rapidly when the user hits play. this is custom code to deal with that
|
||||
|
||||
metronomeFlickerTimeout = @state.metronomeFlickerTimeout
|
||||
|
||||
if mixers.metronome?
|
||||
if metronomeFlickerTimeout?
|
||||
logger.debug("canceling metronome flicker timeout because metronome mixer reappeared")
|
||||
clearTimeout(metronomeFlickerTimeout)
|
||||
metronomeFlickerTimeout = null
|
||||
else
|
||||
if @state.metronomeIsShowing
|
||||
logger.debug("setting metronome flicker timeout")
|
||||
clearTimeout(metronomeFlickerTimeout) if metronomeFlickerTimeout?
|
||||
metronomeFlickerTimeout = setTimeout(@metronomeTrulyGoneCheck, 1000)
|
||||
|
||||
metronomeIsShowing = mixers.metronome?
|
||||
|
||||
state =
|
||||
isRecording: session.isRecording
|
||||
mediaSummary: mixers.mediaSummary
|
||||
backingTracks: mixers.backingTracks
|
||||
jamTracks: mixers.jamTracks
|
||||
recordedTracks: mixers.recordedTracks
|
||||
metronome: mixers.metronome
|
||||
mediaCategoryMixer: mixers.getMediaCategoryMixer(@props.mode)
|
||||
recordingName: mixers.recordingName()
|
||||
jamTrackName: mixers.jamTrackName()
|
||||
metronomeIsShowing: metronomeIsShowing
|
||||
metronomeFlickerTimeout: metronomeFlickerTimeout
|
||||
|
||||
@inputsChangedProcessed(state) if @inputsChangedProcessed?
|
||||
|
||||
@setState(state)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
context = window
|
||||
|
||||
@SessionMyTracksMixin = {
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
|
||||
|
||||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
|
||||
tracks = []
|
||||
|
||||
if session.inSession()
|
||||
participant = session.getParticipant(@app.clientId)
|
||||
|
||||
if participant
|
||||
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url);
|
||||
|
||||
chatMixer = mixers.chatMixer
|
||||
chat = null
|
||||
if chatMixer
|
||||
chat =
|
||||
mixers: chatMixer
|
||||
mode: @props.mode
|
||||
photoUrl: photoUrl
|
||||
|
||||
name = participant.user.name;
|
||||
|
||||
for track in participant.tracks
|
||||
# try to find mixer info for this track
|
||||
mixerFinder = [participant.client_id, track, true] # so that other callers can re-find their mixer data
|
||||
mixerData = mixers.findMixerForTrack(participant.client_id, track, true, @props.mode)
|
||||
|
||||
# todo: sessionModel.setAudioEstablished
|
||||
|
||||
instrumentIcon = context.JK.getInstrumentIcon45(track.instrument_id);
|
||||
|
||||
tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, name: name, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id})
|
||||
|
||||
|
||||
else
|
||||
logger.debug("SessionMyTracks: unable to find participant")
|
||||
|
||||
this.setState(tracks: tracks, session:session, chat: chat)
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
context = window
|
||||
|
||||
@SessionOtherTracksMixin = {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
@AppStore = Reflux.createStore(
|
||||
{
|
||||
listenables: @AppActions
|
||||
|
||||
onAppInit: (app) ->
|
||||
@trigger(app)
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
PLAYBACK_MONITOR_MODE = context.JK.PLAYBACK_MONITOR_MODE
|
||||
RecordingActions = @RecordingActions
|
||||
|
||||
@MediaPlaybackStore = Reflux.createStore(
|
||||
{
|
||||
listenables: @MediaPlaybackActions
|
||||
|
||||
playbackStateChanged: false
|
||||
positionUpdateChanged: false
|
||||
currentTimeChanged: false
|
||||
playbackState: null
|
||||
positionMs: 0
|
||||
durationMs: 0
|
||||
isRecording: false
|
||||
sessionHelper: null
|
||||
|
||||
|
||||
init: () ->
|
||||
this.listenTo(context.SessionStore, this.onSessionChanged);
|
||||
|
||||
onCurrentTimeChanged: (time) ->
|
||||
@time = time
|
||||
@currentTimeChanged = true
|
||||
@issueChange()
|
||||
|
||||
onSessionChanged: (session) ->
|
||||
@isRecording = session.isRecording
|
||||
@sessionHelper = session
|
||||
|
||||
onMediaStartPlay: (data) ->
|
||||
logger.debug("calling jamClient.SessionStartPlay");
|
||||
context.jamClient.SessionStartPlay(data.playbackMode);
|
||||
|
||||
onMediaStopPlay: (data) ->
|
||||
# if a JamTrack is open, and the user hits 'pause' or 'stop', we need to automatically stop the recording
|
||||
if @sessionHelper.jamTracks() && @isRecording
|
||||
logger.debug("preemptive jamtrack stop")
|
||||
@startStopRecording();
|
||||
|
||||
if !data.endReached
|
||||
logger.debug("calling jamClient.SessionStopPlay. endReached:", data.endReached)
|
||||
context.jamClient.SessionStopPlay()
|
||||
|
||||
onMediaPausePlay: (data) ->
|
||||
# if a JamTrack is open, and the user hits 'pause' or 'stop', we need to automatically stop the recording
|
||||
if @sessionHelper.jamTracks() && @isRecording
|
||||
logger.debug("preemptive jamtrack stop")
|
||||
@startStopRecording();
|
||||
|
||||
|
||||
if !data.endReached
|
||||
logger.debug("calling jamClient.SessionPausePlay. endReached:", data.endReached)
|
||||
context.jamClient.SessionPausePlay()
|
||||
|
||||
startStopRecording: () ->
|
||||
if @isRecording
|
||||
RecordingActions.stopRecording.trigger()
|
||||
else
|
||||
RecordingActions.startRecording.trigger()
|
||||
|
||||
onMediaChangePosition: (data) ->
|
||||
seek = data.positionMs;
|
||||
|
||||
if data.playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK
|
||||
# if positionMs == 0, then seek it back to whatever the earliest play start is to catch all the prelude
|
||||
|
||||
if(seek == 0)
|
||||
duration = context.jamClient.SessionGetJamTracksPlayDurationMs();
|
||||
seek = duration.start;
|
||||
|
||||
logger.debug("calling jamClient.SessionTrackSeekMs(" + seek + ")");
|
||||
|
||||
if data.playbackMonitorMode == PLAYBACK_MONITOR_MODE.JAMTRACK
|
||||
context.jamClient.SessionJamTrackSeekMs(seek);
|
||||
else
|
||||
context.jamClient.SessionTrackSeekMs(seek);
|
||||
|
||||
|
||||
issueChange: () ->
|
||||
|
||||
@state =
|
||||
playbackState: @playbackState
|
||||
playbackStateChanged: @playbackStateChanged
|
||||
positionUpdateChanged: @positionUpdateChanged
|
||||
currentTimeChanged: @currentTimeChanged
|
||||
positionMs: @positionMs
|
||||
durationMs: @durationMs
|
||||
isPlaying: @isPlaying
|
||||
time: @time
|
||||
|
||||
this.trigger(@state)
|
||||
@playbackStateChanged = false
|
||||
@positionUpdateChanged = false
|
||||
@currentTimeChanged = false
|
||||
|
||||
onPlaybackStateChange: (text) ->
|
||||
@playbackState = text
|
||||
@playbackStateChanged = true
|
||||
|
||||
@issueChange()
|
||||
|
||||
onPositionUpdate: (playbackMode) ->
|
||||
if playbackMode == PLAYBACK_MONITOR_MODE.JAMTRACK
|
||||
@positionMs = context.jamClient.SessionCurrrentJamTrackPlayPosMs()
|
||||
duration = context.jamClient.SessionGetJamTracksPlayDurationMs()
|
||||
@durationMs = duration.media_len
|
||||
else
|
||||
@positionMs = context.jamClient.SessionCurrrentPlayPosMs()
|
||||
@durationMs = context.jamClient.SessionGetTracksPlayDurationMs()
|
||||
|
||||
@isPlaying = context.jamClient.isSessionTrackPlaying()
|
||||
|
||||
@positionUpdateChanged = true
|
||||
@issueChange()
|
||||
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
rest = context.JK.Rest()
|
||||
|
||||
@MixerStore = Reflux.createStore(
|
||||
{
|
||||
METRO_SOUND_LOOKUP: {
|
||||
0 : "BuiltIn",
|
||||
1 : "SineWave",
|
||||
2 : "Beep",
|
||||
3 : "Click",
|
||||
4 : "Kick",
|
||||
5 : "Snare",
|
||||
6 : "MetroFile"
|
||||
}
|
||||
|
||||
metro: {tempo: 120, cricket: false, sound: "Beep" }
|
||||
noAudioUsers : {}
|
||||
|
||||
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)
|
||||
this.listenTo(context.MixerActions.syncTracks, this.onSyncTracks)
|
||||
this.listenTo(context.MixerActions.mixerModeChanged, this.onMixerModeChanged)
|
||||
this.listenTo(context.MixerActions.loopChanged, this.onLoopChanged)
|
||||
this.listenTo(context.MixerActions.openMetronome, this.onOpenMetronome)
|
||||
this.listenTo(context.MixerActions.metronomeChanged, this.onMetronomeChanged)
|
||||
this.listenTo(context.MixerActions.deadUserRemove, this.onDeadUserRemove)
|
||||
|
||||
context.JK.HandleVolumeChangeCallback2 = @handleVolumeChangeCallback
|
||||
context.JK.HandleMetronomeCallback2 = @handleMetronomeCallback
|
||||
context.JK.HandleBridgeCallback2 = @handleBridgeCallback
|
||||
context.JK.HandleBackingTrackSelectedCallback2 = @handleBackingTrackSelectedCallback
|
||||
|
||||
|
||||
issueChange: () ->
|
||||
@trigger({session: @session, mixers: @mixers})
|
||||
|
||||
handleVolumeChangeCallback: (mixerId, isLeft, value, isMuted) ->
|
||||
# TODO
|
||||
# Visually update mixer
|
||||
# There is no need to actually set the back-end mixer value as the
|
||||
# back-end will already have updated the audio mixer directly prior to sending
|
||||
# me this event. I simply need to visually show the new fader position.
|
||||
# TODO: Use mixer's range
|
||||
#faderValue = percentFromMixerValue(-80, 20, value);
|
||||
#context.JK.FaderHelpers.setFaderValue(mixerId, faderValue);
|
||||
#var $muteControl = $('[control="mute"][mixer-id="' + mixerId + '"]');
|
||||
#_toggleVisualMuteControl($muteControl, isMuted);
|
||||
logger.debug("volume change")
|
||||
|
||||
|
||||
handleMetronomeCallback: (args) ->
|
||||
logger.debug("MetronomeCallback: ", args)
|
||||
@metro.tempo = args.bpm
|
||||
@metro.cricket = args.cricket;
|
||||
@metro.sound = @METRO_SOUND_LOOKUP[args.sound];
|
||||
|
||||
# This isn't actually there, so we rely on the metroSound as set from select on form:
|
||||
# metroSound = args.sound
|
||||
SessionActions.syncWithServer()
|
||||
|
||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||
|
||||
@issueChange()
|
||||
|
||||
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];
|
||||
mode = vuInfo[2];
|
||||
leftValue = vuInfo[3];
|
||||
leftClipping = vuInfo[4];
|
||||
rightValue = vuInfo[5];
|
||||
rightClipping = vuInfo[6];
|
||||
# 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, mode, (leftValue + 80) / 80, leftClipping, (rightValue + 80) / 80, rightClipping)
|
||||
#@mixers.updateVU(mixerId + "_vur", (rightValue + 80) / 80, rightClipping)
|
||||
|
||||
|
||||
handleBackingTrackSelectedCallback: () ->
|
||||
logger.debug("backing track selected")
|
||||
|
||||
onAppInit: (@app) ->
|
||||
@gearUtils = context.JK.GearUtilsInstance
|
||||
@sessionUtils = context.JK.SessionUtils
|
||||
|
||||
context.jamClient.SetVURefreshRate(150)
|
||||
context.jamClient.RegisterVolChangeCallBack("JK.HandleVolumeChangeCallback2")
|
||||
context.jamClient.setMetronomeOpenCallback("JK.HandleMetronomeCallback2")
|
||||
|
||||
|
||||
sessionEnded: () ->
|
||||
@noAudioUsers = {}
|
||||
|
||||
onSessionChange: (session) ->
|
||||
|
||||
@sessionEnded() unless session.inSession()
|
||||
|
||||
@session = session
|
||||
|
||||
@masterMixers = context.jamClient.SessionGetAllControlState(true);
|
||||
@personalMixers = context.jamClient.SessionGetAllControlState(false);
|
||||
|
||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||
|
||||
@issueChange()
|
||||
|
||||
onMute: (mixers, muting) ->
|
||||
|
||||
for mixer in mixers
|
||||
@mixers.mute(mixer.id, mixer.mode, muting);
|
||||
|
||||
# simulate a state change to cause a UI redraw
|
||||
@issueChange()
|
||||
|
||||
onFaderChanged: (data, mixers, groupId) ->
|
||||
|
||||
@mixers.faderChanged(data, mixers, groupId)
|
||||
|
||||
@issueChange()
|
||||
|
||||
onPanChanged: (data, mixers, groupId) ->
|
||||
@mixers.panChanged(data, mixers, groupId)
|
||||
|
||||
@issueChange()
|
||||
|
||||
onLoopChanged: (mixer, shouldLoop) ->
|
||||
@mixers.loopChanged(mixer, shouldLoop)
|
||||
|
||||
onOpenMetronome: () ->
|
||||
context.jamClient.SessionStopPlay()
|
||||
context.jamClient.SessionOpenMetronome(@mixers.metro.tempo, @mixers.metro.sound, 1, 0)
|
||||
|
||||
onMetronomeChanged: (tempo, sound) ->
|
||||
logger.debug("onMetronomeChanged", tempo, sound)
|
||||
|
||||
@metro.tempo = tempo
|
||||
@metro.sound = sound
|
||||
context.jamClient.SessionSetMetronome(@metro.tempo, @metro.sound, 1, 0);
|
||||
|
||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||
@issueChange()
|
||||
|
||||
onDeadUserRemove: (clientId) ->
|
||||
return unless @session.inSession()
|
||||
|
||||
participant = @session.participantsEverSeen[clientId];
|
||||
|
||||
if participant?
|
||||
logger.debug("todo :notify dead user")
|
||||
# XXX TODO trigger some notification store
|
||||
|
||||
#app.notify({
|
||||
# "title": ALERT_TYPES[type].title,
|
||||
# "text": participant.user.name + " is no longer sending audio.",
|
||||
# "icon_url": context.JK.resolveAvatarUrl(participant.user.photo_url)
|
||||
#});
|
||||
|
||||
@noAudioUsers[clientId] = true
|
||||
|
||||
@issueChange()
|
||||
|
||||
onInitGain: (mixer) ->
|
||||
@mixers.initGain(mixer)
|
||||
|
||||
onInitPan: (mixer) ->
|
||||
@mixers.initPan(mixer)
|
||||
|
||||
onMixersChanged: (type, text) ->
|
||||
@masterMixers = context.jamClient.SessionGetAllControlState(true);
|
||||
@personalMixers = context.jamClient.SessionGetAllControlState(false);
|
||||
|
||||
logger.debug("MixerStore: onMixersChanged")
|
||||
|
||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||
|
||||
SessionActions.mixersChanged.trigger(type, text, @mixers.getTrackInfo())
|
||||
|
||||
@issueChange()
|
||||
|
||||
onMixerModeChanged: (mode) ->
|
||||
if mode == MIX_MODES.MASTER
|
||||
@app.layout.showDialog('session-master-mix-dialog') unless @app.layout.isDialogShowing('session-master-mix-dialog')
|
||||
else
|
||||
@app.layout.closeDialog('session-master-mix-dialog') if @app.layout.isDialogShowing('session-master-mix-dialog')
|
||||
|
||||
onSyncTracks: () ->
|
||||
logger.debug("MixerStore: onSyncTracks")
|
||||
unless @session.inSession()
|
||||
logger.debug("dropping queued up sync tracks because no longer in session")
|
||||
return
|
||||
|
||||
allTracks = @mixers.getTrackInfo()
|
||||
|
||||
inputTracks = allTracks.userTracks;
|
||||
backingTracks = allTracks.backingTracks;
|
||||
metronomeTracks = allTracks.metronomeTracks;
|
||||
|
||||
# create a trackSync request based on backend data
|
||||
syncTrackRequest = {}
|
||||
syncTrackRequest.client_id = @app.clientId
|
||||
syncTrackRequest.tracks = inputTracks
|
||||
syncTrackRequest.backing_tracks = backingTracks
|
||||
syncTrackRequest.metronome_open = metronomeTracks.length > 0
|
||||
syncTrackRequest.id = @session.id()
|
||||
|
||||
rest.putTrackSyncChange(syncTrackRequest)
|
||||
.fail((jqXHR)=>
|
||||
if jqXHR.status != 404
|
||||
@app.notify({
|
||||
"title": "Can't Sync Local Tracks",
|
||||
"text": "The client is unable to sync local track information with the server. You should rejoin the session to ensure a good experience.",
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
})
|
||||
|
||||
else
|
||||
logger.debug("Unable to sync local tracks because session is gone.")
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
@RecordingStore = Reflux.createStore(
|
||||
{
|
||||
listenables: @RecordingActions
|
||||
recordingWindow: null
|
||||
|
||||
|
||||
init: ->
|
||||
# Register with the app store to get @app
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
|
||||
onInitModel: (recordingModel) ->
|
||||
@recordingModel = recordingModel
|
||||
this.trigger({isRecording: @recordingModel.isRecording()})
|
||||
|
||||
onStartRecording: () ->
|
||||
@recordingModel.startRecording()
|
||||
|
||||
onStopRecording: () ->
|
||||
@recordingModel.stopRecording()
|
||||
|
||||
onStartingRecording: (details) ->
|
||||
details.cause = 'starting'
|
||||
this.trigger(details)
|
||||
|
||||
@popupRecordingControls() unless @recordingWindow?
|
||||
|
||||
onStartedRecording: (details) ->
|
||||
details.cause = 'started'
|
||||
this.trigger(details)
|
||||
|
||||
@popupRecordingControls() unless @recordingWindow?
|
||||
|
||||
onStoppingRecording: (details) ->
|
||||
details.cause = 'stopping'
|
||||
this.trigger(details)
|
||||
|
||||
onStoppedRecording: (details) ->
|
||||
details.cause = 'stopped'
|
||||
this.trigger(details)
|
||||
|
||||
onAbortedRecording: (details) ->
|
||||
details.cause = 'aborted'
|
||||
this.trigger(details)
|
||||
|
||||
onOpenRecordingControls: () ->
|
||||
logger.debug("recording controls opening")
|
||||
|
||||
if @recordingWindow?
|
||||
@recordingWindow.close()
|
||||
|
||||
@popupRecordingControls()
|
||||
|
||||
onRecordingControlsClosed: () ->
|
||||
logger.debug("recording controls closed")
|
||||
@recordingWindow = null
|
||||
|
||||
popupRecordingControls: () ->
|
||||
@recordingWindow = window.open("/popups/recording-controls", 'Recording', 'scrollbars=yes,toolbar=no,status=no,height=315,width=350')
|
||||
@recordingWindow.ParentRecordingStore = context.RecordingStore
|
||||
@recordingWindow.ParentIsRecording = @recordingModel.isRecording()
|
||||
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
@SessionMediaTracksStore = 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,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,61 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
rest = context.JK.Rest()
|
||||
|
||||
@SessionNotificationStore = Reflux.createStore(
|
||||
{
|
||||
listenables: @NotificationActions
|
||||
|
||||
notifications: []
|
||||
count: 0
|
||||
|
||||
issueChange: () ->
|
||||
@trigger(@notifications)
|
||||
|
||||
onClear: () ->
|
||||
@notifications = []
|
||||
@issueChange()
|
||||
|
||||
onSessionEnded: () ->
|
||||
@notifications = []
|
||||
@issueChange()
|
||||
|
||||
processNotification: (notification) ->
|
||||
notification.id = ++@count
|
||||
|
||||
title = 'n/a'
|
||||
extra = null
|
||||
|
||||
if notification.backend_detail?
|
||||
if notification.backend_detail == 'Network Issues'
|
||||
title = 'Network Issues'
|
||||
extra = notification.msg
|
||||
else
|
||||
title = notification.msg
|
||||
extra = notification.backend_detail
|
||||
else
|
||||
title = notification.msg
|
||||
|
||||
detail = if notification.detail? && notification.detail != "" then notification.detail else null
|
||||
|
||||
data =
|
||||
title: title
|
||||
extra: extra
|
||||
detail: detail
|
||||
help: notification.help
|
||||
|
||||
@notifications.unshift(data)
|
||||
|
||||
if @notifications.length > 100
|
||||
@notifications.pop();
|
||||
@issueChange()
|
||||
|
||||
onBackendNotification: (notification) ->
|
||||
@processNotification(notification)
|
||||
|
||||
onFrontendNotification: (notification) ->
|
||||
@processNotification(notification)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
@SessionOtherTracksStore = 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)
|
||||
}
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,7 +18,7 @@
|
|||
context.JK = context.JK || {};
|
||||
var logger = context.JK.logger;
|
||||
|
||||
context.JK.RecordingModel = function(app, sessionModel, _rest, _jamClient) {
|
||||
context.JK.RecordingModel = function(app, _rest, _jamClient) {
|
||||
var currentRecording = null; // the JSON response from the server for a recording
|
||||
var currentOrLastRecordingId = null;
|
||||
var currentRecordingId = null;
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
var waitingOnStopTimer = null;
|
||||
var jamClient = _jamClient;
|
||||
|
||||
var sessionModel = sessionModel;
|
||||
var sessionId = null;
|
||||
var $self = $(this);
|
||||
|
||||
function isRecording (recordingId) {
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
}
|
||||
|
||||
/** called every time a session is joined, to ensure clean state */
|
||||
function reset() {
|
||||
function reset(_sessionId) {
|
||||
currentlyRecording = false;
|
||||
waitingOnServerStop = false;
|
||||
waitingOnClientStop = false;
|
||||
|
|
@ -57,9 +57,11 @@
|
|||
currentRecording = null;
|
||||
currentRecordingId = null;
|
||||
stoppingRecording = false;
|
||||
sessionId = _sessionId
|
||||
}
|
||||
|
||||
|
||||
|
||||
function groupTracksToClient(recording) {
|
||||
// group N tracks to the same client Id
|
||||
var groupedTracks = {};
|
||||
|
|
@ -84,7 +86,9 @@
|
|||
currentlyRecording = true;
|
||||
stoppingRecording = false;
|
||||
|
||||
currentRecording = rest.startRecording({"music_session_id": sessionModel.id()})
|
||||
context.RecordingActions.startingRecording({isRecording: false})
|
||||
|
||||
currentRecording = rest.startRecording({"music_session_id": sessionId})
|
||||
.done(function(recording) {
|
||||
currentRecordingId = recording.id;
|
||||
currentOrLastRecordingId = recording.id;
|
||||
|
|
@ -94,8 +98,10 @@
|
|||
jamClient.StartRecording(recording["id"], groupedTracks);
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
$self.triggerHandler('startedRecording', { clientId: app.clientId, reason: 'rest', detail: arguments });
|
||||
var details = { clientId: app.clientId, reason: 'rest', detail: arguments, isRecording: false }
|
||||
$self.triggerHandler('startedRecording', details);
|
||||
currentlyRecording = false;
|
||||
context.RecordingActions.startedRecording(details);
|
||||
})
|
||||
|
||||
|
||||
|
|
@ -116,6 +122,7 @@
|
|||
waitingOnStopTimer = setTimeout(timeoutTransitionToStop, 5000);
|
||||
|
||||
$self.triggerHandler('stoppingRecording', {reason: reason, detail: detail});
|
||||
context.RecordingActions.stoppingRecording({reason: reason, detail: detail, isRecording:true})
|
||||
|
||||
// this path assumes that the currentRecording info has, or can be, retrieved
|
||||
// failure for currentRecording is handled elsewhere
|
||||
|
|
@ -145,7 +152,9 @@
|
|||
else {
|
||||
logger.error("unable to stop recording %o", arguments);
|
||||
transitionToStopped();
|
||||
$self.triggerHandler('stoppedRecording', {'recordingId': recording.id, 'reason' : 'rest', 'details' : arguments});
|
||||
var details = {'recordingId': recording.id, 'reason' : 'rest', 'details' : arguments, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -168,7 +177,9 @@
|
|||
|
||||
if(!waitingOnClientStop && !waitingOnServerStop) {
|
||||
transitionToStopped();
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: errorReason, detail: errorDetail});
|
||||
var details = {recordingId: recordingId, reason: errorReason, detail: errorDetail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details)
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -198,12 +209,16 @@
|
|||
|
||||
|
||||
if(success) {
|
||||
$self.triggerHandler('startedRecording', {clientId: app.clientId})
|
||||
var details = {clientId: app.clientId, isRecording:true}
|
||||
$self.triggerHandler('startedRecording', details)
|
||||
context.RecordingActions.startedRecording(details)
|
||||
}
|
||||
else {
|
||||
currentlyRecording = false;
|
||||
logger.error("unable to start the recording %o, %o", reason, detail);
|
||||
$self.triggerHandler('startedRecording', { clientId: app.clientId, reason: reason, detail: detail});
|
||||
var details = { clientId: app.clientId, reason: reason, detail: detail, isRecording: false}
|
||||
$self.triggerHandler('startedRecording', details);
|
||||
context.RecordingActions.startedRecording(details)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +236,9 @@
|
|||
else {
|
||||
transitionToStopped();
|
||||
logger.error("backend unable to stop the recording %o, %o", reason, detail);
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: reason, detail : detail});
|
||||
var details = {recordingId: recordingId, reason: reason, detail : detail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -242,9 +259,14 @@
|
|||
currentOrLastRecordingId = recording.id;
|
||||
});
|
||||
|
||||
$self.triggerHandler('startingRecording', {recordingId: recordingId});
|
||||
var details = {recordingId: recordingId, isRecording: false}
|
||||
$self.triggerHandler('startingRecording', details);
|
||||
context.RecordingActions.startingRecording(details)
|
||||
currentlyRecording = true;
|
||||
$self.triggerHandler('startedRecording', {clientId: clientId, recordingId: recordingId});
|
||||
|
||||
details = {clientId: clientId, recordingId: recordingId, isRecording: true}
|
||||
$self.triggerHandler('startedRecording', details);
|
||||
context.RecordingActions.startedRecording(details)
|
||||
}
|
||||
|
||||
function handleRecordingStopped(recordingId, result) {
|
||||
|
|
@ -253,7 +275,10 @@
|
|||
var detail = result.detail;
|
||||
|
||||
|
||||
$self.triggerHandler('stoppingRecording', {recordingId: recordingId, reason: reason, detail: detail });
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: true }
|
||||
$self.triggerHandler('stoppingRecording', details);
|
||||
context.RecordingActions.stoppingRecording(details)
|
||||
|
||||
// the backend says the recording must be stopped.
|
||||
// tell the server to stop it too
|
||||
rest.stopRecording({
|
||||
|
|
@ -265,18 +290,26 @@
|
|||
.fail(function(jqXHR, textStatus, errorMessage) {
|
||||
if(jqXHR.status == 422) {
|
||||
logger.debug("recording already stopped %o", arguments);
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: reason, detail: detail});
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
else if(jqXHR.status == 404) {
|
||||
logger.debug("recording is already deleted %o", arguments);
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: reason, detail: detail});
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
else {
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: textStatus, detail: errorMessage});
|
||||
var details = {recordingId: recordingId, reason: textStatus, detail: errorMessage, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
}
|
||||
})
|
||||
.done(function() {
|
||||
$self.triggerHandler('stoppedRecording', {recordingId: recordingId, reason: reason, detail: detail});
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false}
|
||||
$self.triggerHandler('stoppedRecording', details);
|
||||
context.RecordingActions.stoppedRecording(details)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -287,7 +320,9 @@
|
|||
|
||||
stoppingRecording = false;
|
||||
|
||||
$self.triggerHandler('abortedRecording', {recordingId: recordingId, reason: reason, detail: detail });
|
||||
var details = {recordingId: recordingId, reason: reason, detail: detail, isRecording: false }
|
||||
$self.triggerHandler('abortedRecording', details);
|
||||
context.RecordingActions.abortedRecording(details)
|
||||
// the backend says the recording must be stopped.
|
||||
// tell the server to stop it too
|
||||
rest.stopRecording({
|
||||
|
|
|
|||
|
|
@ -1425,13 +1425,13 @@
|
|||
var metronome = {}
|
||||
$('.session-recording-name').text(name);//sessionModel.getCurrentSession().backing_track_path);
|
||||
|
||||
var noCorrespondingTracks = false;
|
||||
var mixer = metronomeTrackMixers[0]
|
||||
var preMasteredClass = "";
|
||||
// find the track or tracks that correspond to the mixer
|
||||
var correspondingTracks = []
|
||||
correspondingTracks.push(metronome);
|
||||
|
||||
var noCorrespondingTracks = false;
|
||||
var mixer = metronomeTrackMixers[0]
|
||||
var preMasteredClass = "";
|
||||
// find the track or tracks that correspond to the mixer
|
||||
var correspondingTracks = []
|
||||
correspondingTracks.push(metronome);
|
||||
|
||||
if(correspondingTracks.length == 0) {
|
||||
noCorrespondingTracks = true;
|
||||
app.notify({
|
||||
|
|
@ -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.
|
||||
|
|
@ -2158,8 +2158,8 @@
|
|||
setFormFromMetronome();
|
||||
|
||||
// This isn't actually there, so we rely on the metroSound as set from select on form:
|
||||
// metroSound = args.sound
|
||||
context.JK.CurrentSessionModel.refreshCurrentSession(true);
|
||||
// metroSound = args.sound
|
||||
context.JK.CurrentSessionModel.refreshCurrentSession(true);
|
||||
}
|
||||
|
||||
function handleVolumeChangeCallback(mixerId, isLeft, value, isMuted) {
|
||||
|
|
@ -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];
|
||||
|
|
@ -3025,6 +3025,7 @@
|
|||
function closeMetronomeTrack() {
|
||||
rest.closeMetronome({id: sessionModel.id()})
|
||||
.done(function() {
|
||||
logger.debug("session: SessionCloseMetronome")
|
||||
context.jamClient.SessionCloseMetronome();
|
||||
sessionModel.refreshCurrentSession(true);
|
||||
})
|
||||
|
|
@ -3285,7 +3286,7 @@
|
|||
'beforeLeave' : beforeLeave,
|
||||
'beforeDisconnect' : beforeDisconnect,
|
||||
};
|
||||
app.bindScreen('session', screenBindings);
|
||||
//app.bindScreen('session', screenBindings);
|
||||
|
||||
$recordingManagerViewer = $('#recording-manager-viewer');
|
||||
$screen = $('#session-screen');
|
||||
|
|
|
|||
|
|
@ -263,8 +263,8 @@
|
|||
|
||||
var recordingId = payload.recording_id;
|
||||
|
||||
if(recordingId && context.JK.CurrentSessionModel.recordingModel.isRecording(recordingId)) {
|
||||
context.JK.CurrentSessionModel.recordingModel.onServerStopRecording(recordingId);
|
||||
if(recordingId && context.RecordingStore.recordingModel.isRecording(recordingId)) {
|
||||
context.RecordingStore.recordingModel.onServerStopRecording(recordingId);
|
||||
}
|
||||
else {
|
||||
app.notify({
|
||||
|
|
@ -305,11 +305,11 @@
|
|||
|
||||
logger.debug("Handling SOURCE_UP_REQUESTED msg " + JSON.stringify(payload));
|
||||
|
||||
var current_session_id = context.JK.CurrentSessionModel.id();
|
||||
var current_session_id = context.SessionStore.id();
|
||||
|
||||
if (!current_session_id) {
|
||||
// we are not in a session
|
||||
var last_session = context.JK.CurrentSessionModel.getCurrentOrLastSession();
|
||||
var last_session = context.SessionStore.getCurrentOrLastSession();
|
||||
if(last_session && last_session.id == payload.music_session) {
|
||||
// the last session we were in was responsible for this message. not that odd at all
|
||||
logger.debug("SOURCE_UP_REQUESTED came in for session_id" + payload.music_session + ", but was dropped because we have left that session")
|
||||
|
|
@ -328,7 +328,7 @@
|
|||
'', payload.bitrate)
|
||||
}
|
||||
else {
|
||||
var last_session = context.JK.CurrentSessionModel.getCurrentOrLastSession();
|
||||
var last_session = context.SessionStore.getCurrentOrLastSession();
|
||||
if(last_session && last_session.id == payload.music_session) {
|
||||
// the last session we were in was responsible for this message. not that odd at all
|
||||
logger.debug("SOURCE_UP_REQUESTED came in for session_id" + payload.music_session + ", but was dropped because we have left that session and are in a new one")
|
||||
|
|
@ -346,11 +346,11 @@
|
|||
function registerSourceDownRequested() {
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SOURCE_DOWN_REQUESTED, function(header, payload) {
|
||||
logger.debug("Handling SOURCE_DOWN_REQUESTED msg " + JSON.stringify(payload));
|
||||
var current_session_id = context.JK.CurrentSessionModel.id();
|
||||
var current_session_id = context.SessionStore.id();
|
||||
|
||||
if (!current_session_id) {
|
||||
// we are not in a session
|
||||
var last_session = context.JK.CurrentSessionModel.getCurrentOrLastSession();
|
||||
var last_session = context.SessionStore.getCurrentOrLastSession();
|
||||
if(last_session && last_session.id == payload.music_session) {
|
||||
// the last session we were in was responsible for this message. not that odd at all
|
||||
logger.debug("SOURCE_DOWN_REQUESTED came in for session_id" + payload.music_session + ", but was dropped because we have left that session")
|
||||
|
|
@ -367,7 +367,7 @@
|
|||
context.jamClient.SessionLiveBroadcastStop();
|
||||
}
|
||||
else {
|
||||
var last_session = context.JK.CurrentSessionModel.getCurrentOrLastSession();
|
||||
var last_session = context.SessionStore.getCurrentOrLastSession();
|
||||
if(last_session && last_session.id == payload.music_session) {
|
||||
// the last session we were in was responsible for this message. not that odd at all
|
||||
logger.debug("SOURCE_DOWN_REQUESTED came in for session_id" + payload.music_session + ", but was dropped because we have left that session and are in a new one")
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue