Skip to content

Stellody v0.4.0

Choose a tag to compare

@oernster oernster released this 30 Aug 16:44
· 56 commits to main since this release

Release notes

The cover chooser a listener can see

Right clicking an album offers "Find cover art online...", which opens a grid
of every picture MusicBrainz and the Cover Art Archive hold for it. Picking one
keeps it. That is the whole of the outward reach: a window is given the service
or it is not, so one built without it carries no such entry at all.

It opens on a wait rather than on a result, because there is one. The archive
allows a request a second and a release has to be asked about before there is a
picture, so the dialog says what it is doing, then draws each tile as its own
thumbnail lands rather than making you wait for the last of them.

Closing it lets go. A request already sent runs to its own timeout, since
nothing can reach into a socket and stop it; what closing promises is narrower
and is the part that matters: whatever comes back afterwards is dropped rather
than drawn into a window that has moved on. A test holds a search open, closes
it, releases it and watches nothing arrive.

A picture that is kept is kept the way a local cover is, against the album's
identity rather than a path, so it survives a folder rename. It is marked as
chosen, which means it is checked against no file and outlives a rescan. As
ever, nothing is written into the music folder.

A refused archive is not an album without art

The chooser came back empty for everything. The archive was not saying the
album has no pictures; it was declining to answer at all, so a decline was
being read as an answer.

Measured: MusicBrainz refused 6 of 10 asks for one release at the one request a
second its own terms ask for; two asks five seconds apart were refused while a
third was answered. Three different user agents made no difference and the Cover
Art Archive answered 4 of 4 in the same minute, so it is that one host
declining a share of requests rather than the network, the rate or this
machine.

So a refused search is now asked again, up to five times with a growing pause
between them. Three live searches after the change came back with 10, 31 and 10
pictures, in 9 to 15 seconds each.

What still will not answer is said plainly. A search that is refused every time
now says the archive would not answer and to try again in a moment, rather than
reporting that nothing came back for the album. The two are different claims
and only one of them was ever true.

The chooser shows that it is working

A search takes 10 to 15 seconds against the real archive, which is long enough
that a dialog saying so in words alone reads as one that has hung. There is now
an indicator under the message, following the same pattern the scan uses: it
runs indeterminate while the releases are being looked for, since there is
nothing to count yet, then becomes a real fraction of the pictures as their
thumbnails arrive.

The button beside it says Cancel for exactly as long as there is something to
cancel, then Close. Pressing it does the same thing either way; only the word
changes, so nothing is ever left unable to be stopped. Keeping a chosen picture
is a fetch of its own, so it draws its own wait too.

The picture you picked says so

A picked tile now wears a ring, in the green the rest of the application rings a
chosen thing with. The tint behind the label was doing that job alone and was
too quiet to read against a wall of sleeves. The colour is stated once in the
palette rather than painted in the dialog; the ring is scoped to the chooser,
since the library's own views are choosing nothing.

Picking a picture no longer washes its colour out. Qt draws a selected icon in
its own Selected mode, so the one tile being compared against the others was
the one that could no longer be seen properly; measured, a picked tile rendered
grey where its picture was brown. The same picture is now registered for every
mode, which leaves the ring to say what is picked.

A track shows its shape before it plays

The waveform used to be measured just after a press, so a track nobody had
played yet drew a flat line: nothing at all about the track somebody was
deciding whether to play. With nothing loaded, the bar now follows the
highlight and draws that track's shape.

What is loaded still wins. The line crossing the shape belongs to the track
that is playing, so browsing the library during playback leaves the picture
under it alone; the highlight only supplies the shape when there is no playhead
to contradict.

Measuring starts the moment the highlight lands on a track. It was made to
settle for a fraction of a second first, on the reasoning that a decode per row
passed over would be wasteful; measured, that reasoning had the wrong culprit.
The cost was not the decodes: it was letting go of one, which blocked the whole
interface for two seconds a step, because asking a thread to quit does nothing
to a decode already running.

Both halves are now fixed where they belong. A measurement is told to give up
and stops at the next block it reads, keeping nothing, since half a file is not
a shape. Letting go of one never waits. Measured on the reference library:
stepping through rows mid decode cost 2.00 seconds a step before and 0.2
milliseconds after; shutting down mid decode now takes 0.01 seconds. A
track already measured costs no decode at all and comes back in 0.6
milliseconds, which is the ordinary case for anything played before.

The shape half of the window moved into a module of its own while this was
done, along the seam between what the transport buttons do and which track the
bar is drawing.

The shape draws itself as it is read

A track that had never been played still showed a flat line for a second or
more, a cue-sheet album for a good deal longer, because the whole file had
to be read before there was anything to draw. Now the reading offers what it
has every five seconds of the music and the picture builds from the left.

Measured cold, from the disk rather than from a cache: an ordinary 28 megabyte
track puts its first picture up in 0.02 seconds and finishes in 0.49; a 272
megabyte album FLAC puts one up in 0.01 and fills in over 8.17. A track visited
again is instant, since the finished measurement is kept.

The reading itself also got four times faster, with the same answer bit for
bit. Finding which bucket a frame belonged to was being done one frame at a
time in Python, which turned out to be most of the time rather than the
decoding: a 60 megabyte track folded in 3.2 seconds that way and 0.84 done in
one pass by numpy, while a 390 megabyte album FLAC went from 21.8 seconds to
5.3.

Only the finished measurement is kept. A part of one written down would be
wrong on every redraw afterwards without ever looking wrong enough for anybody
to notice.

A chosen cover comes back after a restart

A cover picked from the chooser was kept correctly and drawn at once, then the
album showed a placeholder again on the next start. The picture was on disk
the whole time; nothing ever asked for it.

Drawing an album asks for its cover. That ask used to answer nothing
without consulting the store at all whenever the album had no file beside the
music and no picture inside the audio. It was a saving: opening nothing is
cheaper than asking a decoder to open nothing. An album with nowhere local to
look is the only kind the chooser is ever offered for, though; a chosen
picture has no file beside the music to be found by, so the saving cost every
chosen cover its next restart.

The store is now asked whatever the candidates say, since it is the thing that
knows whether a cover is kept. The property that saving read was removed with
it, because leaving it there invites the saving back.