jam-cloud/web/app/assets/javascripts/ftue.js

984 lines
35 KiB
JavaScript

/**
* 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;
// 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('', '', '');
}
function beforeShow(data) {
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(faderId, newValue, dragging) {
var setFunction = faderMap[faderId];
// TODO - using hardcoded range of -80 to 20 for output levels.
var mixerLevel = newValue - 80; // Convert our [0-100] to [-80 - +20] range
setFunction(mixerLevel);
}
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 += '<option title="' + devices[deviceKey] + '" value="' + deviceKey + '">' +
devices[deviceKey] + '</option>';
};
for (var i = 0; i < funcs.length; i++) {
optionsHtml = '<option selected="selected" value="">Choose...</option>';
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");
$('[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 = jamClient.FTUEGetDevices();
var driverOptionFunc = function (driverKey, index, list) {
optionsHtml += '<option title="' + drivers[driverKey] + '"value="' + driverKey + '">' +
drivers[driverKey] + '</option>';
};
var optionsHtml = '<option selected="selected" value="">Choose...</option>';
var selectors = [
'[layout-wizard-step="0"] .settings-2-device select',
'[layout-wizard-step="0"] .settings-2-voice select',
'[layout-wizard-step="2"] .settings-driver select'
];
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);
});
}
/** 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();
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');
app.layout.closeDialog('ftue');
if (app.afterFtue) {
// If there's a function to invoke, invoke it.
app.afterFtue();
app.afterFtue = 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) {
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();
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.
newFtueAsioFrameSizeToBackend($('#ftue-2-asio-framesize'));
newFtueAsioInputLatencyToBackend($('#ftue-2-asio-input-latency'));
newFtueAsioOutputLatencyToBackend($('#ftue-2-asio-output-latency'));
//setLevels(0);
renderVolumes();
jamClient.FTUESave(false);
setVuCallbacks();
var latency = jamClient.FTUEGetExpectedLatency();
newFtueUpdateLatencyView(latency);
});
}
function newFtueSave(persist) {
newFtueUpdateLatencyView('loading');
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;
}
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;
}
newFtueSave(false);
});
}
function newFtueSetAsioInputLatency(evt) {
releaseDropdown(function() {
renderStartNewFtueLatencyTesting();
if(!newFtueAsioInputLatencyToBackend($(evt.currentTarget))) {
renderStopNewFtueLatencyTesting();
return;
}
newFtueSave(false);
});
}
function newFtueSetAsioOutputLatency(evt) {
releaseDropdown(function() {
renderStartNewFtueLatencyTesting();
if(!newFtueAsioOutputLatencyToBackend($(evt.currentTarget))) {
renderStopNewFtueLatencyTesting();
return;
}
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'));
}
}
// 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('10');
// TODO FIXME - the old FTUE set the buffers to 1 for WDM/Kernel streaming
// The new FTUE spec says to use 0, as I've done here...
$bin.val('0');
$bout.val('0');
}
} else { // Assuming Mac. TODO: Linux check here
// frame=2.5, buffers=0
$asioBtn.hide();
$frame.val('2.5');
$bin.val('0');
$bout.val('0');
}
}
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();
context.console.debug("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);