2012-10-29 00:37:59 +00:00
// The session model contains information about the music
// sessions that the current client has joined.
( function ( context , $ ) {
2012-12-07 01:02:33 +00:00
"use strict" ;
2012-10-29 00:37:59 +00:00
context . JK = context . JK || { } ;
var logger = context . JK . logger ;
2014-06-11 21:52:46 +00:00
// screen can be null
context . JK . SessionModel = function ( app , server , client , sessionScreen ) {
2014-06-12 02:47:56 +00:00
var ALERT _TYPES = context . JK . ALERT _TYPES ;
2014-08-31 15:30:59 +00:00
var EVENTS = context . JK . EVENTS ;
2014-11-11 22:21:29 +00:00
var MIX _MODES = context . JK . MIX _MODES ;
2015-04-08 19:34:05 +00:00
var gearUtils = context . JK . GearUtilsInstance ;
2014-06-11 21:52:46 +00:00
2014-08-29 02:11:25 +00:00
var userTracks = null ; // comes from the backend
2013-03-02 19:02:42 +00:00
var clientId = client . clientID ;
var currentSessionId = null ; // Set on join, prior to setting currentSession.
var currentSession = null ;
2013-12-30 18:34:15 +00:00
var currentOrLastSession = null ;
2013-03-02 19:02:42 +00:00
var subscribers = { } ;
var users = { } ; // User info for session participants
2013-08-31 13:54:11 +00:00
var rest = context . JK . Rest ( ) ;
2013-11-03 20:55:55 +00:00
var requestingSessionRefresh = false ;
var pendingSessionRefresh = false ;
var recordingModel = new context . JK . RecordingModel ( app , this , rest , context . jamClient ) ;
2014-03-03 22:13:23 +00:00
var currentTrackChanges = 0 ;
2014-06-11 21:52:46 +00:00
var backendMixerAlertThrottleTimer = null ;
2014-03-03 22:13:23 +00:00
// we track all the clientIDs of all the participants ever seen by this session, so that we can reliably convert a clientId from the backend into a username/avatar
var participantsEverSeen = { } ;
2015-01-18 21:46:40 +00:00
var currentParticipants = { } ;
2014-04-23 22:03:00 +00:00
var $self = $ ( this ) ;
2014-08-29 02:11:25 +00:00
var sessionPageEnterDeferred = null ;
var sessionPageEnterTimeout = null ;
2014-08-31 15:30:59 +00:00
var startTime = null ;
2014-10-27 18:26:04 +00:00
var joinDeferred = null ;
2015-03-13 02:53:23 +00:00
var previousAllTracks = { userTracks : [ ] , backingTracks : [ ] , metronomeTracks : [ ] } ;
2015-02-03 15:25:45 +00:00
var openBackingTrack = null ;
2015-02-19 15:31:30 +00:00
var shownAudioMediaMixerHelp = false ;
2015-02-25 22:13:04 +00:00
var controlsLockedForJamTrackRecording = false ;
2014-03-03 22:13:23 +00:00
2014-11-21 23:16:00 +00:00
var mixerMode = MIX _MODES . PERSONAL ;
2014-11-11 22:21:29 +00:00
2014-05-19 21:57:08 +00:00
server . registerOnSocketClosed ( onWebsocketDisconnected ) ;
function id ( ) {
2014-09-15 02:55:43 +00:00
return currentSessionId ;
2013-03-02 19:02:42 +00:00
}
2012-10-29 00:37:59 +00:00
2014-08-31 15:30:59 +00:00
function start ( sessionId ) {
2014-08-29 02:11:25 +00:00
currentSessionId = sessionId ;
2014-08-31 15:30:59 +00:00
startTime = new Date ( ) . getTime ( ) ;
2014-08-29 02:11:25 +00:00
}
function setUserTracks ( _userTracks ) {
userTracks = _userTracks ;
}
2014-06-11 21:52:46 +00:00
function inSession ( ) {
return ! ! currentSessionId ;
}
2013-03-02 19:02:42 +00:00
function participants ( ) {
if ( currentSession ) {
return currentSession . participants ;
} else {
return [ ] ;
}
2012-10-29 00:37:59 +00:00
}
2015-03-13 02:53:23 +00:00
// if any participant has the metronome open, then we say this session has the metronome open
function isMetronomeOpen ( ) {
var metronomeOpen = false ;
context . _ . each ( participants ( ) , function ( participant ) {
if ( participant . metronome _open ) {
metronomeOpen = true ;
return false ;
}
} )
return metronomeOpen ;
}
2014-01-05 03:47:23 +00:00
function isPlayingRecording ( ) {
// this is the server's state; there is no guarantee that the local tracks
// requested from the backend will have corresponding track information
2015-02-16 04:01:06 +00:00
return ! ! ( currentSession && currentSession . claimed _recording ) ;
2014-01-05 03:47:23 +00:00
}
function recordedTracks ( ) {
if ( currentSession && currentSession . claimed _recording ) {
2014-01-06 20:35:35 +00:00
return currentSession . claimed _recording . recording . recorded _tracks
2014-01-05 03:47:23 +00:00
}
else {
return null ;
}
}
2015-02-16 04:01:06 +00:00
function recordedBackingTracks ( ) {
if ( currentSession && currentSession . claimed _recording ) {
return currentSession . claimed _recording . recording . recorded _backing _tracks
}
else {
return null ;
}
}
function backingTracks ( ) {
var 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...
context . _ . each ( participants ( ) , function ( participant ) {
if ( participant . backing _tracks . length > 0 ) {
backingTracks = participant . backing _tracks ;
return false ; // break
}
} )
return backingTracks ;
}
2015-01-09 21:15:12 +00:00
function jamTracks ( ) {
if ( currentSession && currentSession . jam _track ) {
2015-03-09 14:44:12 +00:00
return currentSession . jam _track . tracks . filter ( function ( track ) { return track . track _type == 'Track' } )
2015-01-09 21:15:12 +00:00
}
else {
return null ;
}
}
2015-03-26 22:45:23 +00:00
function recordedJamTracks ( ) {
if ( currentSession && currentSession . claimed _recording ) {
return currentSession . claimed _recording . recording . recorded _jam _track _tracks
}
else {
return null ;
}
}
function jamTrackName ( ) {
if ( currentSession && currentSession . jam _track ) {
return currentSession . jam _track . name ;
}
else {
return null ;
}
}
function recordedJamTrackName ( ) {
if ( currentSession && currentSession . claimed _recording && currentSession . claimed _recording . recording . jam _track ) {
return currentSession . claimed _recording . recording . jam _track . name ;
}
else {
return null ;
}
}
2015-02-25 16:43:21 +00:00
// did I open up the current JamTrack?
function selfOpenedJamTracks ( ) {
2015-05-15 17:34:35 +00:00
logger . debug ( "currentSession" , currentSession )
2015-02-25 16:43:21 +00:00
return currentSession && ( currentSession . jam _track _initiator _id == context . JK . currentUserId )
}
2015-01-16 02:28:34 +00:00
function backingTrack ( ) {
2015-01-09 08:47:46 +00:00
if ( currentSession ) {
2015-01-21 04:14:48 +00:00
// TODO: objectize this for VRFS-2665, VRFS-2666, VRFS-2667, VRFS-2668
return {
path : currentSession . backing _track _path
}
2015-01-09 08:47:46 +00:00
}
else {
return null ;
}
}
2013-09-24 21:23:11 +00:00
function creatorId ( ) {
if ( ! currentSession ) {
throw "creator is not known"
}
return currentSession . user _id ;
}
function alreadyInSession ( ) {
var inSession = false ;
$ . each ( participants ( ) , function ( index , participant ) {
if ( participant . user . id == context . JK . currentUserId ) {
inSession = true ;
return false ;
}
} ) ;
return inSession ;
}
2015-02-25 22:13:04 +00:00
function lockControlsforJamTrackRecording ( ) {
controlsLockedForJamTrackRecording = true ;
}
function unlockControlsforJamTrackRecording ( ) {
controlsLockedForJamTrackRecording = false ;
}
function areControlsLockedForJamTrackRecording ( ) {
return controlsLockedForJamTrackRecording ;
}
2014-11-11 22:21:29 +00:00
function onMixerModeChanged ( newMixerMode )
{
mixerMode = newMixerMode ;
var mode = newMixerMode == MIX _MODES . MASTER ? "master" : "personal" ;
logger . debug ( "onMixerModeChanged:" + mode ) ;
$ ( document ) . triggerHandler ( EVENTS . MIXER _MODE _CHANGED , { mode : mode } ) ;
}
2014-08-29 02:11:25 +00:00
function waitForSessionPageEnterDone ( ) {
sessionPageEnterDeferred = $ . Deferred ( ) ;
// see if we already have tracks; if so, we need to run with these
var inputTracks = context . JK . TrackHelpers . getUserTracks ( context . jamClient ) ;
2015-04-08 19:34:05 +00:00
2015-04-21 18:47:00 +00:00
logger . debug ( "isNoInputProfile" , gearUtils . isNoInputProfile ( ) )
2015-04-08 19:34:05 +00:00
if ( inputTracks . length > 0 || gearUtils . isNoInputProfile ( ) ) {
2014-11-21 23:16:00 +00:00
logger . debug ( "on page enter, tracks are already available" )
2014-08-29 02:11:25 +00:00
sessionPageEnterDeferred . resolve ( inputTracks ) ;
var deferred = sessionPageEnterDeferred ;
sessionPageEnterDeferred = null ;
return deferred ;
}
sessionPageEnterTimeout = setTimeout ( function ( ) {
if ( sessionPageEnterTimeout ) {
if ( sessionPageEnterDeferred ) {
sessionPageEnterDeferred . reject ( 'timeout' ) ;
sessionPageEnterDeferred = null ;
}
sessionPageEnterTimeout = null ;
}
} , 5000 ) ;
return sessionPageEnterDeferred ;
}
2013-03-02 19:02:42 +00:00
/ * *
* Join the session specified by the provided id .
* /
function joinSession ( sessionId ) {
logger . debug ( "SessionModel.joinSession(" + sessionId + ")" ) ;
2014-10-27 18:26:04 +00:00
joinDeferred = joinSessionRest ( sessionId ) ;
joinDeferred
. done ( function ( response ) {
if ( ! inSession ( ) ) {
// the user has left the session before they got joined. We need to issue a leave again to the server to make sure they are out
logger . debug ( "user left before fully joined to session. telling server again that they have left" )
leaveSessionRest ( response . id ) ;
return ;
}
2013-08-31 13:54:11 +00:00
logger . debug ( "calling jamClient.JoinSession" ) ;
2014-07-31 15:32:20 +00:00
// on temporary disconnect scenarios, a user may already be in a session when they enter this path
// so we avoid double counting
2013-09-24 21:23:11 +00:00
if ( ! alreadyInSession ( ) ) {
2014-07-31 15:32:20 +00:00
if ( response . music _session . participant _count == 1 ) {
context . JK . GA . trackSessionMusicians ( context . JK . GA . SessionCreationTypes . create ) ;
}
else {
2013-09-24 21:23:11 +00:00
context . JK . GA . trackSessionMusicians ( context . JK . GA . SessionCreationTypes . join ) ;
2014-07-31 15:32:20 +00:00
}
2013-09-24 21:23:11 +00:00
}
2013-11-03 20:55:55 +00:00
recordingModel . reset ( ) ;
2013-08-31 13:54:11 +00:00
client . JoinSession ( { sessionID : sessionId } ) ;
2014-07-31 15:32:20 +00:00
refreshCurrentSession ( true ) ;
2014-03-03 22:13:23 +00:00
server . registerMessageCallback ( context . JK . MessageType . SESSION _JOIN , trackChanges ) ;
server . registerMessageCallback ( context . JK . MessageType . SESSION _DEPART , trackChanges ) ;
server . registerMessageCallback ( context . JK . MessageType . TRACKS _CHANGED , trackChanges ) ;
2014-05-19 21:57:08 +00:00
server . registerMessageCallback ( context . JK . MessageType . HEARTBEAT _ACK , trackChanges ) ;
2016-05-26 21:25:51 +00:00
$ ( document ) . trigger ( EVENTS . SESSION _STARTED , { session : { id : sessionId , lesson _session : session . lesson _session } } ) ;
2013-09-01 16:28:03 +00:00
} )
. fail ( function ( ) {
2014-08-31 15:30:59 +00:00
updateCurrentSession ( null ) ;
2013-08-31 13:54:11 +00:00
} ) ;
2014-10-27 18:26:04 +00:00
return joinDeferred ;
2013-03-02 19:02:42 +00:00
}
2012-10-29 00:37:59 +00:00
2013-11-16 04:35:40 +00:00
function performLeaveSession ( deferred ) {
logger . debug ( "SessionModel.leaveCurrentSession()" ) ;
2014-03-03 22:13:23 +00:00
// TODO - sessionChanged will be called with currentSession = null\
server . unregisterMessageCallback ( context . JK . MessageType . SESSION _JOIN , trackChanges ) ;
server . unregisterMessageCallback ( context . JK . MessageType . SESSION _DEPART , trackChanges ) ;
server . unregisterMessageCallback ( context . JK . MessageType . TRACKS _CHANGED , trackChanges ) ;
2014-05-19 21:57:08 +00:00
server . unregisterMessageCallback ( context . JK . MessageType . HEARTBEAT _ACK , trackChanges ) ;
2014-02-25 19:22:32 +00:00
//server.unregisterMessageCallback(context.JK.MessageType.MUSICIAN_SESSION_DEPART, refreshCurrentSession);
2013-11-16 04:35:40 +00:00
// leave the session right away without waiting on REST. Why? If you can't contact the server, or if it takes a long
// time, for that entire duration you'll still be sending voice data to the other users.
// this may be bad if someone decides to badmouth others in the left-session during this time
2014-08-29 02:11:25 +00:00
//console.trace();
2014-05-19 21:57:08 +00:00
logger . debug ( "performLeaveSession: calling jamClient.LeaveSession for clientId=" + clientId ) ;
2013-11-16 04:35:40 +00:00
client . LeaveSession ( { sessionID : currentSessionId } ) ;
2014-01-06 21:08:37 +00:00
leaveSessionRest ( currentSessionId )
. done ( function ( ) {
sessionChanged ( ) ;
2014-10-29 03:08:22 +00:00
deferred . resolve ( arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] ) ;
2014-01-06 21:08:37 +00:00
} )
. fail ( function ( ) {
2014-10-29 03:08:22 +00:00
deferred . reject ( arguments [ 0 ] , arguments [ 1 ] , arguments [ 2 ] ) ;
2014-01-06 21:08:37 +00:00
} ) ;
2013-11-16 04:35:40 +00:00
// 'unregister' for callbacks
context . jamClient . SessionRegisterCallback ( "" ) ;
2014-06-11 20:37:15 +00:00
//context.jamClient.SessionSetAlertCallback("");
2014-02-14 08:36:10 +00:00
context . jamClient . SessionSetConnectionStatusRefreshRate ( 0 ) ;
2013-11-16 04:35:40 +00:00
updateCurrentSession ( null ) ;
}
2013-03-02 19:02:42 +00:00
/ * *
2013-08-31 13:54:11 +00:00
* Leave the current session , if there is one .
* callback : called in all conditions ; either after an attempt is made to tell the server that we are leaving ,
* or immediately if there is no session
2013-03-02 19:02:42 +00:00
* /
function leaveCurrentSession ( ) {
2013-11-16 04:35:40 +00:00
var deferred = new $ . Deferred ( ) ;
2013-08-31 13:54:11 +00:00
2013-11-16 04:35:40 +00:00
recordingModel . stopRecordingIfNeeded ( )
. always ( function ( ) {
performLeaveSession ( deferred ) ;
} ) ;
2013-08-31 13:54:11 +00:00
return deferred ;
2012-10-29 00:37:59 +00:00
}
2014-03-03 22:13:23 +00:00
function trackChanges ( header , payload ) {
if ( currentTrackChanges < payload . track _changes _counter ) {
// we don't have the latest info. try and go get it
logger . debug ( "track_changes_counter = stale. refreshing..." )
refreshCurrentSession ( ) ;
}
else {
if ( header . type != 'HEARTBEAT_ACK' ) {
// don't log if HEARTBEAT_ACK, or you will see this log all the time
logger . info ( "track_changes_counter = fresh. skipping refresh..." , header , payload )
}
}
}
2013-03-02 19:02:42 +00:00
/ * *
* Refresh the current session , and participants .
* /
2014-03-04 05:11:54 +00:00
function refreshCurrentSession ( force ) {
2013-11-16 04:35:40 +00:00
// XXX use backend instead: https://jamkazam.atlassian.net/browse/VRFS-854
2014-03-03 22:13:23 +00:00
//logger.debug("SessionModel.refreshCurrentSession(" + currentSessionId +")");
2014-03-05 23:18:53 +00:00
if ( force ) {
logger . debug ( "refreshCurrentSession(force=true)" )
}
else {
}
2014-03-04 05:11:54 +00:00
refreshCurrentSessionRest ( sessionChanged , force ) ;
2013-03-02 19:02:42 +00:00
}
2012-10-29 00:37:59 +00:00
2013-03-02 19:02:42 +00:00
/ * *
* Subscribe for sessionChanged events . Provide a subscriberId
* and a callback to be invoked on session changes .
* /
function subscribe ( subscriberId , sessionChangedCallback ) {
logger . debug ( "SessionModel.subscribe(" + subscriberId + ", [callback])" ) ;
subscribers [ subscriberId ] = sessionChangedCallback ;
2012-10-29 00:37:59 +00:00
}
2012-11-03 20:25:23 +00:00
2013-03-02 19:02:42 +00:00
/ * *
* Notify subscribers that the current session has changed .
* /
function sessionChanged ( ) {
for ( var subscriberId in subscribers ) {
2013-11-03 20:55:55 +00:00
subscribers [ subscriberId ] ( currentSession ) ;
2013-03-02 19:02:42 +00:00
}
}
2012-10-29 00:37:59 +00:00
2014-08-31 15:30:59 +00:00
// universal place to clean up, reset items
2014-10-27 18:26:04 +00:00
// fullyJoined means the user stayed in the screen until they had a successful rest.joinSession
// you might not get a fully joined if you join/leave really quickly before the REST API completes
function sessionEnded ( fullyJoined ) {
2014-08-31 15:30:59 +00:00
//cleanup
server . unregisterMessageCallback ( context . JK . MessageType . SESSION _JOIN , trackChanges ) ;
server . unregisterMessageCallback ( context . JK . MessageType . SESSION _DEPART , trackChanges ) ;
server . unregisterMessageCallback ( context . JK . MessageType . TRACKS _CHANGED , trackChanges ) ;
2014-10-27 18:26:04 +00:00
server . unregisterMessageCallback ( context . JK . MessageType . HEARTBEAT _ACK , trackChanges ) ;
2014-08-31 15:30:59 +00:00
if ( sessionPageEnterDeferred != null ) {
sessionPageEnterDeferred . reject ( 'session_over' ) ;
sessionPageEnterDeferred = null ;
}
userTracks = null ;
startTime = null ;
2014-10-27 18:26:04 +00:00
joinDeferred = null ;
2014-11-11 22:21:29 +00:00
mixerMode = MIX _MODES . PERSONAL ;
2014-10-27 18:26:04 +00:00
if ( fullyJoined ) {
$ ( document ) . trigger ( EVENTS . SESSION _ENDED , { session : { id : currentSessionId } } ) ;
}
2014-08-31 15:30:59 +00:00
currentSessionId = null ;
2015-01-18 21:46:40 +00:00
currentParticipants = { }
2015-03-13 02:53:23 +00:00
previousAllTracks = { userTracks : [ ] , backingTracks : [ ] , metronomeTracks : [ ] }
2015-02-03 15:25:45 +00:00
openBackingTrack = null
2015-02-19 15:31:30 +00:00
shownAudioMediaMixerHelp = false
2015-02-25 22:13:04 +00:00
controlsLockedForJamTrackRecording = false ;
2014-08-31 15:30:59 +00:00
}
2013-12-30 18:34:15 +00:00
// you should only update currentSession with this function
2013-11-03 20:55:55 +00:00
function updateCurrentSession ( sessionData ) {
2013-12-30 18:34:15 +00:00
if ( sessionData != null ) {
currentOrLastSession = sessionData ;
}
2014-02-25 19:22:32 +00:00
2014-08-31 15:30:59 +00:00
var beforeUpdate = currentSession ;
2013-11-03 20:55:55 +00:00
currentSession = sessionData ;
2014-08-29 02:11:25 +00:00
2014-08-31 15:30:59 +00:00
// the 'beforeUpdate != null' makes sure we only do a clean up one time internally
2014-10-27 18:26:04 +00:00
if ( sessionData == null ) {
sessionEnded ( beforeUpdate != null ) ;
2014-08-29 02:11:25 +00:00
}
2013-11-03 20:55:55 +00:00
}
2015-02-16 04:01:06 +00:00
function updateSession ( response ) {
updateSessionInfo ( response , null , true ) ;
}
function updateSessionInfo ( response , callback , force ) {
if ( force === true || currentTrackChanges < response . track _changes _counter ) {
logger . debug ( "updating current track changes from %o to %o" , currentTrackChanges , response . track _changes _counter )
currentTrackChanges = response . track _changes _counter ;
sendClientParticipantChanges ( currentSession , response ) ;
updateCurrentSession ( response ) ;
if ( callback != null ) {
callback ( ) ;
}
}
else {
logger . info ( "ignoring refresh because we already have current: " + currentTrackChanges + ", seen: " + response . track _changes _counter ) ;
}
}
2013-03-02 19:02:42 +00:00
/ * *
* Reload the session data from the REST server , calling
* the provided callback when complete .
* /
2014-03-04 05:11:54 +00:00
function refreshCurrentSessionRest ( callback , force ) {
2014-06-11 21:52:46 +00:00
if ( ! inSession ( ) ) {
logger . debug ( "refreshCurrentSession skipped: " )
return ;
}
2013-03-02 19:02:42 +00:00
var url = "/api/sessions/" + currentSessionId ;
2013-11-03 20:55:55 +00:00
if ( requestingSessionRefresh ) {
// if someone asks for a refresh while one is going on, we ask for another to queue up
2014-03-03 22:13:23 +00:00
logger . debug ( "queueing refresh" ) ;
2013-11-03 20:55:55 +00:00
pendingSessionRefresh = true ;
}
else {
requestingSessionRefresh = true ;
$ . ajax ( {
type : "GET" ,
url : url ,
success : function ( response ) {
2015-02-16 04:01:06 +00:00
updateSessionInfo ( response , callback , force ) ;
2013-11-03 20:55:55 +00:00
} ,
2014-06-11 21:52:46 +00:00
error : function ( jqXHR ) {
if ( jqXHR . status != 404 ) {
app . notifyServerError ( jqXHR , "Unable to refresh session data" )
}
else {
logger . debug ( "refreshCurrentSessionRest: could not refresh data for session because it's gone" )
}
} ,
2013-11-03 20:55:55 +00:00
complete : function ( ) {
requestingSessionRefresh = false ;
if ( pendingSessionRefresh ) {
2014-03-03 22:13:23 +00:00
// and when the request is done, if we have a pending, fire it off again
2013-11-03 20:55:55 +00:00
pendingSessionRefresh = false ;
2014-03-04 05:11:54 +00:00
refreshCurrentSessionRest ( sessionChanged , force ) ;
2013-11-03 20:55:55 +00:00
}
}
} ) ;
}
2013-03-02 19:02:42 +00:00
}
2013-03-08 19:46:56 +00:00
/ * *
* Seems silly . We should just have the bridge take sessionId , clientId
* /
function _toJamClientParticipant ( participant ) {
return {
userID : "" ,
clientID : participant . client _id ,
tcpPort : 0 ,
udpPort : 0 ,
localIPAddress : participant . ip _address , // ?
globalIPAddress : participant . ip _address , // ?
latency : 0 ,
natType : ""
} ;
}
2015-01-18 21:46:40 +00:00
function ParticipantJoined ( newSession , participant ) {
client . ParticipantJoined ( newSession , _toJamClientParticipant ( participant ) ) ;
currentParticipants [ participant . client _id ] = { server : participant , client : { audio _established : null } }
}
function ParticipantLeft ( newSession , participant ) {
client . ParticipantLeft ( newSession , _toJamClientParticipant ( participant ) ) ;
delete currentParticipants [ participant . client _id ]
}
2013-03-08 19:46:56 +00:00
function sendClientParticipantChanges ( oldSession , newSession ) {
2014-03-03 22:13:23 +00:00
var joins = [ ] , leaves = [ ] , leaveJoins = [ ] ; // Will hold JamClientParticipants
2013-03-08 19:46:56 +00:00
var oldParticipants = [ ] ; // will be set to session.participants if session
2014-03-03 22:13:23 +00:00
var oldParticipantIds = { } ;
2013-03-08 19:46:56 +00:00
var newParticipants = [ ] ;
2014-03-03 22:13:23 +00:00
var newParticipantIds = { } ;
2013-03-08 19:46:56 +00:00
if ( oldSession && oldSession . participants ) {
oldParticipants = oldSession . participants ;
$ . each ( oldParticipants , function ( ) {
2014-03-03 22:13:23 +00:00
oldParticipantIds [ this . client _id ] = this ;
2013-03-08 19:46:56 +00:00
} ) ;
}
if ( newSession && newSession . participants ) {
newParticipants = newSession . participants ;
$ . each ( newParticipants , function ( ) {
2014-03-03 22:13:23 +00:00
newParticipantIds [ this . client _id ] = this ;
2013-03-08 19:46:56 +00:00
} ) ;
}
2014-03-03 22:13:23 +00:00
$ . each ( newParticipantIds , function ( client _id , participant ) {
// grow the 'all participants seen' list
if ( ! ( client _id in participantsEverSeen ) ) {
participantsEverSeen [ client _id ] = participant ;
}
if ( client _id in oldParticipantIds ) {
// if the participant is here now, and here before, there is still a chance we missed a
// very fast leave/join. So check if joined_session_at is different
if ( oldParticipantIds [ client _id ] . joined _session _at != participant . joined _session _at ) {
2015-01-18 21:46:40 +00:00
leaveJoins . push ( participant )
2014-03-03 22:13:23 +00:00
}
}
else {
// new participant id that's not in old participant ids: Join
2015-01-18 21:46:40 +00:00
joins . push ( participant ) ;
2013-03-08 19:46:56 +00:00
}
} ) ;
2014-03-03 22:13:23 +00:00
$ . each ( oldParticipantIds , function ( client _id , participant ) {
if ( ! ( client _id in newParticipantIds ) ) {
2013-03-08 19:46:56 +00:00
// old participant id that's not in new participant ids: Leave
2015-01-18 21:46:40 +00:00
leaves . push ( participant ) ;
2013-03-08 19:46:56 +00:00
}
} ) ;
$ . each ( joins , function ( i , v ) {
if ( v . client _id != clientId ) {
2015-01-18 21:46:40 +00:00
logger . debug ( "jamClient.ParticipantJoined" , v . client _id )
ParticipantJoined ( newSession , v )
2013-03-08 19:46:56 +00:00
}
} ) ;
$ . each ( leaves , function ( i , v ) {
if ( v . client _id != clientId ) {
2015-01-18 21:46:40 +00:00
logger . debug ( "jamClient.ParticipantLeft" , v . client _id )
ParticipantLeft ( newSession , v )
2013-03-08 19:46:56 +00:00
}
} ) ;
2014-03-03 22:13:23 +00:00
$ . each ( leaveJoins , function ( i , v ) {
if ( v . client _id != clientId ) {
logger . debug ( "participant had a rapid leave/join" )
2015-01-18 21:46:40 +00:00
logger . debug ( "jamClient.ParticipantLeft" , v . client _id )
ParticipantLeft ( newSession , v )
logger . debug ( "jamClient.ParticipantJoined" , v . client _id )
ParticipantJoined ( newSession , v )
2014-03-03 22:13:23 +00:00
}
} ) ;
2013-03-08 19:46:56 +00:00
}
2013-03-02 19:02:42 +00:00
function participantForClientId ( clientId ) {
var foundParticipant = null ;
$ . each ( currentSession . participants , function ( index , participant ) {
if ( participant . client _id === clientId ) {
foundParticipant = participant ;
return false ;
}
} ) ;
return foundParticipant ;
}
2013-05-23 12:46:40 +00:00
function deleteTrack ( sessionId , trackId ) {
2013-11-16 04:35:40 +00:00
2013-05-22 11:48:37 +00:00
if ( trackId ) {
2013-11-16 04:35:40 +00:00
client . TrackSetCount ( 1 ) ;
client . TrackSaveAssignments ( ) ;
2013-05-22 11:48:37 +00:00
}
}
2013-03-02 19:02:42 +00:00
/ * *
* Make the server calls to join the current user to
* the session provided .
* /
2013-08-31 13:54:11 +00:00
function joinSessionRest ( sessionId ) {
2013-03-02 19:02:42 +00:00
var data = {
client _id : clientId ,
ip _address : server . publicIP ,
as _musician : true ,
2014-08-29 02:11:25 +00:00
tracks : userTracks ,
2014-06-09 20:43:16 +00:00
session _id : sessionId ,
audio _latency : context . jamClient . FTUEGetExpectedLatency ( ) . latency
2013-03-02 19:02:42 +00:00
} ;
2014-05-06 22:50:41 +00:00
2014-05-20 13:26:32 +00:00
return rest . joinSession ( data ) ;
2013-03-02 19:02:42 +00:00
}
2013-08-31 13:54:11 +00:00
function leaveSessionRest ( sessionId ) {
2013-03-02 19:02:42 +00:00
var url = "/api/participants/" + clientId ;
2013-08-31 13:54:11 +00:00
return $ . ajax ( {
2013-03-02 19:02:42 +00:00
type : "DELETE" ,
2014-02-25 19:22:32 +00:00
url : url
2013-03-02 19:02:42 +00:00
} ) ;
}
2014-11-11 22:21:29 +00:00
function setMixerMode ( newMixerMode ) {
if ( mixerMode != newMixerMode ) {
onMixerModeChanged ( newMixerMode ) ;
}
}
function isMasterMixMode ( ) {
return mixerMode == MIX _MODES . MASTER ;
}
2014-11-21 23:16:00 +00:00
function isPersonalMixMode ( ) {
return mixerMode == MIX _MODES . PERSONAL ;
}
2015-02-18 21:41:51 +00:00
function getMixMode ( ) {
return mixerMode ;
}
2014-11-17 23:16:30 +00:00
2015-03-13 02:53:23 +00:00
function syncTracks ( allTracks ) {
2015-02-02 17:39:28 +00:00
// double check that we are in session, since a bunch could have happened since then
if ( ! inSession ( ) ) {
logger . debug ( "dropping queued up sync tracks because no longer in session" ) ;
2015-02-03 15:25:45 +00:00
return null ;
2015-02-02 17:39:28 +00:00
}
2015-03-13 02:53:23 +00:00
if ( allTracks === undefined ) {
allTracks = context . JK . TrackHelpers . getTrackInfo ( context . jamClient ) ;
2015-02-03 15:25:45 +00:00
}
2015-03-13 02:53:23 +00:00
var inputTracks = allTracks . userTracks ;
var backingTracks = allTracks . backingTracks ;
var metronomeTracks = allTracks . metronomeTracks ;
2015-02-02 17:39:28 +00:00
// create a trackSync request based on backend data
var syncTrackRequest = { } ;
syncTrackRequest . client _id = app . clientId ;
syncTrackRequest . tracks = inputTracks ;
2015-02-03 15:25:45 +00:00
syncTrackRequest . backing _tracks = backingTracks ;
2015-03-13 02:53:23 +00:00
syncTrackRequest . metronome _open = metronomeTracks . length > 0 ;
2015-02-02 17:39:28 +00:00
syncTrackRequest . id = id ( ) ;
2015-02-03 15:25:45 +00:00
return rest . putTrackSyncChange ( syncTrackRequest )
2015-02-02 17:39:28 +00:00
. done ( function ( ) {
} )
. fail ( function ( jqXHR ) {
if ( jqXHR . status != 404 ) {
app . notify ( {
"title" : "Can't Sync Local Tracks" ,
"text" : "The client is unable to sync local track information with the server. You should rejoin the session to ensure a good experience." ,
"icon_url" : "/assets/content/icon_alert_big.png"
} ) ;
}
else {
logger . debug ( "Unable to sync local tracks because session is gone." )
}
} )
}
2013-09-01 19:23:09 +00:00
function onWebsocketDisconnected ( in _error ) {
2014-05-19 21:57:08 +00:00
// kill the streaming of the session immediately
if ( currentSessionId ) {
logger . debug ( "onWebsocketDisconnect: calling jamClient.LeaveSession for clientId=" + clientId ) ;
2013-08-31 13:54:11 +00:00
client . LeaveSession ( { sessionID : currentSessionId } ) ;
2014-05-19 21:57:08 +00:00
}
2013-08-31 13:54:11 +00:00
}
2013-11-03 20:55:55 +00:00
// returns a deferred object
function findUserBy ( finder ) {
if ( finder . clientId ) {
var foundParticipant = null ;
$ . each ( participants ( ) , function ( index , participant ) {
if ( participant . client _id == finder . clientId ) {
foundParticipant = participant ;
return false ;
}
} ) ;
if ( foundParticipant ) {
return $ . Deferred ( ) . resolve ( foundParticipant . user ) . promise ( ) ;
}
}
// TODO: find it via some REST API if not found?
return $ . Deferred ( ) . reject ( ) . promise ( ) ;
}
2014-06-11 21:52:46 +00:00
function onDeadUserRemove ( type , text ) {
2014-06-11 21:59:14 +00:00
if ( ! inSession ( ) ) return ;
2014-06-11 21:52:46 +00:00
var clientId = text ;
var participant = participantsEverSeen [ clientId ] ;
if ( participant ) {
app . notify ( {
2014-06-12 02:47:56 +00:00
"title" : ALERT _TYPES [ type ] . title ,
2014-06-11 21:52:46 +00:00
"text" : participant . user . name + " is no longer sending audio." ,
"icon_url" : context . JK . resolveAvatarUrl ( participant . user . photo _url )
} ) ;
var $track = $ ( 'div.track[client-id="' + clientId + '"]' ) ;
$ ( '.disabled-track-overlay' , $track ) . show ( ) ;
}
}
function onWindowBackgrounded ( type , text ) {
2014-09-22 19:20:58 +00:00
app . user ( )
. done ( function ( userProfile ) {
if ( userProfile . show _whats _next &&
window . location . pathname . indexOf ( gon . client _path ) == 0 &&
! app . layout . isDialogShowing ( 'getting-started' ) ) {
app . layout . showDialog ( 'getting-started' ) ;
}
} )
2014-06-11 21:59:14 +00:00
if ( ! inSession ( ) ) return ;
2014-06-12 02:47:56 +00:00
2014-06-11 21:52:46 +00:00
// the window was closed; just attempt to nav to home, which will cause all the right REST calls to happen
if ( sessionScreen ) {
sessionScreen . setPromptLeave ( false ) ;
context . location = '/client#/home'
}
}
2014-12-18 21:13:55 +00:00
function onBroadcastSuccess ( type , 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" )
}
}
function onBroadcastFailure ( type , 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" )
}
}
function onBroadcastStopped ( type , 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" )
}
}
2015-05-04 02:28:17 +00:00
function onPlaybackStateChange ( type , text ) {
// if text is play_start or play_stop, tell the play_controls
if ( sessionScreen ) {
sessionScreen . onPlaybackStateChange ( text ) ;
}
}
2014-06-11 21:52:46 +00:00
function onBackendMixerChanged ( type , text ) {
logger . debug ( "BACKEND_MIXER_CHANGE alert. reason:" + text ) ;
if ( inSession ( ) && text == "RebuildAudioIoControl" ) {
// the backend will send these events rapid-fire back to back.
// the server can still perform correctly, but it is nicer to wait 100 ms to let them all fall through
if ( backendMixerAlertThrottleTimer ) { clearTimeout ( backendMixerAlertThrottleTimer ) ; }
backendMixerAlertThrottleTimer = setTimeout ( function ( ) {
2014-10-27 17:42:30 +00:00
if ( sessionPageEnterDeferred ) {
// this means we are still waiting for the BACKEND_MIXER_CHANGE that indicates we have user tracks built-out/ready
// we will get at least one BACKEND_MIXER_CHANGE that corresponds to the backend doing a 'audio pause', which won't matter much
// so we need to check that we actaully have userTracks before considering ourselves done
var inputTracks = context . JK . TrackHelpers . getUserTracks ( context . jamClient ) ;
if ( inputTracks . length > 0 ) {
logger . debug ( "obtained tracks at start of session" )
sessionPageEnterDeferred . resolve ( inputTracks ) ;
sessionPageEnterDeferred = null ;
}
return ;
}
2014-10-27 18:26:04 +00:00
// wait until we are fully in session before trying to sync tracks to server
if ( joinDeferred ) {
joinDeferred . done ( function ( ) {
2015-02-02 17:39:28 +00:00
syncTracks ( ) ;
2014-06-11 21:52:46 +00:00
} )
2014-10-27 18:26:04 +00:00
}
2014-06-11 21:52:46 +00:00
} , 100 ) ;
}
else if ( inSession ( ) && ( text == 'RebuildMediaControl' || text == 'RebuildRemoteUserControl' ) ) {
2015-02-02 17:39:28 +00:00
2015-03-13 02:53:23 +00:00
var allTracks = context . JK . TrackHelpers . getTrackInfo ( context . jamClient ) ;
var backingTracks = allTracks . backingTracks ;
var previousBackingTracks = previousAllTracks . backingTracks ;
var metronomeTracks = allTracks . metronomeTracks ;
var previousMetronomeTracks = previousAllTracks . metronomeTracks ;
2015-02-03 15:25:45 +00:00
// the way we know if backing tracks changes, or recordings are opened, is via this event.
// but we want to report to the user when backing tracks change; so we need to detect change on our own
2015-03-04 01:06:55 +00:00
if ( ! ( previousBackingTracks . length == 0 && backingTracks . length == 0 ) && previousBackingTracks != backingTracks ) {
logger . debug ( "backing tracks changed" , previousBackingTracks , backingTracks )
2015-03-13 02:53:23 +00:00
syncTracks ( allTracks ) ;
}
else if ( ! ( previousMetronomeTracks . length == 0 && metronomeTracks . length == 0 ) && previousMetronomeTracks != metronomeTracks ) {
logger . debug ( "metronome state changed " , previousMetronomeTracks , metronomeTracks )
syncTracks ( allTracks ) ;
2015-02-03 15:25:45 +00:00
}
else {
refreshCurrentSession ( true ) ;
}
2015-03-13 02:53:23 +00:00
previousAllTracks = allTracks ;
2014-06-11 21:52:46 +00:00
}
2014-11-11 22:21:29 +00:00
else if ( inSession ( ) && ( text == 'Global Peer Input Mixer Mode' ) ) {
setMixerMode ( MIX _MODES . MASTER ) ;
}
else if ( inSession ( ) && ( text == 'Local Peer Stream Mixer Mode' ) ) {
setMixerMode ( MIX _MODES . PERSONAL ) ;
}
2014-06-11 21:52:46 +00:00
}
2013-03-02 19:02:42 +00:00
// Public interface
this . id = id ;
2014-08-31 15:30:59 +00:00
this . start = start ;
2015-01-21 04:14:48 +00:00
this . backingTrack = backingTrack ;
2015-02-16 04:01:06 +00:00
this . backingTracks = backingTracks ;
this . recordedBackingTracks = recordedBackingTracks ;
2015-03-26 22:45:23 +00:00
this . recordedJamTracks = recordedJamTracks ;
2014-08-29 02:11:25 +00:00
this . setUserTracks = setUserTracks ;
2014-01-05 03:47:23 +00:00
this . recordedTracks = recordedTracks ;
2015-03-26 22:45:23 +00:00
this . jamTrackName = jamTrackName ;
this . recordedJamTrackName = recordedJamTrackName ;
2015-01-09 21:15:12 +00:00
this . jamTracks = jamTracks ;
2013-03-02 19:02:42 +00:00
this . participants = participants ;
this . joinSession = joinSession ;
this . leaveCurrentSession = leaveCurrentSession ;
this . refreshCurrentSession = refreshCurrentSession ;
2015-02-16 04:01:06 +00:00
this . updateSession = updateSession ;
2013-03-02 19:02:42 +00:00
this . subscribe = subscribe ;
this . participantForClientId = participantForClientId ;
2014-01-05 03:47:23 +00:00
this . isPlayingRecording = isPlayingRecording ;
2013-05-22 11:48:37 +00:00
this . deleteTrack = deleteTrack ;
2013-08-31 13:54:11 +00:00
this . onWebsocketDisconnected = onWebsocketDisconnected ;
2013-11-03 20:55:55 +00:00
this . recordingModel = recordingModel ;
this . findUserBy = findUserBy ;
2014-08-29 02:11:25 +00:00
this . inSession = inSession ;
2014-11-11 22:21:29 +00:00
this . setMixerMode = setMixerMode ;
this . isMasterMixMode = isMasterMixMode ;
2014-11-21 23:16:00 +00:00
this . isPersonalMixMode = isPersonalMixMode ;
2014-11-17 23:16:30 +00:00
this . getMixMode = getMixMode ;
2015-02-25 16:43:21 +00:00
this . selfOpenedJamTracks = selfOpenedJamTracks ;
2015-03-13 02:53:23 +00:00
this . isMetronomeOpen = isMetronomeOpen ;
2015-02-25 22:13:04 +00:00
this . areControlsLockedForJamTrackRecording = areControlsLockedForJamTrackRecording ;
this . lockControlsforJamTrackRecording = lockControlsforJamTrackRecording ;
this . unlockControlsforJamTrackRecording = unlockControlsforJamTrackRecording ;
2014-06-11 21:52:46 +00:00
// ALERT HANDLERS
this . onBackendMixerChanged = onBackendMixerChanged ;
this . onDeadUserRemove = onDeadUserRemove ;
this . onWindowBackgrounded = onWindowBackgrounded ;
2014-08-29 02:11:25 +00:00
this . waitForSessionPageEnterDone = waitForSessionPageEnterDone ;
2014-12-18 21:13:55 +00:00
this . onBroadcastStopped = onBroadcastStopped ;
this . onBroadcastSuccess = onBroadcastSuccess ;
this . onBroadcastFailure = onBroadcastFailure ;
2015-05-04 02:28:17 +00:00
this . onPlaybackStateChange = onPlaybackStateChange ;
2014-06-11 21:52:46 +00:00
2013-06-10 02:12:43 +00:00
this . getCurrentSession = function ( ) {
return currentSession ;
} ;
2013-12-30 18:34:15 +00:00
this . getCurrentOrLastSession = function ( ) {
return currentOrLastSession ;
} ;
2014-03-03 22:13:23 +00:00
this . getParticipant = function ( clientId ) {
return participantsEverSeen [ clientId ]
} ;
2015-02-03 15:25:45 +00:00
this . setBackingTrack = function ( backingTrack ) {
openBackingTrack = backingTrack ;
} ;
this . getBackingTrack = function ( ) {
return openBackingTrack ;
} ;
2015-02-19 15:31:30 +00:00
this . hasShownAudioMediaMixerHelp = function ( ) {
return shownAudioMediaMixerHelp ;
}
this . markShownAudioMediaMixerHelp = function ( ) {
shownAudioMediaMixerHelp = true ;
}
2015-01-18 21:46:40 +00:00
2015-02-18 21:41:51 +00:00
2015-01-18 21:46:40 +00:00
// call to report if the current user was able to establish audio with the specified clientID
this . setAudioEstablished = function ( clientId , audioEstablished ) {
var participant = currentParticipants [ clientId ]
if ( participant ) {
if ( participant . client . audio _established === null ) {
participant . client . audio _established = audioEstablished ;
2015-01-19 02:51:27 +00:00
context . stats . write ( 'client.audio_established.' + ( audioEstablished ? 'success' : 'failure' ) , { user _id : context . JK . currentUserId , name : context . JK . currentUserName , remote _user _id : participant . server . user . id , remote _name : participant . server . user . name , value : 1 } )
2015-01-18 21:46:40 +00:00
}
else if ( participant . client . audio _established === false && audioEstablished === true ) {
// this means the frontend had declared this audio failed, but later says it works.
// this could mean our threshold of time to wait before audio is considered failed is too low, and needs tweaking
2015-01-19 02:51:27 +00:00
context . stats . write ( 'client.audio_established.delayed' , { user _id : context . JK . currentUserId , name : context . JK . currentUserName , remote _user _id : participant . server . user . id , remote _name : participant . server . user . name , value : 1 } )
2015-01-18 21:46:40 +00:00
}
}
}
2014-08-31 15:30:59 +00:00
this . ensureEnded = function ( ) {
updateCurrentSession ( null ) ;
2014-02-25 19:22:32 +00:00
}
2012-10-29 00:37:59 +00:00
} ;
2012-11-03 20:25:23 +00:00
} ) ( window , jQuery ) ;