diff --git a/web/app/assets/javascripts/application.js b/web/app/assets/javascripts/application.js
index f9d230ae6..cd7ff04cb 100644
--- a/web/app/assets/javascripts/application.js
+++ b/web/app/assets/javascripts/application.js
@@ -39,3 +39,4 @@
//= require utils
//= require custom_controls
//= require_directory .
+//= require_directory ./gear
diff --git a/web/app/assets/javascripts/fakeJamClient.js b/web/app/assets/javascripts/fakeJamClient.js
index af10cb53b..1175d9b28 100644
--- a/web/app/assets/javascripts/fakeJamClient.js
+++ b/web/app/assets/javascripts/fakeJamClient.js
@@ -38,7 +38,15 @@
function RestartApplication() {
}
+ function FTUECancel() {
+ }
+ function FTUEGetMusicProfileName() {
+ return "default"
+ }
+ function FTUESetMusicProfileName() {
+
+ }
function FTUEGetInputLatency() {
dbg("FTUEGetInputLatency");
return 2;
@@ -85,6 +93,9 @@
function FTUEGetStatus() { return ftueStatus; }
function FTUESetStatus(b) { ftueStatus = b; }
function FTUESetMusicDevice(id) { dbg("FTUESetMusicDevice"); }
+ function FTUEGetAudioDevices() {
+ return {"devices":[{"display_name":"Built-in","guid":"Built-in","input_count":1,"name":"Built-in","output_count":1,"port_audio_name":"Built-in"},{"display_name":"JamKazam Virtual Monitor","guid":"JamKazam Virtual Monitor","input_count":0,"name":"JamKazam Virtual Monitor","output_count":1,"port_audio_name":"JamKazam Virtual Monitor"}]}
+ }
function FTUEGetDevices() {
dbg('FTUEGetMusicDevices');
return {
@@ -113,6 +124,11 @@
"Multichannel (FW AP Multi) - Channel 1/Multichannel (FW AP Multi) - Channel 2"
};
}
+ function FTUESetInputMusicDevice() { }
+ function FTUESetOutputMusicDevice() { }
+ function FTUEGetInputMusicDevice() { return null; }
+ function FTUEGetOutputMusicDevice() { return null; }
+
function FTUESetMusicInput() { dbg('FTUESetMusicInput'); }
function FTUESetChatInput() { dbg('FTUESetChatInput'); }
function FTUESetMusicOutput() { dbg('FTUESetMusicOutput'); }
@@ -656,10 +672,17 @@
this.connected = true;
// FTUE (round 3)
+ this.FTUESetInputMusicDevice = FTUESetInputMusicDevice;
+ this.FTUESetOutputMusicDevice = FTUESetOutputMusicDevice;
+ this.FTUEGetInputMusicDevice = FTUEGetInputMusicDevice;
+ this.FTUEGetOutputMusicDevice = FTUEGetOutputMusicDevice;
this.FTUEGetChatInputVolume = FTUEGetChatInputVolume;
this.FTUEGetChatInputs = FTUEGetChatInputs;
this.FTUEGetDevices = FTUEGetDevices;
this.FTUEGetFrameSize = FTUEGetFrameSize;
+ this.FTUECancel = FTUECancel;
+ this.FTUEGetMusicProfileName = FTUEGetMusicProfileName;
+ this.FTUESetMusicProfileName = FTUESetMusicProfileName;
this.FTUEGetInputLatency = FTUEGetInputLatency;
this.FTUEGetInputVolume = FTUEGetInputVolume;
this.FTUEGetMusicInputs = FTUEGetMusicInputs;
diff --git a/web/app/assets/javascripts/gear/gear_wizard.js b/web/app/assets/javascripts/gear/gear_wizard.js
new file mode 100644
index 000000000..bdffef0dc
--- /dev/null
+++ b/web/app/assets/javascripts/gear/gear_wizard.js
@@ -0,0 +1,237 @@
+(function (context, $) {
+
+ "use strict";
+
+
+ context.JK = context.JK || {};
+ context.JK.GearWizard = function (app) {
+
+ 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 $btnBack = null;
+ var $btnNext = null;
+ var $btnClose = null;
+ var $btnCancel = null;
+
+ var self = this;
+
+ 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 stepUnderstandGear = new context.JK.StepUnderstandGear(app, this);
+ var stepSelectGear = new context.JK.StepSelectGear(app, this);
+ var stepConfigureTracks = new context.JK.StepConfigureTracks(app, this);
+ var stepConfigureVoiceChat = new context.JK.StepConfigureVoiceChat(app, this);
+ var stepDirectMonitoring = new context.JK.StepDirectMonitoring(app, this);
+ var stepNetworkTest = new context.JK.StepNetworkTest(app, this);
+ var stepSuccess = new context.JK.StepSuccess(app, this);
+
+ var STEPS = {
+ 0: stepUnderstandGear,
+ 1: stepSelectGear,
+ 2: stepConfigureTracks,
+ 3: stepConfigureVoiceChat,
+ 4: stepDirectMonitoring,
+ 5: stepNetworkTest,
+ 6: stepSuccess
+ }
+
+ function beforeShowStep($step) {
+ var stepInfo = STEPS[step];
+
+ if (!stepInfo) {
+ throw "unknown step: " + step;
+ }
+
+ stepInfo.beforeShow.call(stepInfo);
+ }
+
+ 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);
+
+ // update buttons
+ var $ftueButtonsContent = $(context._.template($templateButtons.html(), {}, {variable: 'data'}));
+
+
+ $btnBack = $ftueButtonsContent.find('.btn-back');
+ $btnNext = $ftueButtonsContent.find('.btn-next');
+ $btnClose = $ftueButtonsContent.find('.btn-close');
+ $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);
+
+ beforeShowStep($currentWizardStep);
+ $currentWizardStep.show();
+
+ }
+
+ 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();
+ context.jamClient.FTUESetStatus(false);
+ findOrCreateFTUEProfile();
+
+ step = args.d1;
+ if (!step) step = 0;
+ step = parseInt(step);
+ moveToStep();
+ }
+
+ function afterShow() {
+
+ }
+
+ function afterHide() {
+ context.jamClient.FTUESetStatus(true);
+ context.jamClient.FTUECancel();
+ }
+
+ function back() {
+ if ($(this).is('.button-grey')) return false;
+ step = step - 1;
+ moveToStep();
+ return false;
+ }
+
+ function next() {
+ if ($(this).is('.button-grey')) return false;
+
+ step = step + 1;
+
+ moveToStep();
+ return false;
+ }
+
+ function closeDialog() {
+ app.layout.closeDialog('gear-wizard');
+ return false;
+ }
+
+ function events() {
+
+ }
+
+ function route() {
+
+ }
+
+ function setNextState(enabled) {
+
+ if(!$btnNext) return;
+
+ $btnNext.removeClass('button-orange button-grey');
+
+ if (enabled) {
+ $btnNext.addClass('button-orange');
+ }
+ else {
+ $btnNext.addClass('button-grey');
+ }
+ }
+
+ function initialize() {
+
+ // on initial page load, we are not in the FTUE. so cancel the FTUE and call FTUESetStatus(true) if needed
+ if(context.jamClient.FTUEGetStatus() == false) {
+ context.jamClient.FTUESetStatus(true);
+ }
+ context.jamClient.FTUECancel();
+
+ 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');
+ $ftueButtons = $dialog.find('.ftue-buttons');
+
+ stepUnderstandGear.initialize($wizardSteps.filter($('[layout-wizard-step=0]')));
+ stepSelectGear.initialize($wizardSteps.filter($('[layout-wizard-step=1]')));
+ stepConfigureTracks.initialize($wizardSteps.filter($('[layout-wizard-step=2]')));
+ stepConfigureVoiceChat.initialize($wizardSteps.filter($('[layout-wizard-step=3]')));
+ stepDirectMonitoring.initialize($wizardSteps.filter($('[layout-wizard-step=4]')));
+ stepNetworkTest.initialize($wizardSteps.filter($('[layout-wizard-step=5]')));
+ stepSuccess.initialize($wizardSteps.filter($('[layout-wizard-step=6]')));
+
+ events();
+ }
+
+ this.setNextState = setNextState;
+ this.initialize = initialize;
+
+ self = this;
+ return this;
+ };
+
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/gear/step_configure_tracks.js b/web/app/assets/javascripts/gear/step_configure_tracks.js
new file mode 100644
index 000000000..ab0be8b4b
--- /dev/null
+++ b/web/app/assets/javascripts/gear/step_configure_tracks.js
@@ -0,0 +1,18 @@
+(function (context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ context.JK.StepConfigureTracks = function (app) {
+
+ var $step = null;
+
+ function initialize(_$step) {
+ $step = _$step;
+ }
+
+ this.initialize = initialize;
+
+ return this;
+ }
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/gear/step_configure_voice_chat.js b/web/app/assets/javascripts/gear/step_configure_voice_chat.js
new file mode 100644
index 000000000..46d9c9d6a
--- /dev/null
+++ b/web/app/assets/javascripts/gear/step_configure_voice_chat.js
@@ -0,0 +1,18 @@
+(function (context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ context.JK.StepConfigureVoiceChat = function (app) {
+
+ var $step = null;
+
+ function initialize(_$step) {
+ $step = _$step;
+ }
+
+ this.initialize = initialize;
+
+ return this;
+ }
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/gear/step_direct_monitoring.js b/web/app/assets/javascripts/gear/step_direct_monitoring.js
new file mode 100644
index 000000000..a8e6aa1ac
--- /dev/null
+++ b/web/app/assets/javascripts/gear/step_direct_monitoring.js
@@ -0,0 +1,18 @@
+(function (context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ context.JK.StepDirectMonitoring = function (app) {
+
+ var $step = null;
+
+ function initialize(_$step) {
+ $step = _$step;
+ }
+
+ this.initialize = initialize;
+
+ return this;
+ }
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/gear/step_network_test.js b/web/app/assets/javascripts/gear/step_network_test.js
new file mode 100644
index 000000000..d04e73343
--- /dev/null
+++ b/web/app/assets/javascripts/gear/step_network_test.js
@@ -0,0 +1,18 @@
+(function (context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ context.JK.StepNetworkTest = function (app) {
+
+ var $step = null;
+
+ function initialize(_$step) {
+ $step = _$step;
+ }
+
+ this.initialize = initialize;
+
+ return this;
+ }
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/gear/step_select_gear.js b/web/app/assets/javascripts/gear/step_select_gear.js
new file mode 100644
index 000000000..ec4db1c6d
--- /dev/null
+++ b/web/app/assets/javascripts/gear/step_select_gear.js
@@ -0,0 +1,952 @@
+(function (context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ context.JK.StepSelectGear = function (app, $dialog) {
+
+ var ASSIGNMENT = context.JK.ASSIGNMENT;
+ var VOICE_CHAT = context.JK.VOICE_CHAT;
+ var self = null;
+ var $step = null;
+ var $watchVideoInput = null;
+ var $watchVideoOutput = null;
+ var $audioInput = null;
+ var $audioOutput = null;
+ var $bufferIn = null;
+ var $bufferOut = null;
+ var $frameSize = null;
+ var $inputChannels = null;
+ var $outputChannels = null;
+ var $knobs = null;
+ var $scoreReport = null;
+ var $latencyScoreSection = null;
+ var $latencyScore = null;
+ var $latencyHeader = null;
+ var $ioHeader = null;
+ var $ioScoreSection = null;
+ var $ioRate = null;
+ var $ioRateScore = null;
+ var $ioVar = null;
+ var $ioVarScore = null;
+ var $ioCountdown = null;
+ var $ioCountdownSecs = null;
+ var $resultsText = null;
+ var $asioInputControlBtn = null;
+ var $asioOutputControlBtn = null;
+ var $resyncBtn = null;
+ var $templateAudioPort = null;
+
+ var operatingSystem = null;
+ var iCheckIgnore = false;
+
+ // cached values between
+ var deviceInformation = null;
+ var selectedDeviceInfo = null;
+ var musicPorts = null;
+ var validLatencyScore = false;
+ var validIOScore = false;
+
+ var audioDeviceBehavior = {
+ MacOSX_builtin: {
+ display: 'MacOSX Built-In',
+ videoURL: undefined,
+ showKnobs: false,
+ showASIO: false
+ },
+ MacOSX_interface: {
+ display: 'MacOSX external interface',
+ videoURL: undefined,
+ showKnobs: false,
+ showASIO: false
+ },
+ Win32_wdm: {
+ display: 'Windows WDM',
+ videoURL: undefined,
+ showKnobs: true,
+ showASIO: false
+ },
+ Win32_asio: {
+ display: 'Windows ASIO',
+ videoURL: undefined,
+ showKnobs: true,
+ showASIO: false
+ },
+ Win32_asio4all: {
+ display: 'Windows ASIO4ALL',
+ videoURL: undefined,
+ showKnobs: false,
+ showASIO: true
+ },
+ Linux: {
+ display: 'Linux',
+ videoURL: undefined,
+ showKnobs: true,
+ showASIO: false
+ }
+ }
+
+ var ASIO_SETTINGS_DEFAULT_TEXT = 'ASIO SETTINGS...';
+ var ASIO_SETTINGS_INPUT_TEXT = 'ASIO INPUT SETTINGS...';
+ var ASIO_SETTINGS_OUTPUT_TEXT = 'ASIO OUTPUT SETTINGS...';
+
+ // 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() {
+ $dialog.setNextState(validLatencyScore && validIOScore);
+ }
+
+ function initializeAudioInput() {
+ var optionsHtml = '';
+ optionsHtml = '';
+ context._.each(deviceInformation, function (deviceInfo, deviceId) {
+
+ console.log(arguments)
+ optionsHtml += '';
+ });
+ $audioInput.html(optionsHtml);
+ context.JK.dropdown($audioInput);
+
+ initializeAudioInputChanged();
+ }
+
+ function initializeAudioOutput() {
+ var optionsHtml = '';
+ optionsHtml = '';
+ context._.each(deviceInformation, function (deviceInfo, deviceId) {
+ optionsHtml += '';
+ });
+ $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);
+ }
+
+ // select 2 (or 1) inputs and 2 outputs for the user. required to get a latency score
+ // also, arguably convenient
+ function autoSelectMinimumValidChannels() {
+
+ var $allInputs = $inputChannels.find('input[type="checkbox"]');
+
+ if ($allInputs.length == 0) {
+ // ERROR: not enough channels
+ context.JK.Banner.showAlert('To be a valid input audio device, the device must have at least 1 input channel.');
+ return false;
+ }
+
+ var $allOutputs = $outputChannels.find('input[type="checkbox"]');
+ if ($allOutputs.length < 2) {
+ // ERROR: not enough channels
+ context.JK.Banner.showAlert('To be a valid output audio device, the device must have at least 2 output channels.');
+ return false;
+ }
+
+ // ensure 1, or preferably 2, input channels are selected
+ var $assignedInputs = $inputChannels.find('input[type="checkbox"]:checked');
+ var $unassignedInputs = $inputChannels.find('input[type="checkbox"]:not(:checked)');
+ if ($assignedInputs.length == 0) {
+ if ($allInputs.length >= 2) {
+ $unassignedInputs.eq(0).iCheck('check').attr('checked', 'checked');
+ // this is required because iCheck change handler re-writes the inputs. So we have to refetch unassigned outputs
+ $unassignedInputs = $inputChannels.find('input[type="checkbox"]:not(:checked)');
+ $unassignedInputs.eq(0).iCheck('check').attr('checked', 'checked');
+ }
+ else {
+ $unassignedInputs.eq(0).iCheck('check').attr('checked', 'checked');
+ }
+ }
+
+ // ensure 2 outputs are selected
+ var $assignedOutputs = $outputChannels.find('input[type="checkbox"]:checked');
+ var $unassignedOutputs = $outputChannels.find('input[type="checkbox"]:not(:checked)');
+
+ console.log("outputs:", $assignedOutputs, $unassignedOutputs);
+ if ($assignedOutputs.length == 0) {
+ console.log("selecting both outputs")
+ $unassignedOutputs.eq(0).iCheck('check').attr('checked', 'checked');
+ // this is required because iCheck change handler re-writes the inputs. So we have to refetch unassigned outputs
+ $unassignedOutputs = $outputChannels.find('input[type="checkbox"]:not(:checked)');
+ $unassignedOutputs.eq(0).iCheck('check').attr('checked', 'checked');
+ }
+ else if ($assignedOutputs.length == 1) {
+ $unassignedOutputs.eq(0).iCheck('check').attr('checked', 'checked');
+ }
+
+ return true;
+ }
+
+ // 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() {
+ $ioHeader.hide();
+ $latencyHeader.hide();
+ $ioRate.hide();
+ $ioRateScore.empty();
+ $ioVar.hide();
+ $ioVarScore.empty();
+ $latencyScore.empty();
+ $resultsText.removeAttr('latency-score');
+ $resultsText.removeAttr('io-var-score');
+ $resultsText.removeAttr('io-rate-score');
+ }
+
+ function renderLatencyScore(latencyValue, latencyClass) {
+ // latencyValue == null implies starting condition
+ if (latencyValue) {
+ $latencyScore.text(latencyValue + ' ms');
+ }
+ else {
+ $latencyScore.text('');
+ }
+ $latencyHeader.show();
+ $resultsText.attr('latency-score', latencyClass);
+ $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, ioRateClass, ioVarClass) {
+ $ioRateScore.text(median !== null ? median : '');
+ $ioVarScore.text(std !== null ? std : '');
+ if (ioClass && ioClass != "starting") {
+ $ioRate.show();
+ $ioVar.show();
+ }
+ if(ioClass == 'starting') {
+ $ioHeader.show();
+ }
+ $resultsText.attr('io-rate-score', ioRateClass);
+ $resultsText.attr('io-var-score', ioVarClass);
+ $ioScoreSection.removeClass('good acceptable bad unknown starting skip')
+ if (ioClass) {
+ $ioScoreSection.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;
+ validIOScore = false;
+ initializeNextButtonState();
+ resetFrameBuffers();
+ clearInputPorts();
+ }
+
+ function renderScoringStarted() {
+ validLatencyScore = false;
+ validIOScore = false;
+ initializeNextButtonState();
+ resetScoreReport();
+ freezeAudioInteraction();
+ renderLatencyScore(null, 'starting');
+ renderIOScore(null, null, null, null, null, null);
+ }
+
+ 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');
+ $asioInputControlBtn.on("click", false);
+ $asioOutputControlBtn.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');
+ $asioInputControlBtn.off("click", false);
+ $asioOutputControlBtn.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 initializeASIOButtons() {
+ $asioInputControlBtn.unbind('click').click(function () {
+ context.jamClient.FTUEOpenControlPanel(); // TODO: supply with ID when VRFS-1707 is done
+ });
+ $asioOutputControlBtn.unbind('click').click(function () {
+ context.jamClient.FTUEOpenControlPanel(); // TODO: supply with ID when VRFS-1707 is done
+ });
+ }
+
+ function initializeKnobs() {
+ $frameSize.unbind('change').change(function () {
+ jamClient.FTUESetFrameSize(selectedFramesize());
+ });
+
+ $bufferIn.unbind('change').change(function () {
+ jamClient.FTUESetInputLatency(selectedBufferIn());
+ });
+
+ $bufferOut.unbind('change').change(function () {
+ jamClient.FTUESetOutputLatency(selectedBufferOut());
+ });
+ }
+
+ function initializeResync() {
+ $resyncBtn.unbind('click').click(function () {
+ attemptScore();
+ return false;
+ })
+ }
+
+ function renderIOScoringStarted(secondsLeft) {
+ $ioCountdownSecs.text(secondsLeft);
+ $ioCountdown.show();
+ }
+
+ function renderIOScoringStopped() {
+ $ioCountdown.hide();
+ }
+
+ function renderIOCountdown(secondsLeft) {
+ $ioCountdownSecs.text(secondsLeft);
+ }
+
+ // sets currentlySelectedDeviceInfo, which contains id, behavior, and info for input and output device
+ function cacheCurrentAudioInfo() {
+
+ 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;
+ }
+
+ var input = findDevice(audioInputDeviceId);
+ var output = findDevice(audioOutputDeviceId);
+
+ var inputBehavior = audioDeviceBehavior[input.type];
+ var outputBehavior = audioDeviceBehavior[output.type];
+
+ selectedDeviceInfo = {
+ input: {
+ id: audioInputDeviceId,
+ info: input,
+ behavior: inputBehavior
+ },
+ output: {
+ id: audioOutputDeviceId,
+ info: output,
+ behavior: outputBehavior
+ }
+ }
+ console.log("selectedDeviceInfo", selectedDeviceInfo);
+ }
+
+ function changeDevice() {
+
+ var audioInputDeviceId = selectedDeviceInfo.input.id;
+ var audioOutputDeviceId = selectedDeviceInfo.output.id;
+
+ // don't re-assign input/output audio devices because it disturbs input/output track association
+ if (jamClient.FTUEGetInputMusicDevice() != audioInputDeviceId) {
+ jamClient.FTUESetInputMusicDevice(audioInputDeviceId);
+ }
+ if (jamClient.FTUEGetOutputMusicDevice() != audioOutputDeviceId) {
+ jamClient.FTUESetOutputMusicDevice(audioOutputDeviceId);
+ }
+
+ initializeChannels();
+
+ var validDevice = autoSelectMinimumValidChannels();
+
+ if (!validDevice) {
+ return false;
+ }
+
+ jamClient.FTUESetInputLatency(selectedBufferIn());
+ jamClient.FTUESetOutputLatency(selectedBufferOut());
+ jamClient.FTUESetFrameSize(selectedFramesize());
+
+ return true;
+ }
+
+ function audioDeviceChanged() {
+ cacheCurrentAudioInfo();
+ updateDialogForCurrentDevices();
+ if (changeDevice()) {
+ attemptScore();
+ }
+ }
+
+ function updateDialogForCurrentDevices() {
+ var inputBehavior = selectedDeviceInfo.input.behavior;
+ var outputBehavior = selectedDeviceInfo.output.behavior;
+
+ // handle framesize/buffers
+ if (inputBehavior && (inputBehavior.showKnobs || outputBehavior.showKnobs)) {
+ $knobs.css('visibility', 'visible')
+ }
+ else {
+ $knobs.css('visibility', 'hidden')
+ }
+
+ // handle ASIO
+ if (inputBehavior) {
+ if (inputBehavior.showASIO && !outputBehavior.showASIO) {
+ // show single ASIO button
+ $asioInputControlBtn.text(ASIO_SETTINGS_DEFAULT_TEXT).show();
+ $asioOutputControlBtn.hide();
+ }
+ else if (!inputBehavior.showASIO && outputBehavior.showASIO) {
+ // show single ASIO button
+ $asioInputControlBtn.text(ASIO_SETTINGS_DEFAULT_TEXT).show();
+ $asioOutputControlBtn.hide();
+ }
+ else if (inputBehavior.showASIO && outputBehavior.showASIO) {
+ // show two ASIO buttons
+ $asioInputControlBtn.text(ASIO_SETTINGS_INPUT_TEXT).show();
+ $asioOutputControlBtn.text(ASIO_SETTINGS_OUTPUT_TEXT).show();
+ }
+ else {
+ // show no ASIO buttons
+ $asioInputControlBtn.hide();
+ $asioOutputControlBtn.hide();
+ }
+ }
+ else {
+ // show no ASIO buttons
+ $asioInputControlBtn.hide();
+ $asioOutputControlBtn.hide();
+ }
+
+ // handle resync button
+ if (inputBehavior) {
+ $resyncBtn.css('visibility', 'visible');
+ }
+ else {
+ $resyncBtn.css('visibility', 'hidden');
+ }
+ }
+
+ function attemptScore() {
+ renderScoringStarted();
+
+ // timer exists to give UI time to update for renderScoringStarted before blocking nature of jamClient.FTUESave(save) kicks in
+ setTimeout(function () {
+ 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', 'starting', '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();
+
+ // 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';
+ }
+
+ // take worst between median or std
+ var ioClassToNumber = {bad: 2, acceptable: 1, good: 0}
+ var aggregrateIOClass = ioClassToNumber[stdIOClass] > ioClassToNumber[medianIOClass] ? stdIOClass : medianIOClass;
+
+ // now base the overall IO score based on both values.
+ renderIOScore(std, median, io, aggregrateIOClass, medianIOClass, stdIOClass);
+
+ // lie for now until IO questions finalize
+ validIOScore = true;
+
+ renderScoringStopped();
+ }
+ }, 1000);
+ }
+ else {
+ renderIOScore(null, null, null, 'skip', 'skip', 'skip');
+ renderScoringStopped();
+ }
+ }, 250);
+ }
+
+ function initializeAudioInputChanged() {
+ $audioInput.unbind('change').change(audioDeviceChanged);
+ }
+
+ function initializeAudioOutputChanged() {
+ $audioOutput.unbind('change').change(audioDeviceChanged);
+ }
+
+ function beforeShow() {
+ loadDevices();
+ initializeFormElements();
+ initializeNextButtonState();
+ initializeWatchVideo();
+ initializeASIOButtons();
+ initializeKnobs();
+ initializeResync();
+ }
+
+ function initialize(_$step) {
+ $step = _$step;
+
+ $watchVideoInput = $step.find('.watch-video.audio-input');
+ $watchVideoOutput = $step.find('.watch-video.audio-output');
+ $audioInput = $step.find('.select-audio-input-device');
+ $audioOutput = $step.find('.select-audio-output-device');
+ $bufferIn = $step.find('.select-buffer-in');
+ $bufferOut = $step.find('.select-buffer-out');
+ $frameSize = $step.find('.select-frame-size');
+ $inputChannels = $step.find('.input-ports');
+ $outputChannels = $step.find('.output-ports');
+ $knobs = $step.find('.frame-and-buffers');
+ $scoreReport = $step.find('.results');
+ $latencyScoreSection = $scoreReport.find('.latency-score-section');
+ $latencyScore = $scoreReport.find('.latency-score');
+ $latencyHeader = $scoreReport.find('.latency');
+ $ioHeader = $scoreReport.find('.io');
+ $ioScoreSection = $scoreReport.find('.io-score-section');
+ $ioRate = $scoreReport.find('.io-rate');
+ $ioRateScore = $scoreReport.find('.io-rate-score');
+ $ioVar = $scoreReport.find('.io-var');
+ $ioVarScore = $scoreReport.find('.io-var-score');
+ $ioCountdown = $scoreReport.find('.io-countdown');
+ $ioCountdownSecs = $scoreReport.find('.io-countdown .secs');
+ $resultsText = $scoreReport.find('.results-text');
+ $asioInputControlBtn = $step.find('.asio-settings-input-btn');
+ $asioOutputControlBtn = $step.find('.asio-settings-output-btn');
+ $resyncBtn = $step.find('.resync-btn');
+ $templateAudioPort = $('#template-audio-port');
+
+ operatingSystem = context.jamClient.GetOSAsString();
+ }
+
+ this.beforeShow = beforeShow;
+ this.initialize = initialize;
+
+ self = this;
+ return this;
+ };
+
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/gear/step_success.js b/web/app/assets/javascripts/gear/step_success.js
new file mode 100644
index 000000000..3dc049d2c
--- /dev/null
+++ b/web/app/assets/javascripts/gear/step_success.js
@@ -0,0 +1,18 @@
+(function (context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ context.JK.StepSuccess = function (app) {
+
+ var $step = null;
+
+ function initialize(_$step) {
+ $step = _$step;
+ }
+
+ this.initialize = initialize;
+
+ return this;
+ }
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/gear/step_understand_gear.js b/web/app/assets/javascripts/gear/step_understand_gear.js
new file mode 100644
index 000000000..d579e84f2
--- /dev/null
+++ b/web/app/assets/javascripts/gear/step_understand_gear.js
@@ -0,0 +1,27 @@
+(function (context, $) {
+
+ "use strict";
+
+ context.JK = context.JK || {};
+ context.JK.StepUnderstandGear = function (app) {
+
+ var $step = null;
+
+ function beforeShow() {
+ var $watchVideo = $step.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 initialize(_$step) {
+ $step = _$step;
+ }
+
+ this.initialize = initialize;
+
+ return this;
+ }
+})(window, jQuery);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/gear_wizard.js b/web/app/assets/javascripts/gear_wizard.js
deleted file mode 100644
index 9012c91be..000000000
--- a/web/app/assets/javascripts/gear_wizard.js
+++ /dev/null
@@ -1,909 +0,0 @@
-(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 = '';
- context._.each(deviceInformation, function (deviceInfo, deviceId) {
-
- console.log(arguments)
- optionsHtml += '';
- });
- $audioInput.html(optionsHtml);
- context.JK.dropdown($audioInput);
-
- initializeAudioInputChanged();
- }
-
- function initializeAudioOutput() {
- var optionsHtml = '';
- optionsHtml = '';
- context._.each(deviceInformation, function (deviceInfo, deviceId) {
- optionsHtml += '';
- });
- $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);
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/gearWizard.css.scss b/web/app/assets/stylesheets/client/gearWizard.css.scss
index aa2ac4bf6..bf8e1384c 100644
--- a/web/app/assets/stylesheets/client/gearWizard.css.scss
+++ b/web/app/assets/stylesheets/client/gearWizard.css.scss
@@ -115,6 +115,7 @@
&.list.ports {
height:100px;
+ overflow:auto;
}
&.instructions {
@@ -151,18 +152,32 @@
margin-bottom:20px;
}
- .asio-settings-btn, .resync-btn {
+ .asio-settings-input-btn, .asio-settings-output-btn, .resync-btn {
width:80%;
display:inline-block;
text-align:center;
}
- .asio-settings-btn {
+ .asio-settings-input-btn, .asio-settings-output-btn {
margin-top:10px;
}
+
+ .asio-settings-input-btn {
+ display:none;
+ }
+
+ .asio-settings-output-btn {
+ display:none;
+ }
+
.resync-btn {
margin-top:10px;
+ visibility:hidden;
+ }
+
+ .frame-and-buffers {
+ display:none;
}
.framesize {
@@ -197,12 +212,24 @@
}
}
+ .audio-port {
+ white-space: nowrap;
+ }
+
+ .audio-channels {
+ margin-top:15px;
+ }
+
.ftue-box.results {
height: 230px !important;
padding:0;
+ .io, .latency {
+ display:none;
+ }
+
.scoring-section {
font-size:15px;
@include border_box_sizing;
@@ -220,6 +247,11 @@
&.unknown {
background-color:#999;
}
+ &.skip {
+ .io-skip-msg {
+ display:inline;
+ }
+ }
}
.io-countdown {
@@ -236,9 +268,48 @@
.io-skip-msg {
display:none;
+ }
- .scoring-section.skip & {
- display:inline;
+ .io-rate {
+ display:none;
+ }
+ .io-var {
+ display:none;
+ }
+
+ ul.results-text {
+ padding:10px 8px;
+
+ li {
+ display:none
+ }
+
+ &[latency-score="good"] li.latency-good {
+ display:list-item;
+ }
+ &[latency-score="acceptable"] li.latency-acceptable {
+ display:list-item;
+ }
+ &[latency-score="bad"] li.latency-bad {
+ display:list-item;
+ }
+ &[io-var-score="good"] li.io-var-good {
+ display:list-item;
+ }
+ &[io-var-score="acceptable"] li.io-var-acceptable {
+ display:list-item;
+ }
+ &[io-var-score="bad"] li.io-var-bad {
+ display:list-item;
+ }
+ &[io-rate-score="good"] li.io-rate-good {
+ display:list-item;
+ }
+ &[io-rate-score="acceptable"] li.io-rate-acceptable {
+ display:list-item;
+ }
+ &[io-rate-score="bad"] li.io-rate-bad {
+ display:list-item;
}
}
}
diff --git a/web/app/views/clients/gear/_gear_wizard.html.haml b/web/app/views/clients/gear/_gear_wizard.html.haml
index 57f0ef1f8..01b4637de 100644
--- a/web/app/views/clients/gear/_gear_wizard.html.haml
+++ b/web/app/views/clients/gear/_gear_wizard.html.haml
@@ -37,15 +37,16 @@
%h2 Audio Input Device
%select.w100.select-audio-input-device
%option None
- %h2 Audio Input Ports
+ %h2.audio-channels Audio Input Ports
.ftue-box.list.ports.input-ports
- %a.button-orange.asio-settings-btn ASIO SETTINGS...
+ %a.button-orange.asio-settings-input-btn ASIO SETTINGS...
+ %a.button-orange.asio-settings-output-btn ASIO SETTINGS...
%a.button-orange.resync-btn RESYNC
.wizard-step-column
%h2 Audio Output Device
%select.w100.select-audio-output-device
%option Same as input
- %h2 Audio Output Ports
+ %h2.audio-channels Audio Output Ports
.ftue-box.list.ports.output-ports
.frame-and-buffers
.framesize
@@ -91,13 +92,27 @@
.p5
.io I/O
%span.io-skip-msg
- Skipped
+ Not Tested
%span.io-countdown
%span.secs
seconds left
- %span.io-rate-score
- %span.io-var-score
-
+ %span.io-rate<
+ Rate=
+ %span.io-rate-score>
+ %span.io-var<
+ Var=
+ %span.io-var-score>
+ .clearall
+ %ul.results-text
+ %li.latency-good Your latency is good.
+ %li.latency-acceptable Your latency is acceptable.
+ %li.latency-bad Your latency is poor.
+ %li.io-rate-good Your I/O rate is good.
+ %li.io-rate-acceptable Your I/O rate is acceptable.
+ %li.io-rate-bad Your I/O rate is poor.
+ %li.io-var-good Your I/O variance is good.
+ %li.io-var-acceptable Your I/O variance is acceptable.
+ %li.io-var-bad Your I/O variance is poor.
.clearall
.wizard-step{ 'layout-wizard-step' => "2", 'dialog-title' => "Configure Tracks", 'dialog-purpose' => "ConfigureTracks" }