diff --git a/admin/Gemfile b/admin/Gemfile index 47096903d..c67712122 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -33,21 +33,24 @@ group :assets do end gem 'will_paginate', '3.0.3' gem 'bootstrap-will_paginate', '0.0.6' -gem 'carrierwave' +gem 'carrierwave', '0.9.0' gem 'uuidtools', '2.1.2' gem 'bcrypt-ruby', '3.0.1' gem 'jquery-rails', '2.3.0' # pinned because jquery-ui-rails was split from jquery-rails, but activeadmin doesn't support this gem yet gem 'rails3-jquery-autocomplete' -gem 'activeadmin' -gem "meta_search", '>= 1.1.0.pre' -gem 'fog', "~> 1.3.1" +gem 'activeadmin', '0.6.2' +gem 'mime-types', '1.25' +gem 'meta_search' +gem 'fog', "~> 1.18.0" +gem 'unf', '0.1.3' #optional fog dependency gem 'country-select' gem 'aasm', '3.0.16' -gem 'postgres-copy' +gem 'postgres-copy', '0.6.0' gem 'aws-sdk' -gem 'bugsnag' +gem 'bugsnag' -gem 'eventmachine', '1.0.0' + +gem 'eventmachine', '1.0.3' gem 'amqp', '0.9.8' gem 'logging-rails', :require => 'logging/rails' @@ -56,8 +59,8 @@ gem 'ruby-protocol-buffers', '1.2.2' gem 'sendgrid', '1.1.0' -gem 'geokit-rails' -gem 'postgres_ext' +gem 'geokit-rails', '2.0.0' +gem 'postgres_ext', '1.0.0' group :libv8 do gem 'libv8', "~> 3.11.8" diff --git a/web/Gemfile b/web/Gemfile index 4a4ae6449..ab19237e8 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -47,6 +47,7 @@ gem 'aws-sdk', '1.8.0' gem 'aasm', '3.0.16' gem 'carrierwave' gem 'fog' +gem 'unf' #optional fog dependency gem 'devise', '>= 1.1.2' #gem 'thin' # the presence of this gem on mac seems to prevent normal startup of rails. gem 'postgres-copy' diff --git a/web/app/assets/javascripts/bandProfile.js b/web/app/assets/javascripts/bandProfile.js index d03ce3bdf..54d0130d1 100644 --- a/web/app/assets/javascripts/bandProfile.js +++ b/web/app/assets/javascripts/bandProfile.js @@ -245,7 +245,7 @@ $('#band-profile-biography').html(band.biography); } else { - + logger.debug("No band found with bandId = " + bandId); } } @@ -266,7 +266,7 @@ function bindSocial() { // FOLLOWERS - url = "/api/bands/" + bandId + "/followers"; + var url = "/api/bands/" + bandId + "/followers"; $.ajax({ type: "GET", dataType: "json", diff --git a/web/app/assets/javascripts/createSession.js b/web/app/assets/javascripts/createSession.js index fe19afe00..9f697366f 100644 --- a/web/app/assets/javascripts/createSession.js +++ b/web/app/assets/javascripts/createSession.js @@ -178,6 +178,13 @@ function submitForm(evt) { evt.preventDefault(); + // If user hasn't completed FTUE - do so now. + if (!(context.jamClient.FTUEGetStatus())) { + app.afterFtue = function() { submitForm(evt); }; + app.layout.showDialog('ftue'); + return; + } + var isValid = validateForm(); if (!isValid) { // app.notify({ @@ -435,4 +442,4 @@ return this; }; - })(window,jQuery); + })(window,jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/findSession.js b/web/app/assets/javascripts/findSession.js index 832f298e6..bc063cc73 100644 --- a/web/app/assets/javascripts/findSession.js +++ b/web/app/assets/javascripts/findSession.js @@ -152,13 +152,15 @@ function containsInvitation(session) { var i, invitation = null; - if ("invitations" in session) { - // user has invitations for this session - for (i=0; i < session.invitations.length; i++) { - invitation = session.invitations[i]; - // session contains an invitation for this user - if (invitation.receiver_id == context.JK.currentUserId) { - return true; + if (session !== undefined) { + if ("invitations" in session) { + // user has invitations for this session + for (i=0; i < session.invitations.length; i++) { + invitation = session.invitations[i]; + // session contains an invitation for this user + if (invitation.receiver_id == context.JK.currentUserId) { + return true; + } } } } @@ -169,12 +171,14 @@ function containsFriend(session) { var i, participant = null; - if ("participants" in session) { - for (i=0; i < session.participants.length; i++) { - participant = session.participants[i]; - // this session participant is a friend - if (participant !== null && participant !== undefined && participant.user.is_friend) { - return true; + if (session !== undefined) { + if ("participants" in session) { + for (i=0; i < session.participants.length; i++) { + participant = session.participants[i]; + // this session participant is a friend + if (participant !== null && participant !== undefined && participant.user.is_friend) { + return true; + } } } } diff --git a/web/app/assets/javascripts/ftue.js b/web/app/assets/javascripts/ftue.js index 9bb2253b2..21414246b 100644 --- a/web/app/assets/javascripts/ftue.js +++ b/web/app/assets/javascripts/ftue.js @@ -25,6 +25,8 @@ }; var faderMap = { + 'ftue-2-audio-input-fader': jamClient.FTUESetInputVolume, + 'ftue-2-voice-input-fader': jamClient.FTUESetOutputVolume, 'ftue-audio-input-fader': jamClient.FTUESetInputVolume, 'ftue-voice-input-fader': jamClient.FTUESetChatInputVolume, 'ftue-audio-output-fader': jamClient.FTUESetOutputVolume @@ -44,6 +46,10 @@ function beforeShow(data) { var vuMeters = [ + '#ftue-2-audio-input-vu-left', + '#ftue-2-audio-input-vu-right', + '#ftue-2-voice-input-vu-left', + '#ftue-2-voice-input-vu-right', '#ftue-audio-input-vu-left', '#ftue-audio-input-vu-right', '#ftue-voice-input-vu-left', @@ -80,6 +86,8 @@ // Always reset the driver select box to "Choose..." which forces everything // to sync properly when the user reselects their driver of choice. // VRFS-375 and VRFS-561 + $('[layout-wizard="ftue"] [layout-wizard-step="0"] .settings-2-device select').val(""); + $('[layout-wizard="ftue"] [layout-wizard-step="0"] .settings-2-voice select').val(""); $('[layout-wizard="ftue"] [layout-wizard-step="2"] .asio-settings .settings-driver select').val(""); } @@ -317,6 +325,15 @@ $('#asio-framesize').on('change', setAsioFrameSize); $('#asio-input-latency').on('change', setAsioInputLatency); $('#asio-output-latency').on('change', setAsioOutputLatency); + // New FTUE events + $('.ftue-new .settings-2-device select').on('change', newFtueAudioDeviceChanged); + $('.ftue-new .settings-2-voice select').on('change', newFtueAudioDeviceChanged); + $('#btn-ftue-2-asio-resync').on('click', newFtueAsioResync); + $('#btn-ftue-2-asio-control-panel').on('click', openASIOControlPanel); + $('#ftue-2-asio-framesize').on('change', newFtueSetAsioFrameSize); + $('#ftue-2-asio-input-latency').on('change', newFtueSetAsioInputLatency); + $('#ftue-2-asio-output-latency').on('change', newFtueSetAsioOutputLatency); + $('#btn-ftue-2-save').on('click', newFtueSaveSettingsHandler); } /** @@ -368,7 +385,6 @@ * Load available drivers and populate the driver select box. */ function loadAudioDrivers() { - var drivers = jamClient.FTUEGetDevices(); var driverOptionFunc = function(driverKey, index, list) { @@ -377,11 +393,249 @@ }; var optionsHtml = ''; - var $select = $('[layout-wizard-step="2"] .settings-driver select'); - $select.empty(); + var selectors = [ + '[layout-wizard-step="0"] .settings-2-device select', + '[layout-wizard-step="0"] .settings-2-voice select', + '[layout-wizard-step="2"] .settings-driver select' + ]; var sortedDeviceKeys = context._.keys(drivers).sort(); context._.each(sortedDeviceKeys, driverOptionFunc); - $select.html(optionsHtml); + $.each(selectors, function(index, selector) { + var $select = $(selector); + $select.empty(); + $select.html(optionsHtml); + }); + } + + /** + * Handler for the new FTUE save button. + */ + function newFtueSaveSettingsHandler(evt) { + evt.preventDefault(); + var $saveButton = $('#btn-ftue-2-save'); + if ($saveButton.hasClass('disabled')) { + return; + } + var selectedAudioDevice = $('.ftue-new .settings-2-device select').val(); + if (!(selectedAudioDevice)) { + app.notify({ + title: "Please select an audio device", + text: "Please choose a usable audio device, or select cancel." + }); + return false; + } + jamClient.FTUESave(true); + jamClient.FTUESetStatus(true); // No FTUE wizard next time + rest.userCertifiedGear({success:true}); + app.layout.closeDialog('ftue'); + if (app.afterFtue) { + // If there's a function to invoke, invoke it. + app.afterFtue(); + app.afterFtue = null; + } + return false; + } + + // Handler for when the audio device is changed in the new FTUE screen + // This works differently from the old FTUE. There is no input/output selection, + // as soon as the user chooses a driver, we auto-assign inputs and outputs. + // We also call jamClient.FTUEGetExpectedLatency, which returns a structure like: + // { latency: 11.1875, latencyknown: true, latencyvar: 1} + function newFtueAudioDeviceChanged(evt) { + var $select = $(evt.currentTarget); + + var $audioSelect = $('.ftue-new .settings-2-device select'); + var $voiceSelect = $('.ftue-new .settings-2-voice select'); + var audioDriverId = $audioSelect.val(); + var voiceDriverId = $voiceSelect.val(); + jamClient.FTUESetMusicDevice(audioDriverId); + jamClient.FTUESetChatInput(voiceDriverId); + if (voiceDriverId) { // Let the back end know whether a voice device is selected + jamClient.TrackSetChatEnable(true); + } else { + jamClient.TrackSetChatEnable(false); + } + if (!audioDriverId) { + // reset back to 'Choose...' + newFtueEnableControls(false); + return; + } + var musicInputs = jamClient.FTUEGetMusicInputs(); + var musicOutputs = jamClient.FTUEGetMusicOutputs(); + + // set the music input to the first available input, + // and output to the first available output + var kin = null, kout = null, k = null; + // TODO FIXME - this jamClient call returns a dictionary. + // It's difficult to know what to auto-choose. + // For example, with my built-in audio, the keys I get back are + // digital in, line in, mic in and stereo mix. Which should we pick for them? + for (k in musicInputs) { + kin = k; + break; + } + for (k in musicOutputs) { + kout = k; + break; + } + var result; + if (kin && kout) { + jamClient.FTUESetMusicInput(kin); + jamClient.FTUESetMusicOutput(kout); + } else { + // TODO FIXME - how to handle a driver selection where we are unable to + // autoset both inputs and outputs? (I'd think this could happen if either + // the input or output side returned no values) + return; + } + + newFtueEnableControls(true); + newFtueOsSpecificSettings(); + setLevels(0); + newFtueUpdateLatencyView('loading'); + jamClient.FTUESave(false); + setVuCallbacks(); + + var latency = jamClient.FTUEGetExpectedLatency(); + newFtueUpdateLatencyView(latency); + + } + + function newFtueSave(persist) { + newFtueUpdateLatencyView('loading'); + jamClient.FTUESave(persist); + var latency = jamClient.FTUEGetExpectedLatency(); + newFtueUpdateLatencyView(latency); + } + + function newFtueAsioResync(evt) { + // In theory, we should be calling the following, but it causes + // us to not have both inputs/outputs loaded, and simply calling + // FTUE Save appears to resync things. + //jamClient.FTUERefreshDevices(); + newFtueSave(false); + } + + function newFtueSetAsioFrameSize(evt) { + var val = parseFloat($(evt.currentTarget).val(),10); + if (isNaN(val)) { + return; + } + logger.debug("Calling FTUESetFrameSize(" + val + ")"); + jamClient.FTUESetFrameSize(val); + newFtueSave(false); + } + function newFtueSetAsioInputLatency(evt) { + var val = parseInt($(evt.currentTarget).val(),10); + if (isNaN(val)) { + return; + } + logger.debug("Calling FTUESetInputLatency(" + val + ")"); + jamClient.FTUESetInputLatency(val); + newFtueSave(false); + } + function newFtueSetAsioOutputLatency(evt) { + var val = parseInt($(evt.currentTarget).val(),10); + if (isNaN(val)) { + return; + } + logger.debug("Calling FTUESetOutputLatency(" + val + ")"); + jamClient.FTUESetOutputLatency(val); + newFtueSave(false); + } + + // Enable or Disable the frame/buffer controls in the new FTUE screen + function newFtueEnableControls(enable) { + var $frame = $('#ftue-2-asio-framesize'); + var $bin = $('#ftue-2-asio-input-latency'); + var $bout = $('#ftue-2-asio-output-latency'); + if (enable) { + $frame.removeAttr("disabled"); + $bin.removeAttr("disabled"); + $bout.removeAttr("disabled"); + } else { + $frame.attr("disabled", "disabled"); + $bin.attr("disabled", "disabled"); + $bout.attr("disabled", "disabled"); + } + } + + // Based on OS and Audio Hardware, set Frame/Buffer settings appropriately + // and show/hide the ASIO button. + function newFtueOsSpecificSettings() { + var $frame = $('#ftue-2-asio-framesize'); + var $bin = $('#ftue-2-asio-input-latency'); + var $bout = $('#ftue-2-asio-output-latency'); + var $asioBtn = $('#btn-ftue-2-asio-control-panel'); + if (jamClient.GetOSAsString() === "Win32") { + if (jamClient.FTUEHasControlPanel()) { + // Win32 + ControlPanel = ASIO + // frame=2.5, buffers=0 + $asioBtn.show(); + $frame.val(2.5); + $bin.val(0); + $bout.val(0); + } else { + // Win32, no ControlPanel = WDM/Kernel Streaming + // frame=10, buffers=0 + $asioBtn.hide(); + $frame.val(10); + // TODO FIXME - the old FTUE set the buffers to 1 for WDM/Kernel streaming + // The new FTUE spec says to use 0, as I've done here... + $bin.val(0); + $bout.val(0); + } + } else { // Assuming Mac. TODO: Linux check here + // frame=2.5, buffers=0 + $asioBtn.hide(); + $frame.val(2.5); + $bin.val(0); + $bout.val(0); + } + + } + + // 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"; + $saveButton.removeClass('disabled'); + } else if (latency.latency <= 20) { + latencyClass = "acceptable"; + $saveButton.removeClass('disabled'); + } else { + latencyClass = "bad"; + $saveButton.addClass('disabled'); + } + } else { + latencyClass = "unknown"; + $saveButton.addClass('disabled'); + } + + $('.ms-label', $report).html(latencyValue); + $('p', $report).html('milliseconds'); + + $report.removeClass('good acceptable bad'); + $report.addClass(latencyClass); + + var instructionClasses = ['neutral', 'good', 'acceptable', 'bad', 'start', 'loading']; + $.each(instructionClasses, function(idx, val) { + $('p.' + val, $instructions).hide(); + }); + if (latency === 'loading') { + $('p.loading', $instructions).show(); + } else { + $('p.' + latencyClass, $instructions).show(); + } } function audioDriverChanged(evt) { @@ -459,6 +713,7 @@ var dialogBindings = { 'beforeShow': beforeShow, 'afterShow': afterShow, 'afterHide': afterHide }; app.bindDialog('ftue', dialogBindings); + app.registerWizardStepFunction("0", settingsInit); app.registerWizardStepFunction("2", settingsInit); app.registerWizardStepFunction("4", testLatency); app.registerWizardStepFunction("6", testComplete); @@ -484,6 +739,8 @@ }; context.JK.ftueAudioInputVUCallback = function(dbValue) { + context.JK.ftueVUCallback(dbValue, '#ftue-2-audio-input-vu-left'); + context.JK.ftueVUCallback(dbValue, '#ftue-2-audio-input-vu-right'); context.JK.ftueVUCallback(dbValue, '#ftue-audio-input-vu-left'); context.JK.ftueVUCallback(dbValue, '#ftue-audio-input-vu-right'); }; @@ -492,6 +749,8 @@ context.JK.ftueVUCallback(dbValue, '#ftue-audio-output-vu-right'); }; context.JK.ftueChatInputVUCallback = function(dbValue) { + context.JK.ftueVUCallback(dbValue, '#ftue-2-voice-input-vu-left'); + context.JK.ftueVUCallback(dbValue, '#ftue-2-voice-input-vu-right'); context.JK.ftueVUCallback(dbValue, '#ftue-voice-input-vu-left'); context.JK.ftueVUCallback(dbValue, '#ftue-voice-input-vu-right'); }; diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index ea66bcbec..341e0d284 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -168,6 +168,14 @@ }); } + function getMusicianFollowers(userId) { + + } + + function getBandFollowers(bandId) { + + } + function getClientDownloads(options) { return $.ajax({ diff --git a/web/app/assets/javascripts/jamkazam.js b/web/app/assets/javascripts/jamkazam.js index 6fade3525..61da06700 100644 --- a/web/app/assets/javascripts/jamkazam.js +++ b/web/app/assets/javascripts/jamkazam.js @@ -255,10 +255,6 @@ } logger.debug("Changing screen to " + url); context.location = url; - - if (!(context.jamClient.FTUEGetStatus())) { - app.layout.showDialog('ftue'); - } } this.unloadFunction = function() { @@ -294,6 +290,9 @@ } }; + // Holder for a function to invoke upon successfully completing the FTUE. + // See createSession.submitForm as an example. + this.afterFtue = null; // enable temporary suspension of heartbeat for fine-grained control this.heartbeatActive = true; diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index 512fe2484..6958bf426 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -303,7 +303,9 @@ padding: '0px' }; $('[layout]').css(layoutStyle); - $('[layout="notify"]').css({"z-index": "9", "padding": "20px"}); + // JW: Setting z-index of notify to 1001, so it will appear above the dialog overlay. + // This allows dialogs to use the notification. + $('[layout="notify"]').css({"z-index": "1001", "padding": "20px"}); $('[layout="panel"]').css({position: 'relative'}); $('[layout-panel="expanded"] [layout-panel="header"]').css({ margin: "0px", @@ -381,12 +383,20 @@ function linkClicked(evt) { evt.preventDefault(); + var $currentTarget = $(evt.currentTarget); // allow links to be disabled - if($(evt.currentTarget).hasClass("disabled") ) { + if($currentTarget.hasClass("disabled") ) { return; } + // If link requires FTUE, show that first. + if ($currentTarget.hasClass("requires-ftue")) { + if (!(context.jamClient.FTUEGetStatus())) { + app.layout.showDialog('ftue'); + } + } + var destination = $(evt.currentTarget).attr('layout-link'); var destinationType = $('[layout-id="' + destination + '"]').attr("layout"); if (destinationType === "screen") { @@ -595,10 +605,11 @@ } notifyQueue.push({message: message, descriptor: descriptor}); - $notify.slideDown(2000) - .delay(2000) + // JW - speeding up the in/out parts of notify. Extending non-moving time. + $notify.slideDown(250) + .delay(4000) .slideUp({ - duration: 2000, + duration: 400, queue: true, complete: function() { notifyDetails = notifyQueue.shift(); @@ -614,7 +625,7 @@ } } }); - } + }; function setNotificationInfo(message, descriptor) { var $notify = $('[layout="notify"]'); diff --git a/web/app/assets/javascripts/profile.js b/web/app/assets/javascripts/profile.js index fc69c53bc..17f07bff2 100644 --- a/web/app/assets/javascripts/profile.js +++ b/web/app/assets/javascripts/profile.js @@ -307,22 +307,22 @@ $('#profile-location').html(user.location); // stats - var text = user.friend_count > 1 || user.friend_count == 0 ? " Friends" : " Friend"; + var text = user.friend_count > 1 || user.friend_count === 0 ? " Friends" : " Friend"; $('#profile-friend-stats').html(user.friend_count + text); - text = user.follower_count > 1 || user.follower_count == 0 ? " Followers" : " Follower"; + text = user.follower_count > 1 || user.follower_count === 0 ? " Followers" : " Follower"; $('#profile-follower-stats').html(user.follower_count + text); - text = user.session_count > 1 || user.session_count == 0 ? " Sessions" : " Session"; + text = user.session_count > 1 || user.session_count === 0 ? " Sessions" : " Session"; $('#profile-session-stats').html(user.session_count + text); - text = user.recording_count > 1 || user.recording_count == 0 ? " Recordings" : " Recording"; + text = user.recording_count > 1 || user.recording_count === 0 ? " Recordings" : " Recording"; $('#profile-recording-stats').html(user.recording_count + text); $('#profile-biography').html(user.biography); } else { - + logger.debug("No user found with userId = " + userId); } } diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index 3eaf1574e..278f733b8 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -621,8 +621,6 @@ $connection.addClass(connectionClass); } else if (eventName === 'add' || eventName === 'remove') { - //logger.dbg('non-vu event: ' + eventName + ',' + mixerId + ',' + value); - // TODO - _renderSession. Note I get streams of these in // sequence, so have Nat fix, or buffer/spam protect // Note - this is already handled from websocket events. @@ -632,7 +630,7 @@ } else { // Examples of other events // Add media file track: "add", "The_Abyss_4T", 0 - logger.dbg('non-vu event: ' + eventName + ',' + mixerId + ',' + value); + logger.debug('non-vu event: ' + eventName + ',' + mixerId + ',' + value); } } } diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index 49af23e00..95ac0cde9 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -137,8 +137,16 @@ // wire up the Join Link to the T&Cs dialog var $parentRow = $('tr[id=' + session.id + ']', tbGroup); - $('#join-link', $parentRow).click(function(evt) { - joinClick(session.id); + + $('.join-link', $parentRow).click(function(evt) { + // If no FTUE, show that first. + if (!(context.jamClient.FTUEGetStatus())) { + app.afterFtue = function() { joinClick(session.id); }; + app.layout.showDialog('ftue'); + return; + } else { + joinClick(session.id); + } }); } } @@ -201,7 +209,7 @@ } function openAlert(sessionId) { - var alertDialog = new context.JK.AlertDialog(app, "YES", + var alertDialog = new context.JK.AlertDialog(app, "YES", "You must be approved to join this session. Would you like to send a request to join?", sessionId, onCreateJoinRequest); @@ -210,7 +218,7 @@ } function sessionNotJoinableAlert() { - var alertDialog = new context.JK.AlertDialog(app, "OK", + var alertDialog = new context.JK.AlertDialog(app, "OK", "This session is over or is no longer public and cannot be joined. Please click Refresh to update the session list.", null, function(evt) { diff --git a/web/app/assets/stylesheets/client/ftue.css.scss b/web/app/assets/stylesheets/client/ftue.css.scss index 9af975c1a..02b4d1f27 100644 --- a/web/app/assets/stylesheets/client/ftue.css.scss +++ b/web/app/assets/stylesheets/client/ftue.css.scss @@ -110,6 +110,155 @@ div.dialog.ftue { margin-top: 12px; } + p.intro { + margin-top:0px; + } + .ftue-new { + clear:both; + position:relative; + width:100%; + height: 54px; + margin-top: 12px; + select { + font-size: 15px; + padding: 3px; + } + .latency { + position: absolute; + top: 120px; + font-size: 12px; + .report { + color:#fff; + position: absolute; + top: 20px; + left: 0px; + width: 105px; + height: 50px; + background-color: #72a43b; + padding: 10px; + text-align: center; + .ms-label { + padding-top: 10px; + font-size: 34px; + font-family: Arial, sans-serif; + } + p { + margin-top: 4px; + } + } + .report.neutral, .report.start, .report.unknown { + background-color: #666; + } + .report.good { + background-color: #72a43b; + } + .report.acceptable { + background-color: #D6A800; + } + .report.bad { + background-color: #7B0C00; + } + .instructions { + color:#fff; + position: absolute; + top: 20px; + left: 125px; + width: 595px; + height: 50px; + padding: 10px; + background-color: #666; + } + .instructions p.start, .instructions p.neutral { + padding-top: 4px; + } + .instructions p.unknown { + margin-top:0px; + padding-top: 4px; + } + .instructions p.good { + padding-top: 4px; + } + .instructions p.acceptable { + margin-top: -6px; + } + .instructions p.bad { + margin-top: -6px; + } + .instructions p a { + font-weight: bold; + } + } + .column { + position:absolute; + width: 220px; + height: 50px; + margin-right:8px; + } + .settings-2-device { + left:0px; + } + .settings-2-center { + left:50%; + margin-left: -110px; + .buttons { + margin-top: 14px; + a { + margin-right: 0px; + } + } + } + .settings-2-voice { + top: 0px; + right:0px; + } + .controls { + margin-top: 16px; + background-color: #222; + height: 48px; + width: 220px; + } + .ftue-vu-left { + position:relative; + top: 0px; + } + .ftue-vu-right { + position:relative; + top: 22px; + } + .ftue-fader { + position:relative; + top: 14px; + left: 8px; + } + .gain-label { + color: $ColorScreenPrimary; + position:absolute; + top: 76px; + right: 6px; + } + + .subcolumn { + position:absolute; + top: 60px; + font-size: 12px !important; + width: 68px; + height: 48px; + } + .subcolumn select { + width: 68px; + } + .subcolumn.first { + left:0px; + } + .subcolumn.second { + left:50%; + margin-left:-34px; + } + .subcolumn.third { + right:0px; + } + } + .asio-settings { clear:both; position:relative; diff --git a/web/app/views/clients/_findSession.html.erb b/web/app/views/clients/_findSession.html.erb index 562dc7f06..af1d851c6 100644 --- a/web/app/views/clients/_findSession.html.erb +++ b/web/app/views/clients/_findSession.html.erb @@ -72,7 +72,7 @@
+ Choose a device to capture and play your session audio. If + you’re not using a mic with this device, then also choose a + voice chat input to talk with others during sessions. Then play + and speak, and adjust the gain faders so that you hear both your + instrument and voice in your headphones at comfortable volumes. +
+ + +Choose an audio device to continue...
+ + + + + +