Merge lp:~ahayzen/music-app/trackqueue-speedup into lp:music-app/trusty

Proposed by Andrew Hayzen on 2014-08-24
Status: Work in progress
Proposed branch: lp:~ahayzen/music-app/trackqueue-speedup
Merge into: lp:music-app/trusty
Diff against target: 557 lines (+159/-62)
7 files modified
LibraryListModel.qml (+16/-2)
MusicNowPlaying.qml (+37/-12)
MusicSearch.qml (+2/-2)
MusicToolbar.qml (+15/-15)
Player.qml (+14/-14)
common/ListItemActions/AddToQueue.qml (+1/-1)
music-app.qml (+74/-16)
To merge this branch: bzr merge lp:~ahayzen/music-app/trackqueue-speedup
Reviewer Review Type Date Requested Status
Ubuntu Phone Apps Jenkins Bot continuous-integration Needs Fixing on 2014-08-24
Music App Developers 2014-08-24 Pending
Review via email: mp+232021@code.launchpad.net

Commit message

* Use worker loaded trackQueueUI infront of trackQueue

Description of the change

* Use worker loaded trackQueueUI infront of trackQueue

BEFORE
* Go to a listview with many items (eg Songs tab) scroll to the bottom
* Select a track near the bottom
* Notice the UI freezes and there is noticeable lag until audio starts playing

AFTER
* Go to a listview with many items (eg Songs tab) scroll to the bottom
* Select a track near the bottom
* Notice the audio starts much faster and the UI is loaded as the data appears from the worker script, making it 'feel' more responsive

Note I had 300+ tracks in the Songs tab and this branch had a very positive affect on the performance, but it is also noticeable when selecting the last track in an album of 10+ tracks.

The performance increase is done by having two models for the trackQueue, one to hold the data that is always 'insync' (required for calculations of eg nextSong() etc) and another that is for the UI that loads items via the worker script as they appear into the first.

To post a comment you must log in.
review: Needs Fixing (continuous-integration)
566. By Andrew Hayzen on 2014-08-24

* Merge of trunk

Victor Thompson (vthompson) wrote :

I'm not sure this is working the way you intended it to. It doesn't really seem to speed things up consistently. I tried it once and it seemed to maybe be half a second faster, but it delayed jumping to the now playing item. Then I tried it again and recorded it. The N4 is running this branch and the N7 is running trunk. Note that the N4 is actually started first by a bit, and note how large the delay is until the now playing item is shown.

http://youtu.be/43IZ7jI5QbY

Victor Thompson (vthompson) wrote :

Also note that this is with 841 tracks on each device.

Andrew Hayzen (ahayzen) wrote :

I would expect the UI to be slower in certain situations, but I would expect the time until the audio starts to be faster. I'll see if I can tweak the UI loading to speed it up.

Unmerged revisions

566. By Andrew Hayzen on 2014-08-24

* Merge of trunk

565. By Andrew Hayzen on 2014-08-24

* Use worker loaded trackQueueUI infront of trackQueue

564. By Andrew Hayzen on 2014-08-12

