Issue with playing music from Micro SD (Solved)

Ah! Maybe what I need to do is simply do a “private fork” but perhaps a better starting point would be Lollypop, depending on what it’s written in.

For my taste Lollypop is not an option.
It’s missing too many features.

In that case maybe try to contact the people in the Silverjuke project and ask them.

It is done in YAML - which originally stood for Yet Another Markup Language - which I guess says it all.

Really? I know the config files are done in that. But the actual squeekboard executable code that reads the files and puts the keyboard on the screen?

Sorry for late reply i busy too much.

I not remember well about that issues i was troubleshooting that bug long time ago like sd to Librem 5 Multimedia stutering, at the moment i can play .flac or .mkv, .webm well from sd to L5, i using ext4 class 10 sd card 64 Gbs Lollipop + Clapper + Linux 5.18 rc7 sd-power-save enabled plus inactive mode.
Purism it tracking, hunting and killing lot bugs overall on L5. stay relax. :shushing_face:

1 Like

Ah, no. :slight_smile: That is a misunderstanding on my part as to what you were asking.

Well, I downloaded what looks to be the silverjuke code.

According to the readme to build you must first run ./autogen.sh.

That dies with an error that something called rst2man cannot be found, and I can’t install it either; apt can’t find anything by that name.

As usual, open source made undoable by obscurity.

EDIT: Got past that with a google search, but now it can’t find sqlite3 despite my having installed it. The script says to create an environment variable if I installed it in a non-standard place…which, of course, I have no idea where it went, and I have no idea what constitutes a non-standard place.

The usual open-source bullshit. People inflict their peculiar environment on everyone else who wants to build the code.

Lollypop may be missing features, but it does fit on the phone screen. If I can get drill down like I want it and have it not show album art in lists, it probably will do everything I actually want.

Interestingly the very last Samsung music player for android is the closest I’ve come to an ideal phone music player. That, of course, is no longer available, so something called MediaMonkey is tolerable.

My intention was only that someone examine the code to see how it handles managing the reading of sound data from the source and the consumption of sound data by the player.

Building would only be necessary if you can’t understand the source by reading it (entirely possible) or you decide that you want to make a change to the source after understanding it.

Usually that would mean “Is it in PATH?”. In other words, if you type which sqlite3 does it give output? The output tells you “where it went”. That doesn’t as such tell you whether that’s where the script expects it to be but it is a start.

On my computer I get /usr/bin/sqlite3 and typing sqlite3 at the shell prompt correctly runs SQLite.

Yeah, well I tried to download lollypop (be sure not to spell it lollipop) source code, and ran into similar issues…an installed package that couldn’t be found by pkgconfig.

To clarify, sqllite3 is in my path and runs when I type sqlite3. It’s just that stupid retarded pkgconfig can’t find it.

What a damned rodeo.

Maybe it is the .pc files that it is having trouble finding.

Silverjuke on the Librem 5 uses GStreamer as it’s backend to do all the heavy lifting, it essentially goes…

uridecodebin -> audioconvert -> capsfilter -> Silverjuke Signal processing -> audiosink 

I get the same interrupts when invoking GStreamer directly from the terminal in the same manner (minus the additional signal processing that Silverjuke does), i.e…

gst-launch-1.0 uridecodebin uri=file:///full/path/to/audio/track.flac ! audioconvert ! autoaudiosink

However, simply enabling the “use-buffering” option to uridecodebin seems to resolve the issue for me and I was able to playback flac file without interrupts…

gst-launch-1.0 uridecodebin use-buffering=1 uri=file:///full/path/to/audio/track.flac ! audioconvert ! autoaudiosink

Shoehorning this into Silverjuke was a matter of adding the option to the SjGstreamerBackend::CreateStream method in src/sjbase/backend_gstreamer.cpp, specifically, just before the // create pipeline line I added…

g_object_set(G_OBJECT(decodebin), "use-buffering", 1, NULL);

