SHARE
TWEET

AudioPlayer.qml

a guest Jun 19th, 2014 276 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // -*- qml -*-
  2.  
  3. import QtQuick 2.0
  4. import Sailfish.Silica 1.0
  5. import Sailfish.Silica.theme 1.0
  6. import Sailfish.Media 1.0
  7. import com.jolla.mediaplayer 1.0
  8. import org.nemomobile.policy 1.0
  9.  
  10. DockedPanel {
  11.     id: player
  12.  
  13.     property bool active: audio.model.count > 0
  14.     property bool showAddToPlaylistButton: true
  15.  
  16.     property bool _grabKeys: active && keysResource.acquired
  17.  
  18.     property alias currentItem: audio.currentItem
  19.     property alias duration: audio.duration
  20.     property int position: audio.position + _seekOffset
  21.     property alias state: audio.state
  22.     property alias playModel: audio.model
  23.     readonly property bool playing: audio.state == Audio.Playing || _resume
  24.     readonly property bool seeking: forwardKey.pressed || rewindKey.pressed
  25.     property bool _resume
  26.     property int _seekOffset
  27.  
  28.     function seekForward(time) {
  29.         player._seekOffset += time
  30.         if (player.position > audio.duration) {
  31.             player._seekOffset = audio.duration - audio.position
  32.         }
  33.         // Wired headsets can overload the fast forward key to mean next if held, but
  34.         // bluetooth headsets will manage this themselves, and will auto repeat the key if held.
  35.         // To support the wired headset we restart a timer on each key press and cancel it on
  36.         // release, triggering the next song action on the timer expiring.  If the key auto
  37.         // repeats the restart will prevent the timer expiring and holding will act as a
  38.         // series of successive presses.
  39.         nextTimer.restart()
  40.     }
  41.  
  42.     function seekBackward(time) {
  43.         player._seekOffset -= time
  44.         if (player.position < 0) {
  45.             player._seekOffset = -audio.position
  46.         }
  47.         previousTimer.restart()
  48.     }
  49.  
  50.     width: parent.width
  51.  
  52.     height: column.height + 2*Theme.paddingLarge
  53.     contentHeight: height
  54.     flickableDirection: Flickable.VerticalFlick
  55.  
  56.     visible: active && root.applicationActive
  57.  
  58.     opacity: Qt.inputMethod.visible ? 0.0 : 1.0
  59.     Behavior on opacity { FadeAnimation {}}
  60.  
  61.  
  62.     function playIndex(index) {
  63.         if (!audio.model) {
  64.             return
  65.         }
  66.  
  67.         audio.model.currentIndex = index
  68.         _play()
  69.     }
  70.  
  71.     function play(model, index) {
  72.         audio.setPlayModel(model)
  73.         audio.model.currentIndex = audio.model.shuffledIndex(index)
  74.  
  75.         _play()
  76.     }
  77.  
  78.     function shuffleAndPlay(model, modelSize) {
  79.         audio.setPlayModel(model)
  80.  
  81.         audio.model.currentIndex = Math.floor(Math.random() * modelSize)
  82.         audio.model.shuffle()
  83.         _play()
  84.     }
  85.  
  86.     function addToQueue(mediaOrModel) {
  87.         audio.addToQueue(mediaOrModel)
  88.     }
  89.  
  90.     function removeFromQueue(index) {
  91.         var isPlaying = audio.state == Audio.Playing
  92.         var isVisible = player.visible
  93.  
  94.         if (index >= audio.model.count || index < 0) {
  95.             console.log("Invalid index passed to removeFromQueue()")
  96.             return
  97.         }
  98.  
  99.         // If it's the current item then we try to play the next one:
  100.         if (index == audio.model.currentIndex) {
  101.             audio.playNext()
  102.  
  103.             if (!isPlaying) {
  104.                 player.stop()
  105.             }
  106.         }
  107.  
  108.         // If it's still the currentIndex then we just stop playback.
  109.         if (index == audio.model.currentIndex) {
  110.             audio.model.currentIndex = -1
  111.         }
  112.  
  113.         audio.removeFromQueue(index)
  114.     }
  115.  
  116.     function removeItemFromQueue(mediaItem)
  117.     {
  118.         for (var i = audio.indexOf(mediaItem, 0); i != -1; i = audio.indexOf(mediaItem, i)) {
  119.             removeFromQueue(i)
  120.         }
  121.     }
  122.  
  123.     function playUrl(url) {
  124.         audio.model.clear()
  125.         audio.model.appendUrl(url)
  126.         playIndex(0)
  127.     }
  128.     function toggleOrNext(){
  129.         toggleTimer.start()
  130.         toggle()
  131.     }
  132.  
  133.     function toggle() {
  134.         if (playing) {
  135.             pause()
  136.         } else {
  137.             _play()
  138.         }
  139.     }
  140.  
  141.     function _play() {
  142.         if (seeking) {
  143.             _resume = true
  144.         } else {
  145.             audio.play()
  146.         }
  147.         showControls()
  148.     }
  149.  
  150.     function pause() {
  151.         _resume = false
  152.         audio.pause()
  153.     }
  154.  
  155.     function playNext() {
  156.         audio.playNext()
  157.         showControls()
  158.     }
  159.  
  160.     function showControls() {
  161.         if (playing) {
  162.             open = true
  163.         }
  164.     }
  165.  
  166.     function hideControls() {
  167.         open = false
  168.     }
  169.  
  170.     onOpenChanged: {
  171.         if (!open) {
  172.             audio.pause()
  173.         }
  174.     }
  175.  
  176.     onSeekingChanged: {
  177.         if (seeking) {
  178.             _resume = audio.state == Audio.Playing
  179.             audio.pause()
  180.         } else {
  181.             audio.position += _seekOffset
  182.             _seekOffset = 0
  183.             if (_resume) {
  184.                 _resume = false
  185.                 audio.play()
  186.             }
  187.         }
  188.     }
  189.  
  190.     onPositionChanged: if (!slider.pressed) slider.value = position / 1000
  191.  
  192.     MediaKey { enabled: player._grabKeys; key: Qt.Key_MediaTogglePlayPause; onPressed: player.toggle() }
  193.     MediaKey {
  194.         enabled: player._grabKeys; key: Qt.Key_MediaPlay; onPressed: player._play()
  195.     }
  196.     MediaKey { enabled: player._grabKeys; key: Qt.Key_MediaPause; onPressed: player.pause()}
  197.     MediaKey { enabled: player._grabKeys; key: Qt.Key_MediaStop; onPressed: audio.stop() }
  198.     MediaKey { enabled: player._grabKeys; key: Qt.Key_MediaNext; onPressed: audio.playNext() }
  199.     MediaKey { enabled: player._grabKeys; key: Qt.Key_MediaPrevious; onPressed: audio.playPrevious() }
  200.     MediaKey { enabled: player._grabKeys; key: Qt.Key_ToggleCallHangup; onPressed: player.toggleOrNext(); onReleased: toggleTimer.stop() }
  201.     Timer { id: toggleTimer; interval: 500; onTriggered: audio.playNext()}
  202.     MediaKey {
  203.         id: forwardKey
  204.  
  205.         enabled: player._grabKeys
  206.         key: Qt.Key_AudioForward
  207.         onPressed: player.seekForward(5000)
  208.         onRepeat: player.seekForward(1000)
  209.         onReleased: nextTimer.stop()
  210.     }
  211.     Timer { id: nextTimer; interval: 500; onTriggered: audio.playNext() }
  212.  
  213.     MediaKey {
  214.         id: rewindKey
  215.  
  216.         enabled: player._grabKeys
  217.         key: Qt.Key_AudioRewind
  218.         onPressed: player.seekBackward(5000)
  219.         onRepeat: player.seekBackward(1000)
  220.         onReleased: previousTimer.stop()
  221.     }
  222.     Timer { id: previousTimer; interval: 500; onTriggered: audio.playPrevious() }
  223.  
  224.     Permissions {
  225.         enabled: player.active
  226.         applicationClass: "player"
  227.  
  228.         Resource {
  229.             id: keysResource
  230.             type: Resource.HeadsetButtons
  231.             optional: true
  232.         }
  233.     }
  234.  
  235.     Audio {
  236.         id: audio
  237.         onEndOfMedia: audio.playNext()
  238.         model.currentIndex: audio.model.currentIndex
  239.         model.onShuffledChanged: shuffleSwitch.checked = model.shuffled
  240.  
  241.         model.onCurrentIndexChanged: {
  242.             if (model.currentIndex == -1) {
  243.                 audio.model.currentIndex = -1
  244.             }
  245.         }
  246.  
  247.         onCurrentItemChanged: {
  248.             player._seekOffset = 0
  249.  
  250.             var metadata
  251.             if (currentItem) {
  252.                 metadata = {
  253.                     'title'     : currentItem.title,
  254.                     'artist'    : currentItem.author,
  255.                     'album'     : currentItem.album,
  256.                     'genre'     : "",
  257.                     'track'     : model.currentIndex,
  258.                     'trackCount': model.count,
  259.                     'duration'  : currentItem.duration * 1000
  260.                 }
  261.             }
  262.             bluetoothMediaPlayer.metadata = metadata
  263.         }
  264.  
  265.         onStateChanged: {
  266.             if (state == Audio.Stopped) {
  267.                 player._resume = false
  268.             }
  269.         }
  270.         function playNext() {
  271.             var index = model.currentIndex + 1
  272.             if (index >= model.count) {
  273.                 if (!repeatSwitch.checked) {
  274.                     return
  275.                 }
  276.                 index = 0
  277.             }
  278.  
  279.             model.currentIndex = index
  280.             play()
  281.         }
  282.  
  283.         function playPrevious() {
  284.             var position = audio.position
  285.  
  286.             // We play previous if less than 5 seconds have elapsed.
  287.             // otherwise we rewind the playing song
  288.             if (position >= 5000) {
  289.                 audio.position = 0
  290.                 return
  291.             }
  292.  
  293.             var index = model.currentIndex - 1
  294.             if (index < 0) {
  295.                 return
  296.             }
  297.  
  298.             model.currentIndex = index
  299.             play()
  300.         }
  301.  
  302.         function setShuffle(shuffle) {
  303.             model.shuffled = shuffle
  304.         }
  305.     }
  306.     BluetoothMediaPlayer {
  307.         id: bluetoothMediaPlayer
  308.  
  309.         status: {
  310.             if (audio.state == Audio.Playing) {
  311.                 return BluetoothMediaPlayer.Playing
  312.             } else if (audio.state == Audio.Stopped) {
  313.                 return BluetoothMediaPlayer.Stopped
  314.             } else if (rewindKey.pressed) {
  315.                 return BluetoothMediaPlayer.ReverseSeek
  316.             } else if (forwardKey.pressed) {
  317.                 return BluetoothMediaPlayer.ForwardSeek
  318.             } else {
  319.                 return BluetoothMediaPlayer.Paused
  320.             }
  321.         }
  322.  
  323.         repeat: repeatSwitch.checked
  324.                     ? BluetoothMediaPlayer.RepeatAllTracks
  325.                     : BluetoothMediaPlayer.RepeatOff
  326.  
  327.         shuffle: shuffleSwitch.checked
  328.                     ? BluetoothMediaPlayer.ShuffleAllTracks
  329.                     : BluetoothMediaPlayer.ShuffleOff
  330.  
  331.         position: audio.position
  332.  
  333.         onChangeRepeat: {
  334.             if (repeat == BluetoothMediaPlayer.RepeatOff) {
  335.                 repeatSwitch.checked = false
  336.             } else if (repeat == BluetoothMediaPlayer.RepeatAllTracks) {
  337.                 repeatSwitch.checked = true
  338.             }
  339.         }
  340.  
  341.         onChangeShuffle: {
  342.             if (shuffle == BluetoothMediaPlayer.ShuffleOff) {
  343.                 shuffleSwitch.checked = false
  344.             } else if (shuffle == BluetoothMediaPlayer.ShuffleAllTracks) {
  345.                 shuffleSwitch.checked = true
  346.             }
  347.         }
  348.     }
  349.  
  350.     Column {
  351.         id: column
  352.  
  353.         width: parent.width
  354.         y: Theme.paddingMedium
  355.         spacing: Theme.paddingLarge
  356.  
  357.         Slider {
  358.             id: slider
  359.  
  360.             property string author: currentItem ? currentItem.author : ""
  361.             property string title: currentItem ? currentItem.title : ""
  362.  
  363.             width: parent.width
  364.             handleVisible: false
  365.             valueText: Format.formatDuration(slider.value, slider.value >= 3600
  366.                                              ? Format.DurationLong
  367.                                              : Format.DurationShort)
  368.  
  369.             label: {
  370.                 if (author.length > 0) {
  371.                     if (title.length > 0) {
  372.                         return "%0 - %1".arg(author).arg(title)
  373.                     } else {
  374.                         return author
  375.                     }
  376.                 } else if (title.length > 0) {
  377.                     return title
  378.                 }
  379.                 return ""
  380.             }
  381.  
  382.             minimumValue: 0
  383.             maximumValue: audio.duration / 1000
  384.             onReleased: audio.position = value * 1000
  385.         }
  386.         Row {
  387.             id: navigation
  388.             width: parent.width
  389.  
  390.             IconButton {
  391.                 id: gotoPrevious
  392.                 width: parent.width / 3
  393.                 icon.source: "image://theme/icon-m-previous"
  394.                 anchors.verticalCenter: parent.verticalCenter
  395.                 onClicked: audio.playPrevious()
  396.             }
  397.  
  398.             IconButton {
  399.                 id: playPause
  400.                 width: parent.width / 3
  401.                 icon.source: audio.state == Audio.Playing ? "image://theme/icon-m-pause?" + Theme.highlightColor : "image://theme/icon-m-play"
  402.                 onClicked: audio.toggle()
  403.             }
  404.  
  405.             IconButton {
  406.                 id: gotoNext
  407.                 width: parent.width / 3
  408.                 icon.source: "image://theme/icon-m-next"
  409.                 anchors.verticalCenter: parent.verticalCenter
  410.                 onClicked: audio.playNext()
  411.             }
  412.         }
  413.     }
  414.  
  415.     PushUpMenu {
  416.         id: bottomMenu
  417.  
  418.         Row {
  419.             id: row
  420.             width: parent.width
  421.  
  422.             property real childWidth: width / (showAddToPlaylistButton ? 3 : 2)
  423.  
  424.             Behavior on childWidth {
  425.                 NumberAnimation { duration: 250 }
  426.             }
  427.  
  428.             IconButton {
  429.                 id: addToPlaylistButton
  430.                 width: row.childWidth
  431.                 anchors.bottom: parent.bottom
  432.                 anchors.bottomMargin: -Theme.paddingSmall
  433.                 icon.source: "image://theme/icon-m-add"
  434.                 opacity: showAddToPlaylistButton ? 1.0 : 0
  435.                 visible: opacity != 0
  436.  
  437.                 Behavior on opacity { FadeAnimation {} }
  438.  
  439.                 onClicked: {
  440.                     bottomMenu.hide()
  441.                     pageStack.push(Qt.resolvedUrl("AddToPlaylistPage.qml"), {media: audio.currentItem})
  442.                 }
  443.             }
  444.  
  445.             /*
  446.             TODO: Implement sharing
  447.             IconButton {
  448.                 width: row.childWidth
  449.                 anchors.bottom: parent.bottom
  450.                 icon.source: "image://theme/icon-l-share"
  451.             }
  452.             */
  453.  
  454.             Switch {
  455.                 id: shuffleSwitch
  456.                 width: row.childWidth
  457.                 anchors.bottom: parent.bottom
  458.                 icon.source: "image://theme/icon-m-shuffle"
  459.                 onCheckedChanged: audio.setShuffle(checked)
  460.             }
  461.  
  462.             Switch {
  463.                 id: repeatSwitch
  464.                 width: row.childWidth
  465.                 anchors.bottom: parent.bottom
  466.                 icon.source: "image://theme/icon-m-repeat"
  467.             }
  468.         }
  469.     }
  470. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top