2012-10-06 16:36:05 +00:00
( function ( context , $ ) {
2012-12-07 00:28:48 +00:00
"use strict" ;
2012-10-06 16:36:05 +00:00
context . JK = context . JK || { } ;
context . JK . SessionScreen = function ( app ) {
2014-10-23 04:10:49 +00:00
var EVENTS = context . JK . EVENTS ;
2014-11-11 22:21:29 +00:00
var MIX _MODES = context . JK . MIX _MODES ;
var NAMED _MESSAGES = context . JK . NAMED _MESSAGES ;
2014-06-25 21:54:31 +00:00
var gearUtils = context . JK . GearUtils ;
2014-08-29 02:11:25 +00:00
var sessionUtils = context . JK . SessionUtils ;
2014-11-11 22:21:29 +00:00
var modUtils = context . JK . ModUtils ;
2012-10-14 15:48:56 +00:00
var logger = context . JK . logger ;
2014-02-23 04:41:42 +00:00
var self = this ;
2013-03-02 19:02:42 +00:00
var sessionModel = null ;
2012-10-29 15:10:02 +00:00
var sessionId ;
2012-10-22 02:55:05 +00:00
var tracks = { } ;
2013-05-15 05:59:09 +00:00
var myTracks = [ ] ;
2014-11-17 23:16:30 +00:00
var masterMixers = [ ] ;
var personalMixers = [ ] ;
2014-11-21 23:16:00 +00:00
var allMixers = { } ;
var mixersByResourceId = { } ;
var mixersByTrackId = { } ;
2013-05-22 05:03:34 +00:00
var configureTrackDialog ;
2013-05-23 12:46:40 +00:00
var addNewGearDialog ;
2014-01-05 03:47:23 +00:00
var localRecordingsDialog = null ;
2014-01-06 20:35:35 +00:00
var recordingFinishedDialog = null ;
2014-01-13 16:11:26 +00:00
var friendSelectorDialog = null ;
2014-01-14 06:12:00 +00:00
var inviteMusiciansUtil = null ;
2013-05-31 02:07:33 +00:00
var screenActive = false ;
2013-02-03 22:47:17 +00:00
var currentMixerRangeMin = null ;
var currentMixerRangeMax = null ;
2013-02-14 07:02:05 +00:00
var lookingForMixersCount = 0 ;
var lookingForMixersTimer = null ;
2014-11-21 23:16:00 +00:00
var lookingForMixers = [ ] ;
2013-11-03 20:55:55 +00:00
var $recordingTimer = null ;
var recordingTimerInterval = null ;
var startTimeDate = null ;
var startingRecording = false ; // double-click guard
2014-01-05 03:47:23 +00:00
var claimedRecording = null ;
var playbackControls = null ;
2014-02-23 04:41:42 +00:00
var promptLeave = false ;
2014-05-01 01:48:57 +00:00
var rateSessionDialog = null ;
2014-08-29 02:11:25 +00:00
var friendInput = null ;
var sessionPageDone = null ;
2014-10-23 04:10:49 +00:00
var $recordingManagerViewer = null ;
var $screen = null ;
2014-11-09 15:13:22 +00:00
var $mixModeDropdown = null ;
2014-11-11 22:21:29 +00:00
var $templateMixerModeChange = null ;
2013-02-14 07:02:05 +00:00
2014-11-11 22:21:29 +00:00
var rest = context . JK . Rest ( ) ;
2013-09-10 02:07:12 +00:00
var RENDER _SESSION _DELAY = 750 ; // When I need to render a session, I have to wait a bit for the mixers to be there.
2013-02-05 05:10:37 +00:00
var defaultParticipant = {
2013-02-10 00:33:47 +00:00
tracks : [ {
instrument _id : "unknown"
} ] ,
2013-02-05 05:10:37 +00:00
user : {
first _name : 'Unknown' ,
last _name : 'User' ,
photo _url : null
}
} ;
2013-08-03 18:30:45 +00:00
// Be sure to copy/extend these instead of modifying in place
var trackVuOpts = {
vuType : "vertical" ,
lightCount : 13 ,
lightWidth : 3 ,
lightHeight : 17
} ;
// Must add faderId key to this
var trackFaderOpts = {
faderType : "vertical" ,
height : 83
} ;
2013-02-26 03:54:09 +00:00
// Recreate ChannelGroupIDs ENUM from C++
var ChannelGroupIds = {
"MasterGroup" : 0 ,
"MonitorGroup" : 1 ,
"AudioInputMusicGroup" : 2 ,
"AudioInputChatGroup" : 3 ,
"MediaTrackGroup" : 4 ,
"StreamOutMusicGroup" : 5 ,
"StreamOutChatGroup" : 6 ,
"UserMusicInputGroup" : 7 ,
"UserChatInputGroup" : 8 ,
2013-09-11 22:29:32 +00:00
"PeerAudioInputMusicGroup" : 9 ,
2013-09-12 12:02:13 +00:00
"PeerMediaTrackGroup" : 10
2013-02-26 03:54:09 +00:00
} ;
2013-02-05 05:10:37 +00:00
2012-10-29 15:10:02 +00:00
function beforeShow ( data ) {
sessionId = data . id ;
2014-08-29 02:11:25 +00:00
if ( ! sessionId ) {
window . location = '/client#/home' ;
}
2014-02-23 04:41:42 +00:00
promptLeave = true ;
2013-02-06 04:32:56 +00:00
$ ( '#session-mytracks-container' ) . empty ( ) ;
2013-11-03 20:55:55 +00:00
displayDoneRecording ( ) ; // assumption is that you can't join a recording session, so this should be safe
2014-02-07 05:57:31 +00:00
var shareDialog = new JK . ShareDialog ( context . JK . app , sessionId , "session" ) ;
shareDialog . initialize ( context . JK . FacebookHelperInstance ) ;
2012-10-29 15:10:02 +00:00
}
2012-10-29 00:37:59 +00:00
2014-04-09 17:25:52 +00:00
function beforeDisconnect ( ) {
return { freezeInteraction : true } ;
}
2014-03-03 22:13:23 +00:00
2014-03-01 23:01:45 +00:00
function initializeSession ( ) {
// Subscribe for callbacks on audio events
context . jamClient . SessionRegisterCallback ( "JK.HandleBridgeCallback" ) ;
context . jamClient . RegisterRecordingCallbacks ( "JK.HandleRecordingStartResult" , "JK.HandleRecordingStopResult" , "JK.HandleRecordingStarted" , "JK.HandleRecordingStopped" , "JK.HandleRecordingAborted" ) ;
context . jamClient . SessionSetConnectionStatusRefreshRate ( 1000 ) ;
// If you load this page directly, the loading of the current user
// is happening in parallel. We can't join the session until the
// current user has been completely loaded. Poll for the current user
// before proceeding with session joining.
function checkForCurrentUser ( ) {
if ( context . JK . userMe ) {
afterCurrentUserLoaded ( ) ;
} else {
context . setTimeout ( checkForCurrentUser , 100 ) ;
}
}
checkForCurrentUser ( ) ;
}
2012-10-06 16:36:05 +00:00
function afterShow ( data ) {
2013-05-31 02:07:33 +00:00
2014-06-26 02:50:20 +00:00
if ( ! context . JK . JamServer . connected ) {
promptLeave = false ;
app . notifyAlert ( "Not Connected" , 'To create or join a session, you must be connected to the server.' ) ;
window . location = '/client#/home'
return ;
}
2014-04-09 17:25:52 +00:00
2014-08-29 02:11:25 +00:00
// The SessionModel is a singleton.
// a client can only be in one session at a time,
// and other parts of the code want to know at any certain times
// about the current session, if any (for example, reconnect logic)
if ( context . JK . CurrentSessionModel ) {
2014-08-31 15:30:59 +00:00
context . JK . CurrentSessionModel . ensureEnded ( ) ;
2014-08-29 02:11:25 +00:00
}
2014-08-31 15:30:59 +00:00
2014-08-29 02:11:25 +00:00
context . JK . CurrentSessionModel = sessionModel = new context . JK . SessionModel (
context . JK . app ,
context . JK . JamServer ,
context . jamClient ,
self
) ;
2014-08-31 15:30:59 +00:00
sessionModel . start ( sessionId ) ;
2014-08-29 02:11:25 +00:00
// indicate that the screen is active, so that
// body-scoped drag handlers can go active
screenActive = true ;
2014-06-25 21:54:31 +00:00
gearUtils . guardAgainstInvalidConfiguration ( app )
. fail ( function ( ) {
promptLeave = false ;
window . location = '/client#/home'
} )
. done ( function ( ) {
2014-08-29 02:11:25 +00:00
var result = sessionUtils . SessionPageEnter ( ) ;
gearUtils . guardAgainstActiveProfileMissing ( app , result )
. fail ( function ( data ) {
promptLeave = false ;
if ( data && data . reason == 'handled' ) {
if ( data . nav == 'BACK' ) {
window . history . go ( - 1 ) ;
}
else {
window . location = data . nav ;
}
}
else {
window . location = '/client#/home' ;
}
} )
. done ( function ( ) {
sessionModel . waitForSessionPageEnterDone ( )
. done ( function ( userTracks ) {
context . JK . CurrentSessionModel . setUserTracks ( userTracks ) ;
initializeSession ( ) ;
} )
. fail ( function ( data ) {
if ( data == "timeout" ) {
context . JK . alertSupportedNeeded ( 'The audio system has not reported your configured tracks in a timely fashion.' )
}
else if ( data == 'session_over' ) {
// do nothing; session ended before we got the user track info. just bail
}
else {
contetx . JK . alertSupportedNeeded ( 'Unable to determine configured tracks due to reason: ' + data )
}
promptLeave = false ;
window . location = '/client#/home'
} ) ;
} )
2014-06-25 21:54:31 +00:00
} )
2014-07-27 23:39:11 +00:00
2014-08-29 02:11:25 +00:00
2013-02-26 03:54:09 +00:00
}
2013-11-16 04:35:40 +00:00
function notifyWithUserInfo ( title , text , clientId ) {
sessionModel . findUserBy ( { clientId : clientId } )
. done ( function ( user ) {
app . notify ( {
"title" : title ,
"text" : user . name + " " + text ,
"icon_url" : context . JK . resolveAvatarUrl ( user . photo _url )
} ) ;
} )
. fail ( function ( ) {
app . notify ( {
"title" : title ,
"text" : 'Someone ' + text ,
"icon_url" : "/assets/content/icon_alert_big.png"
} ) ;
} ) ;
}
2013-02-26 03:54:09 +00:00
function afterCurrentUserLoaded ( ) {
2014-08-29 02:11:25 +00:00
var sessionModel = context . JK . CurrentSessionModel ;
2013-08-31 13:54:11 +00:00
2013-11-03 20:55:55 +00:00
$ ( sessionModel . recordingModel )
. on ( 'startingRecording' , function ( e , data ) {
2013-11-16 04:35:40 +00:00
displayStartingRecording ( ) ;
} )
. on ( 'startedRecording' , function ( e , data ) {
2013-11-03 20:55:55 +00:00
if ( data . reason ) {
2013-11-16 04:35:40 +00:00
var reason = data . reason ;
var detail = data . detail ;
var title = "Could Not Start Recording" ;
if ( data . reason == 'client-no-response' ) {
notifyWithUserInfo ( title , 'did not respond to the start signal.' , detail ) ;
}
else if ( data . reason == 'empty-recording-id' ) {
app . notifyAlert ( title , "No recording ID specified." ) ;
}
else if ( data . reason == 'missing-client' ) {
notifyWithUserInfo ( title , 'could not be signalled to start recording.' , detail ) ;
}
else if ( data . reason == 'already-recording' ) {
2014-11-03 21:24:46 +00:00
app . notifyAlert ( title , 'Already recording. If this appears incorrect, try restarting JamKazam.' ) ;
2013-11-16 04:35:40 +00:00
}
else if ( data . reason == 'recording-engine-unspecified' ) {
notifyWithUserInfo ( title , 'had a problem writing recording data to disk.' , detail ) ;
}
else if ( data . reason == 'recording-engine-create-directory' ) {
notifyWithUserInfo ( title , 'had a problem creating a recording folder.' , detail ) ;
}
else if ( data . reason == 'recording-engine-create-file' ) {
notifyWithUserInfo ( title , 'had a problem creating a recording file.' , detail ) ;
}
else if ( data . reason == 'recording-engine-sample-rate' ) {
notifyWithUserInfo ( title , 'had a problem recording at the specified sample rate.' , detail ) ;
}
2014-01-05 03:47:23 +00:00
else if ( data . reason == 'rest' ) {
var jqXHR = detail [ 0 ] ;
app . notifyServerError ( jqXHR ) ;
}
2013-11-16 04:35:40 +00:00
else {
notifyWithUserInfo ( title , 'Error Reason: ' + reason ) ;
}
2013-11-03 20:55:55 +00:00
displayDoneRecording ( ) ;
}
2013-11-16 04:35:40 +00:00
else
{
displayStartedRecording ( ) ;
displayWhoCreated ( data . clientId ) ;
2013-11-03 20:55:55 +00:00
}
} )
. on ( 'stoppingRecording' , function ( e , data ) {
displayStoppingRecording ( data ) ;
} )
. on ( 'stoppedRecording' , function ( e , data ) {
if ( data . reason ) {
2014-05-19 23:49:02 +00:00
logger . warn ( "Recording Discarded: " , data ) ;
2013-11-03 20:55:55 +00:00
var reason = data . reason ;
2013-11-16 04:35:40 +00:00
var detail = data . detail ;
var title = "Recording Discarded" ;
2013-11-03 20:55:55 +00:00
if ( data . reason == 'client-no-response' ) {
2013-11-16 04:35:40 +00:00
notifyWithUserInfo ( title , 'did not respond to the stop signal.' , detail ) ;
2013-11-03 20:55:55 +00:00
}
2013-11-16 04:35:40 +00:00
else if ( data . reason == 'missing-client' ) {
notifyWithUserInfo ( title , 'could not be signalled to stop recording.' , detail ) ;
}
else if ( data . reason == 'empty-recording-id' ) {
app . notifyAlert ( title , "No recording ID specified." ) ;
}
else if ( data . reason == 'wrong-recording-id' ) {
app . notifyAlert ( title , "Wrong recording ID specified." ) ;
}
else if ( data . reason == 'not-recording' ) {
app . notifyAlert ( title , "Not currently recording." ) ;
}
else if ( data . reason == 'already-stopping' ) {
app . notifyAlert ( title , "Already stopping the current recording." ) ;
}
else if ( data . reason == 'start-before-stop' ) {
notifyWithUserInfo ( title , 'asked that we start a new recording; cancelling the current one.' , detail ) ;
}
else {
app . notifyAlert ( title , "Error reason: " + reason ) ;
}
2013-11-03 20:55:55 +00:00
displayDoneRecording ( ) ;
}
else {
displayDoneRecording ( ) ;
promptUserToSave ( data . recordingId ) ;
}
} )
2013-11-16 04:35:40 +00:00
. on ( 'abortedRecording' , function ( e , data ) {
var reason = data . reason ;
var detail = data . detail ;
2013-11-03 20:55:55 +00:00
2013-11-16 04:35:40 +00:00
var title = "Recording Cancelled" ;
2013-11-03 20:55:55 +00:00
2013-11-16 04:35:40 +00:00
if ( data . reason == 'client-no-response' ) {
notifyWithUserInfo ( title , 'did not respond to the start signal.' , detail ) ;
}
else if ( data . reason == 'missing-client' ) {
notifyWithUserInfo ( title , 'could not be signalled to start recording.' , detail ) ;
}
else if ( data . reason == 'populate-recording-info' ) {
notifyWithUserInfo ( title , 'could not synchronize with the server.' , detail ) ;
}
else if ( data . reason == 'recording-engine-unspecified' ) {
notifyWithUserInfo ( title , 'had a problem writing recording data to disk.' , detail ) ;
}
else if ( data . reason == 'recording-engine-create-directory' ) {
notifyWithUserInfo ( title , 'had a problem creating a recording folder.' , detail ) ;
}
else if ( data . reason == 'recording-engine-create-file' ) {
notifyWithUserInfo ( title , 'had a problem creating a recording file.' , detail ) ;
}
else if ( data . reason == 'recording-engine-sample-rate' ) {
notifyWithUserInfo ( title , 'had a problem recording at the specified sample rate.' , detail ) ;
}
else {
app . notifyAlert ( title , "Error reason: " + reason ) ;
}
displayDoneRecording ( ) ;
} )
2013-11-03 20:55:55 +00:00
2013-03-02 19:02:42 +00:00
sessionModel . subscribe ( 'sessionScreen' , sessionChanged ) ;
2013-08-31 13:54:11 +00:00
sessionModel . joinSession ( sessionId )
. fail ( function ( xhr , textStatus , errorMessage ) {
if ( xhr . status == 404 ) {
// we tried to join the session, but it's already gone. kick user back to join session screen
2014-02-23 05:21:36 +00:00
promptLeave = false ;
2014-02-06 13:03:44 +00:00
context . window . location = "/client#/findSession" ;
2013-08-31 13:54:11 +00:00
app . notify (
{ title : "Unable to Join Session" ,
text : "The session you attempted to join is over."
} ,
2014-05-14 05:16:33 +00:00
null ,
true ) ;
2013-10-03 07:16:27 +00:00
}
2014-07-31 15:32:20 +00:00
else if ( xhr . status == 422 ) {
2014-08-29 02:11:25 +00:00
var response = JSON . parse ( xhr . responseText ) ;
2014-07-31 15:32:20 +00:00
if ( response [ "errors" ] && response [ "errors" ] [ "tracks" ] && response [ "errors" ] [ "tracks" ] [ 0 ] == "Please select at least one track" ) {
app . notifyAlert ( "No Inputs Configured" , $ ( '<span>You will need to reconfigure your audio device.</span>' ) ) ;
}
2014-10-29 03:08:22 +00:00
else if ( response [ "errors" ] && response [ "errors" ] [ "music_session" ] && response [ "errors" ] [ "music_session" ] [ 0 ] == [ "is currently recording" ] ) {
promptLeave = false ;
context . window . location = "/client#/findSession" ;
app . notify ( { title : "Unable to Join Session" , text : "The session is currently recording." } , null , true ) ;
}
else {
app . notifyServerError ( xhr , 'Unable to Join Session' ) ;
}
2014-07-31 15:32:20 +00:00
}
2013-10-03 07:16:27 +00:00
else {
2014-03-01 23:01:45 +00:00
app . notifyServerError ( xhr , 'Unable to Join Session' ) ;
2013-08-31 13:54:11 +00:00
}
2013-09-05 04:24:28 +00:00
} ) ;
2013-02-10 18:51:37 +00:00
}
2014-02-25 02:20:17 +00:00
// not leave session but leave screen
2014-02-23 04:41:42 +00:00
function beforeLeave ( data ) {
if ( promptLeave ) {
var leaveSessionWarningDialog = new context . JK . LeaveSessionWarningDialog ( context . JK . app ,
function ( ) { promptLeave = false ; context . location . hash = data . hash } ) ;
leaveSessionWarningDialog . initialize ( ) ;
app . layout . showDialog ( 'leave-session-warning' ) ;
return false ;
}
2014-02-25 19:22:32 +00:00
return true ;
2014-02-23 04:41:42 +00:00
}
2014-02-22 18:43:11 +00:00
function beforeHide ( data ) {
2014-03-01 23:01:45 +00:00
if ( screenActive ) {
// this path is possible if FTUE is invoked on session page, and they cancel
sessionModel . leaveCurrentSession ( )
2014-10-29 03:08:22 +00:00
. fail ( function ( jqXHR ) {
if ( jqXHR . status != 404 ) {
logger . debug ( "leave session failed" ) ;
app . ajaxError ( arguments )
}
} ) ;
2014-03-01 23:01:45 +00:00
}
screenActive = false ;
2014-08-29 02:11:25 +00:00
sessionUtils . SessionPageLeave ( ) ;
2012-10-06 16:36:05 +00:00
}
2014-01-05 03:47:23 +00:00
function handleTransitionsInRecordingPlayback ( ) {
// let's see if we detect a transition to start playback or stop playback
var currentSession = sessionModel . getCurrentSession ( ) ;
if ( claimedRecording == null && ( currentSession && currentSession . claimed _recording != null ) ) {
// this is a 'started with a claimed_recording' transition.
// we need to start a timer to watch for the state of the play session
2014-01-06 20:35:35 +00:00
playbackControls . startMonitor ( ) ;
2014-01-05 03:47:23 +00:00
}
else if ( claimedRecording && ( currentSession == null || currentSession . claimed _recording == null ) ) {
2014-01-06 20:35:35 +00:00
playbackControls . stopMonitor ( ) ;
2014-01-05 03:47:23 +00:00
}
claimedRecording = currentSession == null ? null : currentSession . claimed _recording ;
}
2013-03-02 19:02:42 +00:00
function sessionChanged ( ) {
2013-09-06 12:34:57 +00:00
2014-01-05 03:47:23 +00:00
handleTransitionsInRecordingPlayback ( ) ;
2013-08-19 00:54:32 +00:00
// TODO - in the specific case of a user changing their tracks using the configureTrack dialog,
// this event appears to fire before the underlying mixers have updated. I have no event to
// know definitively when the underlying mixers are up to date, so for now, we just delay slightly.
// This obviously has the possibility of introducing time-based bugs.
2013-09-10 02:07:12 +00:00
context . setTimeout ( renderSession , RENDER _SESSION _DELAY ) ;
2013-08-19 00:54:32 +00:00
}
/ * *
* the mixers object is a list . In order to find one by key ,
* you must iterate . Convenience method to locate a particular
* mixer by id .
* /
2014-11-21 23:16:00 +00:00
function getMixer ( mixerId ) {
return allMixers [ mixerId ] ;
}
function getMixerByResourceId ( resourceId , mode ) {
var mixerPair = mixersByResourceId [ resourceId ] ;
if ( ! mixerPair ) { return null ; }
if ( mode === undefined ) {
return mixerPair ;
}
else {
if ( mode == MIX _MODES . MASTER ) {
return mixerPair . master ;
}
else {
return mixerPair . personal ;
}
}
}
function getMixerByTrackId ( trackId , mode ) {
var mixerPair = mixersByTrackId [ trackId ] ;
if ( ! mixerPair ) { return null ; }
if ( mode === undefined ) {
return mixerPair ;
}
else {
if ( mode == MIX _MODES . MASTER ) {
return mixerPair . master ;
}
else {
return mixerPair . personal ;
}
}
2012-11-03 20:25:23 +00:00
}
function renderSession ( ) {
2013-02-14 07:02:05 +00:00
$ ( '#session-mytracks-container' ) . empty ( ) ;
2013-06-24 03:08:38 +00:00
$ ( '.session-track' ) . remove ( ) ; // Remove previous tracks
2013-09-06 02:27:43 +00:00
var $voiceChat = $ ( '#voice-chat' ) ;
$voiceChat . hide ( ) ;
2013-01-30 16:50:43 +00:00
_updateMixers ( ) ;
2012-11-03 20:25:23 +00:00
_renderTracks ( ) ;
2014-01-05 03:47:23 +00:00
_renderLocalMediaTracks ( ) ;
2013-02-07 04:58:41 +00:00
_wireTopVolume ( ) ;
2013-04-10 15:01:29 +00:00
_wireTopMix ( ) ;
2013-02-07 04:58:41 +00:00
_addVoiceChat ( ) ;
2013-05-22 05:03:34 +00:00
_initDialogs ( ) ;
2013-06-24 03:08:38 +00:00
if ( $ ( '.session-livetracks .track' ) . length === 0 ) {
$ ( '.session-livetracks .when-empty' ) . show ( ) ;
}
2014-01-05 03:47:23 +00:00
if ( $ ( '.session-recordings .track' ) . length === 0 ) {
$ ( '.session-recordings .when-empty' ) . show ( ) ;
$ ( '.session-recording-name-wrapper' ) . hide ( ) ;
2014-01-06 20:35:35 +00:00
$ ( '.session-recordings .recording-controls' ) . hide ( ) ;
2014-01-05 03:47:23 +00:00
}
2013-05-22 05:03:34 +00:00
}
2013-05-12 05:43:36 +00:00
2013-05-22 05:03:34 +00:00
function _initDialogs ( ) {
configureTrackDialog . initialize ( ) ;
2013-05-23 12:46:40 +00:00
addNewGearDialog . initialize ( ) ;
2013-01-30 16:50:43 +00:00
}
2014-11-21 23:16:00 +00:00
// Get the latest list of underlying audio mixer channels, and populates:
// * mixersByResourceId - a hash of resourceId / { master: mixer, personal: mixer } personal: can be null in case of PeerAudioInputMusicGroup
// * mixersByTrackId - a hash of track id / {master: mixer, personal: mixer}.
// * allMixers - a hash of mixer.id / mixer
// * masterMixers - array of master mode mixers
// * personalMixers - array of personal mode mixers
2013-01-30 16:50:43 +00:00
function _updateMixers ( ) {
2014-11-17 23:16:30 +00:00
masterMixers = context . jamClient . SessionGetAllControlState ( true ) ;
personalMixers = context . jamClient . SessionGetAllControlState ( false ) ;
2014-11-11 22:21:29 +00:00
2014-11-21 23:16:00 +00:00
//logger.debug("masterMixers", masterMixers)
//logger.debug("personalMixers", personalMixers)
mixersByResourceId = { }
mixersByTrackId = { }
allMixers = { }
var i ;
for ( i = 0 ; i < masterMixers . length ; i ++ ) {
var masterMixer = masterMixers [ i ] ;
allMixers [ masterMixer . id ] = masterMixer ; // populate allMixers by mixer.id
// populate mixer pair
var mixerPair = { }
mixersByResourceId [ masterMixer . rid ] = mixerPair
mixersByTrackId [ masterMixer . id ] = mixerPair
mixerPair . master = masterMixer ;
}
for ( i = 0 ; i < personalMixers . length ; i ++ ) {
var personalMixer = personalMixers [ i ] ;
if ( personalMixer . group _id == ChannelGroupIds . MediaTrackGroup ) {
continue ;
}
allMixers [ personalMixer . id ] = personalMixer
// populate other side of mixer pair
var mixerPair = mixersByResourceId [ personalMixer . rid ]
if ( ! mixerPair ) {
if ( personalMixer . group _id != ChannelGroupIds . MonitorGroup ) {
logger . warn ( "there is no master version of " , personalMixer )
}
mixerPair = { }
mixersByResourceId [ personalMixer . rid ] = mixerPair
}
mixersByTrackId [ personalMixer . id ] = mixerPair ;
mixerPair . personal = personalMixer ;
}
2013-04-10 15:01:29 +00:00
// Always add a hard-coded simplified 'mixer' for the L2M mix
2014-11-09 15:13:22 +00:00
2014-11-11 22:21:29 +00:00
/ * *
var l2m _mixer = {
id : '__L2M__' ,
range _low : - 80 ,
range _high : 20 ,
volume _left : context . jamClient . SessionGetMasterLocalMix ( )
} ;
mixers . push ( l2m _mixer ) ; * /
2012-10-21 23:03:46 +00:00
}
2014-11-17 23:16:30 +00:00
function _mixersForGroupId ( groupId , mixMode ) {
2014-01-05 03:47:23 +00:00
var foundMixers = [ ] ;
2014-11-17 23:16:30 +00:00
var mixers = mixMode == MIX _MODES . MASTER ? masterMixers : personalMixers ;
2014-01-05 03:47:23 +00:00
$ . each ( mixers , function ( index , mixer ) {
2014-11-17 23:16:30 +00:00
if ( mixer . group _id === groupId ) {
2014-01-05 03:47:23 +00:00
foundMixers . push ( mixer ) ;
}
} ) ;
return foundMixers ;
}
2014-12-17 15:08:45 +00:00
function _getMyVoiceChatMixers ( ) {
var mixers = _mixersForGroupId ( ChannelGroupIds . AudioInputChatGroup , sessionModel . getMixMode ( ) ) ;
if ( mixers . length == 0 ) { return null ; }
var oppositeMixers = _mixersForGroupId ( ChannelGroupIds . AudioInputChatGroup , ! sessionModel . getMixMode ( ) ) ;
if ( oppositeMixers . length == 0 ) {
logger . warn ( "unable to find opposite mixer for voice chat" ) ;
return null ;
}
var mixer = mixers [ 0 ] ;
var oppositeMixer = oppositeMixers [ 0 ] ;
return {
mixer : mixer ,
oppositeMixer : oppositeMixer ,
vuMixer : mixer ,
muteMixer : mixer
}
}
2014-03-03 22:49:53 +00:00
2014-11-17 23:16:30 +00:00
function _clientIdForUserInputMixer ( mixerId , mixMode ) {
2014-03-03 22:49:53 +00:00
var found = null ;
2014-11-17 23:16:30 +00:00
var mixers = mixMode == MIX _MODES . MASTER ? masterMixers : personalMixers ;
2014-03-03 22:49:53 +00:00
$ . each ( mixers , function ( index , mixer ) {
if ( mixer . group _id === ChannelGroupIds . UserMusicInputGroup && mixer . id == mixerId ) {
found = mixer . client _id ;
return false ;
}
} ) ;
return found ;
}
2013-08-23 03:20:07 +00:00
// TODO FIXME - This needs to support multiple tracks for an individual
// client id and group.
2013-09-05 04:24:28 +00:00
function _mixerForClientId ( clientId , groupIds , usedMixers ) {
2014-06-20 17:50:31 +00:00
//logger.debug("clientId", clientId, "groupIds", groupIds, "mixers", mixers)
2013-02-14 07:02:05 +00:00
var foundMixer = null ;
$ . each ( mixers , function ( index , mixer ) {
if ( mixer . client _id === clientId ) {
for ( var i = 0 ; i < groupIds . length ; i ++ ) {
if ( mixer . group _id === groupIds [ i ] ) {
2013-09-05 04:24:28 +00:00
if ( ! ( mixer . id in usedMixers ) ) {
foundMixer = mixer ;
return false ;
}
2013-02-14 07:02:05 +00:00
}
}
2013-02-05 05:10:37 +00:00
}
} ) ;
2013-02-14 07:02:05 +00:00
return foundMixer ;
2013-01-30 16:50:43 +00:00
}
2014-11-17 23:16:30 +00:00
function _groupedMixersForClientId ( clientId , groupIds , usedMixers , mixMode ) {
2014-11-12 02:39:46 +00:00
//logger.debug("clientId", clientId, "groupIds", groupIds, "mixers", mixers)
var foundMixers = { } ;
2014-11-17 23:16:30 +00:00
var mixers = mixMode == MIX _MODES . MASTER ? masterMixers : personalMixers ;
2014-11-21 23:16:00 +00:00
// console.log("_groupedMixersForClientId", mixers)
2014-11-12 02:39:46 +00:00
$ . each ( mixers , function ( index , mixer ) {
if ( mixer . client _id === clientId ) {
for ( var i = 0 ; i < groupIds . length ; i ++ ) {
if ( mixer . group _id === groupIds [ i ] ) {
if ( ( mixer . groupId != ChannelGroupIds . UserMusicInputGroup ) && ! ( mixer . id in usedMixers ) ) {
var mixers = foundMixers [ mixer . group _id ]
if ( ! mixers ) {
mixers = [ ]
foundMixers [ mixer . group _id ] = mixers ;
}
mixers . push ( mixer )
}
}
}
}
} ) ;
return foundMixers ;
}
2013-02-07 04:58:41 +00:00
function _wireTopVolume ( ) {
2013-07-31 23:39:10 +00:00
var gainPercent = 0 ;
2013-03-14 03:29:57 +00:00
var mixerIds = [ ] ;
2014-11-17 23:16:30 +00:00
var mixers = sessionModel . isMasterMixMode ( ) ? masterMixers : personalMixers ;
2013-02-07 04:58:41 +00:00
$ . each ( mixers , function ( index , mixer ) {
2014-11-11 22:21:29 +00:00
if ( sessionModel . isMasterMixMode ( ) && mixer . group _id === ChannelGroupIds . MasterGroup ) {
2013-03-14 03:29:57 +00:00
mixerIds . push ( mixer . id ) ;
2013-07-31 23:39:10 +00:00
gainPercent = percentFromMixerValue (
2013-02-13 05:30:07 +00:00
mixer . range _low , mixer . range _high , mixer . volume _left ) ;
2013-02-07 04:58:41 +00:00
}
2014-11-11 22:21:29 +00:00
else if ( ! sessionModel . isMasterMixMode ( ) && mixer . group _id === ChannelGroupIds . MonitorGroup ) {
2013-03-14 03:29:57 +00:00
mixerIds . push ( mixer . id ) ;
2014-11-11 22:21:29 +00:00
gainPercent = percentFromMixerValue (
mixer . range _low , mixer . range _high , mixer . volume _left ) ;
2013-02-07 04:58:41 +00:00
}
} ) ;
2014-11-17 23:16:30 +00:00
if ( mixerIds . length == 0 ) {
logger . debug ( "did not find master/monitor volume" , mixers )
}
2013-07-31 23:39:10 +00:00
var faderId = mixerIds . join ( ',' ) ;
2014-06-13 17:51:03 +00:00
var $volume = $ ( '#volume' ) ;
$volume . attr ( 'mixer-id' , faderId ) ;
2013-07-31 23:39:10 +00:00
var faderOpts = {
faderId : faderId ,
faderType : "horizontal" ,
width : 50 ,
style : {
"background-image" : "none" ,
"background-repeat" : "no-repeat" ,
"height" : "24px"
}
} ;
2014-06-13 17:51:03 +00:00
context . JK . FaderHelpers . renderFader ( $volume , faderOpts ) ;
$volume . on ( 'fader_change' , faderChanged ) ;
2013-07-31 23:39:10 +00:00
// Visually update fader to underlying mixer start value.
2013-08-07 20:04:22 +00:00
// Always do this, even if gainPercent is zero.
2014-02-13 20:17:16 +00:00
2013-08-07 20:04:22 +00:00
context . JK . FaderHelpers . setFaderValue ( faderId , gainPercent ) ;
2013-02-07 04:58:41 +00:00
}
2013-04-10 15:01:29 +00:00
/ * *
* This control has it 's own Set/Get methods, so we don' t need to
* line it up with some mixer later . We ' ll use a special mixer - id value
* to let us know we ' re dealing with the mix control .
* /
function _wireTopMix ( ) {
var $mixSlider = $ ( '#l2m' ) ;
var l2m _mixer = {
range _low : - 80 ,
range _high : 20 ,
volume _left : context . jamClient . SessionGetMasterLocalMix ( )
} ;
2013-09-05 21:51:29 +00:00
// var gainPercent = percentFromMixerValue(
// l2m_mixer.range_low, l2m_mixer.range_high, l2m_mixer.volume_left);
2013-07-31 23:39:10 +00:00
var faderId = '#l2m' ; // also the selector for renderFader
var faderOpts = {
faderId : faderId ,
faderType : "horizontal" ,
width : 70 ,
style : {
"background-image" : "none" ,
"background-repeat" : "no-repeat" ,
"height" : "24px"
}
} ;
2014-06-13 17:51:03 +00:00
context . JK . FaderHelpers . renderFader ( $mixSlider , faderOpts ) ;
$mixSlider . on ( 'fader_change' , l2mChanged ) ;
2013-09-05 21:51:29 +00:00
2014-03-03 22:13:23 +00:00
var value = context . jamClient . SessionGetMasterLocalMix ( ) ;
context . JK . FaderHelpers . setFaderValue ( faderId , percentFromMixerValue ( - 80 , 20 , value ) ) ;
2013-07-31 23:39:10 +00:00
}
/ * *
* This has a specialized jamClient call , so custom handler .
* /
2014-06-13 17:51:03 +00:00
function l2mChanged ( e , data ) {
2014-03-03 22:13:23 +00:00
//var dbValue = context.JK.FaderHelpers.convertLinearToDb(newValue);
2014-06-13 17:51:03 +00:00
context . jamClient . SessionSetMasterLocalMix ( data . percentage - 80 ) ;
2013-04-10 15:01:29 +00:00
}
2013-02-07 04:58:41 +00:00
function _addVoiceChat ( ) {
2013-02-08 05:08:05 +00:00
// If, and only if, there is a mixer in group 3 (voice chat)
// Add the voice chat controls below my tracks, and hook up the mixer.
// Assumption is that there is only ever one, so we just take the first one.
2014-12-17 15:08:45 +00:00
var voiceChatMixers = _getMyVoiceChatMixers ( ) ;
if ( voiceChatMixers ) {
var mixer = voiceChatMixers . mixer ;
var $voiceChat = $ ( '#voice-chat' ) ;
$voiceChat . show ( ) ;
$voiceChat . attr ( 'mixer-id' , mixer . id ) ;
var $voiceChatGain = $voiceChat . find ( '.voicechat-gain' ) ;
$voiceChatGain . attr ( 'mixer-id' , mixer . id ) ;
var $voiceChatMute = $voiceChat . find ( '.voicechat-mute' ) . attr ( 'mixer-id' , mixer . id ) . data ( 'mixer' , mixer ) . data ( 'opposite-mixer' , voiceChatMixers . oppositeMixer )
var gainPercent = percentFromMixerValue (
mixer . range _low , mixer . range _high , mixer . volume _left ) ;
var faderOpts = {
faderId : mixer . id ,
faderType : "horizontal" ,
width : 50
} ;
context . JK . FaderHelpers . renderFader ( $voiceChatGain , faderOpts ) ;
$voiceChatGain . on ( 'fader_change' , faderChanged ) ;
context . JK . FaderHelpers . setFaderValue ( mixer . id , gainPercent ) ;
if ( mixer . mute ) {
_toggleVisualMuteControl ( $voiceChatMute , mixer . mute ) ;
}
}
2013-02-07 04:58:41 +00:00
}
2014-01-05 03:47:23 +00:00
function _renderLocalMediaTracks ( ) {
2014-11-21 23:16:00 +00:00
2015-01-06 22:51:19 +00:00
// first gather all master mode media mixers and peer media mixers
var localMediaMixers = _mixersForGroupId ( ChannelGroupIds . MediaTrackGroup , MIX _MODES . MASTER ) ;
var peerLocalMediaMixers = _mixersForGroupId ( ChannelGroupIds . PeerMediaTrackGroup , MIX _MODES . MASTER ) ;
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
// with mixer info, we use these to decide what kind of tracks are open in the backend
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
// each mixer has a media_type field, which describes the type of media track it is.
// * JamTrack
// * BackingTrack
// * RecordingTrack
// * MetronomeTrack
// * "" - adhoc track (not supported visually)
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
// it is supposed to be the case that there are only one type of track open at a time, however, that's a business policy/logic
// constraint; and may be buggy. **So, we should render whatever we have, so that it's obvious what's really going on.**
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
// so, let's group up all mixers by type, and then ask them to be rendered
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
var recordingTrackMixers = [ ] ;
var backingTrackMixers = [ ] ;
var jamTrackMixers = [ ] ;
var metronomeTrackMixers = [ ] ;
var adhocTrackMixers = [ ] ;
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
function groupByType ( mixers ) {
context . _ . each ( mixers , function ( mixer ) {
var mediaType = mixer . media _type ;
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
if ( mediaType == 'RecordingTrack' ) {
recordingTrackMixers . push ( mixer )
}
else if ( mediaType == 'BackingTrack' ) {
backingTrackMixers . push ( mixer ) ;
}
else if ( mediaType == 'MetronomeTrack' ) {
metronomeTrackMixers . push ( mixer ) ;
}
else if ( mediaType == 'JamTrack' ) {
jamTrackMixers . push ( mixer ) ;
}
else {
adhocTrackMixers . push ( mixer ) ;
}
} ) ;
}
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
groupByType ( localMediaMixers ) ;
groupByType ( peerLocalMediaMixers ) ;
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
if ( recordingTrackMixers . length > 0 ) {
renderRecordingTracks ( recordingTrackMixers )
}
if ( backingTrackMixers . length > 0 ) {
renderBackingTracks ( backingTrackMixers )
}
if ( jamTrackMixers . length > 0 ) {
renderJamTracks ( jamTrackMixers ) ;
}
if ( metronomeTrackMixers . length > 0 ) {
renderMetronomeTracks ( jamTrackMixers ) ;
}
if ( adhocTrackMixers . length > 0 ) {
logger . warn ( "some tracks are open that we don't know how to show" )
}
}
function renderBackingTracks ( backingTrackMixers ) {
logger . error ( "do not know how to draw backing tracks yet" )
}
function renderJamTracks ( jamTrackMixers ) {
logger . error ( "do not know how to draw jam tracks yet" )
}
function renderMetronomeTracks ( metronomeTrackMixers ) {
logger . error ( "do not know how to draw metronome tracks yet" )
}
function renderRecordingTracks ( recordingMixers ) {
// get the server's info for the recording
var recordedTracks = sessionModel . recordedTracks ( ) ;
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
if ( recordedTracks && recordingMixers . length == 0 ) {
// if we are the creator, then rather than raise an error, tell the server the recording is over.
// this shoudl only happen if we get temporarily disconnected by forced reload, which isn't a very normal scenario
if ( sessionModel . getCurrentSession ( ) . claimed _recording _initiator _id == context . JK . userMe . id ) {
closeRecording ( ) ;
return ;
}
}
// pluck the 1st mixer, and assume that all other mixers in this group are of the same type (between Local vs Peer)
// if it's a locally opened track (MediaTrackGroup), then we can say this person is the opener
var isOpener = recordingMixers [ 0 ] . group _id == ChannelGroupIds . MediaTrackGroup ;
// using the server's info in conjuction with the client's, draw the recording tracks
if ( recordedTracks ) {
$ ( '.session-recording-name' ) . text ( sessionModel . getCurrentSession ( ) . claimed _recording . name ) ;
var noCorrespondingTracks = false ;
$ . each ( recordingMixers , function ( index , mixer ) {
var preMasteredClass = "" ;
// find the track or tracks that correspond to the mixer
var correspondingTracks = [ ]
$ . each ( recordedTracks , function ( i , recordedTrack ) {
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" ) ;
2014-01-05 03:47:23 +00:00
}
2015-01-06 22:51:19 +00:00
} ) ;
if ( correspondingTracks . length == 0 ) {
noCorrespondingTracks = true ;
app . notify ( {
title : "Unable to Open Recording" ,
text : "Could not correlate server and client tracks" ,
icon _url : "/assets/content/icon_alert_big.png" } ) ;
return false ;
}
// prune found recorded tracks
recordedTracks = $ . grep ( recordedTracks , function ( value ) {
return $ . inArray ( value , correspondingTracks ) < 0 ;
} ) ;
var oneOfTheTracks = correspondingTracks [ 0 ] ;
var instrumentIcon = context . JK . getInstrumentIcon45 ( oneOfTheTracks . instrument _id ) ;
var photoUrl = "/assets/content/icon_recording.png" ;
var name = oneOfTheTracks . user . name ;
if ( ! ( name ) ) {
name = oneOfTheTracks . user . first _name + ' ' + oneOfTheTracks . user . last _name ;
}
// Default trackData to participant + no Mixer state.
var trackData = {
trackId : oneOfTheTracks . id ,
clientId : oneOfTheTracks . client _id ,
name : name ,
instrumentIcon : instrumentIcon ,
avatar : photoUrl ,
latency : "good" ,
gainPercent : 0 ,
muteClass : 'muted' ,
mixerId : "" ,
avatarClass : 'avatar-recording' ,
preMasteredClass : preMasteredClass
} ;
var gainPercent = percentFromMixerValue (
mixer . range _low , mixer . range _high , mixer . volume _left ) ;
var muteClass = "enabled" ;
if ( mixer . mute ) {
muteClass = "muted" ;
}
trackData . gainPercent = gainPercent ;
trackData . muteClass = muteClass ;
trackData . mixerId = mixer . id ; // the master mixer controls the volume control for recordings (no personal controls in either master or personal mode)
trackData . vuMixerId = mixer . id ; // the master mixer controls the VUs for recordings (no personal controls in either master or personal mode)
trackData . muteMixerId = mixer . id ; // the master mixer controls the mute for recordings (no personal controls in either master or personal mode)
if ( sessionModel . isPersonalMixMode ( ) || ! isOpener ) {
trackData . mediaControlsDisabled = true ;
trackData . mediaTrackOpener = isOpener ;
}
_addRecordingTrack ( trackData ) ;
} ) ;
if ( ! noCorrespondingTracks && recordedTracks . length > 0 ) {
logger . error ( "unable to find all recorded tracks against client tracks" ) ;
app . notify ( { title : "All tracks not found" ,
text : "Some tracks in the recording are not present in the playback" ,
icon _url : "/assets/content/icon_alert_big.png" } )
2014-01-05 03:47:23 +00:00
}
2015-01-06 22:51:19 +00:00
}
2014-01-05 03:47:23 +00:00
}
2014-11-13 15:20:08 +00:00
function trackMuteSelected ( e , data ) {
var muteOption = data . muteOption ; // muteOption is going to be either 'master' or 'personal'. We mute the correct one, based on track info
var $muteControl = $ ( this ) ;
// mixer is the mixer object returned from the backend corresponding to the mixer in this particular mode
// oppositeMixer is the mixer correspond to the opposite mode.
// Note that oppositeMixer is not ever set for ChannelGroupIds.AudioInputMusicGroup or ChannelGroupIds.MediaTrackGroup
var mixer = $muteControl . data ( 'mixer' )
var oppositeMixer = $muteControl . data ( 'opposite-mixer' )
2014-11-21 23:16:00 +00:00
logger . debug ( "muting tracks. current mixer id=" + mixer . id + ", opposite mixer id=" + oppositeMixer . id )
var mixerPair = { }
if ( sessionModel . isMasterMixMode ( ) ) {
mixerPair . master = mixer ;
mixerPair . personal = oppositeMixer ;
2014-11-13 15:20:08 +00:00
}
2014-11-21 23:16:00 +00:00
else {
mixerPair . master = oppositeMixer ;
mixerPair . personal = mixer ;
}
if ( muteOption == 'master' ) {
_toggleAudioMute ( mixerPair . master . id , true , mixerPair . master . mode ) ;
_toggleAudioMute ( mixerPair . personal . id , true , mixerPair . personal . mode ) ;
}
else {
_toggleAudioMute ( mixerPair . personal . id , true , mixerPair . personal . mode ) ;
_toggleAudioMute ( mixerPair . master . id , false , mixerPair . master . mode ) ;
}
_toggleVisualMuteControl ( $muteControl , true ) ;
}
// find backend mixer based on track data, and target client_id
function findMixerForTrack ( client _id , track , myTrack ) {
var mixer = null ; // what is the best mixer for this track/client ID?
var oppositeMixer = null ; // what is the corresponding mixer in the opposite mode?
var vuMixer = null ;
var muteMixer = null ;
var mixMode = sessionModel . getMixMode ( ) ;
if ( myTrack ) {
// when it's your track, look it up by the backend resource ID
mixer = getMixerByTrackId ( track . client _track _id , mixMode )
vuMixer = mixer ;
muteMixer = mixer ;
// sanity checks
if ( mixer && ( mixer . group _id != ChannelGroupIds . AudioInputMusicGroup ) ) { logger . error ( "found local mixer that was not of groupID: AudioInputMusicGroup" , mixer ) }
if ( mixer ) {
// find the matching AudioInputMusicGroup for the opposite mode
oppositeMixer = getMixerByTrackId ( track . client _track _id , ! mixMode )
if ( mixMode == MIX _MODES . PERSONAL ) {
muteMixer = oppositeMixer ; // make the master mixer the mute mixer
}
// sanity checks
if ( ! oppositeMixer ) { logger . error ( "unable to find opposite mixer for local mixer" , mixer ) }
else if ( oppositeMixer . group _id != ChannelGroupIds . AudioInputMusicGroup ) { logger . error ( "found local mixer in opposite mode that was not of groupID: AudioInputMusicGroup" , mixer , oppositeMixer ) }
}
else {
logger . debug ( "local track is not present: " , track )
}
}
else {
if ( mixMode === MIX _MODES . MASTER ) {
// when it's a remote track and in master mode, we should find the PeerAudioInputMusicGroup
mixer = getMixerByTrackId ( track . client _track _id , MIX _MODES . MASTER )
if ( mixer && ( mixer . group _id != ChannelGroupIds . PeerAudioInputMusicGroup ) ) { logger . error ( "found remote mixer that was not of groupID: PeerAudioInputMusicGroup" , mixer ) }
vuMixer = mixer ;
muteMixer = mixer ;
if ( mixer ) {
// we should be able to find a UserMusicInputGroup for this clientId in personal mode
var oppositeMixers = _groupedMixersForClientId ( client _id , [ ChannelGroupIds . UserMusicInputGroup ] , { } , MIX _MODES . PERSONAL ) ;
if ( oppositeMixers [ ChannelGroupIds . UserMusicInputGroup ] ) { oppositeMixer = oppositeMixers [ ChannelGroupIds . UserMusicInputGroup ] [ 0 ] ; }
if ( ! oppositeMixer ) { logger . error ( "unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer" , mixer ) }
}
}
else {
// when it's a remote track and in personal mode, we want the 'Peer Stream', which is UserMusicInputGroup
// this spans N tracks for the remote user
var mixers = _groupedMixersForClientId ( client _id , [ ChannelGroupIds . UserMusicInputGroup ] , { } , MIX _MODES . PERSONAL ) ;
if ( mixers [ ChannelGroupIds . UserMusicInputGroup ] ) { mixer = mixers [ ChannelGroupIds . UserMusicInputGroup ] [ 0 ] ; }
vuMixer = mixer ;
muteMixer = mixer ;
if ( mixer ) {
// now grab the PeerAudioInputMusicGroup in master mode to satisfy the 'opposite' mixer
oppositeMixer = getMixerByTrackId ( track . client _track _id , MIX _MODES . MASTER )
if ( ! oppositeMixer ) { logger . debug ( "unable to find a PeerAudioInputMusicGroup master mixer matching a UserMusicInput" , track . client _track _id , mixersByTrackId ) }
else if ( oppositeMixer . group _id != ChannelGroupIds . PeerAudioInputMusicGroup ) { logger . error ( "found remote mixer that was not of groupID: PeerAudioInputMusicGroup" , mixer ) }
vuMixer = oppositeMixer ; // for personal mode, use the PeerAudioInputMusicGroup's VUs
}
}
2014-11-13 15:20:08 +00:00
}
2014-11-21 23:16:00 +00:00
return {
mixer : mixer ,
oppositeMixer : oppositeMixer ,
vuMixer : vuMixer ,
muteMixer : muteMixer
}
2014-11-13 15:20:08 +00:00
}
2012-11-03 20:25:23 +00:00
function _renderTracks ( ) {
2013-05-15 05:59:09 +00:00
myTracks = [ ] ;
2013-02-14 07:02:05 +00:00
// Participants are here now, but the mixers don't update right away.
// Draw tracks from participants, then setup timers to look for the
// mixers that go with those participants, if they're missing.
2014-11-21 23:16:00 +00:00
lookingForMixers = [ ] // clear this back out as we are restarting from scratch
2013-02-14 07:02:05 +00:00
lookingForMixersCount = 0 ;
2013-03-02 19:02:42 +00:00
$ . each ( sessionModel . participants ( ) , function ( index , participant ) {
2013-02-14 07:02:05 +00:00
var name = participant . user . name ;
if ( ! ( name ) ) {
name = participant . user . first _name + ' ' + participant . user . last _name ;
}
2014-11-21 23:16:00 +00:00
var myTrack = app . clientId == participant . client _id ;
2013-09-05 04:24:28 +00:00
2013-05-15 05:59:09 +00:00
// loop through all tracks for each participant
$ . each ( participant . tracks , function ( index , track ) {
var instrumentIcon = context . JK . getInstrumentIcon45 ( track . instrument _id ) ;
var photoUrl = context . JK . resolveAvatarUrl ( participant . user . photo _url ) ;
// Default trackData to participant + no Mixer state.
var trackData = {
trackId : track . id ,
2013-05-22 11:48:37 +00:00
connection _id : track . connection _id ,
2014-11-21 23:16:00 +00:00
client _track _id : track . client _track _id ,
client _resource _id : track . client _resource _id ,
2013-05-15 05:59:09 +00:00
clientId : participant . client _id ,
name : name ,
instrumentIcon : instrumentIcon ,
avatar : photoUrl ,
latency : "good" ,
gainPercent : 0 ,
muteClass : 'muted' ,
2014-01-05 03:47:23 +00:00
mixerId : "" ,
avatarClass : 'avatar-med' ,
2014-12-17 15:08:45 +00:00
preMasteredClass : "" ,
myTrack : myTrack
2013-05-15 05:59:09 +00:00
} ;
2014-11-21 23:16:00 +00:00
var mixerData = findMixerForTrack ( participant . client _id , track , myTrack )
var mixer = mixerData . mixer ;
var vuMixer = mixerData . vuMixer ;
var muteMixer = mixerData . muteMixer ;
var oppositeMixer = mixerData . oppositeMixer ;
2014-11-11 22:21:46 +00:00
2014-11-21 23:16:00 +00:00
if ( mixer && oppositeMixer ) {
2013-05-15 05:59:09 +00:00
myTrack = ( mixer . group _id === ChannelGroupIds . AudioInputMusicGroup ) ;
var gainPercent = percentFromMixerValue (
mixer . range _low , mixer . range _high , mixer . volume _left ) ;
var muteClass = "enabled" ;
if ( mixer . mute ) {
muteClass = "muted" ;
}
2014-03-05 06:15:46 +00:00
2013-05-15 05:59:09 +00:00
trackData . gainPercent = gainPercent ;
trackData . muteClass = muteClass ;
trackData . mixerId = mixer . id ;
2014-11-21 23:16:00 +00:00
trackData . vuMixerId = vuMixer . id ;
trackData . oppositeMixer = oppositeMixer ;
trackData . muteMixerId = muteMixer . id ;
2014-03-03 22:13:23 +00:00
trackData . noaudio = false ;
2014-11-11 22:21:29 +00:00
trackData . group _id = mixer . group _id ;
2013-09-24 07:22:41 +00:00
context . jamClient . SessionSetUserName ( participant . client _id , name ) ;
2014-03-03 22:13:23 +00:00
2013-05-15 05:59:09 +00:00
} else { // No mixer to match, yet
2014-11-21 23:16:00 +00:00
lookingForMixers . push ( { track : track , clientId : participant . client _id } )
2014-03-03 22:13:23 +00:00
trackData . noaudio = true ;
2013-05-15 05:59:09 +00:00
if ( ! ( lookingForMixersTimer ) ) {
2014-03-20 11:53:26 +00:00
logger . debug ( "waiting for mixer to show up for track: " + track . id )
2013-08-19 00:59:49 +00:00
lookingForMixersTimer = context . setInterval ( lookForMixers , 500 ) ;
2013-05-15 05:59:09 +00:00
}
2013-02-10 02:00:29 +00:00
}
2013-09-06 12:34:57 +00:00
2014-03-03 22:13:23 +00:00
var allowDelete = myTrack && index > 0 ;
2014-11-21 23:16:00 +00:00
_addTrack ( allowDelete , trackData , mixer , oppositeMixer ) ;
2013-05-22 05:03:34 +00:00
2013-05-15 05:59:09 +00:00
// Show settings icons only for my tracks
if ( myTrack ) {
2013-05-24 01:16:00 +00:00
myTracks . push ( trackData ) ;
2013-09-06 02:04:36 +00:00
}
2013-05-15 05:59:09 +00:00
} ) ;
2012-11-03 20:25:23 +00:00
} ) ;
2013-05-22 05:03:34 +00:00
configureTrackDialog = new context . JK . ConfigureTrackDialog ( app , myTracks , sessionId , sessionModel ) ;
2014-02-23 04:41:42 +00:00
addNewGearDialog = new context . JK . AddNewGearDialog ( app , self ) ;
2013-09-12 12:02:13 +00:00
}
2014-11-21 23:16:00 +00:00
function connectTrackToMixer ( trackSelector , track , mixerId , gainPercent , groupId ) {
2013-08-28 03:05:53 +00:00
var vuOpts = $ . extend ( { } , trackVuOpts ) ;
var faderOpts = $ . extend ( { } , trackFaderOpts ) ;
faderOpts . faderId = mixerId ;
var vuLeftSelector = trackSelector + " .track-vu-left" ;
var vuRightSelector = trackSelector + " .track-vu-right" ;
var faderSelector = trackSelector + " .track-gain" ;
2014-11-11 22:21:29 +00:00
var $fader = $ ( faderSelector ) . attr ( 'mixer-id' , mixerId ) . data ( 'groupId' , groupId )
2015-01-06 22:51:19 +00:00
if ( track . mediaControlsDisabled ) {
$fader . data ( 'media-controls-disabled' , true ) . data ( 'media-track-opener' , track . mediaTrackOpener ) // this we be applied later to the fader handle $element
2014-11-21 23:16:00 +00:00
}
2013-09-05 04:24:28 +00:00
var $track = $ ( trackSelector ) ;
2013-08-28 03:05:53 +00:00
// Set mixer-id attributes and render VU/Fader
context . JK . VuHelpers . renderVU ( vuLeftSelector , vuOpts ) ;
2014-11-21 23:16:00 +00:00
$track . find ( '.track-vu-left' ) . attr ( 'mixer-id' , track . vuMixerId + '_vul' ) . data ( 'groupId' , groupId )
2013-08-28 03:05:53 +00:00
context . JK . VuHelpers . renderVU ( vuRightSelector , vuOpts ) ;
2014-11-21 23:16:00 +00:00
$track . find ( '.track-vu-right' ) . attr ( 'mixer-id' , track . vuMixerId + '_vur' ) . data ( 'groupId' , groupId )
2014-06-13 17:51:03 +00:00
context . JK . FaderHelpers . renderFader ( $fader , faderOpts ) ;
2013-08-28 03:05:53 +00:00
// Set gain position
context . JK . FaderHelpers . setFaderValue ( mixerId , gainPercent ) ;
2014-06-13 17:51:03 +00:00
$fader . on ( 'fader_change' , faderChanged ) ;
2014-11-21 23:16:00 +00:00
return $track ;
2013-08-28 03:05:53 +00:00
}
2013-02-14 07:02:05 +00:00
// Function called on an interval when participants change. Mixers seem to
// show up later, so we render the tracks from participants, but keep track
// of the ones there weren't any mixers for, and continually try to find them
// and get them connected to the mixers underneath.
function lookForMixers ( ) {
lookingForMixersCount ++ ;
_updateMixers ( ) ;
2013-09-05 04:24:28 +00:00
var usedMixers = { } ;
2013-02-14 07:02:05 +00:00
var keysToDelete = [ ] ;
2014-11-21 23:16:00 +00:00
context . _ . each ( lookingForMixers , function ( data ) {
var clientId = data . clientId ;
var track = data . track ;
var myTrack = app . clientId == clientId ;
var mixerData = findMixerForTrack ( clientId , track , myTrack )
var mixer = mixerData . mixer ;
var oppositeMixer = mixerData . oppositeMixer ;
var vuMixer = mixerData . vuMixer ;
var muteMixer = mixerData . muteMixer ;
if ( mixer && oppositeMixer ) {
var participant = ( sessionModel . getParticipant ( clientId ) || { name : 'unknown' } ) . name ;
logger . debug ( "found mixer=" + mixer . id + ", participant=" + participant )
usedMixers [ mixer . id ] = true ;
keysToDelete . push ( data ) ;
var gainPercent = percentFromMixerValue (
mixer . range _low , mixer . range _high , mixer . volume _left ) ;
var trackSelector = 'div.track[track-id="' + track . id + '"]' ;
connectTrackToMixer ( trackSelector , track , mixer . id , gainPercent , mixer . group _id ) ;
var $track = $ ( 'div.track[client-id="' + clientId + '"]' ) ;
var $trackIconMute = $track . find ( '.track-icon-mute' )
$trackIconMute . attr ( 'mixer-id' , muteMixer . id ) . data ( 'mixer' , mixer ) . data ( 'opposite-mixer' , oppositeMixer )
$trackIconMute . muteSelector ( ) . on ( EVENTS . MUTE _SELECTED , trackMuteSelected )
// hide overlay for all tracks associated with this client id (if one mixer is present, then all tracks are valid)
$ ( '.disabled-track-overlay' , $track ) . hide ( ) ;
$ ( '.track-connection' , $track ) . removeClass ( 'red yellow green' ) . addClass ( 'grey' ) ;
// Set mute state
_toggleVisualMuteControl ( $trackIconMute , mixer . mute || oppositeMixer . mute ) ;
}
else {
// if 1 second has gone by and still no mixer, then we gray the participant's tracks
if ( lookingForMixersCount == 2 ) {
var $track = $ ( 'div.track[client-id="' + clientId + '"]' ) ;
$ ( '.disabled-track-overlay' , $track ) . show ( ) ;
$ ( '.track-connection' , $track ) . removeClass ( 'red yellow green' ) . addClass ( 'red' ) ;
2014-03-03 22:13:23 +00:00
}
2014-11-21 23:16:00 +00:00
var participant = ( sessionModel . getParticipant ( clientId ) || { user : { name : 'unknown' } } ) . user . name ;
logger . debug ( "still looking for mixer for participant=" + participant + ", clientId=" + clientId )
}
} )
2013-02-14 07:02:05 +00:00
for ( var i = 0 ; i < keysToDelete . length ; i ++ ) {
2014-11-21 23:16:00 +00:00
var index = lookingForMixers . indexOf ( keysToDelete [ i ] ) ;
lookingForMixers . splice ( index , 1 ) ;
2013-02-14 07:02:05 +00:00
}
2014-11-21 23:16:00 +00:00
if ( lookingForMixers . length === 0 ||
2013-08-19 00:59:49 +00:00
lookingForMixersCount > 20 ) {
2013-02-14 07:02:05 +00:00
lookingForMixersCount = 0 ;
2014-11-21 23:16:00 +00:00
lookingForMixers = [ ]
2013-02-14 07:02:05 +00:00
context . clearTimeout ( lookingForMixersTimer ) ;
lookingForMixersTimer = null ;
}
}
2013-01-30 16:50:43 +00:00
// Given a mixerID and a value between 0.0-1.0,
// light up the proper VU lights.
2014-11-21 23:16:00 +00:00
function _updateVU ( mixerId , value , isClipping ) {
2013-08-19 00:54:32 +00:00
// Special-case for mono tracks. If mono, and it's a _vul id,
// update both sides, otherwise do nothing.
// If it's a stereo track, just do the normal thing.
var selector ;
var pureMixerId = mixerId . replace ( "_vul" , "" ) ;
pureMixerId = pureMixerId . replace ( "_vur" , "" ) ;
2014-11-21 23:16:00 +00:00
var mixer = getMixer ( pureMixerId ) ;
2013-08-19 00:54:32 +00:00
if ( mixer ) {
if ( ! ( mixer . stereo ) ) { // mono track
if ( mixerId . substr ( - 4 ) === "_vul" ) {
// Do the left
2014-06-13 17:51:03 +00:00
selector = $ ( '#tracks [mixer-id="' + pureMixerId + '_vul"]' ) ;
2013-08-19 00:54:32 +00:00
context . JK . VuHelpers . updateVU ( selector , value ) ;
// Do the right
2014-06-13 17:51:03 +00:00
selector = $ ( '#tracks [mixer-id="' + pureMixerId + '_vur"]' ) ;
2013-08-19 00:54:32 +00:00
context . JK . VuHelpers . updateVU ( selector , value ) ;
} // otherwise, it's a mono track, _vur event - ignore.
} else { // stereo track
2014-06-13 17:51:03 +00:00
selector = $ ( '#tracks [mixer-id="' + mixerId + '"]' ) ;
2013-08-19 00:54:32 +00:00
context . JK . VuHelpers . updateVU ( selector , value ) ;
}
}
2013-01-30 16:50:43 +00:00
}
2014-11-21 23:16:00 +00:00
function _addTrack ( allowDelete , trackData , mixer , oppositeMixer ) {
2014-03-03 22:13:23 +00:00
2013-07-20 15:38:52 +00:00
var parentSelector = '#session-mytracks-container' ;
var $destination = $ ( parentSelector ) ;
2013-02-05 05:10:37 +00:00
if ( trackData . clientId !== app . clientId ) {
2013-07-20 15:38:52 +00:00
parentSelector = '#session-livetracks-container' ;
$destination = $ ( parentSelector ) ;
2013-06-24 03:08:38 +00:00
$ ( '.session-livetracks .when-empty' ) . hide ( ) ;
2013-02-05 05:10:37 +00:00
}
2012-10-21 23:03:46 +00:00
var template = $ ( '#template-session-track' ) . html ( ) ;
2014-01-05 03:47:23 +00:00
var newTrack = $ ( context . JK . fillTemplate ( template , trackData ) ) ;
2014-03-03 22:13:23 +00:00
var audioOverlay = $ ( '.disabled-track-overlay' , newTrack ) ;
2014-11-13 15:20:08 +00:00
var $trackIconMute = newTrack . find ( '.track-icon-mute' )
$trackIconMute . muteSelector ( ) . on ( EVENTS . MUTE _SELECTED , trackMuteSelected )
2014-11-21 23:16:00 +00:00
$trackIconMute . data ( 'mixer' , mixer ) . data ( 'opposite-mixer' , oppositeMixer )
2014-11-13 15:20:08 +00:00
2014-03-03 22:13:23 +00:00
audioOverlay . hide ( ) ; // always start with overlay hidden, and only show if no audio persists
2013-02-05 05:10:37 +00:00
$destination . append ( newTrack ) ;
2013-05-22 05:03:34 +00:00
2013-07-26 14:46:38 +00:00
// Render VU meters and gain fader
2013-09-05 04:24:28 +00:00
var trackSelector = parentSelector + ' .session-track[track-id="' + trackData . trackId + '"]' ;
2013-08-28 03:05:53 +00:00
var gainPercent = trackData . gainPercent || 0 ;
2014-11-21 23:16:00 +00:00
connectTrackToMixer ( trackSelector , trackData , trackData . mixerId , gainPercent , trackData . group _id ) ;
2013-07-20 15:38:52 +00:00
2013-05-22 05:03:34 +00:00
var $closeButton = $ ( '#div-track-close' , 'div[track-id="' + trackData . trackId + '"]' ) ;
2014-03-03 22:13:23 +00:00
if ( ! allowDelete ) {
2013-05-22 05:03:34 +00:00
$closeButton . hide ( ) ;
}
else {
$closeButton . click ( deleteTrack ) ;
}
2015-01-06 22:51:19 +00:00
// is this used?
2013-05-15 05:59:09 +00:00
tracks [ trackData . trackId ] = new context . JK . SessionTrack ( trackData . clientId ) ;
2012-10-21 23:03:46 +00:00
}
2014-01-05 03:47:23 +00:00
2015-01-06 22:51:19 +00:00
function _addRecordingTrack ( trackData ) {
2014-01-05 03:47:23 +00:00
var parentSelector = '#session-recordedtracks-container' ;
var $destination = $ ( parentSelector ) ;
$ ( '.session-recordings .when-empty' ) . hide ( ) ;
$ ( '.session-recording-name-wrapper' ) . show ( ) ;
2014-01-06 20:35:35 +00:00
$ ( '.session-recordings .recording-controls' ) . show ( ) ;
2014-01-05 03:47:23 +00:00
var template = $ ( '#template-session-track' ) . html ( ) ;
var newTrack = $ ( context . JK . fillTemplate ( template , trackData ) ) ;
$destination . append ( newTrack ) ;
if ( trackData . preMasteredClass ) {
context . JK . helpBubble ( $ ( '.track-instrument' , newTrack ) , 'pre-processed-track' , { } , { offsetParent : newTrack . closest ( '.content-body' ) } ) ;
}
// Render VU meters and gain fader
var trackSelector = parentSelector + ' .session-track[track-id="' + trackData . trackId + '"]' ;
var gainPercent = trackData . gainPercent || 0 ;
2014-11-21 23:16:00 +00:00
var $track = connectTrackToMixer ( trackSelector , trackData , trackData . mixerId , gainPercent , null ) ;
var $trackIconMute = $track . find ( '.track-icon-mute' )
2015-01-06 22:51:19 +00:00
if ( trackData . mediaControlsDisabled ) {
$trackIconMute . data ( 'media-controls-disabled' , true ) . data ( 'media-track-opener' , trackData . mediaTrackOpener )
2014-11-21 23:16:00 +00:00
}
2015-01-06 22:51:19 +00:00
// is this used?
2014-01-05 03:47:23 +00:00
tracks [ trackData . trackId ] = new context . JK . SessionTrack ( trackData . clientId ) ;
}
2013-07-26 14:46:38 +00:00
/ * *
* Will be called when fader changes . The fader id ( provided at subscribe time ) ,
* the new value ( 0 - 100 ) and whether the fader is still being dragged are passed .
* /
2014-06-13 17:51:03 +00:00
function faderChanged ( e , data ) {
var $target = $ ( this ) ;
var faderId = $target . attr ( 'mixer-id' ) ;
2014-11-11 22:21:29 +00:00
var groupId = $target . data ( 'groupId' ) ;
2013-07-26 14:46:38 +00:00
var mixerIds = faderId . split ( ',' ) ;
$ . each ( mixerIds , function ( i , v ) {
2014-06-13 17:51:03 +00:00
var broadcast = ! ( data . dragging ) ; // If fader is still dragging, don't broadcast
2013-08-02 21:16:49 +00:00
fillTrackVolumeObject ( v , broadcast ) ;
2014-06-13 17:51:03 +00:00
setMixerVolume ( v , data . percentage ) ;
2014-11-11 22:21:29 +00:00
if ( groupId == ChannelGroupIds . UserMusicInputGroup ) {
// there may be other mixers with this same ID in the case of a Peer Music Stream, so update them as well
context . JK . FaderHelpers . setFaderValue ( v , data . percentage ) ;
}
2013-07-26 14:46:38 +00:00
} ) ;
}
2013-10-05 18:24:22 +00:00
function handleVolumeChangeCallback ( mixerId , isLeft , value , isMuted ) {
2013-08-07 20:04:22 +00:00
// Visually update mixer
2013-08-27 02:31:18 +00:00
// 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.
2013-08-07 20:04:22 +00:00
// TODO: Use mixer's range
var faderValue = percentFromMixerValue ( - 80 , 20 , value ) ;
context . JK . FaderHelpers . setFaderValue ( mixerId , faderValue ) ;
2014-12-17 15:08:45 +00:00
var $muteControl = $ ( '[control="mute"][mixer-id="' + mixerId + '"]' ) ;
_toggleVisualMuteControl ( $muteControl , isMuted ) ;
2013-08-07 20:04:22 +00:00
}
2014-11-21 23:16:00 +00:00
function handleBridgeCallback ( vuData ) {
var j ;
2013-01-31 05:23:30 +00:00
var eventName = null ;
var mixerId = null ;
var value = null ;
2014-11-21 23:16:00 +00:00
var vuInfo = null ;
for ( j = 0 ; j < vuData . length ; j ++ ) {
vuInfo = vuData [ j ] ;
var eventName = vuInfo [ 0 ] ;
var vuVal = 0.0 ;
if ( eventName === "vu" ) {
var mixerId = vuInfo [ 1 ] ;
var leftValue = vuInfo [ 2 ] ;
var leftClipping = vuInfo [ 3 ] ;
var rightValue = vuInfo [ 4 ] ;
var rightClipping = vuInfo [ 5 ] ;
// TODO - no guarantee range will be -80 to 20. Get from the
// GetControlState for this mixer which returns min/max
// value is a DB value from -80 to 20. Convert to float from 0.0-1.0
_updateVU ( mixerId + "_vul" , ( leftValue + 80 ) / 100 , leftClipping ) ;
_updateVU ( mixerId + "_vur" , ( rightValue + 80 ) / 100 , rightClipping ) ;
}
else if ( eventName === 'connection_status' ) {
var mixerId = vuInfo [ 1 ] ;
var value = vuInfo [ 2 ] ;
// Connection Quality Change
var connectionClass = 'green' ;
if ( value < 7 ) {
connectionClass = 'yellow' ;
}
if ( value < 4 ) {
connectionClass = 'red' ;
}
var mixerPair = getMixerByTrackId ( mixerId ) ;
2014-03-03 22:49:53 +00:00
2014-11-21 23:16:00 +00:00
var clientId = mixerPair ? mixerPair . master . client _id : null ;
if ( clientId ) {
var $connection = $ ( '.session-track[client-id="' + clientId + '"] .track-connection' ) ;
if ( $connection . length == 0 ) {
logger . debug ( "connection status: looking for clientId: " + clientId + ", mixer: " + mixerId )
}
else {
$connection . removeClass ( 'red yellow green grey' ) ;
$connection . addClass ( connectionClass ) ;
}
2013-01-31 05:23:30 +00:00
}
2014-11-21 23:16:00 +00:00
}
else if ( eventName === 'add' || eventName === 'remove' ) {
// TODO - _renderSession. Note I get streams of these in
// sequence, so have Nat fix, or buffer/spam protect
// Note - this is already handled from websocket events.
// However, there may be use of these two events to avoid
// the polling-style check for when a mixer has been added
// to match a participant track.
}
else {
logger . debug ( 'non-vu event: ' + JSON . stringify ( vuInfo ) ) ;
}
2013-01-30 16:50:43 +00:00
}
2014-11-21 23:16:00 +00:00
}
2013-01-30 16:50:43 +00:00
2012-10-06 16:36:05 +00:00
function deleteSession ( evt ) {
2012-12-07 00:28:48 +00:00
var sessionId = $ ( evt . currentTarget ) . attr ( "action-id" ) ;
2012-10-06 16:36:05 +00:00
if ( sessionId ) {
$ . ajax ( {
type : "DELETE" ,
2013-02-26 03:54:09 +00:00
url : "/api/sessions/" + sessionId ,
success : function ( response ) {
2014-02-06 13:03:44 +00:00
context . location = "/client#/home" ;
2013-02-26 03:54:09 +00:00
} ,
error : function ( jqXHR , textStatus , errorThrown ) {
logger . error ( "Error deleting session " + sessionId ) ;
}
} ) ;
2012-10-06 16:36:05 +00:00
}
}
2013-05-22 05:03:34 +00:00
function deleteTrack ( evt ) {
var trackId = $ ( evt . currentTarget ) . attr ( "track-id" ) ;
2013-05-23 12:46:40 +00:00
sessionModel . deleteTrack ( sessionId , trackId ) ;
2013-05-22 05:03:34 +00:00
}
2014-11-21 23:16:00 +00:00
function _toggleVisualMuteControl ( $control , mute ) {
if ( mute ) {
2013-01-31 16:32:32 +00:00
$control . removeClass ( 'enabled' ) ;
$control . addClass ( 'muted' ) ;
} else {
$control . removeClass ( 'muted' ) ;
$control . addClass ( 'enabled' ) ;
}
}
2014-11-21 23:16:00 +00:00
function _toggleAudioMute ( mixerId , muting , mode ) {
2013-03-15 02:44:51 +00:00
fillTrackVolumeObject ( mixerId ) ;
2013-01-31 16:32:32 +00:00
context . trackVolumeObject . mute = muting ;
2014-11-21 23:16:00 +00:00
if ( mode === undefined ) {
mode = sessionModel . getMixMode ( ) ;
}
context . jamClient . SessionSetControlState ( mixerId , mode ) ;
2013-01-31 16:32:32 +00:00
}
2014-11-13 15:20:08 +00:00
function showMuteDropdowns ( $control ) {
$control . btOn ( ) ;
}
2013-01-31 16:32:32 +00:00
function toggleMute ( evt ) {
var $control = $ ( evt . currentTarget ) ;
var muting = ( $control . hasClass ( 'enabled' ) ) ;
2013-03-15 02:44:51 +00:00
var mixerIds = $control . attr ( 'mixer-id' ) . split ( ',' ) ;
2014-11-13 15:20:08 +00:00
// track icons have a special mute behavior
if ( $control . is ( '.track-icon-mute' ) ) {
2015-01-06 22:51:19 +00:00
var mediaControlsDisabled = $control . data ( 'media-controls-disabled' ) ;
if ( mediaControlsDisabled ) {
var mediaTrackOpener = $control . data ( 'media-track-opener' ) ;
context . JK . prodBubble ( $control , 'media-controls-disabled' , { mediaTrackOpener : mediaTrackOpener } , { positions : [ 'bottom' ] , offsetParent : $control . closest ( '.screen' ) } )
2014-11-21 23:16:00 +00:00
return false ;
2014-11-13 15:20:08 +00:00
}
2014-11-21 23:16:00 +00:00
$ . each ( mixerIds , function ( i , v ) {
var mixerId = v ;
// behavior: if this is the user's track in personal mode, then we mute the track globally
// otherwise, for any other track (user+master mode, or remote track in any mode)
// we just mute the type of track for that mode
var mixer = $control . data ( 'mixer' ) ;
var oppositeMixer = $control . data ( 'opposite-mixer' )
2014-12-17 15:08:45 +00:00
if ( mixer && oppositeMixer && mixer . group _id == ChannelGroupIds . AudioInputMusicGroup ) {
// this is the user's local track; mute both personal and master mode
_toggleAudioMute ( mixer . id , muting , getMixer ( mixer . id ) . mode )
_toggleAudioMute ( oppositeMixer . id , muting , getMixer ( oppositeMixer . id ) . mode )
}
else {
_toggleAudioMute ( mixer . id , muting , getMixer ( mixer . id ) . mode )
}
2014-11-21 23:16:00 +00:00
// look for all controls matching this mixer id (important when it's personal mode + UserMusicInputGroup)
var $controls = $screen . find ( '.track-icon-mute[mixer-id=' + mixerId + ']' ) ;
_toggleVisualMuteControl ( $controls , muting ) ;
} ) ;
2014-11-13 15:20:08 +00:00
}
else {
2014-12-17 15:08:45 +00:00
// this path is taken for voice chat, but maybe others eventually
2014-11-13 15:20:08 +00:00
$ . each ( mixerIds , function ( i , v ) {
2014-12-17 15:08:45 +00:00
var mixerId = v ;
var mixer = $control . data ( 'mixer' ) ;
var oppositeMixer = $control . data ( 'opposite-mixer' )
if ( mixer && oppositeMixer && mixer . group _id == ChannelGroupIds . AudioInputChatGroup ) {
_toggleAudioMute ( mixer . id , muting , mixer . mode ) ;
_toggleAudioMute ( oppositeMixer . id , muting , oppositeMixer . mode ) ;
}
else {
_toggleAudioMute ( mixerId , muting ) ;
}
2014-11-13 15:20:08 +00:00
} ) ;
_toggleVisualMuteControl ( $control , muting ) ;
}
2013-01-31 16:32:32 +00:00
}
2013-03-24 20:47:45 +00:00
function fillTrackVolumeObject ( mixerId , broadcast ) {
2013-03-15 02:44:51 +00:00
_updateMixers ( ) ;
2013-03-24 20:47:45 +00:00
var _broadcast = true ;
if ( broadcast !== undefined ) {
_broadcast = broadcast ;
}
2014-11-21 23:16:00 +00:00
var mixer = getMixer ( mixerId ) ;
context . trackVolumeObject . clientID = mixer . client _id ;
context . trackVolumeObject . broadcast = _broadcast ;
context . trackVolumeObject . master = mixer . master ;
context . trackVolumeObject . monitor = mixer . monitor ;
context . trackVolumeObject . mute = mixer . mute ;
context . trackVolumeObject . name = mixer . name ;
context . trackVolumeObject . record = mixer . record ;
context . trackVolumeObject . volL = mixer . volume _left ;
context . trackVolumeObject . volR = mixer . volume _right ;
// trackVolumeObject doesn't have a place for range min/max
currentMixerRangeMin = mixer . range _low ;
currentMixerRangeMax = mixer . range _high ;
2013-02-03 22:47:17 +00:00
}
2013-02-10 02:00:29 +00:00
// Given a mixer's min/max and current value, return it as
// a percent from 0-100. Return an integer.
function percentFromMixerValue ( min , max , value ) {
2013-08-07 20:04:22 +00:00
try {
var range = Math . abs ( max - min ) ;
var magnitude = value - min ;
var percent = Math . round ( 100 * ( magnitude / range ) ) ;
return percent ;
} catch ( err ) {
return 0 ;
}
2013-02-10 02:00:29 +00:00
}
// Given a mixer's min/max and a percent value, return it as
// the mixer's value. Returns an integer.
function percentToMixerValue ( min , max , percent ) {
var range = Math . abs ( max - min ) ;
var multiplier = percent / 100 ; // Change 85 into 0.85
var value = min + ( multiplier * range ) ;
// Protect against percents < 0 and > 100
if ( value < min ) {
value = min ;
}
if ( value > max ) {
value = max ;
}
return value ;
}
2013-02-03 22:47:17 +00:00
2013-02-10 02:00:29 +00:00
// Given a volume percent (0-100), set the underlying
2013-02-03 19:48:39 +00:00
// audio volume level of the passed mixerId to the correct
// value.
function setMixerVolume ( mixerId , volumePercent ) {
2013-02-03 22:47:17 +00:00
// The context.trackVolumeObject has been filled with the mixer values
// that go with mixerId, and the range of that mixer
// has been set in currentMixerRangeMin-Max.
// All that needs doing is to translate the incoming percent
// into the real value ont the sliders range. Set Left/Right
// volumes on trackVolumeObject, and call SetControlState to stick.
2013-02-10 02:00:29 +00:00
var sliderValue = percentToMixerValue (
currentMixerRangeMin , currentMixerRangeMax , volumePercent ) ;
2014-07-31 01:05:04 +00:00
context . trackVolumeObject . volL = context . JK . FaderHelpers . convertPercentToAudioTaper ( volumePercent ) ;
context . trackVolumeObject . volR = context . JK . FaderHelpers . convertPercentToAudioTaper ( volumePercent ) ;
2013-04-10 15:01:29 +00:00
// Special case for L2M mix:
if ( mixerId === '__L2M__' ) {
2013-09-05 21:51:29 +00:00
logger . debug ( "L2M volumePercent=" + volumePercent ) ;
var dbValue = context . JK . FaderHelpers . convertLinearToDb ( volumePercent ) ;
context . jamClient . SessionSetMasterLocalMix ( dbValue ) ;
// context.jamClient.SessionSetMasterLocalMix(sliderValue);
2013-04-10 15:01:29 +00:00
} else {
2014-11-21 23:16:00 +00:00
context . jamClient . SessionSetControlState ( mixerId , sessionModel . getMixMode ( ) ) ;
2013-04-10 15:01:29 +00:00
}
2013-02-03 19:48:39 +00:00
}
2014-05-01 06:35:16 +00:00
function bailOut ( ) {
promptLeave = false ;
context . window . location = '/client#/home' ;
}
2014-02-25 02:20:17 +00:00
function sessionLeave ( evt ) {
evt . preventDefault ( ) ;
2014-04-30 16:44:37 +00:00
rateSession ( ) ;
2014-05-01 06:35:16 +00:00
bailOut ( ) ;
2014-04-30 16:44:37 +00:00
return false ;
}
2014-02-25 02:20:17 +00:00
2014-04-30 16:44:37 +00:00
function rateSession ( ) {
2014-05-01 01:48:57 +00:00
if ( rateSessionDialog === null ) {
2014-05-01 06:35:16 +00:00
rateSessionDialog = new context . JK . RateSessionDialog ( context . JK . app ) ;
2014-05-01 01:48:57 +00:00
rateSessionDialog . initialize ( ) ;
}
2014-05-01 06:35:16 +00:00
rateSessionDialog . showDialog ( ) ;
2014-04-30 16:44:37 +00:00
return true ;
2014-02-25 02:20:17 +00:00
}
2013-09-14 17:27:03 +00:00
function sessionResync ( evt ) {
evt . preventDefault ( ) ;
2013-09-16 01:55:12 +00:00
var response = context . jamClient . SessionAudioResync ( ) ;
2013-09-14 17:27:03 +00:00
if ( response ) {
app . notify ( {
"title" : "Error" ,
"text" : response ,
"icon_url" : "/assets/content/icon_alert_big.png" } ) ;
}
return false ;
}
2013-11-03 20:55:55 +00:00
// http://stackoverflow.com/questions/2604450/how-to-create-a-jquery-clock-timer
function updateRecordingTimer ( ) {
function pretty _time _string ( num ) {
return ( num < 10 ? "0" : "" ) + num ;
}
var total _seconds = ( new Date - startTimeDate ) / 1000 ;
var hours = Math . floor ( total _seconds / 3600 ) ;
total _seconds = total _seconds % 3600 ;
var minutes = Math . floor ( total _seconds / 60 ) ;
total _seconds = total _seconds % 60 ;
var seconds = Math . floor ( total _seconds ) ;
hours = pretty _time _string ( hours ) ;
minutes = pretty _time _string ( minutes ) ;
seconds = pretty _time _string ( seconds ) ;
if ( hours > 0 ) {
var currentTimeString = hours + ":" + minutes + ":" + seconds ;
}
else {
var currentTimeString = minutes + ":" + seconds ;
}
$recordingTimer . text ( '(' + currentTimeString + ')' ) ;
}
function displayStartingRecording ( ) {
$ ( '#recording-start-stop' ) . addClass ( 'currently-recording' ) ;
$ ( '#recording-status' ) . text ( "Starting..." )
}
function displayStartedRecording ( ) {
startTimeDate = new Date ;
$recordingTimer = $ ( "<span id='recording-timer'>(0:00)</span>" ) ;
var $recordingStatus = $ ( '<span></span>' ) . append ( "<span>Stop Recording</span>" ) . append ( $recordingTimer ) ;
$ ( '#recording-status' ) . html ( $recordingStatus ) ;
recordingTimerInterval = setInterval ( updateRecordingTimer , 1000 ) ;
}
function displayStoppingRecording ( data ) {
if ( data ) {
if ( data . reason ) {
app . notify ( {
"title" : "Recording Aborted" ,
"text" : "The recording was aborted due to '" + data . reason + '"' ,
"icon_url" : "/assets/content/icon_alert_big.png"
} ) ;
}
}
$ ( '#recording-status' ) . text ( "Stopping..." ) ;
}
function displayDoneRecording ( ) {
if ( recordingTimerInterval ) {
clearInterval ( recordingTimerInterval ) ;
recordingTimerInterval = null ;
startTimeDate = null ;
}
$recordingTimer = null ;
$ ( '#recording-start-stop' ) . removeClass ( 'currently-recording' ) ;
$ ( '#recording-status' ) . text ( "Make a Recording" ) ;
}
function displayWhoCreated ( clientId ) {
if ( app . clientId != clientId ) { // don't show to creator
sessionModel . findUserBy ( { clientId : clientId } )
. done ( function ( user ) {
app . notify ( {
"title" : "Recording Started" ,
"text" : user . name + " started a recording" ,
"icon_url" : context . JK . resolveAvatarUrl ( user . photo _url )
} ) ;
} )
. fail ( function ( ) {
app . notify ( {
"title" : "Recording Started" ,
"text" : "Oops! Can't determine who started this recording" ,
"icon_url" : "/assets/content/icon_alert_big.png"
} ) ;
} )
}
}
function promptUserToSave ( recordingId ) {
rest . getRecording ( { id : recordingId } )
. done ( function ( recording ) {
2014-01-06 20:35:35 +00:00
recordingFinishedDialog . setRecording ( recording ) ;
2014-10-23 04:10:49 +00:00
app . layout . showDialog ( 'recordingFinished' ) . one ( EVENTS . DIALOG _CLOSED , function ( e , data ) {
if ( data . result && data . result . keep ) {
context . JK . prodBubble ( $recordingManagerViewer , 'file-manager-poke' , { } , { positions : [ 'top' , 'left' , 'right' , 'bottom' ] , offsetParent : $screen . parent ( ) } )
}
} )
2013-11-03 20:55:55 +00:00
} )
. fail ( app . ajaxError ) ;
}
2014-01-05 03:47:23 +00:00
function openRecording ( e ) {
// just ignore the click if they are currently recording for now
if ( sessionModel . recordingModel . 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 false ;
}
if ( ! localRecordingsDialog . isShowing ( ) ) {
app . layout . showDialog ( 'localRecordings' ) ;
}
return false ;
}
function closeRecording ( ) {
rest . stopPlayClaimedRecording ( { id : sessionModel . id ( ) , claimed _recording _id : sessionModel . getCurrentSession ( ) . claimed _recording . id } )
. done ( function ( ) {
sessionModel . refreshCurrentSession ( ) ;
} )
. fail ( function ( 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 ( ) ;
return false ;
}
function onPause ( ) {
logger . debug ( "calling jamClient.SessionStopPlay" ) ;
context . jamClient . SessionStopPlay ( ) ;
}
2014-01-06 20:35:35 +00:00
function onPlay ( e , data ) {
2014-01-05 03:47:23 +00:00
logger . debug ( "calling jamClient.SessionStartPlay" ) ;
2014-01-06 20:35:35 +00:00
context . jamClient . SessionStartPlay ( data . playbackMode ) ;
2014-01-05 03:47:23 +00:00
}
function onChangePlayPosition ( e , data ) {
logger . debug ( "calling jamClient.SessionTrackSeekMs(" + data . positionMs + ")" ) ;
context . jamClient . SessionTrackSeekMs ( data . positionMs ) ;
}
2013-11-03 20:55:55 +00:00
function startStopRecording ( ) {
if ( sessionModel . recordingModel . isRecording ( ) ) {
sessionModel . recordingModel . stopRecording ( ) ;
}
else {
sessionModel . recordingModel . startRecording ( ) ;
}
}
2014-01-14 01:00:42 +00:00
function inviteMusicians ( ) {
2014-07-27 23:39:11 +00:00
friendInput = inviteMusiciansUtil . inviteSessionUpdate ( '#update-session-invite-musicians' ,
sessionId ) ;
inviteMusiciansUtil . loadFriends ( ) ;
$ ( friendInput ) . show ( ) ;
2014-11-11 22:21:29 +00:00
}
function onMixerModeChanged ( e , data )
{
$mixModeDropdown . easyDropDown ( 'select' , data . mode , true ) ;
setTimeout ( renderSession , 1 ) ;
}
function onUserChangeMixMode ( e ) {
var mode = $mixModeDropdown . val ( ) == "master" ? MIX _MODES . MASTER : MIX _MODES . PERSONAL ;
context . jamClient . SetMixerMode ( mode )
modUtils . shouldShow ( NAMED _MESSAGES . MASTER _VS _PERSONAL _MIX ) . done ( function ( shouldShow ) {
if ( shouldShow ) {
var modeChangeHtml = $ ( $templateMixerModeChange . html ( ) ) ;
context . JK . Banner . show ( { title : 'Master vs. Personal Mix' , text : modeChangeHtml , no _show : NAMED _MESSAGES . MASTER _VS _PERSONAL _MIX } ) ;
}
} )
return true ;
2014-01-14 01:00:42 +00:00
}
2012-10-06 16:36:05 +00:00
function events ( ) {
2014-02-25 02:20:17 +00:00
$ ( '#session-leave' ) . on ( 'click' , sessionLeave ) ;
$ ( '#session-resync' ) . on ( 'click' , sessionResync ) ;
2012-10-06 16:36:05 +00:00
$ ( '#session-contents' ) . on ( "click" , '[action="delete"]' , deleteSession ) ;
2013-01-31 16:32:32 +00:00
$ ( '#tracks' ) . on ( 'click' , 'div[control="mute"]' , toggleMute ) ;
2014-01-05 03:47:23 +00:00
$ ( '#recording-start-stop' ) . on ( 'click' , startStopRecording ) ;
$ ( '#open-a-recording' ) . on ( 'click' , openRecording ) ;
2014-01-14 01:00:42 +00:00
$ ( '#session-invite-musicians' ) . on ( 'click' , inviteMusicians ) ;
2014-07-27 23:39:11 +00:00
$ ( '#session-invite-musicians2' ) . on ( 'click' , inviteMusicians ) ;
2013-10-26 01:34:40 +00:00
$ ( '#track-settings' ) . click ( function ( ) {
2014-03-03 22:13:23 +00:00
configureTrackDialog . refresh ( ) ;
2013-05-22 05:03:34 +00:00
configureTrackDialog . showVoiceChatPanel ( true ) ;
2013-10-26 01:34:40 +00:00
configureTrackDialog . showMusicAudioPanel ( true ) ;
2013-05-18 05:59:25 +00:00
} ) ;
2014-01-25 15:37:15 +00:00
2014-01-05 03:47:23 +00:00
$ ( '#close-playback-recording' ) . on ( 'click' , closeRecording ) ;
$ ( playbackControls )
. on ( 'pause' , onPause )
. on ( 'play' , onPlay )
. on ( 'change-position' , onChangePlayPosition ) ;
2014-07-27 23:39:11 +00:00
$ ( friendInput ) . focus ( function ( ) { $ ( this ) . val ( '' ) ; } )
2014-11-11 22:21:29 +00:00
$ ( document ) . on ( EVENTS . MIXER _MODE _CHANGED , onMixerModeChanged )
$mixModeDropdown . change ( onUserChangeMixMode )
2012-10-06 16:36:05 +00:00
}
2014-07-09 22:20:20 +00:00
this . initialize = function ( localRecordingsDialogInstance , recordingFinishedDialogInstance , friendSelectorDialog ) {
inviteMusiciansUtil = new JK . InviteMusiciansUtil ( JK . app ) ;
inviteMusiciansUtil . initialize ( friendSelectorDialog ) ;
2014-01-05 03:47:23 +00:00
localRecordingsDialog = localRecordingsDialogInstance ;
2014-01-06 20:35:35 +00:00
recordingFinishedDialog = recordingFinishedDialogInstance ;
2013-02-15 05:15:36 +00:00
context . jamClient . SetVURefreshRate ( 150 ) ;
2014-02-13 18:54:54 +00:00
context . jamClient . RegisterVolChangeCallBack ( "JK.HandleVolumeChangeCallback" ) ;
2014-01-05 03:47:23 +00:00
playbackControls = new context . JK . PlaybackControls ( $ ( '.session-recordings .recording-controls' ) ) ;
2012-12-07 00:28:48 +00:00
var screenBindings = {
2012-10-29 15:10:02 +00:00
'beforeShow' : beforeShow ,
2012-10-29 00:37:59 +00:00
'afterShow' : afterShow ,
2014-02-23 04:41:42 +00:00
'beforeHide' : beforeHide ,
2014-04-09 17:25:52 +00:00
'beforeLeave' : beforeLeave ,
'beforeDisconnect' : beforeDisconnect ,
2012-10-14 15:48:56 +00:00
} ;
app . bindScreen ( 'session' , screenBindings ) ;
2014-05-22 16:26:56 +00:00
2014-10-23 04:10:49 +00:00
$recordingManagerViewer = $ ( '#recording-manager-viewer' ) ;
$screen = $ ( '#session-screen' ) ;
2014-11-09 15:13:22 +00:00
$mixModeDropdown = $screen . find ( 'select.monitor-mode' )
2014-11-11 22:21:29 +00:00
$templateMixerModeChange = $ ( '#template-mixer-mode-change' ) ;
events ( ) ;
2014-11-09 15:13:22 +00:00
// make sure no previous plays are still going on by accident
2014-05-22 16:26:56 +00:00
context . jamClient . SessionStopPlay ( ) ;
2014-08-31 20:23:42 +00:00
if ( context . jamClient . SessionRemoveAllPlayTracks ) {
// upgrade guard
context . jamClient . SessionRemoveAllPlayTracks ( ) ;
}
2012-10-06 16:36:05 +00:00
} ;
2013-10-12 22:35:12 +00:00
2012-10-22 02:55:05 +00:00
this . tracks = tracks ;
2013-06-10 02:12:43 +00:00
this . getCurrentSession = function ( ) {
return sessionModel . getCurrentSession ( ) ;
2013-06-09 17:02:53 +00:00
} ;
2014-03-05 23:18:53 +00:00
this . refreshCurrentSession = function ( force ) {
sessionModel . refreshCurrentSession ( force ) ;
2013-06-11 01:53:49 +00:00
} ;
2014-02-23 04:41:42 +00:00
this . setPromptLeave = function ( _promptLeave ) {
promptLeave = _promptLeave ;
}
2013-08-07 20:04:22 +00:00
context . JK . HandleVolumeChangeCallback = handleVolumeChangeCallback ;
2013-01-30 16:50:43 +00:00
context . JK . HandleBridgeCallback = handleBridgeCallback ;
2012-10-06 16:36:05 +00:00
} ;
} ) ( window , jQuery ) ;