I compiled the source and wrapped it up in a .deb package, after installing, Silverjuke on the Librem 5 can now playback .flac files without interruption for me. I took a 24bit 96KHz flac file and converted it to a .wav and that also played back with no interruptions or other issues.

So, the quick and dirty fix seems to be to enable buffering for uridecodebin.

5 Likes

You need the build headers, not the executables. On a Debian based system, they’ll be called libsqlite3-dev. E.g. on my system, I currently have:

ii  libmono-sqlite4.0-cil                                       6.8.0.105+dfsg-2                                    all          Mono Sqlite library (for CLI 4.0)
ii  libqt5sql5-sqlite:amd64                                     5.12.8+dfsg-0ubuntu2.1                              amd64        Qt 5 SQLite 3 database driver
ii  libsqlite3-0:amd64                                          3.31.1-4ubuntu0.3                                   amd64        SQLite 3 shared library
ii  libsqlite3-0:i386                                           3.31.1-4ubuntu0.3                                   i386         SQLite 3 shared library
ii  libsqlite3-dev:amd64                                        3.31.1-4ubuntu0.3                                   amd64        SQLite 3 development files
ii  lua-sql-sqlite3:amd64                                       2.3.4-1build2                                       amd64        luasql library for the Lua language
ii  sqlite3                                                     3.31.1-4ubuntu0.3                                   amd64        Command line interface for SQLite 3

Same goes for other stuff you might encounter. In Linux, things are usually broken up into:

  • The “end-user” packages which offer the things you need to run it (the executables). In this example, that would be the sqlite3 package.
  • The “library” packages, which contain the libraries, so every application that has this functionality doesn’t need to install its own separate copy. In the example above these are the libsqlite3-0 packages.
  • and the “developer” packages, which contain the library definitions (header files) that you can include in your code if your application needs to be able to use this functionality. In this example: libsqlite3-dev.

In the case of sqlite3, the sqlite3 package contains the sqlite3 and sqlitediff executables and some documentation. The libsqlite3-dev package, on the other hand, contains the sqlite3.h and sqlite3ext.h files that the build requires.

tl;dr: when building stuff, if “foo” is a build dependency, what you need to install is “foo-dev” (or something with “foo” in the name and ending in “-dev” if there is no direct match).

1 Like

Was able to build silverjuke thanks to clues offered by previous replies.

But in Lollypop land, I can’t get past a missing subproject meson.build file.

Although silverjuke is very poorly sized for L5, I can build it and it’s written in a language not named after a reptile (not that I have anything against reptiles), so I ought to be able to make some headway with it.

1 Like

Hi Loki,

When I read your message I jumped for joy. A solution has been found. Great!!!
At first I needed to install gst-launch-1.0 wich isn’t installed by default.
Took me a while to find out in what package it was included.
For those having the same problem: You need to install gstreamer1.0-tools.

After using the 2 commands on a .wav file the outcome was for me disappointing.
The interruptions are still there.

My next test was done with a MP3 file.
The difference between buffering or not using buffering (use-buffering=1) with an MP3 file is the same as you experience with a .flac file.
When using use-buffering=1, the interruptions are gone.
For .wav however this doesn’t make a difference. The interruptions are still there.
Somehow gstreamer has a problem using .wav files (when stored on a slow medium).
I wonder if it is necessary for a music player to use gstreamer as a man in the middle.
Can it not directly communicate to ALSA or PulesAudio?
VLC seems not to need gstreamer.

I’m going to be off line for the next couple of days, so I won’t respond immediately.

And again I need to thank all of you who are participating in this thread and trying to help.
I greatly appreciate it.

It looks like for the .wav file I may have been caught out by filesystem caching so giving the illusion that all was good. Looking at it again, I can see that use-buffering solves the issue for .flac (and .ogg which I tested) but the interrupt issue still remains for .wav files.

Interestingly, although gst-launch-1.0 uridecodebin... interupts with .wav files, playbin seems to handle it fine i.e…

gst-launch-1.0 playbin uri=/full/path/to/audio/track.wav

