909 lines
29 KiB
JavaScript
909 lines
29 KiB
JavaScript
(function (context, $) {
|
|
|
|
"use strict";
|
|
|
|
|
|
context.JK = context.JK || {};
|
|
context.JK.GearWizard = function (app) {
|
|
|
|
var ASSIGNMENT = context.JK.ASSIGNMENT;
|
|
var VOICE_CHAT = context.JK.VOICE_CHAT;
|
|
|
|
var $dialog = null;
|
|
var $wizardSteps = null;
|
|
var $currentWizardStep = null;
|
|
var step = 0;
|
|
var $templateSteps = null;
|
|
var $templateButtons = null;
|
|
var $templateAudioPort = null;
|
|
var $ftueButtons = null;
|
|
var self = null;
|
|
var operatingSystem = null;
|
|
|
|
// populated by loadDevices
|
|
var deviceInformation = null;
|
|
var musicPorts = null;
|
|
|
|
|
|
var validLatencyScore = false;
|
|
var validIOScore = false;
|
|
|
|
// SELECT TRACKS STATE
|
|
|
|
var TOTAL_STEPS = 7;
|
|
var STEP_INTRO = 0;
|
|
var STEP_SELECT_DEVICE = 1;
|
|
var STEP_SELECT_TRACKS = 2;
|
|
var STEP_SELECT_CHAT = 3;
|
|
var STEP_DIRECT_MONITOR = 4;
|
|
var STEP_ROUTER_NETWORK = 5;
|
|
var STEP_SUCCESS = 6;
|
|
|
|
var PROFILE_DEV_SEP_TOKEN = '^';
|
|
|
|
var iCheckIgnore = false;
|
|
|
|
var audioDeviceBehavior = {
|
|
MacOSX_builtin: {
|
|
display: 'MacOSX Built-In',
|
|
videoURL: undefined
|
|
},
|
|
MacOSX_interface: {
|
|
display: 'MacOSX external interface',
|
|
videoURL: undefined
|
|
},
|
|
Win32_wdm: {
|
|
display: 'Windows WDM',
|
|
videoURL: undefined
|
|
},
|
|
Win32_asio: {
|
|
display: 'Windows ASIO',
|
|
videoURL: undefined
|
|
},
|
|
Win32_asio4all: {
|
|
display: 'Windows ASIO4ALL',
|
|
videoURL: undefined
|
|
},
|
|
Linux: {
|
|
display: 'Linux',
|
|
videoURL: undefined
|
|
}
|
|
}
|
|
|
|
function beforeShowIntro() {
|
|
var $watchVideo = $currentWizardStep.find('.watch-video');
|
|
var videoUrl = 'https://www.youtube.com/watch?v=VexH4834o9I';
|
|
if (operatingSystem == "Win32") {
|
|
$watchVideo.attr('href', 'https://www.youtube.com/watch?v=VexH4834o9I');
|
|
}
|
|
$watchVideo.attr('href', videoUrl);
|
|
}
|
|
|
|
function beforeSelectDevice() {
|
|
|
|
var $watchVideoInput = $currentWizardStep.find('.watch-video.audio-input');
|
|
var $watchVideoOutput = $currentWizardStep.find('.watch-video.audio-output');
|
|
var $audioInput = $currentWizardStep.find('.select-audio-input-device');
|
|
var $audioOutput = $currentWizardStep.find('.select-audio-output-device');
|
|
var $bufferIn = $currentWizardStep.find('.select-buffer-in');
|
|
var $bufferOut = $currentWizardStep.find('.select-buffer-out');
|
|
var $frameSize = $currentWizardStep.find('.select-frame-size');
|
|
var $inputChannels = $currentWizardStep.find('.input-ports');
|
|
var $outputChannels = $currentWizardStep.find('.output-ports');
|
|
var $scoreReport = $currentWizardStep.find('.results');
|
|
var $latencyScoreSection = $scoreReport.find('.latency-score-section');
|
|
var $latencyScore = $scoreReport.find('.latency-score');
|
|
var $ioScoreSection = $scoreReport.find('.io-score-section');
|
|
var $ioRateScore = $scoreReport.find('.io-rate-score');
|
|
var $ioVarScore = $scoreReport.find('.io-var-score');
|
|
var $ioCountdown = $scoreReport.find('.io-countdown');
|
|
var $ioCountdownSecs = $scoreReport.find('.io-countdown .secs');
|
|
var $nextButton = $ftueButtons.find('.btn-next');
|
|
var $asioControlPanelBtn = $currentWizardStep.find('.asio-settings-btn');
|
|
var $resyncBtn = $currentWizardStep.find('resync-btn')
|
|
|
|
// should return one of:
|
|
// * MacOSX_builtin
|
|
// * MACOSX_interface
|
|
// * Win32_wdm
|
|
// * Win32_asio
|
|
// * Win32_asio4all
|
|
// * Linux
|
|
function determineDeviceType(deviceId, displayName) {
|
|
if (operatingSystem == "MacOSX") {
|
|
if (displayName.toLowerCase().trim() == "built-in") {
|
|
return "MacOSX_builtin";
|
|
}
|
|
else {
|
|
return "MacOSX_interface";
|
|
}
|
|
}
|
|
else if (operatingSystem == "Win32") {
|
|
if (context.jamClient.FTUEIsMusicDeviceWDM(deviceId)) {
|
|
return "Win32_wdm";
|
|
}
|
|
else if (displayName.toLowerCase().indexOf("asio4all") > -1) {
|
|
return "Win32_asio4all"
|
|
}
|
|
else {
|
|
return "Win32_asio";
|
|
}
|
|
}
|
|
else {
|
|
return "Linux";
|
|
}
|
|
}
|
|
|
|
function loadDevices() {
|
|
|
|
var oldDevices = context.jamClient.FTUEGetDevices(false);
|
|
var devices = context.jamClient.FTUEGetAudioDevices();
|
|
console.log("oldDevices: " + JSON.stringify(oldDevices));
|
|
console.log("devices: " + JSON.stringify(devices));
|
|
|
|
var loadedDevices = {};
|
|
|
|
// augment these devices by determining their type
|
|
context._.each(devices.devices, function (device) {
|
|
|
|
if(device.name == "JamKazam Virtual Monitor") {
|
|
return;
|
|
}
|
|
|
|
var deviceInfo = {};
|
|
|
|
deviceInfo.id = device.guid;
|
|
deviceInfo.type = determineDeviceType(device.guid, device.display_name);
|
|
console.log("deviceInfo.type: " + deviceInfo.type)
|
|
deviceInfo.displayType = audioDeviceBehavior[deviceInfo.type].display;
|
|
deviceInfo.displayName = device.display_name;
|
|
|
|
loadedDevices[device.guid] = deviceInfo;
|
|
|
|
logger.debug("loaded device: ", deviceInfo);
|
|
})
|
|
|
|
deviceInformation = loadedDevices;
|
|
|
|
logger.debug(context.JK.dlen(deviceInformation) + " devices loaded.", deviceInformation);
|
|
}
|
|
|
|
// returns a deviceInfo hash for the device matching the deviceId, or undefined.
|
|
function findDevice(deviceId) {
|
|
return deviceInformation[deviceId];
|
|
}
|
|
|
|
function selectedAudioInput() {
|
|
return $audioInput.val();
|
|
}
|
|
|
|
function selectedAudioOutput() {
|
|
return $audioOutput.val();
|
|
}
|
|
|
|
function selectedFramesize() {
|
|
return parseFloat($frameSize.val());
|
|
}
|
|
|
|
function selectedBufferIn() {
|
|
return parseFloat($frameSize.val());
|
|
}
|
|
|
|
function selectedBufferOut() {
|
|
return parseFloat($frameSize.val());
|
|
}
|
|
|
|
function initializeNextButtonState() {
|
|
$nextButton.removeClass('button-orange button-grey');
|
|
|
|
if (validLatencyScore) $nextButton.addClass('button-orange');
|
|
else $nextButton.addClass('button-grey');
|
|
}
|
|
|
|
function initializeAudioInput() {
|
|
var optionsHtml = '';
|
|
optionsHtml = '<option selected="selected" value="">Choose...</option>';
|
|
context._.each(deviceInformation, function (deviceInfo, deviceId) {
|
|
|
|
console.log(arguments)
|
|
optionsHtml += '<option title="' + deviceInfo.displayName + '" value="' + deviceId + '">' + deviceInfo.displayName + '</option>';
|
|
});
|
|
$audioInput.html(optionsHtml);
|
|
context.JK.dropdown($audioInput);
|
|
|
|
initializeAudioInputChanged();
|
|
}
|
|
|
|
function initializeAudioOutput() {
|
|
var optionsHtml = '';
|
|
optionsHtml = '<option selected="selected" value="">Same as Input...</option>';
|
|
context._.each(deviceInformation, function (deviceInfo, deviceId) {
|
|
optionsHtml += '<option title="' + deviceInfo.displayName + '" value="' + deviceId + '">' + deviceInfo.displayName + '</option>';
|
|
});
|
|
$audioOutput.html(optionsHtml);
|
|
context.JK.dropdown($audioOutput);
|
|
|
|
initializeAudioOutputChanged();
|
|
}
|
|
|
|
function initializeFramesize() {
|
|
context.JK.dropdown($frameSize);
|
|
}
|
|
|
|
function initializeBuffers() {
|
|
context.JK.dropdown($bufferIn);
|
|
context.JK.dropdown($bufferOut);
|
|
}
|
|
|
|
|
|
// reloads the backend's channel state for the currently selected audio devices,
|
|
// and update's the UI accordingly
|
|
function initializeChannels() {
|
|
musicPorts = jamClient.FTUEGetChannels();
|
|
console.log("musicPorts: %o", JSON.stringify(musicPorts));
|
|
|
|
initializeInputPorts(musicPorts);
|
|
initializeOutputPorts(musicPorts);
|
|
}
|
|
|
|
// during this phase of the FTUE, we have to assign selected input channels
|
|
// to tracks. The user, however, does not have a way to indicate which channel
|
|
// goes to which track (that's not until the next step of the wizard).
|
|
// so, we just auto-generate a valid assignment
|
|
function newInputAssignment() {
|
|
var assigned = 0;
|
|
context._.each(musicPorts.inputs, function(inputChannel) {
|
|
if(isChannelAssigned(inputChannel)) {
|
|
assigned += 1;
|
|
}
|
|
});
|
|
|
|
var newAssignment = Math.floor(assigned / 2) + 1;
|
|
return newAssignment;
|
|
}
|
|
|
|
function inputChannelChanged() {
|
|
if(iCheckIgnore) return;
|
|
|
|
var $checkbox = $(this);
|
|
var channelId = $checkbox.attr('data-id');
|
|
var isChecked = $checkbox.is(':checked');
|
|
|
|
if(isChecked) {
|
|
var newAssignment = newInputAssignment();
|
|
logger.debug("assigning input channel %o to track: %o", channelId, newAssignment);
|
|
context.jamClient.TrackSetAssignment(channelId, true, newAssignment);
|
|
}
|
|
else {
|
|
logger.debug("unassigning input channel %o", channelId);
|
|
context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.UNASSIGNED);
|
|
// unassigning creates a hole in our auto-assigned tracks. reassign them all to keep it consistent
|
|
var $assignedInputs = $inputChannels.find('input[type="checkbox"]:checked');
|
|
var assigned = 0;
|
|
context._.each($assignedInputs, function(assignedInput) {
|
|
var $assignedInput = $(assignedInput);
|
|
var assignedChannelId = $assignedInput.attr('data-id');
|
|
var newAssignment = Math.floor(assigned / 2) + 1;
|
|
logger.debug("re-assigning input channel %o to track: %o", assignedChannelId, newAssignment);
|
|
context.jamClient.TrackSetAssignment(assignedChannelId, true, newAssignment);
|
|
assigned += 1;
|
|
});
|
|
}
|
|
|
|
initializeChannels();
|
|
}
|
|
|
|
// should be called in a ifChanged callback if you want to cancel.
|
|
// you have to use this instead of 'return false' like a typical input 'change' event.
|
|
function cancelICheckChange($checkbox) {
|
|
iCheckIgnore = true;
|
|
var checked = $checkbox.is(':checked');
|
|
setTimeout(function() {
|
|
if(checked) $checkbox.iCheck('uncheck').removeAttr('checked');
|
|
else $checkbox.iCheck('check').attr('checked', 'checked');
|
|
iCheckIgnore = false;
|
|
}, 1);
|
|
}
|
|
|
|
function outputChannelChanged() {
|
|
if(iCheckIgnore) return;
|
|
var $checkbox = $(this);
|
|
var channelId = $checkbox.attr('data-id');
|
|
var isChecked = $checkbox.is(':checked');
|
|
|
|
// don't allow more than 2 output channels selected at once
|
|
if($outputChannels.find('input[type="checkbox"]:checked').length > 2) {
|
|
context.JK.Banner.showAlert('You can only have a maximum of 2 output ports selected.');
|
|
// can't allow uncheck of last output
|
|
cancelICheckChange($checkbox);
|
|
return;
|
|
}
|
|
|
|
if(isChecked) {
|
|
logger.debug("assigning output channel %o", channelId);
|
|
context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.OUTPUT);
|
|
}
|
|
else {
|
|
logger.debug("unassigning output channel %o", channelId);
|
|
context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.UNASSIGNED);
|
|
}
|
|
|
|
initializeChannels();
|
|
}
|
|
|
|
// checks if it's an assigned OUTPUT or ASSIGNED CHAT
|
|
function isChannelAssigned(channel) {
|
|
return channel.assignment == ASSIGNMENT.CHAT || channel.assignment == ASSIGNMENT.OUTPUT || channel.assignment > 0;
|
|
}
|
|
|
|
function initializeInputPorts(musicPorts) {
|
|
$inputChannels.empty();
|
|
var inputPorts = musicPorts.inputs;
|
|
context._.each(inputPorts, function(inputChannel) {
|
|
var $inputChannel = $(context._.template($templateAudioPort.html(), inputChannel, { variable: 'data' }));
|
|
var $checkbox = $inputChannel.find('input');
|
|
if(isChannelAssigned(inputChannel)) {
|
|
$checkbox.attr('checked', 'checked');
|
|
}
|
|
context.JK.checkbox($checkbox);
|
|
$checkbox.on('ifChanged', inputChannelChanged);
|
|
$inputChannels.append($inputChannel);
|
|
});
|
|
}
|
|
|
|
function initializeOutputPorts(musicPorts) {
|
|
$outputChannels.empty();
|
|
var outputChannels = musicPorts.outputs;
|
|
context._.each(outputChannels, function(outputChannel) {
|
|
var $outputPort = $(context._.template($templateAudioPort.html(), outputChannel, { variable: 'data' }));
|
|
var $checkbox = $outputPort.find('input');
|
|
if(isChannelAssigned(outputChannel)) {
|
|
$checkbox.attr('checked', 'checked');
|
|
}
|
|
context.JK.checkbox($checkbox);
|
|
$checkbox.on('ifChanged', outputChannelChanged);
|
|
$outputChannels.append($outputPort);
|
|
});
|
|
}
|
|
|
|
function initializeFormElements() {
|
|
if (!deviceInformation) throw "devices are not initialized";
|
|
|
|
initializeAudioInput();
|
|
initializeAudioOutput();
|
|
initializeFramesize();
|
|
initializeBuffers();
|
|
}
|
|
|
|
function resetFrameBuffers() {
|
|
$frameSize.val('2.5');
|
|
$bufferIn.val('0');
|
|
$bufferOut.val('0');
|
|
}
|
|
|
|
function clearInputPorts() {
|
|
$inputChannels.empty();
|
|
}
|
|
|
|
function clearOutputPorts() {
|
|
$outputChannels.empty();
|
|
}
|
|
|
|
function resetScoreReport() {
|
|
$ioRateScore.empty();
|
|
$ioVarScore.empty();
|
|
$latencyScore.empty();
|
|
}
|
|
|
|
function renderLatencyScore(latencyValue, latencyClass) {
|
|
if(latencyValue) {
|
|
$latencyScore.text(latencyValue + ' ms');
|
|
}
|
|
else {
|
|
$latencyScore.text('');
|
|
}
|
|
$latencyScoreSection.removeClass('good acceptable bad unknown starting').addClass(latencyClass);
|
|
}
|
|
|
|
// std deviation is the worst value between in/out
|
|
// media is the worst value between in/out
|
|
// io is the value returned by the backend, which has more info
|
|
// ioClass is the pre-computed rollup class describing the result in simple terms of 'good', 'acceptable', bad'
|
|
function renderIOScore(std, median, ioData, ioClass) {
|
|
$ioRateScore.text(median ? median : '');
|
|
$ioVarScore.text(std ? std : '');
|
|
$ioScoreSection.removeClass('good acceptable bad unknown starting skip').addClass(ioClass);
|
|
// TODO: show help bubble of all data in IO data
|
|
}
|
|
|
|
function updateScoreReport(latencyResult) {
|
|
var latencyClass = "neutral";
|
|
var latencyValue = 'N/A';
|
|
var validLatency = false;
|
|
if (latencyResult && latencyResult.latencyknown) {
|
|
var latencyValue = latencyResult.latency;
|
|
latencyValue = Math.round(latencyValue * 100) / 100;
|
|
if (latencyValue <= 10) {
|
|
latencyClass = "good";
|
|
validLatency = true;
|
|
} else if (latencyValue <= 20) {
|
|
latencyClass = "acceptable";
|
|
validLatency = true;
|
|
} else {
|
|
latencyClass = "bad";
|
|
}
|
|
}
|
|
else {
|
|
latencyClass = 'unknown';
|
|
}
|
|
|
|
validLatencyScore = validLatency;
|
|
|
|
renderLatencyScore(latencyValue, latencyClass);
|
|
}
|
|
|
|
function audioInputDeviceUnselected() {
|
|
validLatencyScore = false;
|
|
initializeNextButtonState();
|
|
resetFrameBuffers();
|
|
clearInputPorts();
|
|
}
|
|
|
|
function renderScoringStarted() {
|
|
validLatencyScore = false;
|
|
initializeNextButtonState();
|
|
resetScoreReport();
|
|
freezeAudioInteraction();
|
|
renderLatencyScore(null, 'starting');
|
|
}
|
|
|
|
function renderScoringStopped() {
|
|
initializeNextButtonState();
|
|
unfreezeAudioInteraction();
|
|
}
|
|
|
|
|
|
function freezeAudioInteraction() {
|
|
$audioInput.attr("disabled", "disabled").easyDropDown('disable');
|
|
$audioOutput.attr("disabled", "disabled").easyDropDown('disable');
|
|
$frameSize.attr("disabled", "disabled").easyDropDown('disable');
|
|
$bufferIn.attr("disabled", "disabled").easyDropDown('disable');
|
|
$bufferOut.attr("disabled", "disabled").easyDropDown('disable');
|
|
$asioControlPanelBtn.on("click", false);
|
|
$resyncBtn.on('click', false);
|
|
iCheckIgnore = true;
|
|
$inputChannels.find('input[type="checkbox"]').iCheck('disable');
|
|
$outputChannels.find('input[type="checkbox"]').iCheck('disable');
|
|
}
|
|
|
|
function unfreezeAudioInteraction() {
|
|
$audioInput.removeAttr("disabled").easyDropDown('enable');
|
|
$audioOutput.removeAttr("disabled").easyDropDown('enable');
|
|
$frameSize.removeAttr("disabled").easyDropDown('enable');
|
|
$bufferIn.removeAttr("disabled").easyDropDown('enable');
|
|
$bufferOut.removeAttr("disabled").easyDropDown('enable');
|
|
$asioControlPanelBtn.off("click", false);
|
|
$resyncBtn.off('click', false);
|
|
$inputChannels.find('input[type="checkbox"]').iCheck('enable');
|
|
$outputChannels.find('input[type="checkbox"]').iCheck('enable');
|
|
iCheckIgnore = false;
|
|
}
|
|
|
|
// 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 initializeWatchVideo() {
|
|
$watchVideoInput.unbind('click').click(function () {
|
|
|
|
var audioDevice = findDevice(selectedAudioInput());
|
|
if (!audioDevice) {
|
|
context.JK.Banner.showAlert('You must first choose an Audio Input Device so that we can determine which video to show you.');
|
|
}
|
|
else {
|
|
var videoURL = audioDeviceBehavior[audioDevice.type].videoURL;
|
|
|
|
if (videoURL) {
|
|
$(this).attr('href', videoURL);
|
|
return true;
|
|
}
|
|
else {
|
|
context.JK.Banner.showAlert('No help video for this type of device (' + audioDevice.displayType + ')');
|
|
}
|
|
}
|
|
|
|
return false;
|
|
});
|
|
|
|
$watchVideoOutput.unbind('click').click(function () {
|
|
|
|
var audioDevice = findDevice(selectedAudioOutput());
|
|
if (!audioDevice) {
|
|
throw "this button should be hidden";
|
|
}
|
|
else {
|
|
var videoURL = audioDeviceBehavior[audioDevice.type].videoURL;
|
|
|
|
if (videoURL) {
|
|
$(this).attr('href', videoURL);
|
|
return true;
|
|
}
|
|
else {
|
|
context.JK.Banner.showAlert('No help video for this type of device (' + audioDevice.displayType + ')');
|
|
}
|
|
}
|
|
|
|
return false;
|
|
});
|
|
}
|
|
|
|
function renderIOScoringStarted(secondsLeft) {
|
|
$ioCountdownSecs.text(secondsLeft);
|
|
$ioCountdown.show();
|
|
}
|
|
|
|
function renderIOScoringStopped() {
|
|
$ioCountdown.hide();
|
|
}
|
|
|
|
function renderIOCountdown(secondsLeft) {
|
|
$ioCountdownSecs.text(secondsLeft);
|
|
}
|
|
|
|
function attemptScore() {
|
|
var audioInputDeviceId = selectedAudioInput();
|
|
var audioOutputDeviceId = selectedAudioOutput();
|
|
if (!audioInputDeviceId) {
|
|
audioInputDeviceUnselected();
|
|
return false;
|
|
}
|
|
|
|
var audioInputDevice = findDevice(audioInputDeviceId);
|
|
if (!audioInputDevice) {
|
|
context.JK.alertSupportedNeeded('Unable to find information for input device: ' + audioInputDeviceId);
|
|
return false;
|
|
}
|
|
|
|
if(!audioOutputDeviceId) {
|
|
audioOutputDeviceId = audioInputDeviceId;
|
|
}
|
|
var audioOutputDevice = findDevice(audioOutputDeviceId);
|
|
if (!audioInputDevice) {
|
|
context.JK.alertSupportedNeeded('Unable to find information for output device: ' + audioOutputDeviceId);
|
|
return false;
|
|
}
|
|
|
|
jamClient.FTUESetInputMusicDevice(audioInputDeviceId);
|
|
jamClient.FTUESetOutputMusicDevice(audioOutputDeviceId);
|
|
|
|
initializeChannels();
|
|
|
|
jamClient.FTUESetInputLatency(selectedBufferIn());
|
|
jamClient.FTUESetOutputLatency(selectedBufferOut());
|
|
jamClient.FTUESetFrameSize(selectedFramesize());
|
|
|
|
renderScoringStarted();
|
|
logger.debug("Calling FTUESave(false)");
|
|
jamClient.FTUESave(false);
|
|
|
|
var latency = jamClient.FTUEGetExpectedLatency();
|
|
console.log("FTUEGetExpectedLatency: %o", latency);
|
|
|
|
updateScoreReport(latency);
|
|
|
|
// if there was a valid latency score, go on to the next step
|
|
if(validLatencyScore) {
|
|
renderIOScore(null, null, null, 'starting');
|
|
var testTimeSeconds = 10; // allow 10 seconds for IO to establish itself
|
|
context.jamClient.FTUEStartIoPerfTest();
|
|
renderIOScoringStarted(testTimeSeconds);
|
|
renderIOCountdown(testTimeSeconds);
|
|
var interval = setInterval(function() {
|
|
testTimeSeconds -= 1;
|
|
renderIOCountdown(testTimeSeconds);
|
|
if(testTimeSeconds == 0) {
|
|
clearInterval(interval);
|
|
renderIOScoringStopped();
|
|
var io = context.jamClient.FTUEGetIoPerfData();
|
|
|
|
console.log("io: ", io);
|
|
|
|
// take the higher variance, which is apparently actually std dev
|
|
var std = io.in_var > io.out_var ? io.in_var : io.out_var;
|
|
std = Math.round(std * 100) / 100;
|
|
// take the furthest-off-from-target io rate
|
|
var median = Math.abs(io.in_median - io.in_target ) > Math.abs(io.out_median - io.out_target ) ? [io.in_median, io.in_target] : [io.out_median, io.out_target];
|
|
var medianTarget = median[1];
|
|
median = Math.round(median[0]);
|
|
|
|
var stdIOClass = 'bad';
|
|
if(std <= 0.50) {
|
|
stdIOClass = 'good';
|
|
}
|
|
else if(std <= 1.00) {
|
|
stdIOClass = 'acceptable';
|
|
}
|
|
|
|
var medianIOClass = 'bad';
|
|
if(Math.abs(median - medianTarget) <= 1) {
|
|
medianIOClass = 'good';
|
|
}
|
|
else if(Math.abs(median - medianTarget) <= 2) {
|
|
medianIOClass = 'acceptable';
|
|
}
|
|
|
|
// now base the overall IO score based on both values.
|
|
renderIOScore(std, median, io, ioClass);
|
|
|
|
// lie for now until IO questions finalize
|
|
validIOScore = true;
|
|
|
|
renderScoringStopped();
|
|
}
|
|
}, 1000);
|
|
}
|
|
else {
|
|
renderIOScore(null, null, null, 'skip');
|
|
renderScoringStopped();
|
|
}
|
|
|
|
}
|
|
|
|
function initializeAudioInputChanged() {
|
|
$audioInput.unbind('change').change(attemptScore);
|
|
}
|
|
|
|
function initializeAudioOutputChanged() {
|
|
|
|
}
|
|
|
|
function initializeStep() {
|
|
loadDevices();
|
|
initializeFormElements();
|
|
initializeNextButtonState();
|
|
initializeWatchVideo();
|
|
}
|
|
|
|
initializeStep();
|
|
}
|
|
|
|
function beforeSelectTracks() {
|
|
|
|
}
|
|
|
|
function beforeSelectChat() {
|
|
|
|
}
|
|
|
|
function beforeDirectMonitor() {
|
|
|
|
}
|
|
|
|
function beforeTestNetwork() {
|
|
|
|
}
|
|
|
|
function beforeSuccess() {
|
|
|
|
}
|
|
|
|
var STEPS = {
|
|
0: {
|
|
beforeShow: beforeShowIntro
|
|
},
|
|
1: {
|
|
beforeShow: beforeSelectDevice
|
|
},
|
|
2: {
|
|
beforeShow: beforeSelectTracks
|
|
},
|
|
3: {
|
|
beforeShow: beforeSelectChat
|
|
},
|
|
4: {
|
|
beforeShow: beforeDirectMonitor
|
|
},
|
|
5: {
|
|
beforeShow: beforeTestNetwork
|
|
},
|
|
6: {
|
|
beforeShow: beforeSuccess
|
|
}
|
|
}
|
|
|
|
function beforeShowStep($step) {
|
|
var stepInfo = STEPS[step];
|
|
|
|
if (!stepInfo) {
|
|
throw "unknown step: " + step;
|
|
}
|
|
|
|
stepInfo.beforeShow.call(self);
|
|
}
|
|
|
|
function moveToStep() {
|
|
var $nextWizardStep = $wizardSteps.filter($('[layout-wizard-step=' + step + ']'));
|
|
|
|
$wizardSteps.hide();
|
|
|
|
$currentWizardStep = $nextWizardStep;
|
|
|
|
var $ftueSteps = $(context._.template($templateSteps.html(), {}, { variable: 'data' }));
|
|
var $activeStep = $ftueSteps.find('.ftue-stepnumber[data-step-number="' + step + '"]');
|
|
$activeStep.addClass('.active');
|
|
$activeStep.next().show(); // show the .ftue-step-title
|
|
$currentWizardStep.find('.ftuesteps').replaceWith($ftueSteps);
|
|
beforeShowStep($currentWizardStep);
|
|
$currentWizardStep.show();
|
|
|
|
// update buttons
|
|
var $ftueButtonsContent = $(context._.template($templateButtons.html(), {}, {variable: 'data'}));
|
|
|
|
|
|
var $btnBack = $ftueButtonsContent.find('.btn-back');
|
|
var $btnNext = $ftueButtonsContent.find('.btn-next');
|
|
var $btnClose = $ftueButtonsContent.find('.btn-close');
|
|
var $btnCancel = $ftueButtonsContent.find('.btn-cancel');
|
|
|
|
// hide back button if 1st step or last step
|
|
if (step == 0 && step == TOTAL_STEPS - 1) {
|
|
$btnBack.hide();
|
|
}
|
|
// hide next button if not on last step
|
|
if (step == TOTAL_STEPS - 1) {
|
|
$btnNext.hide();
|
|
}
|
|
// hide close if on last step
|
|
if (step != TOTAL_STEPS - 1) {
|
|
$btnClose.hide();
|
|
}
|
|
// hide cancel if not on last step
|
|
if (step == TOTAL_STEPS - 1) {
|
|
$btnCancel.hide();
|
|
}
|
|
|
|
$btnNext.on('click', next);
|
|
$btnBack.on('click', back);
|
|
$btnClose.on('click', closeDialog);
|
|
$btnCancel.on('click', closeDialog);
|
|
|
|
$ftueButtons.empty();
|
|
$ftueButtons.append($ftueButtonsContent);
|
|
}
|
|
|
|
function reset() {
|
|
$currentWizardStep = null;
|
|
}
|
|
|
|
// checks if we already have a profile called 'FTUE...'; if not, create one. if so, re-use it.
|
|
function findOrCreateFTUEProfile() {
|
|
var profileName = context.jamClient.FTUEGetMusicProfileName();
|
|
|
|
logger.debug("current profile name: " + profileName);
|
|
|
|
if(profileName && profileName.indexOf('FTUE') == 0) {
|
|
|
|
}
|
|
else {
|
|
var newProfileName = 'FTUEAttempt-' + new Date().getTime().toString();
|
|
logger.debug("setting FTUE-prefixed profile name to: " + newProfileName);
|
|
context.jamClient.FTUESetMusicProfileName(newProfileName);
|
|
}
|
|
|
|
var profileName = context.jamClient.FTUEGetMusicProfileName();
|
|
|
|
logger.debug("name on exit: " + profileName);
|
|
|
|
}
|
|
|
|
function beforeShow(args) {
|
|
context.jamClient.FTUECancel();
|
|
findOrCreateFTUEProfile();
|
|
|
|
step = args.d1;
|
|
if (!step) step = 0;
|
|
step = parseInt(step);
|
|
moveToStep();
|
|
}
|
|
|
|
function afterShow() {
|
|
|
|
}
|
|
|
|
function afterHide() {
|
|
context.jamClient.FTUECancel();
|
|
}
|
|
|
|
function back() {
|
|
if ($(this).is('.button-grey')) return;
|
|
step = step - 1;
|
|
moveToStep();
|
|
return false;
|
|
}
|
|
|
|
function next() {
|
|
if ($(this).is('.button-grey')) return;
|
|
|
|
step = step + 1;
|
|
|
|
moveToStep();
|
|
return false;
|
|
}
|
|
|
|
function closeDialog() {
|
|
app.layout.closeDialog('gear-wizard');
|
|
return false;
|
|
}
|
|
|
|
function events() {
|
|
}
|
|
|
|
function route() {
|
|
|
|
}
|
|
|
|
function initialize() {
|
|
|
|
var dialogBindings = { beforeShow: beforeShow, afterShow: afterShow, afterHide: afterHide };
|
|
|
|
app.bindDialog('gear-wizard', dialogBindings);
|
|
|
|
$dialog = $('#gear-wizard-dialog');
|
|
$wizardSteps = $dialog.find('.wizard-step');
|
|
$templateSteps = $('#template-ftuesteps');
|
|
$templateButtons = $('#template-ftue-buttons');
|
|
$templateAudioPort = $('#template-audio-port');
|
|
$ftueButtons = $dialog.find('.ftue-buttons');
|
|
|
|
operatingSystem = context.jamClient.GetOSAsString();
|
|
|
|
events();
|
|
}
|
|
|
|
this.initialize = initialize;
|
|
|
|
self = this;
|
|
return this;
|
|
};
|
|
|
|
})(window, jQuery); |