diff --git a/db/manifest b/db/manifest index 5a9ba3bbd..0a8041934 100755 --- a/db/manifest +++ b/db/manifest @@ -368,4 +368,5 @@ retailers.sql second_ed.sql second_ed_v2.sql retailers_v2.sql -retailer_interest.sql \ No newline at end of file +retailer_interest.sql +connection_role.sql \ No newline at end of file diff --git a/db/up/connection_role.sql b/db/up/connection_role.sql new file mode 100644 index 000000000..754df6635 --- /dev/null +++ b/db/up/connection_role.sql @@ -0,0 +1,2 @@ +ALTER TABLE connections ADD COLUMN client_role VARCHAR; +ALTER TABLE connections ADD COLUMN parent_client_id VARCHAR; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/connection_manager.rb b/ruby/lib/jam_ruby/connection_manager.rb index 92d960b22..92aea6228 100644 --- a/ruby/lib/jam_ruby/connection_manager.rb +++ b/ruby/lib/jam_ruby/connection_manager.rb @@ -402,7 +402,7 @@ SQL music_session.creator end - def join_music_session(user, client_id, music_session, as_musician, tracks, audio_latency, video_sources=nil) + def join_music_session(user, client_id, music_session, as_musician, tracks, audio_latency, client_role = nil, parent_client_id = nil, video_sources=nil) connection = nil ConnectionManager.active_record_transaction do |connection_manager| @@ -418,7 +418,7 @@ SQL raise JamPermissionError, "wrong user_id associated with connection #{client_id}" end - connection.join_the_session(music_session, as_musician, tracks, user, audio_latency, video_sources) + connection.join_the_session(music_session, as_musician, tracks, user, audio_latency, client_role, parent_client_id, video_sources) JamRuby::MusicSessionUserHistory.join_music_session(user.id, music_session.id, client_id) # connection.music_session_id = music_session.id # connection.as_musician = as_musician diff --git a/ruby/lib/jam_ruby/models/active_music_session.rb b/ruby/lib/jam_ruby/models/active_music_session.rb index dd809d35f..41a8ebfa5 100644 --- a/ruby/lib/jam_ruby/models/active_music_session.rb +++ b/ruby/lib/jam_ruby/models/active_music_session.rb @@ -501,7 +501,7 @@ module JamRuby [music_sessions, user_scores] end - def self.participant_create(user, music_session_id, client_id, as_musician, tracks, audio_latency, video_sources=nil) + def self.participant_create(user, music_session_id, client_id, as_musician, tracks, audio_latency, client_role = nil, parent_client_id = nil, video_sources=nil) music_session = MusicSession.find(music_session_id) # USERS ARE ALREADY IN SESSION @@ -514,7 +514,7 @@ module JamRuby active_music_session.with_lock do # VRFS-1297 active_music_session.tick_track_changes # VRFS-3986 - connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, video_sources) + connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, client_role, parent_client_id, video_sources) if connection.errors.any? # rollback the transaction to make sure nothing is disturbed in the database @@ -573,7 +573,7 @@ module JamRuby # auto-join this user into the newly created session as_musician = true - connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, video_sources) + connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, client_role, parent_client_id, video_sources) unless connection.errors.any? user.update_progression_field(:first_music_session_at) diff --git a/ruby/lib/jam_ruby/models/connection.rb b/ruby/lib/jam_ruby/models/connection.rb index 18f250616..8bd75a4a6 100644 --- a/ruby/lib/jam_ruby/models/connection.rb +++ b/ruby/lib/jam_ruby/models/connection.rb @@ -176,11 +176,13 @@ module JamRuby true end - def join_the_session(music_session, as_musician, tracks, user, audio_latency, videos=nil) + def join_the_session(music_session, as_musician, tracks, user, audio_latency, client_role = nil, parent_client_id = nil, videos=nil) self.music_session_id = music_session.id self.as_musician = as_musician == true # this is deliberate; otherwise we create a warning in one our tests that passes 'blarg' (rails warning about casting strings to false) self.joining_session = true self.joined_session_at = Time.now + self.client_role = client_role + self.parent_client_id = parent_client_id associate_tracks(tracks) unless tracks.nil? associate_videos(videos) unless videos.nil? self.save diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 9c87bf6f8..dbfd2e01e 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -272,7 +272,9 @@ module JamRuby music_session.connections.each do |connection| connection.tracks.each do |track| - recording.recorded_tracks << RecordedTrack.create_from_track(track, recording) + if connection.client_role != 'child' + recording.recorded_tracks << RecordedTrack.create_from_track(track, recording) + end end connection.video_sources.each do |video| diff --git a/web/app/assets/javascripts/dialog/localRecordingsDialog.js b/web/app/assets/javascripts/dialog/localRecordingsDialog.js index 2402c0023..dbca1c4c2 100644 --- a/web/app/assets/javascripts/dialog/localRecordingsDialog.js +++ b/web/app/assets/javascripts/dialog/localRecordingsDialog.js @@ -65,6 +65,8 @@ return; } + console.log("GetLocalRecordingState", localResults) + $.each(claimedRecordings, function(index, claimedRecording) { var options = { diff --git a/web/app/assets/javascripts/dialog/recordingFinishedDialog.js b/web/app/assets/javascripts/dialog/recordingFinishedDialog.js index 97d746144..04c3a602d 100644 --- a/web/app/assets/javascripts/dialog/recordingFinishedDialog.js +++ b/web/app/assets/javascripts/dialog/recordingFinishedDialog.js @@ -10,6 +10,8 @@ var $dialog = null; var $saveVideoCheckbox = null var $uploadToYoutube = null + var timeout = null + var CLIENT_ROLE = context.JK.CLIENT_ROLE function resetForm() { // remove all display errors @@ -44,6 +46,47 @@ resetForm(); + if(context.jamClient.getClientParentChildRole() == CLIENT_ROLE.CHILD) { + + logger.debug("child client; launching preview after xfer"); + $('#recording-finished-dialog span.nowait').addClass('hidden') + $('#recording-finished-dialog span.pleasewait').removeClass('hidden') + $('#recording-finished-dialog .preview-area').css('visibility', 'hidden') + $('#recording-finished-dialog form').css('visibility', 'hidden') + waitForMixTransfer() + } + else { + console.log("normal client; launching preview immediately") + $('#recording-finished-dialog span.pleasewait').addClass('hidden') + $('#recording-finished-dialog span.nowait').removeClass('hidden') + $('#recording-finished-dialog .preview-area').css('visibility', 'visible') + $('#recording-finished-dialog form').css('visibility', 'visible') + launchPreview(); + } + + } + + + function waitForMixTransfer() { + timeout = setTimeout(function() { + console.log("checking for file transfer", window.RecordingStore.mixTransferred) + + if(window.RecordingStore.mixTransferred) { + $('#recording-finished-dialog span.pleasewait').addClass('hidden') + $('#recording-finished-dialog span.nowait').removeClass('hidden') + $('#recording-finished-dialog .preview-area').css('visibility', 'visible') + $('#recording-finished-dialog form').css('visibility', 'visible') + timeout = null + launchPreview() + } + else { + waitForMixTransfer(); + } + + }, 1000) + } + + function launchPreview() { var parentSelector = '#recording-finished-dialog div.genre-selector'; context.JK.GenreSelectorHelper.render(parentSelector); @@ -117,10 +160,12 @@ playbackControls.startMonitor(); } } - } - function afterHide() { + if(timeout) { + clearTimeout(timeout) + timeout = null + } if(recording && recording.video) { var name = $('#recording-finished-dialog form input[name=name]').val(); name = name.replace(/[^A-Za-z0-9\-\ ]/g, ''); diff --git a/web/app/assets/javascripts/playbackControls.js b/web/app/assets/javascripts/playbackControls.js index 1ad2b3aea..43f8c02d3 100644 --- a/web/app/assets/javascripts/playbackControls.js +++ b/web/app/assets/javascripts/playbackControls.js @@ -496,7 +496,10 @@ monitoring = false; logger.debug("playbackControl.stopMonitor") if (monitorPlaybackTimeout != null) { - clearTimeout(monitorPlaybackTimeout); + if(clearTimeout) { + clearTimeout(monitorPlaybackTimeout); + } + monitorPlaybackTimeout = null; } } diff --git a/web/app/assets/javascripts/react-components/JamBlasterScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/JamBlasterScreen.js.jsx.coffee index 1a62c0ce1..b6467f523 100644 --- a/web/app/assets/javascripts/react-components/JamBlasterScreen.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/JamBlasterScreen.js.jsx.coffee @@ -249,7 +249,7 @@ JamBlasterActions = @JamBlasterActions audio: () -> `
- + To edit the JamBlaster audio settings, get into a session, and click the Settings link under My Tracks.
` ipSettingsChanged: (key, e) -> diff --git a/web/app/assets/javascripts/react-components/PopupMediaControls.js.jsx.coffee b/web/app/assets/javascripts/react-components/PopupMediaControls.js.jsx.coffee index 3f4dc1230..327ef1007 100644 --- a/web/app/assets/javascripts/react-components/PopupMediaControls.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/PopupMediaControls.js.jsx.coffee @@ -716,7 +716,6 @@ mixins.push(Reflux.listenTo(UserStore, 'onUserChanged')) setTimeout(@resizeWindow, 1000) shouldComponentUpdate: () -> - console.log("THIS UNLOADED", @unloaded) return !@unloaded resizeWindow: () => diff --git a/web/app/assets/javascripts/react-components/PopupRecordingStartStop.js.jsx.coffee b/web/app/assets/javascripts/react-components/PopupRecordingStartStop.js.jsx.coffee index 8c9092b1e..d9c0ad6ce 100644 --- a/web/app/assets/javascripts/react-components/PopupRecordingStartStop.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/PopupRecordingStartStop.js.jsx.coffee @@ -36,6 +36,10 @@ if accessOpener # this.setState(chatMixer: mixers.chatMixer) onRecordingStateChanged: (recordingState) -> + if @unloaded + #console.log("PopupMediaControls unloaded. ignore onMixersChnaged") + return + this.setState(isRecording: recordingState.isRecording, recordedOnce: this.state.recordedOnce || recordingState.isRecording) startStopRecording: () -> @@ -165,6 +169,9 @@ if accessOpener ` windowUnloaded: () -> + @unloaded = true + window.unloaded = true + window.opener.RecordingActions.recordingControlsClosed() onChatHelp: (e) -> @@ -215,6 +222,9 @@ if accessOpener $root = jQuery(this.getDOMNode()) $includeChat = $root.find('#include-chat') + shouldComponentUpdate: () -> + return !@unloaded + resizeWindow: () => $container = $('#minimal-container') width = $container.width() diff --git a/web/app/assets/javascripts/react-components/SessionMyTrack.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionMyTrack.js.jsx.coffee index fb025d2e5..a631b41db 100644 --- a/web/app/assets/javascripts/react-components/SessionMyTrack.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionMyTrack.js.jsx.coffee @@ -119,7 +119,7 @@ ConfigureTracksActions = @ConfigureTracksActions $connectionState, 'SessionStatsHover', () => - {participant: {client_id: this.props.clientId, user: name: 'You', possessive: 'Your'}, } + {myTrack: true, participant: {client_id: this.props.connStatsClientId, user: name: 'You', possessive: 'Your'}, } , {width:385, positions:['right', 'left'], offsetParent:$root.closest('.screen'), extraClasses: 'self'}) diff --git a/web/app/assets/javascripts/react-components/SessionOtherTracks.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionOtherTracks.js.jsx.coffee index 298dff75a..5566c8595 100644 --- a/web/app/assets/javascripts/react-components/SessionOtherTracks.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionOtherTracks.js.jsx.coffee @@ -9,15 +9,29 @@ MixerActions = context.MixerActions session = sessionMixers.session mixers = sessionMixers.mixers noAudioUsers = mixers.noAudioUsers + clientsWithAudioOverride = mixers.clientsWithAudioOverride participants = [] if session.inSession() + self = session.getParticipant(@app.clientId) + + myJamBlasterClientId = null + if self? && self.client_role == 'child' && self.parent_client_id? + myJamBlasterClientId = self.parent_client_id + for participant in session.otherParticipants() - #if participant.is_jamblaster - #continue + if myJamBlasterClientId? && participant.client_id == myJamBlasterClientId + # don't show my parent jamblaster in 'others' + continue + + if participant.client_role == 'child' && participant.parent_client_id? + #participant.parent = session.getParticipant(participant.parent_client_id) + # don't show children nodes + continue + if participant.client_id == @app.clientId participant.user.possessive = "Your" @@ -55,6 +69,10 @@ MixerActions = context.MixerActions name = "#{name}: #{instrumentDescription}" + noAudio = false + if !clientsWithAudioOverride[participant.client_id] + noAudio = noAudioUsers[participant.client_id] + participantState = { participant: participant, tracks: tracks, @@ -63,7 +81,7 @@ MixerActions = context.MixerActions instrumentIcon: instrumentIcon, photoUrl: photoUrl, hasMixer: hasMixer, - noAudio: noAudioUsers[participant.client_id] + noAudio: noAudio } MixerActions.missingPeerMixer(participant.client_id) unless hasMixer diff --git a/web/app/assets/javascripts/react-components/SessionSelfVolumeHover.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionSelfVolumeHover.js.jsx.coffee index 68ddde797..f2e91c536 100644 --- a/web/app/assets/javascripts/react-components/SessionSelfVolumeHover.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionSelfVolumeHover.js.jsx.coffee @@ -81,7 +81,7 @@ MixerActions = @MixerActions
Volume
{monitorVolumeLeft}dB
- +
@@ -108,7 +108,7 @@ MixerActions = @MixerActions
Volume
{chatVolumeLeft}dB
- +
diff --git a/web/app/assets/javascripts/react-components/SessionStatsHover.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionStatsHover.js.jsx.coffee index 3eeac6bc1..f0c331233 100644 --- a/web/app/assets/javascripts/react-components/SessionStatsHover.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionStatsHover.js.jsx.coffee @@ -143,7 +143,7 @@ StatsInfo = { audio = @state.stats?.audio aggregateTag = null - if aggregate? + if aggregate? && !this.props.myTrack if aggregate.latency aggregateStats.push(@stat(aggregate, 'aggregate', 'Tot Latency', 'latency', Math.round(aggregate.latency))) @@ -173,10 +173,16 @@ StatsInfo = { audio_type = 'WDM' else if audio_long.indexOf('core') > -1 audio_type = 'CoreAudio' + else if audio_long.indexOf('alsa') > -1 + audio_type = 'JamBlaster' audioStats.push(@stat(audio, 'audio', 'Gear Driver', 'audio_in_type', audio_type)) if audio.framesize? framesize = '?' - if audio.framesize == 2.5 + if audio.framesize == 1.0 + framesize = '1 ms' + else if audio.framesize == 2.0 + framesize = '1 ms' + else if audio.framesize == 2.5 framesize = '2.5 ms' else if audio.framesize == 5 framesize = '5 ms' @@ -185,7 +191,7 @@ StatsInfo = { audioStats.push(@stat(audio, 'audio', 'Frame Size', 'framesize', framesize)) networkTag = null - if network? + if network? && !this.props.myTrack if network.ping? networkStats.push(@stat(network, 'network', 'Latency', 'ping', (network.ping / 2).toFixed(1) + ' ms')) if network.audiojq_median? @@ -238,7 +244,11 @@ StatsInfo = { onStatsChanged: (stats) -> stats = window.SessionStatsStore.stats if stats? - clientStats = stats[@props.participant.client_id] + if stats.parent? + # if we have a parent, then use stats from the JamBlaster (parent), not ourselves. Otherwise we'll get bad stats (no Audio etc) + clientStats = stats.parent[@props.participant.client_id] + else + clientStats = stats[@props.participant.client_id] else clientStats = null @setState({stats: clientStats}) diff --git a/web/app/assets/javascripts/react-components/SessionTrackGain.js.jsx.coffee b/web/app/assets/javascripts/react-components/SessionTrackGain.js.jsx.coffee index 000d176c7..aa2781b65 100644 --- a/web/app/assets/javascripts/react-components/SessionTrackGain.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/SessionTrackGain.js.jsx.coffee @@ -8,6 +8,7 @@ MIX_MODES = context.JK.MIX_MODES propTypes: { gainType: React.PropTypes.string + controlGroup: React.PropTypes.string } getInitialState: () -> @@ -22,12 +23,11 @@ MIX_MODES = context.JK.MIX_MODES mixers = @state.mixers.mixer - # if this is a media track, jam track , or media category, affect volume of both mixer and opposing mixer if @state.mixers.mixer.group_id == ChannelGroupIds.MediaTrackGroup || @state.mixers.mixer.group_id == ChannelGroupIds.JamTrackGroup || ((@state.mixers.mixer.group_id == ChannelGroupIds.MonitorCatGroup || @state.mixers.mixer.group_id == ChannelGroupIds.MasterCatGroup) && @state.mixers.mixer.name == CategoryGroupIds.MediaTrack) - MixerActions.faderChanged(data, [@state.mixers.mixer, @state.mixers.oppositeMixer], @props.gainType) + MixerActions.faderChanged(data, [@state.mixers.mixer, @state.mixers.oppositeMixer], @props.gainType, @props.controlGroup) else - MixerActions.faderChanged(data, mixers, @props.gainType) + MixerActions.faderChanged(data, mixers, @props.gainType, @props.controlGroup) render: () -> # mixer can be a single item or array diff --git a/web/app/assets/javascripts/react-components/actions/MixerActions.js.coffee b/web/app/assets/javascripts/react-components/actions/MixerActions.js.coffee index 68c71dfba..cbb7916cc 100644 --- a/web/app/assets/javascripts/react-components/actions/MixerActions.js.coffee +++ b/web/app/assets/javascripts/react-components/actions/MixerActions.js.coffee @@ -14,4 +14,5 @@ context = window metronomeChanged: {} deadUserRemove: {} missingPeerMixer: {} + clientsWithAudio: {} }) diff --git a/web/app/assets/javascripts/react-components/actions/RecordingActions.js.coffee b/web/app/assets/javascripts/react-components/actions/RecordingActions.js.coffee index f6b111ac9..ad49fc0ca 100644 --- a/web/app/assets/javascripts/react-components/actions/RecordingActions.js.coffee +++ b/web/app/assets/javascripts/react-components/actions/RecordingActions.js.coffee @@ -11,4 +11,5 @@ context = window abortedRecording: {} openRecordingControls: {} recordingControlsClosed: {} + mixTransferred: {} }) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee b/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee index aa28215d6..a475caad5 100644 --- a/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee +++ b/web/app/assets/javascripts/react-components/helpers/MixerHelper.js.coffee @@ -8,7 +8,7 @@ MIX_MODES = context.JK.MIX_MODES; @MixerHelper = class MixerHelper - constructor: (@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixMode) -> + constructor: (@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixMode) -> @mixMode = MIX_MODES.PERSONAL # TODO - remove mixMode from MixerHelper? Or at least stop using it in most functions @app = @session.app @mixersByResourceId = {} @@ -622,13 +622,12 @@ MIX_MODES = context.JK.MIX_MODES; return mixerPair.personal - findMixerForTrack: (client_id, track, myTrack, mode = MIX_MODES.PERSONAL) -> + findMixerForTrack: (client_id, track, myTrack, mode) -> mixer = null # what is the best mixer for this track/client ID? oppositeMixer = null # what is the corresponding mixer in the opposite mode? vuMixer = null muteMixer = null - if myTrack # when it's your track, look it up by the backend resource ID mixer = @getMixerByTrackId(track.client_track_id, mode) @@ -674,7 +673,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, @personalMixers ) + 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) @@ -693,6 +692,8 @@ MIX_MODES = context.JK.MIX_MODES; logger.error("personaol: found remote mixer that was not of groupID: PeerAudioInputMusicGroup", client_id, track.client_track_id, mixer) #vuMixer = oppositeMixer; # for personal mode, use the PeerAudioInputMusicGroup's VUs + else + logger.error("no UserMusicInputGroup for client_id #{client_id} in PERSONAL mode", mixers) { mixer: mixer, @@ -729,26 +730,43 @@ MIX_MODES = context.JK.MIX_MODES; originalVolume - faderChanged: (data, mixers, gainType) -> + faderChanged: (data, mixers, gainType, controlGroup) -> mixers = [mixers] unless $.isArray(mixers) originalVolume = @getOriginalVolume(mixers, gainType) - for mixer in mixers - broadcast = !(data.dragging) # If fader is still dragging, don't broadcast - mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast) + if controlGroup? + mixers = [mixers[0]] - relative = gainType == 'music' && (mixer.name == CategoryGroupIds.UserMedia || mixer.name == CategoryGroupIds.MediaTrack) + for mixer in mixers + broadcast = !(data.dragging) # If fader is still dragging, don't broadcast + mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast) - @setMixerVolume(mixer, data.percentage, relative, originalVolume) + relative = gainType == 'music' && (mixer.name == CategoryGroupIds.UserMedia || mixer.name == CategoryGroupIds.MediaTrack) - # keep state of mixer in sync with backend - mixer = @getMixer(mixer.id, mixer.mode) - mixer.volume_left = context.trackVolumeObject.volL + @setMixerVolume(mixer, data.percentage, relative, originalVolume, controlGroup) - #if groupId == ChannelGroupIds.UserMusicInputGroup - # # there may be other mixers with this same ID in the case of a Peer Music Stream, so update them as well - # context.JK.FaderHelpers.setFaderValue(mixerId, data.percentage) + # keep state of mixer in sync with backend + mixer = @getMixer(mixer.id, mixer.mode) + mixer.volume_left = context.trackVolumeObject.volL + + else + + for mixer in mixers + broadcast = !(data.dragging) # If fader is still dragging, don't broadcast + mixer = @fillTrackVolumeObject(mixer.id, mixer.mode, broadcast) + + relative = gainType == 'music' && (mixer.name == CategoryGroupIds.UserMedia || mixer.name == CategoryGroupIds.MediaTrack) + + @setMixerVolume(mixer, data.percentage, relative, originalVolume) + + # keep state of mixer in sync with backend + mixer = @getMixer(mixer.id, mixer.mode) + mixer.volume_left = context.trackVolumeObject.volL + + #if groupId == ChannelGroupIds.UserMusicInputGroup + # # there may be other mixers with this same ID in the case of a Peer Music Stream, so update them as well + # context.JK.FaderHelpers.setFaderValue(mixerId, data.percentage) initGain: (mixer) -> if $.isArray(mixer) @@ -791,7 +809,7 @@ MIX_MODES = context.JK.MIX_MODES; mixer = @getMixer(mixer.id, mixer.mode) mixer.loop = context.trackVolumeObject.loop - setMixerVolume: (mixer, volumePercent, relative, originalVolume) -> + setMixerVolume: (mixer, volumePercent, relative, originalVolume, controlGroup) -> ### // The context.trackVolumeObject has been filled with the mixer values // that go with mixerId, and the range of that mixer @@ -821,7 +839,15 @@ MIX_MODES = context.JK.MIX_MODES; else context.trackVolumeObject.volL = newVolume context.trackVolumeObject.volR = newVolume - context.jamClient.SessionSetControlState(mixer.id, mixer.mode); + if controlGroup? + + if mixer.mode == MIX_MODES.PERSONAL + controlGroupsArg = 0 + else + controlGroupsArg = 1 + context.jamClient.setSessionMixerCategoryPlayoutState(controlGroup == 'music', controlGroupsArg); + else + context.jamClient.SessionSetControlState(mixer.id, mixer.mode); percentFromMixerValue: (min, max, value) -> try diff --git a/web/app/assets/javascripts/react-components/landing/RedeemGiftCardPage.js.jsx.coffee b/web/app/assets/javascripts/react-components/landing/RedeemGiftCardPage.js.jsx.coffee index 57ca08d88..cfb102bc1 100644 --- a/web/app/assets/javascripts/react-components/landing/RedeemGiftCardPage.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/landing/RedeemGiftCardPage.js.jsx.coffee @@ -22,7 +22,7 @@ badCode = 'This is not a valid code. Please carefully re-enter the code and try if errorText? && errorText.indexOf('already claimed') > -1 errorText = 'This card has already been claimed. If you believe this is in error, please email us at support@jamkazam.com to report this problem.' - + buttonClassnames = classNames({'redeem-giftcard': true, 'button-orange': true, disabled: @state.processing || @state.done }) diff --git a/web/app/assets/javascripts/react-components/mixins/SessionMyTracksMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/SessionMyTracksMixin.js.coffee index a25950505..6a6430b33 100644 --- a/web/app/assets/javascripts/react-components/mixins/SessionMyTracksMixin.js.coffee +++ b/web/app/assets/javascripts/react-components/mixins/SessionMyTracksMixin.js.coffee @@ -28,6 +28,12 @@ MIDI_TRACK = context.JK.MIDI_TRACK if session.inSession() participant = session.getParticipant(@app.clientId) + connStatsClientId = @app.clientId + + if participant.client_role == 'child' && participant.parent_client_id? + participant.parent = session.getParticipant(participant.parent_client_id) + connStatsClientId = participant.parent_client_id + if participant photoUrl = context.JK.resolveAvatarUrl(participant.user.photo_url); @@ -79,7 +85,7 @@ MIDI_TRACK = context.JK.MIDI_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}) + tracks.push({track: track, mixerFinder: mixerFinder, mixers: mixerData, hasMixer:hasMixer, name: name, trackName: trackName, instrumentIcon: instrumentIcon, photoUrl: photoUrl, clientId: participant.client_id, associatedVst: associatedVst, connStatsClientId: connStatsClientId}) else logger.warn("SessionMyTracks: unable to find participant") diff --git a/web/app/assets/javascripts/react-components/stores/CallbackStore.js.coffee b/web/app/assets/javascripts/react-components/stores/CallbackStore.js.coffee index 65bb1d115..f5b81df87 100644 --- a/web/app/assets/javascripts/react-components/stores/CallbackStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/CallbackStore.js.coffee @@ -1,6 +1,7 @@ $ = jQuery context = window logger = context.JK.logger +RecordingActions = @RecordingActions SessionActions = @SessionActions JamBlasterActions = @JamBlasterActions @@ -27,6 +28,9 @@ JamBlasterActions = @JamBlasterActions JamBlasterActions.pairState(map) else if map.cmd == 'jamblaster_tracks_updated' JamBlasterActions.jamblasterTracksUpdated() + else if map.cmd == 'file_xfer_from_parent' + if map.filename && map.filename.indexOf('RT-mix.wav') > -1 + RecordingActions.mixTransferred() } ) diff --git a/web/app/assets/javascripts/react-components/stores/MixerStore.js.coffee b/web/app/assets/javascripts/react-components/stores/MixerStore.js.coffee index d98600ca5..7518f8735 100644 --- a/web/app/assets/javascripts/react-components/stores/MixerStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/MixerStore.js.coffee @@ -20,6 +20,7 @@ rest = context.JK.Rest() checkingMissingPeers : {} missingMixerPeers : {} recheckTimeout : null + clientsWithAudioOverride : {} init: -> # Register with the app store to get @app @@ -38,6 +39,7 @@ rest = context.JK.Rest() this.listenTo(context.MixerActions.metronomeChanged, this.onMetronomeChanged) this.listenTo(context.MixerActions.deadUserRemove, this.onDeadUserRemove) this.listenTo(context.MixerActions.missingPeerMixer, this.onMissingPeerMixer) + this.listenTo(context.MixerActions.clientsWithAudio, this.onClientsWithAudio) context.JK.HandleVolumeChangeCallback2 = @handleVolumeChangeCallback context.JK.HandleMetronomeCallback2 = @handleMetronomeCallback @@ -76,7 +78,7 @@ rest = context.JK.Rest() # metroSound = args.sound SessionActions.syncWithServer() - @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL) + @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL) @issueChange() @@ -132,7 +134,7 @@ rest = context.JK.Rest() @masterMixers = context.jamClient.SessionGetAllControlState(true); @personalMixers = context.jamClient.SessionGetAllControlState(false); - @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL) + @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL) @issueChange() @@ -146,9 +148,9 @@ rest = context.JK.Rest() # simulate a state change to cause a UI redraw @issueChange() - onFaderChanged: (data, mixers, gainType) -> + onFaderChanged: (data, mixers, gainType, controlGroup) -> - @mixers.faderChanged(data, mixers, gainType) + @mixers.faderChanged(data, mixers, gainType, controlGroup) @issueChange() @@ -171,7 +173,7 @@ rest = context.JK.Rest() @metro.sound = sound context.jamClient.SessionSetMetronome(@metro.tempo, @metro.sound, 1, 0); - @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL) + @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL) @issueChange() onDeadUserRemove: (clientId) -> @@ -193,6 +195,9 @@ rest = context.JK.Rest() @issueChange() + onClientsWithAudio: (clients) -> + @clientsWithAudioOverride = clients + onMissingPeerMixer: (clientId) -> missingPeerAttempts = @missingMixerPeers[clientId] @@ -219,7 +224,7 @@ rest = context.JK.Rest() @masterMixers = context.jamClient.SessionGetAllControlState(true); @personalMixers = context.jamClient.SessionGetAllControlState(false); logger.debug("MixerStore: recheckForMixers") - @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL) + @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL) @issueChange() onInitGain: (mixer) -> @@ -234,7 +239,7 @@ rest = context.JK.Rest() logger.debug("MixerStore: onMixersChanged") - @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @mixers?.mixMode || MIX_MODES.PERSONAL) + @mixers = new context.MixerHelper(@session, @masterMixers, @personalMixers, @metro, @noAudioUsers, @clientsWithAudioOverride, @mixers?.mixMode || MIX_MODES.PERSONAL) SessionActions.mixersChanged.trigger(type, text, @mixers.getTrackInfo()) diff --git a/web/app/assets/javascripts/react-components/stores/RecordingStore.js.jsx.coffee b/web/app/assets/javascripts/react-components/stores/RecordingStore.js.jsx.coffee index 682d8744e..70e49ee33 100644 --- a/web/app/assets/javascripts/react-components/stores/RecordingStore.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/stores/RecordingStore.js.jsx.coffee @@ -33,10 +33,11 @@ BackendToFrontendFPS = { this.trigger({isRecording: @recordingModel.isRecording()}) onStartRecording: (recordVideo, recordChat) -> - - frameRate = context.jamClient.GetCurrentVideoFrameRate() || 0; - - frameRate = BackendToFrontendFPS[frameRate] + frameRate = 0 + if recordVideo + if context.jamClient.GetCurrentVideoFrameRate? + frameRate = context.jamClient.GetCurrentVideoFrameRate() || 0; + frameRate = BackendToFrontendFPS[frameRate] NoVideoRecordActive = 0 WebCamRecordActive = 1 @@ -49,12 +50,14 @@ BackendToFrontendFPS = { onStartingRecording: (details) -> details.cause = 'starting' + @mixTransferred = false this.trigger(details) @popupRecordingControls() unless @recordingWindow? onStartedRecording: (details) -> details.cause = 'started' + @mixTransferred = false this.trigger(details) @popupRecordingControls() unless @recordingWindow? @@ -92,6 +95,9 @@ BackendToFrontendFPS = { logger.debug("recording controls closed") @recordingWindow = null + onMixTransferred: () -> + @mixTransferred = true + popupRecordingControls: () -> logger.debug("poupRecordingControls") @recordingWindow = window.open("/popups/recording-controls", 'Recording', 'scrollbars=yes,toolbar=no,status=no,height=315,width=340') diff --git a/web/app/assets/javascripts/react-components/stores/SessionStatsStore.js.coffee b/web/app/assets/javascripts/react-components/stores/SessionStatsStore.js.coffee index 00439ff3c..30c8e2f26 100644 --- a/web/app/assets/javascripts/react-components/stores/SessionStatsStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/SessionStatsStore.js.coffee @@ -6,6 +6,7 @@ EVENTS = context.JK.EVENTS MIX_MODES = context.JK.MIX_MODES SessionActions = @SessionActions +MixerActions = @MixerActions SessionStatThresholds = gon.session_stat_thresholds NetworkThresholds = SessionStatThresholds.network @@ -16,7 +17,9 @@ AggregateThresholds = SessionStatThresholds.aggregate @SessionStatsStore = Reflux.createStore( { listenables: @SessionStatsActions - rawStats: null + rawStats: null, + parentStats: null + clientsWithAudio: null init: -> # Register with the app store to get @app @@ -24,8 +27,9 @@ AggregateThresholds = SessionStatThresholds.aggregate onAppInit: (@app) -> - onPushStats: (stats) -> + onPushStats: (stats, parentStats) -> @rawStats = stats + @parentStats = parentStats @changed() classify: (holder, field, threshold) -> @@ -65,17 +69,15 @@ AggregateThresholds = SessionStatThresholds.aggregate else holder[fieldLevel] = 'good' - - changed: () -> - @stats = {} - + classifyStats: (statBag) -> + container = {} self = null - for participant in @rawStats + for participant in statBag if participant.id == @app.clientId self = participant break - for participant in @rawStats + for participant in statBag aggregate = {} @@ -93,6 +95,9 @@ AggregateThresholds = SessionStatThresholds.aggregate network = participant.network if network? + if network.audio_bitrate_rx > 0 && network.audio_bitrate_tx > 0 + @clientsWithAudio[participant.id] = true + @classify(network, 'audiojq_median', NetworkThresholds) @classify(network, 'jitter_var', NetworkThresholds) @classify(network, 'audio_bitrate_rx', NetworkThresholds) @@ -151,8 +156,23 @@ AggregateThresholds = SessionStatThresholds.aggregate else participant.classification = 'unknown' - @stats[participant.id] = participant + container[participant.id] = participant + return container + + changed: () -> + @clientsWithAudio = {} + @stats = {} + + @stats = @classifyStats(@rawStats) + if @parentStats + @stats.parent = @classifyStats(@parentStats) + else + @stats.parent = null + + MixerActions.clientsWithAudio(@clientsWithAudio) + + # see if we can reset noAudio @trigger(@stats) } ) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee b/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee index 139c3b1d5..4c4b564ec 100644 --- a/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/SessionStore.js.coffee @@ -728,8 +728,19 @@ ConfigureTracksActions = @ConfigureTracksActions context.jamClient.SessionRegisterCallback("JK.HandleBridgeCallback2"); context.jamClient.RegisterRecordingCallbacks("JK.HandleRecordingStartResult", "JK.HandleRecordingStopResult", "JK.HandleRecordingStarted", "JK.HandleRecordingStopped", "JK.HandleRecordingAborted"); context.jamClient.SessionSetConnectionStatusRefreshRate(1000); + clientRole = context.jamClient.getClientParentChildRole(); + parentClientId = context.jamClient.getParentClientId(); + logger.debug("role when joining session: #{clientRole}, parent client id #{parentClientId}") #context.JK.HelpBubbleHelper.jamtrackGuideSession($screen.find('li.open-a-jamtrack'), $screen) + if clientRole == 0 + clientRole = 'child' + else if clientRole == 1 + clientRole = 'parent' + + if clientRole == '' || !clientRole + clientRole = null + # subscribe to events from the recording model @recordingRegistration() @@ -741,6 +752,8 @@ ConfigureTracksActions = @ConfigureTracksActions as_musician: true, tracks: @userTracks, session_id: @currentSessionId, + client_role: clientRole, + parent_client_id: parentClientId audio_latency: context.jamClient.FTUEGetExpectedLatency().latency }) .done((response) => @@ -840,8 +853,11 @@ ConfigureTracksActions = @ConfigureTracksActions @backendStatsInterval = window.setInterval((() => (@updateBackendStats())), 1000) updateBackendStats: () -> - connectionStats = window.jamClient.getConnectionDetail('') - SessionStatsActions.pushStats(connectionStats) + connectionStats = window.jamClient.getConnectionDetail('', false) + parentConnectionStats = window.jamClient.getConnectionDetail('', true) + #console.log("CONNECTION STATES", connectionStats) + #console.log("PARENT STATES", parentConnectionStats) + SessionStatsActions.pushStats(connectionStats, parentConnectionStats) trackChanges: (header, payload) -> if @currentTrackChanges < payload.track_changes_counter diff --git a/web/app/controllers/api_music_sessions_controller.rb b/web/app/controllers/api_music_sessions_controller.rb index 6ee432d4e..35d3111e2 100644 --- a/web/app/controllers/api_music_sessions_controller.rb +++ b/web/app/controllers/api_music_sessions_controller.rb @@ -297,7 +297,9 @@ class ApiMusicSessionsController < ApiController params[:client_id], params[:as_musician], params[:tracks], - params[:audio_latency] + params[:audio_latency], + params[:client_role], + params[:parent_client_id] ) if @connection.errors.any? diff --git a/web/app/views/api_music_sessions/show.rabl b/web/app/views/api_music_sessions/show.rabl index 4fc73ea8a..2b931e40e 100644 --- a/web/app/views/api_music_sessions/show.rabl +++ b/web/app/views/api_music_sessions/show.rabl @@ -50,7 +50,7 @@ else child(:connections => :participants) { collection @music_sessions, :object_root => false - attributes :ip_address, :client_id, :joined_session_at, :audio_latency, :id, :metronome_open, :is_jamblaster + attributes :ip_address, :client_id, :joined_session_at, :audio_latency, :id, :metronome_open, :is_jamblaster, :client_role, :parent_client_id node :user do |connection| { :id => connection.user.id, :photo_url => connection.user.photo_url, :name => connection.user.name, :is_friend => connection.user.friends?(current_user), :connection_state => connection.aasm_state } diff --git a/web/app/views/dialogs/_recordingFinishedDialog.html.haml b/web/app/views/dialogs/_recordingFinishedDialog.html.haml index 2fadd425c..17f324423 100644 --- a/web/app/views/dialogs/_recordingFinishedDialog.html.haml +++ b/web/app/views/dialogs/_recordingFinishedDialog.html.haml @@ -4,8 +4,11 @@ = image_tag "content/recordbutton-off.png", {:height => 20, :width => 20, :class => 'content-icon'} %h1 recording finished .dialog-inner - Fill out the fields below and click the "SAVE" button to save this recording to your library. If you do not want to - keep the recording, click the "DISCARD" button. + %span.nowait + Fill out the fields below and click the "SAVE" button to save this recording to your library. If you do not want to + keep the recording, click the "DISCARD" button. + %span.pleasewait + Please wait while we transfer the necessary audio files from your JamBlaster to your client... %br/ %br/ %form.left.w40.mr20 @@ -36,7 +39,7 @@ %input{:checked => "checked", :name => "is_public", :type => "checkbox"}/ %label{:for => "is_public"} Public Recording / < - .left.w50.ml30 + .left.w50.ml30.preview-area Preview Recording: \#{render "clients/play_controls"} diff --git a/web/app/views/users/_user_dropdown.html.erb b/web/app/views/users/_user_dropdown.html.erb index 31cd39683..3bb28d4b6 100644 --- a/web/app/views/users/_user_dropdown.html.erb +++ b/web/app/views/users/_user_dropdown.html.erb @@ -48,7 +48,7 @@ <% end %> <% if @nativeClient && Rails.application.config.jamblaster_menu %> -
  • <%= link_to "JamBlaster", '/client#/jamblaster' %>
  • +
  • <%= link_to "JamBlaster Settings", '/client#/jamblaster' %>
  • <% end %> <% if current_user && current_user.musician? %>
  • <%= link_to "Band Setup", '/client#/band/setup/new' %>
  • diff --git a/websocket-gateway/lib/jam_websockets/router.rb b/websocket-gateway/lib/jam_websockets/router.rb index 0899281c6..a2e329286 100644 --- a/websocket-gateway/lib/jam_websockets/router.rb +++ b/websocket-gateway/lib/jam_websockets/router.rb @@ -740,6 +740,10 @@ module JamWebsockets ipv4_link_local = options["ipv4_link_local"] ipv6_link_local = options["ipv6_link_local"] + # it's nice to have client_ids not flap in the wind, and we can do that with jamblasters + if jamblaster_serial_no && client_id.nil? + client_id = jamblaster_serial_no + end # TESTING #if jamblaster_serial_no.nil? # jamblaster_serial_no = 'hi'