diff --git a/db/manifest b/db/manifest index 6bc1571b3..f10027f6e 100755 --- a/db/manifest +++ b/db/manifest @@ -159,3 +159,4 @@ update_get_work_for_larger_radius.sql periodic_emails.sql remember_extra_scoring_data.sql indexing_for_regions.sql +latency_tester.sql diff --git a/db/up/latency_tester.sql b/db/up/latency_tester.sql new file mode 100644 index 000000000..000126654 --- /dev/null +++ b/db/up/latency_tester.sql @@ -0,0 +1,8 @@ +CREATE TABLE latency_testers ( + id VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(), + client_id VARCHAR(64) UNIQUE NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +ALTER TABLE connections ALTER COLUMN user_id DROP NOT NULL; \ No newline at end of file diff --git a/ruby/Gemfile b/ruby/Gemfile index 7ecd9ef31..3ae116dfd 100644 --- a/ruby/Gemfile +++ b/ruby/Gemfile @@ -56,6 +56,7 @@ group :test do gem 'faker' gem 'resque_spec' #, :path => "/home/jam/src/resque_spec/" gem 'timecop' + gem 'rspec-prof' end # Specify your gem's dependencies in jam_ruby.gemspec diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 93f414273..64544ad7d 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -78,6 +78,7 @@ require "jam_ruby/models/band_invitation" require "jam_ruby/models/band_musician" require "jam_ruby/models/connection" require "jam_ruby/models/diagnostic" +require "jam_ruby/models/latency_tester" require "jam_ruby/models/friendship" require "jam_ruby/models/active_music_session" require "jam_ruby/models/music_session_comment" diff --git a/ruby/lib/jam_ruby/connection_manager.rb b/ruby/lib/jam_ruby/connection_manager.rb index 5d4eefd2c..efd6dd12b 100644 --- a/ruby/lib/jam_ruby/connection_manager.rb +++ b/ruby/lib/jam_ruby/connection_manager.rb @@ -153,11 +153,11 @@ SQL # NOTE this is only used for testing purposes; # actual deletes will be processed in the websocket context which cleans up dependencies def expire_stale_connections() - self.stale_connection_client_ids().each { |client| self.delete_connection(client[:client_id]) } + self.stale_connection_client_ids.each { |client| self.delete_connection(client[:client_id]) } end # expiring connections in stale state, which deletes them - def stale_connection_client_ids() + def stale_connection_client_ids clients = [] ConnectionManager.active_record_transaction do |connection_manager| conn = connection_manager.pg_conn diff --git a/ruby/lib/jam_ruby/constants/validation_messages.rb b/ruby/lib/jam_ruby/constants/validation_messages.rb index 2a13a87b7..20edf028d 100644 --- a/ruby/lib/jam_ruby/constants/validation_messages.rb +++ b/ruby/lib/jam_ruby/constants/validation_messages.rb @@ -41,7 +41,7 @@ module ValidationMessages INVALID_FPFILE = "is not valid" #connection - + USER_OR_LATENCY_TESTER_PRESENT = "user or latency_tester must be present" SELECT_AT_LEAST_ONE = "Please select at least one track" # DO NOT CHANGE THIS TEXT MESSAGE UNLESS YOU CHANGE createSession.js.erb, which is looking for it FAN_CAN_NOT_JOIN_AS_MUSICIAN = "A fan can not join a music session as a musician" MUSIC_SESSION_MUST_BE_SPECIFIED = "A music session must be specified" diff --git a/ruby/lib/jam_ruby/models/connection.rb b/ruby/lib/jam_ruby/models/connection.rb index 82236b295..2fb66d0ae 100644 --- a/ruby/lib/jam_ruby/models/connection.rb +++ b/ruby/lib/jam_ruby/models/connection.rb @@ -6,6 +6,7 @@ module JamRuby # client_types TYPE_CLIENT = 'client' TYPE_BROWSER = 'browser' + TYPE_LATENCY_TESTER = 'latency_tester' attr_accessor :joining_session @@ -13,11 +14,14 @@ module JamRuby belongs_to :user, :class_name => "JamRuby::User" belongs_to :music_session, :class_name => "JamRuby::ActiveMusicSession", foreign_key: :music_session_id + has_one :latency_tester, class_name: 'JamRuby::LatencyTester', foreign_key: :client_id, primary_key: :client_id has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all validates :as_musician, :inclusion => {:in => [true, false]} - validates :client_type, :inclusion => {:in => [TYPE_CLIENT, TYPE_BROWSER]} + validates :client_type, :inclusion => {:in => [TYPE_CLIENT, TYPE_BROWSER, TYPE_LATENCY_TESTER]} validate :can_join_music_session, :if => :joining_session? + validate :user_or_latency_tester_present + after_save :require_at_least_one_track_when_in_session, :if => :joining_session? after_create :did_create after_save :report_add_participant @@ -190,5 +194,11 @@ module JamRuby end end + def user_or_latency_tester_present + if user.nil? && client_type != TYPE_LATENCY_TESTER + errors.add(:connection, ValidationMessages::USER_OR_LATENCY_TESTER_PRESENT) + end + end + end end diff --git a/ruby/lib/jam_ruby/models/latency_tester.rb b/ruby/lib/jam_ruby/models/latency_tester.rb new file mode 100644 index 000000000..3b254e5d3 --- /dev/null +++ b/ruby/lib/jam_ruby/models/latency_tester.rb @@ -0,0 +1,67 @@ +module JamRuby + class LatencyTester < ActiveRecord::Base + + belongs_to :connection, class_name: 'JamRuby::Connection', foreign_key: :client_id, primary_key: :client_id + + + # we need to find that latency_tester with the specified connection (and reconnect it) + # or bootstrap a new latency_tester + def self.connect(options) + client_id = options[:client_id] + ip_address = options[:ip_address] + connection_stale_time = options[:connection_stale_time] + connection_expire_time = options[:connection_expire_time] + # first try to find a LatencyTester with that client_id + latency_tester = LatencyTester.find_by_client_id(client_id) + + if latency_tester + if latency_tester.connection + connection = latency_tester.connection + else + connection = Connection.new + connection.client_id = client_id + latency_tester.connection = connection + end + else + latency_tester = LatencyTester.new + unless latency_tester.save + return latency_tester + end + connection = Connection.new + connection.latency_tester = latency_tester + connection.client_id = client_id + end + + if ip_address and !ip_address.eql?(connection.ip_address) + # locidispid stuff + addr = JamIsp.ip_to_num(ip_address) + isp = JamIsp.lookup(addr) + if isp.nil? then ispid = 0 else ispid = isp.coid end + block = GeoIpBlocks.lookup(addr) + if block.nil? then locid = 0 else locid = block.locid end + location = GeoIpLocations.lookup(locid) + if location.nil? + # todo what's a better default location? + locidispid = 0 + else + locidispid = locid*1000000+ispid + end + + connection.ip_address = ip_address + connection.addr = addr + connection.locidispid = locidispid + end + + connection.client_type = 'latency_tester' + connection.aasm_state = Connection::CONNECT_STATE.to_s + connection.stale_time = connection_stale_time + connection.expire_time = connection_expire_time + connection.as_musician = false + unless connection.save + return connection + end + + return latency_tester + end + end +end diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 0d6dd9e16..ab776bce5 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -112,6 +112,7 @@ FactoryGirl.define do addr 0 locidispid 0 client_type 'client' + association :user, factory: :user end factory :invitation, :class => JamRuby::Invitation do @@ -464,4 +465,8 @@ FactoryGirl.define do creator JamRuby::Diagnostic::CLIENT data Faker::Lorem.sentence end + + factory :latency_tester, :class => JamRuby::LatencyTester do + association :connection + end end diff --git a/ruby/spec/jam_ruby/models/latency_tester_spec.rb b/ruby/spec/jam_ruby/models/latency_tester_spec.rb new file mode 100644 index 000000000..901072fd2 --- /dev/null +++ b/ruby/spec/jam_ruby/models/latency_tester_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe LatencyTester do + + let(:params) {{client_id: 'abc', ip_address: '10.1.1.1', connection_stale_time:40, connection_expire_time:60} } + + it "success" do + latency_tester = FactoryGirl.create(:latency_tester) + latency_tester.connection.should_not be_nil + latency_tester.connection.latency_tester.should_not be_nil + + end + + describe "connect" do + it "no existing latency tester" do + latency_tester = LatencyTester.connect(params) + latency_tester.errors.any?.should be_false + latency_tester.connection.ip_address.should == params[:ip_address] + latency_tester.connection.stale_time.should == params[:connection_stale_time] + latency_tester.connection.expire_time.should == params[:connection_expire_time] + end + + it "existing latency tester, no connection" do + latency_tester = FactoryGirl.create(:latency_tester, connection: nil) + latency_tester.connection.should be_nil + + latency_tester.client_id = params[:client_id ] + latency_tester.save! + + found = LatencyTester.connect(params) + found.should == latency_tester + found.connection.should_not be_nil + found.connection.aasm_state.should == Connection::CONNECT_STATE.to_s + found.connection.client_id.should == latency_tester.client_id + end + + it "existing latency tester, existing connection" do + latency_tester = FactoryGirl.create(:latency_tester) + + latency_tester.connection.aasm_state = Connection::STALE_STATE.to_s + latency_tester.save! + set_updated_at(latency_tester.connection, 1.days.ago) + + params[:client_id] = latency_tester.connection.client_id + + found = LatencyTester.connect(params) + found.should == latency_tester + found.connection.should == latency_tester.connection + # state should have refreshed from stale to connected + found.connection.aasm_state.should == Connection::CONNECT_STATE.to_s + # updated_at needs to be poked on connection to keep stale non-stale + (found.connection.updated_at - latency_tester.connection.updated_at).to_i.should == 60 * 60 * 24 # 1 day + end + end +end diff --git a/ruby/spec/spec_db.rb b/ruby/spec/spec_db.rb index b825973f5..184b6f4c1 100644 --- a/ruby/spec/spec_db.rb +++ b/ruby/spec/spec_db.rb @@ -6,7 +6,13 @@ class SpecDb def self.recreate_database conn = PG::Connection.open("dbname=postgres user=postgres password=postgres host=localhost") conn.exec("DROP DATABASE IF EXISTS #{TEST_DB_NAME}") - conn.exec("CREATE DATABASE #{TEST_DB_NAME}") + if ENV['TABLESPACE'] + conn.exec("CREATE DATABASE #{TEST_DB_NAME} WITH TABLESPACE=#{ENV['TABLESPACE']}") + else + conn.exec("CREATE DATABASE #{TEST_DB_NAME}") + end + + JamDb::Migrator.new.migrate(:dbname => TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost") end end diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 1ab24989e..faaeab0b8 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -3,6 +3,7 @@ ENV["RAILS_ENV"] = "test" require 'simplecov' require 'support/utilities' +require 'support/profile' require 'active_record' require 'jam_db' require 'spec_db' diff --git a/ruby/spec/support/profile.rb b/ruby/spec/support/profile.rb new file mode 100644 index 000000000..60eab2a83 --- /dev/null +++ b/ruby/spec/support/profile.rb @@ -0,0 +1,22 @@ +if ENV['PROFILE'] + require 'ruby-prof' + RSpec.configure do |c| + def profile + result = RubyProf.profile { yield } + name = example.metadata[:full_description].downcase.gsub(/[^a-z0-9_-]/, "-").gsub(/-+/, "-") + printer = RubyProf::CallTreePrinter.new(result) + Dir.mkdir('tmp/performance') + open("tmp/performance/callgrind.#{name}.#{Time.now.to_i}.trace", "w") do |f| + printer.print(f) + end + end + + c.around(:each) do |example| + if ENV['PROFILE'] == 'all' or (example.metadata[:profile] and ENV['PROFILE']) + profile { example.run } + else + example.run + end + end + end +end diff --git a/web/app/assets/javascripts/gear/gear_wizard.js b/web/app/assets/javascripts/gear/gear_wizard.js index a1c4b382b..09d1714ab 100644 --- a/web/app/assets/javascripts/gear/gear_wizard.js +++ b/web/app/assets/javascripts/gear/gear_wizard.js @@ -9,7 +9,8 @@ var $dialog = null; var $wizardSteps = null; var $currentWizardStep = null; - var step = 0; + var step = null; + var previousStep = null; var $templateSteps = null; var $templateButtons = null; var $templateAudioPort = null; @@ -48,11 +49,21 @@ 6: stepSuccess } - function beforeHideStep($step) { - var stepInfo = STEPS[step]; + function newSession() { + context._.each(STEPS, function(stepInfo, stepNumber) { + if(stepInfo.newSession) { + stepInfo.newSession.call(stepInfo); + } + }); + } + + function beforeHideStep() { + if(!previousStep) {return} + + var stepInfo = STEPS[previousStep]; if (!stepInfo) { - throw "unknown step: " + step; + throw "unknown step: " + previousStep; } if(stepInfo.beforeHide) { @@ -73,7 +84,7 @@ function moveToStep() { var $nextWizardStep = $wizardSteps.filter($('[layout-wizard-step=' + step + ']')); - beforeHideStep($currentWizardStep); + beforeHideStep(); $wizardSteps.hide(); @@ -141,6 +152,7 @@ var newProfileName = 'FTUEAttempt-' + new Date().getTime().toString(); logger.debug("setting FTUE-prefixed profile name to: " + newProfileName); context.jamClient.FTUESetMusicProfileName(newProfileName); + newSession(); } var profileName = context.jamClient.FTUEGetMusicProfileName(); @@ -150,6 +162,8 @@ } function beforeShow(args) { + previousStep = null; + context.jamClient.FTUECancel(); context.jamClient.FTUESetStatus(false); findOrCreateFTUEProfile(); @@ -157,7 +171,7 @@ step = args.d1; if (!step) step = 0; step = parseInt(step); - moveToStep(); + moveToStep(null); } function afterShow() { @@ -165,12 +179,14 @@ } function afterHide() { + step = null; context.jamClient.FTUESetStatus(true); context.jamClient.FTUECancel(); } function back() { if ($(this).is('.button-grey')) return false; + previousStep = step; step = step - 1; moveToStep(); return false; @@ -185,6 +201,7 @@ if(!result) {return false;} } + previousStep = step; step = step + 1; moveToStep(); diff --git a/web/app/assets/javascripts/gear/step_configure_tracks.js b/web/app/assets/javascripts/gear/step_configure_tracks.js index 7a5c76915..655f98596 100644 --- a/web/app/assets/javascripts/gear/step_configure_tracks.js +++ b/web/app/assets/javascripts/gear/step_configure_tracks.js @@ -18,7 +18,6 @@ var $instrumentsHolder = null; - function loadChannels() { var musicPorts = jamClient.FTUEGetChannels(); @@ -100,7 +99,7 @@ if(track.channels.length > 0) { tracks.tracks.push(track); } - var $instrument = $instrumentsHolder.find('.icon-instrument-select[data-num="' + index + '"]'); + var $instrument = $instrumentsHolder.find('[data-num="' + index + '"]').find('.icon-instrument-select'); track.instrument_id = $instrument.data('instrument_id'); }) return tracks; @@ -140,10 +139,36 @@ context.jamClient.TrackSetAssignment(channelId, true, trackNumber); }); - context.jamClient.TrackSetInstrument(trackNumber, track.instrument_id); + logger.debug("context.jamClient.TrackSetInstrument(trackNumber, track.instrument_id)", trackNumber, track.instrument_id); + context.jamClient.TrackSetInstrument(trackNumber, context.JK.instrument_id_to_instrument[track.instrument_id].client_id); }); - context.jamClient.TrackSaveAssignments(); + var result = context.jamClient.TrackSaveAssignments(); + + if(!result || result.length == 0) { + // success + return true; + } + else { + context.JK.Banner.showAlert('Unable to save assignments. ' + result); + return false; + } + } + + function loadTrackInstruments() { + var $trackInstruments = $instrumentsHolder.find('.track-instrument'); + + context._.each($trackInstruments, function(trackInstrument) { + var $trackInstrument = $(trackInstrument); + + var trackIndex = parseInt($trackInstrument.attr('data-num')) + 1; + + var clientInstrument = context.jamClient.TrackGetInstrument(trackIndex); + + var instrument = context.JK.client_to_server_instrument_map[clientInstrument]; + + $trackInstrument.instrumentSelectorSet(instrument ? instrument.server_id : instrument); + }); } function handleNext() { @@ -153,13 +178,12 @@ return false; } - save(tracks); - - return true; + return save(tracks); } function beforeShow() { loadChannels(); + loadTrackInstruments(); } function unassignChannel($channel) { @@ -210,9 +234,9 @@ function initializeInstrumentDropdown() { var i; for(i = 0; i < MAX_TRACKS; i++) { - var $instrumentSelect = context.JK.iconInstrumentSelect(); - $instrumentSelect.attr('data-num', i); - $instrumentsHolder.append($instrumentSelect); + var $root = $('
'); + $root.instrumentSelector().attr('data-num', i); + $instrumentsHolder.append($root); } } diff --git a/web/app/assets/javascripts/gear/step_configure_voice_chat.js b/web/app/assets/javascripts/gear/step_configure_voice_chat.js index 1e9be3ad4..a967a0564 100644 --- a/web/app/assets/javascripts/gear/step_configure_voice_chat.js +++ b/web/app/assets/javascripts/gear/step_configure_voice_chat.js @@ -5,16 +5,143 @@ context.JK = context.JK || {}; context.JK.StepConfigureVoiceChat = function (app) { + var ASSIGNMENT = context.JK.ASSIGNMENT; + var VOICE_CHAT = context.JK.VOICE_CHAT; + var $step = null; + var $reuseAudioInputRadio = null; + var $useChatInputRadio = null; + var $chatInputs = null; + var $templateChatInput = null; + + function newSession() { + $reuseAudioInputRadio.attr('checked', 'checked').iCheck('check'); + } + + function isChannelAvailableForChat(chatChannelId, musicPorts) { + var result = true; + context._.each(musicPorts.input, function(inputChannel) { + // if the channel is currently assigned to a track, it not unassigned + if(inputChannel.id == chatChannelId && (inputChannel.assignment > 0)) { + result = false; + return false; // break + } + }); + + return result; + } + + function isChatEnabled() { + return $useChatInputRadio.is(':checked'); + } function beforeShow() { + if(isChatEnabled()) { + enableChat(); + } + else { + disableChat(); + } + + var musicPorts = jamClient.FTUEGetChannels(); + var chatInputs = context.jamClient.FTUEGetChatInputs(); + + $chatInputs.empty(); + + context._.each(chatInputs, function(chatChannelName, chatChannelId) { + if(isChannelAvailableForChat(chatChannelId, musicPorts)) { + var $chat = $(context._.template($templateChatInput.html(), {id: chatChannelId, name: chatChannelName}, { variable: 'data' })); + $chat.hide(); // we'll show it once it's styled with iCheck + $chatInputs.append($chat); + } + }); + + var $radioButtons = $chatInputs.find('input[name="chat-device"]'); + context.JK.checkbox($radioButtons).on('iChecked', function(e) { + var $input = $(e.currentTarget); + var channelId = $input.attr('data-channel-id'); + context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.CHAT); + var result = context.jamClient.TrackSaveAssignments(); + + if(!result || result.length == 0) { + // success + } + else { + context.JK.Banner.showAlert('Unable to save assignments. ' + result); + return false; + } + }); + + if(!isChatEnabled()) { + $radioButtons.iCheck('disable'); + } + + $chatInputs.find('.chat-input').show().on('click', function() { + if(!isChatEnabled()) { + context.JK.prodBubble($step.find('.use-chat-input h3'), 'chat-not-enabled', {}, { positions:['left']}); + } + }) + } + + function disableChat() { + logger.debug("FTUE: disabling chat"); + context.jamClient.TrackSetChatEnable(false); + var result = context.jamClient.TrackSaveAssignments(); + + if(!result || result.length == 0) { + // success + } + else { + context.JK.Banner.showAlert('Unable to disable chat. ' + result); + return false; + } + + var $radioButtons = $chatInputs.find('input[name="chat-device"]'); + $radioButtons.iCheck('disable'); + } + + function enableChat() { + logger.debug("FTUE: enabling chat"); + context.jamClient.TrackSetChatEnable(true); + var result = context.jamClient.TrackSaveAssignments(); + + if(!result || result.length == 0) { + // success + } + else { + context.JK.Banner.showAlert('Unable to enable chat. ' + result); + return false; + } + + var $radioButtons = $chatInputs.find('input[name="chat-device"]'); + $radioButtons.iCheck('enable'); + } + + function handleChatEnabledToggle() { + context.JK.checkbox($reuseAudioInputRadio); + context.JK.checkbox($useChatInputRadio); + + // plugin sets to relative on the element; have to do this as an override + $reuseAudioInputRadio.closest('.iradio_minimal').css('position', 'absolute'); + $useChatInputRadio.closest('.iradio_minimal').css('position', 'absolute'); + + $reuseAudioInputRadio.on('ifChecked', disableChat); + $useChatInputRadio.on('ifChecked', enableChat) } function initialize(_$step) { $step = _$step; + + $reuseAudioInputRadio = $step.find('.reuse-audio-input input'); + $useChatInputRadio = $step.find('.use-chat-input input'); + $chatInputs = $step.find('.chat-inputs'); + $templateChatInput = $('#template-chat-input'); + + handleChatEnabledToggle(); } + this.newSession = newSession; this.beforeShow = beforeShow; this.initialize = initialize; diff --git a/web/app/assets/javascripts/gear/step_select_gear.js b/web/app/assets/javascripts/gear/step_select_gear.js index 5a14006df..1f8049e41 100644 --- a/web/app/assets/javascripts/gear/step_select_gear.js +++ b/web/app/assets/javascripts/gear/step_select_gear.js @@ -181,11 +181,23 @@ } function selectedBufferIn() { - return parseFloat($frameSize.val()); + return parseFloat($bufferIn.val()); } function selectedBufferOut() { - return parseFloat($frameSize.val()); + return parseFloat($bufferOut.val()); + } + + function setFramesize(value) { + $frameSize.val(value); + } + + function setBufferIn(value) { + $bufferIn.val(value) + } + + function setBufferOut(value) { + $bufferOut.val(value) } function initializeNextButtonState() { @@ -633,15 +645,16 @@ function initializeASIOButtons() { $asioInputControlBtn.unbind('click').click(function () { - context.jamClient.FTUEOpenControlPanel(); // TODO: supply with ID when VRFS-1707 is done + context.jamClient.FTUEOpenControlPanel(selectedAudioInput()); }); $asioOutputControlBtn.unbind('click').click(function () { - context.jamClient.FTUEOpenControlPanel(); // TODO: supply with ID when VRFS-1707 is done + context.jamClient.FTUEOpenControlPanel(selectedAudioOutput()); }); } function initializeKnobs() { $frameSize.unbind('change').change(function () { + updateDefaultBuffers(); jamClient.FTUESetFrameSize(selectedFramesize()); }); @@ -841,6 +854,46 @@ else { $resyncBtn.css('visibility', 'hidden'); } + + updateDefaultFrameSize(); + + updateDefaultBuffers(); + } + + function updateDefaultFrameSize() { + if(selectedDeviceInfo.input.type == 'Win32_wdm' || selectedDeviceInfo.output.type == 'Win32_wdm') { + setFramesize('10'); + } + + jamClient.FTUESetFrameSize(selectedFramesize()); + } + + function updateDefaultBuffers() { + + // handle specific framesize settings + if(selectedDeviceInfo.input.type == 'Win32_wdm' || selectedDeviceInfo.output.type == 'Win32_wdm') { + var framesize = selectedFramesize(); + + if(framesize == 2.5) { + setBufferIn('1'); + setBufferOut('1'); + } + else if(framesize == 5) { + setBufferIn('3'); + setBufferOut('2'); + } + else { + setBufferIn('6'); + setBufferOut('5'); + } + } + else { + setBufferIn(0); + setBufferOut(0); + } + + jamClient.FTUESetInputLatency(selectedBufferIn()); + jamClient.FTUESetOutputLatency(selectedBufferOut()); } function processIOScore(io) { @@ -932,15 +985,22 @@ else { renderIOScore(null, null, null, 'starting', 'starting', 'starting'); var testTimeSeconds = gon.ftue_io_wait_time; // allow time for IO to establish itself - context.jamClient.FTUEStartIoPerfTest(); + var startTime = testTimeSeconds / 2; // start measuring half way through the test, to get past IO oddities renderIOScoringStarted(testTimeSeconds); renderIOCountdown(testTimeSeconds); var interval = setInterval(function () { testTimeSeconds -= 1; renderIOCountdown(testTimeSeconds); + + if(testTimeSeconds == startTime) { + logger.debug("Starting IO Perf Test starting at " + startTime + "s in") + context.jamClient.FTUEStartIoPerfTest(); + } + if (testTimeSeconds == 0) { clearInterval(interval); renderIOScoringStopped(); + logger.debug("Ending IO Perf Test at " + testTimeSeconds + "s in") var io = context.jamClient.FTUEGetIoPerfData(); lastIOScore = io; processIOScore(io); @@ -1014,6 +1074,7 @@ } function beforeHide() { + console.log("beforeHide:"); $(window).off('focus', onFocus); } diff --git a/web/app/assets/javascripts/jquery.instrumentSelector.js b/web/app/assets/javascripts/jquery.instrumentSelector.js new file mode 100644 index 000000000..8f054800a --- /dev/null +++ b/web/app/assets/javascripts/jquery.instrumentSelector.js @@ -0,0 +1,85 @@ +(function(context, $) { + + "use strict"; + + context.JK = context.JK || {}; + + + // creates an iconic/graphical instrument selector. useful when there is minimal real-estate + + $.fn.instrumentSelector = function(options) { + + return this.each(function(index) { + + function select(instrument_id) { + if(instrument_id == null) { + $currentInstrument.text('?'); + $currentInstrument.addClass('none'); + $select.data('instrument_id', null); + } + else { + $currentInstrument.empty(); + $currentInstrument.removeClass('none'); + $currentInstrument.append('