* Disable trackQueue from now playing page

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'LibraryListModel.qml'
2--- LibraryListModel.qml 2014-08-20 17:35:52 +0000
3+++ LibraryListModel.qml 2014-08-24 23:27:56 +0000
4@@ -34,6 +34,10 @@
5 property bool canLoad: true
6 property bool preLoadComplete: false
7
8+ property alias onlyAppend: worker.onlyAppend
9+ property alias list: worker.list
10+ property alias workerCompleted: worker.completed
11+
12 onCanLoadChanged: {
13 /* If canLoad has been set back to true then check if there are any
14 remaining items to load in the model */
15@@ -58,9 +62,19 @@
16 property int i: 0
17 property var list: null
18
19+ property bool onlyAppend: false
20+
21 onListChanged: {
22- reset();
23- clear();
24+ if (onlyAppend) {
25+ if (canLoad) {
26+ process()
27+ }
28+
29+ onlyAppend = false
30+ } else {
31+ reset();
32+ clear();
33+ }
34 }
35
36 onMessage: {
37
38=== modified file 'MusicNowPlaying.qml'
39--- MusicNowPlaying.qml 2014-08-20 17:35:52 +0000
40+++ MusicNowPlaying.qml 2014-08-24 23:27:56 +0000
41@@ -55,8 +55,6 @@
42 return;
43 }
44
45- queuelist.currentIndex = player.currentIndex;
46-
47 customdebug("MusicQueue update currentIndex: " + player.source);
48
49 // Always jump to current track
50@@ -70,14 +68,24 @@
51 // If the toolbar is shown, the page is now playing and snaptrack is enabled
52 if (shown && currentPage === nowPlaying && Settings.getSetting("snaptrack") === "1")
53 {
54- // Then position the view at the current index
55- queuelist.positionViewAtIndex(queuelist.currentIndex, ListView.Beginning);
56+
57+ // Only jump if the item has been created otherwise jump on load
58+ if (queuelist.count >= player.currentIndex && trackQueueUI.workerCompleted) {
59+ // Then position the view at the current index
60+ queuelist.positionViewAtIndex(player.currentIndex, ListView.Beginning);
61+ } else {
62+ queuelist.jumpOnLoad = player.currentIndex
63+ }
64 }
65 }
66
67 function positionAt(index) {
68- queuelist.positionViewAtIndex(index, ListView.Beginning);
69- queuelist.contentY -= header.height;
70+ if (queuelist.count >= index && trackQueueUI.workerCompleted) {
71+ queuelist.positionViewAtIndex(index, ListView.Beginning);
72+ queuelist.contentY -= header.height;
73+ } else {
74+ queuelist.jumpOnLoad = index
75+ }
76 }
77
78 ListView {
79@@ -86,7 +94,7 @@
80 anchors.fill: parent
81 anchors.bottomMargin: musicToolbar.mouseAreaOffset + musicToolbar.minimizedHeight
82 delegate: queueDelegate
83- model: trackQueue.model
84+ model: trackQueueUI.model
85 highlightFollowsCurrentItem: false
86 state: "normal"
87 states: [
88@@ -113,21 +121,39 @@
89 property int currentHeight: units.gu(40)
90 property int transitionDuration: 250 // transition length of animations
91
92+ property int jumpOnLoad: -1
93+
94 onCountChanged: {
95- customdebug("Queue: Now has: " + queuelist.count + " tracks")
96+ customdebug("Queue: Now has: " + queuelist.count + " tracks" + " " + jumpOnLoad)
97+
98+ if (jumpOnLoad < count && jumpOnLoad !== -1) { // < due to count being +1 to index
99+ positionAtAfterDelay.start()
100+ }
101 }
102
103 onMovementStarted: {
104 musicToolbar.hideToolbar();
105 }
106
107+ Timer {
108+ id: positionAtAfterDelay
109+ interval: 250
110+ repeat: false
111+ onTriggered: {
112+ // Then position the view at the current index
113+ queuelist.positionViewAtIndex(queuelist.jumpOnLoad, ListView.Beginning);
114+
115+ queuelist.jumpOnLoad = -1
116+ }
117+ }
118+
119 Component {
120 id: queueDelegate
121 ListItemWithActions {
122 id: queueListItem
123 color: "transparent"
124 height: queuelist.normalHeight
125- state: queuelist.currentIndex == index && !reordering ? "current" : ""
126+ state: player.currentIndex == index && !reordering ? "current" : ""
127
128 leftSideAction: Remove {
129 onTriggered: {
130@@ -143,7 +169,7 @@
131 player.currentIndex -= 1;
132 }
133
134- queuelist.model.remove(index);
135+ trackQueueUI.remove(index);
136 }
137 }
138 reorderable: true
139@@ -161,8 +187,7 @@
140 onReorder: {
141 console.debug("Move: ", from, to);
142
143- queuelist.model.move(from, to, 1);
144-
145+ trackQueueUI.move(from, to, 1);
146
147 // Maintain currentIndex with current song
148 if (from === player.currentIndex) {
149
150=== modified file 'MusicSearch.qml'
151--- MusicSearch.qml 2014-08-21 19:32:12 +0000
152+++ MusicSearch.qml 2014-08-24 23:27:56 +0000
153@@ -173,8 +173,8 @@
154 onItemClicked: {
155 console.debug("Debug: "+title+" added to queue")
156 // now play this track, but keep current queue
157- trackQueue.append(model)
158- trackQueueClick(trackQueue.model.count - 1);
159+ trackQueue.appendItem(makeDict(model))
160+ trackQueueClick(trackQueue.count - 1);
161 onDoneClicked: PopupUtils.close(searchTrack)
162 }
163
164
165=== modified file 'MusicToolbar.qml'
166--- MusicToolbar.qml 2014-08-21 19:32:12 +0000
167+++ MusicToolbar.qml 2014-08-24 23:27:56 +0000
168@@ -223,7 +223,7 @@
169
170 /* Clicking in the area shows the queue */
171 function trigger() {
172- if (trackQueue.model.count !== 0 && currentPage !== nowPlaying) {
173+ if (trackQueue.count !== 0 && currentPage !== nowPlaying) {
174 tabs.pushNowPlaying();
175 }
176 else if (currentPage === nowPlaying) {
177@@ -244,7 +244,7 @@
178 elide: Text.ElideRight
179 fontSize: "medium"
180 objectName: "playercontroltitle"
181- text: trackQueue.model.count === 0 ? "" : player.currentMetaTitle === "" ? player.currentMetaFile : player.currentMetaTitle
182+ text: trackQueue.count === 0 ? "" : player.currentMetaTitle === "" ? player.currentMetaFile : player.currentMetaTitle
183 }
184
185 /* Artist of track */
186@@ -259,7 +259,7 @@
187 color: styleMusic.playerControls.labelColor
188 elide: Text.ElideRight
189 fontSize: "small"
190- text: trackQueue.model.count === 0 ? "" : player.currentMetaArtist
191+ text: trackQueue.count === 0 ? "" : player.currentMetaArtist
192 }
193
194 /* Album of track */
195@@ -274,7 +274,7 @@
196 color: styleMusic.playerControls.labelColor
197 elide: Text.ElideRight
198 fontSize: "small"
199- text: trackQueue.model.count === 0 ? "" : player.currentMetaAlbum
200+ text: trackQueue.count === 0 ? "" : player.currentMetaAlbum
201 }
202 }
203
204@@ -318,11 +318,11 @@
205 anchors.verticalCenter: parent.verticalCenter
206 height: units.gu(6)
207 objectName: "previousshape"
208- opacity: trackQueue.model.count === 0 ? .4 : 1
209+ opacity: trackQueue.count === 0 ? .4 : 1
210 width: height
211
212 function trigger() {
213- if (trackQueue.model.count === 0) {
214+ if (trackQueue.count === 0) {
215 return;
216 }
217
218@@ -409,7 +409,7 @@
219 return;
220 }
221
222- if (trackQueue.model.count === 0) {
223+ if (trackQueue.count === 0) {
224 playRandomSong();
225 }
226 else {
227@@ -442,11 +442,11 @@
228 anchors.verticalCenter: parent.verticalCenter
229 height: units.gu(6)
230 objectName: "forwardshape"
231- opacity: trackQueue.model.count === 0 ? .4 : 1
232+ opacity: trackQueue.count === 0 ? .4 : 1
233 width: height
234
235 function trigger() {
236- if (trackQueue.model.count === 0 || emptyPage.noMusic) {
237+ if (trackQueue.count === 0 || emptyPage.noMusic) {
238 return;
239 }
240
241@@ -569,7 +569,7 @@
242 anchors.verticalCenter: parent.verticalCenter
243 color: "transparent"
244 height: units.gu(1);
245- state: trackQueue.model.count === 0 ? "disabled" : "enabled"
246+ state: trackQueue.count === 0 ? "disabled" : "enabled"
247
248 states: [
249 State {
250@@ -716,7 +716,7 @@
251 id: musicToolbarPlayerControls
252 anchors.fill: parent
253 color: styleMusic.playerControls.backgroundColor
254- state: trackQueue.model.count === 0 ? "disabled" : "enabled"
255+ state: trackQueue.count === 0 ? "disabled" : "enabled"
256 states: [
257 State {
258 name: "disabled"
259@@ -746,7 +746,7 @@
260 id: disabledPlayerControlsGroup
261 anchors.fill: parent
262 color: "transparent"
263- visible: trackQueue.model.count === 0
264+ visible: trackQueue.count === 0
265
266 Label {
267 id: noSongsInQueueLabel
268@@ -779,7 +779,7 @@
269 return;
270 }
271
272- if (trackQueue.model.count === 0) {
273+ if (trackQueue.count === 0) {
274 playRandomSong();
275 }
276 else {
277@@ -861,7 +861,7 @@
278 id: enabledPlayerControlsGroup
279 anchors.fill: parent
280 color: "transparent"
281- visible: trackQueue.model.count !== 0
282+ visible: trackQueue.count !== 0
283
284 /* Settings button */
285 // TODO: Enable settings when it is practical
286@@ -911,7 +911,7 @@
287 return;
288 }
289
290- if (trackQueue.model.count === 0) {
291+ if (trackQueue.count === 0) {
292 playRandomSong();
293 }
294 else {
295
296=== modified file 'Player.qml'
297--- Player.qml 2014-08-22 00:26:30 +0000
298+++ Player.qml 2014-08-24 23:27:56 +0000
299@@ -59,12 +59,12 @@
300 }
301
302 Connections {
303- target: trackQueue.model
304+ target: trackQueue
305 onCountChanged: {
306- if (trackQueue.model.count === 1) {
307+ if (trackQueue.count === 1) {
308 player.currentIndex = 0;
309- player.source = Qt.resolvedUrl(trackQueue.model.get(0).filename)
310- } else if (trackQueue.model.count === 0) {
311+ player.source = Qt.resolvedUrl(trackQueue.get(0).filename)
312+ } else if (trackQueue.count === 0) {
313 player.currentMetaFile = ""
314 player.source = ""
315 }
316@@ -79,7 +79,7 @@
317 return;
318 }
319
320- if (trackQueue.model.count == 0)
321+ if (trackQueue.count == 0)
322 {
323 customdebug("No tracks in queue.");
324 return;
325@@ -91,26 +91,26 @@
326 var newIndex;
327
328 console.log("currentIndex: " + currentIndex)
329- console.log("trackQueue.count: " + trackQueue.model.count)
330+ console.log("trackQueue.count: " + trackQueue.count)
331
332 // Do not shuffle if repeat is off and there is only one track in the queue
333- if (shuffle && !(trackQueue.model.count === 1 && !repeat)) {
334+ if (shuffle && !(trackQueue.count === 1 && !repeat)) {
335 var now = new Date();
336 var seed = now.getSeconds();
337
338 // trackQueue must be above 1 otherwise an infinite loop will occur
339 do {
340- newIndex = (Math.floor((trackQueue.model.count)
341+ newIndex = (Math.floor((trackQueue.count)
342 * Math.random(seed)));
343- } while (newIndex === currentIndex && trackQueue.model.count > 1)
344+ } while (newIndex === currentIndex && trackQueue.count > 1)
345 } else {
346- if ((currentIndex < trackQueue.model.count - 1 && direction === 1 )
347+ if ((currentIndex < trackQueue.count - 1 && direction === 1 )
348 || (currentIndex > 0 && direction === -1)) {
349 newIndex = currentIndex + direction
350 } else if(direction === 1 && (repeat || fromControls)) {
351 newIndex = 0
352 } else if(direction === -1 && (repeat || fromControls)) {
353- newIndex = trackQueue.model.count - 1
354+ newIndex = trackQueue.count - 1
355 }
356 else
357 {
358@@ -120,11 +120,11 @@
359 }
360
361 if (startPlaying) { // only start the track if told
362- playSong(trackQueue.model.get(newIndex).filename, newIndex)
363+ playSong(trackQueue.get(newIndex).filename, newIndex)
364 }
365 else {
366 currentIndex = newIndex
367- source = Qt.resolvedUrl(trackQueue.model.get(newIndex).filename)
368+ source = Qt.resolvedUrl(trackQueue.get(newIndex).filename)
369 }
370 }
371
372@@ -184,7 +184,7 @@
373 player.stop()
374 }
375 else {
376- var obj = trackQueue.model.get(player.currentIndex);
377+ var obj = trackQueue.get(player.currentIndex);
378 currentMetaAlbum = obj.album;
379 currentMetaArtist = obj.author;
380 currentMetaFile = obj.filename;
381
382=== modified file 'common/ListItemActions/AddToQueue.qml'
383--- common/ListItemActions/AddToQueue.qml 2014-08-20 17:35:52 +0000
384+++ common/ListItemActions/AddToQueue.qml 2014-08-24 23:27:56 +0000
385@@ -26,6 +26,6 @@
386
387 onTriggered: {
388 console.debug("Debug: Add track to queue: " + model)
389- trackQueue.append(model)
390+ trackQueue.appendItem(makeDict(model))
391 }
392 }
393
394=== modified file 'music-app.qml'
395--- music-app.qml 2014-08-22 00:26:30 +0000
396+++ music-app.qml 2014-08-24 23:27:56 +0000
397@@ -236,15 +236,15 @@
398
399 if (play) {
400 // clear play queue
401- trackQueue.model.clear()
402+ trackQueue.clearItems()
403 }
404
405 // enqueue
406- trackQueue.append(makeDict(track));
407+ trackQueue.appendItem(makeDict(track));
408
409 // play first URI
410 if (play) {
411- trackQueueClick(trackQueue.model.count - 1);
412+ trackQueueClick(trackQueue.count - 1);
413 }
414 }
415
416@@ -394,7 +394,7 @@
417 else {
418 stopTimer();
419
420- trackQueue.model.clear();
421+ trackQueue.clearItems();
422
423 trackQueue.append(makeDict(model));
424 trackQueueClick(0);
425@@ -637,9 +637,13 @@
426 model = model.linkLibraryListModel;
427 }
428
429+ var list = []
430+
431 for (var i=0; i < model.rowCount; i++) {
432- trackQueue.model.append(makeDict(model.get(i, model.RoleModelData)));
433+ list.push(makeDict(model.get(i, model.RoleModelData)));
434 }
435+
436+ trackQueue.appendList(list)
437 }
438
439 // Converts an duration in ms to a formated string ("minutes:seconds")
440@@ -679,14 +683,14 @@
441 if (!clear) {
442 // If same track and on now playing page then toggle
443 if (musicToolbar.currentPage === nowPlaying &&
444- trackQueue.model.get(player.currentIndex) !== undefined &&
445- Qt.resolvedUrl(trackQueue.model.get(player.currentIndex).filename) === file) {
446+ trackQueue.get(player.currentIndex) !== undefined &&
447+ Qt.resolvedUrl(trackQueue.get(player.currentIndex).filename) === file) {
448 player.toggle()
449 return;
450 }
451 }
452
453- trackQueue.model.clear(); // clear the old model
454+ trackQueue.clearItems(); // clear the old model
455
456 addQueueFromModel(model);
457
458@@ -709,7 +713,7 @@
459 player.toggle();
460 }
461 else {
462- player.playSong(trackQueue.model.get(index).filename, index);
463+ player.playSong(trackQueue.get(index).filename, index);
464 }
465
466 // Show the Now Playing page and make sure the track is visible
467@@ -721,7 +725,7 @@
468
469 function playRandomSong(shuffle)
470 {
471- trackQueue.model.clear();
472+ trackQueue.clearItems();
473
474 var now = new Date();
475 var seed = now.getSeconds();
476@@ -853,13 +857,67 @@
477 }
478
479 // list of tracks on startup. This is just during development
480- LibraryListModel {
481+ ListModel {
482 id: trackQueue
483
484- function append(listElement)
485- {
486- model.append(makeDict(listElement))
487- console.debug(JSON.stringify(makeDict(listElement)));
488+ /* Pretent to be like a mediascanner2 listmodel */
489+ property alias rowCount: trackQueue.count
490+
491+ function appendItem(item) {
492+ append(item)
493+
494+ trackQueueUI.liveList.push(item)
495+ trackQueueUI.onlyAppend = true
496+ trackQueueUI.list = trackQueueUI.liveList
497+ }
498+
499+ function appendList(list)
500+ {
501+ for (var i=0; i < list.length; i++) {
502+ append(list[i])
503+ }
504+
505+ trackQueueUI.liveList = list
506+ trackQueueUI.list = list
507+
508+ console.debug(JSON.stringify(list));
509+ }
510+
511+ function clearItems()
512+ {
513+ clear()
514+
515+ trackQueueUI.clear()
516+ trackQueueUI.liveList = []
517+ trackQueueUI.list = []
518+ }
519+ }
520+
521+ LibraryListModel {
522+ id: trackQueueUI
523+ canLoad: true
524+
525+ property var liveList: null
526+
527+ function move(from, to, count)
528+ {
529+ trackQueue.move(from, to, count)
530+ model.move(from, to, count)
531+
532+ // list.splice(to, 0, *list.splice(from, count))
533+
534+ var args = [to, 0];
535+ args.push.apply(args, liveList.splice(from, count))
536+
537+ liveList.splice.apply(liveList, args)
538+ }
539+
540+ function remove(index)
541+ {
542+ trackQueue.remove(index)
543+ model.remove(index)
544+
545+ liveList.splice(index, 1)
546 }
547 }
548
549@@ -910,7 +968,7 @@
550 onClicked: {
551 console.debug("Debug: Add track to queue: " + JSON.stringify(chosenElement))
552 PopupUtils.close(trackPopover)
553- trackQueue.append(chosenElement)
554+ trackQueue.appendItem(makeDict(chosenElement))
555 }
556 }
557 ListItem.Standard {

Subscribers

People subscribed via source and target branches

to status/vote changes: