initial version of jamblaster management page

This commit is contained in:
Seth Call 2016-06-24 09:15:04 -05:00
parent e95c7fe8e5
commit bd0dc40a4c
43 changed files with 1512 additions and 261 deletions

View File

@ -66,8 +66,8 @@ module JamRuby
return
end
#wav_file = File.join(tmp_dir, File.basename(click_track_file[:original_filename]))
#JamTrackImporter.song_storage_manager.download(click_track_file[:original_filename], wav_file)
wav_file = File.join(tmp_dir, File.basename(click_track_file[:original_filename]))
JamTrackImporter.song_storage_manager.download(click_track_file[:original_filename], wav_file)
JamTrack.transaction do
click_track = jam_track.click_track
@ -81,6 +81,8 @@ module JamRuby
click_track.instrument_id = 'computer'
click_track.jam_track = jam_track
click_track.position = 10000
click_track.wav_file = wav_file
if !click_track.save
@@log.error("unable to create jamtrack click track #{click_track.errors.inspect}")
finish("jam_track_click", "unable to create: #{click_track.errors.inspect}")
@ -1601,6 +1603,8 @@ module JamRuby
end
end
def assign_instrument_parts(wav_file, tracks, addt_files, reassign = false)
if !reassign
track = JamTrackTrack.new
@ -2651,6 +2655,34 @@ module JamRuby
importer
end
# created for helbing tracks which had .mp3 as click track
def convert_click_track_to_wav(jam_track)
importer = JamTrackImporter.new
importer.name = jam_track.name
track = jam_track.click_track
if !track
Dir.mktmpdir do |tmp_dir|
# something like: "mapped/Victor Young - Stella By Starlight/meta.yml"
metalocation = jam_track.metalocation
base_dir = metalocation[0...metalocation.rindex('/')]
click_mp3 = File.join(tmp_dir, 'Click.mp3')
click_wav = File.join(tmp_dir, 'Click.wav')
song_storage_manager.download(base_dir + '/Click.mp3', click_mp3)
`ffmpeg -i "#{click_mp3}" "#{click_wav}"`
song_storage_manager.upload(base_dir + '/Click.wav', click_wav)
importer.finish("success", nil)
end
else
importer.finish('success', nil)
end
importer
end
def synchronize_jamtrack_master_preview(jam_track)
importer = JamTrackImporter.new
importer.name = jam_track.name
@ -2717,7 +2749,8 @@ module JamRuby
def import_click_tracks
importers = []
JamTrack.all.each do |jam_track|
licensor = JamTrackLicensor.find_by_name!('Stockton Helbing')
JamTrack.where(licensor_id: licensor.id).each do |jam_track|
#jam_track = JamTrack.find('126')
importers << import_click_track(jam_track)
end
@ -2796,6 +2829,32 @@ module JamRuby
end
end
def convert_click_track_to_wavs
importers = []
licensor = JamTrackLicensor.find_by_name!('Stockton Helbing')
JamTrack.where(licensor_id: licensor.id).each do |jam_track|
importers << convert_click_track_to_wav(jam_track)
end
@@log.info("SUMMARY")
@@log.info("-------")
importers.each do |importer|
if importer
if importer.reason == "success" || importer.reason == "jam_track_exists" || importer.reason == "other_processing"
@@log.info("#{importer.name} #{importer.reason}")
else
@@log.error("#{importer.name} failed to import.")
@@log.error("#{importer.name} reason=#{importer.reason}")
@@log.error("#{importer.name} detail=#{importer.detail}")
end
else
@@log.error("NULL IMPORTER")
end
end
end
def synchronize_jamtrack_aac_previews
importers = []
@ -2818,8 +2877,6 @@ module JamRuby
else
@@log.error("NULL IMPORTER")
end
end
end
@ -3118,11 +3175,11 @@ module JamRuby
def resync_instruments(licensor)
load_paris_mappings if @paris_mapping.nil?
load_paris_mappings if @paris_mapping.nil? && is_paris_storage?
JamTrack.where(licensor_id: licensor.id).each do |jam_track|
if @paris_metadata[jam_track.vendor_id].nil?
if is_paris_storage? && @paris_metadata[jam_track.vendor_id].nil?
next
end
puts "RESYNCING JAMTRACK #{jam_track.id}"
@ -3135,6 +3192,7 @@ module JamRuby
#puts ">>>>>>>>> HIT KEY TO CONTINUE <<<<<<<<<<"
#STDIN.gets
break
end
end

View File

@ -323,7 +323,16 @@ module JamRuby
end
if options[:artist].present?
query = query.where("original_artist=?", options[:artist])
artist_param = options[:artist]
# todo: add licensor option
if artist_param == 'Stockton Helbing'
licensor = JamTrackLicensor.find_by_name('Stockton Helbing')
if licensor
query = query.where(licensor_id: licensor.id)
end
else
query = query.where("original_artist=?", options[:artist])
end
end
if options[:song].present?

View File

