* wip
This commit is contained in:
parent
910b052204
commit
4be1117a02
|
|
@ -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})
|
||||
}
|
||||
else {
|
||||
logger.debug("Unhandled Backend Event type %o, data %o", type, text)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function alertCallback(type, text) {
|
||||
|
|
@ -104,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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')) {
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -334,4 +334,14 @@
|
|||
"MetronomeGroup": 16
|
||||
};
|
||||
|
||||
context.JK.CategoryGroupIds = {
|
||||
"AudioInputMusic" : "AudioInputMusic",
|
||||
"AudioInputChat" : "AudioInputChat",
|
||||
"UserMusic" : "UserMusic",
|
||||
"UserChat" : "UserChat",
|
||||
"UserMedia" : "UserMedia",
|
||||
"MediaTrack" : "MediaTrack",
|
||||
"Metronome" : "Metronome"
|
||||
}
|
||||
|
||||
})(window,jQuery);
|
||||
|
|
@ -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')
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,9 +3,12 @@
|
|||
//= 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
|
||||
|
|
@ -14,6 +17,7 @@
|
|||
//= require jam_rest
|
||||
//= require ga
|
||||
//= require utils
|
||||
//= require playbackControls
|
||||
//= require react
|
||||
//= require react_ujs
|
||||
//= require react-init
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@
|
|||
//= 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
|
||||
|
|
@ -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,124 @@
|
|||
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', () =>
|
||||
console.log("@props", @props)
|
||||
MixerActions.loopChanged(@props.backingTracks[0].mixers.mixer, true)
|
||||
)
|
||||
$loop.on('ifUnchecked', () =>
|
||||
MixerActions.loopChanged(@props.backingTracks[0].mixers.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)
|
||||
})
|
||||
|
|
@ -8,7 +8,6 @@ if window.opener
|
|||
|
||||
@PopupRecordingStartStop = React.createClass({
|
||||
|
||||
# this comes from the parent window
|
||||
mixins: mixins
|
||||
|
||||
onRecordingStateChanged: (recordingState) ->
|
||||
|
|
|
|||
|
|
@ -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,80 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionBackingTrack = 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
|
||||
"backing-track" : 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.shortFilename}</div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" 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('.screen')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionJamTrack = 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" : 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.part}</div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" 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:@props.mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
})
|
||||
|
|
@ -1,13 +1,316 @@
|
|||
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: [Reflux.listenTo(@SessionMediaTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
|
||||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
|
||||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
|
||||
backingTracks = mixers.backingTracks
|
||||
jamTracks = mixers.jamTracks
|
||||
recordedTracks = mixers.recordedTracks
|
||||
metronome = mixers.metronome
|
||||
|
||||
state =
|
||||
isRecording: session.isRecording
|
||||
mediaSummary: mixers.mediaSummary
|
||||
backingTracks: backingTracks
|
||||
jamTracks: jamTracks
|
||||
recordedTracks: recordedTracks
|
||||
metronome: metronome
|
||||
|
||||
if state.mediaSummary.mediaOpen
|
||||
if !@state.childWindow?
|
||||
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.childWindow?
|
||||
@state.childWindow.DontAutoCloseMedia = true
|
||||
@state.childWindow.close()
|
||||
state.childWindow = null
|
||||
|
||||
@setState(state)
|
||||
|
||||
|
||||
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>`
|
||||
|
||||
|
||||
for backingTrack in @state.backingTracks
|
||||
mediaTracks.push(`<SessionBackingTrack key={backingTrack.track.id} {...backingTrack} />`)
|
||||
|
||||
for jamTrack in @state.jamTracks
|
||||
mediaTracks.push(`<SessionJamTrack key={jamTrack.id} {...jamTrack} />`)
|
||||
|
||||
for recordedTrack in @state.recordedTracks
|
||||
mediaTracks.push(`<SessionRecordedTrack key={recordedTrack.track.id} {...recordedTrack} />`)
|
||||
|
||||
if @state.metronome
|
||||
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>
|
||||
<div className="session-tracks-scroller">
|
||||
|
||||
{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()
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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 = mixers.mixer.pan
|
||||
|
||||
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="vul" 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('.screen')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
})
|
||||
|
|
@ -15,21 +15,24 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
|||
if session.inSession()
|
||||
participant = session.getParticipant(@app.clientId)
|
||||
|
||||
name = participant.user.name;
|
||||
if participant
|
||||
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)
|
||||
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)
|
||||
|
||||
# todo: sessionModel.setAudioEstablished
|
||||
# todo: sessionModel.setAudioEstablished
|
||||
|
||||
instrumentIcon = context.JK.getInstrumentIcon45(track.instrument_id);
|
||||
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url);
|
||||
instrumentIcon = context.JK.getInstrumentIcon45(track.instrument_id);
|
||||
photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url);
|
||||
|
||||
tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, name: name, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id})
|
||||
tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, name: name, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id})
|
||||
|
||||
# TODO: also deal with chat
|
||||
# TODO: also deal with chat
|
||||
else
|
||||
logger.debug("SessionMyTracks: unable to find participant")
|
||||
|
||||
this.setState(tracks: tracks, session:session)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
context = window
|
||||
|
||||
@SessionNotification = React.createClass({
|
||||
|
||||
render: () ->
|
||||
`<div className="session-notification">
|
||||
<div className="msg">{this.props.msg}</div>
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
|
||||
if @props.detail
|
||||
context.JK.hoverBubble($root, @props.detail)
|
||||
})
|
||||
|
|
@ -1,13 +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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ MixerActions = @MixerActions
|
|||
"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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
context = window
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
|
||||
@SessionOtherTracks = React.createClass({
|
||||
|
||||
|
|
@ -9,6 +9,7 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
|||
|
||||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
noAudioUsers = mixers.noAudioUsers
|
||||
|
||||
participants = []
|
||||
|
||||
|
|
@ -36,7 +37,7 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
|||
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}
|
||||
participantState = {participant:participant, tracks: tracks, name: name, instrumentIcon: instrumentIcon, photoUrl: photoUrl, hasMixer: hasMixer, noAudio: noAudioUsers[participant.client_id]}
|
||||
|
||||
participants.push(participantState)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
|
||||
@SessionRecordedTrack = 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
|
||||
"recorded-track" : 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.userName}</div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="vul" 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:@props.mixers}
|
||||
,
|
||||
{width:235, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$pan,
|
||||
'SessionTrackPanHover',
|
||||
() =>
|
||||
{mixers:@props.mixers}
|
||||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
})
|
||||
|
|
@ -1,39 +1,61 @@
|
|||
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)
|
||||
|
||||
this.setState({inputGroupMixers: inputGroupMixers, chatGroupMixers: chatGroupMixers})
|
||||
|
||||
getInitialState: () ->
|
||||
{mixers: this.props.mixers}
|
||||
{inputGroupMixers: @props.inputGroupMixers, chatGroupMixers: @props.chatGroupMixers}
|
||||
|
||||
handleMute: (e) ->
|
||||
handleAudioInputMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([this.props.mixers.mixer], muting)
|
||||
MixerActions.mute([@state.inputGroupMixers.muteMixer], muting)
|
||||
|
||||
handleMuteCheckbox: (e) ->
|
||||
handleChatInputMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
muting = $(e.currentTarget).is('.enabled')
|
||||
|
||||
MixerActions.mute([@state.chatGroupMixers.muteMixer], muting)
|
||||
|
||||
handleAudioInputMuteCheckbox: (e) ->
|
||||
muting = $(e.target).is(':checked')
|
||||
|
||||
MixerActions.mute([this.props.mixers.mixer, this.props.mixers.oppositeMixer], muting)
|
||||
MixerActions.mute([@state.inputGroupMixers.muteMixer], muting)
|
||||
|
||||
handleChatMuteCheckbox: (e) ->
|
||||
muting = $(e.target).is(':checked')
|
||||
|
||||
MixerActions.mute([@state.chatGroupMixers.muteMixer], muting)
|
||||
|
||||
render: () ->
|
||||
|
||||
monitorMuteMixer = this.props.inputGroupMixers.muteMixer
|
||||
monitorMuteMixer = @state.inputGroupMixers.muteMixer
|
||||
monitorMuteMixerId = monitorMuteMixer?.id
|
||||
monitorVolumeLeft = this.props.inputGroupMixers.mixer?.volume_left
|
||||
monitorVolumeLeft = @state.inputGroupMixers.mixer?.volume_left
|
||||
monitorMuteClasses = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !monitorMuteMixer?.mute
|
||||
'muted' : monitorMuteMixer?.mute
|
||||
})
|
||||
|
||||
chatMuteMixer = this.props.chatGroupMixers.muteMixer
|
||||
chatMuteMixer = @state.chatGroupMixers.muteMixer
|
||||
chatMuteMixerId = chatMuteMixer?.id
|
||||
chatVolumeLeft = this.props.chatGroupMixers.mixer?.volume_left
|
||||
chatVolumeLeft = @state.chatGroupMixers.mixer?.volume_left
|
||||
chatMuteClasses = classNames({
|
||||
'track-icon-mute': true
|
||||
'enabled' : !chatMuteMixer?.mute
|
||||
|
|
@ -45,20 +67,20 @@ MixerActions = @MixerActions
|
|||
<h3>Music</h3>
|
||||
<div className="session-track track">
|
||||
<div className="track-vu-left">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vul" mixers={this.props.inputGroupMixers} />
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vul" mixers={this.state.inputGroupMixers} />
|
||||
</div>
|
||||
<div className="track-vu-right">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vur" mixers={this.props.inputGroupMixers} />
|
||||
<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.props.inputGroupMixers} />
|
||||
<div className={monitorMuteClasses} data-control="mute" data-mixer-id={monitorMuteMixerId} onClick={this.handleMute}/>
|
||||
<SessionTrackGain mixers={this.state.inputGroupMixers} />
|
||||
<div className={monitorMuteClasses} data-control="mute" data-mixer-id={monitorMuteMixerId} onClick={this.handleAudioInputMute}/>
|
||||
|
||||
<input type="checkbox" name="mute"/>
|
||||
<label for="mute">Mute</label>
|
||||
<label htmlFor="mute">Mute</label>
|
||||
</div>
|
||||
|
||||
<div className="textual-help">
|
||||
|
|
@ -72,20 +94,20 @@ MixerActions = @MixerActions
|
|||
<h3>Chat</h3>
|
||||
<div className="session-track track">
|
||||
<div className="track-vu-left">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vul" mixers={this.props.chatGroupMixers} />
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vul" mixers={this.state.chatGroupMixers} />
|
||||
</div>
|
||||
<div className="track-vu-right">
|
||||
<SessionTrackVU orientation="vertical" lightCount={13} lightWidth={3} lightHeight={17} side="vur" mixers={this.props.chatGroupMixers} />
|
||||
<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.props.chatGroupMixers} />
|
||||
<div className={chatMuteClasses} data-control="mute" data-mixer-id={chatMuteMixerId} onClick={this.handleMute}/>
|
||||
<SessionTrackGain mixers={this.state.chatGroupMixers} />
|
||||
<div className={chatMuteClasses} data-control="mute" data-mixer-id={chatMuteMixerId} onClick={this.handleChatMute}/>
|
||||
|
||||
<input type="checkbox" name="mute"/>
|
||||
<label for="mute">Mute</label>
|
||||
<label htmlFor="mute">Mute</label>
|
||||
</div>
|
||||
|
||||
<div className="textual-help">
|
||||
|
|
@ -99,23 +121,40 @@ MixerActions = @MixerActions
|
|||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
# initialize icheck
|
||||
$checkbox = $root.find('input')
|
||||
context.JK.checkbox($checkbox)
|
||||
$checkbox.on('ifChanged', this.handleMuteCheckbox);
|
||||
$chatMuteCheckbox = $root.find('.chat-mixer input')
|
||||
context.JK.checkbox($chatMuteCheckbox)
|
||||
$chatMuteCheckbox.on('ifChanged', @handleChatMuteCheckbox);
|
||||
|
||||
#if this.props.mixers.muteMixer.mute
|
||||
# $checkbox.iCheck('check').attr('checked', true)
|
||||
#else
|
||||
# $checkbox.iCheck('uncheck').attr('checked', false)
|
||||
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
|
||||
$checkbox = $root.find('input')
|
||||
$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)
|
||||
|
||||
#if nextState.mixers.muteMixer?.mute
|
||||
# $checkbox.iCheck('check').attr('checked', true)
|
||||
#else
|
||||
# $checkbox.iCheck('uncheck').attr('checked', false)
|
||||
})
|
||||
|
|
@ -17,12 +17,14 @@ context = window
|
|||
|
||||
lights.push(`<td key={i} width={this.props.lightWidth} height={this.props.lightHeight} className={lightClasses}></td>`)
|
||||
|
||||
tableClasses = classNames('vu', 'horizontal', this.props.side + '-' + this.props.mixers.mixer?.id)
|
||||
tableClasses = classNames('vu', 'horizontal')
|
||||
|
||||
`<table className={tableClasses} data-light-count={this.props.lightCount}>
|
||||
<tr>
|
||||
{lights}
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
{lights}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`
|
||||
else
|
||||
|
||||
|
|
@ -31,11 +33,39 @@ context = window
|
|||
|
||||
lightClasses = classNames('vulight', 'vu' + i, lightClass)
|
||||
|
||||
lights.push(`<tr><td key={i} width={this.props.lightWidth} height={this.props.lightHeight} className={lightClasses}></td></tr>`)
|
||||
lights.push(`<tr key={i}><td width={this.props.lightWidth} height={this.props.lightHeight} className={lightClasses}></td></tr>`)
|
||||
|
||||
tableClasses = classNames('vu', 'vertical', this.props.side + '-' + this.props.mixers.mixer?.id)
|
||||
tableClasses = classNames('vu', 'vertical')
|
||||
|
||||
`<table className={tableClasses} data-light-count={this.props.lightCount}>
|
||||
<tbody>
|
||||
{lights}
|
||||
</tbody>
|
||||
</table>`
|
||||
|
||||
getInitialState: () ->
|
||||
{registered: null}
|
||||
|
||||
registerVU: (mixerId) ->
|
||||
|
||||
return if @state.registered? || !mixerId?
|
||||
|
||||
$root = $(this.getDOMNode())
|
||||
|
||||
context.JK.VuHelpers.registerVU('single', mixerId, @render, @props.orientation == 'horizontal', @props.lightCount, $root.find('td'))
|
||||
|
||||
@setState(registered: {mixerId: mixerId, ptr: @render})
|
||||
|
||||
|
||||
componentWillReceiveProps: (nextProps) ->
|
||||
@registerVU(nextProps.mixers.mixer?.id)
|
||||
|
||||
componentDidMount: () ->
|
||||
@registerVU(@props.mixers.mixer?.id)
|
||||
|
||||
componentWillUnmount: () ->
|
||||
console.log("UNMOUNTING")
|
||||
if @state.registered?
|
||||
context.JK.VuHelpers.unregisterVU(@state.registered.mixerId, @state.registered.ptr)
|
||||
|
||||
})
|
||||
|
|
@ -58,7 +58,7 @@ MixerActions = @MixerActions
|
|||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
|
||||
<input type="checkbox" name="mute"/>
|
||||
<label for="mute">Mute</label>
|
||||
<label htmlFor="mute">Mute</label>
|
||||
</div>
|
||||
|
||||
<div className="textual-help">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
@SessionVolumeSettingsBtn = React.createClass({
|
||||
|
||||
|
|
@ -20,7 +21,7 @@ context = window
|
|||
$root,
|
||||
'SessionSelfVolumeHover',
|
||||
() =>
|
||||
{inputGroupMixers: this.state.mixers.getAudioInputChatGroupMixer(), chatGroupMixers: this.state.mixers.getChatGroupMixer()}
|
||||
{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,11 @@
|
|||
context = window
|
||||
|
||||
@MediaPlaybackActions = Reflux.createActions({
|
||||
playbackStateChange: {}
|
||||
positionUpdate:{}
|
||||
mediaStartPlay: {}
|
||||
mediaStopPlay: {}
|
||||
mediaPausePlay: {}
|
||||
mediaChangePosition: {}
|
||||
currentTimeChanged: {}
|
||||
})
|
||||
|
|
@ -9,4 +9,8 @@ context = window
|
|||
mixersChanged: {}
|
||||
syncTracks: {}
|
||||
mixerModeChanged: {}
|
||||
loopChanged: {}
|
||||
openMetronome: {}
|
||||
metronomeChanged: {}
|
||||
deadUserRemove: {}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
context = window
|
||||
|
||||
@NotificationActions = Reflux.createActions({
|
||||
clear:{}
|
||||
backendNotification: {}
|
||||
frontendNotification: {}
|
||||
sessionEnded: {}
|
||||
})
|
||||
|
||||
|
|
@ -8,4 +8,15 @@ context = window
|
|||
syncWithServer: {}
|
||||
toggleSessionVideo : {}
|
||||
audioResync: {}
|
||||
openBackingTrack: {}
|
||||
closeMedia: {}
|
||||
updateSession: {}
|
||||
downloadingJamTrack : {}
|
||||
openMetronome: {}
|
||||
showNativeMetronomeGui: {}
|
||||
metronomeCricketChange: {}
|
||||
windowBackgrounded: {}
|
||||
broadcastFailure: {}
|
||||
broadcastSuccess: {}
|
||||
broadcastStopped: {}
|
||||
})
|
||||
|
|
@ -1,31 +1,25 @@
|
|||
context = window
|
||||
|
||||
ChannelGroupIds = context.JK.ChannelGroupIds
|
||||
CategoryGroupIds = context.JK.CategoryGroupIds
|
||||
MIX_MODES = context.JK.MIX_MODES;
|
||||
|
||||
|
||||
@MixerHelper = class MixerHelper
|
||||
|
||||
constructor: (@session, @masterMixers, @personalMixers, @mixMode) ->
|
||||
constructor: (@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixMode) ->
|
||||
@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()
|
||||
|
||||
updateMixers: (type, text, @masterMixers, @personalMixers) ->
|
||||
|
||||
@organize()
|
||||
|
||||
|
||||
|
||||
|
||||
organize: () ->
|
||||
for masterMixer in @masterMixers
|
||||
@allMixers['M' + masterMixer.id] = masterMixer; # populate allMixers by mixer.id
|
||||
|
|
@ -84,20 +78,20 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
so, let's group up all mixers by type, and then ask them to be rendered
|
||||
###
|
||||
|
||||
recordingTrackMixers = []
|
||||
backingTrackMixers = []
|
||||
jamTrackMixers = []
|
||||
metronomeTrackMixers = []
|
||||
adhocTrackMixers = []
|
||||
@recordingTrackMixers = []
|
||||
@backingTrackMixers = []
|
||||
@jamTrackMixers = []
|
||||
@metronomeTrackMixers = []
|
||||
@adhocTrackMixers = []
|
||||
|
||||
groupByType = (mixers, isLocalMixer) ->
|
||||
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)
|
||||
@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
|
||||
|
|
@ -119,7 +113,7 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
break
|
||||
|
||||
if isJamTrack
|
||||
jamTrackMixers.push(mixer)
|
||||
@jamTrackMixers.push(mixer)
|
||||
else
|
||||
isBackingTrack = false
|
||||
if recordedBackingTracks
|
||||
|
|
@ -135,21 +129,21 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
break
|
||||
|
||||
if isBackingTrack
|
||||
backingTrackMixers.push(mixer)
|
||||
@backingTrackMixers.push(mixer)
|
||||
else
|
||||
# couldn't resolve this as a JamTrack or Backing track, must be a normal recorded file
|
||||
recordingTrackMixers.push(mixer)
|
||||
@recordingTrackMixers.push(mixer)
|
||||
|
||||
else if mediaType == 'PeerMediaTrack' || mediaType == 'BackingTrack'
|
||||
backingTrackMixers.push(mixer)
|
||||
@backingTrackMixers.push(mixer)
|
||||
else if mediaType == 'JamTrack'
|
||||
jamTrackMixers.push(mixer);
|
||||
@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)
|
||||
@recordingTrackMixers.push(mixer)
|
||||
else
|
||||
logger.warn("Unknown track type: " + mediaType)
|
||||
adhocTrackMixers.push(mixer)
|
||||
@adhocTrackMixers.push(mixer)
|
||||
|
||||
groupByType(localMediaMixers, true);
|
||||
groupByType(peerLocalMediaMixers, false);
|
||||
|
|
@ -170,17 +164,261 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
checkMetronomeTransition();
|
||||
###
|
||||
|
||||
if adhocTrackMixers.length > 0
|
||||
@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: {mixer: mixer, oppositeMixer: oppositeMixer, vuMixer: mixer, muteMixer: mixer}
|
||||
|
||||
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?
|
||||
|
||||
oppositeMixer = @getMixerByResourceId(mixer.rid, MIX_MODES.PERSONAL)
|
||||
|
||||
data =
|
||||
name: jamTrackName
|
||||
part: part
|
||||
isOpener: isOpener
|
||||
instrumentIcon: instrumentIcon
|
||||
track: oneOfTheTracks
|
||||
mixers: {mixer: mixer, oppositeMixer: oppositeMixer, vuMixer: mixer, muteMixer: mixer}
|
||||
|
||||
_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?
|
||||
oppositeMixer = @getMixerByResourceId(mixer.rid, MIX_MODES.PERSONAL)
|
||||
|
||||
isOpener = mixer.group_id == ChannelGroupIds.MediaTrackGroup
|
||||
data =
|
||||
recordingName: recordingName
|
||||
isOpener: isOpener
|
||||
userName: userName
|
||||
instrumentIcon: instrumentIcon
|
||||
track: oneOfTheTracks
|
||||
mixers: {mixer: mixer, oppositeMixer: oppositeMixer, vuMixer: mixer, muteMixer: mixer}
|
||||
|
||||
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
|
||||
|
||||
mixersForGroupIds: (groupIds, mixMode) ->
|
||||
foundMixers = []
|
||||
mixers = if mixMode == MIX_MODES.MASTER then @masterMixers else @personalMixers;
|
||||
|
||||
for mixer in mixers
|
||||
groupIdLen = groupIds.length
|
||||
for i in groupIdLen
|
||||
if mixer.group_id == groupIds[i]
|
||||
for groupId in groupIds
|
||||
if mixer.group_id == groupId
|
||||
foundMixers.push(mixer)
|
||||
|
||||
foundMixers
|
||||
|
|
@ -231,6 +469,20 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
|
||||
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) ->
|
||||
mixer = null # what is the best mixer for this track/client ID?
|
||||
oppositeMixer = null # what is the corresponding mixer in the opposite mode?
|
||||
|
|
@ -373,6 +625,17 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
context.trackVolumeObject.pan = context.JK.PanHelpers.convertPercentToPan(panPercent);
|
||||
context.jamClient.SessionSetControlState(mixer.id, mixer.mode);
|
||||
|
||||
loopChanged: (mixer, shouldLoop) ->
|
||||
console.log("mixer, shouldLoop", 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
|
||||
|
|
@ -438,15 +701,16 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
@currentMixerRangeMax = mixer.range_high;
|
||||
mixer
|
||||
|
||||
updateVU: (mixerId, value, isClipping) ->
|
||||
selector = null
|
||||
pureMixerId = mixerId.replace("_vul", "")
|
||||
pureMixerId = pureMixerId.replace("_vur", "")
|
||||
mixer = @getMixer(pureMixerId, @mixMode)
|
||||
updateVU: (mixerId, leftValue, leftClipping, rightValue, rightClipping) ->
|
||||
mixer = @getMixer(mixerId, @mixMode)
|
||||
unless mixer
|
||||
# try again, in the opposite mode (awful that this is necessary)
|
||||
mixer = @getMixer(pureMixerId, !@mixMode)
|
||||
mixer = @getMixer(mixerId, !@mixMode)
|
||||
|
||||
if mixer
|
||||
context.JK.VuHelpers.updateVU3(mixer, leftValue, leftClipping, rightValue, rightClipping)
|
||||
|
||||
###
|
||||
if mixer
|
||||
if mixer.stereo # // stereo track
|
||||
if mixerId.substr(-4) == "_vul"
|
||||
|
|
@ -459,30 +723,37 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
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: (groupId, mode) ->
|
||||
mixers = @mixersForGroupId(groupId, MIX_MODES.PERSONAL)
|
||||
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 {}
|
||||
|
||||
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 {}
|
||||
else
|
||||
mixer = mixers[0]
|
||||
{
|
||||
mixer: mixer,
|
||||
muteMixer : mixer,
|
||||
vuMixer: mixer,
|
||||
oppositeMixer: mixer
|
||||
mixer: found,
|
||||
muteMixer : found,
|
||||
vuMixer: found,
|
||||
oppositeMixer: found
|
||||
}
|
||||
|
||||
console.log("M MIXERS", @masterMixers)
|
||||
console.log("P MIXERS", @personalMixers)
|
||||
getAudioInputCategoryMixer: (mode) ->
|
||||
@getGroupMixer(CategoryGroupIds.AudioInputMusic, mode)
|
||||
|
||||
getAudioInputChatGroupMixer: () ->
|
||||
@getGroupMixer(ChannelGroupIds.AudioInputMusicGroup, MIX_MODES.PERSONAL)
|
||||
|
||||
getChatGroupMixer: () ->
|
||||
@getGroupMixer(ChannelGroupIds.AudioInputChatGroup, MIX_MODES.PERSONAL)
|
||||
getChatCategoryMixer: (mode) ->
|
||||
@getGroupMixer(CategoryGroupIds.AudioInputChat, mode)
|
||||
|
|
@ -2,10 +2,12 @@ context = window
|
|||
|
||||
@SessionHelper = class SessionHelper
|
||||
|
||||
constructor: (app, session, isRecording) ->
|
||||
constructor: (app, session, participantsEverSeen, isRecording, downloadingJamTrack) ->
|
||||
@app = app
|
||||
@session = session
|
||||
@participantsEverSeen = participantsEverSeen
|
||||
@isRecording = isRecording
|
||||
@downloadingJamTrack = downloadingJamTrack
|
||||
|
||||
inSession: () ->
|
||||
@session?
|
||||
|
|
@ -55,6 +57,7 @@ context = window
|
|||
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
|
||||
|
|
@ -62,6 +65,14 @@ context = window
|
|||
|
||||
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)->
|
||||
|
|
@ -70,12 +81,22 @@ context = window
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
}
|
||||
)
|
||||
|
|
@ -5,6 +5,19 @@ 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);
|
||||
|
|
@ -17,17 +30,47 @@ rest = context.JK.Rest()
|
|||
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
|
||||
|
||||
handleVolumeChangeCallback: () ->
|
||||
|
||||
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: () ->
|
||||
logger.debug("metronome callback")
|
||||
|
||||
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) ->
|
||||
|
||||
|
|
@ -49,8 +92,8 @@ rest = context.JK.Rest()
|
|||
# GetControlState for this mixer which returns min/max
|
||||
# value is a DB value from -80 to 20. Convert to float from 0.0-1.0
|
||||
|
||||
@mixers.updateVU(mixerId + "_vul", (leftValue + 80) / 80, leftClipping)
|
||||
@mixers.updateVU(mixerId + "_vur", (rightValue + 80) / 80, rightClipping)
|
||||
@mixers.updateVU(mixerId, (leftValue + 80) / 80, leftClipping, (rightValue + 80) / 80, rightClipping)
|
||||
#@mixers.updateVU(mixerId + "_vur", (rightValue + 80) / 80, rightClipping)
|
||||
|
||||
|
||||
handleBackingTrackSelectedCallback: () ->
|
||||
|
|
@ -60,17 +103,26 @@ rest = context.JK.Rest()
|
|||
@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, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||
|
||||
this.trigger({session: @session, mixers: @mixers})
|
||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||
|
||||
@issueChange()
|
||||
|
||||
onMute: (mixers, muting) ->
|
||||
|
||||
|
|
@ -78,18 +130,54 @@ rest = context.JK.Rest()
|
|||
@mixers.mute(mixer.id, mixer.mode, muting);
|
||||
|
||||
# simulate a state change to cause a UI redraw
|
||||
this.trigger({session: @session, mixers: @mixers})
|
||||
@issueChange()
|
||||
|
||||
onFaderChanged: (data, mixerIds, groupId) ->
|
||||
|
||||
@mixers.faderChanged(data, mixerIds, groupId)
|
||||
|
||||
this.trigger({session: @session, mixers: @mixers})
|
||||
@issueChange()
|
||||
|
||||
onPanChanged: (data, mixerIds, groupId) ->
|
||||
@mixers.panChanged(data, mixerIds, groupId)
|
||||
|
||||
this.trigger({session: @session, mixers: @mixers})
|
||||
@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)
|
||||
|
|
@ -99,19 +187,23 @@ rest = context.JK.Rest()
|
|||
|
||||
onMixersChanged: (type, text) ->
|
||||
|
||||
console.log("MixerStore: onMixersChanged")
|
||||
|
||||
@masterMixers = context.jamClient.SessionGetAllControlState(true);
|
||||
@personalMixers = context.jamClient.SessionGetAllControlState(false);
|
||||
|
||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||
console.log("masterMixers", @masterMixers)
|
||||
console.log("personalMixers", @personalMixers)
|
||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL)
|
||||
|
||||
SessionActions.mixersChanged.trigger(type, text, @mixers.getTrackInfo())
|
||||
|
||||
this.trigger({session: @session, mixers: @mixers})
|
||||
@issueChange()
|
||||
|
||||
onMixerModeChanged: (mode) ->
|
||||
|
||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, mode)
|
||||
this.trigger({session: @session, mixers: @mixers})
|
||||
@mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, mode)
|
||||
@issueChange()
|
||||
|
||||
onSyncTracks: () ->
|
||||
logger.debug("MixerStore: onSyncTracks")
|
||||
|
|
|
|||
|
|
@ -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,39 @@
|
|||
$ = 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
|
||||
@notifications.unshift(notification)
|
||||
|
||||
if @notifications.length > 100
|
||||
@notifications.pop();
|
||||
@issueChange()
|
||||
|
||||
onBackendNotification: (notification) ->
|
||||
@processNotification(notification)
|
||||
|
||||
onFrontendNotification: (notification) ->
|
||||
@processNotification(notification)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -8,6 +8,7 @@ MIX_MODES = context.JK.MIX_MODES
|
|||
|
||||
SessionActions = @SessionActions
|
||||
RecordingActions = @RecordingActions
|
||||
NotificationActions = @NotificationActions
|
||||
|
||||
@SessionStore = Reflux.createStore(
|
||||
{
|
||||
|
|
@ -33,6 +34,9 @@ RecordingActions = @RecordingActions
|
|||
isRecording: false
|
||||
previousAllTracks: {userTracks: [], backingTracks: [], metronomeTracks: []}
|
||||
webcamViewer: null
|
||||
openBackingTrack: null
|
||||
helper: null
|
||||
downloadingJamTrack: false
|
||||
|
||||
init: ->
|
||||
# Register with the app store to get @app
|
||||
|
|
@ -48,6 +52,131 @@ RecordingActions = @RecordingActions
|
|||
@sessionUtils = context.JK.SessionUtils
|
||||
@recordingModel = new context.JK.RecordingModel(@app, rest, context.jamClient);
|
||||
RecordingActions.initModel(@recordingModel)
|
||||
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack)
|
||||
|
||||
issueChange: () ->
|
||||
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack)
|
||||
this.trigger(@helper)
|
||||
|
||||
onWindowBackgrounded: () ->
|
||||
@app.user()
|
||||
.done((userProfile) =>
|
||||
if userProfile.show_whats_next &&
|
||||
window.location.pathname.indexOf(gon.client_path) == 0 &&
|
||||
!@app.layout.isDialogShowing('getting-started')
|
||||
@app.layout.showDialog('getting-started')
|
||||
)
|
||||
|
||||
return unless @inSession()
|
||||
|
||||
# the window was closed; just attempt to nav to home, which will cause all the right REST calls to happen
|
||||
logger.debug("leaving session because window was closed")
|
||||
SessionActions.leaveSession({location: '/client#/home'})
|
||||
|
||||
onBroadcastFailure: (text) ->
|
||||
logger.debug("SESSION_LIVEBROADCAST_FAIL alert. reason:" + text);
|
||||
|
||||
if @currentSession? && @currentSession.mount?
|
||||
rest.createSourceChange({
|
||||
mount_id: @currentSession.mount.id,
|
||||
source_direction: true,
|
||||
success: false,
|
||||
reason: text,
|
||||
client_id: @app.clientId
|
||||
})
|
||||
else
|
||||
logger.debug("unable to report source change because no mount seen on session")
|
||||
|
||||
onBroadcastSuccess: (text) ->
|
||||
logger.debug("SESSION_LIVEBROADCAST_ACTIVE alert. reason:" + text);
|
||||
|
||||
if @currentSession? && @currentSession.mount?
|
||||
rest.createSourceChange({
|
||||
mount_id: @currentSession.mount.id,
|
||||
source_direction: true,
|
||||
success: true,
|
||||
reason: text,
|
||||
client_id: @app.clientId
|
||||
})
|
||||
else
|
||||
logger.debug("unable to report source change because no mount seen on session")
|
||||
|
||||
onBroadcastStopped: (text) ->
|
||||
logger.debug("SESSION_LIVEBROADCAST_STOPPED alert. reason:" + text);
|
||||
|
||||
if @currentSession? && @currentSession.mount?
|
||||
rest.createSourceChange({
|
||||
mount_id: @currentSession.mount.id,
|
||||
source_direction: false,
|
||||
success: true,
|
||||
reason: text,
|
||||
client_id: @app.clientId
|
||||
})
|
||||
else
|
||||
logger.debug("unable to report source change because no mount seen on session")
|
||||
|
||||
onShowNativeMetronomeGui: () ->
|
||||
context.jamClient.SessionShowMetronomeGui()
|
||||
|
||||
onOpenMetronome: () ->
|
||||
unstable = @unstableNTPClocks()
|
||||
if @participants().length > 1 && unstable.length > 0
|
||||
names = unstable.join(", ")
|
||||
logger.debug("Unstable clocks: ", names, unstable)
|
||||
context.JK.Banner.showAlert("Couldn't open metronome", context._.template($('#template-help-metronome-unstable').html(), {names: names}, { variable: 'data' }));
|
||||
else
|
||||
data =
|
||||
value: 1
|
||||
session_size: @participants().length
|
||||
user_id: context.JK.currentUserId
|
||||
user_name: context.JK.currentUserName
|
||||
|
||||
context.stats.write('web.metronome.open', data)
|
||||
rest.openMetronome({id: @currentSessionId})
|
||||
.done((response) =>
|
||||
MixerActions.openMetronome()
|
||||
@updateSessionInfo(response, true)
|
||||
)
|
||||
.fail((jqXHR) =>
|
||||
@app.notify({
|
||||
"title": "Couldn't open metronome",
|
||||
"text": "Couldn't inform the server to open metronome. msg=" + jqXHR.responseText,
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
})
|
||||
)
|
||||
|
||||
onMetronomeCricketChange: (isCricket) ->
|
||||
context.jamClient.setMetronomeCricketTestState(isCricket);
|
||||
|
||||
unstableNTPClocks: () ->
|
||||
unstable = []
|
||||
# This should be handled in the below loop, actually:
|
||||
myState = context.jamClient.getMyNetworkState()
|
||||
map = null
|
||||
for participant in @participants()
|
||||
isSelf = participant.client_id == @app.clientId
|
||||
|
||||
if isSelf
|
||||
isStable = myState.ntp_stable
|
||||
else
|
||||
map = context.jamClient.getPeerState(participant.client_id)
|
||||
isStable = map.ntp_stable
|
||||
|
||||
if !isStable
|
||||
name = participant.user.name
|
||||
|
||||
if isSelf
|
||||
name += " (this computer)"
|
||||
|
||||
unstable.push(name)
|
||||
unstable
|
||||
|
||||
|
||||
|
||||
onDownloadingJamTrack: (downloading) ->
|
||||
@downloadingJamTrack = downloading
|
||||
|
||||
@issueChange()
|
||||
|
||||
onToggleSessionVideo: () ->
|
||||
logger.debug("toggle session video")
|
||||
|
|
@ -71,6 +200,133 @@ RecordingActions = @RecordingActions
|
|||
@sessionPageEnterDeferred.resolve(inputTracks);
|
||||
@sessionPageEnterDeferred = null
|
||||
|
||||
|
||||
|
||||
onCloseMedia: () ->
|
||||
|
||||
logger.debug("SessionStore: onCloseMedia")
|
||||
if @helper.recordedTracks()
|
||||
@closeRecording()
|
||||
else if @helper.jamTracks() || @downloadingJamTrack
|
||||
@closeJamTrack()
|
||||
else if @helper.backingTrack() && @helper.backingTrack().path
|
||||
@closeBackingTrack()
|
||||
else if @helper.isMetronomeOpen()
|
||||
@closeMetronomeTrack()
|
||||
else
|
||||
logger.error("don't know how to close open media");
|
||||
|
||||
closeJamTrack: () ->
|
||||
logger.debug("closing jam track");
|
||||
|
||||
if @isRecording
|
||||
logger.debug("can't close jamtrack while recording")
|
||||
@app.notify({title: 'Can Not Close JamTrack', text: 'A JamTrack can not be closed while recording.'})
|
||||
return
|
||||
|
||||
unless @selfOpenedJamTracks()
|
||||
logger.debug("can't close jamtrack if not the opener")
|
||||
@app.notify({title: 'Can Not Close JamTrack', text: 'Only the person who opened the JamTrack can close it.'})
|
||||
return
|
||||
|
||||
rest.closeJamTrack({id: @currentSessionId})
|
||||
.done(() =>
|
||||
@refreshCurrentSession(true)
|
||||
)
|
||||
.fail((jqXHR) =>
|
||||
@app.notify({
|
||||
"title": "Couldn't Close JamTrack",
|
||||
"text": "Couldn't inform the server to close JamTrack. msg=" + jqXHR.responseText,
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
})
|
||||
)
|
||||
|
||||
context.jamClient.JamTrackStopPlay()
|
||||
|
||||
|
||||
onOpenBackingTrack: (result) ->
|
||||
unless @inSession()
|
||||
logger.debug("ignoring backing track selected callback (not in session)")
|
||||
return
|
||||
|
||||
if result.success
|
||||
logger.debug("backing track selected: " + result.file);
|
||||
|
||||
rest.openBackingTrack({id: @currentSessionId, backing_track_path: result.file})
|
||||
.done(() =>
|
||||
|
||||
openResult = context.jamClient.SessionOpenBackingTrackFile(result.file, false);
|
||||
|
||||
if openResult
|
||||
# storing session state in memory, not in response of Session server response. bad.
|
||||
@openBackingTrack = result.file
|
||||
else
|
||||
@app.notify({
|
||||
"title": "Couldn't Open Backing Track",
|
||||
"text": "Is the file a valid audio file?",
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
});
|
||||
@closeBackingTrack()
|
||||
)
|
||||
.fail((jqXHR) =>
|
||||
@app.notifyServerError(jqXHR, "Unable to Open Backing Track For Playback");
|
||||
)
|
||||
|
||||
closeRecording: () ->
|
||||
logger.debug("closing recording");
|
||||
|
||||
rest.stopPlayClaimedRecording({id: @currentSessionId, claimed_recording_id: @currentSession.claimed_recording.id})
|
||||
.done((response) =>
|
||||
#sessionModel.refreshCurrentSession(true);
|
||||
# update session info
|
||||
@onUpdateSession(response)
|
||||
)
|
||||
.fail((jqXHR) =>
|
||||
@app.notify({
|
||||
"title": "Couldn't Stop Recording Playback",
|
||||
"text": "Couldn't inform the server to stop playback. msg=" + jqXHR.responseText,
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
})
|
||||
)
|
||||
|
||||
context.jamClient.CloseRecording()
|
||||
|
||||
closeMetronomeTrack:() ->
|
||||
logger.debug("SessionStore: closeMetronomeTrack")
|
||||
rest.closeMetronome({id: @currentSessionId})
|
||||
.done(() =>
|
||||
context.jamClient.SessionCloseMetronome()
|
||||
@refreshCurrentSession(true)
|
||||
)
|
||||
.fail((jqXHR) =>
|
||||
@app.notify({
|
||||
"title": "Couldn't Close MetronomeTrack",
|
||||
"text": "Couldn't inform the server to close MetronomeTrack. msg=" + jqXHR.responseText,
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
})
|
||||
)
|
||||
|
||||
closeBackingTrack: () ->
|
||||
if @isRecording
|
||||
logger.debug("can't close backing track while recording")
|
||||
return
|
||||
|
||||
rest.closeBackingTrack({id: @currentSessionId})
|
||||
.done(() =>
|
||||
)
|
||||
.fail(() =>
|
||||
@app.notify({
|
||||
"title": "Couldn't Close Backing Track",
|
||||
"text": "Couldn't inform the server to close Backing Track. msg=" + jqXHR.responseText,
|
||||
"icon_url": "/assets/content/icon_alert_big.png"
|
||||
});
|
||||
)
|
||||
|
||||
# '' closes all open backing tracks
|
||||
context.jamClient.SessionStopPlay();
|
||||
context.jamClient.SessionCloseBackingTrackFile('');
|
||||
|
||||
|
||||
onMixersChanged: (type, text, trackInfo) ->
|
||||
|
||||
return unless @inSession()
|
||||
|
|
@ -227,7 +483,7 @@ RecordingActions = @RecordingActions
|
|||
else
|
||||
@app.notifyAlert(title, "Error reason: " + reason)
|
||||
|
||||
this.trigger(new context.SessionHelper(@app, @currentSession, @isRecording))
|
||||
@issueChange()
|
||||
|
||||
notifyWithUserInfo: (title , text, clientId) ->
|
||||
@findUserBy({clientId: clientId})
|
||||
|
|
@ -504,7 +760,7 @@ RecordingActions = @RecordingActions
|
|||
# give the session to settle just a little (call a timeout of 1 second)
|
||||
setTimeout(()=>
|
||||
# tell the server we are about to open a jamtrack
|
||||
rest.openJamTrack({id: context.JK.CurrentSessionModel.id(), jam_track_id: jamTrack.id})
|
||||
rest.openJamTrack({id: @currentSessionId, jam_track_id: jamTrack.id})
|
||||
.done((response) =>
|
||||
logger.debug("jamtrack opened")
|
||||
# now actually load the jamtrack
|
||||
|
|
@ -567,6 +823,9 @@ RecordingActions = @RecordingActions
|
|||
@refreshCurrentSessionRest(force)
|
||||
)
|
||||
|
||||
onUpdateSession: (session) ->
|
||||
@updateSessionInfo(session, true)
|
||||
|
||||
updateSessionInfo: (session, force) ->
|
||||
if force == true || @currentTrackChanges < session.track_changes_counter
|
||||
logger.debug("updating current track changes from %o to %o", @currentTrackChanges, session.track_changes_counter)
|
||||
|
|
@ -668,7 +927,7 @@ RecordingActions = @RecordingActions
|
|||
|
||||
console.log("SESSION CHANGED", sessionData)
|
||||
|
||||
this.trigger(new context.SessionHelper(@app, @currentSession, @isRecording))
|
||||
@issueChange()
|
||||
|
||||
ensureConnected: () ->
|
||||
unless context.JK.JamServer.connected
|
||||
|
|
@ -740,7 +999,7 @@ RecordingActions = @RecordingActions
|
|||
|
||||
@sessionEnded()
|
||||
|
||||
this.trigger(new context.SessionHelper(@app, @currentSession, @isRecording))
|
||||
@issueChange()
|
||||
|
||||
selfOpenedJamTracks: () ->
|
||||
@currentSession && (@currentSession.jam_track_initiator_id == context.JK.currentUserId)
|
||||
|
|
@ -776,6 +1035,17 @@ RecordingActions = @RecordingActions
|
|||
@previousAllTracks = {userTracks: [], backingTracks: [], metronomeTracks: []}
|
||||
@openBackingTrack = null
|
||||
@shownAudioMediaMixerHelp = false
|
||||
@controlsLockedForJamTrackRecording = false;
|
||||
@controlsLockedForJamTrackRecording = false
|
||||
@openBackingTrack = null
|
||||
@downloadingJamTrack = false
|
||||
|
||||
NotificationActions.sessionEnded()
|
||||
|
||||
id: () ->
|
||||
@currentSessionId
|
||||
|
||||
getCurrentOrLastSession: () ->
|
||||
@currentOrLastSession
|
||||
|
||||
}
|
||||
)
|
||||
|
|
@ -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({
|
||||
|
|
@ -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) {
|
||||
|
|
@ -3025,6 +3025,7 @@
|
|||
function closeMetronomeTrack() {
|
||||
rest.closeMetronome({id: sessionModel.id()})
|
||||
.done(function() {
|
||||
logger.debug("session: SessionCloseMetronome")
|
||||
context.jamClient.SessionCloseMetronome();
|
||||
sessionModel.refreshCurrentSession(true);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -672,7 +672,7 @@ context.JK.SyncViewer = class SyncViewer
|
|||
|
||||
sendCommand: ($retry, cmd) =>
|
||||
|
||||
if context.JK.CurrentSessionModel and context.JK.CurrentSessionModel.inSession()
|
||||
if context.SessionStore.inSession()
|
||||
context.JK.ackBubble($retry, 'sync-viewer-paused', {}, {offsetParent: $retry.closest('.dialog')})
|
||||
else
|
||||
context.jamClient.OnTrySyncCommand(cmd)
|
||||
|
|
@ -817,7 +817,7 @@ context.JK.SyncViewer = class SyncViewer
|
|||
|
||||
exportRecording: (e) =>
|
||||
$export = $(e.target)
|
||||
if context.JK.CurrentSessionModel and context.JK.CurrentSessionModel.inSession()
|
||||
if context.SessionStore.inSession()
|
||||
context.JK.ackBubble($export, 'sync-viewer-paused', {}, {offsetParent: $export.closest('.dialog')})
|
||||
return
|
||||
|
||||
|
|
@ -837,7 +837,7 @@ context.JK.SyncViewer = class SyncViewer
|
|||
|
||||
deleteRecording: (e) =>
|
||||
$delete = $(e.target)
|
||||
if context.JK.CurrentSessionModel and context.JK.CurrentSessionModel.inSession()
|
||||
if context.SessionStore.inSession()
|
||||
context.JK.ackBubble($delete, 'sync-viewer-paused', {}, {offsetParent: $delete.closest('.dialog')})
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -1422,7 +1422,7 @@
|
|||
|
||||
/** validates that no changes are being made to tracks while recording */
|
||||
context.JK.verifyNotRecordingForTrackChange = function (app) {
|
||||
if (context.JK.CurrentSessionModel.recordingModel.isRecording()) {
|
||||
if (context.RecordingStore.recordingModel.isRecording()) {
|
||||
app.notify({
|
||||
title: "Currently Recording",
|
||||
text: "Tracks cannot be modified while recording.",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
// take all necessary arguments to complete its work.
|
||||
context.JK.VuHelpers = {
|
||||
|
||||
registeredMixers: [],
|
||||
|
||||
/**
|
||||
* Render a VU meter into the provided selector.
|
||||
* vuType can be either "horizontal" or "vertical"
|
||||
|
|
@ -95,6 +97,105 @@
|
|||
|
||||
},
|
||||
|
||||
// type can be 'single' or 'double', meaning how the VU is represented (one set of lights, two)
|
||||
// mixerId is the ID of the mixer
|
||||
// and someFunction is used to make the registration (equality check).
|
||||
registerVU: function(type, mixerId, someFunction, horizontal, lightCount, leftLights, rightLights) {
|
||||
var registrations = this.registeredMixers[mixerId]
|
||||
if (!registrations) {
|
||||
registrations = []
|
||||
this.registeredMixers[mixerId] = registrations
|
||||
}
|
||||
|
||||
if(type == 'single') {
|
||||
registrations.push({type:type, ptr: someFunction, horizontal: horizontal, lightCount: lightCount, lights:leftLights})
|
||||
}
|
||||
else {
|
||||
registrations.push({type:type, ptr: someFunction, horizontal: horizontal, lightCount: lightCount, leftLights:leftLights, rightLights: rightLights})
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
unregisterVU: function(mixerId, someFunction) {
|
||||
var registrations = this.registeredMixers[mixerId]
|
||||
if (!registrations || registrations.length == 0) {
|
||||
logger.debug("no registration found for: " + type + ":" + mixerId)
|
||||
return
|
||||
}
|
||||
|
||||
var origLength = registrations.length
|
||||
registrations = registrations.filter(function(element) {
|
||||
someFunction !== element.ptr
|
||||
})
|
||||
|
||||
this.registeredMixers[mixerId] = registrations
|
||||
|
||||
if(origLength == registrations.length) {
|
||||
logger.warn("did not find anything to unregister for: " + type + ':' + mixerId)
|
||||
}
|
||||
},
|
||||
|
||||
updateSingleVU: function(horizontal, lightCount, $lights, value, isClipping) {
|
||||
|
||||
var i = 0;
|
||||
var state = 'on';
|
||||
var lights = Math.round(value * lightCount);
|
||||
var redSwitch = Math.round(lightCount * 0.6666667);
|
||||
|
||||
var $light = null;
|
||||
var colorClass = 'vu-green-';
|
||||
var thisLightSelector = null;
|
||||
|
||||
// Remove all light classes from all lights
|
||||
$lights.removeClass('vu-green-off vu-green-on vu-red-off vu-red-on');
|
||||
|
||||
// Set the lights
|
||||
for (i = 0; i < lightCount; i++) {
|
||||
colorClass = 'vu-green-';
|
||||
state = 'on';
|
||||
if (i >= redSwitch) {
|
||||
colorClass = 'vu-red-';
|
||||
}
|
||||
if (i >= lights) {
|
||||
state = 'off';
|
||||
}
|
||||
|
||||
var lightIndex = horizontal ? i : lightCount - i - 1;
|
||||
$lights.eq(lightIndex).addClass(colorClass + state);
|
||||
}
|
||||
},
|
||||
|
||||
// sentMixerId ends with vul or vur
|
||||
updateVU3: function(mixer, leftValue, leftClipping, rightValue, rightClipping) {
|
||||
|
||||
var registrations = this.registeredMixers[mixer.id]
|
||||
if (registrations) {
|
||||
var j;
|
||||
for(j = 0; j < registrations.length; j++) {
|
||||
var registration = registrations[j]
|
||||
var horizontal = registration.horizontal;
|
||||
var lightCount = registration.lightCount;
|
||||
|
||||
if(registration.type == 'single') {
|
||||
// TODO: find 'active' VU ... is it left value, or right value?
|
||||
var $lights = registration.lights;
|
||||
this.updateSingleVU(horizontal, lightCount, $lights, leftValue, leftClipping)
|
||||
}
|
||||
else {
|
||||
if(mixer.stereo) {
|
||||
this.updateSingleVU(horizontal, lightCount, registration.leftLights, leftValue, leftClipping)
|
||||
this.updateSingleVU(horizontal, lightCount, registration.rightLights, rightValue, rightClipping)
|
||||
}
|
||||
else {
|
||||
this.updateSingleVU(horizontal, lightCount, registration.leftLights, leftValue, leftClipping)
|
||||
this.updateSingleVU(horizontal, lightCount, registration.rightLights, leftValue, leftClipping)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a selector representing a container for a VU meter and
|
||||
* a value between 0.0 and 1.0, light the appropriate lights.
|
||||
|
|
|
|||
|
|
@ -343,3 +343,7 @@ $labelFontSize: 12px;
|
|||
text-transform: capitalize
|
||||
}
|
||||
|
||||
.vertical-helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
@import "client/common";
|
||||
|
||||
.metronome-playback-mode-selector-popup {
|
||||
text-align:left;
|
||||
.bt-content {
|
||||
width:180px;
|
||||
background-color:#333;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,206 @@
|
|||
@import "client/common";
|
||||
|
||||
.media-controls {
|
||||
padding: 3px 0;
|
||||
width:100%;
|
||||
min-width:100%;
|
||||
background-color: #242323;
|
||||
position: relative;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
@include border_box_sizing;
|
||||
height: 36px;
|
||||
display:block;
|
||||
white-space:nowrap;
|
||||
|
||||
.play-buttons {
|
||||
float:left;
|
||||
margin-top:5px;
|
||||
}
|
||||
|
||||
.recording-position {
|
||||
float:left;
|
||||
margin-left:10px;
|
||||
|
||||
}
|
||||
|
||||
.recording-time {
|
||||
float:left;
|
||||
margin-top:8px;
|
||||
&.start-time {
|
||||
margin-left:10px;
|
||||
}
|
||||
&.duration-time {
|
||||
float:right;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-playback {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.recording-current {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.recording-playback {
|
||||
display:inline-block;
|
||||
background-image:url(/assets/content/bkg_playcontrols.png);
|
||||
background-repeat:repeat-x;
|
||||
position:relative;
|
||||
width:calc(100% - 115px);
|
||||
margin-left:83px;
|
||||
margin-right:20px;
|
||||
margin-top:8px;
|
||||
cursor:pointer;
|
||||
height:16px;
|
||||
position:absolute;
|
||||
left:0;
|
||||
|
||||
}
|
||||
|
||||
.recording-slider {
|
||||
position:absolute;
|
||||
left:0;
|
||||
top:0;
|
||||
|
||||
img {
|
||||
position:absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.metronome-playback-options {
|
||||
float:left;
|
||||
margin-left:10px;
|
||||
margin-top:8px;
|
||||
}
|
||||
|
||||
.metronome-options {
|
||||
float:right;
|
||||
}
|
||||
|
||||
&.jamtrack-mode, &.mediafile-mode {
|
||||
.metronome-playback-options {
|
||||
display:none;
|
||||
}
|
||||
.metronome-text {
|
||||
display:none;
|
||||
}
|
||||
.metronome-options {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.metronome-mode {
|
||||
.recording-time {display:none}
|
||||
.recording-playback {display:none}
|
||||
.recording-current {display:none}
|
||||
.playback-mode-buttons {display:none}
|
||||
.stop-button {display:none}
|
||||
|
||||
select {
|
||||
width:75px;
|
||||
float:right;
|
||||
}
|
||||
|
||||
label {
|
||||
float: right !important;
|
||||
margin-left: 5px;
|
||||
margin-top: 7px !important;
|
||||
margin-right: 5px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-status {
|
||||
font-size:15px;
|
||||
}
|
||||
|
||||
.recording-status .recording-duration {
|
||||
font-family:Arial, Helvetica, sans-serif;
|
||||
display:inline-block;
|
||||
font-size:18px;
|
||||
position:absolute;
|
||||
//top:3px;
|
||||
right:4px;
|
||||
}
|
||||
|
||||
.recording-slider {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
|
||||
&.has-mix {
|
||||
.recording-status {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.has-mix) {
|
||||
|
||||
border-width: 0; // override screen_common's .error
|
||||
|
||||
.play-button {
|
||||
display:none;
|
||||
}
|
||||
.recording-current {
|
||||
display:none;
|
||||
}
|
||||
.recording-position {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
.jam-track-get-ready, .media-seeking {
|
||||
display:none;
|
||||
position:absolute;
|
||||
top:-29px;
|
||||
margin-left:-50px;
|
||||
width:100px;
|
||||
vertical-align:middle;
|
||||
height:32px;
|
||||
line-height:32px;
|
||||
left:50%;
|
||||
|
||||
.spinner-small {
|
||||
vertical-align:middle;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
span {
|
||||
vertical-align:middle;
|
||||
}
|
||||
}
|
||||
|
||||
.jam-track-get-ready[data-mode="JAMTRACK"][data-current-time="0"] {
|
||||
display:block;
|
||||
}
|
||||
|
||||
.media-seeking[data-mode="SEEKING"] {
|
||||
display:block;
|
||||
}
|
||||
|
||||
.playback-mode-buttons {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.play-button, .stop-button {
|
||||
outline:none;
|
||||
}
|
||||
|
||||
.stop-button {
|
||||
margin-left:3px;
|
||||
}
|
||||
|
||||
.play-button img.pausebutton {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.metronome-controls {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.metronome-options {
|
||||
float:right;
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,10 @@
|
|||
padding: 15px;
|
||||
height: 100%;
|
||||
margin-bottom: 15px;
|
||||
color:$ColorTextTypical;
|
||||
overflow:hidden;
|
||||
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.session-notifications {
|
||||
|
|
@ -62,10 +66,21 @@
|
|||
}
|
||||
|
||||
.session-tracks-scroller {
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
|
||||
position: absolute;
|
||||
top: 90px;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
&.media-options-showing {
|
||||
top:180px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
@ -73,10 +88,13 @@
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.download-jamtrack {
|
||||
margin-top:20px;
|
||||
}
|
||||
|
||||
.session-track {
|
||||
float:left;
|
||||
margin: 10px 0;
|
||||
|
||||
color: $ColorTextTypical;
|
||||
background-color: #242323;
|
||||
border-radius: 6px;
|
||||
|
|
@ -96,15 +114,71 @@
|
|||
border-radius: 6px;
|
||||
}
|
||||
|
||||
&.no-mixer {
|
||||
&.no-mixer, &.no-audio {
|
||||
.disabled-track-overlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity:0.5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// media overrides
|
||||
&.backing-track, &.recorded-track, &.jam-track, &.metronome {
|
||||
width:210px;
|
||||
table.vu {
|
||||
float: right;
|
||||
margin-top: 30px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.track-controls {
|
||||
float:right;
|
||||
}
|
||||
.track-buttons {
|
||||
float:right;
|
||||
}
|
||||
.track-icon-pan {
|
||||
float:right;
|
||||
margin-right:20px;
|
||||
}
|
||||
.track-icon-mute{
|
||||
float:right;
|
||||
}
|
||||
}
|
||||
|
||||
&.metronome {
|
||||
.track-instrument {
|
||||
float:left;
|
||||
margin-left:0;
|
||||
margin-right: 8px;
|
||||
margin-top: -3px;
|
||||
}
|
||||
.track-controls {
|
||||
margin-left:0;
|
||||
}
|
||||
}
|
||||
|
||||
&.recorded-track, &.jam-track {
|
||||
height:56px;
|
||||
min-height:56px;
|
||||
.track-buttons {
|
||||
margin-top:2px;
|
||||
}
|
||||
.track-controls {
|
||||
margin-left:0;
|
||||
}
|
||||
table.vu {
|
||||
margin-top:10px;
|
||||
}
|
||||
.track-instrument {
|
||||
float: left;
|
||||
margin: -2px 7px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.react-holder {
|
||||
&.SessionTrackVolumeHover, &.SessionSelfVolumeHover {
|
||||
height:331px;
|
||||
|
|
@ -423,29 +497,114 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.session-track-settings {
|
||||
height:18px;
|
||||
height:20px;
|
||||
cursor:pointer;
|
||||
color:white;
|
||||
padding-bottom:1px; // to line up with SessionOtherTracks
|
||||
color:$ColorTextTypical;
|
||||
|
||||
span {
|
||||
top: -4px;
|
||||
top: -5px;
|
||||
position: relative;
|
||||
left:3px;
|
||||
}
|
||||
}
|
||||
|
||||
.session-invite-musicians {
|
||||
height:19px;
|
||||
height:20px;
|
||||
cursor: pointer;
|
||||
color:white;
|
||||
color:$ColorTextTypical;
|
||||
|
||||
span {
|
||||
top:-5px;
|
||||
position:relative;
|
||||
left:3px;
|
||||
}
|
||||
}
|
||||
|
||||
.closeAudio, .session-clear-notifications {
|
||||
cursor: pointer;
|
||||
color:$ColorTextTypical;
|
||||
height:20px;
|
||||
|
||||
img {
|
||||
top:-2px
|
||||
}
|
||||
span {
|
||||
top: -5px;
|
||||
position: relative;
|
||||
left: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.open-media-file-header, .use-metronome-header {
|
||||
font-size:16px;
|
||||
line-height:100%;
|
||||
margin:0;
|
||||
|
||||
img {
|
||||
position:relative;
|
||||
top:3px;
|
||||
}
|
||||
}
|
||||
.open-media-file-header {
|
||||
|
||||
img {
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
.open-text {
|
||||
margin-left:5px;
|
||||
vertical-align:bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.use-metronome-header {
|
||||
clear: both;
|
||||
a {
|
||||
color:$ColorTextTypical;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-media-file-options {
|
||||
font-size:14px;
|
||||
margin: 7px 0 0 7px !important;
|
||||
color:$ColorTextTypical;
|
||||
li {
|
||||
margin-bottom:5px !important;
|
||||
margin-left:38px !important;
|
||||
a {
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
color:$ColorTextTypical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-metronome {
|
||||
margin-left:5px;
|
||||
}
|
||||
|
||||
.media-options {
|
||||
padding-bottom:10px;
|
||||
}
|
||||
|
||||
.session-notification {
|
||||
color: white;
|
||||
background-color: #666666;
|
||||
border-radius: 6px;
|
||||
min-height: 36px;
|
||||
width:100%;
|
||||
position:relative;
|
||||
@include border_box_sizing;
|
||||
padding:6px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
@import "client/common";
|
||||
|
||||
body.media-controls-popup.popup {
|
||||
|
||||
text-align:center;
|
||||
|
||||
background-color: #242323;
|
||||
|
||||
#minimal-container {
|
||||
padding-bottom:0px;
|
||||
}
|
||||
|
||||
.media-controls-popup {
|
||||
padding:15px 15px 3px 15px;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-top:20px;
|
||||
}
|
||||
|
||||
.icheckbox_minimal {
|
||||
float:left;
|
||||
}
|
||||
|
||||
label {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
margin-top:2px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
text-align:left;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
.close-link {
|
||||
margin-top:20px;
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
.display-metronome {
|
||||
font-size:12px;
|
||||
margin-top:35px;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,5 +8,8 @@
|
|||
*= require icheck/minimal/minimal
|
||||
*= require minimal/popup
|
||||
*= require minimal/recording_controls
|
||||
*= require minimal/media_controls
|
||||
*= require minimal/minimal_main
|
||||
*= require client/metronomePlaybackModeSelect
|
||||
*= require_directory ../client/react-components
|
||||
*/
|
||||
|
|
@ -6,4 +6,8 @@ class PopupsController < ApplicationController
|
|||
render :layout => "minimal"
|
||||
end
|
||||
|
||||
def media_controls
|
||||
render :layout => "minimal"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
object @music_session
|
||||
|
||||
extends "api_music_sessions/show"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
object @music_session
|
||||
|
||||
extends "api_music_sessions/show"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
object @music_session
|
||||
|
||||
extends "api_music_sessions/show"
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
object @music_session
|
||||
|
||||
attributes :id
|
||||
|
|
@ -8,4 +8,4 @@ script type='text/template' id='template-metronome-playback-mode'
|
|||
a href='#' - Play cluster test
|
||||
|
||||
li data-ui-option="show-metronome-window"
|
||||
a href='#' - Show visual metronome
|
||||
a href='#' - Show visual metronome
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
script type='text/template' id='template-metronome-playback-mode'
|
||||
p.please-select Please select one:
|
||||
ul
|
||||
li data-playback-option="self"
|
||||
a href='#' - Play metronome
|
||||
|
||||
li data-playback-option="cricket"
|
||||
a href='#' - Play cluster test
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
h1 Learn How to Make Money by Referring Users
|
||||
.video-wrapper
|
||||
.video-container
|
||||
iframe src="//www.youtube.com/embed/ylYcvTY9CVo" frameborder="0" allowfullscreen
|
||||
iframe src="//www.youtube.com/embed/96YTnO_H9a4" frameborder="0" allowfullscreen
|
||||
br clear="all"
|
||||
.row
|
||||
h1 JamKazam Affiliate Agreement
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
- provide(:page_name, 'media-controls-popup popup')
|
||||
= render "clients/metronome_playback_mode2"
|
||||
= react_component 'PopupWrapper', {component: 'PopupMediaControls'}
|
||||
|
|
@ -136,7 +136,8 @@ SampleApp::Application.routes.draw do
|
|||
match '/extras/settings', to: 'extras#settings'
|
||||
|
||||
scope '/popups' do
|
||||
match '/recording-controls', to: 'popups#recording_controls'
|
||||
match '/recording-controls', to: 'popups#recording_controls'
|
||||
match '/media-controls', to: 'popups#media_controls'
|
||||
end
|
||||
|
||||
scope '/corp' do
|
||||
|
|
|
|||
Loading…
Reference in New Issue