I haven’t looked too closely at how playbin works, I had always thought it was just a high level ease of use wrapper around the more complex pipeline creation under the hood. It does show that GStreamer can handle .wav files just fine if given the right parameters.

Raising the debug level on uridecodebin I did see a lot of…

pulse pulsesink.c:gst_pulsering_stream_underflow_cb: Got underflow

…warnings being produced when the interrupts occurred. This I would interpret as empty buffers from data being processed faster than it’s coming in (could be wrong though).

I did spin up jumpdrive on the phone, and from my desktop, ran gst-launch-1.0 uridecodebin uri=/full/path/to/audio/via/jumpdrive/track.wav and that ran just fine on my (amd64) desktop with the same GStreamer version. As the card was still in the phone and connected via jumpdrive I was still seeing the ~12MB/s transfer rates, so it does not appear to be a blanket ‘affects all issue’.

I would think it may be easiest to dig into the source of the playbin workings and see how the underlying pipeline is created/defined and then see if that can be merged across to the methods used within Silverjuke. Debug logs of playbin vs uridecode might also be useful.

I’m going to read a bit more about the 2 methods of G-Streamer.
uridecodebin and playbin.
What are the (dis)advantages of choosing one or the other?

Interesting to read is that the problem does not occur when playing music ‘remotely’.
This raises the question again if G-Streamer is causing this issue or is there more than the eye can see?

It needs more investigation, that’s for sure.

playbin and uridecodebin are two different things and are not interchangable. playbin is an “all in one”, essentially a pre-rolled pipeline that’s ready to go. uridecodebin is a discrete single element of a pipeline, the pipeline itself you have to construct by linking the various required elements together.

Personally, I doubt GStreamer is the issue, media players typically have some sort of buffer implementation (many are user configurable), Silverjuke from as much as I have looked at (which isn’t much admittedly) doesn’t have any real sense of buffering, this does not help. I think ultimately the lack of buffering within Silverjuke is most likely the root if the issue and the slow read/access performance of the card/reader within the Librem 5 exacerbates the issue.

I looked into it a little, playbin uses multiple queues (queue, queue2 and multiqueue) at multiple points in it’s pipeline. Most pertinent to Silverjuke is the queue element, this is the preferred when networking is of no concern and it’s also the most lightweight of the choices.

Translating this to gst-launch-1.0 for evaluation you get…

gst-launch-1.0 uridecodebin uri=file:///full/path/to/audio/track.wav ! queue ! audioconvert ! autoaudiosink

This works with uninterrupted playback of the .wav file for me.

However, this applies the defaults for queue which are 200 buffers, 10MB of data or 1 second of data whichever is reached first. I suspect it’ll always be the 1 second of data that is reached first for most audio files. Experimenting a little, it seems that a 1 second buffer is pretty much on the limit for uninterrupted playback so the defaults don’t provide any headroom.

I experimented a little and settled on a 2 second buffer with a 500ms (25%) minimum threshold. Translating this to gst-launch (times are in nanoseconds)…

gst-launch-1.0 uridecodebin uri=file:///full/path/to/audio/track.wav ! queue max-size-buffers=0 max-size-bytes=0 min-threshold-time=500000000 max-size-time=2000000000 silent=1 ! audioconvert ! autoaudiosink

I’d advise experimenting with the numbers to see hat works best for you.

Shoehorning this into Silverjuke is not major surgery but it’s not the simple “use-buffering” one-liner applied previously that addressed the issue for .flac files. Rather than trying to describe the changes I created two patch files that can be applied against the Silverjuke source package from PureOS Byzantium, one to add a queue to the pipeline which will use the defaults, then a second patch file (which must be applied after the first) that sets the queue properties rather than the defaults.

First patch file which I named 0001-add-queue-buffering-to-pipeline.patch

