/** * FtueAudioSelectionScreen * Javascript that goes with the screen where initial * selection of the audio devices takes place. * Corresponds to /#ftue2 */ (function (context, $) { "use strict"; context.JK = context.JK || {}; context.JK.FtueWizard = function (app) { context.JK.FtueWizard.latencyTimeout = true; context.JK.FtueWizard.latencyMS = Number.MAX_VALUE; var rest = context.JK.Rest(); var logger = context.JK.logger; var jamClient = context.jamClient; var win32 = true; var batchModify = false; var pendingFtueSave = false; var successfulFtue = false; // tracks in the loopback FTUe what the currently chosen audio driver is var currentAudioDriverId = null; var deviceSetMap = { 'audio-input': jamClient.FTUESetMusicInput, 'audio-output': jamClient.FTUESetMusicOutput, 'voice-chat-input': jamClient.FTUESetChatInput }; var faderMap = { 'ftue-2-audio-input-fader': jamClient.FTUESetInputVolume, 'ftue-2-voice-input-fader': jamClient.FTUESetOutputVolume, 'ftue-audio-input-fader': jamClient.FTUESetInputVolume, 'ftue-voice-input-fader': jamClient.FTUESetChatInputVolume, 'ftue-audio-output-fader': jamClient.FTUESetOutputVolume }; var faderReadMap = { 'ftue-2-audio-input-fader': jamClient.FTUEGetInputVolume, 'ftue-2-voice-input-fader': jamClient.FTUEGetOutputVolume, 'ftue-audio-input-fader': jamClient.FTUEGetInputVolume, 'ftue-voice-input-fader': jamClient.FTUEGetChatInputVolume, 'ftue-audio-output-fader': jamClient.FTUEGetOutputVolume }; function latencyTimeoutCheck() { if (context.JK.FtueWizard.latencyTimeout) { jamClient.FTUERegisterLatencyCallback(''); context.JK.app.setWizardStep("5"); } } function afterHide(data) { // Unsubscribe from FTUE VU callbacks. jamClient.FTUERegisterVUCallbacks('', '', ''); if (!successfulFtue && app.cancelFtue) { app.cancelFtue(); app.afterFtue = null; app.cancelFtue = null; } } function beforeShow(data) { successfulFtue = false; var vuMeters = [ '#ftue-2-audio-input-vu-left', '#ftue-2-audio-input-vu-right', '#ftue-2-voice-input-vu-left', '#ftue-2-voice-input-vu-right', '#ftue-audio-input-vu-left', '#ftue-audio-input-vu-right', '#ftue-voice-input-vu-left', '#ftue-voice-input-vu-right', '#ftue-audio-output-vu-left', '#ftue-audio-output-vu-right' ]; $.each(vuMeters, function () { context.JK.VuHelpers.renderVU(this, {vuType: "horizontal", lightCount: 12, lightWidth: 15, lightHeight: 3}); }); var faders = context._.keys(faderMap); $.each(faders, function () { var fid = this; context.JK.FaderHelpers.renderFader('#' + fid, {faderId: fid, faderType: "horizontal", width: 163}); context.JK.FaderHelpers.subscribe(fid, faderChange); }); } function afterShow(data) { } // renders volumes based on what the backend says function renderVolumes() { $.each(context._.keys(faderReadMap), function (index, faderId) { // faderChange takes a value from 0-100 var $fader = $('[fader-id="' + faderId + '"]'); var db = faderReadMap[faderId](); var faderPct = db + 80; context.JK.FaderHelpers.setHandlePosition($fader, faderPct); //faderChange(faderId, faderPct); }); } function faderChange(e, data) { } function setSaveButtonState($save, enabled) { if (enabled) { $save.removeClass('disabled'); } else { $save.addClass('disabled'); } } function checkValidStateForTesting() { var reqMissing = !musicInAndOutSet() || currentAudioDriverId == null || currentAudioDriverId == ''; if (reqMissing) { renderDisableTest(); } else { renderEnableTest(); } } function renderDisableTest() { $('#btn-ftue-test').addClass('disabled'); } function renderEnableTest() { $('#btn-ftue-test').removeClass('disabled'); } function renderStartNewFtueLatencyTesting() { setSaveButtonState($('#btn-ftue-2-save'), false); } function renderStopNewFtueLatencyTesting() { } function settingsInit() { jamClient.FTUEInit(); //setLevels(0); resetFtueLatencyView(); setSaveButtonState($('#btn-ftue-2-save'), false); if (jamClient.GetOSAsString() !== "Win32") { $('#btn-ftue-2-asio-control-panel').hide(); } renderDisableTest(); // Always reset the driver select box to "Choose..." which forces everything // to sync properly when the user reselects their driver of choice. // VRFS-375 and VRFS-561 $('[layout-wizard="ftue"] [layout-wizard-step="0"] .settings-2-device select').val(""); $('[layout-wizard="ftue"] [layout-wizard-step="0"] .settings-2-voice select').val(""); $('[layout-wizard="ftue"] [layout-wizard-step="0"] #ftue-2-asio-framesize').val("").easyDropDown('disable'); $('[layout-wizard="ftue"] [layout-wizard-step="0"] #ftue-2-asio-input-latency').val("0").easyDropDown('disable'); $('[layout-wizard="ftue"] [layout-wizard-step="0"] #ftue-2-asio-output-latency').val("0").easyDropDown('disable'); // with the old ftue, this is pretty annoying to reset these everytime $('[layout-wizard="ftue"] [layout-wizard-step="2"] .asio-settings .settings-driver select').val(""); $('[layout-wizard="ftue"] [layout-wizard-step="2"] .settings-controls select[data-device="audio-input"]').val(""); $('[layout-wizard="ftue"] [layout-wizard-step="2"] .settings-controls select[data-device="audio-output"]').val(""); $('[layout-wizard="ftue"] [layout-wizard-step="2"] .settings-controls select[data-device="voice-chat-output"]').val(""); } function setLevels(db) { if (db < -80 || db > 20) { throw ("BUG! ftue.js:setLevels db arg must be between -80 and 20"); } var trackIds = jamClient.SessionGetIDs(); var controlStates = jamClient.SessionGetControlState(trackIds); $.each(controlStates, function (index, value) { context.JK.Mixer.fillTrackVolume(value, false); // Default input/output to 0 DB context.trackVolumeObject.volL = db; context.trackVolumeObject.volR = db; jamClient.SessionSetControlState(trackIds[index]); }); $.each(context._.keys(faderMap), function (index, faderId) { // faderChange takes a value from 0-100 var $fader = $('[fader-id="' + faderId + '"]'); var faderPct = db + 80; context.JK.FaderHelpers.setHandlePosition($fader, faderPct); faderChange(faderId, faderPct); }); } function testComplete() { logger.debug("Test complete"); var latencyMS = context.JK.FtueWizard.latencyMS; var ftueSucceeded = latencyMS <= 20; if (ftueSucceeded) { logger.debug(latencyMS + " is <= 20. Setting FTUE status to true"); ftueSave(true); // Save the profile context.jamClient.FTUESetStatus(true); // No FTUE wizard next time rest.userCertifiedGear({success: true}); // notify anyone curious about how it went $('div[layout-id=ftue]').trigger('ftue_success'); } else { rest.userCertifiedGear({success: false, reason: "latency=" + latencyMS}); } updateGauge(); } function updateGauge() { var latencyMS = context.JK.FtueWizard.latencyMS; // Round to 2 decimal places latencyMS = (Math.round(latencyMS * 100)) / 100; logger.debug("Latency Value: " + latencyMS); if (latencyMS > 20) { $('#ftue-latency-congrats').hide(); $('#ftue-latency-fail').show(); } else { $('#ftue-latency-ms').html(latencyMS); $('#ftue-latency-congrats').show(); $('#ftue-latency-fail').hide(); if (latencyMS <= 10) { $('[layout-wizard-step="6"] .btnHelp').hide(); $('[layout-wizard-step="6"] .btnRepeat').hide(); } } setNeedleValue(latencyMS); } // Function to calculate an absolute value and an absolute value range into // a number of degrees on a circualar "speedometer" gauge. The 0 degrees value // points straight up to the middle of the real-world value range. // Arguments: // value: The real-world value (e.g. 20 milliseconds) // minValue: The real-world minimum value (e.g. 0 milliseconds) // maxValue: The real-world maximum value (e.g. 40 milliseconds) // degreesUsed: The number of degrees used to represent the full real-world // range. 360 would be the entire circle. 270 would be 3/4ths // of the circle. The unused gap will be at the bottom of the // gauge. // (e.g. 300) function degreesFromRange(value, minValue, maxValue, degreesUsed) { if (value > maxValue) { value = maxValue; } if (value < minValue) { value = minValue; } var range = maxValue - minValue; var floatVal = value / range; var degrees = Math.round(floatVal * degreesUsed); degrees -= Math.round(degreesUsed / 2); if (degrees < 0) { degrees += 360; } return degrees; } // Given a number of MS, and assuming the gauge has a range from // 0 to 40 ms. Update the gauge to the proper value. function setNeedleValue(ms) { logger.debug("setNeedleValue: " + ms); var deg = degreesFromRange(ms, 0, 40, 300); // Supporting Firefix, Chrome, Safari, Opera and IE9+. // Should we need to support < IE9, this will need more work // to compute the matrix transformations in those browsers - // and I don't believe they support transparent PNG graphic // rotation, so we'll have to change the needle itself. var css = { //"behavior":"url(-ms-transform.htc)", /* Firefox */ "-moz-transform": "rotate(" + deg + "deg)", /* Safari and Chrome */ "-webkit-transform": "rotate(" + deg + "deg)", /* Opera */ "-o-transform": "rotate(" + deg + "deg)", /* IE9 */ "-ms-transform": "rotate(" + deg + "deg)" /* IE6,IE7 */ //"filter": "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476)", /* IE8 */ //"-ms-filter": '"progid:DXImageTransform.Microsoft.Matrix(SizingMethod=\'auto expand\', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476)"' }; $('#ftue-latency-numerical').html(ms); $('#ftue-latency-needle').css(css); } function testLatency() { // we'll just register for call back right here and unregister in the callback. context.JK.FtueWizard.latencyTimeout = true; var cbFunc = 'JK.ftueLatencyCallback'; logger.debug("Registering latency callback: " + cbFunc); jamClient.FTUERegisterLatencyCallback('JK.ftueLatencyCallback'); var now = new Date(); logger.debug("Starting Latency Test..." + now); context.setTimeout(latencyTimeoutCheck, 300 * 1000); // Timeout to 5 minutes jamClient.FTUEStartLatency(); } function openASIOControlPanel(evt) { if (win32) { logger.debug("Calling FTUEOpenControlPanel()"); jamClient.FTUEOpenControlPanel(); } } function asioResync(evt) { jamClient.FTUERefreshDevices(); ftueSave(false); } function ftueSave(persist) { // Explicitly set inputs and outputs to dropdown values // before save as the client seems to want this on changes to // things like frame size, etc.. var $audioSelects = $('[layout-wizard-step="2"] .settings-controls select'); $.each($audioSelects, function (index, value) { var $select = $(value); setAudioDevice($select); }); if (musicInAndOutSet()) { renderVolumes(); // If there is no voice-chat-input selected, let the back-end know // that we're using music for voice-chat. if ($('[layout-wizard-step="2"] select[data-device="voice-chat-input"]').val()) { // Voice input selected jamClient.TrackSetChatEnable(true); } else { // No voice input selected. jamClient.TrackSetChatEnable(false); } setDefaultInstrumentFromProfile(); logger.debug("Calling FTUESave(" + persist + ")"); var response = jamClient.FTUESave(persist); //setLevels(0); if (response) { logger.warn(response); // TODO - we may need to do something about errors on save. // per VRFS-368, I'm hiding the alert, and logging a warning. // context.alert(response); } } else { logger.debug("Aborting FTUESave as we need input + output selected."); } } function setAsioFrameSize(evt) { var val = parseFloat($(evt.currentTarget).val(), 10); if (isNaN(val)) { return; } logger.debug("Calling FTUESetFrameSize(" + val + ")"); jamClient.FTUESetFrameSize(val); ftueSave(false); } function setAsioInputLatency(evt) { var val = parseInt($(evt.currentTarget).val(), 10); if (isNaN(val)) { return; } logger.debug("Calling FTUESetInputLatency(" + val + ")"); jamClient.FTUESetInputLatency(val); ftueSave(false); } function setAsioOutputLatency(evt) { var val = parseInt($(evt.currentTarget).val(), 10); if (isNaN(val)) { return; } logger.debug("Calling FTUESetOutputLatency(" + val + ")"); jamClient.FTUESetOutputLatency(val); ftueSave(false); } function testRequested(evt) { evt.preventDefault(); var $testButton = $('#btn-ftue-test'); if (!$testButton.hasClass('disabled')) { app.setWizardStep(3); } return false; } function videoLinkClicked(evt) { var myOS = jamClient.GetOSAsString(); var link; if (myOS === 'MacOSX') { link = $(evt.currentTarget).attr('external-link-mac'); } else if (myOS === 'Win32') { link = $(evt.currentTarget).attr('external-link-win'); } if (link) { context.jamClient.OpenSystemBrowser(link); } } function events() { $('.ftue-video-link').hover( function (evt) { // handlerIn $(this).addClass('hover'); }, function (evt) { // handlerOut $(this).removeClass('hover'); } ); $('.ftue-video-link').on('click', videoLinkClicked); $('[layout-wizard-step="2"] .settings-driver select').on('change', audioDriverChanged); $('[layout-wizard-step="2"] .settings-controls select').on('change', audioDeviceChanged); $('#btn-asio-control-panel').on('click', openASIOControlPanel); $('#btn-asio-resync').on('click', asioResync); $('#asio-framesize').on('change', setAsioFrameSize); $('#asio-input-latency').on('change', setAsioInputLatency); $('#asio-output-latency').on('change', setAsioOutputLatency); $('#btn-ftue-test').on('click', testRequested) // New FTUE events $('.ftue-new .settings-2-device select').on('change', newFtueAudioDeviceChanged); $('.ftue-new .settings-2-voice select').on('change', newFtueAudioDeviceChanged); $('#btn-ftue-2-asio-resync').on('click', newFtueAsioResync); $('#btn-ftue-2-asio-control-panel').on('click', openASIOControlPanel); $('#ftue-2-asio-framesize').on('change', newFtueSetAsioFrameSize); $('#ftue-2-asio-input-latency').on('change', newFtueSetAsioInputLatency); $('#ftue-2-asio-output-latency').on('change', newFtueSetAsioOutputLatency); $('#btn-ftue-2-save').on('click', newFtueSaveSettingsHandler); } /** * This function loads the available audio devices from jamClient, and * builds up the select dropdowns in the audio-settings step of the FTUE wizard. */ function loadAudioDevices() { var funcs = [ jamClient.FTUEGetMusicInputs, jamClient.FTUEGetChatInputs, jamClient.FTUEGetMusicOutputs ]; var selectors = [ '[layout-wizard-step="2"] .audio-input select', '[layout-wizard-step="2"] .voice-chat-input select', '[layout-wizard-step="2"] .audio-output select' ]; var optionsHtml = ''; var deviceOptionFunc = function (deviceKey, index, list) { optionsHtml += ''; }; for (var i = 0; i < funcs.length; i++) { optionsHtml = ''; var devices = funcs[i](); // returns hash of device id: device name var $select = $(selectors[i]); $select.empty(); var sortedDeviceKeys = context._.keys(devices).sort(); context._.each(sortedDeviceKeys, deviceOptionFunc); $select.html(optionsHtml); context.JK.dropdown($select); $select.removeAttr("disabled").easyDropDown('enable'); $('[layout-wizard-step="2"] .settings-asio select').removeAttr("disabled").easyDropDown('enable') // Set selects to lowest possible values to start: $('#asio-framesize').val('2.5').change(); $('#asio-input-latency').val('0').change(); $('#asio-output-latency').val('0').change(); // Special-case for a non-ASIO device, set to 1 if (jamClient.GetOSAsString() === "Win32") { // Limit this check to Windows only. if (!(jamClient.FTUEHasControlPanel())) { $('#asio-input-latency').val('1').change(); $('#asio-output-latency').val('1').change(); } } } } /** * Load available drivers and populate the driver select box. */ function loadAudioDrivers() { var drivers = context.jamClient.FTUEGetDevices(false); var chatDrivers = jamClient.FTUEGetChatInputs(false, false); var optionsHtml = ''; var chatOptionsHtml = ''; var driverOptionFunc = function (driverKey, index, list) { if(!drivers[driverKey]) { logger.debug("skipping unknown device:", driverKey) } else { optionsHtml += ''; } }; var chatOptionFunc = function (driverKey, index, list) { chatOptionsHtml += ''; }; var selectors = [ '[layout-wizard-step="0"] .settings-2-device select', '[layout-wizard-step="2"] .settings-driver select' ]; // handle standard devices var sortedDeviceKeys = context._.keys(drivers).sort(); context._.each(sortedDeviceKeys, driverOptionFunc); $.each(selectors, function (index, selector) { var $select = $(selector); $select.empty(); $select.html(optionsHtml); context.JK.dropdown($select); }); selectors = ['[layout-wizard-step="0"] .settings-2-voice select']; var sortedVoiceDeviceKeys = context._.keys(chatDrivers).sort(); // handle voice inputs context._.each(sortedVoiceDeviceKeys, chatOptionFunc); $.each(selectors, function (index, selector) { var $select = $(selector); $select.empty(); $select.html(chatOptionsHtml); context.JK.dropdown($select); }); } /** Once a configuration is decided upon, we set the user's default instrument based on data from their profile */ function setDefaultInstrumentFromProfile() { var defaultInstrumentId; if (context.JK.userMe.instruments && context.JK.userMe.instruments.length > 0) { defaultInstrumentId = context.JK.instrument_id_to_instrument[context.JK.userMe.instruments[0].instrument_id].client_id; } else { defaultInstrumentId = context.JK.server_to_client_instrument_map['Other'].client_id; } jamClient.TrackSetInstrument(1, defaultInstrumentId); } /** * Handler for the new FTUE save button. */ function newFtueSaveSettingsHandler(evt) { evt.preventDefault(); var $saveButton = $('#btn-ftue-2-save'); if ($saveButton.hasClass('disabled')) { return; } var selectedAudioDevice = $('.ftue-new .settings-2-device select').val(); if (!(selectedAudioDevice)) { app.notify({ title: "Please select an audio device", text: "Please choose a usable audio device, or select cancel." }); return false; } setDefaultInstrumentFromProfile(); logger.debug("Calling FTUESave(" + true + ")"); jamClient.FTUESave(true); jamClient.FTUESetStatus(true); // No FTUE wizard next time rest.userCertifiedGear({success: true}); // notify anyone curious about how it went $('div[layout-id=ftue]').trigger('ftue_success'); successfulFtue = true; // important to before closeDialog app.layout.closeDialog('ftue'); if (app.afterFtue) { // If there's a function to invoke, invoke it. app.afterFtue(); app.afterFtue = null; app.cancelFtue = null; } return false; } // used in a .change event in a select to allow the select to reliably close // needed because ftue testing blocks the UI function releaseDropdown(proc) { setTimeout(function () { proc() }, 1) } // Handler for when the audio device is changed in the new FTUE screen // This works differently from the old FTUE. There is no input/output selection, // as soon as the user chooses a driver, we auto-assign inputs and outputs. // We also call jamClient.FTUEGetExpectedLatency, which returns a structure like: // { latency: 11.1875, latencyknown: true, latencyvar: 1} function newFtueAudioDeviceChanged(evt) { releaseDropdown(function () { renderStartNewFtueLatencyTesting(); var $select = $(evt.currentTarget); var $audioSelect = $('.ftue-new .settings-2-device select'); var $voiceSelect = $('.ftue-new .settings-2-voice select'); var audioDriverId = $audioSelect.val(); var voiceDriverId = $voiceSelect.val(); jamClient.FTUESetMusicDevice(audioDriverId); jamClient.FTUESetChatInput(voiceDriverId); if (voiceDriverId) { // Let the back end know whether a voice device is selected jamClient.TrackSetChatEnable(true); } else { jamClient.TrackSetChatEnable(false); } if (!audioDriverId) { console.log("no audio driver ID"); renderStopNewFtueLatencyTesting(); // reset back to 'Choose...' newFtueEnableControls(false); return; } var musicInputs = jamClient.FTUEGetMusicInputs(); var musicOutputs = jamClient.FTUEGetMusicOutputs(); // set the music input to the first available input, // and output to the first available output var kin = null, kout = null, k = null; // TODO FIXME - this jamClient call returns a dictionary. // It's difficult to know what to auto-choose. // For example, with my built-in audio, the keys I get back are // digital in, line in, mic in and stereo mix. Which should we pick for them? for (k in musicInputs) { kin = k; break; } for (k in musicOutputs) { kout = k; break; } var result; if (kin && kout) { jamClient.FTUESetMusicInput(kin); jamClient.FTUESetMusicOutput(kout); } else { // TODO FIXME - how to handle a driver selection where we are unable to // autoset both inputs and outputs? (I'd think this could happen if either // the input or output side returned no values) renderStopNewFtueLatencyTesting(); console.log("invalid kin/kout %o/%o", kin, kout); return; } newFtueUpdateLatencyView('loading'); newFtueEnableControls(true); newFtueOsSpecificSettings(); // make sure whatever the user sees in the frontend is what the backend thinks // this is necesasry because if you do a FTUE pass, close the client, and pick the same device // the backend will *silently* use values from before, because the frontend does not query the backend // for these values anywhere. // setting all 3 of these cause 3 FTUE's. Instead, we set batchModify to true so that they don't call FtueSave(false), and call later ourselves batchModify = true; newFtueAsioFrameSizeToBackend($('#ftue-2-asio-framesize')); newFtueAsioInputLatencyToBackend($('#ftue-2-asio-input-latency')); newFtueAsioOutputLatencyToBackend($('#ftue-2-asio-output-latency')); batchModify = false; //setLevels(0); renderVolumes(); logger.debug("Calling FTUESave(" + false + ")"); jamClient.FTUESave(false) pendingFtueSave = false; // this is not really used in any real fashion. just setting back to false due to batch modify above setVuCallbacks(); var latency = jamClient.FTUEGetExpectedLatency(); console.log("FTUEGetExpectedLatency: %o", latency); newFtueUpdateLatencyView(latency); }); } function newFtueSave(persist) { logger.debug("newFtueSave persist(" + persist + ")") newFtueUpdateLatencyView('loading'); logger.debug("Calling FTUESave(" + persist + ")"); jamClient.FTUESave(persist); var latency = jamClient.FTUEGetExpectedLatency(); newFtueUpdateLatencyView(latency); } function newFtueAsioResync(evt) { // In theory, we should be calling the following, but it causes // us to not have both inputs/outputs loaded, and simply calling // FTUE Save appears to resync things. //jamClient.FTUERefreshDevices(); newFtueSave(false); } // simply tells backend what frontend shows in the UI function newFtueAsioFrameSizeToBackend($input) { var val = parseFloat($input.val(), 10); if (isNaN(val)) { logger.warn("unable to get value from framesize input: %o", $input.val()); return false; } // we need to help WDM users start with good starting input/output values. if(isWDM()) { var defaultInput = 1; var defaultOutput = 1; if(val == 2.5) { defaultInput = 1; defaultOutput = 1; } else if(val == 5) { defaultInput = 3; defaultOutput = 2; } else if(val == 10) { defaultInput = 6; defaultOutput = 5; } $('#ftue-2-asio-input-latency').val(defaultInput.toString()); $('#ftue-2-asio-output-latency').val(defaultOutput.toString()); logger.debug("Defaulting WDM input/output"); logger.debug("Calling FTUESetInputLatency(" + defaultInput + ")"); jamClient.FTUESetInputLatency(defaultInput); logger.debug("Calling FTUESetOutputLatency(" + defaultOutput + ")"); jamClient.FTUESetOutputLatency(defaultOutput); } logger.debug("Calling FTUESetFrameSize(" + val + ")"); jamClient.FTUESetFrameSize(val); return true; } function newFtueAsioInputLatencyToBackend($input) { var val = parseInt($input.val(), 10); if (isNaN(val)) { logger.warn("unable to get value from input latency input: %o", $input.val()); return false; } logger.debug("Calling FTUESetInputLatency(" + val + ")"); jamClient.FTUESetInputLatency(val); return true; } function newFtueAsioOutputLatencyToBackend($input) { var val = parseInt($input.val(), 10); if (isNaN(val)) { logger.warn("unable to get value from output latency input: %o", $input.val()); return false; } logger.debug("Calling FTUESetOutputLatency(" + val + ")"); jamClient.FTUESetOutputLatency(val); return true; } function newFtueSetAsioFrameSize(evt) { releaseDropdown(function () { renderStartNewFtueLatencyTesting(); if (!newFtueAsioFrameSizeToBackend($(evt.currentTarget))) { renderStopNewFtueLatencyTesting(); return; } if (batchModify) { pendingFtueSave = true; } else { newFtueSave(false); } }); } function newFtueSetAsioInputLatency(evt) { releaseDropdown(function () { renderStartNewFtueLatencyTesting(); if (!newFtueAsioInputLatencyToBackend($(evt.currentTarget))) { renderStopNewFtueLatencyTesting(); return; } if (batchModify) { pendingFtueSave = true; } else { newFtueSave(false); } }); } function newFtueSetAsioOutputLatency(evt) { releaseDropdown(function () { renderStartNewFtueLatencyTesting(); if (!newFtueAsioOutputLatencyToBackend($(evt.currentTarget))) { renderStopNewFtueLatencyTesting(); return; } if (batchModify) { pendingFtueSave = true; } else { newFtueSave(false); } }); } // Enable or Disable the frame/buffer controls in the new FTUE screen function newFtueEnableControls(enable) { var $frame = $('#ftue-2-asio-framesize'); var $bin = $('#ftue-2-asio-input-latency'); var $bout = $('#ftue-2-asio-output-latency'); if (enable) { $frame.removeAttr("disabled").easyDropDown('enable'); $bin.removeAttr("disabled").easyDropDown('enable'); $bout.removeAttr("disabled").easyDropDown('enable'); } else { $frame.attr("disabled", "disabled").easyDropDown('disable'); $bin.attr("disabled", "disabled").easyDropDown('disable'); $bout.attr("disabled", "disabled".easyDropDown('disable')); } } function isWDM() { return jamClient.GetOSAsString() === "Win32" && !jamClient.FTUEHasControlPanel(); } // Based on OS and Audio Hardware, set Frame/Buffer settings appropriately // and show/hide the ASIO button. function newFtueOsSpecificSettings() { var $frame = $('#ftue-2-asio-framesize'); var $bin = $('#ftue-2-asio-input-latency'); var $bout = $('#ftue-2-asio-output-latency'); var $asioBtn = $('#btn-ftue-2-asio-control-panel'); if (jamClient.GetOSAsString() === "Win32") { if (jamClient.FTUEHasControlPanel()) { // Win32 + ControlPanel = ASIO // frame=2.5, buffers=0 $asioBtn.show(); $frame.val('2.5'); $bin.val('0'); $bout.val('0'); } else { // Win32, no ControlPanel = WDM/Kernel Streaming // frame=10, buffers=0 $asioBtn.hide(); $frame.val('2.5'); $bin.val('1'); $bout.val('1'); } } else { // Assuming Mac. TODO: Linux check here // frame=2.5, buffers=0 $asioBtn.hide(); $frame.val('2.5'); $bin.val('0'); $bout.val('0'); } // you need to give these to the backend now; if you don't, when the device is selected, the backend // may fail to open it (especially with kernel streaming). Causes scary looking errors to user. newFtueAsioFrameSizeToBackend($('#ftue-2-asio-framesize')); newFtueAsioInputLatencyToBackend($('#ftue-2-asio-input-latency')); newFtueAsioOutputLatencyToBackend($('#ftue-2-asio-output-latency')); } function resetFtueLatencyView() { var $report = $('.ftue-new .latency .report'); var $instructions = $('.ftue-new .latency .instructions'); $('.ms-label', $report).empty(); $('p', $report).empty(); var latencyClass = 'start'; $report.removeClass('good acceptable bad'); $report.addClass(latencyClass); var instructionClasses = ['neutral', 'good', 'acceptable', 'bad', 'start', 'loading']; $.each(instructionClasses, function (idx, val) { $('p.' + val, $instructions).hide(); }); $('p.' + latencyClass, $instructions).show(); } // Given a latency structure, update the view. function newFtueUpdateLatencyView(latency) { var $report = $('.ftue-new .latency .report'); var $instructions = $('.ftue-new .latency .instructions'); var latencyClass = "neutral"; var latencyValue = "N/A"; var $saveButton = $('#btn-ftue-2-save'); if (latency && latency.latencyknown) { latencyValue = latency.latency; // Round latency to two decimal places. latencyValue = Math.round(latencyValue * 100) / 100; if (latency.latency <= 10) { latencyClass = "good"; setSaveButtonState($saveButton, true); } else if (latency.latency <= 20) { latencyClass = "acceptable"; setSaveButtonState($saveButton, true); } else { latencyClass = "bad"; setSaveButtonState($saveButton, false); } } else { latencyClass = "unknown"; setSaveButtonState($saveButton, false); } $('.ms-label', $report).html(latencyValue); $('p', $report).html('milliseconds'); $report.removeClass('good acceptable bad unknown'); $report.addClass(latencyClass); var instructionClasses = ['neutral', 'good', 'acceptable', 'unknown', 'bad', 'start', 'loading']; $.each(instructionClasses, function (idx, val) { $('p.' + val, $instructions).hide(); }); if (latency === 'loading') { $('p.loading', $instructions).show(); } else { $('p.' + latencyClass, $instructions).show(); renderStopNewFtueLatencyTesting(); } } function audioDriverChanged(evt) { var $select = $(evt.currentTarget); currentAudioDriverId = $select.val(); jamClient.FTUESetMusicDevice(currentAudioDriverId); loadAudioDevices(); setAsioSettingsVisibility(); checkValidStateForTesting(); } function audioDeviceChanged(evt) { var $select = $(evt.currentTarget); setAudioDevice($select); if (musicInAndOutSet()) { ftueSave(false); setVuCallbacks(); } checkValidStateForTesting(); } function setAudioDevice($select) { var device = $select.data('device'); var deviceId = $select.val(); // Note: We always set, even on the "Choose" value of "", which clears // the current setting. var setFunction = deviceSetMap[device]; setFunction(deviceId); } /** * Return a boolean indicating whether both the MusicInput * and MusicOutput devices are set. */ function musicInAndOutSet() { var audioInput = $('[layout-wizard-step="2"] .audio-input select').val(); var audioOutput = $('[layout-wizard-step="2"] .audio-output select').val(); return (audioInput && audioOutput); } function setVuCallbacks() { jamClient.FTUERegisterVUCallbacks( "JK.ftueAudioOutputVUCallback", "JK.ftueAudioInputVUCallback", "JK.ftueChatInputVUCallback" ); jamClient.SetVURefreshRate(200); } function setAsioSettingsVisibility() { logger.debug("jamClient.FTUEHasControlPanel()=" + jamClient.FTUEHasControlPanel()); if (jamClient.FTUEHasControlPanel()) { logger.debug("Showing ASIO button"); $('#btn-asio-control-panel').show(); } else { logger.debug("Hiding ASIO button"); $('#btn-asio-control-panel').hide(); } } function initialize() { // If not on windows, hide ASIO settings if (jamClient.GetOSAsString() != "Win32") { logger.debug("Not on Win32 - modifying UI for Mac/Linux"); win32 = false; $('[layout-wizard-step="2"] p[os="win32"]').hide(); $('[layout-wizard-step="2"] p[os="mac"]').show(); $('#btn-asio-control-panel').hide(); $('[layout-wizard-step="2"] .settings-controls select').removeAttr("disabled"); loadAudioDevices(); } setAsioSettingsVisibility(); events(); var dialogBindings = { 'beforeShow': beforeShow, 'afterShow': afterShow, 'afterHide': afterHide }; app.bindDialog('ftue', dialogBindings); app.registerWizardStepFunction("0", settingsInit); app.registerWizardStepFunction("2", settingsInit); app.registerWizardStepFunction("4", testLatency); app.registerWizardStepFunction("6", testComplete); loadAudioDrivers(); } // Expose publics this.initialize = initialize; // Expose degreesFromRange outside for testing this._degreesFromRange = degreesFromRange; return this; }; // Common VU updater taking a dbValue (-80 to 20) and a CSS selector for the VU. context.JK.ftueVUCallback = function (dbValue, selector) { // Convert DB into a value from 0.0 - 1.0 var floatValue = (dbValue + 80) / 100; context.JK.VuHelpers.updateVU($(selector), floatValue); }; context.JK.ftueAudioInputVUCallback = function (dbValue) { context.JK.ftueVUCallback(dbValue, '#ftue-2-audio-input-vu-left'); context.JK.ftueVUCallback(dbValue, '#ftue-2-audio-input-vu-right'); context.JK.ftueVUCallback(dbValue, '#ftue-audio-input-vu-left'); context.JK.ftueVUCallback(dbValue, '#ftue-audio-input-vu-right'); }; context.JK.ftueAudioOutputVUCallback = function (dbValue) { context.JK.ftueVUCallback(dbValue, '#ftue-audio-output-vu-left'); context.JK.ftueVUCallback(dbValue, '#ftue-audio-output-vu-right'); }; context.JK.ftueChatInputVUCallback = function (dbValue) { context.JK.ftueVUCallback(dbValue, '#ftue-2-voice-input-vu-left'); context.JK.ftueVUCallback(dbValue, '#ftue-2-voice-input-vu-right'); context.JK.ftueVUCallback(dbValue, '#ftue-voice-input-vu-left'); context.JK.ftueVUCallback(dbValue, '#ftue-voice-input-vu-right'); }; // Latency Callback context.JK.ftueLatencyCallback = function (latencyMS) { // We always show gauge screen if we hit this. // Clear out the 'timeout' variable. context.JK.FtueWizard.latencyTimeout = false; var now = new Date(); console.log("ftueLatencyCallback: " + now); context.JK.FtueWizard.latencyMS = latencyMS; // Unregister callback: context.jamClient.FTUERegisterLatencyCallback(''); // Go to 'congrats' screen -- although latency may be too high. context.JK.app.setWizardStep("6"); }; })(window, jQuery);