diff --git a/web/app/assets/javascripts/feed_item_recording.js b/web/app/assets/javascripts/feed_item_recording.js index fbc030982..05dfd3706 100644 --- a/web/app/assets/javascripts/feed_item_recording.js +++ b/web/app/assets/javascripts/feed_item_recording.js @@ -9,6 +9,11 @@ var $description = $('.description', $feedItem); var $musicians = $('.musician-detail', $feedItem); var $controls = $('.recording-controls', $feedItem); + var $sliderBar = $('.recording-position', $feedItem); + var $statusBar = $('.recording-status', $feedItem); + var $currentTime = $('.recording-current', $feedItem); + var $status = $('.status-text', $feedItem); + var $playButton = $('.play-button', $feedItem); var playing = false; var toggledOpen = false; @@ -44,6 +49,13 @@ function stateChange(e, data) { if(data.isEnd) stopPlay(); + if(data.isError) { + $sliderBar.hide(); + $playButton.hide(); + $currentTime.hide(); + $statusBar.show(); + $status.text(data.displayText); + } } function toggleDetails() { diff --git a/web/app/assets/javascripts/jquery.listenRecording.js b/web/app/assets/javascripts/jquery.listenRecording.js index 1ce3717a4..c7fee2482 100644 --- a/web/app/assets/javascripts/jquery.listenRecording.js +++ b/web/app/assets/javascripts/jquery.listenRecording.js @@ -20,9 +20,10 @@ var self = this; var waitForBufferingTimeout = null; var destroyed = false; - var lastTime = 0; + var lastTime = -1; var dragging = false; var loaded = null; + var isPlaying = false; var PlayStateNone = 'none'; var PlayStateInitializing = 'initializing'; // user clicked play--nothing has happened yet @@ -39,9 +40,18 @@ var canPlay = false; function waitForLoaded() { - if(!loaded) { + + if(needsReloadOnEveryPlay() || !loaded) { loaded = $.Deferred(); - audioDomElement.load(); + + if(supportsLoadBeforePlay()) { + audioDomElement.load(); + } + else { + audioDomElement.play(); + audioDomElement.pause(); + //loaded.resolve(); + } } return loaded; @@ -57,15 +67,15 @@ if(!audioDomElement) throw "no audio element supplied; the user should not be able to attempt a play" - waitForLoaded.done(function() { - audioDomElement.currentTime = lastTime; + transition(PlayStateInitializing); + // keep this after transition, because any transition clears this timer + waitForBufferingTimeout = setTimeout(noBuffer, WAIT_FOR_BUFFER_TIMEOUT); + + waitForLoaded().done(function() { + if(lastTime > -1) {audioDomElement.currentTime = lastTime;} audioDomElement.play(); - transition(PlayStateInitializing); - // keep this after transition, because any transition clears this timer - waitForBufferingTimeout = setTimeout(noBuffer, WAIT_FOR_BUFFER_TIMEOUT); + isPlaying = true; }) - - } function pause(e) { @@ -81,7 +91,7 @@ transition(PlayStateNone); audioDomElement.pause(); - //recreateAudioElement(); + isPlaying = false; } function destroy() { @@ -96,6 +106,7 @@ function noBuffer() { logger.debug("never received indication of buffering or playing"); + alert("never received indication of buffering or playing"); transition(PlayStateFailedStart); } @@ -125,9 +136,20 @@ function transition(newState) { logger.debug("transitioning from " + playState + " to " + newState); + if(playState != PlayStateInitializing) { + clearBufferTimeout(); + } + playState = newState; - clearBufferTimeout(); + + // sometimes browsers don't quite make it to 100% if we end at 99%, reset it + var percentComplete = (audioDomElement.currentTime / audioDomElement.duration) * 100; + if(playState == PlayStateEnded && percentComplete > 99) { + updateSliderPosition(0); + $currentTime.html("0:00"); + lastTime = 0 + } if( playState == PlayStateNone || playState == PlayStateEnded || @@ -135,6 +157,7 @@ playState == PlayStateFailedPlaying || playState == PlayStateNetworkError) { context.JK.ListenBroadcastCurrentlyPlaying = null; + isPlaying = false; } triggerStateChange(); @@ -143,7 +166,7 @@ function isNoisyEvent(eventName) { if(playState == PlayStateNone) { - console.log("ignoring: " + eventName) + logger.log("ignoring: " + eventName) return true; } @@ -165,6 +188,11 @@ if(isNoisyEvent('pause')) return; logger.debug("pause", arguments); + if(treatPauseLikeEnd()) { + transition(PlayStateEnded); + return; + }; + transition(PlayStateStalled); } @@ -176,6 +204,7 @@ transition(PlayStateFailedPlaying); } else { + alert("ON ERROR") transition(PlayStateFailedStart); } } @@ -195,6 +224,8 @@ function onAbort() { if(isNoisyEvent('abort')) return; logger.debug("abort", arguments); + + transition(PlayStateFailedStart); } function onStalled() { @@ -212,8 +243,6 @@ if(isNoisyEvent('suspend')) return; logger.debug("onsuspend", arguments); - // fires in FF on page load - transition(PlayStateStalled); } @@ -239,7 +268,7 @@ } function updateSliderPosition(percent) { - console.log("updateSliderPosition", percent) + //logger.log("updateSliderPosition", percent) $slider.css({'left': percent + '%'}); } @@ -261,7 +290,10 @@ var percent = ui.position.left / $sliderBar.width() * 100; updateSliderPosition(percent); - audioDomElement.currentTime = percent * audioDomElement.duration; + waitForLoaded() + .done(function() { + audioDomElement.currentTime = percent * audioDomElement.duration / 100; + }); } function onDrag(e, ui) { @@ -273,6 +305,7 @@ var isEnd = false; var displayText = null; var refresh = false; + var isError = false; if(playState == 'none') { //$status.text('SESSION IN PROGRESS'); @@ -296,14 +329,17 @@ else if(playState == 'failed_start') { displayText = 'AUDIO DID NOT START'; isEnd = true; + isError = true; } else if(playState == 'failed_playing') { displayText = 'STREAM DISCONNECTED'; isEnd = true; + isError = true; } else if(playState == 'network_error') { displayText = 'NO NETWORK'; isEnd = true; + isError = true; } else { logger.error("unknown state: " + playState) @@ -313,10 +349,24 @@ { state: playState, displayText: displayText, - isEnd: isEnd + isEnd: isEnd, + isError: isError }) } + function supportsLoadBeforePlay() { + // firefox does not need you to call .load to mess with currentTime + return !(navigator.userAgent.toLowerCase().indexOf('firefox') > -1); + } + function needsReloadOnEveryPlay() { + return navigator.userAgent.toLowerCase().indexOf('jamkazam') > -1; + } + + function treatPauseLikeEnd() { + // never see a pause event until the session is over, in the rich client + return playState == PlayStatePlaying && navigator.userAgent.toLowerCase().indexOf('jamkazam') > -1; + } + function audioBind() { $audio.bind('play', onPlay); $audio.bind('playing', onPlaying); @@ -343,7 +393,6 @@ $audio = $('audio', $parent); if($audio.length == 0) { - logger.debug("listen_recording: no audio element. deactivating") return; } if($audio.length > 1) { @@ -361,9 +410,11 @@ var offset = e.pageX - $(this).offset().left; var percent = offset / $sliderBar.width() * 100; updateSliderPosition(percent); - - - audioDomElement.currentTime = percent * audioDomElement.duration; + waitForLoaded() + .done(function() { + audioDomElement.currentTime = percent / 100 * audioDomElement.duration; + if(isPlaying) audioDomElement.play(); + }) return false; }); diff --git a/web/app/assets/javascripts/jquery.listenbroadcast.js b/web/app/assets/javascripts/jquery.listenbroadcast.js index 440c0a1a8..e04a099d2 100644 --- a/web/app/assets/javascripts/jquery.listenbroadcast.js +++ b/web/app/assets/javascripts/jquery.listenbroadcast.js @@ -280,9 +280,6 @@ return; } - if(ignoreStalls()) return; - // fires in Chrome on page load - if(playState == PlayStateBuffering || playState == PlayStatePlaying) { transition(PlayStateStalled); } @@ -412,10 +409,6 @@ return playState == PlayStatePlaying && navigator.userAgent.toLowerCase().indexOf('jamkazam') > -1; } - function ignoreStalls() { - return false; - } - function needsCanPlayGuard() { var ua = navigator.userAgent.toLowerCase(); @@ -461,7 +454,6 @@ $audio = $('audio', $parent); if($audio.length == 0) { - logger.log("listen_broadcast: no audio element. deactivating") return; } if($audio.length > 1) { diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index ab4a50d65..10dcc7921 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -563,7 +563,6 @@ var mixerIds = context.jamClient.SessionGetIDs(); var holder = $.extend(true, {}, {mixers: context.jamClient.SessionGetControlState(mixerIds)}); mixers = holder.mixers; - // Always add a hard-coded simplified 'mixer' for the L2M mix var l2m_mixer = { id: '__L2M__', @@ -894,6 +893,7 @@ if (mixer.mute) { muteClass = "muted"; } + trackData.gainPercent = gainPercent; trackData.muteClass = muteClass; trackData.mixerId = mixer.id; @@ -968,6 +968,7 @@ var gainPercent = percentFromMixerValue( mixer.range_low, mixer.range_high, mixer.volume_left); var trackSelector = 'div.track[track-id="' + key + '"]'; + connectTrackToMixer(trackSelector, key, mixer.id, gainPercent); var $track = $('div.track[client-id="' + clientId + '"]'); $track.find('.track-icon-mute').attr('mixer-id', mixer.id); @@ -1033,7 +1034,6 @@ var parentSelector = '#session-mytracks-container'; var $destination = $(parentSelector); if (trackData.clientId !== app.clientId) { - console.log("ADDING REMOTE TRACK: noaudio", trackData.noaudio) parentSelector = '#session-livetracks-container'; $destination = $(parentSelector); $('.session-livetracks .when-empty').hide(); diff --git a/web/app/assets/stylesheets/web/audioWidgets.css.scss b/web/app/assets/stylesheets/web/audioWidgets.css.scss index 375575487..c81ce047b 100644 --- a/web/app/assets/stylesheets/web/audioWidgets.css.scss +++ b/web/app/assets/stylesheets/web/audioWidgets.css.scss @@ -26,6 +26,38 @@ text-align: center; @include border_box_sizing; height: 36px; + + .recording-status { + font-size:15px; + } + + .recording-status .recording-duration { + font-family:Arial, Helvetica, sans-serif; + display:inline-block; + font-size:18px; + float:right; + top:3px; + right:4px; + } + + + &.has-mix { + .recording-status { + display:none; + } + } + &.no-mix { + + .play-button { + display:none; + } + .recording-current { + display:none; + } + .recording-position { + display:none; + } + } } @@ -101,6 +133,10 @@ .recording-time { display:inline; + vertical-align:top; + time { + vertical-align:top; + } } } } diff --git a/web/app/views/users/_feed_recording.html.haml b/web/app/views/users/_feed_recording.html.haml index c206736e3..9e8c252f6 100644 --- a/web/app/views/users/_feed_recording.html.haml +++ b/web/app/views/users/_feed_recording.html.haml @@ -1,4 +1,4 @@ -.feed-entry.recording-entry +.feed-entry.recording-entry{id: feed_item.id, candidate_claimed_recording_id: feed_item.candidate_claimed_recording.id} / avatar .avatar-small.ib = recording_avatar(feed_item) @@ -17,7 +17,7 @@ / timeline and controls .right.w40 / recording play controls - .recording-controls + .recording-controls{ class: feed_item.candidate_claimed_recording.has_mix? ? 'has-mix' : 'no-mix'} / play button %a.left.play-button{:href => "#"} = image_tag 'content/icon_playbutton.png', width:20, height:20, class:'play-icon' @@ -25,6 +25,10 @@ %audio{preload: 'none'} %source{src: claimed_recording_download_url(feed_item.candidate_claimed_recording.id, 'mp3'), type:'audio/mpeg'} %source{src: claimed_recording_download_url(feed_item.candidate_claimed_recording.id, 'ogg'), type:'audio/ogg'} + .recording-status + %span.status-text STILL MIXING + .recording-duration + = recording_duration(feed_item) / playback position .recording-position / start time