--- a/src/sjbase/backend_gstreamer.cpp	2022-05-24 10:01:53.260018356 +0000
+++ b/src/sjbase/backend_gstreamer.cpp	2022-05-24 10:04:54.058217630 +0000
@@ -322,6 +322,7 @@
 	stream->m_pipeline       = gst_pipeline_new        (                 "sjPlayer"    );
 	GstElement* decodebin    = gst_element_factory_make("uridecodebin",  "sjSource"    );
 	GstElement* audioconvert = gst_element_factory_make("audioconvert",  "sjAudioEntry");
+	GstElement* queue        = gst_element_factory_make("queue",         NULL          );
 	GstElement* capsfilter   = gst_element_factory_make("capsfilter",    NULL          );
 	GstElement* volume       = gst_element_factory_make("volume",        "sjVolume"    );
 	GstElement* audiosink    = gst_parse_bin_from_description(m_iniAudioPipeline, true, &error);
@@ -330,15 +331,15 @@
 		wxLogError("GStreamer Error: %s. Please check the audio configuration at Settings/Advanced.", errormessageWxStr.c_str());
 		g_error_free(error);
 	} // no "return", no "else" - it may be possible, the pipeline is created even on errors, see http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/gstreamer-GstParse.html#gst-parse-launch
-	if( !stream->m_pipeline || !decodebin || !audioconvert || !capsfilter || !volume || !audiosink ) {
+	if( !stream->m_pipeline || !decodebin || !audioconvert || !queue || !capsfilter || !volume || !audiosink ) {
 		wxLogError("GStreamer error: Cannot create objects.");
 		delete stream;
 		return NULL; // error
 	}

 	// create pipeline
-	gst_bin_add_many(GST_BIN(stream->m_pipeline), decodebin, audioconvert, capsfilter, volume, audiosink, NULL); // NULL marks end of list
-	gst_element_link_many(audioconvert, capsfilter, volume, audiosink, NULL);
+	gst_bin_add_many(GST_BIN(stream->m_pipeline), decodebin, audioconvert, queue, capsfilter, volume, audiosink, NULL); // NULL marks end of list
+	gst_element_link_many(audioconvert, queue, capsfilter, volume, audiosink, NULL);
 	g_signal_connect(decodebin, "pad-added", G_CALLBACK(on_pad_added), stream /*userdata*/);

 	// add a message handler

The second to be applied if you wish to use non-default property values, I named this one 0002-set-queue-buffering-properties.patch

--- a/src/sjbase/backend_gstreamer.cpp	2022-05-24 10:06:58.775597134 +0000
+++ b/src/sjbase/backend_gstreamer.cpp	2022-05-24 10:08:03.572877044 +0000
@@ -337,6 +337,13 @@
 		return NULL; // error
 	}

+	// set buffering properties
+	g_object_set(G_OBJECT(queue), "max-size-buffers",   0,          NULL);
+	g_object_set(G_OBJECT(queue), "max-size-bytes",     0,          NULL);
+	g_object_set(G_OBJECT(queue), "min-threshold-time", 500000000,  NULL);
+	g_object_set(G_OBJECT(queue), "max-size-time",      2000000000, NULL);
+	g_object_set(G_OBJECT(queue), "silent",             true,       NULL);
+
 	// create pipeline
 	gst_bin_add_many(GST_BIN(stream->m_pipeline), decodebin, audioconvert, queue, capsfilter, volume, audiosink, NULL); // NULL marks end of list
 	gst_element_link_many(audioconvert, queue, capsfilter, volume, audiosink, NULL);

3 Likes

Using those queue parameters did the trick.
I’m playing .wav files now without any interruptions.
Coool!

As soon as I’ve managed to rebuild Silverjuke on my L5, I will take the dive into the deep and try to implement the provided patches.

2 Likes

And now the conclusion:

With help from Loki I’ve managed to change the code of Silverjuke (actualy: he did the research and wrote the code and I applied the changes).

After building and compiling (again with help from Loki), I’ve now a working version of Silverjuke on the L5.
My .wav files are being played without interruption.

Awesome. :star_struck:

4 Likes