(function(context,$) { "use strict"; context.JK = context.JK || {}; context.JK.SessionScreen = function(app) { var logger = context.JK.logger; var sessionModel = null; var sessionId; var tracks = {}; var mixers = []; var myTrackCount = 0; // TODO Consolidate dragged controls and handles var $draggingFaderHandle = null; var $draggingFader = null; var $draggingVolumeHandle = null; var $draggingVolume = null; var $draggingChatHandle = null; var $draggingChat = null; var currentMixerIds = null; var currentMixerRangeMin = null; var currentMixerRangeMax = null; var lookingForMixersCount = 0; var lookingForMixersTimer = null; var lookingForMixers = {}; var configure_audio_instructions = { "Win32": "Choose the driver to use for audio and check its settings. Then use arrow " + "buttons to assign audio inputs to your tracks, to indicate what instrument you are playing on " + "each track, and to assign audio outputs for listening. If you don't see an audio device you think " + "should be listed, view this help topic to understand why.", "MacOSX": "Use arrow buttons to assign audio inputs to your tracks, to indicate what " + "instrument you are playing on each track, and to assign audio outputs for listening. If you don't " + "see an audio device you think should be listed, view this help topic to understand why.", "Unix": "Use arrow buttons to assign audio inputs to your tracks, to indicate what " + "instrument you are playing on each track, and to assign audio outputs for listening. If you don't " + "see an audio device you think should be listed, view this help topic to understand why." }; var configure_voice_instructions = "If you are using a microphone to capture your instrumental or vocal audio, you can simply use that mic " + "for both music and chat. Otherwise, choose a device to use for voice chat, and use arrow buttons to " + "select an input on that device."; var defaultParticipant = { tracks: [{ instrument_id: "unknown" }], user: { first_name: 'Unknown', last_name: 'User', photo_url: null } }; var instrumentIcons = context.JK.getInstrumentIconMap45(); // 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, "PeerAudioInputMusicGroup": 9 }; function beforeShow(data) { sessionId = data.id; $('#session-mytracks-container').empty(); $('#session-livetracks-container').empty(); } function afterShow(data) { // Subscribe for callbacks on audio events context.jamClient.SessionRegisterCallback("JK.HandleBridgeCallback"); // 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) { logger.debug("current user loaded. Proceeding to join session."); afterCurrentUserLoaded(); } else { logger.debug("Current user not loaded yet. Waiting..."); context.setTimeout(checkForCurrentUser, 100); } } checkForCurrentUser(); setAudioInstructions(); } function afterCurrentUserLoaded() { sessionModel = new context.JK.SessionModel( context.JK.JamServer, context.jamClient, context.JK.userMe ); sessionModel.subscribe('sessionScreen', sessionChanged); sessionModel.joinSession(sessionId); } function beforeHide(data) { sessionModel.leaveCurrentSession(sessionId); // 'unregister' for callbacks context.jamClient.SessionRegisterCallback(""); } function sessionChanged() { renderSession(); } function renderSession() { $('#session-mytracks-container').empty(); $('#session-livetracks-container').empty(); _updateMixers(); _renderTracks(); _wireTopVolume(); _wireTopMix(); _addVoiceChat(); toggleTrack2ConfigDetails(myTrackCount > 1); } // Get the latest list of underlying audio mixer channels function _updateMixers() { var mixerIds = context.jamClient.SessionGetIDs(); var holder = $.extend(true, {}, {mixers: context.jamClient.SessionGetControlState(mixerIds)}); mixers = holder.mixers; // Always add a hard-coded simplified 'mixer' for the L2M mix var l2m_mixer = { id: '__L2M__', range_low: -80, range_high: 20, volume_left: context.jamClient.SessionGetMasterLocalMix() }; mixers.push(l2m_mixer); } function _mixerForClientId(clientId, groupIds) { var foundMixer = null; $.each(mixers, function(index, mixer) { if (mixer.client_id === clientId) { for (var i=0; i 10) { lookingForMixersCount = 0; lookingForMixers = {}; context.clearTimeout(lookingForMixersTimer); lookingForMixersTimer = null; } } // Given a mixerID and a value between 0.0-1.0, // light up the proper VU lights. function _updateVU(mixerId, value) { // Minor tweak to support VU values -- the mixerId here will // have a suffix added _vul or _vur to indicate the channel. // There are 13 VU lights. Figure out how many to // light based on the incoming value. var i = 0; var state = 'on'; var lights = Math.round(13 * value); var selector = null; var $light = null; var colorClass = 'vu-green-'; // Remove all light classes from all lights var allLightsSelector = '#tracks table[mixer-id="' + mixerId + '"] td.vulight'; $(allLightsSelector).removeClass('vu-green-off vu-green-on vu-red-off vu-red-on'); // Set the lights for (i=0; i<13; i++) { colorClass = 'vu-green-'; state = 'on'; if (i > 8) { colorClass = 'vu-red-'; } if (i >= lights) { state = 'off'; } selector = '#tracks table[mixer-id="' + mixerId + '"] td.vu' + i; $light = $(selector); $light.addClass(colorClass + state); } } function _addTrack(trackData) { var $destination = $('#session-mytracks-container'); if (trackData.clientId !== app.clientId) { $destination = $('#session-livetracks-container'); } trackData["left-vu"] = $('#template-vu').html(); trackData["right-vu"] = trackData["left-vu"]; var template = $('#template-session-track').html(); var newTrack = context.JK.fillTemplate(template, trackData); $destination.append(newTrack); $('div[mixer-id="' + trackData.mixerId + '"].track-icon-settings').click(function() { initMusicAudioPanel(); }); tracks[trackData.clientId] = new context.JK.SessionTrack(trackData.clientId); } function handleBridgeCallback() { var eventName = null; var mixerId = null; var value = null; var tuples = arguments.length / 3; for (var i=0; i 100) { faderPct = 100; } return faderPct; } function fillTrackVolumeObject(mixerId, broadcast) { _updateMixers(); var mixer = null; var _broadcast = true; if (broadcast !== undefined) { _broadcast = broadcast; } for (var i=0; i 100 if (value < min) { value = min; } if (value > max) { value = max; } return value; } // Given a volume percent (0-100), set the underlying // audio volume level of the passed mixerId to the correct // value. function setMixerVolume(mixerId, volumePercent) { // The context.trackVolumeObject has been filled with the mixer values // that go with mixerId, and the range of that mixer // has been set in currentMixerRangeMin-Max. // All that needs doing is to translate the incoming percent // into the real value ont the sliders range. Set Left/Right // volumes on trackVolumeObject, and call SetControlState to stick. var sliderValue = percentToMixerValue( currentMixerRangeMin, currentMixerRangeMax, volumePercent); context.trackVolumeObject.volL = sliderValue; context.trackVolumeObject.volR = sliderValue; // Special case for L2M mix: if (mixerId === '__L2M__') { context.jamClient.SessionSetMasterLocalMix(sliderValue); } else { context.jamClient.SessionSetControlState(mixerId); } } // Refactor. Only need one of these xxxDown methods. function faderHandleDown(evt) { evt.stopPropagation(); $draggingFaderHandle = $(evt.currentTarget); $draggingFader = $draggingFaderHandle.closest('div[control="fader"]'); currentMixerIds = $draggingFader.closest('[mixer-id]').attr('mixer-id').split(','); var mixerIds = currentMixerIds; $.each(mixerIds, function(i,v) { fillTrackVolumeObject(v); }); return false; } function faderMouseUp(evt) { evt.stopPropagation(); if ($draggingFaderHandle) { $draggingFaderHandle = null; $draggingFader = null; currentMixerIds = null; } return false; } function faderMouseMove(evt) { // bail out early if there's no in-process drag if (!($draggingFaderHandle)) { return false; } var $fader = $draggingFader; var $handle = $draggingFaderHandle; evt.stopPropagation(); var orientation = $fader.attr('orientation'); var getPercentFunction = getVerticalFaderPercent; var absolutePosition = evt.clientY; var handleCssAttribute = 'bottom'; if (orientation && orientation == 'horizontal') { getPercentFunction = getHorizontalFaderPercent; absolutePosition = evt.clientX; handleCssAttribute = 'left'; } var faderPct = getPercentFunction(absolutePosition, $fader); var mixerIds = currentMixerIds; $.each(mixerIds, function(i,v) { setMixerVolume(v, faderPct); }); if (faderPct > 90) { faderPct = 90; } // Visual limit $handle.css(handleCssAttribute, faderPct + '%'); return false; } function faderClick(evt) { evt.stopPropagation(); if ($draggingFaderHandle) { return; } var $fader = $(evt.currentTarget); // Switch based on fader orientation var orientation = $fader.attr('orientation'); var getPercentFunction = getVerticalFaderPercent; var absolutePosition = evt.clientY; var handleCssAttribute = 'bottom'; if (orientation && orientation == 'horizontal') { getPercentFunction = getHorizontalFaderPercent; absolutePosition = evt.clientX; handleCssAttribute = 'left'; } var $handle = $fader.find('div[control="fader-handle"]'); var faderPct = getPercentFunction(absolutePosition, $fader); // mixerIds can be a comma-separated list var mixerIds = $fader.closest('[mixer-id]').attr('mixer-id').split(','); $.each(mixerIds, function(i,v) { fillTrackVolumeObject(v); setMixerVolume(v, faderPct); }); if (faderPct > 90) { faderPct = 90; } // Visual limit $handle.css(handleCssAttribute, faderPct + '%'); return false; } function events() { $('#session-contents').on("click", '[action="delete"]', deleteSession); $('#tracks').on('click', 'div[control="mute"]', toggleMute); $('#tracks').on('click', 'div[control="fader"]', faderClick); $('#tracks').on('mousedown', 'div[control="fader-handle"]', faderHandleDown); $('#tracks').on('mousemove', faderMouseMove); $('body').on('mouseup', faderMouseUp); $('#session-controls .fader').on('click', 'div[control="fader"]', faderClick); $('#session-controls .fader').on('mousedown', '.handle', faderHandleDown); $('body').on('mousemove', faderMouseMove); $('body').on('mouseup', faderMouseUp); // Go ahead and wire up voice-chat events, although it won't be visible // (and can't fire events) unless the user has a voice chat mixer $('#voice-chat').on('click', 'div[control="fader"]', faderClick); $('#voice-chat').on('mousedown', 'div[control="fader-handle"]', faderHandleDown); $('#voice-chat').on('mousemove', faderMouseMove); $('body').on('mouseup', faderMouseUp); $('#tab-configure-audio').click(function() { // validate voice chat settings if (validateVoiceChatSettings()) { setAudioInstructions(); initMusicAudioPanel(); } }); $('#tab-configure-voice').click(function() { // validate audio settings if (validateAudioSettings()) { initVoiceChatPanel(); $('#instructions', 'div[layout-id="configure-audio"]').html(configure_voice_instructions); } }); // Track 1 Add $('#img-track1-input-add').click(function() { $('#audio-inputs-unused option:selected').remove().appendTo('#track1-input'); }); // Track 1 Remove $('#img-track1-input-remove').click(function() { $('#track1-input option:selected').remove().appendTo('#audio-inputs-unused'); }); // Track 2 Add $('#img-track2-input-add').click(function() { $('#audio-inputs-unused option:selected').remove().appendTo('#track2-input'); }); // Track 2 Remove $('#img-track2-input-remove').click(function() { $('#track2-input option:selected').remove().appendTo('#audio-inputs-unused'); }); // Audio Output Add $('#img-audio-output-add').click(function() { $('#audio-output-unused option:selected').remove().appendTo('#audio-output-selection'); }); // Audio Output Remove $('#img-audio-output-remove').click(function() { $('#audio-output-selection option:selected').remove().appendTo('#audio-output-unused'); }); $('.voicechat-settings').click(function() { initVoiceChatPanel(); }); $('#btn-driver-settings').click(function() { context.jamClient.TrackSetMusicDevice($('#audio-drivers').val()); if (context.jamClient.TrackHasControlPanel()) { logger.debug("Opening control panel"); context.jamClient.TrackOpenControlPanel(); } }); $('#btn-save-settings').click(function() { if (!validateAudioSettings()) { return false; } if (!validateVoiceChatSettings()) { } }); } function initMusicAudioPanel() { $('div[tab-id="music-audio"]').show(); $('div[tab-id="voice-chat"]').hide(); $('#tab-configure-audio').addClass('selected'); $('#tab-configure-voice').removeClass('selected'); $('#audio-drivers').empty(); // load Audio Driver dropdown var devices = context.jamClient.TrackGetDevices(); $.each(devices, function(index, val) { var template = $('#template-dropdown').html(); var audioDriversHtml = context.JK.fillTemplate(template, { value: val.device_id, label: val.device_name }); $('#audio-drivers').append(audioDriversHtml); }); } function initVoiceChatPanel() { $('div[tab-id="music-audio"]').hide(); $('div[tab-id="voice-chat"]').show(); $('#tab-configure-audio').removeClass('selected'); $('#tab-configure-voice').addClass('selected'); } function validateAudioSettings() { return true; } function validateVoiceChatSettings() { return true; } function setAudioInstructions() { var os = context.jamClient.GetOSAsString(); $('#instructions', 'div[layout-id="configure-audio"]').html(configure_audio_instructions[os]); } this.initialize = function() { context.jamClient.SetVURefreshRate(150); events(); var screenBindings = { 'beforeShow': beforeShow, 'afterShow': afterShow, 'beforeHide': beforeHide }; app.bindScreen('session', screenBindings); }; this.tracks = tracks; context.JK.HandleBridgeCallback = handleBridgeCallback; }; })(window,jQuery);