Merge branch 'feature/stats' into develop
This commit is contained in:
commit
f82f64f2db
|
|
@ -1,28 +1,27 @@
|
|||
ActiveAdmin.register JamRuby::CrashDump, :as => 'Crash Dump' do
|
||||
# Note: a lame thing is it's not obvious how to make it search on email instead of user_id.
|
||||
filter :timestamp
|
||||
filter :user_email, :as => :string
|
||||
filter :client_id
|
||||
filter :user_id
|
||||
|
||||
menu :parent => 'Misc'
|
||||
|
||||
config.sort_order = 'created_at DESC'
|
||||
|
||||
index do
|
||||
column 'User' do |oo| oo.user ? link_to(oo.user.email, oo.user.admin_url, {:title => oo.user.email}) : '' end
|
||||
column "Client Version", :client_version
|
||||
column "Client Type", :client_type
|
||||
column "Download" do |post|
|
||||
link_to 'Link', post.sign_url
|
||||
end
|
||||
column "Timestamp" do |post|
|
||||
(post.timestamp || post.created_at).strftime('%b %d %Y, %H:%M')
|
||||
end
|
||||
column "Client Type", :client_type
|
||||
column "Dump URL" do |post|
|
||||
link_to post.uri, post.uri
|
||||
column "Description" do |post|
|
||||
post.description
|
||||
end
|
||||
|
||||
column "User ID", :user_id
|
||||
|
||||
# FIXME (?): This isn't performant (though it likely doesn't matter). Could probably do a join.
|
||||
column "User Email" do |post|
|
||||
unless post.user_id.nil?
|
||||
post.user_email
|
||||
end
|
||||
end
|
||||
column "Client ID", :client_id
|
||||
actions
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ ActiveAdmin.register JamRuby::DownloadTracker, :as => 'DownloadTrackers' do
|
|||
|
||||
index do
|
||||
column 'User' do |oo| oo.user ? link_to(oo.user.email, oo.user.admin_url, {:title => oo.user.email}) : '' end
|
||||
column 'Remote IP' do |oo| oo.remote_ip end
|
||||
column 'Created' do |oo| oo.created_at end
|
||||
column 'JamTrack' do |oo| oo.jam_track end
|
||||
column 'Paid' do |oo| oo.paid end
|
||||
column 'Blacklisted?' do |oo| IpBlacklist.listed(oo.remote_ip) ? 'Yes' : 'No' end
|
||||
column 'Remote IP' do |oo| oo.remote_ip end
|
||||
column "" do |oo|
|
||||
link_to 'Blacklist This IP', "download_trackers/#{oo.id}/blacklist_by_ip"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
module JamRuby
|
||||
class CrashDump < ActiveRecord::Base
|
||||
|
||||
include JamRuby::S3ManagerMixin
|
||||
|
||||
self.table_name = "crash_dumps"
|
||||
|
||||
self.primary_key = 'id'
|
||||
|
|
@ -23,5 +25,8 @@ module JamRuby
|
|||
self.user.email
|
||||
end
|
||||
|
||||
def sign_url(expiration_time = 3600 * 24 * 7, secure=true)
|
||||
s3_manager.sign_url(self[:ri], {:expires => expiration_time, :secure => secure})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -111,10 +111,10 @@ module JamRuby
|
|||
# the idea is that this is used when a user who has the rights to this tries to download this JamTrack
|
||||
# we would verify their rights (can_download?), and generates a URL in response to the click so that they can download
|
||||
# but the url is short lived enough so that it wouldn't be easily shared
|
||||
def sign_url(expiration_time = 120, bitrate=48, secure=true)
|
||||
field_name = (bitrate==48) ? "url_48" : "url_44"
|
||||
s3_manager.sign_url(self[field_name], {:expires => expiration_time, :secure => secure})
|
||||
end
|
||||
def sign_url(expiration_time = 120, bitrate=48, secure=true)
|
||||
field_name = (bitrate==48) ? "url_48" : "url_44"
|
||||
s3_manager.sign_url(self[field_name], {:expires => expiration_time, :secure => secure})
|
||||
end
|
||||
|
||||
def delete_s3_files
|
||||
remove_url_48!
|
||||
|
|
|
|||
|
|
@ -138,7 +138,8 @@
|
|||
context.VideoActions.videoWindowClosed()
|
||||
}
|
||||
else if (type === ALERT_NAMES.VST_CHANGED) {
|
||||
context.ConfigureTracksActions.onVstChanged()
|
||||
console.log("VST CHANGED!")
|
||||
context.ConfigureTracksActions.vstChanged()
|
||||
}
|
||||
else if((!context.JK.CurrentSessionModel || !context.JK.CurrentSessionModel.inSession()) &&
|
||||
(ALERT_NAMES.INPUT_IO_RATE == type || ALERT_NAMES.INPUT_IO_JTR == type || ALERT_NAMES.OUTPUT_IO_RATE == type || ALERT_NAMES.OUTPUT_IO_JTR== type)) {
|
||||
|
|
|
|||
|
|
@ -22,136 +22,7 @@
|
|||
var $instrumentsHolder = null;
|
||||
var isDragging = false;
|
||||
|
||||
function removeHoverer($hoverChannel) {
|
||||
var $channel = $hoverChannel.data('original')
|
||||
$channel.data('cloned', null);
|
||||
$hoverChannel.remove();
|
||||
}
|
||||
|
||||
function hoverIn($channel) {
|
||||
if(isDragging) return;
|
||||
|
||||
var $container = $channel.closest('.target');
|
||||
var inTarget = $container.length > 0;
|
||||
if(!inTarget) {
|
||||
$container = $channel.closest('.channels-holder')
|
||||
}
|
||||
|
||||
var $inputs = $container.find('.ftue-input');
|
||||
|
||||
var index = $inputs.index($channel);
|
||||
// $channel.css('padding', '0 5px');
|
||||
if(inTarget) {
|
||||
$channel.data('container', $container)
|
||||
$channel.addClass('hovering');
|
||||
$channel.css('color', 'white')
|
||||
$channel.css('background-color', '#333');
|
||||
$channel.css('border', '#333');
|
||||
$channel.css('border-radius', '2px');
|
||||
$channel.css('min-width', '49%');
|
||||
$channel.css('width', 'auto');
|
||||
$channel.css('position', 'absolute');
|
||||
$container.css('overflow', 'visible');
|
||||
}
|
||||
else {
|
||||
var $offsetParent = $channel.offsetParent();
|
||||
var parentOffset = $offsetParent.offset();
|
||||
|
||||
var hoverChannel = $(context._.template($templateAssignablePort.html(), {id: 'bogus', name: $channel.text(), direction: 'bogus'}, { variable: 'data' }));
|
||||
hoverChannel
|
||||
.css('position', 'absolute')
|
||||
.css('color', 'white')
|
||||
.css('left', $channel.position().left)
|
||||
.css('top', $channel.position().top)
|
||||
.css('background-color', '#333')
|
||||
.css('min-width', $channel.width())
|
||||
.css('min-height', $channel.height())
|
||||
.css('z-index', 10000)
|
||||
.css('background-position', '4px 6px')
|
||||
.css('padding-left', '14px')
|
||||
.data('original', $channel);
|
||||
|
||||
$channel.data('cloned', hoverChannel);
|
||||
hoverChannel
|
||||
.hover(function(e) {
|
||||
var hoverCheckTimeout = hoverChannel.data('hoverCheckTimeout');
|
||||
if(hoverCheckTimeout) {
|
||||
clearTimeout(hoverCheckTimeout);
|
||||
hoverChannel.data('hoverCheckTimeout', null);
|
||||
}
|
||||
}, function() { removeHoverer($(this)); })
|
||||
.mousedown(function(e) {
|
||||
// because we have obscured the element the user wants to drag,
|
||||
// we proxy a mousedown on the hover-element to the covered .ftue-input ($channel).
|
||||
// this causes jquery.drag to get going even though the user clicked a different element
|
||||
$channel.trigger(e)
|
||||
})
|
||||
hoverChannel.data('hoverCheckTimeout', setTimeout(function() {
|
||||
// check if element has already been left
|
||||
hoverChannel.data('hoverCheckTimeout', null);
|
||||
removeHoverer(hoverChannel);
|
||||
}, 500));
|
||||
hoverChannel.prependTo($offsetParent);
|
||||
}
|
||||
|
||||
$channel.css('z-index', 10000)
|
||||
if(inTarget && $inputs.length == 2) {
|
||||
|
||||
if(index == 0) {
|
||||
$channel.css('right', '50%')
|
||||
}
|
||||
else {
|
||||
$channel.css('left', '51%')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function hoverOut($channel) {
|
||||
|
||||
var $cloned = $channel.data('cloned');
|
||||
if($cloned) {
|
||||
return; // let the cloned handle the rest of hover out logic when it's hovered-out
|
||||
}
|
||||
|
||||
$channel
|
||||
.removeClass('hovering')
|
||||
.css('color', '')
|
||||
.css('font-weight', '')
|
||||
.css('position', '')
|
||||
.css('width', '')
|
||||
.css('background-color', '')
|
||||
.css('padding', '')
|
||||
.css('padding-left', '')
|
||||
.css('background-position', '')
|
||||
.css('border', '')
|
||||
.css('border-radius', '')
|
||||
.css('right', '')
|
||||
.css('left', '')
|
||||
.css('min-width', '')
|
||||
.css('z-index', '')
|
||||
.css('margin-left', '')
|
||||
.css('max-width', 'auto');
|
||||
|
||||
//var $container = $channel.closest('.target');
|
||||
var $container = $channel.data('container');
|
||||
if($container) {
|
||||
$container.css('overflow', '')
|
||||
}
|
||||
}
|
||||
|
||||
function fixClone($clone) {
|
||||
$clone
|
||||
.css('color', '')
|
||||
.css('font-weight', '')
|
||||
.css('width', 'auto')
|
||||
.css('background-color', '')
|
||||
.css('padding', '')
|
||||
.css('border', '')
|
||||
.css('border-radius', '')
|
||||
.css('right', '')
|
||||
.css('min-width', '')
|
||||
}
|
||||
|
||||
// inputChannelFilter is an optional argument that is used by the Gear Wizard.
|
||||
// basically, if an input channel isn't in there, it's not going to be displayed
|
||||
|
|
@ -176,11 +47,6 @@
|
|||
|
||||
var $channel = $(context._.template($templateAssignablePort.html(), $.extend({}, inputChannel, {direction:'in'}), { variable: 'data' }));
|
||||
|
||||
$channel.hover(
|
||||
function() { hoverIn ($(this)) },
|
||||
function() { hoverOut($(this)) }
|
||||
);
|
||||
|
||||
if(forceInputsToUnassign || inputChannel.assignment == ASSIGNMENT.UNASSIGNED) {
|
||||
unassignInputChannel($channel);
|
||||
}
|
||||
|
|
@ -205,10 +71,7 @@
|
|||
context._.each(outputChannels, function (outputChannel, index) {
|
||||
var $channel = $(context._.template($templateAssignablePort.html(), $.extend({}, outputChannel, {direction:'out'}), { variable: 'data' }));
|
||||
|
||||
$channel.hover(
|
||||
function() { hoverIn ($(this)) },
|
||||
function() { hoverOut($(this)) }
|
||||
);
|
||||
|
||||
|
||||
if(outputChannel.assignment == ASSIGNMENT.UNASSIGNED) {
|
||||
unassignOutputChannel($channel);
|
||||
|
|
@ -223,29 +86,6 @@
|
|||
}
|
||||
addChannelToOutput($channel, $output.find('.output-target'));
|
||||
}
|
||||
|
||||
$channel.draggable({
|
||||
helper: 'clone',
|
||||
start: function(e,ui) {
|
||||
isDragging = true;
|
||||
var $channel = $(this);
|
||||
fixClone(ui.helper);
|
||||
var $output = $channel.closest('.output-target');
|
||||
var isUnassigned = $output.length == 0;
|
||||
if(isUnassigned) {
|
||||
$outputChannelHolder.find('.output-target').addClass('possible-target');
|
||||
}
|
||||
else {
|
||||
$outputChannelHolder.find('.output-target').addClass('possible-target');
|
||||
$unassignedOutputsHolder.addClass('possible-target');
|
||||
}
|
||||
},
|
||||
stop: function() {
|
||||
isDragging = false;
|
||||
$outputChannelHolder.find('.output-target').removeClass('possible-target');
|
||||
$unassignedOutputsHolder.removeClass('possible-target')
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -439,112 +279,9 @@
|
|||
$originallyAssignedTrack.attr('output-count', $originallyAssignedTrack.find('.ftue-input:not(.ui-draggable-dragging)').length)
|
||||
}
|
||||
|
||||
|
||||
function initializeUnassignedOutputDroppable() {
|
||||
$unassignedOutputsHolder.droppable(
|
||||
{
|
||||
accept: '.ftue-input[data-direction="out"]',
|
||||
activeClass: 'drag-in-progress',
|
||||
hoverClass: 'drag-hovering',
|
||||
drop: function( event, ui ) {
|
||||
var $channel = ui.draggable;
|
||||
|
||||
//$channel.css('left', '0').css('top', '0');
|
||||
unassignOutputChannel($channel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initializeUnassignedInputDroppable() {
|
||||
$unassignedInputsHolder.droppable(
|
||||
{
|
||||
accept: '.ftue-input[data-direction="in"]',
|
||||
activeClass: 'drag-in-progress',
|
||||
hoverClass: 'drag-hovering',
|
||||
drop: function( event, ui ) {
|
||||
var $channel = ui.draggable;
|
||||
//$channel.css('left', '0').css('top', '0');
|
||||
unassignInputChannel($channel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initializeOutputDroppables() {
|
||||
var i;
|
||||
for(i = 0; i < MAX_OUTPUTS; i++) {
|
||||
var $target = $(context._.template($templateOutputTarget.html(), {num: i }, { variable: 'data' }));
|
||||
$outputChannelHolder.append($target);
|
||||
$target.find('.output-target').droppable(
|
||||
{
|
||||
accept: '.ftue-input[data-direction="out"]',
|
||||
activeClass: 'drag-in-progress',
|
||||
hoverClass: 'drag-hovering',
|
||||
drop: function( event, ui ) {
|
||||
var $slot = $(this);
|
||||
if($slot.attr('output-count') == 1) {
|
||||
return false; // max of 1 output per slot
|
||||
}
|
||||
var $channel = ui.draggable;
|
||||
//$channel.css('left', '0').css('top', '0');
|
||||
addChannelToOutput($channel, $slot);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTrackDroppables() {
|
||||
var i;
|
||||
for(i = 0; i < MAX_TRACKS; i++) {
|
||||
var $target = $(context._.template($templateTrackTarget.html(), {num: i }, { variable: 'data' }));
|
||||
$tracksHolder.append($target);
|
||||
$target.find('.track-target').droppable(
|
||||
{
|
||||
accept: '.ftue-input[data-direction="in"]',
|
||||
activeClass: 'drag-in-progress',
|
||||
hoverClass: 'drag-hovering',
|
||||
drop: function( event, ui ) {
|
||||
var $track = $(this);
|
||||
if($track.attr('track-count') == 2) {
|
||||
return false; // max of 2 inputs per track
|
||||
}
|
||||
|
||||
var $channel = ui.draggable;
|
||||
//$channel.css('left', '0').css('top', '0');
|
||||
addChannelToTrack($channel, $track);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initializeInstrumentDropdown() {
|
||||
var i;
|
||||
for(i = 0; i < MAX_TRACKS; i++) {
|
||||
var $root = $('<div class="track-instrument"></div>');
|
||||
$root.instrumentSelector().attr('data-num', i);
|
||||
$instrumentsHolder.append($root);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function initialize(_$parent) {
|
||||
$parent = _$parent;
|
||||
|
||||
$templateAssignablePort = $('#template-assignable-port');
|
||||
$templateTrackTarget = $('#template-track-target');
|
||||
$templateOutputTarget = $('#template-output-target');
|
||||
$unassignedInputsHolder = $parent.find('.unassigned-input-channels')
|
||||
$unassignedOutputsHolder = $parent.find('.unassigned-output-channels');
|
||||
$tracksHolder = $parent.find('.tracks');
|
||||
$instrumentsHolder = $parent.find('.instruments');
|
||||
$outputChannelHolder = $parent.find('.output-channels');
|
||||
|
||||
|
||||
initializeUnassignedInputDroppable();
|
||||
initializeTrackDroppables();
|
||||
initializeInstrumentDropdown();
|
||||
|
||||
initializeUnassignedOutputDroppable();
|
||||
initializeOutputDroppables();
|
||||
}
|
||||
|
||||
this.initialize = initialize;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
function setInstructions(type) {
|
||||
if (type === 'audio') {
|
||||
$instructions.html('Choose your audio device. Drag and drop to assign input ports to tracks, and specify the instrument for each track. Drag and drop to assign a pair of output ports for session stereo audio monitoring.')
|
||||
$instructions.html("Click the 'ADD LIVE TRACK' button to add more tracks. You may set up a live track for each instrumental and/or vocal part to perform in sessions. You must also set up exactly two Session Audio Output ports to deliver the stereo audio in your sessions.")
|
||||
return;
|
||||
var os = context.jamClient.GetOSAsString();
|
||||
$instructions.html(configure_audio_instructions[os]);
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
}
|
||||
|
||||
function validateAudioSettings() {
|
||||
return configureTracksHelper.trySave();
|
||||
return true;
|
||||
}
|
||||
|
||||
function showVoiceChatPanel() {
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
$musicAudioTabSelector.click(function () {
|
||||
// validate voice chat settings
|
||||
if (validateVoiceChatSettings()) {
|
||||
configureTracksHelper.reset();
|
||||
window.ConfigureTracksActions.reset(false);
|
||||
voiceChatHelper.reset();
|
||||
showMusicAudioPanel();
|
||||
}
|
||||
|
|
@ -113,7 +113,7 @@
|
|||
// validate audio settings
|
||||
if (validateAudioSettings()) {
|
||||
logger.debug("initializing voice chat helper")
|
||||
configureTracksHelper.reset();
|
||||
window.ConfigureTracksActions.reset(false);
|
||||
voiceChatHelper.reset();
|
||||
showVoiceChatPanel();
|
||||
}
|
||||
|
|
@ -133,7 +133,7 @@
|
|||
//});
|
||||
|
||||
$btnUpdateTrackSettings.click(function() {
|
||||
if(configureTracksHelper.trySave() && voiceChatHelper.trySave()) {
|
||||
if(voiceChatHelper.trySave()) {
|
||||
app.layout.closeDialog('configure-tracks');
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +152,7 @@
|
|||
});
|
||||
$certifiedAudioProfile.html(optionsHtml);
|
||||
|
||||
context.JK.dropdown($certifiedAudioProfile);
|
||||
//context.JK.dropdown($certifiedAudioProfile);
|
||||
}
|
||||
|
||||
function deviceChanged() {
|
||||
|
|
@ -183,7 +183,7 @@
|
|||
|
||||
currentProfile = profile;
|
||||
|
||||
configureTracksHelper.reset();
|
||||
window.ConfigureTracksActions.reset(false);
|
||||
}
|
||||
|
||||
function beforeShow() {
|
||||
|
|
@ -207,13 +207,16 @@
|
|||
return;
|
||||
}
|
||||
|
||||
configureTracksHelper.reset();
|
||||
window.ConfigureTracksActions.reset(false);
|
||||
voiceChatHelper.reset();
|
||||
voiceChatHelper.beforeShow();
|
||||
}
|
||||
|
||||
function afterShow() {
|
||||
sessionUtils.SessionPageEnter();
|
||||
|
||||
//context.ConfigureTracksActions.vstScan();
|
||||
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
|
|
@ -247,8 +250,8 @@
|
|||
$btnAddNewGear = $dialog.find('.btn-add-new-audio-gear');
|
||||
$btnUpdateTrackSettings = $dialog.find('.btn-update-settings');
|
||||
|
||||
configureTracksHelper = new context.JK.ConfigureTracksHelper(app);
|
||||
configureTracksHelper.initialize($dialog);
|
||||
//configureTracksHelper = new context.JK.ConfigureTracksHelper(app);
|
||||
//configureTracksHelper.initialize($dialog);
|
||||
|
||||
voiceChatHelper = new context.JK.VoiceChatHelper(app);
|
||||
voiceChatHelper.initialize($dialog, 'configure_track_dialog', true, {vuType: "vertical", lightCount: 10, lightWidth: 3, lightHeight: 17}, 191);
|
||||
|
|
|
|||
|
|
@ -1052,6 +1052,16 @@
|
|||
function GetAutoStart() { return true; }
|
||||
function SaveSettings() {}
|
||||
|
||||
|
||||
function VSTScan(callback) {setTimeout(eval(callback+ "()"), 1000)}
|
||||
function hasVstHost() { return false;}
|
||||
function getPluginList() { return {vsts:[]} }
|
||||
|
||||
function clearPluginList() {}
|
||||
function listTrackAssignments() {
|
||||
return {}
|
||||
}
|
||||
|
||||
// Javascript Bridge seems to camel-case
|
||||
// Set the instance functions:
|
||||
this.AbortRecording = AbortRecording;
|
||||
|
|
@ -1315,6 +1325,11 @@
|
|||
this.StopNetworkTest = StopNetworkTest;
|
||||
this.log = log;
|
||||
this.getOperatingMode = getOperatingMode;
|
||||
this.VSTScan = VSTScan;
|
||||
this.hasVstHost = hasVstHost;
|
||||
this.getPluginList = getPluginList;
|
||||
this.clearPluginList = clearPluginList;
|
||||
this.listTrackAssignments = listTrackAssignments;
|
||||
this.clientID = "devtester";
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,9 @@
|
|||
METRONOME_PLAYBACK_MODE_SELECTED: 'metronome_playback_mode_selected',
|
||||
CHECKOUT_SIGNED_IN: 'checkout_signed_in',
|
||||
CHECKOUT_SKIP_SIGN_IN: 'checkout_skip_sign_in',
|
||||
PREVIEW_PLAYED: 'preview_played'
|
||||
PREVIEW_PLAYED: 'preview_played',
|
||||
VST_OPERATION_SELECTED: 'vst_operation_selected',
|
||||
VST_EFFECT_SELECTED: 'vst_effect_selected'
|
||||
};
|
||||
|
||||
context.JK.PLAYBACK_MONITOR_MODE = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
(function(context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
|
||||
|
||||
// creates an iconic/graphical instrument selector. useful when there is minimal real-estate
|
||||
|
||||
$.fn.manageVsts = function(options) {
|
||||
|
||||
return this.each(function(index) {
|
||||
|
||||
function close() {
|
||||
$parent.btOff();
|
||||
$parent.focus();
|
||||
}
|
||||
|
||||
var $parent = $(this);
|
||||
|
||||
function onManageVstSelected() {
|
||||
var $li = $(this);
|
||||
var vstOperation = $li.attr('data-manage-vst-option');
|
||||
|
||||
close();
|
||||
$parent.triggerHandler(context.JK.EVENTS.VST_OPERATION_SELECTED, {vstOperation: vstOperation});
|
||||
return false;
|
||||
};
|
||||
|
||||
// if the user goes into the bubble, remove
|
||||
function waitForBubbleHover($bubble) {
|
||||
$bubble.hoverIntent({
|
||||
over: function() {
|
||||
if(timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
},
|
||||
out: function() {
|
||||
$parent.btOff();
|
||||
}});
|
||||
}
|
||||
|
||||
var timeout = null;
|
||||
|
||||
context.JK.hoverBubble($parent, $('#template-manage-vsts').html(), {
|
||||
trigger:'none',
|
||||
cssClass: 'manage-vsts-popup',
|
||||
spikeGirth:0,
|
||||
spikeLength:0,
|
||||
width:190,
|
||||
closeWhenOthersOpen: true,
|
||||
offsetParent: $parent.closest('.dialog'),
|
||||
positions:['bottom'],
|
||||
preShow: function() {
|
||||
},
|
||||
postShow:function(container) {
|
||||
$(container).find('li').click(onManageVstSelected)
|
||||
if(timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
waitForBubbleHover($(container))
|
||||
timeout = setTimeout(function() {$parent.btOff()}, 3000)
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
})(window, jQuery);
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
(function(context, $) {
|
||||
|
||||
"use strict";
|
||||
|
||||
context.JK = context.JK || {};
|
||||
|
||||
|
||||
// creates an iconic/graphical instrument selector. useful when there is minimal real-estate
|
||||
|
||||
$.fn.trackEffects = function(options) {
|
||||
|
||||
return this.each(function(index) {
|
||||
|
||||
function close() {
|
||||
$parent.btOff();
|
||||
$parent.focus();
|
||||
}
|
||||
|
||||
var $parent = $(this);
|
||||
|
||||
function onOptionSelected() {
|
||||
var $li = $(this);
|
||||
var vstOperation = $li.attr('data-manage-vst-option');
|
||||
|
||||
close();
|
||||
$parent.triggerHandler(context.JK.EVENTS.VST_EFFECT_SELECTED, {vstOperation: vstOperation});
|
||||
return false;
|
||||
};
|
||||
|
||||
// if the user goes into the bubble, remove
|
||||
function waitForBubbleHover($bubble) {
|
||||
$bubble.hoverIntent({
|
||||
over: function() {
|
||||
if(timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
},
|
||||
out: function() {
|
||||
$parent.btOff();
|
||||
}});
|
||||
}
|
||||
|
||||
var timeout = null;
|
||||
|
||||
context.JK.hoverBubble($parent, $('#template-vst-effects').html(), {
|
||||
trigger:'none',
|
||||
cssClass: 'vst-effects-popup',
|
||||
spikeGirth:0,
|
||||
spikeLength:0,
|
||||
width:220,
|
||||
closeWhenOthersOpen: true,
|
||||
offsetParent: $parent.closest('.screen'),
|
||||
positions:['bottom'],
|
||||
preShow: function() {
|
||||
|
||||
},
|
||||
postShow:function(container) {
|
||||
if (options && options['postShow']) {
|
||||
options['postShow']($(container))
|
||||
}
|
||||
$(container).find('li').click(onOptionSelected)
|
||||
if(timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
waitForBubbleHover($(container))
|
||||
timeout = setTimeout(function() {$parent.btOff()}, 3000)
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
})(window, jQuery);
|
||||
|
|
@ -7,7 +7,9 @@
|
|||
//= require ./react-components/stores/RecordingStore
|
||||
//= require ./react-components/stores/VideoStore
|
||||
//= require ./react-components/stores/SessionStore
|
||||
//= require ./react-components/stores/SessionStatsStore
|
||||
//= require ./react-components/stores/MixerStore
|
||||
//= require ./react-components/stores/ConfigureTracksStore
|
||||
//= require ./react-components/stores/JamTrackStore
|
||||
//= require ./react-components/stores/SessionNotificationStore
|
||||
//= require ./react-components/stores/MediaPlaybackStore
|
||||
|
|
|
|||
|
|
@ -0,0 +1,419 @@
|
|||
context = window
|
||||
ConfigureTracksStore = @ConfigureTracksStore
|
||||
@ConfigureLiveTracksDialog = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@ConfigureTracksStore,"onConfigureTracksChanged"), Reflux.listenTo(@AppStore, "onAppInit")]
|
||||
|
||||
onConfigureTracksChanged:(configureTracks) ->
|
||||
@setState({configureTracks: configureTracks})
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
getInitialState: () ->
|
||||
{configureTracks: null, midiInterface: null}
|
||||
|
||||
renderAudio: () ->
|
||||
inputOneOptions = []
|
||||
inputTwoOptions = []
|
||||
|
||||
defaultSelectionOne = `<option value="">Select an input port for this track (required)</option>`
|
||||
defaultSelectionTwo = `<option value="">Select an input port for this track (optional)</option>`
|
||||
|
||||
inputOneOptions.push(defaultSelectionOne)
|
||||
inputTwoOptions.push(defaultSelectionTwo)
|
||||
inputOneValue = ''
|
||||
inputTwoValue = ''
|
||||
selectedInstrument = ''
|
||||
selectedVst = 'NONE'
|
||||
|
||||
|
||||
instruments = []
|
||||
instruments.push(`<option value="">Select the instrument for this track</option>`)
|
||||
for displayName, value of context.JK.server_to_client_instrument_map
|
||||
instruments.push(`<option value={value.server_id}>{displayName}</option>`)
|
||||
|
||||
vsts = []
|
||||
|
||||
instrumentDisabled = true
|
||||
vstDisabled = true
|
||||
|
||||
|
||||
if @state.configureTracks?
|
||||
|
||||
if @state.configureTracks.scanningVsts
|
||||
scan =
|
||||
`<div className="vstScan">
|
||||
<div className="spinner-small"></div><span>Scanning your system<br/>for VST & AU plug-ins...</span>
|
||||
</div>`
|
||||
|
||||
selectedInstrument = @state.configureTracks.editingTrack.instrument_id if @state.configureTracks.editingTrack.instrument_id?
|
||||
|
||||
if @state.configureTracks.editingTrack.length == 1
|
||||
input = @state.configureTracks.editingTrack[0]
|
||||
if input.number == 0
|
||||
inputOneValue = input.id
|
||||
else
|
||||
inputTwoValue = input.id
|
||||
|
||||
if @state.configureTracks.editingTrack.length > 1
|
||||
inputOneValue = @state.configureTracks.editingTrack[0].id
|
||||
inputTwoValue = @state.configureTracks.editingTrack[1].id
|
||||
|
||||
instrumentDisabled = @state.configureTracks.editingTrack.length == 0
|
||||
vstDisabled = @state.configureTracks.editingTrack.length == 0
|
||||
|
||||
for input in @state.configureTracks.musicPorts.inputs
|
||||
|
||||
include = false
|
||||
# we need to see that this input is unassigned, or one of the two selected
|
||||
for unassignedInputs in @state.configureTracks.trackAssignments.inputs.unassigned
|
||||
if unassignedInputs.id == input.id
|
||||
include = true
|
||||
break
|
||||
|
||||
if !include
|
||||
# not see if it's the currently edited track
|
||||
for currentInput in @state.configureTracks.editingTrack
|
||||
if currentInput.id == input.id
|
||||
include = true
|
||||
|
||||
if include
|
||||
item = `<option value={input.id}>{input.name}</option>`
|
||||
inputOneOptions.push(item)
|
||||
inputTwoOptions.push(item)
|
||||
|
||||
|
||||
for plugin in @state.configureTracks.vstPluginList.vsts
|
||||
if plugin.isInstrument == false && plugin.category == 'Effect'
|
||||
vsts.push(`<option value={plugin.file}>{plugin.name} by {plugin.manuf}</option>`)
|
||||
else if plugin.category == 'NONE'
|
||||
vsts.push(`<option value={plugin.file}>No VST/AU plugin selected</option>`)
|
||||
|
||||
if @state.configureTracks.editingTrack.vst?
|
||||
vstAssignedThisTrack = true
|
||||
selectedVst = @state.configureTracks.editingTrack.vst.file
|
||||
|
||||
vstSettingBtnClasses = classNames({'button-orange': vstAssignedThisTrack, 'button-grey': !vstAssignedThisTrack})
|
||||
`<div className="audio">
|
||||
<div className="audio-input-ports">
|
||||
<h3>Audio Input Ports</h3>
|
||||
<p>Select one or two inputs ports to assign to this track. Note that if you assign a single input port, the app will automatically duplicate this port into a stereo track.</p>
|
||||
<select className="input-one" name="input-one" onChange={this.inputChanged} value={inputOneValue}>
|
||||
{inputOneOptions}
|
||||
</select>
|
||||
<select className="input-two" name="input-two" onChange={this.inputChanged} value={inputTwoValue}>
|
||||
{inputTwoOptions}
|
||||
</select>
|
||||
</div>
|
||||
<div className="instrument-selection">
|
||||
<h3>Instrument</h3>
|
||||
<select className="instrument-pick" name="instrument" onChange={this.instrumentSelected} value={selectedInstrument} disabled={instrumentDisabled}>
|
||||
{instruments}
|
||||
</select>
|
||||
</div>
|
||||
<div className="audio-effects">
|
||||
<h3>Audio Effects (optional)</h3>
|
||||
<select className="vsts" name="vsts" onChange={this.vstsChanged} value={selectedVst} disabled={vstDisabled}>
|
||||
{vsts}
|
||||
</select>
|
||||
<a className="manage-audio-plugins" onClick={this.manageAudioPlugins}>manage audio plugins <div className="down-arrow"></div></a>
|
||||
<div className="settings-holder">
|
||||
<a onClick={this.vstSettings} className={vstSettingBtnClasses}>SETTINGS . . .</a>
|
||||
</div>
|
||||
{scan}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
renderMidi: () ->
|
||||
midiInterfaces = []
|
||||
midiInterfaces.push(`<option value="">Select a MIDI interface</option>`)
|
||||
midiInstruments = []
|
||||
|
||||
instruments = []
|
||||
for displayName, value of context.JK.server_to_client_instrument_map
|
||||
instruments.push(`<option value={value.server_id}>{displayName}</option>`)
|
||||
|
||||
selectedMidiInterface = ''
|
||||
selectedInstrument = context.JK.client_to_server_instrument_map[50].server_id # default to electric guitar
|
||||
selectedMidiInstrument = ''
|
||||
|
||||
instrumentDisabled = true
|
||||
midiInstrumentDisabled = true
|
||||
vstAssignedThisTrack = false
|
||||
|
||||
if @state.configureTracks?
|
||||
|
||||
logger.debug("current midi device: " + @state.configureTracks.editingTrack.midiDeviceIndex)
|
||||
selectedMidiInterface = @state.configureTracks.editingTrack.midiDeviceIndex
|
||||
|
||||
selectedInstrument = @state.configureTracks.editingTrack.instrument_id if @state.configureTracks.editingTrack.instrument_id?
|
||||
instrumentDisabled = !@state.midiInterface? || !selectedMidiInterface?
|
||||
midiInstrumentDisabled = !@state.midiInterface? || !selectedMidiInterface?
|
||||
midiInstrumentDisabled = false
|
||||
instrumentDisabled = false
|
||||
|
||||
if @state.configureTracks.editingTrack.vst?
|
||||
vstAssignedThisTrack = true
|
||||
selectedMidiInstrument = @state.configureTracks.editingTrack.vst.file
|
||||
vstSettingBtnClasses = classNames({'button-orange': vstAssignedThisTrack, 'button-grey': !vstAssignedThisTrack})
|
||||
|
||||
for midiDevice in @state.configureTracks.attachedMidiDevices.midiDevices
|
||||
midiInterfaces.push(`<option value={midiDevice.deviceIndex}>{midiDevice.deviceName}</option>`)
|
||||
|
||||
for plugin in @state.configureTracks.vstPluginList.vsts
|
||||
if plugin.isInstrument == true
|
||||
midiInstruments.push(`<option value={plugin.file}>{plugin.name} by {plugin.manuf}</option>`)
|
||||
else if plugin.category == 'NONE'
|
||||
midiInstruments.push(`<option value={plugin.file}>Select a VST or AU instrument</option>`)
|
||||
|
||||
`<div className="midi">
|
||||
<div className="midi-interface">
|
||||
<h3>MIDI Interface</h3>
|
||||
<select className="midi-select" name="midi-select" onChange={this.midiInterfaceChanged} value={selectedMidiInterface}>
|
||||
{midiInterfaces}
|
||||
</select>
|
||||
<a className="scan-midi" onClick={this.scanMidi}>scan for connected MIDI interfaces</a>
|
||||
</div>
|
||||
<div className="instrument-selection">
|
||||
<h3>Instrument</h3>
|
||||
<select className="instrument-pick" name="instrument" onChange={this.instrumentSelected} value={selectedInstrument} disabled={instrumentDisabled}>
|
||||
{instruments}
|
||||
</select>
|
||||
</div>
|
||||
<div className="midi-instrument">
|
||||
<h3>MIDI Instrument (VST or AU Plugin)</h3>
|
||||
<select className="vsts" name="midi-instrument" onChange={this.vstsChanged} value={selectedMidiInstrument} disabled={midiInstrumentDisabled}>
|
||||
{midiInstruments}
|
||||
</select>
|
||||
<a className="manage-audio-plugins" onClick={this.manageAudioPlugins}>manage audio plugins <div className="down-arrow"></div></a>
|
||||
<div className="settings-holder">
|
||||
<a onClick={this.vstSettings} className={vstSettingBtnClasses}>SETTING . . .</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
render: () ->
|
||||
|
||||
action = 'ADD TRACK'
|
||||
header = 'add track'
|
||||
|
||||
isAudio = !@state.configureTracks? || @state.configureTracks.trackType == 'audio'
|
||||
isMidi = !isAudio
|
||||
|
||||
if isAudio
|
||||
activeElement = @renderAudio()
|
||||
else
|
||||
activeElement = @renderMidi()
|
||||
|
||||
if !@state.configureTracks?.newTrack
|
||||
action = 'CLOSE'
|
||||
header = 'update track'
|
||||
else
|
||||
cancelBtn = `<a onClick={this.onCancel} className="button-grey">CANCEL</a>`
|
||||
|
||||
`<div>
|
||||
<div className="content-head">
|
||||
<img className="content-icon" src="/assets/content/icon_add.png" height={19} width={19}/>
|
||||
<h1>{header}</h1>
|
||||
</div>
|
||||
<div className="dialog-inner">
|
||||
<div className="track-type">
|
||||
<h3>Track Type</h3>
|
||||
<div className="track-type-option"><input type="radio" value="audio" name="track-type" checked={isAudio} /><label>Audio</label></div>
|
||||
<div className="track-type-option"><input type="radio" value="midi" name="track-type" checked={isMidi} /><label>MIDI</label></div>
|
||||
</div>
|
||||
|
||||
{activeElement}
|
||||
|
||||
<div className="actions">
|
||||
{cancelBtn}
|
||||
<a onClick={this.doClose} className="button-orange">{action}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
inputChanged: (e) ->
|
||||
$root = $(@getDOMNode())
|
||||
$select1 = $root.find('.input-one')
|
||||
$select2 = $root.find('.input-two')
|
||||
|
||||
audioInput1 = $select1.val()
|
||||
audioInput2 = $select2.val()
|
||||
|
||||
if audioInput1 == ''
|
||||
audioInput1 = null
|
||||
|
||||
if audioInput2 == ''
|
||||
audioInput2 = null
|
||||
|
||||
if audioInput1? && audioInput1 == audioInput2
|
||||
e.preventDefault()
|
||||
# TODO: tell user they can't do this
|
||||
return
|
||||
|
||||
ConfigureTracksActions.associateInputsWithTrack(audioInput1, audioInput2)
|
||||
|
||||
vstsChanged: (e) ->
|
||||
$root = $(@getDOMNode())
|
||||
$select = $root.find('select.vsts')
|
||||
vstSelected = $select.val()
|
||||
if vstSelected != 'NONE'
|
||||
vstSelected = {file: vstSelected}
|
||||
|
||||
if @state.configureTracks?.trackType == 'midi'
|
||||
@updateMidiAssociations()
|
||||
else
|
||||
ConfigureTracksActions.associateVSTWithTrack(vstSelected)
|
||||
|
||||
|
||||
@setState({midiInterface: null})
|
||||
|
||||
instrumentSelected: (e) ->
|
||||
$root = $(@getDOMNode())
|
||||
$select = $root.find('.instrument-pick')
|
||||
|
||||
instrumentId = $select.val()
|
||||
ConfigureTracksActions.associateInstrumentWithTrack(instrumentId)
|
||||
|
||||
|
||||
doClose: (e) ->
|
||||
e.preventDefault()
|
||||
# check that instrument is selected
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
$instrument = $root.find('.instrument-pick')
|
||||
|
||||
instrumentId = $instrument.val()
|
||||
|
||||
$select1 = $root.find('.input-one')
|
||||
$select2 = $root.find('.input-two')
|
||||
|
||||
audioInput1 = $select1.val()
|
||||
audioInput2 = $select2.val()
|
||||
|
||||
if audioInput1 == ''
|
||||
audioInput1 = null
|
||||
|
||||
if audioInput2 == ''
|
||||
audioInput2 = null
|
||||
|
||||
if audioInput1 == null && audioInput2 == null
|
||||
context.JK.Banner.showAlert("At least one input must be specified.")
|
||||
return
|
||||
|
||||
if instrumentId == null || instrumentId == ''
|
||||
context.JK.Banner.showAlert("Please select an instrument.")
|
||||
return
|
||||
|
||||
@app.layout.closeDialog('configure-live-tracks-dialog', false)
|
||||
|
||||
onCancel: (e) ->
|
||||
|
||||
ConfigureTracksActions.cancelEdit()
|
||||
|
||||
@app.layout.closeDialog('configure-live-tracks-dialog', true)
|
||||
|
||||
vstSettings: (e) ->
|
||||
e.preventDefault()
|
||||
ConfigureTracksActions.showVstSettings()
|
||||
|
||||
|
||||
componentDidMount: () ->
|
||||
$root = $(@getDOMNode())
|
||||
$radio = context.JK.checkbox($root.find('input[type="radio"]'))
|
||||
$radio.on("ifChanged", @trackTypeChanged);
|
||||
|
||||
componentWillUpdate: () ->
|
||||
@ignoreICheck = true
|
||||
$root = $(@getDOMNode())
|
||||
$radio = $root.find('input[type="radio"]')
|
||||
#$radio.iCheck('enable')
|
||||
$radio.iCheck('enable')
|
||||
|
||||
componentDidUpdate: () ->
|
||||
$root = $(@getDOMNode())
|
||||
$radio = $root.find('input[type="radio"]')
|
||||
$radio = context.JK.checkbox($root.find('input[type="radio"]'))
|
||||
$radio.on("ifChanged", @trackTypeChanged);
|
||||
if @state.configureTracks.editingTrack.assignment == 1
|
||||
$radio.iCheck('disable')
|
||||
else
|
||||
$radio.iCheck('enable')
|
||||
|
||||
@ignoreICheck = false
|
||||
|
||||
$manageAudioPlugins = $root.find('.manage-audio-plugins')
|
||||
|
||||
unless $manageAudioPlugins.data('initialized')
|
||||
$manageAudioPlugins.manageVsts().on(context.JK.EVENTS.VST_OPERATION_SELECTED, @vstOperation).data('initialized', true)
|
||||
|
||||
trackTypeChanged: (event) ->
|
||||
|
||||
if @ignoreICheck
|
||||
logger.debug("ignoring track type changed")
|
||||
return
|
||||
|
||||
$checkedType = $(event.target);
|
||||
value = $checkedType.val()
|
||||
logger.debug("trackTypeChanged: " + value, $checkedType)
|
||||
ConfigureTracksActions.desiredTrackType(value)
|
||||
#@setState({trackType: value})
|
||||
|
||||
vstOperation: (e, data) ->
|
||||
|
||||
if data.vstOperation == 'scan'
|
||||
ConfigureTracksActions.vstScan()
|
||||
else if data.vstOperation == 'clear'
|
||||
ConfigureTracksActions.clearVsts()
|
||||
|
||||
manageAudioPlugins: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
$manageAudioPlugins = $root.find('.manage-audio-plugins')
|
||||
$manageAudioPlugins.btOn()
|
||||
|
||||
scanMidi: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
ConfigureTracksActions.midiScan()
|
||||
|
||||
|
||||
midiInterfaceChanged: (e) ->
|
||||
|
||||
@updateMidiAssociations()
|
||||
|
||||
updateMidiAssociations: (e) ->
|
||||
$root = $(@getDOMNode())
|
||||
$select = $root.find('select.midi-select')
|
||||
midiInterface = $select.val()
|
||||
|
||||
$select = $root.find('select.vsts')
|
||||
vstSelected = $select.val()
|
||||
|
||||
logger.debug("updateMidiAssocations", vstSelected, midiInterface)
|
||||
if vstSelected != 'NONE'
|
||||
vstSelected = {file: vstSelected}
|
||||
else
|
||||
vstSelected = null
|
||||
|
||||
if midiInterface == ''
|
||||
midiInterface = null
|
||||
|
||||
midi = @state.midiInterface || midiInterface
|
||||
|
||||
if vstSelected? && midi?
|
||||
logger.debug("updating midi:#{midi} & vst: #{vstSelected.file}")
|
||||
|
||||
ConfigureTracksActions.associateVSTWithTrack(vstSelected)
|
||||
setTimeout((() =>
|
||||
ConfigureTracksActions.associateMIDIWithTrack(midi)
|
||||
), 250)
|
||||
|
||||
else if midi?
|
||||
logger.debug("updating midi:#{midiInterface}")
|
||||
ConfigureTracksActions.associateMIDIWithTrack(midiInterface)
|
||||
|
||||
@setState({midiInterface: midiInterface})
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
context = window
|
||||
ConfigureTracksStore = @ConfigureTracksStore
|
||||
@ConfigureOutputsDialog = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@ConfigureTracksStore, "onConfigureTracksChanged"), Reflux.listenTo(@AppStore, "onAppInit")]
|
||||
|
||||
onConfigureTracksChanged: (configureTracks) ->
|
||||
@setState({configureTracks: configureTracks})
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
getInitialState: () ->
|
||||
{configureTracks: null}
|
||||
|
||||
render: () ->
|
||||
|
||||
outputs = []
|
||||
outputs.push(`<option value="">Select an output port for session audio</option>`)
|
||||
|
||||
if @state.configureTracks?
|
||||
for output in @state.configureTracks.musicPorts.outputs
|
||||
outputs.push(`<option value={output.id}>{output.name}</option>`)
|
||||
|
||||
`<div>
|
||||
<div className="content-head">
|
||||
<img className="content-icon" src="/assets/shared/icon_session.png" height={24} width={24}/>
|
||||
<h1>session audio outputs</h1>
|
||||
</div>
|
||||
<div className="dialog-inner">
|
||||
<p>Select two audio output ports that will be used to deliver the stereo audio of your sessions to your headphones or monitor.</p>
|
||||
<select className="output-1" >
|
||||
{outputs}
|
||||
</select>
|
||||
<select className="output-2" >
|
||||
{outputs}
|
||||
</select>
|
||||
<div className="actions">
|
||||
<a onClick={this.onCancel} className="button-grey">CANCEL</a>
|
||||
<a onClick={this.onClose} className="button-orange">UPDATE PORTS</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
componentDidUpdate: () ->
|
||||
$root = $(@getDOMNode())
|
||||
$output1 = $root.find('.output-1')
|
||||
$output2 = $root.find('.output-2')
|
||||
|
||||
if @state.configureTracks? && @state.configureTracks.trackAssignments.outputs.assigned.length == 2
|
||||
|
||||
output1 = @state.configureTracks.trackAssignments.outputs.assigned[0].id
|
||||
output2 = @state.configureTracks.trackAssignments.outputs.assigned[1].id
|
||||
|
||||
$output1.val(output1)
|
||||
$output2.val(output2)
|
||||
|
||||
onClose: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
$output1 = $root.find('.output-1')
|
||||
$output2 = $root.find('.output-2')
|
||||
|
||||
output1 = $output1.val()
|
||||
output2 = $output2.val()
|
||||
|
||||
if output1 == null || output1 == ''
|
||||
context.JK.Banner.showAlert("Both output ports must have a selection.")
|
||||
return
|
||||
|
||||
if output2 == null || output2 == ''
|
||||
context.JK.Banner.showAlert("Both output ports must have a selection.")
|
||||
return
|
||||
|
||||
if output1? && output1 != '' && output1 == output2
|
||||
context.JK.Banner.showAlert("Both output ports can not be the same.")
|
||||
return
|
||||
|
||||
ConfigureTracksActions.updateOutputs(output1, output2)
|
||||
|
||||
@app.layout.closeDialog('configure-outputs-dialog', false)
|
||||
|
||||
onCancel: (e) ->
|
||||
|
||||
@app.layout.closeDialog('configure-outputs-dialog', true)
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
context = window
|
||||
rest = context.JK.Rest()
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
ASSIGNMENT = context.JK.ASSIGNMENT
|
||||
VOICE_CHAT = context.JK.VOICE_CHAT
|
||||
MAX_TRACKS = context.JK.MAX_TRACKS
|
||||
MAX_OUTPUTS = context.JK.MAX_OUTPUTS
|
||||
gearUtils = context.JK.GearUtils
|
||||
|
||||
@ConfigureTracks = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@ConfigureTracksStore,"onConfigureTracksChanged")]
|
||||
|
||||
getInitialState: () ->
|
||||
{configureTracks: null}
|
||||
|
||||
onConfigureTracksChanged: (configureTracks) ->
|
||||
@setState({configureTracks: configureTracks})
|
||||
|
||||
render: () ->
|
||||
|
||||
liveTracks = []
|
||||
outputs = []
|
||||
|
||||
trackAssignments = @state.configureTracks?.trackAssignments
|
||||
|
||||
if trackAssignments
|
||||
|
||||
for inputsForTrack in trackAssignments.inputs.assigned
|
||||
candidate = inputsForTrack[0]
|
||||
|
||||
inputs = []
|
||||
for input in inputsForTrack
|
||||
inputs.push(`<div className="live-input">{input.name}</div>`)
|
||||
|
||||
if !inputsForTrack.instrument_id?
|
||||
instrument = `<span className="none">?</span>`
|
||||
else
|
||||
instrument = `<span><img src={context.JK.getInstrumentIconMap24()[inputsForTrack.instrument_id].asset} /></span>`
|
||||
|
||||
trackTypeLabel = 'AUDIO'
|
||||
|
||||
vstName = 'None'
|
||||
if inputsForTrack.vst? && inputsForTrack.vst != 'NONE'
|
||||
vstName = "#{inputsForTrack.vst.name} by #{inputsForTrack.vst.manuf}"
|
||||
|
||||
liveTracks.push(
|
||||
`<div key={candidate.assignment} className="live-track">
|
||||
<div className={classNames({'input-track-info': true, one: inputsForTrack.length == 1, two: inputsForTrack.length == 2})}><span className="assignment">{candidate.assignment}:</span><span className="track-type-label">{trackTypeLabel}</span>{inputs}</div>
|
||||
<div className="plugin-info">{vstName}</div>
|
||||
<div className="plugin-instrument">{instrument}</div>
|
||||
<div className="live-track-actions">
|
||||
<a className="update-live-track" onClick={this.onUpdateLiveTrack.bind(this, inputsForTrack)}>update</a>
|
||||
<a className="delete-live-track" onClick={this.onDeleteLiveTrack.bind(this, inputsForTrack)}>delete</a>
|
||||
</div>
|
||||
</div>`)
|
||||
|
||||
for output, i in trackAssignments.outputs.assigned
|
||||
outputs.push(
|
||||
`<div key={output.id} className="output-track">
|
||||
<div className="output-track-info"><span className="assignment">{i + 1}:</span><div className="output">{output.name}</div></div>
|
||||
</div>`)
|
||||
|
||||
`<div className="ConfigureTracks">
|
||||
<select className="certified-audio-profile" style={{display:'none'}}></select>
|
||||
<div className="inputs-view">
|
||||
<div>
|
||||
<h3 className="session-audio-inputs-header">Session Audio Inputs (Live Performance Tracks)</h3>
|
||||
<h3 className="plugin-header">Plugin</h3>
|
||||
<h3 className="instrument-header">Instrument</h3>
|
||||
</div>
|
||||
<div className="live-tracks">
|
||||
{liveTracks}
|
||||
</div>
|
||||
<div className="add-track-action">
|
||||
<a onClick={this.openLiveTrackDialog} className="button-orange">ADD TRACK . . . </a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="outputs-view">
|
||||
<div>
|
||||
<h3 className="session-audio-outputs-header">Session Audio Outputs (Requires 2 Ports)</h3>
|
||||
<div className="output-tracks">
|
||||
{outputs}
|
||||
</div>
|
||||
<a onClick={this.openOutputTrackDialog} className="button-orange">UPDATE OUTPUTS . . . </a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="clearall"></div>
|
||||
</div>`
|
||||
|
||||
onUpdateLiveTrack:(liveTrack, e) ->
|
||||
e.preventDefault()
|
||||
|
||||
ConfigureTracksActions.showEditTrack(liveTrack.assignment)
|
||||
|
||||
onDeleteLiveTrack:(liveTrack, e) ->
|
||||
e.preventDefault()
|
||||
|
||||
if liveTrack.assignment == 1
|
||||
# can't delete the last assignment
|
||||
context.JK.Banner.showAlert('You can not delete the 1st audio track.')
|
||||
else
|
||||
context.JK.Banner.showYesNo({
|
||||
title: "Confirm Deletion",
|
||||
html: "Are you sure you want to delete this live track?",
|
||||
yes: =>
|
||||
ConfigureTracksActions.deleteTrack(liveTrack.assignment)
|
||||
})
|
||||
|
||||
|
||||
openLiveTrackDialog: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
ConfigureTracksActions.showAddNewTrack()
|
||||
|
||||
openOutputTrackDialog: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
ConfigureTracksActions.showEditOutputs()
|
||||
})
|
||||
|
|
@ -1,9 +1,22 @@
|
|||
context = window
|
||||
|
||||
MixerActions = @MixerActions
|
||||
ConfigureTracksActions = @ConfigureTracksActions
|
||||
|
||||
@SessionMyTrack = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@SessionStatsStore,"onStatsChanged")]
|
||||
|
||||
onStatsChanged: (stats) ->
|
||||
@setState({stats: stats[@props.clientId]})
|
||||
|
||||
getInitialState: () ->
|
||||
stats = window.SessionStatsStore.stats
|
||||
if stats?
|
||||
clientStats = stats[@props.clientId]
|
||||
else
|
||||
clientStats = null
|
||||
{stats: clientStats}
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
|
@ -42,7 +55,17 @@ MixerActions = @MixerActions
|
|||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
# <div className="track-icon-equalizer" />
|
||||
classification = @state.stats?.classification
|
||||
|
||||
if !classification?
|
||||
classification = 'unknown'
|
||||
|
||||
connectionStateClasses = { 'track-connection-state': true}
|
||||
connectionStateClasses[classification] = true
|
||||
|
||||
if @props.associatedVst?
|
||||
@equalizerSet = true
|
||||
vst = `<div className="track-icon-equalizer" />`
|
||||
|
||||
`<div className={trackClasses}>
|
||||
<div className="disabled-track-overlay" />
|
||||
|
|
@ -51,15 +74,15 @@ MixerActions = @MixerActions
|
|||
<div className="track-avatar"><img src={this.props.photoUrl}/></div>
|
||||
<div className="track-instrument"><img height="45" src={this.props.instrumentIcon} width="45" /></div>
|
||||
<div className="track-controls">
|
||||
<SessionTrackVU orientation="horizontal" lightCount={4} lightWidth={17} lightHeight={3} side="best" mixers={this.props.mixers} />
|
||||
<SessionTrackVU orientation="horizontal" lightCount={5} lightWidth={17} lightHeight={3} side="best" mixers={this.props.mixers} />
|
||||
<div className="track-buttons">
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
{vst}
|
||||
<div className={classNames(connectionStateClasses)}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
||||
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
</div>`
|
||||
|
|
@ -71,6 +94,7 @@ MixerActions = @MixerActions
|
|||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
$connectionState = $root.find('.track-connection-state')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
|
|
@ -88,6 +112,19 @@ MixerActions = @MixerActions
|
|||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.top-parent')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$connectionState,
|
||||
'SessionStatsHover',
|
||||
() =>
|
||||
{participant: {client_id: this.props.clientId, user: name: 'You', possessive: 'Your'}, }
|
||||
,
|
||||
{width:380, positions:['right', 'left'], offsetParent:$root.closest('.screen'), extraClasses: 'self'})
|
||||
|
||||
unless this.props.hasMixer
|
||||
$mute.on("mouseenter", false)
|
||||
$mute.on("mouseleave", false)
|
||||
$pan.on("mouseentere", false)
|
||||
$pan.on("mouseleave", false)
|
||||
unless this.props.hasMixer
|
||||
$mute.on("mouseenter", false)
|
||||
$mute.on("mouseleave", false)
|
||||
|
|
@ -96,6 +133,8 @@ MixerActions = @MixerActions
|
|||
|
||||
context.JK.helpBubble($root.find('.disabled-track-overlay'), 'missing-my-tracks', {}, {positions:['top'], offsetParent: $root.closest('.top-parent')})
|
||||
|
||||
@initializeVstEffects()
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
|
|
@ -116,4 +155,32 @@ MixerActions = @MixerActions
|
|||
$mute.on("mouseleave", false)
|
||||
$pan.on("mouseentere", false)
|
||||
$pan.on("mouseleave", false)
|
||||
|
||||
componentDidUpdate:() ->
|
||||
@initializeVstEffects()
|
||||
|
||||
initializeVstEffects: () ->
|
||||
$root = $(this.getDOMNode())
|
||||
$equalizer = $root.find('.track-icon-equalizer')
|
||||
if $equalizer.length > 0 && !$equalizer.data('initialized')
|
||||
logger.debug("initializing trackEffects", $equalizer)
|
||||
$equalizer.trackEffects({postShow: @prepVstEffects}).on(context.JK.EVENTS.VST_EFFECT_SELECTED, @vstOperation).data('initialized', true).click(() =>
|
||||
logger.debug("clicked!")
|
||||
$equalizer.btOn()
|
||||
)
|
||||
prepVstEffects: ($container) ->
|
||||
$container.find('.vst-name').text(@props.associatedVst?.name)
|
||||
|
||||
vstOperation: (e, data) ->
|
||||
logger.debug("track effect selection: " + data.vstOperation)
|
||||
|
||||
if !@props.associatedVst?
|
||||
logger.warn("no associated VST")
|
||||
return
|
||||
|
||||
if data.vstOperation == 'open-vst'
|
||||
ConfigureTracksActions.showVstSettings(@props.associatedVst.track)
|
||||
else
|
||||
ConfigureTracksActions.showEditTrack(@props.associatedVst.track + 1)
|
||||
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
|||
|
||||
@SessionMyTracks = React.createClass({
|
||||
|
||||
mixins: [@SessionMyTracksMixin, Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
mixins: [@SessionMyTracksMixin, Reflux.listenTo(@SessionMyTracksStore,"onInputsChanged"), Reflux.listenTo(@AppStore,"onAppInit"), Reflux.listenTo(@ConfigureTracksStore, "onConfigureTracksChanged")]
|
||||
|
||||
goToFtue: (e) ->
|
||||
e.preventDefault()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,19 @@ MixerActions = @MixerActions
|
|||
|
||||
@SessionOtherTrack = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@SessionStatsStore,"onStatsChanged")]
|
||||
|
||||
onStatsChanged: (stats) ->
|
||||
@setState({stats: stats[@props.participant.client_id]})
|
||||
|
||||
getInitialState: () ->
|
||||
stats = window.SessionStatsStore.stats
|
||||
if stats?
|
||||
clientStats = stats[@props.participant.client_id]
|
||||
else
|
||||
clientStats = null
|
||||
{stats: clientStats}
|
||||
|
||||
handleMute: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
|
|
@ -48,6 +61,14 @@ MixerActions = @MixerActions
|
|||
WebkitTransform: "rotate(#{pan}deg)"
|
||||
}
|
||||
|
||||
classification = @state.stats?.classification
|
||||
|
||||
if !classification?
|
||||
classification = 'unknown'
|
||||
|
||||
connectionStateClasses = { 'track-connection-state': true}
|
||||
connectionStateClasses[classification] = true
|
||||
|
||||
`<div className={componentClasses}>
|
||||
<div className="disabled-track-overlay" />
|
||||
<div className="session-track-contents">
|
||||
|
|
@ -59,6 +80,7 @@ MixerActions = @MixerActions
|
|||
<div className="track-buttons">
|
||||
<div className={classes} data-control="mute" data-mixer-id={muteMixerId} onClick={this.handleMute}/>
|
||||
<div className="track-icon-pan" style={panStyle}/>
|
||||
<div className={classNames(connectionStateClasses)}/>
|
||||
</div>
|
||||
<br className="clearall"/>
|
||||
</div>
|
||||
|
|
@ -77,6 +99,7 @@ MixerActions = @MixerActions
|
|||
$root = $(this.getDOMNode())
|
||||
$mute = $root.find('.track-icon-mute')
|
||||
$pan = $root.find('.track-icon-pan')
|
||||
$connectionState = $root.find('.track-connection-state')
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$mute,
|
||||
|
|
@ -96,6 +119,14 @@ MixerActions = @MixerActions
|
|||
,
|
||||
{width:331, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
context.JK.interactReactBubble(
|
||||
$connectionState,
|
||||
'SessionStatsHover',
|
||||
() =>
|
||||
{participant: this.props.participant}
|
||||
,
|
||||
{width:380, positions:['right', 'left'], offsetParent:$root.closest('.screen')})
|
||||
|
||||
unless this.props.hasMixer
|
||||
$mute.on("mouseenter", false)
|
||||
$mute.on("mouseleave", false)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
|||
|
||||
for participant in session.otherParticipants()
|
||||
|
||||
if participant.user.id == context.JK.currentUserId
|
||||
participant.user.possessive = "Your"
|
||||
else
|
||||
participant.user.possessive = participant.user.name + "'s"
|
||||
|
||||
name = participant.user.name
|
||||
tracks = []
|
||||
firstTrack = participant.tracks[0]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,217 @@
|
|||
context = window
|
||||
ChannelGroupIds = context.JK.ChannelGroupIds
|
||||
MixerActions = @MixerActions
|
||||
ptrCount = 0
|
||||
|
||||
StatsInfo = {
|
||||
system: {
|
||||
cpu: {
|
||||
good: (user, stats) -> "#{user.possessive} computer processor is not overworked by JamKazam or the your system.",
|
||||
warn: (user, stats) -> "#{user.possessive} computer processor is being heavily used. There is little spare capacity, and this means your processor may not be able to handle all of it's tasks, causing audio quality, latency, and other issues.",
|
||||
poor: (user, stats) -> "#{user.possessive} computer processor is being very heavily used. There is little spare capacity, and this means your processor may not be able to handle all of it's tasks, causing audio quality, latency, and other issues."
|
||||
}
|
||||
},
|
||||
network: {
|
||||
wifi: {
|
||||
good: (user, stats) -> "#{user.name} is using wired ethernet.",
|
||||
warn: (user, stats) -> "#{user.name} is using Wi-Fi, which will create audio quality issues and additional latency.",
|
||||
poor: (user, stats) -> "#{user.name} is using Wi-Fi, which will create audio quality issues and additional latency.",
|
||||
},
|
||||
net_bitrate: {
|
||||
good: (user, stats) -> "#{user.name} has enough bandwidth to send you a high quality audio stream.",
|
||||
warn: (user, stats) -> "#{user.name} has bandwidth to send you a degraded, but sufficient, audio stream.",
|
||||
poor: (user, stats) -> "#{user.name} has not enough bandwidth to send you a decent quality audio stream.",
|
||||
},
|
||||
ping: {
|
||||
good: (user, stats) -> "The internet connection between you and #{user.name} has very low latency.",
|
||||
warn: (user, stats) -> "The internet connection between you and #{user.name} has average latency, which may affect staying in sync.",
|
||||
poor: (user, stats) -> "The internet connection between you and #{user.name} has high latency, making it very difficult to stay in sync.",
|
||||
},
|
||||
pkt_loss: {
|
||||
good: (user, stats) -> "The internet connection between you and #{user.name} loses a small % of packets. It should not affect your audio quality.",
|
||||
warn: (user, stats) -> "The internet connection between you and #{user.name} loses a significant % of packets. This may result in periodical audio artifacts.",
|
||||
poor: (user, stats) -> "The internet connection between you and #{user.name} loses a high % of packets. This will result in frequent audio artifacts.",
|
||||
},
|
||||
audiojq_median: {
|
||||
good: (user, stats) -> "JamKazam has to maintain a only a small buffer of audio to preserve audio quality, resulting in minimal added latency.",
|
||||
warn: (user, stats) -> "JamKazam has to maintain a significant buffer of audio to preserve audio quality, resulting in potentially noticeable additional latency.",
|
||||
poor: (user, stats) -> "JamKazam has to maintain a large buffer of audio to preserve audio quality, resulting in noticeabley added latency.",
|
||||
}
|
||||
},
|
||||
audio: {
|
||||
framesize: {
|
||||
good: (user, stats) -> "#{user.possessive} gear is reading and writing audio data at a very high rate, keeping gear-added latency low.",
|
||||
warn: (user, stats) -> "#{user.possessive} gear is reading and writing audio at a average rate, causing a few milliseconds extra latency compared to a 2.5 Frame Size.",
|
||||
poor: (user, stats) -> "#{user.possessive} gear is reading and writing audio at a slow rate, causing a decent amount of latency before the internet is involved.",
|
||||
},
|
||||
latency: {
|
||||
good: (user, stats) -> "#{user.possessive} gear has a small amount of latency.",
|
||||
warn: (user, stats) -> "#{user.possessive} gear has a significant amount of latency.",
|
||||
poor: (user, stats) -> "#{user.possessive} gear has a large amount of latency, making it difficult to play in time."
|
||||
},
|
||||
input_jitter: {
|
||||
good: (user, stats) -> "#{user.possessive} gear has a small amount of input jitter, meaning it is keeping good time with JamKazam as it reads in your input signal.",
|
||||
warn: (user, stats) -> "#{user.possessive} gear has a significant amount of input jitter, meaning it might be periodically adding delay or audio artifacts to your inputs.",
|
||||
poor: (user, stats) -> "#{user.possessive} gear has a large amount of input jitter, meaning it likely adding delay and audio artifacts to your inputs.",
|
||||
},
|
||||
output_jitter: {
|
||||
good: (user, stats) -> "#{user.possessive} gear has a small amount of output jitter, meaning it is keeping good time with your JamKazam as writes out your audio output.",
|
||||
warn: (user, stats) -> "#{user.possessive} gear has a significant amount of output jitter, meaning it might be periodically adding delay and audio artifacts to your output.",
|
||||
poor: (user, stats) -> "#{user.possessive} gear has a large amount of output jitter, meaning it likely adding delay and audio artifacts to your output.",
|
||||
},
|
||||
audio_in_type: {
|
||||
good: (user, stats) -> "#{user.name} using an ideal driver type for #{user.possessive.toLowerCase()} gear.",
|
||||
warn: (user, stats) -> "#{user.name} using a problematic driver type for #{user.possessive.toLowerCase()} gear.",
|
||||
poor: (user, stats) -> "#{user.name} using a driver type considered problematic.",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SessionStatsHover = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
clientId: React.PropTypes.string
|
||||
}
|
||||
|
||||
mixins: [Reflux.listenTo(@SessionStatsStore, "onStatsChanged")]
|
||||
|
||||
hover: (type, field) ->
|
||||
logger.debug("hover! #{type} #{field}")
|
||||
@setState({hoverType: type, hoverField:field})
|
||||
|
||||
hoverOut: () ->
|
||||
logger.debug("hover out!")
|
||||
@setState({hoverType: null, hoverField: null})
|
||||
|
||||
|
||||
stat: (properties, type, name, field, value) ->
|
||||
classes = {'status-icon': true}
|
||||
classifier = properties[field + '_level']
|
||||
classes[classifier] = true
|
||||
`<div className='stat' onMouseOver={this.hover.bind(this, type, field)}><span className="title">- {name}</span><div className={classNames(classes)}></div><span className="stat-value">{value}</span></div>`
|
||||
|
||||
render: () ->
|
||||
extraInfo = 'Hover over a stat to learn more.'
|
||||
|
||||
if @state.hoverType?
|
||||
type = @state.hoverType
|
||||
field = @state.hoverField
|
||||
|
||||
extraInfo = 'No extra info for this metric.'
|
||||
|
||||
classifier = @state.stats?[type]?[field + '_level']
|
||||
|
||||
if classifier?
|
||||
info = StatsInfo[type]?[field]?[classifier](@props.participant.user, @state.stats)
|
||||
if info?
|
||||
extraInfo = info
|
||||
|
||||
# "Windows WDM-KS"
|
||||
|
||||
computerStats = []
|
||||
networkStats = []
|
||||
audioStats = []
|
||||
|
||||
network = @state.stats?.network
|
||||
system = @state.stats?.system
|
||||
audio = @state.stats?.audio
|
||||
|
||||
if system?
|
||||
if system.cpu?
|
||||
computerStats.push(@stat(system, 'system', 'Processor', 'cpu', Math.round(system.cpu) + ' %'))
|
||||
|
||||
if audio?
|
||||
if audio.audio_in_type?
|
||||
audio_type = '?'
|
||||
audio_long = audio.audio_in_type.toLowerCase()
|
||||
if audio_long.indexOf('asio') > -1
|
||||
audio_type = 'ASIO'
|
||||
else if audio_long.indexOf('wdm') > -1
|
||||
audio_type = 'WDM'
|
||||
else if audio_long.indexOf('core') > -1
|
||||
audio_type = 'CoreAudio'
|
||||
audioStats.push(@stat(audio, 'audio', 'Gear Driver', 'audio_in_type', audio_type))
|
||||
if audio.framesize?
|
||||
framesize = '?'
|
||||
if audio.framesize == 2.5
|
||||
framesize = '2.5'
|
||||
else if audio.framesize == 5
|
||||
framesize = '5'
|
||||
else if audio.framesize == 10
|
||||
framesize = '10'
|
||||
audioStats.push(@stat(audio, 'audio', 'Frame Size', 'framesize', framesize))
|
||||
if audio.latency?
|
||||
audioStats.push(@stat(audio, 'audio', 'Latency', 'latency', audio.latency.toFixed(1) + ' ms'))
|
||||
if audio.input_jitter?
|
||||
audioStats.push(@stat(audio, 'audio', 'Input Jitter', 'input_jitter', audio.input_jitter.toFixed(2)))
|
||||
if audio.output_jitter?
|
||||
audioStats.push(@stat(audio, 'audio', 'Output Jitter', 'output_jitter', audio.output_jitter.toFixed(2)))
|
||||
|
||||
networkTag = null
|
||||
if network?
|
||||
if network.net_bitrate?
|
||||
networkStats.push(@stat(network, 'network', 'Bandwidth', 'net_bitrate', Math.round(network.net_bitrate) + ' k'))
|
||||
|
||||
if network.ping?
|
||||
networkStats.push(@stat(network, 'network', 'Latency', 'ping', (network.ping / 2).toFixed(1) + ' ms'))
|
||||
#if network.jitter?
|
||||
if network.audiojq_median?
|
||||
networkStats.push(@stat(network, 'network', 'Jitter Queue', 'audiojq_median', network.audiojq_median.toFixed(1)))
|
||||
# networkStats.
|
||||
if network.pkt_loss?
|
||||
networkStats.push(@stat(network, 'network', 'Packet Loss', 'pkt_loss', network.pkt_loss.toFixed(1) + ' %'))
|
||||
|
||||
if network.wifi?
|
||||
if network.wifi
|
||||
value = 'Wi-Fi'
|
||||
else
|
||||
value = 'Ethernet'
|
||||
networkStats.push(@stat(network, 'network', 'Connectivity', 'wifi', value))
|
||||
|
||||
networkTag =
|
||||
`<div className="network-stats stats-holder">
|
||||
<h3>Internet</h3>
|
||||
{networkStats}
|
||||
</div>`
|
||||
|
||||
`<div className="stats-hover">
|
||||
<h3>Session Diagnostics & Stats: {this.props.participant.user.name}</h3>
|
||||
|
||||
<div className="stats-area">
|
||||
<div className="computer-stats stats-holder">
|
||||
<h3>Computer</h3>
|
||||
{computerStats}
|
||||
</div>
|
||||
<div className="audio-stats stats-holder">
|
||||
<h3>Audio Interface</h3>
|
||||
{audioStats}
|
||||
</div>
|
||||
{networkTag}
|
||||
</div>
|
||||
<div className="stats-info">
|
||||
{extraInfo}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
onStatsChanged: (stats) ->
|
||||
stats = window.SessionStatsStore.stats
|
||||
if stats?
|
||||
clientStats = stats[@props.participant.client_id]
|
||||
else
|
||||
clientStats = null
|
||||
@setState({stats: clientStats})
|
||||
|
||||
getInitialState: () ->
|
||||
stats = window.SessionStatsStore.stats
|
||||
if stats?
|
||||
clientStats = stats[@props.participant.client_id]
|
||||
else
|
||||
clientStats = null
|
||||
{stats: clientStats, hoverType: null, hoverField: null}
|
||||
|
||||
|
||||
closeHover: (e) ->
|
||||
e.preventDefault()
|
||||
$container = $(this.getDOMNode()).closest('.react-holder')
|
||||
$container.data('bt').btOff()
|
||||
})
|
||||
|
|
@ -19,4 +19,5 @@ context = window
|
|||
associateVSTWithTrack: {}
|
||||
associateMIDIWithTrack: {}
|
||||
desiredTrackType: {}
|
||||
vstChanged: {}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
context = window
|
||||
|
||||
@SessionStatsActions = Reflux.createActions({
|
||||
pushStats: {}
|
||||
})
|
||||
|
|
@ -4,9 +4,21 @@ context = window
|
|||
|
||||
onInputsChanged: (sessionMixers) ->
|
||||
|
||||
@sessionMixers = sessionMixers
|
||||
|
||||
session = sessionMixers.session
|
||||
mixers = sessionMixers.mixers
|
||||
@recompute()
|
||||
|
||||
onConfigureTracksChanged: (configureTracks) ->
|
||||
|
||||
@configureTracks = configureTracks
|
||||
|
||||
@recompute()
|
||||
|
||||
recompute: () ->
|
||||
return if !@sessionMixers?
|
||||
|
||||
session = @sessionMixers.session
|
||||
mixers = @sessionMixers.mixers
|
||||
|
||||
tracks = []
|
||||
|
||||
|
|
@ -38,11 +50,30 @@ context = window
|
|||
instrumentIcon = context.JK.getInstrumentIcon45(track.instrument_id);
|
||||
|
||||
trackName = "#{name}: #{track.instrument}"
|
||||
tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, hasMixer:hasMixer, name: name, trackName: trackName, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id})
|
||||
|
||||
associatedVst = null
|
||||
# find any VST info
|
||||
if hasMixer && @configureTracks?
|
||||
|
||||
# bug in the backend; track is wrong for personal mixers (always 1), but correct for master mix
|
||||
trackAssignment = -1
|
||||
if @props.mode == context.JK.MIX_MODES.MASTER
|
||||
trackAssignment = mixerData.mixer.track
|
||||
else
|
||||
trackAssignment = mixerData.oppositeMixer?.track
|
||||
|
||||
console.log("checking associations", @configureTracks.vstTrackAssignments.vsts, mixerData.mixer)
|
||||
for vst in @configureTracks.vstTrackAssignments.vsts
|
||||
if vst.track == trackAssignment - 1 && vst.name != 'NONE'
|
||||
logger.debug("found VST on track", vst, track)
|
||||
associatedVst = vst
|
||||
break
|
||||
|
||||
tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, hasMixer:hasMixer, name: name, trackName: trackName, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id, associatedVst: associatedVst})
|
||||
else
|
||||
logger.warn("SessionMyTracks: unable to find participant")
|
||||
|
||||
this.setState(tracks: tracks, session:session, chat: chat)
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,439 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
ASSIGNMENT = context.JK.ASSIGNMENT
|
||||
VOICE_CHAT = context.JK.VOICE_CHAT
|
||||
MAX_TRACKS = context.JK.MAX_TRACKS
|
||||
MAX_OUTPUTS = context.JK.MAX_OUTPUTS
|
||||
gearUtils = context.JK.GearUtils
|
||||
|
||||
###
|
||||
|
||||
QVariantMap scanForPlugins();
|
||||
QVariantMap VSTListVsts();
|
||||
void VSTClearAll();
|
||||
QVariantMap VSTSetTrackAssignment(const QVariantMap vst, const QString& trackId);
|
||||
QVariantMap VSTListTrackAssignments();
|
||||
void VSTShowHideGui(bool show,const QString& trackId);
|
||||
void VST_ScanForMidiDevices();
|
||||
QVariantMap VST_GetMidiDeviceList();
|
||||
bool VST_EnableMidiForTrack(const QString& trackId, bool enableMidi, int midiDeviceIndex);
|
||||
###
|
||||
|
||||
@ConfigureTracksStore = Reflux.createStore(
|
||||
{
|
||||
listenables: ConfigureTracksActions
|
||||
|
||||
musicPorts: {inputs: [], outputs: []}
|
||||
trackNumber: null
|
||||
editingTrack: null
|
||||
vstPluginList: {vsts: []}
|
||||
vstTrackAssignments: {vsts: []}
|
||||
attachedMidiDevices: {midiDevices: []}
|
||||
midiTrackAssignments: {tracks: []}
|
||||
scanningVsts: false
|
||||
trackType: 'audio'
|
||||
|
||||
init: () ->
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
this.listenTo(context.MixerStore, this.onMixersChanged)
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
editingTrackValid: () ->
|
||||
true
|
||||
|
||||
onMixersChanged: (mixers) ->
|
||||
@loadChannels()
|
||||
@loadTrackInstruments()
|
||||
@changed()
|
||||
|
||||
onReset: (force) ->
|
||||
logger.debug("ConfigureTracksStore:reset", this)
|
||||
@trackNumber = null
|
||||
@editingTrack = null
|
||||
@loadChannels()
|
||||
@loadTrackInstruments()
|
||||
|
||||
if force || context.jamClient.hasVstAssignment()
|
||||
@performVstScan()
|
||||
@performMidiScan()
|
||||
@changed()
|
||||
|
||||
|
||||
onTrySave: () ->
|
||||
logger.debug("ConfigureTracksStore:trySave")
|
||||
@trySave()
|
||||
|
||||
trySave: () ->
|
||||
|
||||
onVstScan: () ->
|
||||
@performVstScan(true)
|
||||
|
||||
@changed()
|
||||
|
||||
performVstScan: (sendChanged) ->
|
||||
@hasVst = gon.global.vst_enabled & context.jamClient.hasVstHost()
|
||||
logger.debug("hasVst", @hasVst)
|
||||
if @hasVst
|
||||
logger.debug("vstScan starting")
|
||||
@scanningVsts = true
|
||||
result = context.jamClient.VSTScan("window.ConfigureTracksStore.onVstScanComplete")
|
||||
|
||||
onClearVsts: () ->
|
||||
context.jamClient.VSTClearAll()
|
||||
|
||||
setTimeout((() =>
|
||||
@listVsts()
|
||||
|
||||
@changed()
|
||||
), 250)
|
||||
|
||||
onVstScanComplete: () ->
|
||||
# XXX must wait a long time to get track assignments after scan/
|
||||
console.log("vst scan complete")
|
||||
@scanningVsts = false
|
||||
setTimeout((() =>
|
||||
@listVsts()
|
||||
@changed()
|
||||
), 100 )
|
||||
|
||||
onVstChanged: () ->
|
||||
setTimeout()
|
||||
logger.debug("vst changed")
|
||||
|
||||
setTimeout((() =>
|
||||
@listVsts()
|
||||
@changed()
|
||||
), 0)
|
||||
|
||||
listVsts: () ->
|
||||
|
||||
@vstPluginList = context.jamClient.VSTListVsts()
|
||||
@vstTrackAssignments = context.jamClient.VSTListTrackAssignments()
|
||||
|
||||
console.log("@vstTrackAssignments", @vstTrackAssignments)
|
||||
|
||||
onMidiScan: () ->
|
||||
@performMidiScan()
|
||||
@changed()
|
||||
|
||||
performMidiScan: () ->
|
||||
|
||||
if !@hasVst
|
||||
logger.debug("performMidiScan skipped due to no VST")
|
||||
return
|
||||
context.jamClient.VST_ScanForMidiDevices();
|
||||
@attachedMidiDevices = context.jamClient.VST_GetMidiDeviceList();
|
||||
|
||||
# trackNumber is 0-based, and optional
|
||||
onShowVstSettings: (trackNumber) ->
|
||||
if !@hasVst
|
||||
logger.debug("onShowVstSettings skipped due to no VST")
|
||||
return
|
||||
|
||||
if !trackNumber?
|
||||
trackNumber = @trackNumber - 1 if @trackNumber?
|
||||
|
||||
logger.debug("show VST GUI", trackNumber)
|
||||
|
||||
context.jamClient.VSTShowHideGui(true, trackNumber) if trackNumber?
|
||||
|
||||
changed: () ->
|
||||
@editingTrack = []
|
||||
@editingTrack.assignment = @trackNumber
|
||||
|
||||
if @trackNumber?
|
||||
|
||||
for inputsForTrack in @trackAssignments.inputs.assigned
|
||||
if inputsForTrack.assignment == @trackNumber
|
||||
@editingTrack = inputsForTrack
|
||||
break
|
||||
|
||||
|
||||
# slap on vst, if any, from list of vst assignments
|
||||
for vst in @vstTrackAssignments.vsts
|
||||
if vst.track == @editingTrack.assignment - 1
|
||||
@editingTrack.vst = vst
|
||||
@editingTrack.midiDeviceIndex = vst.midiDeviceIndex
|
||||
break
|
||||
|
||||
for inputsForTrack in @trackAssignments.inputs.assigned
|
||||
if vst.track == inputsForTrack.assignment - 1
|
||||
inputsForTrack.vst = vst
|
||||
|
||||
if @editingTrack.vst?
|
||||
logger.debug("current track has a VST assigned:" + @editingTrack.vst.file)
|
||||
|
||||
logger.debug("trackAssignments:", @trackAssignments)
|
||||
logger.debug("editingTrack:", @editingTrack)
|
||||
|
||||
@item = {
|
||||
musicPorts: @musicPorts,
|
||||
trackAssignments: @trackAssignments,
|
||||
trackNumber: @trackNumber,
|
||||
editingTrack: @editingTrack,
|
||||
vstPluginList: @vstPluginList,
|
||||
vstTrackAssignments: @vstTrackAssignments,
|
||||
attachedMidiDevices: @attachedMidiDevices,
|
||||
nextTrackNumber: @nextTrackNumber,
|
||||
newTrack: @newTrack,
|
||||
midiTrackAssignments: @midiTrackAssignments,
|
||||
scanningVsts: @scanningVsts,
|
||||
trackType: @trackType
|
||||
}
|
||||
|
||||
@trigger(@item)
|
||||
|
||||
loadChannels: (forceInputsToUnassign, inputChannelFilter) ->
|
||||
# inputChannelFilter is an optional argument that is used by the Gear Wizard.
|
||||
# basically, if an input channel isn't in there, it's not going to be displayed
|
||||
@musicPorts = context.jamClient.FTUEGetChannels()
|
||||
|
||||
# let's populate this bad boy
|
||||
@trackAssignments = {inputs: {unassigned: [], assigned: [], chat: []}, outputs: {unassigned: [], assigned: []}}
|
||||
|
||||
nextTrackNumber = 0
|
||||
|
||||
for input in @musicPorts.inputs
|
||||
if input.assignment == ASSIGNMENT.UNASSIGNED
|
||||
@trackAssignments.inputs.unassigned.push(input)
|
||||
else if input.assignment == ASSIGNMENT.CHAT
|
||||
@trackAssignments.inputs.chat.push(input)
|
||||
else
|
||||
nextTrackNumber = input.assignment if input.assignment > nextTrackNumber
|
||||
|
||||
# make sure this assignment isn't already preset (you can have multiple inputs per 'track slot')
|
||||
found = false
|
||||
for assigned in @trackAssignments.inputs.assigned
|
||||
if assigned.assignment == input.assignment
|
||||
assigned.push(input)
|
||||
found = true
|
||||
|
||||
if !found
|
||||
initial = [input]
|
||||
initial.assignment = input.assignment # store the assignment on the array itself, so we don't have to check inside the array for an input's assignment (which will all be the same)
|
||||
@trackAssignments.inputs.assigned.push(initial)
|
||||
for output in @musicPorts.outputs
|
||||
if output.assignment == ASSIGNMENT.OUTPUT
|
||||
@trackAssignments.outputs.assigned.push(output)
|
||||
else
|
||||
@trackAssignments.outputs.unassigned.push(output)
|
||||
|
||||
@nextTrackNumber = nextTrackNumber + 1
|
||||
|
||||
|
||||
loadTrackInstruments: (forceInputsToUnassign) ->
|
||||
for inputsForTrack in @trackAssignments.inputs.assigned
|
||||
|
||||
clientInstrument = context.jamClient.TrackGetInstrument(inputsForTrack.assignment)
|
||||
|
||||
if clientInstrument == 0
|
||||
logger.debug("defaulting track instrument for assignment #{@trackNumber}")
|
||||
# ensure that we always have an instrument set (50 = electric guitar
|
||||
context.jamClient.TrackSetInstrument(inputsForTrack.assignment, 50)
|
||||
clientInstrument = 50
|
||||
|
||||
instrument = context.JK.client_to_server_instrument_map[clientInstrument];
|
||||
|
||||
inputsForTrack.instrument_id = instrument?.server_id
|
||||
|
||||
|
||||
onAssociateInputsWithTrack: (inputId1, inputId2) ->
|
||||
return unless @trackNumber?
|
||||
|
||||
for inputs in @editingTrack
|
||||
context.jamClient.TrackSetAssignment(inputs.id, true, ASSIGNMENT.UNASSIGNED)
|
||||
|
||||
if inputId1?
|
||||
logger.debug("setting input1 #{inputId1} to #{@trackNumber}")
|
||||
context.jamClient.TrackSetAssignment(inputId1, true, @trackNumber)
|
||||
|
||||
if inputId2?
|
||||
logger.debug("setting input2 #{inputId2} to #{@trackNumber}")
|
||||
context.jamClient.TrackSetAssignment(inputId2, true, @trackNumber)
|
||||
|
||||
result = context.jamClient.TrackSaveAssignments();
|
||||
|
||||
if(!result || result.length == 0)
|
||||
|
||||
else
|
||||
context.JK.Banner.showAlert('Unable to save assignments. ' + result);
|
||||
|
||||
onAssociateInstrumentWithTrack: (instrumentId) ->
|
||||
return unless @trackNumber?
|
||||
|
||||
logger.debug("context.jamClient.TrackSetInstrument(trackNumber, track.instrument_id)", @trackNumber, instrumentId)
|
||||
|
||||
if instrumentId != null && instrumentId != ''
|
||||
context.jamClient.TrackSetInstrument(@trackNumber, context.JK.instrument_id_to_instrument[instrumentId].client_id)
|
||||
else
|
||||
context.jamClient.TrackSetInstrument(@trackNumber, 0)
|
||||
|
||||
if(!result || result.length == 0)
|
||||
|
||||
else
|
||||
context.JK.Banner.showAlert('Unable to save assignments. ' + result);
|
||||
|
||||
|
||||
result = context.jamClient.TrackSaveAssignments()
|
||||
|
||||
if(!result || result.length == 0)
|
||||
|
||||
else
|
||||
context.JK.Banner.showAlert('Unable to save assignments. ' + result);
|
||||
|
||||
onAssociateVSTWithTrack: (vst) ->
|
||||
|
||||
if !@hasVst
|
||||
logger.debug("onAssociateVSTWithTrack skipped due to no VST")
|
||||
return
|
||||
|
||||
if vst?
|
||||
logger.debug("associating track:#{@trackNumber - 1} with VST:#{vst.file}")
|
||||
|
||||
found = null
|
||||
for knownVst in @vstPluginList.vsts
|
||||
if knownVst.file == vst.file
|
||||
found = knownVst
|
||||
break
|
||||
if found?
|
||||
context.jamClient.VSTSetTrackAssignment(found, @trackNumber - 1)
|
||||
else
|
||||
logger.error("unable to locate vst for #{vst}")
|
||||
else
|
||||
logger.debug("unassociated track:#{@trackNumber} with VST")
|
||||
# no way to unset VST assignment yet
|
||||
|
||||
setTimeout((() => (
|
||||
@listVsts()
|
||||
|
||||
@changed()
|
||||
)), 250)
|
||||
|
||||
onCancelEdit: () ->
|
||||
if @newTrack
|
||||
for input in @editingTrack
|
||||
context.jamClient.TrackSetAssignment(input.id, true, ASSIGNMENT.UNASSIGNED)
|
||||
result = context.jamClient.TrackSaveAssignments()
|
||||
if(!result || result.length == 0)
|
||||
|
||||
else
|
||||
context.JK.Banner.showAlert('Unable to save assignments. ' + result);
|
||||
else
|
||||
logger.error("unable to process cancel for an existing track")
|
||||
|
||||
onDeleteTrack: (assignment) ->
|
||||
track = null
|
||||
for inputsForTrack in @trackAssignments.inputs.assigned
|
||||
if inputsForTrack.assignment == assignment
|
||||
track = inputsForTrack
|
||||
break
|
||||
|
||||
if track?
|
||||
for input in inputsForTrack
|
||||
context.jamClient.TrackSetAssignment(input.id, true, ASSIGNMENT.UNASSIGNED)
|
||||
result = context.jamClient.TrackSaveAssignments()
|
||||
|
||||
if(!result || result.length == 0)
|
||||
|
||||
else
|
||||
context.JK.Banner.showAlert('Unable to save assignments. ' + result);
|
||||
else
|
||||
logger.error("unable to find track to delete")
|
||||
|
||||
onShowAddNewTrack: () ->
|
||||
|
||||
# check if we have what we need... namely, free ports
|
||||
|
||||
if @trackAssignments.inputs.unassigned.length == 0
|
||||
context.JK.Banner.showAlert('You have no more unassigned input ports.<br/><br/>You can free some up by editing an AUDIO track.')
|
||||
return
|
||||
|
||||
@openLiveTrackDialog(@nextTrackNumber)
|
||||
|
||||
onShowEditTrack: (trackNumber) ->
|
||||
@openLiveTrackDialog(trackNumber)
|
||||
|
||||
openLiveTrackDialog: (trackNumber) ->
|
||||
@trackNumber = trackNumber
|
||||
logger.debug("opening live track dialog for track #{trackNumber}")
|
||||
|
||||
@newTrack = true
|
||||
for inputsForTrack in @trackAssignments.inputs.assigned
|
||||
logger.debug("inputsForTrack.assignment @trackNumber", inputsForTrack.assignment, @trackNumber )
|
||||
if inputsForTrack.assignment == @trackNumber
|
||||
@newTrack = false
|
||||
break
|
||||
|
||||
if @newTrack
|
||||
@trackType = 'audio'
|
||||
else
|
||||
if @trackNumber == 1
|
||||
@trackType = 'audio'
|
||||
else
|
||||
@trackType = 'audio'
|
||||
for trackAssignment in @vstTrackAssignments.vsts
|
||||
if trackAssignment.track == @trackNumber - 1
|
||||
if trackAssignment.midiDeviceIndex > -1
|
||||
logger.debug("editing midi track")
|
||||
@trackType = 'midi'
|
||||
break
|
||||
|
||||
if @newTrack
|
||||
|
||||
assignment = context.jamClient.TrackGetInstrument(@trackNumber)
|
||||
|
||||
if assignment == 0
|
||||
logger.debug("defaulting track instrument for assignment #{@trackNumber}")
|
||||
# ensure that we always have an instrument set (50 = electric guitar
|
||||
context.jamClient.TrackSetInstrument(@trackNumber, 50)
|
||||
|
||||
@performVstScan()
|
||||
@performMidiScan()
|
||||
|
||||
@changed()
|
||||
|
||||
@app.layout.showDialog('configure-live-tracks-dialog')
|
||||
|
||||
onDesiredTrackType: (trackType) ->
|
||||
@trackType = trackType
|
||||
|
||||
if @trackType == 'midi'
|
||||
@trackNumber = 100
|
||||
@changed()
|
||||
|
||||
onUpdateOutputs: (outputId1, outputId2) ->
|
||||
|
||||
context.jamClient.TrackSetAssignment(outputId1, true, ASSIGNMENT.OUTPUT);
|
||||
context.jamClient.TrackSetAssignment(outputId2, true, ASSIGNMENT.OUTPUT);
|
||||
|
||||
result = context.jamClient.TrackSaveAssignments();
|
||||
|
||||
if(!result || result.length == 0)
|
||||
|
||||
else
|
||||
context.JK.Banner.showAlert('Unable to save assignments. ' + result);
|
||||
|
||||
onShowEditOutputs: () ->
|
||||
@app.layout.showDialog('configure-outputs-dialog')
|
||||
|
||||
onAssociateMIDIWithTrack: (midiInterface) ->
|
||||
|
||||
@trackNumber = 100
|
||||
|
||||
if !midiInterface? || midiInterface == ''
|
||||
logger.debug("disabling midiInterface:#{midiInterface}, track:#{@trackNumber - 1}")
|
||||
context.jamClient.VST_EnableMidiForTrack(@trackNumber - 1, false, 0)
|
||||
else
|
||||
logger.debug("enabling midiInterface:#{midiInterface}, track:#{@trackNumber - 1}")
|
||||
context.jamClient.VST_EnableMidiForTrack(@trackNumber - 1, true, midiInterface)
|
||||
|
||||
setTimeout((() => (
|
||||
@listVsts()
|
||||
|
||||
@changed()
|
||||
)), 250)
|
||||
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
rest = context.JK.Rest()
|
||||
EVENTS = context.JK.EVENTS
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
SessionActions = @SessionActions
|
||||
|
||||
SessionStatThresholds = gon.session_stat_thresholds
|
||||
NetworkThresholds = SessionStatThresholds.network
|
||||
SystemThresholds = SessionStatThresholds.system
|
||||
AudioThresholds = SessionStatThresholds.audio
|
||||
|
||||
@SessionStatsStore = Reflux.createStore(
|
||||
{
|
||||
listenables: @SessionStatsActions
|
||||
rawStats: null
|
||||
|
||||
onPushStats: (stats) ->
|
||||
@rawStats = stats
|
||||
@changed()
|
||||
|
||||
classify: (holder, field, threshold) ->
|
||||
value = holder[field]
|
||||
fieldLevel = field + '_level'
|
||||
fieldThreshold = threshold[field]
|
||||
if value? && fieldThreshold?
|
||||
|
||||
if fieldThreshold.inverse
|
||||
if value <= fieldThreshold.poor
|
||||
holder[fieldLevel] = 'poor'
|
||||
@participantClassification = 3
|
||||
else if value <= fieldThreshold.warn
|
||||
holder[fieldLevel] = 'warn'
|
||||
@participantClassification = 2 if @participantClassification == 1
|
||||
else
|
||||
holder[fieldLevel] = 'good'
|
||||
else if fieldThreshold.eql
|
||||
if value == fieldThreshold.poor
|
||||
holder[fieldLevel] = 'poor'
|
||||
@participantClassification = 3
|
||||
else if value == fieldThreshold.warn
|
||||
holder[fieldLevel] = 'warn'
|
||||
@participantClassification = 2 if @participantClassification == 1
|
||||
else
|
||||
holder[fieldLevel] = 'good'
|
||||
else
|
||||
if value >= fieldThreshold.poor
|
||||
holder[fieldLevel] = 'poor'
|
||||
@participantClassification = 3
|
||||
else if value >= fieldThreshold.warn
|
||||
holder[fieldLevel] = 'warn'
|
||||
@participantClassification = 2 if @participantClassification == 1
|
||||
else
|
||||
holder[fieldLevel] = 'good'
|
||||
|
||||
|
||||
changed: () ->
|
||||
@stats = {}
|
||||
|
||||
console.log("raw stats", @rawStats)
|
||||
for participant in @rawStats
|
||||
|
||||
@participantClassification = 1 # 1=good, 2=warn, 3=poor
|
||||
|
||||
# CPU is 0-100
|
||||
if participant.cpu?
|
||||
system = {cpu: participant.cpu}
|
||||
|
||||
@classify(system, 'cpu', SystemThresholds)
|
||||
|
||||
participant.system = system
|
||||
|
||||
network = participant.network
|
||||
|
||||
if network?
|
||||
# audio_bitrate: 256
|
||||
# net_bitrate: 286.19244384765625
|
||||
# ping: 0.08024691045284271
|
||||
# ping_var: 0.6403124332427979
|
||||
# pkt_loss: 100
|
||||
# wifi: false
|
||||
|
||||
@classify(network, 'audiojq_median', NetworkThresholds)
|
||||
@classify(network, 'net_bitrate', NetworkThresholds)
|
||||
@classify(network, 'ping', NetworkThresholds)
|
||||
@classify(network, 'pkt_loss', NetworkThresholds)
|
||||
@classify(network, 'wifi', NetworkThresholds)
|
||||
|
||||
audio = participant.audio
|
||||
|
||||
if audio?
|
||||
# acpu: 5.148329734802246
|
||||
# audio_in: "Fast Track"
|
||||
# audio_in_type: "Core Audio"
|
||||
# cpu: 22.44668960571289
|
||||
# framesize: 2.5
|
||||
# in_latency: 5.020833492279053
|
||||
# input_iio_jitter: -0.0015926361083984375
|
||||
# input_jitter: 0.2977011799812317
|
||||
# input_median: 400.16632080078125
|
||||
# io_out_latency: "Expected Latency = 9.54 +/- 1.00 ms [Raw/PaBuff/PaRing Latency: 9.54 / 12.04 / 0.00 ms]"
|
||||
# out_latency: 4.520833492279053
|
||||
# output_iio_jitter: -0.07366180419921875
|
||||
# output_jitter: 0.40290364623069763
|
||||
# output_median: 400.0581970214844
|
||||
# output_name: 4
|
||||
# samplerate: 48000
|
||||
|
||||
if audio.cpu?
|
||||
system = {cpu: audio.cpu}
|
||||
@classify(system, 'cpu', SystemThresholds)
|
||||
participant.system = system
|
||||
|
||||
if audio.in_latency? and audio.out_latency?
|
||||
audio.latency = audio.in_latency + audio.out_latency
|
||||
|
||||
|
||||
@classify(audio, 'framesize', AudioThresholds)
|
||||
@classify(audio, 'latency', AudioThresholds)
|
||||
@classify(audio, 'input_jitter', AudioThresholds)
|
||||
@classify(audio, 'output_jitter', AudioThresholds)
|
||||
@classify(audio, 'audio_in_type', AudioThresholds)
|
||||
|
||||
switch @participantClassification
|
||||
when 1 then participant.classification = 'good'
|
||||
when 2 then participant.classification = 'warn'
|
||||
when 3 then participant.classification = 'poor'
|
||||
else
|
||||
participant.classification = 'unknown'
|
||||
|
||||
@stats[participant.id] = participant
|
||||
|
||||
@trigger(@stats)
|
||||
}
|
||||
)
|
||||
|
|
@ -10,6 +10,7 @@ SessionActions = @SessionActions
|
|||
RecordingActions = @RecordingActions
|
||||
NotificationActions = @NotificationActions
|
||||
VideoActions = @VideoActions
|
||||
ConfigureTracksActions = @ConfigureTracksActions
|
||||
|
||||
@SessionStore = Reflux.createStore(
|
||||
{
|
||||
|
|
@ -731,6 +732,9 @@ VideoActions = @VideoActions
|
|||
$(document).trigger(EVENTS.SESSION_STARTED, {session: {id: @currentSessionId}}) if document
|
||||
|
||||
@handleAutoOpenJamTrack()
|
||||
|
||||
@watchBackendStats()
|
||||
ConfigureTracksActions.reset(false)
|
||||
)
|
||||
.fail((xhr) =>
|
||||
@updateCurrentSession(null)
|
||||
|
|
@ -762,6 +766,13 @@ VideoActions = @VideoActions
|
|||
@app.notifyServerError(xhr, 'Unable to Join Session');
|
||||
)
|
||||
|
||||
watchBackendStats: () ->
|
||||
@backendStatsInterval = window.setInterval((() => (@updateBackendStats())), 1000)
|
||||
|
||||
updateBackendStats: () ->
|
||||
connectionStats = window.jamClient.getConnectionDetail('')
|
||||
SessionStatsActions.pushStats(connectionStats)
|
||||
|
||||
trackChanges: (header, payload) ->
|
||||
if @currentTrackChanges < payload.track_changes_counter
|
||||
# we don't have the latest info. try and go get it
|
||||
|
|
@ -1050,6 +1061,10 @@ VideoActions = @VideoActions
|
|||
@userTracks = null;
|
||||
@startTime = null;
|
||||
|
||||
if @backendStatsInterval?
|
||||
window.clearInterval(@backendStatsInterval)
|
||||
@backendStatsInterval = null
|
||||
|
||||
if @joinDeferred?.state() == 'resolved'
|
||||
$(document).trigger(EVENTS.SESSION_ENDED, {session: {id: @currentSessionId}})
|
||||
|
||||
|
|
|
|||
|
|
@ -284,7 +284,8 @@
|
|||
|
||||
options.cssStyles = {}
|
||||
options.padding = 0;
|
||||
context.JK.hoverBubble($element, '<div class="react-holder ' + reactElementName + '"></div>', options)
|
||||
var extra = options.extraClasses || ''
|
||||
context.JK.hoverBubble($element, '<div class="react-holder ' + reactElementName + ' ' + extra + '"></div>', options)
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
|
@ -733,6 +734,10 @@
|
|||
return date.toLocaleTimeString();
|
||||
}
|
||||
|
||||
context.JK.iconMapBase = function() {
|
||||
return icon_map_base
|
||||
}
|
||||
|
||||
context.JK.formatUtcTime = function(date, change) {
|
||||
if (change) {
|
||||
date.setMinutes(Math.ceil(date.getMinutes() / 30) * 30);
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@
|
|||
}
|
||||
|
||||
function handleNext() {
|
||||
var saved = configureTracksHelper.trySave();
|
||||
/** var saved = configureTracksHelper.trySave();
|
||||
|
||||
if(saved) {
|
||||
context.JK.GA.trackConfigureTracksCompletion(context.JK.detectOS());
|
||||
successfullyAssignedOnce = true;
|
||||
}
|
||||
|
||||
return saved;
|
||||
*/
|
||||
return context.ConfigureTracksStore.editingTrackValid()
|
||||
}
|
||||
|
||||
function newSession() {
|
||||
|
|
@ -38,7 +38,8 @@
|
|||
function beforeShow() {
|
||||
var forceInputsToUnassigned = !successfullyAssignedOnce;
|
||||
|
||||
configureTracksHelper.reset(forceInputsToUnassigned, wizard.getChosenInputs())
|
||||
window.ConfigureTracksActions.reset(false);
|
||||
//configureTracksHelper.reset(forceInputsToUnassigned, wizard.getChosenInputs())
|
||||
}
|
||||
|
||||
function initialize(_$step, _wizard) {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@
|
|||
*= require dialogs/dialog
|
||||
*= require ./iconInstrumentSelect
|
||||
*= require ./muteSelect
|
||||
*= require ./manageVsts
|
||||
*= require ./vstEffects
|
||||
*= require ./metronomePlaybackModeSelect
|
||||
*= require ./terms
|
||||
*= require ./createSession
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
@import "client/common";
|
||||
|
||||
.manage-vsts-popup {
|
||||
.bt-content {
|
||||
height:38px;
|
||||
width:190px;
|
||||
background-color:#333;
|
||||
overflow:auto;
|
||||
border:1px solid #ED3618;
|
||||
text-align:left;
|
||||
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||
ul {
|
||||
@include vertical-align-column;
|
||||
height:100%;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
li {
|
||||
font-size:12px;
|
||||
margin-left:0 !important;
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
@import "client/common";
|
||||
|
||||
.ConfigureTracks {
|
||||
|
||||
.inputs-view {
|
||||
border-width:1px 0;
|
||||
border-color:$ColorText;
|
||||
border-style:solid;
|
||||
padding:20px 0;
|
||||
height:220px;
|
||||
|
||||
.live-tracks {
|
||||
height:165px;
|
||||
overflow:auto;
|
||||
a {
|
||||
margin-left:3px;
|
||||
}
|
||||
}
|
||||
|
||||
.live-input {
|
||||
display:inline-block;
|
||||
&:before {
|
||||
content: '('
|
||||
}
|
||||
&:after {
|
||||
content: ')'
|
||||
}
|
||||
}
|
||||
.live-track {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
a.delete-live-track {
|
||||
margin-left:20px;
|
||||
}
|
||||
.assignment {
|
||||
display:inline-block;
|
||||
width:20px;
|
||||
}
|
||||
|
||||
.input-track-info {
|
||||
@include border_box_sizing;
|
||||
width:60%;
|
||||
display:inline-block;
|
||||
|
||||
&.one {
|
||||
.live-input {
|
||||
width:50%;
|
||||
@include border_box_sizing;
|
||||
}
|
||||
}
|
||||
|
||||
&.two {
|
||||
.live-input {
|
||||
max-width:40%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@include border_box_sizing;
|
||||
&:nth-of-type(1) {
|
||||
padding-right:5px;
|
||||
}
|
||||
&:nth-of-type(2) {
|
||||
padding-left:5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.track-type-label {
|
||||
display:inline-block;
|
||||
white-space:nowrap;
|
||||
padding-right:7px;
|
||||
}
|
||||
|
||||
.plugin-info {
|
||||
@include border_box_sizing;
|
||||
width:30%;
|
||||
display:inline-block;
|
||||
white-space:nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.plugin-instrument {
|
||||
@include border_box_sizing;
|
||||
width:10%;
|
||||
display:inline-block;
|
||||
text-align:center;
|
||||
|
||||
img {
|
||||
vertical-align:middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.outputs-view {
|
||||
border-width:0 0 1px;
|
||||
border-color:$ColorText;
|
||||
border-style:solid;
|
||||
padding:20px 0;
|
||||
|
||||
.assignment {
|
||||
display:inline-block;
|
||||
width:20px;
|
||||
}
|
||||
|
||||
|
||||
.output-tracks {
|
||||
height:73px;
|
||||
overflow:auto;
|
||||
a {
|
||||
margin-left:3px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.output-track {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.output-track-info {
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.output {
|
||||
display:inline-block;
|
||||
margin-bottom:0 !important;
|
||||
&:before {
|
||||
content: '('
|
||||
}
|
||||
&:after {
|
||||
content: ')'
|
||||
}
|
||||
}
|
||||
}
|
||||
h3 {
|
||||
display:inline-block;
|
||||
font-size:14px;
|
||||
margin:0 0 20px;
|
||||
@include border_box_sizing;
|
||||
&.session-audio-inputs-header {
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
width:60%;
|
||||
}
|
||||
|
||||
&.session-audio-outputs-header {
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
width:60%;
|
||||
}
|
||||
|
||||
&.plugin-header {
|
||||
width:30%;
|
||||
}
|
||||
&.instrument-header {
|
||||
width:10%;
|
||||
text-align:center;
|
||||
}
|
||||
}
|
||||
.live-track-actions {
|
||||
display:block;
|
||||
padding-left: 17px;
|
||||
margin-top: 5px;
|
||||
a {
|
||||
font-size:12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ $session-screen-divider: 1190px;
|
|||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin session-normal {
|
||||
@media (min-width: #{$session-screen-divider}) {
|
||||
@content;
|
||||
|
|
@ -13,97 +14,171 @@ $session-screen-divider: 1190px;
|
|||
}
|
||||
|
||||
#session-screen {
|
||||
.session-container {
|
||||
overflow-x: hidden;
|
||||
.session-container {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.session-track {
|
||||
@include session-small {
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
.session-track {
|
||||
&.metronome, &.jam-track, &.recorded-track, &.backing-track {
|
||||
@include session-small {
|
||||
max-width: 120px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&.metronome, &.jam-track, &.recorded-track, &.backing-track {
|
||||
.track-icon-pan {
|
||||
@include session-small {
|
||||
height:auto;
|
||||
}
|
||||
.track-icon-pan {
|
||||
@include session-small {
|
||||
margin-right:0;
|
||||
}
|
||||
}
|
||||
|
||||
.track-buttons {
|
||||
@include session-small {
|
||||
margin:12px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
table.vu {
|
||||
@include session-small {
|
||||
margin-top:5px;
|
||||
}
|
||||
}
|
||||
|
||||
.track-controls {
|
||||
@include session-small {
|
||||
margin-right:8px;
|
||||
}
|
||||
}
|
||||
|
||||
.track-icon-pan {
|
||||
@include session-small {
|
||||
margin-right:2px;
|
||||
}
|
||||
}
|
||||
.track-instrument {
|
||||
@include session-small {
|
||||
margin: -4px 12px 0 0;
|
||||
}
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
&.jam-track-category, &.recorded-category {
|
||||
.track-controls {
|
||||
@include session-small {
|
||||
margin-top:5px;
|
||||
margin-right:8px;
|
||||
}
|
||||
|
||||
.track-buttons {
|
||||
@include session-small {
|
||||
margin: 12px 0 0;
|
||||
}
|
||||
table.vu {
|
||||
@include session-small {
|
||||
margin-top:0;
|
||||
}
|
||||
}
|
||||
|
||||
table.vu {
|
||||
@include session-small {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.jam-track-header {
|
||||
@include session-small {
|
||||
float:left;
|
||||
}
|
||||
}
|
||||
|
||||
.track-controls {
|
||||
@include session-small {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.name {
|
||||
@include session-small {
|
||||
float:left;
|
||||
}
|
||||
}
|
||||
|
||||
.track-icon-pan {
|
||||
@include session-small {
|
||||
margin-right: 2px;
|
||||
}
|
||||
.track-buttons {
|
||||
@include session-small {
|
||||
margin-top:12px;
|
||||
}
|
||||
}
|
||||
.track-instrument {
|
||||
@include session-small {
|
||||
margin: -4px 12px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.jam-track-category, &.recorded-category {
|
||||
.track-controls {
|
||||
@include session-small {
|
||||
margin-top: 5px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
table.vu {
|
||||
@include session-small {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
.jam-track-header {
|
||||
@include session-small {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
.name {
|
||||
@include session-small {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
.track-buttons {
|
||||
@include session-small {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.track-controls {
|
||||
@include session-small {
|
||||
margin-top:8px;
|
||||
.track-controls {
|
||||
@include session-small {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.track-buttons {
|
||||
@include session-small {
|
||||
margin-left: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.self.SessionStatsHover .stats-info {
|
||||
height:200px;
|
||||
}
|
||||
|
||||
.stats-info {
|
||||
float: left;
|
||||
width: 120px;
|
||||
border-radius: 15px;
|
||||
border-color: #fc0;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
padding:10px;
|
||||
height: 341px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.stats-area {
|
||||
float: left;
|
||||
padding: 0 20px 20px 20px;
|
||||
color: #cccccc;
|
||||
|
||||
h3 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.stat {
|
||||
margin: 7px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.stats-holder {
|
||||
margin-top: 20px;
|
||||
h3 {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
}
|
||||
.status-icon {
|
||||
border-radius: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 10px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
&.poor {
|
||||
background-color: $poor;
|
||||
}
|
||||
&.warn {
|
||||
background-color: $fair;
|
||||
}
|
||||
&.unknown {
|
||||
background-color: $unknown;
|
||||
}
|
||||
&.good {
|
||||
background-color: $good;
|
||||
}
|
||||
}
|
||||
|
||||
.track-buttons {
|
||||
@include session-small {
|
||||
margin-left:14px;
|
||||
}
|
||||
.stat-value {
|
||||
width: 50px;
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
line-height: 20px;
|
||||
margin-right:6px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
|
|
@ -125,18 +200,18 @@ $session-screen-divider: 1190px;
|
|||
padding: 10px;
|
||||
height: 100%;
|
||||
margin-bottom: 15px;
|
||||
color:$ColorTextTypical;
|
||||
overflow:hidden;
|
||||
position:relative;
|
||||
color: $ColorTextTypical;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.session-media-tracks {
|
||||
width:34%;
|
||||
width: 34%;
|
||||
}
|
||||
|
||||
.session-notifications {
|
||||
border-right-width: 0;
|
||||
display:none; //temp
|
||||
display: none; //temp
|
||||
}
|
||||
|
||||
.in-session-controls {
|
||||
|
|
@ -162,20 +237,20 @@ $session-screen-divider: 1190px;
|
|||
|
||||
a {
|
||||
img, .volume-icon {
|
||||
vertical-align:top;
|
||||
vertical-align: top;
|
||||
margin-right: 4px;
|
||||
}
|
||||
span {
|
||||
vertical-align:middle;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.button-grey {
|
||||
margin:0 5px;
|
||||
margin: 0 5px;
|
||||
padding: 3px 7px;
|
||||
|
||||
&.session-leave {
|
||||
margin-right:10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -192,15 +267,15 @@ $session-screen-divider: 1190px;
|
|||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align:left;
|
||||
text-align: left;
|
||||
|
||||
&.media-options-showing {
|
||||
top:180px;
|
||||
top: 180px;
|
||||
}
|
||||
|
||||
border-right: 1px solid #4c4c4c;
|
||||
margin-bottom:10px;
|
||||
margin-top:10px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
@ -209,57 +284,56 @@ $session-screen-divider: 1190px;
|
|||
}
|
||||
|
||||
.download-jamtrack {
|
||||
margin-top:20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
.when-empty {
|
||||
margin-top:25px;
|
||||
margin-left:22px;
|
||||
color:$ColorTextTypical;
|
||||
overflow:hidden;
|
||||
margin-top: 25px;
|
||||
margin-left: 22px;
|
||||
color: $ColorTextTypical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.session-track-settings {
|
||||
height:20px;
|
||||
cursor:pointer;
|
||||
padding-bottom:1px; // to line up with SessionOtherTracks
|
||||
color:$ColorTextTypical;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
padding-bottom: 1px; // to line up with SessionOtherTracks
|
||||
color: $ColorTextTypical;
|
||||
|
||||
&:hover {
|
||||
color:white;
|
||||
color: white;
|
||||
}
|
||||
|
||||
span {
|
||||
top: -5px;
|
||||
position: relative;
|
||||
left:3px;
|
||||
left: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.session-invite-musicians {
|
||||
height:20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
color:$ColorTextTypical;
|
||||
color: $ColorTextTypical;
|
||||
|
||||
&:hover {
|
||||
color:white;
|
||||
color: white;
|
||||
}
|
||||
|
||||
span {
|
||||
top:-5px;
|
||||
position:relative;
|
||||
left:3px;
|
||||
top: -5px;
|
||||
position: relative;
|
||||
left: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.closeAudio, .session-clear-notifications {
|
||||
cursor: pointer;
|
||||
color:$ColorTextTypical;
|
||||
height:20px;
|
||||
color: $ColorTextTypical;
|
||||
height: 20px;
|
||||
|
||||
img {
|
||||
top:-2px
|
||||
top: -2px
|
||||
}
|
||||
span {
|
||||
top: -5px;
|
||||
|
|
@ -268,115 +342,113 @@ $session-screen-divider: 1190px;
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.open-media-file-header, .use-metronome-header {
|
||||
font-size:14px;
|
||||
line-height:100%;
|
||||
margin:0;
|
||||
font-size: 14px;
|
||||
line-height: 100%;
|
||||
margin: 0;
|
||||
|
||||
img {
|
||||
position:relative;
|
||||
top:3px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
}
|
||||
.open-media-file-header {
|
||||
|
||||
img {
|
||||
vertical-align:middle;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.open-text {
|
||||
margin-left:5px;
|
||||
vertical-align:bottom;
|
||||
margin-left: 5px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.use-metronome-header {
|
||||
clear: both;
|
||||
a {
|
||||
color:$ColorTextTypical;
|
||||
color: $ColorTextTypical;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color:white;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-media-file-options {
|
||||
font-size:14px;
|
||||
font-size: 14px;
|
||||
margin: 7px 0 0 7px !important;
|
||||
color:$ColorTextTypical;
|
||||
color: $ColorTextTypical;
|
||||
li {
|
||||
margin-bottom:5px !important;
|
||||
margin-left:38px !important;
|
||||
margin-bottom: 5px !important;
|
||||
margin-left: 38px !important;
|
||||
a {
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color:white;
|
||||
color: white;
|
||||
}
|
||||
color:$ColorTextTypical;
|
||||
color: $ColorTextTypical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-metronome {
|
||||
margin-left:5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.media-options {
|
||||
padding-bottom:10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.session-mytracks-notracks p.notice {
|
||||
font-size:14px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.session-notification {
|
||||
color: white;
|
||||
background-color: #666666;
|
||||
border-radius: 6px;
|
||||
min-height: 36px;
|
||||
width:100%;
|
||||
position:relative;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
@include border_box_sizing;
|
||||
padding:6px;
|
||||
margin:10px 0;
|
||||
padding: 6px;
|
||||
margin: 10px 0;
|
||||
|
||||
&.has-details {
|
||||
cursor:pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.msg {
|
||||
font-size:14px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.detail {
|
||||
font-size:12px;
|
||||
margin-top:5px;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.notify-help {
|
||||
color:#ffcc00;
|
||||
color: #ffcc00;
|
||||
text-decoration: none;
|
||||
font-size:12px;
|
||||
margin-left:5px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
|
||||
&:hover {
|
||||
text-decoration:underline !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close-window {
|
||||
text-align:center;
|
||||
clear:both;
|
||||
text-align: center;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.session-volume-settings .volume-icon {
|
||||
display:inline-block;
|
||||
width:14px;
|
||||
height:14px;
|
||||
background-image:url('/assets/content/icon_mute_sm.png');
|
||||
background-repeat:no-repeat;
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-image: url('/assets/content/icon_mute_sm.png');
|
||||
background-repeat: no-repeat;
|
||||
|
||||
&.muted {
|
||||
background-position: 0 0;
|
||||
|
|
|
|||
|
|
@ -119,6 +119,29 @@
|
|||
}
|
||||
}
|
||||
|
||||
.track-connection-state {
|
||||
width:20px;
|
||||
height:20px;
|
||||
float:left;
|
||||
cursor:pointer;
|
||||
text-align: center;
|
||||
margin-left:10px;
|
||||
border-radius:10px;
|
||||
|
||||
&.poor {
|
||||
background-color:$poor;
|
||||
}
|
||||
&.warn {
|
||||
background-color:$fair;
|
||||
}
|
||||
&.unknown {
|
||||
background-color:$unknown;
|
||||
}
|
||||
&.good {
|
||||
background-color:$good;
|
||||
}
|
||||
}
|
||||
|
||||
.track-icon-equalizer {
|
||||
float:left;
|
||||
cursor: pointer;
|
||||
|
|
@ -134,6 +157,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.my-track {
|
||||
width:235px;
|
||||
max-width:235px;
|
||||
}
|
||||
|
||||
|
||||
// media overrides
|
||||
&.backing-track, &.recorded-track, &.jam-track, &.metronome, &.recorded-category, &.jam-track-category {
|
||||
|
|
@ -357,6 +385,7 @@
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
&.SessionTrackVolumeHover {
|
||||
.session-track {
|
||||
margin-bottom:0;
|
||||
|
|
@ -368,6 +397,22 @@
|
|||
height:380px ! important;
|
||||
}
|
||||
|
||||
&.SessionStatsHover {
|
||||
width:380px;
|
||||
height:450px;
|
||||
@include border_box_sizing;
|
||||
|
||||
h3 {
|
||||
margin-top:20px;
|
||||
color: white;
|
||||
margin-left:20px;
|
||||
}
|
||||
|
||||
&.self {
|
||||
height:290px;
|
||||
}
|
||||
}
|
||||
|
||||
&.SessionTrackPanHover {
|
||||
width:331px;
|
||||
height:197px;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
@import "client/common";
|
||||
|
||||
.vst-effects-popup {
|
||||
margin-top: 3px;
|
||||
margin-left: 120px;
|
||||
|
||||
.bt-content {
|
||||
width:220px;
|
||||
background-color:#333;
|
||||
overflow:auto;
|
||||
border:1px solid #ED3618;
|
||||
text-align:left;
|
||||
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||
ul {
|
||||
@include vertical-align-column;
|
||||
height:100%;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
li {
|
||||
font-size:12px;
|
||||
margin-left:0 !important;
|
||||
list-style-type: none;
|
||||
margin-bottom:0 !important;
|
||||
|
||||
padding:7px 0 10px 0;
|
||||
|
||||
.vst-name {
|
||||
text-overflow:ellipsis;
|
||||
overflow:hidden;
|
||||
}
|
||||
&:nth-of-type(1) {
|
||||
|
||||
border-width:0 0 1px 0 !important;
|
||||
border-style:solid !important;;
|
||||
border-color:#ED3618 !important;;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -197,7 +197,7 @@
|
|||
width: 25%;
|
||||
|
||||
&:nth-of-type(2) {
|
||||
width: 21%;
|
||||
width: 71%;
|
||||
}
|
||||
|
||||
&:nth-of-type(3) {
|
||||
|
|
@ -217,9 +217,64 @@
|
|||
margin-top: 45px;
|
||||
}
|
||||
|
||||
.output-channels, .unassigned-output-channels {
|
||||
.outputs-view {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.inputs-view {
|
||||
padding:0;
|
||||
border-width:0;
|
||||
|
||||
h3.session-audio-inputs-header {
|
||||
font-weight:normal;
|
||||
width:70%;
|
||||
font-size:14px;
|
||||
color:white;
|
||||
}
|
||||
|
||||
h3.plugin-header {
|
||||
width:20%;
|
||||
font-size:14px;
|
||||
color:white;
|
||||
}
|
||||
|
||||
h3.instrument-header {
|
||||
width:10%;
|
||||
font-size:14px;
|
||||
color:white;
|
||||
}
|
||||
|
||||
.input-track-info {
|
||||
width:70%;
|
||||
}
|
||||
|
||||
.plugin-info {
|
||||
width:20%;
|
||||
}
|
||||
|
||||
.plugin-instrument {
|
||||
width:10%;
|
||||
}
|
||||
|
||||
.live-tracks {
|
||||
height:198px;
|
||||
}
|
||||
.live-input {
|
||||
display:block;
|
||||
max-width:90%;
|
||||
&:before {
|
||||
content: ''
|
||||
}
|
||||
&:after {
|
||||
content: ''
|
||||
}
|
||||
}
|
||||
.input-track-info .live-input {
|
||||
padding-left:19px;
|
||||
padding-right:0;
|
||||
}
|
||||
.add-track-action { text-align:center;}
|
||||
}
|
||||
}
|
||||
|
||||
.wizard-step[layout-wizard-step="3"] {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,184 @@
|
|||
@import "client/common";
|
||||
|
||||
#configure-live-tracks-dialog {
|
||||
width: 800px;
|
||||
|
||||
.dialog-inner {
|
||||
width:auto;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color:white;
|
||||
font-weight:bold;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.manage-audio-plugins {
|
||||
font-size:12px;
|
||||
}
|
||||
.actions {
|
||||
clear:both;
|
||||
text-align:center;
|
||||
}
|
||||
.track-type {
|
||||
width:100%;
|
||||
@include border_box_sizing;
|
||||
padding:10px;
|
||||
|
||||
.track-type-option {
|
||||
margin-bottom:10px;
|
||||
}
|
||||
label {
|
||||
vertical-align:middle;
|
||||
display:inline-block;
|
||||
margin-left:10px;
|
||||
}
|
||||
.iradio_minimal {
|
||||
vertical-align:middle;
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
.audio-input-ports {
|
||||
width:60%;
|
||||
@include border_box_sizing;
|
||||
float:left;
|
||||
margin-bottom:40px;
|
||||
padding:10px;
|
||||
|
||||
select {
|
||||
width: 90%;
|
||||
@include border_box_sizing;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
p {
|
||||
margin-bottom:10px;
|
||||
line-height:125%;
|
||||
}
|
||||
}
|
||||
|
||||
.audio {
|
||||
.instrument-selection {
|
||||
width:40%;
|
||||
@include border_box_sizing;
|
||||
float:left;
|
||||
margin-bottom:26px;
|
||||
padding:10px;
|
||||
select {
|
||||
width:90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.midi-interface {
|
||||
@include border_box_sizing;
|
||||
float:left;
|
||||
margin-bottom:20px;
|
||||
padding:10px;
|
||||
position:relative;
|
||||
width:50%;
|
||||
|
||||
select {
|
||||
width:80%;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
a {
|
||||
font-size:12px;
|
||||
}
|
||||
}
|
||||
.midi {
|
||||
.instrument-selection {
|
||||
width:50%;
|
||||
@include border_box_sizing;
|
||||
float:left;
|
||||
margin-bottom:26px;
|
||||
padding:10px;
|
||||
select {
|
||||
width:80%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.midi-instrument {
|
||||
@include border_box_sizing;
|
||||
float:left;
|
||||
margin-bottom:20px;
|
||||
padding:10px;
|
||||
position:relative;
|
||||
width:50%;
|
||||
clear:both;
|
||||
|
||||
select {
|
||||
width:80%;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.down-arrow {
|
||||
cursor:pointer;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-top: 8px solid #fc0;
|
||||
position: relative;
|
||||
top: -8px;
|
||||
right: -125px;
|
||||
}
|
||||
|
||||
.settings-holder {
|
||||
float: right;
|
||||
margin-right: 65px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
}
|
||||
.audio-effects {
|
||||
width:40%;
|
||||
@include border_box_sizing;
|
||||
float:left;
|
||||
margin-bottom:20px;
|
||||
padding:10px;
|
||||
position:relative;
|
||||
|
||||
select {
|
||||
width:90%;
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
a.manage-audio-plugins {
|
||||
position:relative;
|
||||
}
|
||||
.down-arrow {
|
||||
cursor:pointer;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-top: 8px solid #fc0;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: -20px;
|
||||
}
|
||||
.settings-holder {
|
||||
right:10%;
|
||||
text-align:right;
|
||||
position:absolute;
|
||||
margin-top: -27px;
|
||||
@include border_box_sizing;
|
||||
padding: 10px 0 10px 10px;
|
||||
}
|
||||
a.button-orange {
|
||||
}
|
||||
}
|
||||
|
||||
.vstScan {
|
||||
|
||||
margin-top:20px;
|
||||
|
||||
.spinner-small {
|
||||
float:left;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size:12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
@import "client/common";
|
||||
|
||||
#configure-outputs-dialog {
|
||||
width: 425px;
|
||||
|
||||
.dialog-inner {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
clear: both;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom:10px;
|
||||
line-height:125%;
|
||||
}
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
|
||||
&.output-1 {
|
||||
margin-bottom:15px;
|
||||
}
|
||||
&.output-2 {
|
||||
margin-bottom:50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,6 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
#configure-tracks-dialog {
|
||||
min-height: 700px;
|
||||
max-height: 700px;
|
||||
width:800px;
|
||||
|
||||
&[current-screen="account/audio"] {
|
||||
|
|
@ -120,21 +118,21 @@
|
|||
}
|
||||
|
||||
.buttons {
|
||||
bottom: 25px;
|
||||
position: absolute;
|
||||
right: 25px;
|
||||
left:25px;
|
||||
position:static;
|
||||
margin-top:20px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.btn-add-new-audio-gear {
|
||||
float:left;
|
||||
position:absolute;
|
||||
left:20px;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
float:right;
|
||||
|
||||
}
|
||||
|
||||
.btn-update-settings {
|
||||
float:right;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -69,6 +69,7 @@ module ClientHelper
|
|||
gon.ftue_maximum_gear_latency = Rails.application.config.ftue_maximum_gear_latency
|
||||
gon.musician_search_meta = MusicianSearch.search_filter_meta
|
||||
gon.band_search_meta = BandSearch.search_filter_meta
|
||||
gon.session_stat_thresholds = Rails.application.config.session_stat_thresholds
|
||||
|
||||
# is this the native client or browser?
|
||||
@nativeClient = is_native_client?
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
script type='text/template' id='template-manage-vsts'
|
||||
ul
|
||||
li data-manage-vst-option="scan"
|
||||
a href='#' scan for new or updated plugins
|
||||
|
||||
li data-manage-vst-option="clear"
|
||||
a href='#' clear plug-in list
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
script type='text/template' id='template-vst-effects'
|
||||
ul
|
||||
li data-manage-vst-option="open-vst"
|
||||
a href='#'
|
||||
| Open
|
||||
span.vst-name
|
||||
|
||||
li data-manage-vst-option="update-track"
|
||||
a href='#' Update Track . . .
|
||||
|
|
@ -20,6 +20,8 @@
|
|||
<%= render "jamServer" %>
|
||||
<%= render "iconInstrumentSelect" %>
|
||||
<%= render "muteSelect" %>
|
||||
<%= render "manageVsts" %>
|
||||
<%= render "vstEffects" %>
|
||||
<%= render "metronome_playback_mode" %>
|
||||
<%= render "clients/wizard/buttons" %>
|
||||
<%= render "clients/wizard/gear/gear_wizard" %>
|
||||
|
|
|
|||
|
|
@ -81,16 +81,7 @@
|
|||
.center
|
||||
%a.button-orange.watch-video{href:'https://www.youtube.com/watch?v=SjMeMZpKNR4', rel:'external'} WATCH VIDEO
|
||||
.wizard-step-column
|
||||
%h2 Unassigned Ports
|
||||
.unassigned-input-channels.channels-holder
|
||||
.wizard-step-column
|
||||
%h2 Track Input Port(s)
|
||||
.tracks
|
||||
.wizard-step-column
|
||||
%h2 Instrument
|
||||
.instruments
|
||||
.output-channels
|
||||
.unassigned-output-channels.channels-holder
|
||||
= react_component 'ConfigureTracks', {}
|
||||
|
||||
.wizard-step{ 'layout-wizard-step' => "3", 'dialog-title' => "Configure Voice Chat", 'dialog-purpose' => "ConfigureVoiceChat" }
|
||||
.ftuesteps
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
.dialog.dialog-overlay-sm.top-parent layout='dialog' layout-id='configure-live-tracks-dialog' id='configure-live-tracks-dialog'
|
||||
= react_component 'ConfigureLiveTracksDialog', {}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
.dialog.dialog-overlay-sm.top-parent layout='dialog' layout-id='configure-outputs-dialog' id='configure-outputs-dialog'
|
||||
= react_component 'ConfigureOutputsDialog', {}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
%h1 configure tracks
|
||||
.dialog-inner
|
||||
.dialog-tabs
|
||||
%a.selected.tab-configure-audio Music Audio
|
||||
%a.selected.tab-configure-audio Inputs & Outputs
|
||||
%a.tab-configure-voice Voice Chat
|
||||
|
||||
.instructions
|
||||
|
|
@ -16,32 +16,8 @@
|
|||
|
||||
.tab.no-selection-range{'tab-id' => 'music-audio'}
|
||||
|
||||
.column
|
||||
.certified-audio-profile-section
|
||||
.sub-header Certified Audio Profile
|
||||
%select.certified-audio-profile
|
||||
.clearall
|
||||
= react_component 'ConfigureTracks', {}
|
||||
|
||||
.unused-audio-inputs-section
|
||||
.sub-header Unused Input Ports
|
||||
.unassigned-input-channels.channels-holder
|
||||
|
||||
.unused-audio-outputs-section
|
||||
.sub-header Unused Output Ports
|
||||
.unassigned-output-channels.channels-holder
|
||||
|
||||
.column
|
||||
.input-tracks-section
|
||||
.sub-column
|
||||
.sub-header Track Input Port(s)
|
||||
.input-tracks.tracks
|
||||
.sub-column
|
||||
.sub-header Instrument
|
||||
.instruments
|
||||
|
||||
.output-channels-section
|
||||
.sub-header Audio Output Port
|
||||
.output-channels
|
||||
.clearall
|
||||
|
||||
|
||||
|
|
@ -72,5 +48,6 @@
|
|||
|
||||
.buttons
|
||||
%a.btn-add-new-audio-gear.button-grey{'layout-link' => 'add-new-audio-gear'} ADD NEW AUDIO GEAR
|
||||
%a.button-orange.btn-update-settings{href:'#'} UPDATE SETTINGS
|
||||
%a.button-grey.btn-cancel{href:'#'} CANCEL
|
||||
%a.button-orange.btn-update-settings{href:'#'} SAVE SETTINGS
|
||||
|
||||
|
|
|
|||
|
|
@ -44,3 +44,5 @@
|
|||
= render 'dialogs/recordingSelectorDialog'
|
||||
= render 'dialogs/soundCloudPlayerDialog'
|
||||
= render 'dialogs/deleteVideoConfirmDialog'
|
||||
= render 'dialogs/configureLiveTracksDialog'
|
||||
= render 'dialogs/configureOutputsDialog'
|
||||
|
|
@ -377,5 +377,27 @@ if defined?(Bundler)
|
|||
config.download_tracker_day_range = 30
|
||||
config.max_user_ip_address = 10
|
||||
config.max_multiple_users_same_ip = 2
|
||||
config.session_stat_thresholds = {
|
||||
network: {
|
||||
wifi: {warn: true, poor: true, eql: true},
|
||||
net_bitrate: {warn: 210, poor: 155, inverse:true},
|
||||
ping: {warn: 40, poor: 70},
|
||||
pkt_loss: {warn: 3, poor: 10},
|
||||
audiojq_median: {warn: 2.1, poor: 5.1}
|
||||
},
|
||||
system: {
|
||||
cpu: {warn: 70, poor:85}
|
||||
},
|
||||
audio: {
|
||||
audio_in_type: {warn: 'Windows WDM-KS', poor: nil, eql:true},
|
||||
audio_out_type: {warn: 'Windows WDM-KS', poor: nil, eql:true},
|
||||
framesize: {warn: 2.6, poor: 2.6},
|
||||
latency: {warn: 10, poor: 20},
|
||||
input_jitter: {warn: 0.5, poor: 1},
|
||||
output_jitter: {warn: 0.5, poor: 1},
|
||||
}
|
||||
}
|
||||
config.vst_enabled = true
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -102,4 +102,6 @@ SampleApp::Application.configure do
|
|||
config.react.variant = :development
|
||||
|
||||
config.time_shift_style = :sox # or sbsms
|
||||
|
||||
config.vst_enabled = true
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,4 +22,5 @@ Gon.global.jamtrack_landing_bubbles_enabled = Rails.application.config.jamtrack_
|
|||
Gon.global.jamtrack_browser_bubbles_enabled = Rails.application.config.jamtrack_browser_bubbles_enabled
|
||||
Gon.global.bugsnag_key = Rails.application.config.bugsnag_key
|
||||
Gon.global.bugsnag_notify_release_stages = Rails.application.config.bugsnag_notify_release_stages
|
||||
Gon.global.vst_enabled = Rails.application.config.vst_enabled
|
||||
Gon.global.env = Rails.env
|
||||
|
|
|
|||
|
|
@ -95,6 +95,11 @@ namespace :jam_tracks do
|
|||
JamTrackImporter.synchronize_all(skip_audio_upload: false)
|
||||
end
|
||||
|
||||
task sync_tim_tracks: :environment do |task, args|
|
||||
JamTrackImporter.storage_format = 'TimTracks'
|
||||
JamTrackImporter.synchronize_all(skip_audio_upload:false)
|
||||
end
|
||||
|
||||
task tency_dups: :environment do |task, args|
|
||||
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue