From 39c73755b45c8999f61f8a0ba83b439576261a82 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 6 Jan 2024 18:23:02 -0500 Subject: [PATCH 1/4] Add scrub function to audio trickplay bar --- components/music/AudioPlayerView.bs | 178 ++++++++++++++++++++++++--- components/music/AudioPlayerView.xml | 5 +- images/icons/circle.png | Bin 0 -> 2429 bytes 3 files changed, 161 insertions(+), 22 deletions(-) create mode 100644 images/icons/circle.png diff --git a/components/music/AudioPlayerView.bs b/components/music/AudioPlayerView.bs index d9c3e1f9..3391395a 100644 --- a/components/music/AudioPlayerView.bs +++ b/components/music/AudioPlayerView.bs @@ -5,6 +5,7 @@ import "pkg:/source/utils/config.bs" sub init() m.top.optionsAvailable = false + m.inScrubMode = false setupAudioNode() setupAnimationTasks() @@ -32,6 +33,8 @@ sub init() pageContentChanged() setShuffleIconState() setLoopButtonImage() + + m.buttons.setFocus(true) end sub sub onScreensaverTimeoutLoaded() @@ -117,6 +120,7 @@ sub setupInfoNodes() m.playPosition = m.top.findNode("playPosition") m.bufferPosition = m.top.findNode("bufferPosition") m.seekBar = m.top.findNode("seekBar") + m.thumb = m.top.findNode("thumb") m.shuffleIndicator = m.top.findNode("shuffleIndicator") m.loopIndicator = m.top.findNode("loopIndicator") m.positionTimestamp = m.top.findNode("positionTimestamp") @@ -124,6 +128,8 @@ sub setupInfoNodes() end sub sub bufferPositionChanged() + if m.inScrubMode then return + if not isValid(m.global.audioPlayer.bufferingStatus) bufferPositionBarWidth = m.seekBar.width else @@ -141,6 +147,10 @@ sub bufferPositionChanged() end sub sub audioPositionChanged() + if m.inScrubMode then return + + stopLoadingSpinner() + if m.global.audioPlayer.position = 0 m.playPosition.width = 0 end if @@ -159,6 +169,8 @@ sub audioPositionChanged() playPositionBarWidth = m.seekBar.width end if + moveSeekbarThumb(playPositionBarWidth) + ' Use animation to make the display smooth m.playPositionAnimationWidth.keyValue = [m.playPosition.width, playPositionBarWidth] m.playPositionAnimation.control = "start" @@ -217,6 +229,7 @@ sub audioStateChanged() if m.global.audioPlayer.state = "finished" ' User has enabled single song loop, play current song again if m.global.audioPlayer.loopMode = "one" + m.global.audioPlayer.content.playStart = 0 playAction() return end if @@ -255,6 +268,13 @@ function playAction() as boolean ' Write screen tracker for screensaver WriteAsciiFile("tmp:/scene.temp", "nowplaying") MoveFile("tmp:/scene.temp", "tmp:/scene") + else if m.global.audioPlayer.state = "buffering" + m.inScrubMode = false + startLoadingSpinner() + m.global.audioPlayer.control = "play" + ' Write screen tracker for screensaver + WriteAsciiFile("tmp:/scene.temp", "nowplaying") + MoveFile("tmp:/scene.temp", "tmp:/scene") end if return true @@ -540,6 +560,72 @@ sub setBackdropImage(data) end if end sub +' setSelectedButtonState: Changes the icon state url for the currently selected button +' +' @param {string} oldState - current state to replace in icon url +' @param {string} newState - state to replace {oldState} with in icon url +sub setSelectedButtonState(oldState as string, newState as string) + selectedButton = m.buttons.getChild(m.top.selectedButtonIndex) + selectedButton.uri = selectedButton.uri.Replace(oldState, newState) +end sub + +' processScrubAction: Handles +/- seeking for the audio trickplay bar +' +' @param {integer} seekStep - seconds to move the trickplay position (negative values allowed) +sub processScrubAction(seekStep as integer) + m.inScrubMode = true + ' Change audio player control method + m.global.audioPlayer.control = "prebuffer" + + ' Prepare starting playStart property value + if m.global.audioPlayer.position <> 0 + m.global.audioPlayer.content.playStart = m.global.audioPlayer.position + end if + + ' Don't let seek to go past the end of the song + if m.global.audioPlayer.content.playStart + seekStep > m.songDuration + return + end if + + if seekStep > 0 + ' Move seek forward + m.global.audioPlayer.content.playStart += seekStep + else if m.global.audioPlayer.content.playStart >= Abs(seekStep) + ' If back seek won't go below 0, move seek back + m.global.audioPlayer.content.playStart += seekStep + else + ' Back seek would go below 0, set to 0 directly + m.global.audioPlayer.content.playStart = 0 + end if + + ' Move the seedbar thumb forward + songPercentComplete = m.global.audioPlayer.content.playStart / m.songDuration + playPositionBarWidth = m.seekBar.width * songPercentComplete + + moveSeekbarThumb(playPositionBarWidth) + + ' Change the displayed position timestamp + m.positionTimestamp.text = secondsToHuman(m.global.audioPlayer.content.playStart, false) +end sub + +' moveSeekbarThumb: Positions the thumb on the seekbar +' +' @param {float} playPositionBarWidth - width of the play position bar +sub moveSeekbarThumb(playPositionBarWidth as float) + ' Center the thumb on the play position bar + thumbPostionLeft = playPositionBarWidth - 10 + + ' Don't let thumb go below 0 + if thumbPostionLeft < 0 then thumbPostionLeft = 0 + + ' Don't let thumb go past end of seekbar + if thumbPostionLeft > m.seekBar.width - 25 + thumbPostionLeft = m.seekBar.width - 25 + end if + + m.thumb.translation = [thumbPostionLeft, m.thumb.translation[1]] +end sub + ' Process key press events function onKeyEvent(key as string, press as boolean) as boolean @@ -551,9 +637,55 @@ function onKeyEvent(key as string, press as boolean) as boolean return true end if + ' Key Event handler when m.thumb is in focus + if m.thumb.hasFocus() + if key = "right" + processScrubAction(10) + return true + end if + + if key = "left" + processScrubAction(-10) + return true + end if + + if key = "OK" or key = "play" + if m.inScrubMode + startLoadingSpinner() + m.inScrubMode = false + m.global.audioPlayer.control = "play" + end if + return true + end if + end if + if key = "play" return playAction() - else if key = "back" + end if + + if key = "up" + if not m.thumb.visible + m.thumb.visible = true + setSelectedButtonState("-selected", "-default") + end if + + m.thumb.setFocus(true) + m.buttons.setFocus(false) + return true + end if + + if key = "down" + if m.thumb.visible + m.thumb.visible = false + setSelectedButtonState("-default", "-selected") + end if + + m.buttons.setFocus(true) + m.thumb.setFocus(false) + return true + end if + + if key = "back" m.global.audioPlayer.control = "stop" m.global.audioPlayer.loopMode = "" else if key = "rewind" @@ -561,30 +693,36 @@ function onKeyEvent(key as string, press as boolean) as boolean else if key = "fastforward" return nextClicked() else if key = "left" - if m.global.queueManager.callFunc("getCount") = 1 then return false + if m.buttons.hasFocus() + if m.global.queueManager.callFunc("getCount") = 1 then return false - if m.top.selectedButtonIndex > 0 - m.previouslySelectedButtonIndex = m.top.selectedButtonIndex - m.top.selectedButtonIndex = m.top.selectedButtonIndex - 1 + if m.top.selectedButtonIndex > 0 + m.previouslySelectedButtonIndex = m.top.selectedButtonIndex + m.top.selectedButtonIndex = m.top.selectedButtonIndex - 1 + end if + return true end if - return true else if key = "right" - if m.global.queueManager.callFunc("getCount") = 1 then return false + if m.buttons.hasFocus() + if m.global.queueManager.callFunc("getCount") = 1 then return false - m.previouslySelectedButtonIndex = m.top.selectedButtonIndex - if m.top.selectedButtonIndex < m.buttonCount - 1 then m.top.selectedButtonIndex = m.top.selectedButtonIndex + 1 - return true + m.previouslySelectedButtonIndex = m.top.selectedButtonIndex + if m.top.selectedButtonIndex < m.buttonCount - 1 then m.top.selectedButtonIndex = m.top.selectedButtonIndex + 1 + return true + end if else if key = "OK" - if m.buttons.getChild(m.top.selectedButtonIndex).id = "play" - return playAction() - else if m.buttons.getChild(m.top.selectedButtonIndex).id = "previous" - return previousClicked() - else if m.buttons.getChild(m.top.selectedButtonIndex).id = "next" - return nextClicked() - else if m.buttons.getChild(m.top.selectedButtonIndex).id = "shuffle" - return shuffleClicked() - else if m.buttons.getChild(m.top.selectedButtonIndex).id = "loop" - return loopClicked() + if m.buttons.hasFocus() + if m.buttons.getChild(m.top.selectedButtonIndex).id = "play" + return playAction() + else if m.buttons.getChild(m.top.selectedButtonIndex).id = "previous" + return previousClicked() + else if m.buttons.getChild(m.top.selectedButtonIndex).id = "next" + return nextClicked() + else if m.buttons.getChild(m.top.selectedButtonIndex).id = "shuffle" + return shuffleClicked() + else if m.buttons.getChild(m.top.selectedButtonIndex).id = "loop" + return loopClicked() + end if end if end if end if diff --git a/components/music/AudioPlayerView.xml b/components/music/AudioPlayerView.xml index 3ef06156..aaa4f03c 100644 --- a/components/music/AudioPlayerView.xml +++ b/components/music/AudioPlayerView.xml @@ -4,8 +4,8 @@ -