@ -128,10 +128,11 @@
return false;
});
//$btnAddNewGear.click(function() {
$btnAddNewGear.click(function() {
// return false;
//});
app.layout.showDialog("add-new-audio-gear")
return false;
});
$btnUpdateTrackSettings.click(function() {
if(voiceChatHelper.trySave()) {
@ -245,6 +246,8 @@
function afterShow() {
sessionUtils.SessionPageEnter();
window.JamBlasterActions.resyncBonjour()
//context.ConfigureTracksActions.vstScan();
}
@ -271,9 +274,9 @@
$dialog = $('#configure-tracks-dialog');
$instructions = $dialog.find('.instructions span');
$musicAudioTab = $dialog.find('div[tab-id="music-audio"]');
$musicAudioTab = $dialog.find('div[data-tab-id="music-audio"]');
$musicAudioTabSelector = $dialog.find('.tab-configure-audio');
$voiceChatTab = $dialog.find('div[tab-id="voice-chat"]');
$voiceChatTab = $dialog.find('div[data-tab-id="voice-chat"]');
$voiceChatTabSelector = $dialog.find('.tab-configure-voice');
$certifiedAudioProfile = $dialog.find('.certified-audio-profile');
$btnCancel = $dialog.find('.btn-cancel');

View File

@ -210,6 +210,7 @@
JK.JamServer.registerMessageCallback(JK.MessageType.STOP_APPLICATION, function(header, payload) {
context.jamClient.ShutdownApplication();
});
}
function handleGettingStarted(app) {

View File

@ -49,9 +49,15 @@
var html = context._.template($('#template-jamblaster-options').html(), options, { variable: 'data' })
var extraClasses = ' '
var width = 120;
if(options.isDynamicPorts || options.autoconnect) {
extraClasses += 'isDynamicPorts '
width = 140;
}
context.JK.hoverBubble($parent, html, {
trigger:'none',
cssClass: 'jamblaster-options-popup',
cssClass: 'jamblaster-options-popup' + extraClasses,
spikeGirth:0,
spikeLength:0,
overlap: -10,

View File

@ -8,6 +8,7 @@
//= require ./react-components/stores/UserActivityStore
//= require ./react-components/stores/LessonTimerStore
//= require ./react-components/stores/SchoolStore
//= require ./react-components/stores/JamBlasterStore
//= require ./react-components/stores/StripeStore
//= require ./react-components/stores/AvatarStore
//= require ./react-components/stores/AttachmentStore

View File

@ -0,0 +1,92 @@
context = window
JamBlasterActions = @JamBlasterActions
@ConfigureTracksDialogContents = React.createClass({
mixins: [Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@JamBlasterStore, "onJamBlasterChanged")]
onAppInit: (@app) ->
onJamBlasterChanged: (jamblasterState) ->
@setState(jamblasterState)
getInitialState: () ->
{
timer: null,
pairing: false,
pairStart: null,
allJamBlasters: [],
pairingTimeout: false,
paired: false,
userJamBlasters: [],
localJamBlasters: []
}
render: () ->
pairedJamBlaster = this.state.pairedJamBlaster
hasPairedJamBlaster = pairedJamBlaster?
if hasPairedJamBlaster
`<JamBlasterTrackConfig />`
else
`<div>
<div className="dialog-tabs">
<a className="selected tab-configure-audio">Inputs &amp; Outputs</a>
<a className="tab-configure-voice">Voice Chat</a>
</div>
<div className="instructions">
<span>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.</span>
</div>
<div className="clearall"></div>
<div className="tab no-selection-range" data-tab-id="music-audio">
<ConfigureTracks />
<div className="clearall"></div>
</div>
<div className="tab" data-tab-id="voice-chat">
<form className="select-voice-chat-option section voice">
<div className="sub-header">Select Voice Chat Option</div>
<div className="voicechat-option reuse-audio-input">
<input type="radio" name="voicechat" defaultChecked="checked"/>
<h3>Use Music Microphone</h3>
<p>I am already using a microphone to capture my vocal or instrumental music, so I can talk with other
musicians using that microphone</p>
</div>
<div className="voicechat-option use-chat-input">
<input type="radio" name="voicechat"/>
<h3>Use Chat Microphone</h3>
<p>I am not using a microphone for acoustic instruments or vocals, so use the input selected to the right
for voice chat during my sessions</p>
</div>
</form>
<div className="clearall"></div>
<div className="select-voice-chat">
<div className="sub-header">Voice Chat Input</div>
<div className="ftue-box chat-inputs"></div>
<div className="vu-meter">
<div className="ftue-controls">
<div className="ftue-vu-left voice-chat-vu-left"></div>
<div className="ftue-fader chat-fader"></div>
<div className="gain-label">GAIN</div>
<div className="ftue-vu-right voice-chat-vu-right"></div>
</div>
</div>
</div>
<div className="clearall"></div>
</div>
<div className="buttons">
<a className="btn-add-new-audio-gear button-grey">ADD NEW AUDIO GEAR</a>
<a className="button-grey btn-cancel">CANCEL</a>
<a className="button-orange btn-update-settings">SAVE SETTINGS</a>
</div>
</div>`
}
)

View File

@ -44,7 +44,7 @@ context = window
name = @root.find('.name').val()
characterMatch = /^[a-z0-9,' -]+$/i
characterMatch = /^[^a-z0-9,' -]+$/i
if name.length == 0 || name == ''
context.JK.Banner.showAlert('invalid name', 'Please specify a name.')
@ -64,7 +64,7 @@ context = window
if !result
context.JK.Banner.showAlert('unable to set the name',
'Please email support@jamkazam.com with the name you are trying to set, or refresh the page and try again.')
'Please email support@jamkazam.com and let us know the name you are specifying unsuccessfully, or refresh the page and try again.')
else
@app.layout.closeDialog('jamblaster-name-dialog')
render: () ->
@ -74,13 +74,15 @@ context = window
<h1>update name of JamBlaster</h1>
</div>
<div>
<div className="dialog-inner">
<p>You can change the display name for this JamBlaster. The name can only contain A-Z, 0-9, commas, apostrophes,
<p className="help-text">You can change the display name for this JamBlaster. The name can only contain A-Z, 0-9, commas, apostrophes,
spaces, or hyphens. A valid example: "John Doe's JamBlaster"</p>
<div className="field">
<label>JamBlaster Name:</label>
<input className="name" type="text" value={this.state.name} onChange={this.onNameChange}></input>
</div>
<div className="actions">
<a onClick={this.doCancel} className="button-grey">CANCEL</a>

View File

@ -1,13 +1,15 @@
context = window
JamBlasterActions = @JamBlasterActions
@JamBlasterPairingDialog = React.createClass({
mixins: [@BonjourMixin, Reflux.listenTo(@AppStore, "onAppInit")]
teacher: null
mixins: [@BonjourMixin, Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@JamBlasterStore, "onJamBlasterChanged")]
beforeShow: (args) ->
logger.debug("JamBlasterPairingDialog.beforeShow", args.d1)
@setState({timer: null, pairing: false, bonjourClientId: args.d1, pairingTimeout: false, paired: false})
@resyncBonjour()
JamBlasterActions.resyncBonjour()
@setTimer(false)
afterHide: () ->
@ -21,16 +23,19 @@ context = window
@app.bindDialog('jamblaster-pairing-dialog', dialogBindings);
onJamBlasterChanged: (jamblasterState) ->
@setState(jamblasterState)
getInitialState: () ->
{
timer: null
timer: null,
pairing: false,
pairStart: null,
clients: [],
pairingTimeout: false
allJamBlasters: [],
pairingTimeout: false,
paired: false,
userJamBlasters: [],
localClients: []
localJamBlasters: []
}
clearTimer: () ->
@ -51,7 +56,7 @@ context = window
else
time = 60000 # every minute
@interval = setInterval((() => @resyncBonjour()), time)
@interval = setInterval((() => JamBlasterActions.resyncBonjour()), time)
pairingTimer: () ->
@clearPairingTimer()
@ -101,7 +106,7 @@ context = window
if @state.pairing
return
@setState({pairing: true, pairStart: new Date().getTime(), timer: 60})
@setState({pairing: true, pairStart: new Date().getTime(), timer: 60, pairingTimeout: false, paired: false})
@setTimer(true)
client = @findJamBlaster(this.state.bonjourClientId)
@ -110,52 +115,55 @@ context = window
context.JK.Banner.showNotice("JamBlaster already paired", "This JamBlaster is already paired.")
@app.layout.closeDialog("jamblaster-pairing-dialog", true)
else if client?
if client.connect_url?
context.jamClient.startPairing(client.connect_url)
else
context.JK.Banner.showAlert("JamBlaster offline", "JamBlaster appears to be offline. Please reboot it")
logger.debug("trying to connect to #{client.connect_url}")
if client.connect_url?
@pairingTimer()
context.jamClient.startPairing(client.connect_url)
else
context.JK.Banner.showAlert("JamBlaster offline", "JamBlaster appears to be offline. Please reboot it")
context.JK.Banner.showAlert("JamBlaster offline", "JamBlaster appears to be offline. Please reboot it.")
else
context.JK.Banner.showAlert("JamBlaster offline", "JamBlaster appears to be offline. Please reboot it.")
render: () ->
if @state.pairing
countdown = @state.timer
countdown = `<div className="countdown-msg">You have <span className="countdown">{Math.round(this.state.timer)}</span> seconds to push the button on the back of the JamBlaster.
</div>`
else
countdown = null
cancelClasses = {"button-grey": true, disabled: @state.pairing}
connectClasses = {"button-orange": true, disabled: @state.pairing}
actions = `<div className="actions">
<a onClick={this.doCancel} className={cancelClasses}>CANCEL</a>
<a onClick={this.pair} className={connectClasses}>CONNECT</a>
<span className="countdown">{countdown}</span>
<a onClick={this.doCancel} className={classNames(cancelClasses)}>CANCEL</a>
<a onClick={this.pair} className={classNames(connectClasses)}>CONNECT</a>
</div>`
if @state.paired
message = `<p>You have successfully connected to this JamBlaster!</p>`
message = `<p className="message">You have successfully connected to this JamBlaster!</p>`
actions = `<div className="actions">
<a onClick={this.close} className="button-orange">CLOSE</a>
</div>`
else if @state.pairingTimeout
message = `<p>No connection established. You may click the CONNECT button to try again. If you cannot connect, please contact us at support@jamkazam.com.</p>`
message = `<p className="message">No connection established. You may click the CONNECT button to try again. If you cannot connect, please contact us at support@jamkazam.com.</p>`
else
cancelClasses = {"button-grey": true, disabled: @state.pairing}
connectClasses = {"button-orange": true, disabled: @state.pairing}
`<div>
<div className="content-head">
<img className="content-icon" src="/assets/content/icon_add.png" height={19} width={19}/>
<h1>connect to JamBlaster</h1>
</div>
<div>
<div className="dialog-inner">
<p>To connect this application/device with the selected JamBlaster, please click the Connect button below, and then push the small black plastic button located on the back of the JamBlaster between the USB and power ports to confirm this pairing within 60 seconds of clicking the Connect button below.</p>
<p>To connect this application/device with the selected JamBlaster, please click the CONNECT button below, and then push the small black plastic button located on the back of the JamBlaster between the USB and power ports to confirm this pairing within 60 seconds of clicking the Connect button below.</p>
{message}
{actions}
{countdown} {actions}
</div>
</div>`

View File

@ -0,0 +1,83 @@
context = window
@JamBlasterPortDialog = React.createClass({
mixins: [Reflux.listenTo(@AppStore, "onAppInit")]
teacher: null
beforeShow: (args) ->
logger.debug("JamBlasterPortDialog.beforeShow")
afterHide: () ->
onAppInit: (@app) ->
dialogBindings = {
'beforeShow': @beforeShow,
'afterHide': @afterHide
};
@app.bindDialog('jamblaster-port-dialog', dialogBindings);
getInitialState: () ->
{
name: ''
}
componentDidMount: () ->
@root = $(@getDOMNode())
@dialog = @root.closest('.dialog')
doCancel: (e) ->
e.preventDefault()
@app.layout.closeDialog('jamblaster-port-dialog', true);
updatePort: (e) ->
e.preventDefault()
# validate
staticPort = @root.find('.port').val()
staticPort = new Number(staticPort);
console.log("staticPort", staticPort)
if context._.isNaN(staticPort)
@app.layout.notify({title: 'No Settings Have Been Saved!', text: 'Please enter a number from 1026-49150.'})
return
if staticPort < 1026 || staticPort >= 65525
@app.layout.notify({title: 'No Settings Have Been Saved!', text: 'Please pick a port from 1026 to 65524.'})
return
result = context.jamClient.setJbPortBindState({use_static_port: true, static_port: staticPort})
if !result
context.JK.Banner.showAlert('unable to set a static port',
'Please email support@jamkazam.com and let us know the port number you are specifying unsuccessfully, or refresh the page and try again.')
else
@app.layout.closeDialog('jamblaster-port-dialog')
render: () ->
`<div>
<div className="content-head">
<img className="content-icon" src="/assets/content/icon_add.png" height={19} width={19}/>
<h1>set static port for JamBlaster</h1>
</div>
<div className="dialog-inner">
<p className="help-text">You can specify any port you like, but we recommend an even number in the range including 1026-49150 to avoid conflicts with other programs. When configuring Port Forwarding in your router, be sure to open this port <b>along with the next 10</b>. For example, if this field is 12000, then in your router, forward ports 12000-12010 to your computer's IP address.</p>
<div className="field">
<label>Port:</label>
<input className="port" type="text"></input>
</div>
<div className="actions">
<a onClick={this.doCancel} className="button-grey">CANCEL</a>
<a onClick={this.updatePort} className="button-orange name">SAVE</a>
</div>
</div>
</div>`
})

View File

@ -1,6 +1,7 @@
context = window
rest = context.JK.Rest()
logger = context.JK.logger
JamBlasterActions = @JamBlasterActions
@JamBlasterScreen = React.createClass({
@ -9,7 +10,8 @@ logger = context.JK.logger
@PostProcessorMixin,
@BonjourMixin,
Reflux.listenTo(AppStore, "onAppInit"),
Reflux.listenTo(UserStore, "onUserChanged")
Reflux.listenTo(UserStore, "onUserChanged"),
Reflux.listenTo(JamBlasterStore, "onJamBlasterChanged")
]
TILE_AUDIO: 'audio'
@ -17,7 +19,10 @@ logger = context.JK.logger
TILE_MANAGEMENT: 'management'
TILE_USB: 'usb'
TILES: ['audio', 'internet', 'management', 'usb']
TILES: ['management', 'audio', 'internet']
networkStale: false
ipRegex: /^0([0-9])+/
onAppInit: (@app) ->
@app.bindScreen('jamblaster',
@ -26,44 +31,120 @@ logger = context.JK.logger
onUserChanged: (userState) ->
@setState({user: userState?.user})
componentDidMount: () ->
@root = $(@getDOMNode())
onJamBlasterChanged: (jamblasterState) ->
@setState(jamblasterState)
componentDidMount: () ->
@checkboxes = [{selector: 'input.dhcp', stateKey: 'dhcp'}]
@root = $(@getDOMNode())
@iCheckify()
componentWillUpdate: (nextProps, nextState) ->
componentDidUpdate: () ->
@iCheckify()
items = @root.find('.jamtable .optionsColumn .jamblaster-options-btn')
$.each(items, (i, node) => (
$node = $(node)
jamblaster = @findJamBlaster($node.attr('data-jamblaster-id'))
jamblaster = @findJamBlaster({
server_id: $node.attr('data-jamblaster-id'),
ipv6_addr: $node.attr('data-jamblaster-addr')
})
$node.jamblasterOptions(jamblaster).off(context.JK.EVENTS.JAMBLASTER_ACTION).on(context.JK.EVENTS.JAMBLASTER_ACTION,
@jamblasterOptionSelected)
))
@root.find('input.networksettings').inputmask({
alias: "ip",
"placeholder": "_"
});
checkboxChanged: (e) ->
checked = $(e.target).is(':checked')
value = $(e.target).val()
console.log("checkbox changed: ", value)
@setState({userdhcp: "true" == value})
componentWillUpdate: (nextProps, nextState) ->
if @networkStale && @state.pairedJamBlaster?
console.log("stale network update", @state)
nextState.userdhcp = @state.pairedJamBlaster.network?.dhcp
nextState.useraddr = @state.pairedJamBlaster.network?.addr
nextState.usersubnet = @state.pairedJamBlaster.network?.subnet
nextState.usergateway = @state.pairedJamBlaster.network?.gateway
nextState.userdns1 = @state.pairedJamBlaster.network?.dns1
nextState.userdns2 = @state.pairedJamBlaster.network?.dns2
nextState.userdhcperror = false
nextState.useraddrerror = false
nextState.usersubneterror = false
nextState.usergatewayerror = false
nextState.userdns1error = false
nextState.userdns2error = false
@networkStale = false
#context.JK.popExternalLinks(@root)
jamblasterOptionSelected: (e, data) ->
jamblasterId = data.options.id
jamblaster = @findJamBlaster(jamblasterId)
jamblaster = data.options
jamblaster = @findJamBlaster(jamblaster)
if data.option == 'auto-connect'
context.JK.Banner.showNotice('Auto-Connect',
'Auto-Connect is always on by default. It can not currently configurable.')
JamBlasterActions.setAutoPair(!jamblaster.autoconnect)
else if data.option == 'restart'
context.JK.Banner.showNotice('Restart',
'To restart the JamBlaster, you must manually cycle power (unplug, then plug).')
context.JK.Banner.showYesNo({
title: "reboot JamBlaster",
html: "Are you sure?"
yes: =>
result = context.jamClient.rebootJamBlaster()
if result
setTimeout((() => context.JK.Banner.showNotice("JamBlaster is rebooting",
"It should be back online within a minute.")), 1)
setTimeout((() => JamBlasterActions.resyncBonjour()), 1000)
else
setTimeout((() => context.JK.Banner.showAlert("could not reboot",
"The JamBlaster could not be rebooted remotely. Please cycle the power manually.")), 1)
})
else if data.option == 'name'
context.layout.showDialog('jamblaster-name-dialog').one(context.JK.EVENTS.DIALOG_CLOSED, (e, data) =>
@resyncBonjour()
@app.layout.showDialog('jamblaster-name-dialog', {d1: jamblaster.name}).one(context.JK.EVENTS.DIALOG_CLOSED,
(e, data) =>
setTimeout((() => JamBlasterActions.resyncBonjour()), 1000)
)
else if data.option == 'check-for-updates'
context.JK.Banner.showNotice('Check for Update',
'The JamBlaster only checks for updates on start up. Please reboot the JamBlaster')
'The JamBlaster only checks for updates when booting up. Please reboot the JamBlaster to initiate an update check.')
else if data.option == 'set-static-ports'
context.layout.showDialog('jamblaster-port-dialog')
if jamblaster.isDynamicPorts
context.JK.Banner.showYesNo({
title: "revert to dynamic ports",
html: "Your JamBlaster is currently configured to use ports #{jamblaster.portState.static_port} - #{jamblaster.portState.static_port + 10}). Would you like to revert to the use of dynamic ports for UDP communication?"
yes: =>
context.jamClient.setJbPortBindState({use_static_port: false, static_port: 12000})
JamBlasterActions.clearPortBindState()
#setTimeout((() => JamBlasterActions.resyncBonjour()), 1000)
JamBlasterActions.resyncBonjour()
setTimeout((() => context.JK.Banner.showNotice("reboot JamBlaster",
"For these settings to take effect, you must restart the JamBlaster.")), 1)
})
else
@app.layout.showDialog('jamblaster-port-dialog').one(context.JK.EVENTS.DIALOG_CLOSED, (e, data) =>
JamBlasterActions.clearPortBindState()
JamBlasterActions.resyncBonjour()
context.JK.Banner.showNotice("reboot JamBlaster",
"For these settings to take effect, you must restart the JamBlaster.")
)
else if data.option == 'factory-reset'
context.JK.Banner.showNotice('Factory Reset',
'The JamBlaster only checks for updates when it boots up, and if there is an update available, it will automatically begin updating.<br/><br/>Please reboot the JamBlaster to initiate an update check.')
@ -74,17 +155,18 @@ logger = context.JK.logger
selected: 'management',
user: null,
userJamBlasters: [],
localClients: [],
clients: []
localJamBlasters: [],
allJamBlasters: []
}
beforeHide: (e) ->
@clearTimer()
beforeShow: (e) ->
@setTimer()
afterShow: (e) ->
@resyncBonjour()
JamBlasterActions.resyncBonjour()
openMenu: (client, e) ->
logger.debug("open jamblaster options menu")
@ -93,18 +175,34 @@ logger = context.JK.logger
$this = $this.closest('.jamblaster-options-btn')
$this.btOn()
clearTimer: () ->
if @interval?
clearInterval(@interval)
@interval = null
# Refresh bonjour status every 30 second, so that we catch major changes
setTimer: () ->
@clearTimer()
time = 30000 # every 30 seconds
@interval = setInterval((() => JamBlasterActions.resyncBonjour()), time)
connect: (client, e) ->
logger.debug("beginning pairing to #{client.connect_url}")
context.jamClient.startPairing(client.connect_url)
@clearTimer()
@app.layout.showDialog('jamblaster-pairing-dialog', {d1: client}).one(context.JK.EVENTS.DIALOG_CLOSED, (e, data) =>
JamBlasterActions.resyncBonjour()
@setTimer()
)
disconnect: (client, e) ->
logger.debug("disconnecting from currently paired client #{client.connect_url}")
context.jamClient.endPairing()
mergeClients: () ->
setTimeout((() => JamBlasterActions.resyncBonjour()), 1000)
mergeClients: () ->
clientsJsx = []
for client in @state.clients
for client in @state.allJamBlasters
if client.display_name?
displayName = client.display_name
else
@ -122,7 +220,9 @@ logger = context.JK.logger
else
connect = `<span className="connectColumn">offline</span>`
options = `<span className="optionsColumn"><a data-jamblaster-id={client.id} className="jamblaster-options-btn"
options = `<span className="optionsColumn"><a data-jamblaster-id={client.server_id}
data-jamblaster-addr={client.ipv6_addr}
className="jamblaster-options-btn"
onClick={this.openMenu.bind(this, client)}>more options
<div className="details-arrow arrow-down"/>
</a></span>`
@ -134,6 +234,10 @@ logger = context.JK.logger
clientsJsx
mainContent: () ->
if !@state.user?.id || !@state.userJamBlasters? || !@state.localJamBlasters?
return `<div>Loading ...</div>`
if @state.selected == @TILE_AUDIO
@audio()
else if @state.selected == @TILE_INTERNET
@ -143,9 +247,141 @@ logger = context.JK.logger
else if @state.selected == @TILE_USB
@usb()
audio: () ->
`<div className="audio-content">
<JamBlasterTrackConfig />
</div>`
ipSettingsChanged: (key, e) ->
userKey = 'user' + key
state = {}
ip = $(e.target).val()
state[userKey] = ip
if ip?
bits = ip.split('.')
console.log("bits", bits)
for bit in bits
result = @ipRegex.test(bit.replace(/_/g, ''))
console.log("STILL GOT THAT _?", result)
error = false
if result == true || bit == "___"
error = true
break
if error
console.log("SETTING ERROR for " + userKey + 'error')
state[userKey + 'error'] = true
else
state[userKey + 'error'] = false
this.setState(state)
onSaveNetworkSettings: (e) ->
e.preventDefault()
JamBlasterActions.saveNetworkSettings({
dhcp: this.state.userdhcp,
addr: this.state.useraddr,
gateway: this.state.usergateway,
subnet: this.state.usersubnet,
dns1: this.state.userdns1,
dns2: this.state.userdns2
})
@networkStale = true
usb: () ->
`<div className="networking-content">
<h3>USB Settings</h3>
</div>`
internet: () ->
pairedJamBlaster = this.state.pairedJamBlaster
hasPairedJamBlaster = pairedJamBlaster?
isJamBlasterDhcp = !!this.state.userdhcp
if hasPairedJamBlaster
status =
`<div className="jamblaster-network-status">
<div className="status-field"><span className="status-label">Ethernet:</span><span>Connected</span></div>
<div className="status-field"><span className="status-label">Internet:</span><span>Connected</span></div>
<div className="status-field"><span className="status-label">Streaming:</span><span>Supported</span></div>
</div>`
else
status =
`<div className="jamblaster-network-status">
<div className="status-field"><span className="status-label">Ethernet:</span><span>Unknown</span></div>
<div className="status-field"><span className="status-label">Internet:</span><span>Unknown</span></div>
<div className="status-field"><span className="status-label">Streaming:</span><span>Unknown</span></div>
</div>`
addrClasses = {field: true, error: this.state.useraddrerror}
subnetClasses = {field: true, error: this.state.usersubneterror}
gatewayClasses = {field: true, error: this.state.usergatewayerror}
dns1Classes = {field: true, error: this.state.userdns1error}
dns2Classes = {field: true, error: this.state.userdns2error}
saveBtnClasses = {}
saveBtnClasses["save-settings-btn"] = true
saveBtnClasses["button-orange"] = true
saveBtnClasses["disabled"] = !hasPairedJamBlaster
ipdisabled = !hasPairedJamBlaster || isJamBlasterDhcp
`<div className="networking-content">
<h3>Internet Settings</h3>
<div className="column-left column">
<div className="assign-ip-address">
<h4>Assign IP Address</h4>
<div className="dhcpfield">
<input className="dhcp" type="radio" name="dhcp" readOnly={true} value="true" defaultChecked={isJamBlasterDhcp}
disabled={!hasPairedJamBlaster}/><label>Using DHCP</label>
</div>
<div className="dhcpfield">
<input className="dhcp" type="radio" name="dhcp" readOnly={true} value="false" defaultChecked={!isJamBlasterDhcp}
disabled={!hasPairedJamBlaster}/><label>Manually</label>
</div>
</div>
<div className="manual-settings">
<h4>Manual Settings</h4>
<div className={classNames(addrClasses)}>
<label>IP Address:</label><input className="addr networksettings" type="text" value={this.state.useraddr} name="addr" onBlur={this.ipSettingsChanged.bind(this, 'addr')} disabled={ipdisabled}></input>
</div>
<div className={classNames(subnetClasses)}>
<label>Subnet Mask:</label><input className="subnet networksettings" type="text" value={this.state.usersubnet} name="subnet" onBlur={this.ipSettingsChanged.bind(this, 'subnet')} disabled={ipdisabled}></input>
</div>
<div className={classNames(gatewayClasses)}>
<label>Router:</label><input className="gateway networksettings" type="text" value={this.state.usergateway} name="gateway" onBlur={this.ipSettingsChanged.bind(this, 'gateway')} disabled={ipdisabled}></input>
</div>
<div className={classNames(dns1Classes)}>
<label>DNS Server 1:</label><input className="dns1 networksettings" type="text" value={this.state.userdns1} name="dns1" onBlur={this.ipSettingsChanged.bind(this, 'dns1')} disabled={ipdisabled}></input>
</div>
<div className={classNames(dns1Classes)}>
<label>DNS Server 2:</label><input className="dns2 networksettings" type="text" value={this.state.userdns2} name="dns1" onBlur={this.ipSettingsChanged.bind(this, 'dns2')} disabled={ipdisabled}></input>
</div>
</div>
<div className="save-settings">
<a onClick={this.onSaveNetworkSettings} className={classNames(saveBtnClasses)}>SAVE SETTINGS</a>
</div>
</div>
<div className="column-right column">
<h4>Network Status</h4>
{status}
</div>
</div>`
management: () ->
clients = @mergeClients()
if @state.refreshingBonjour
refreshingText = 'SCANNING'
else
refreshingText = 'SCAN NETWORK'
refreshClasses = {"resync-bonjour": true, "button-orange": true, disabled: @state.refreshingBonjour}
`<div className="management-content">
<table className="jamtable">
<thead>
<tr>
@ -156,23 +392,53 @@ logger = context.JK.logger
{clients}
</tbody>
</table>
<p>If you don't see your JamBlaster listed above, please check to make sure you have power connected to your JamBlaster,
and make sure your JamBlaster is connected via an Ethernet cable to the same router/network as the device on which you are viewing this application.
<a onClick={JamBlasterActions.resyncBonjour} className={classNames(refreshClasses)}>{refreshingText}</a>
<p className="help-text">If you don't see your JamBlaster listed above, please check to make sure you have power
connected to your JamBlaster,
and make sure your JamBlaster is connected via an Ethernet cable to the same router/network as the device on
which you are viewing this application.
</p>
</div>`
tiles: () ->
@TILES
selectionMade: (selection, e) ->
e.preventDefault()
if selection == @TILE_INTERNET
@networkStale = true
@setState({selected: selection})
createTileLink: (i, tile) ->
if this.state.selected?
active = this.state.selected == tile
else
active = tile == @TILE_MANAGEMENT
tileClasses = {activeTile: active, 'jamblaster-tile': true}
tileClasses = classNames(tileClasses)
classes = classNames({last: i == @tiles().length - 1})
return `<div key={i} className={tileClasses}><a className={classes}
onClick={this.selectionMade.bind(this, tile)}>{tile}</a></div>`
render: () ->
disabled = @state.updating
if !@state.user?.id || !@state.userJamBlasters? || !@state.localClients?
return `<div>Loading</div>`
tiles = []
for tile, i in @tiles()
tiles.push(@createTileLink(i, tile))
`<div className="content-body-scroller">
<div className="">
<h2>jamblaster settings</h2>
<div className="tiles">
{tiles}
</div>
<div className="jamclass-section">

View File

@ -0,0 +1,187 @@
context = window
JamBlasterActions = @JamBlasterActions
@JamBlasterTrackConfig = React.createClass({
mixins: [@ICheckMixin, @BonjourMixin, Reflux.listenTo(@AppStore, "onAppInit"),
Reflux.listenTo(@JamBlasterStore, "onJamBlasterChanged")]
beforeShow: () ->
onAppInit: (@app) ->
onJamBlasterChanged: (jamblasterState) ->
@setState(jamblasterState)
componentDidMount: () ->
@checkboxes = [
{selector: 'input.track1Active', stateKey: 'track1Active'},
{selector: 'input.track2Active', stateKey: 'track2Active'},
{selector: 'input.micActive', stateKey: 'micActive'},
{selector: 'input.track1Phantom', stateKey: 'track1Phantom'},
{selector: 'input.track2Phantom', stateKey: 'track2Phantom'},
{selector: 'input.inputTypeTrack1', stateKey: 'inputTypeTrack1'},
{selector: 'input.inputTypeTrack2', stateKey: 'inputTypeTrack2'},
{selector: 'input.combined', stateKey: 'combined'}]
@root = $(@getDOMNode())
@iCheckify()
componentDidUpdate: () ->
@iCheckify()
checkboxChanged: (e) ->
checked = $(e.target).is(':checked')
value = $(e.target).val()
name = $(e.target).attr('name')
console.log("checkbox changed: ", value)
if $(e.target).attr('type') == 'checkbox'
state = {}
state[name] = checked
@setState(state)
JamBlasterActions.updateAudio(name, checked)
else
state = {}
value = value == 'line'
state[name] = value
@setState(state)
JamBlasterActions.updateAudio(name, value)
getInitialState: () ->
{
allJamBlasters: [],
userJamBlasters: [],
localJamBlasters: []
}
convertToClientInstrument: (instrumentId) ->
clientInstrumentId = null
if instrumentId != null && instrumentId != ''
clientInstrumentId = context.JK.instrument_id_to_instrument[instrumentId].client_id
else
clientInstrumentId = 10
clientInstrumentId
render: () ->
pairedJamBlaster = this.state.pairedJamBlaster
hasPairedJamBlaster = pairedJamBlaster?
masterDisabled = this.props.disabled
if !hasPairedJamBlaster
return `<div className="jamblaster-track-config">
You have no paired JamBlaster currently. If you've paired the JamBlaster in the past, be sure it's plugged in
and connected to an ethernet cable. If you have not paired a JamBlaster before, please go to the <a
href="/client#/jamblaster">JamBlaster management page</a>.
</div>`
instruments = []
for instrument in context.JK.server_to_client_instrument_alpha
instruments.push(`<option key={instrument.server_id} value={instrument.server_id}>{instrument.display}</option>`)
combined = pairedJamBlaster.tracks?.combined
track1Active = pairedJamBlaster.tracks?.track1Active
track2Active = pairedJamBlaster.tracks?.track2Active
inputTypeTrack1 = pairedJamBlaster.tracks?.inputTypeTrack1
inputTypeTrack2 = pairedJamBlaster.tracks?.inputTypeTrack2
track1Phantom = pairedJamBlaster.tracks?.track1Phantom
track2Phantom = pairedJamBlaster.tracks?.track2Phantom
micActive = pairedJamBlaster.tracks?.micActive
track1Instrument = context.JK.convertClientInstrumentToServer(pairedJamBlaster.tracks?.track1Instrument)
track2Instrument = context.JK.convertClientInstrumentToServer(pairedJamBlaster.tracks?.track2Instrument)
`<div className="jamblaster-track-config">
<div className="input-1">
<h3>Input 1</h3>
<div className="track-active-field">
<input className="track1Active input-active" name="track1Active" type="checkbox" readOnly={true} disabled={masterDisabled || combined}
checked={track1Active}></input><label>Active</label>
</div>
<div className="input-type-section">
<h4>Type</h4>
<div className="input-type field">
<div className="input-type-choice">
<input className="input-type inputTypeTrack1" type="radio" name="inputTypeTrack1" readOnly={true} value="line" disabled={masterDisabled}
defaultChecked={inputTypeTrack1}/><label>Line</label>
</div>
<div className="input-type-choice">
<input className="input-type inputTypeTrack1" type="radio" name="inputTypeTrack1" readOnly={true} value="instrument" disabled={masterDisabled}
defaultChecked={!inputTypeTrack1}/><label>Instrument</label>
</div>
</div>
</div>
<div className="phantom-section">
<h4>Power</h4>
<div className="phantom field">
<input className="phantom track1Phantom" name="track1Phantom" type="checkbox" readOnly={true} disabled={masterDisabled}
checked={track1Phantom}/><label>Phantom power</label>
</div>
</div>
<div className="instrument-section">
<h4>Instrument</h4>
<select name="track1Instrument" value={track1Instrument} disabled={masterDisabled}>
{instruments}
</select>
</div>
</div>
<div className="input-2">
<h3>Input 2</h3>
<div className="track-active-field">
<input className="track2Active input-active" name="track2Active" type="checkbox" readOnly={true} disabled={masterDisabled || combined}
checked={track2Active}></input><label>Active</label>
</div>
<div className="input-type-section">
<h4>Type</h4>
<div className="input-type field">
<div className="input-type-choice">
<input className="input-type inputTypeTrack2" type="radio" name="inputTypeTrack2" readOnly={true} value="line" disabled={masterDisabled}
defaultChecked={inputTypeTrack2}/><label>Line</label>
</div>
<div className="input-type-choice">
<input className="input-type inputTypeTrack2" type="radio" name="inputTypeTrack2" readOnly={true} value="instrument" disabled={masterDisabled}
defaultChecked={!inputTypeTrack2}/><label>Instrument</label>
</div>
</div>
</div>
<div className="phantom-section">
<h4>Power</h4>
<div className="phantom field">
<input className="phantom track2Phantom" name="track2Phantom" type="checkbox" disabled={masterDisabled}
readOnly={true} checked={track2Phantom}/><label>Phantom power</label>
</div>
</div>
<div className="instrument-section">
<h4>Instrument</h4>
<select name="track2Instrument" value={track2Instrument} disabled={masterDisabled}>
{instruments}
</select>
</div>
</div>
<div className="microphone">
<h3>Mic</h3>
<div className="track-active-field">
<input className="micActive input-active" name="micActive" type="checkbox" readOnly={true} disabled={masterDisabled}
checked={micActive}></input><label>Active</label>
</div>
</div>
<br className="clearall"/>
<div className="combined-option">
<input className="combined" type="checkbox" name="combined" checked={combined} readOnly={true} disabled={masterDisabled} ></input><label>combined inputs 1 &amp; 2 into a single stereo track</label>
</div>
</div>`
})

View File

@ -1,6 +1,5 @@
context = window
rest = context.JK.Rest()
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
@SessionMasterCategoryControls = React.createClass({

View File

@ -1,7 +1,6 @@
context = window
rest = context.JK.Rest()
SessionActions = @SessionActions
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
EVENTS = context.JK.EVENTS
ChannelGroupIds = context.JK.ChannelGroupIds

View File

@ -1,5 +1,4 @@
context = window
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
logger = context.JK.logger

View File

@ -1,5 +1,4 @@
context = window
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
@SessionMasterOtherTracks = React.createClass({

View File

@ -2,7 +2,6 @@ context = window
rest = context.JK.Rest()
SessionActions = @SessionActions
JamTrackActions = @JamTrackActions
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
MIX_MODES = context.JK.MIX_MODES
EVENTS = context.JK.EVENTS
ChannelGroupIds = context.JK.ChannelGroupIds
@ -302,9 +301,7 @@ ChannelGroupIds = context.JK.ChannelGroupIds
<h2>recorded audio</h2>
{contents}
<div className={scrollerClasses}>
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
{mediaTracks}
</ReactCSSTransitionGroup>
{mediaTracks}
</div>
</div>`

View File

@ -1,7 +1,6 @@
context = window
MIX_MODES = context.JK.MIX_MODES
SessionActions = context.SessionActions
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
@SessionMyTracks = React.createClass({
@ -44,9 +43,7 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
{delayVstEnable}
<div className="session-tracks-scroller">
{content}
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
{tracks}
</ReactCSSTransitionGroup>
{tracks}
</div>
</div>`

View File

@ -1,5 +1,4 @@
context = window
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
NotificationActions = @NotificationActions
@SessionNotifications = React.createClass({
@ -30,9 +29,8 @@ NotificationActions = @NotificationActions
Clear Notifications
</a>
<div className="session-tracks-scroller">
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
{notifications}
</ReactCSSTransitionGroup>
</div>
</div>`

View File

@ -1,6 +1,5 @@
context = window
MixerActions = context.MixerActions
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
@SessionOtherTracks = React.createClass({
@ -88,9 +87,8 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
<SessionInviteMusiciansBtn />
<div className="session-tracks-scroller">
{content}
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
{participants}
</ReactCSSTransitionGroup>
{participants}
</div>
</div>`

View File

@ -0,0 +1,6 @@
context = window
@CallbackActions = Reflux.createActions({
genericCallback: {}
})

View File

@ -0,0 +1,10 @@
context = window
@JamBlasterActions = Reflux.createActions({
resyncBonjour: {},
clearPortBindState: {},
saveNetworkSettings: {},
pairState: {},
setAutoPair: {},
updateAudio: {}
})

View File

@ -674,7 +674,7 @@ MIX_MODES = context.JK.MIX_MODES;
oppositeMixer = oppositeMixers[ChannelGroupIds.UserMusicInputGroup][0]
if !oppositeMixer
logger.warn("unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer", mixer )
logger.warn("unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer", mixer, @personalMixers )
when MIX_MODES.PERSONAL
mixers = @groupedMixersForClientId(client_id, [ ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL)

View File

@ -2,96 +2,16 @@ context = window
teacherActions = window.JK.Actions.Teacher
@BonjourMixin = {
resyncBonjour: () ->
rest.getUserJamBlasters({client_id: @app.clientId}).done((response) => @getUserJamBlastersDone(response)).fail((response) => @getUserJamBlastersFail(response))
getUserJamBlastersDone: (response) ->
@setState({userJamBlasters: response})
@getLocalClients(response)
findJamBlaster: (id) ->
findJamBlaster: (oldClient) ->
found = null
if @clients?
for client in @clients
if client.id == id
if @state.allJamBlasters?
for client in @state.allJamBlasters
if oldClient.server_id? && client.server_id == oldClient.server_id
found = client
break
if client.ipv6_addr == id
if oldClient.ipv6_addr? && client.ipv6_addr == oldClient.ipv6_addr
found = client
break
found
getUserJamBlastersFail: (jqXHR) ->
@app.layout.ajaxError(jqXHR)
mergeBonjourClients: (localClients, userJamBlasters) ->
console.log("@state.localClients", localClients)
console.log("@state.userJamBlasters", userJamBlasters)
# for localClient in @state.localClients
for localClient in localClients
if localClient.connect_url.indexOf(':30330') && localClient.is_jb
client = {}
client.ipv6_addr = localClient.ipv6_addr
client.isPaired = localClient.isPaired
client.name = localClient.name
client.has_local = true
client.has_server = false
client.id = client.ipv6_addr
client.connect_url = localClient.connect_url
for serverClient in userJamBlasters
# see if we can join on ipv6
if ipv6_addr == serverClient.ipv6_link_local
# ok, matched! augment with server data
client.serial_no = serverClient.serial_no
client.user_id = serverClient.user_id
client.id = serverClient.id
client.client_id = serverClient.client_id
client.ipv4_link_local = serverClient.ipv4_link_local
client.display_name = serverClient.display_name
client.has_server = true
break
clients.push(client)
for serverClient in userJamBlasters
foundLocal = false
for localClient in localClients
if ipv6_addr == serverClient.ipv6_link_local
foundLocal = true
break
if !foundLocal
# this server version of the client has not been spoken for in the earlier loop above
# so we need to add it in to the client list
client = {}
client.serial_no = serverClient.serial_no
client.user_id = serverClient.user_id
client.id = serverClient.id
client.client_id = serverClient.client_id
client.ipv4_link_local = serverClient.ipv4_link_local
client.display_name = serverClient.display_name
client.has_local = false
client.has_server = true
clients.push(client)
console.log("all client", clients)
@clients = clients
@setState({clients: clients})
getLocalClients: (userJamBlasters) ->
localClients = context.jamClient.getLocalClients()
@mergeBonjourClients(localClients, userJamBlasters)
@setState({localClients: localClients})
}

View File

@ -0,0 +1,24 @@
$ = jQuery
context = window
logger = context.JK.logger
SessionActions = @SessionActions
JamBlasterActions = @JamBlasterActions
@CallbackStore = Reflux.createStore(
{
init: () ->
# Register with the app store to get @app
this.listenTo(context.AppStore, this.onAppInit)
onAppInit: (@app) ->
if context.jamClient.RegisterGenericCallBack?
context.jamClient.RegisterGenericCallBack('CallbackActions.genericCallback')
onGenericCallback: (map) ->
if map.cmd == 'join_session'
SessionActions.joinSession(map['music_session_id'])
else if map.cmd == 'client_pair_state'
JamBlasterActions.pairState(map)
}
)

View File

@ -0,0 +1,345 @@
$ = jQuery
context = window
logger = context.JK.logger
@JamBlasterStore = Reflux.createStore(
{
listenables: @JamBlasterActions
userJamBlasters: []
localJamBlasters: []
allJamBlasters: []
init: () ->
# Register with the app store to get @app
this.listenTo(context.AppStore, this.onAppInit)
onAppInit: (@app) ->
onUpdateAudio: (name, value) ->
# input1_linemode
# input2_linemode
# input1_48V
# input2_48V
# has_chat
# track1 = {left, right, inst, stereo)
# track1 = {left, right, inst, stereo)
if @pairedJamBlaster? && @pairedJamBlaster.tracks?
logger.debug("onUpdateAudio name=#{name} value=#{value}", @pairedJamBlaster.tracks)
audio = jQuery({}, @pairedJamBlaster.tracks)
if name == 'inputTypeTrack1'
audio.input1_linemode = value
else if name == 'inputTypeTrack2'
audio.input2_linemode = value
else if name == 'track1Phantom'
audio.input1_48V = value
else if name == 'track2Phantom'
audio.input2_48V = value
else if name == 'micActive'
audio.has_chat = value
track1Active = @pairedJamBlaster.tracks.track1Active
if name == 'track1Active'
track1Active = value
track2Active = @pairedJamBlaster.tracks.track2Active
if name == 'track2Active'
track2Active = value
combined = @pairedJamBlaster.tracks.combined
if name == 'combined'
combined = value
track1Instrument = audio.track1Instrument
track2Instrument = audio.track2Instrument
if name == 'track1Instrument'
track1Instrument = @convertToClientInstrument(value)
if name == 'track2Instrument'
track2Instrument = @convertToClientInstrument(value)
if combined
# user has chosen to combine both inputs into one track. stereo=true is the key flag her
audio.track1 = {stereo: true, left: true, inst: track1Instrument}
else
if track1Active && track2Active
audio.track1 = {stereo: false, left: true, inst: track1Instrument}
audio.track2 = {stereo: false, right: true, inst: track2Instrument}
else if track1Active #(means track)
audio.track1 = {stereo: false, left: true, inst: track1Instrument}
else # input2Active
audio.track2 = {stereo: false, right: true, inst: track2Instrument}
logger.debug("updating JamBlaster track state", audio)
context.jamClient.setJbTrackState(audio);
else
context.JK.Banner.showAlert('no paired JamBlaster', 'it seems your JamBlaster has become disconnected. Please ensure it is powered on and connected via an ethernet cable.')
convertToClientInstrument: (instrumentId) ->
clientInstrumentId = null
if instrumentId != null && instrumentId != ''
clientInstrumentId = context.JK.instrument_id_to_instrument[instrumentId].client_id
else
clientInstrumentId = 10
clientInstrumentId
onSetAutoPair: (autopair) ->
if !autopair
context.jamClient.setJBAutoPair(autopair)
@lastClientAutoPair = null
JamBlasterActions.resyncBonjour()
setTimeout((() => context.JK.Banner.showNotice("autoconnect removed",
"To use the JamBlaster in the future, you will need to come to this screen and click the connect link.")), 1)
else
context.JK.Banner.showYesNo({
title: "enable auto-connect",
html: "If you would like to automatically connect to your JamBlaster whenever you start this app, click the AUTO CONNECT button below.",
yes_text: 'AUTO CONNECT',
yes: =>
context.jamClient.setJBAutoPair(autopair)
@lastClientAutoPair = null
JamBlasterActions.resyncBonjour()
setTimeout((() => context.JK.Banner.showNotice("autoconnect enabled",
"Your desktop JamKazam application will automatically reconnect to the JamBlaster .")), 1)
})
onPairState: (state) ->
if state.client_pair_state == 10
# fully paired
logger.debug("backend indicates we are paired with a client")
@onResyncBonjour()
onSaveNetworkSettings: (settings) ->
logger.debug("onSaveNetworkSettings", settings)
result = context.jamClient.setJbNetworkState(settings)
if !result
context.JK.Banner.showAlert('unable to save network settings', 'Please double-check that your JamBlaster is online and paired.')
return
else
context.JK.Banner.showAlert('network settings updated', 'Please reboot the JamBlaster.')
# it will be refreshed by backend
@onClearNetworkState()
@onResyncBonjour()
onResyncBonjour: () ->
if @refreshingBonjour
logger.debug("already refreshing bonjour")
return
@refreshingBonjour = true
@changed()
rest.getUserJamBlasters({client_id: @app.clientId}).done((response) => @getUserJamBlastersDone(response)).fail((response) => @getUserJamBlastersFail(response))
getUserJamBlastersDone: (response) ->
@userJamBlasters = response
@changed()
@getLocalClients(response)
findJamBlaster: (oldClient) ->
found = null
if @clients?
for client in @clients
if oldClient.server_id? && client.server_id == oldClient.server_id
found = client
break
if oldClient.ipv6_addr? && client.ipv6_addr == oldClient.ipv6_addr
found = client
break
found
getUserJamBlastersFail: (jqXHR) ->
@refreshingBonjour = false
@changed()
@app.layout.ajaxError(jqXHR)
getAutoPair: () ->
if @lastClientAutoPair?
return @lastClientAutoPair
else
return @getJbAutoPair()
getNetworkState: (client) ->
if @lastClientNetworkState? && @lastClientNetworkState.ipv6_addr == client.ipv6_addr
return @lastClientNetworkState
else
return @getJbNetworkState(client)
getPortState: (client) ->
if @lastClientPortState? && @lastClientPortState.ipv6_addr == client.ipv6_addr
return @lastClientPortState
else
return @getJbPortBindState(client)
getJbPortBindState:(client) ->
@lastClientPortState = context.jamClient.getJbPortBindState()
console.log("context.jamClient.getJbPortBindState()", @lastClientPortState)
@lastClientPortState.ipv6_addr = client.ipv6_addr
return @lastClientPortState
getJbNetworkState:(client) ->
@lastClientNetworkState = context.jamClient.getJbNetworkState()
console.log("context.jamClient.getJbNetworkState()", @lastClientNetworkState)
@lastClientNetworkState.ipv6_addr = client.ipv6_addr
return @lastClientNetworkState
getJbAutoPair:() ->
@lastClientAutoPair = context.jamClient.getJBAutoPair()
console.log("context.jamClient.getJBAutoPair()", @lastClientAutoPair)
return @lastClientAutoPair
getJbTrackState:(client) ->
@lastClientTrackState = context.jamClient.getJbTrackState()
console.log("context.jamClient.getJbTrackState()", @lastClientTrackState)
@lastClientTrackState.ipv6_addr = client.ipv6_addr
return @lastClientTrackState
onClearPortBindState: () ->
@lastClientPortState = null
onClearNetworkState: () ->
@lastClientNetworkState = null
mergeBonjourClients: (localClients, userJamBlasters) ->
console.log("@state.localClients", localClients)
console.log("@state.userJamBlasters", userJamBlasters)
# for localClient in @state.localClients
autoconnect = @getAutoPair()
foundPaired = null
clients = []
for localClient in localClients
if localClient.connect_url.indexOf(':30330') > -1 && localClient.is_jb
client = {}
client.ipv6_addr = localClient.ipv6_addr
client.isConnected = localClient.isPaired
client.name = localClient.name
client.has_local = true
client.has_server = false
client.id = client.ipv6_addr
client.connect_url = localClient.connect_url
client.isPaired = localClient.pstate? && localClient.pstate == 10 # ePairingState.Paired
client.autoconnect = autoconnect
if client.isPaired
client.portState = @getPortState(client)
client.network = @getNetworkState(client)
client.tracks = @getJbTrackState(client)
client.isDynamicPorts = client.portState?.use_static_port
foundPaired = client
if client.tracks?
client.tracks.inputTypeTrack1 = client.tracks.input1_linemode
client.tracks.inputTypeTrack2 = client.tracks.input2_linemode
client.tracks.track1Phantom = client.tracks.input1_48V
client.tracks.track2Phantom = client.tracks.input2_48V
client.tracks.micActive = client.tracks.has_chat
# combined
track1 = client.tracks.track1
track2 = client.tracks.track1
if track1?
client.tracks.combined = track1.stereo
if track1.stereo
client.tracks.track1Active = true
client.tracks.track2Active = true
client.tracks.track1Active = track1.left
client.tracks.track2Active = track1.right
client.tracks.track1Instrument = track1.inst
if track2?
client.tracks.track2Instrument = track2.inst
client.tracks.track1Active = track2.left
client.tracks.track2Active = track2.right
# map["adaptiveframe"] = jbcfg.adaptiveframe();
for serverClient in userJamBlasters
# see if we can join on ipv6
if ipv6_addr == serverClient.ipv6_link_local
# ok, matched! augment with server data
client.serial_no = serverClient.serial_no
client.user_id = serverClient.user_id
client.id = serverClient.id
client.server_id = serverClient.id
client.client_id = serverClient.client_id
client.ipv4_link_local = serverClient.ipv4_link_local
client.display_name = serverClient.display_name
client.has_server = true
break
clients.push(client)
for serverClient in userJamBlasters
foundLocal = false
for localClient in localClients
if ipv6_addr == serverClient.ipv6_link_local
foundLocal = true
break
if !foundLocal
# this server version of the client has not been spoken for in the earlier loop above
# so we need to add it in to the client list
client = {}
client.serial_no = serverClient.serial_no
client.user_id = serverClient.user_id
client.id = serverClient.id
client.client_id = serverClient.client_id
client.ipv4_link_local = serverClient.ipv4_link_local
client.display_name = serverClient.display_name
client.has_local = false
client.has_server = true
client.autoconnect = autoconnect
clients.push(client)
@pairedJamBlaster = foundPaired
console.log("all client", clients)
@clients = clients
@changed()
getLocalClients: (userJamBlasters) ->
@localClients = context.jamClient.getLocalClients()
@mergeBonjourClients(@localClients, userJamBlasters)
@refreshingBonjour = false
@changed()
changed: () ->
@trigger({userJamBlasters: @userJamBlasters, allJamBlasters: @clients, localJamBlasters: @localClients, refreshingBonjour: @refreshingBonjour, pairedJamBlaster: @pairedJamBlaster})
}
)

View File

@ -101,7 +101,16 @@
track.instrument_id = context.JK.server_to_client_instrument_map["Other"].server_id;
}
else {
track.instrument_id = context.JK.client_to_server_instrument_map[localMusicTracks[i].instrument_id].server_id;
var instrument = context.JK.client_to_server_instrument_map[localMusicTracks[i].instrument_id]
if (instrument) {
track.instrument_id = instrument.server_id
}
else {
logger.debug("backend reported an invalid instrument ID of " + localMusicTracks[i].instrument_id)
track.instrument_id = 'other'
}
}
if (localMusicTracks[i].stereo) {
track.sound = "stereo";

View File

@ -688,6 +688,15 @@
return instrumentArray;
}
context.JK.convertClientInstrumentToServer = function(clientId) {
var serverInstrument = context.JK.client_to_server_instrument_map[clientId]
if (!serverInstrument)
return 'other'
else
return serverInstrument.server_id
}
context.JK.showErrorDialog = function (app, msg, title) {
app.layout.showDialog('error-dialog');
$('#error-msg', 'div[layout-id="error-dialog"]').html(msg);

View File

@ -453,7 +453,7 @@ ul.shortcuts {
padding:2px;
}
.account-home, .band-setup, .account-menu-group, .get-help, .community-forum, .invite-friends {
.account-home, .band-setup, .account-menu-group, .get-help, .community-forum, .invite-friends, .jamblaster-config {
border-bottom:1px;
border-style:solid;
border-color:#ED3618;

View File

@ -11,6 +11,10 @@
border-bottom:0 !important;
}
&.isDynamicPorts .bt-content{
width:140px !important;
}
.bt-content {
height:80px;
width:100px;

View File

@ -5,12 +5,40 @@
div[data-react-class="JamBlasterScreen"] {
height:100%;
}
h3 {
font-weight:bold;
font-size:18px;
color:white;
margin-bottom:20px;
}
h4 {
font-size:16px;
color:white;
font-weight:bold;
margin:30px 0 10px 0;
}
.content-body-scroller {
height:100%;
padding:30px;
@include border_box_sizing;
}
.column {
float:left;
width:50%;
@include border_box_sizing;
&.column-left {
padding-right:20px;
}
&.column-right {
padding-left:20px;
}
}
ol {
li {
margin-left:15px;
@ -20,6 +48,58 @@
}
}
.tiles {
float:right;
margin-bottom: 40px;
margin-top: -3px;
}
.jamblaster-tile {
float:left;
@include border-box_sizing;
width:117px;
border-radius:4px;
margin-left:7px;
height: 32px;
line-height:32px;
position: relative;
background-color:#535353;
&.activeTile
{
background-color: #ed3618;
}
a {
position: absolute;
text-align: center;
bottom:0;
left:0;
width: 100%;
margin: 0 auto;
padding: 0 10px 0;
color:white;
background-color:transparent;
@include border-box_sizing;
}
}
.networking-content {
.field {
margin-bottom:10px;
}
.iradio_minimal {
display: inline-block;
top: 4px;
margin-right: 5px;
}
label {
display:inline-block;
}
.dhcpfield {
margin-bottom:10px;
}
}
.student-right-content {
p {
margin-bottom:10px;
@ -35,28 +115,11 @@
}
}
h2 {
font-size: 20px;
font-size: 22px;
font-weight:700;
margin-bottom: 20px !important;
margin-bottom: 40px !important;
display:inline-block;
}
.column {
@include border_box_sizing;
width:50%;
}
.column-left {
float:left;
width:70%;
}
.column-right {
float:right;
width:30%;
padding-left:20px;
.jamclass-section {
padding-top:36px;
}
}
span.price {
color:white;
}
@ -112,7 +175,7 @@
height:100%;
width:100%;
table-layout:fixed;
margin-bottom:20px;
margin-bottom:10px;
a {
text-decoration: none !important;
color:#fc0 !important;
@ -178,4 +241,55 @@
}
}
}
.resync-bonjour {
float:right;
margin-right:3px;
margin-bottom:20px;
width:110px;
}
p.help-text {
clear:both;
}
.save-settings-btn {
width:200px;
}
.manual-settings {
margin:20px 0;
label {
display:inline-block;
color: $ColorTextTypical;
width: 90px;
text-align: right;
padding-right: 10px;
}
input {
display:inline-block;
}
}
.status-label {
width:80px;
display:inline-block;
}
.status-field {
margin-bottom:10px;
color:$ColorTextTypical;
}
.input-1 {
@include border-box_sizing;
width:200px;
margin-right:20px;
}
.input-2 {
@include border-box_sizing;
width:200px;
margin-right:20px;
}
.microphone {
@include border-box_sizing;
width:200px;
}
}

View File

@ -0,0 +1,80 @@
@import "client/common";
.jamblaster-track-config {
h3 {
font-weight:bold;
font-size:18px;
color:white;
margin-bottom:10px;
width:100%;
border-width:0 0 1px 0;
border-color: $ColorTextTypical;
border-style:solid;
padding-bottom:5px;
}
h4 {
font-size:14px;
color:white;
margin:0 0 10px 0;
}
.input-1 {
position:relative;
@include border-box_sizing;
width:200px;
margin-right:20px;
float:left;
}
.input-2 {
position:relative;
@include border-box_sizing;
width:200px;
margin-right:20px;
float:left;
}
.microphone {
position:relative;
@include border-box_sizing;
width:200px;
float:left;
}
.iradio_minimal {
display: inline-block;
top: 4px;
margin-right: 5px;
}
.icheckbox_minimal {
display: inline-block;
top: 2px;
margin-right: 5px;
}
label {
display:inline-block;
}
.input-type-section {
margin:20px 0 30px;
}
.phantom-section {
margin:20px 0 30px;
}
.instrument-section {
margin:20px 0 30px;
}
.track-active-field {
position:absolute;
top:-4px;
right:0;
}
.input-type-choice {
&:first-of-type {
margin-bottom:10px;
}
}
}

View File

@ -65,7 +65,7 @@
.tab {
padding-top:20px;
}
.tab[tab-id="music-audio"] {
.tab[data-tab-id="music-audio"] {
.column {
&:nth-of-type(1) {
width: 30%;
@ -87,7 +87,7 @@
}
}
.tab[tab-id="voice-chat"] {
.tab[data-tab-id="voice-chat"] {
.column {
&:nth-of-type(1) {
width: 50%;

View File

@ -35,6 +35,9 @@
}
.actions {
float:right;
margin:0 -13px 30px 0;
margin:20px 0 30px 0;
}
.help-text {
margin:0 0 20px 0;
}
}

View File

@ -2,7 +2,6 @@
#jamblaster-pairing-dialog {
width: 600px;
max-height:600px;
h2 {
color:white;
@ -11,7 +10,8 @@
}
.dialog-inner {
width: auto;
height:calc(100% - 29px)
height:calc(100% - 29px);
padding-bottom:75px;
}
.field {
@ -34,8 +34,13 @@
}
.actions {
float:right;
margin:0 -13px 30px 0;
position:absolute;
right:0;
bottom:0;
margin:0 20px 30px 0;
}
.countdown-msg {
margin-top:30px;
}
.countdown {
color:white;
@ -44,5 +49,9 @@
padding:0 10px;
width:30px;
display:inline-block;
text-align:center;
}
.message {
margin-top:20px;
}
}

View File

@ -0,0 +1,43 @@
@import "client/common";
#jamblaster-port-dialog {
width: 600px;
max-height:600px;
h2 {
color:white;
margin-bottom:10px;
font-size:16px;
}
.dialog-inner {
width: auto;
height:calc(100% - 29px)
}
.field {
margin-bottom:10px;
}
input {
display:inline-block;
}
label {
display:inline-block;
}
.iradio_minimal {
display:inline-block;
margin-right: 5px;
top: 4px;
}
div[data-react-class="JamBlasterNameDialog"] {
}
.actions {
float:right;
margin:20px 0 30px 0;
}
.help-text {
margin:0 0 20px 0;
}
}

View File

@ -1,8 +1,13 @@
script type='text/template' id='template-jamblaster-options'
ul
= '{% if (data.autoconnect) { %}'
li data-jamblaster-option="auto-connect"
a href='#' Cancel Auto Connect
= '{% } else { %}'
li data-jamblaster-option="auto-connect"
a href='#' Auto Connect
= '{% } %}'
li data-jamblaster-option="restart"
a href='#' Restart
@ -13,8 +18,12 @@ script type='text/template' id='template-jamblaster-options'
li data-jamblaster-option="check-for-updates"
a href='#' Check for Updates
= '{% if (data.isDynamicPorts) { %}'
li data-jamblaster-option="set-static-ports"
a href='#' Revert To Dynamic Ports
= '{% } else { %}'
li data-jamblaster-option="set-static-ports"
a href='#' Set Static Ports
= '{% } %}'
li data-jamblaster-option="factory-reset"
a href='#' Factory Reset

View File

@ -19,7 +19,7 @@
.input
input type="number" name="static-port"
label for="static-port" The first port that the client will open
p.hint You can specify any port you like, but we recommend an even number in the range including 1026-49150 to avoid conflicts with other programs. When configuring Port Forwarding in your router, be sure to open this port along with the next ten. For example, if this field is 12000, then in your router, forward ports 12000-12010 to your computer's IP address.
p.hint You can specify any port you like, but we recommend an even number in the range including 1026-49150 to avoid conflicts with other programs. When configuring Port Forwarding in your router, be sure to open this port along with the next 10. For example, if this field is 12000, then in your router, forward ports 12000-12010 to your computer's IP address.
br clear='all'
br clear='all'
.buttons

View File

@ -3,51 +3,4 @@
= image_tag "content/icon_add.png", {:width => 19, :height => 19, :class => 'content-icon' }
%h1 configure tracks
.dialog-inner
.dialog-tabs
%a.selected.tab-configure-audio Inputs & Outputs
%a.tab-configure-voice Voice Chat
.instructions
%span
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.
.clearall
.tab.no-selection-range{'tab-id' => 'music-audio'}
= react_component 'ConfigureTracks', {}
.clearall
.tab{'tab-id' => 'voice-chat'}
%form.select-voice-chat-option.section.voice
.sub-header Select Voice Chat Option
.voicechat-option.reuse-audio-input
%input{type:"radio", name: "voicechat", checked:"checked"}
%h3 Use Music Microphone
%p I am already using a microphone to capture my vocal or instrumental music, so I can talk with other musicians using that microphone
.voicechat-option.use-chat-input
%input{type:"radio", name: "voicechat"}
%h3 Use Chat Microphone
%p I am not using a microphone for acoustic instruments or vocals, so use the input selected to the right for voice chat during my sessions
.clearall
.select-voice-chat
.sub-header Voice Chat Input
.ftue-box.chat-inputs
.vu-meter
.ftue-controls
.ftue-vu-left.voice-chat-vu-left
.ftue-fader.chat-fader
.gain-label GAIN
.ftue-vu-right.voice-chat-vu-right
.clearall
.buttons
%a.btn-add-new-audio-gear.button-grey{'layout-link' => 'add-new-audio-gear'} ADD NEW AUDIO GEAR
%a.button-grey.btn-cancel{href:'#'} CANCEL
%a.button-orange.btn-update-settings{href:'#'} SAVE SETTINGS
= react_component 'ConfigureTracksDialogContents', {}

View File

@ -57,4 +57,5 @@
= render 'dialogs/musicNotationUploadDialog'
= render 'dialogs/testDrivePackageDialog'
= render 'dialogs/jamblasterNameDialog'
= render 'dialogs/jamblasterPortDialog'
= render 'dialogs/jamblasterPairingDialog'

View File

@ -0,0 +1,2 @@
.dialog.dialog-overlay-sm.top-parent layout='dialog' layout-id='jamblaster-port-dialog' id='jamblaster-port-dialog'
= react_component 'JamBlasterPortDialog', {}

View File

@ -47,6 +47,9 @@
<% if current_user && current_user.affiliate_partner.present? %>
<li class="affiliate account-menu-group"><%= link_to "Affiliate Report", '/client#/account/affiliate' %></li>
<% end %>
<% if @nativeClient %>
<li class="jamblaster-config"><%= link_to "JamBlaster", '/client#/jamblaster' %></li>
<% end %>
<% if current_user && current_user.musician? %>
<li class="band-setup"><%= link_to "Band Setup", '/client#/band/setup/new' %></li>
<% end %>

View File

@ -1,7 +1,7 @@
namespace :jam_tracks do
task import_click_tracks: :environment do |task, args|
JamTrackImporter.storage_format = 'Tency'
JamTrackImporter.storage_format = 'Helbing'
JamTrackImporter.import_click_tracks
end
@ -124,8 +124,8 @@ namespace :jam_tracks do
end
task resync_instruments: :environment do |task, args|
JamTrackImporter.storage_format = 'Paris'
JamTrackImporter.resync_instruments(JamTrackLicensor.find_by_name!('Paris Music'))
JamTrackImporter.storage_format = ENV['STORAGE_FORMAT']
JamTrackImporter.resync_instruments(JamTrackLicensor.find_by_name!('Stockton Helbing'))
end
@ -249,6 +249,11 @@ namespace :jam_tracks do
JamTrackImporter.synchronize_jamtrack_aac_previews
end
task sync_click_mp3_to_wav: :environment do |task, args|
JamTrackImporter.storage_format = 'Helbing'
JamTrackImporter.convert_click_track_to_wavs
end
# popuplate preview info without uploading/processing audio files (use what's in S3)
task sync_previews_dev: :environment do |task, args|
JamTrackImporter.synchronize_previews_dev