This commit is contained in:
Seth Call 2015-12-10 20:54:09 -06:00
commit b07aedc9cc
40 changed files with 1885 additions and 335 deletions

View File

@ -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)) {

View File

@ -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;

View File

@ -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);

View File

@ -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";
};

View File

@ -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 = {

View File

@ -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);

View File

@ -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);

View File

@ -9,6 +9,7 @@
//= 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

View File

@ -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 &amp; 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})
})

View File

@ -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)
}
)

View File

@ -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()
})

View File

@ -1,6 +1,7 @@
context = window
MixerActions = @MixerActions
ConfigureTracksActions = @ConfigureTracksActions
@SessionMyTrack = React.createClass({
@ -42,7 +43,9 @@ MixerActions = @MixerActions
WebkitTransform: "rotate(#{pan}deg)"
}
# <div className="track-icon-equalizer" />
if @props.associatedVst?
@equalizerSet = true
vst = `<div className="track-icon-equalizer" />`
`<div className={trackClasses}>
<div className="disabled-track-overlay" />
@ -55,11 +58,10 @@ 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}/>
{vst}
</div>
<br className="clearall"/>
</div>
<br className="clearall"/>
</div>
</div>`
@ -96,6 +98,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 +120,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)
})

View File

@ -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()

View File

@ -19,4 +19,5 @@ context = window
associateVSTWithTrack: {}
associateMIDIWithTrack: {}
desiredTrackType: {}
vstChanged: {}
})

View File

@ -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)
}

View File

@ -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)
}
)

View File

@ -10,6 +10,7 @@ SessionActions = @SessionActions
RecordingActions = @RecordingActions
NotificationActions = @NotificationActions
VideoActions = @VideoActions
ConfigureTracksActions = @ConfigureTracksActions
@SessionStore = Reflux.createStore(
{
@ -733,6 +734,7 @@ VideoActions = @VideoActions
@handleAutoOpenJamTrack()
@watchBackendStats()
ConfigureTracksActions.reset(false)
)
.fail((xhr) =>
@updateCurrentSession(null)

View File

@ -733,6 +733,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);

View File

@ -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) {

View File

@ -53,6 +53,8 @@
*= require dialogs/dialog
*= require ./iconInstrumentSelect
*= require ./muteSelect
*= require ./manageVsts
*= require ./vstEffects
*= require ./metronomePlaybackModeSelect
*= require ./terms
*= require ./createSession

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;;
}
}
}
}

View File

@ -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"] {

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -0,0 +1,9 @@
script type='text/template' id='template-vst-effects'
ul
li data-manage-vst-option="open-vst"
a href='#'
| Open&nbsp;
span.vst-name
li data-manage-vst-option="update-track"
a href='#' Update Track . . .

View File

@ -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" %>

View File

@ -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

View File

@ -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', {}

View File

@ -0,0 +1,2 @@
.dialog.dialog-overlay-sm.top-parent layout='dialog' layout-id='configure-outputs-dialog' id='configure-outputs-dialog'
= react_component 'ConfigureOutputsDialog', {}

View File

@ -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

View File

@ -44,3 +44,5 @@
= render 'dialogs/recordingSelectorDialog'
= render 'dialogs/soundCloudPlayerDialog'
= render 'dialogs/deleteVideoConfirmDialog'
= render 'dialogs/configureLiveTracksDialog'
= render 'dialogs/configureOutputsDialog'

View File

@ -395,5 +395,7 @@ if defined?(Bundler)
output_jitter: {warn: 0.5, poor: 1},
}
}
config.vst_enabled = true
end
end

View File

@ -102,4 +102,6 @@ SampleApp::Application.configure do
config.react.variant = :development
config.time_shift_style = :sox # or sbsms
config.vst_enabled = true
end

View File

@ -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

View File

@ -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