Posts Tagged ‘Amarok’

MPRIS2 Support in NowPlaying

10th November 2011

Do you recall NowPlaying?  The dataengine/widget pair for Plasma that tells you what your media player is currently playing, and allows you to control it?

The Now Playing widget (with an old theme)

Well, now it supports MPRIS2.  What does this mean for you?

Well, probably not much right now.  Juk doesn’t support MPRIS2 (although I intend to change that for 4.9/5.0), Amarok worked before (although it should use marginally less power with MPRIS2 rather than the old MPRIS interface), VLC doesn’t support MPRIS2 (yet; version 1.2 will).  A handful of other players support MPRIS2, though, including the Raven Music Server.

The main thing, though, is that support for MPRIS2 is increasing in media players, partly because of Ubuntu’s adoption of it as the mechanism for its sound menu to talk to media players.  Spotify now supports MPRIS2, for example.  And now the Now Playing widget can support them.

MPRIS2 has many advantages over the original MPRIS specification, not least of which is not having to query the media player every second for up-to-date position information.  As a result, the nowplaying dataengine will prefer the MPRIS2 interface to the MPRIS interface for a media player that offers both.

The only quirk to be aware of is that Amarok’s MPRIS2 support isn’t quite right in the 2.4.x series, and this will affect a couple of features of the Now Playing widget (seeking and enabling/disabling of the next/previous buttons); this shouldn’t be an issue though, as these problems are fixed for Amarok 2.5, which will be released before KDE Plasma Workspace 4.8.

Akuadec

9th July 2009

Another day of the Gran Canaria Desktop Summit. After yesterday’s post, I got several offers of power adaptors. However, I ended up buying one on my way back to the hotel, along with a bunch of food for lunch today (the lunches here at the university are poor and quite expensive for what they are).

In fact, speaking of food, I had the worst meal I’ve had this week, and one of the worst I’ve had in my life, last night. We went to a Chinese all-you-can-eat buffet. Now, now, don’t scoff. I’m had some good (although never excellent) food at all-you-can-eat buffets. It seems that the Canary Islands is not the place for them, though. This one was so bad that after my first plateful, I was still hungry but didn’t want to eat any more. Avoid like the plague.

This was in direct contrast to the meal I had with the Amarok guys the night before, which was really nice (if expensive) – masses of paella, salad, chips and warm chocolate brownie. It was seriously good.

Today we had more Amarok discussions. So far we’ve discussed:

  • liblastfm on Windows
  • Playlist synchronisation
  • Unit testing
  • Whether scripts should be able to respond to Amarok quitting (and potentially slow it down)
  • A UPnP collection
  • Reorganising the source files
  • Playdar
  • The UI for dynamic playlists, and context-sensitive information for the area on the left of Amarok (where collections and services etc. are)
  • The EngineController class
  • Mac/Windows ports

Still to come:

  • Media devices update
  • The evil “organise files” bug (pro tip: don’t use Amarok 2 to organise your files for now)
  • UI clutter

We’ve had a very productive couple of days, and Leo’s been taking photos of the whiteboard, so we have a permanent record of our discussions.  Expect blog posts about the cooler things we discussed.

Guademy

8th July 2009

It’s been a while since I posted. Such is life.

I’m really enjoying my first Akademy. I managed to forget a power adapter to convert between my British plug and the European sockets, but Nuno lent me one for a few days. I now have to find my own, though, as he went home today.

So, the conference. The keynotes on Saturday morning were really good. I recommend watching them when the videos are online (which they may be already). Of course, there is some furore over Richard Stallman’s talk, but I think that was always expected.

Yesterday was the KDE e.v. meeting, which I didn’t go to (not being a member of the e.v.). Instead, I slept in (sleep has been in short supply), went to the beach, and hacked. I put together a small plasmoid (87 lines of javascript) to show some controls for a media player. The idea is that you can put this on an auto-hiding panel on your desktop so that they don’t take up screen real estate, but are easily accessibly just by moving your mouse. The other half of the job, of course, is to do one that displays information. Then you can have the information about what song is currently playing permanently visible, and the controls only appear when you want them.

Today we had a very productive discussion on moving KDE to Git. The aim (if everything goes swimmingly) seems to be to move before KDE 4.4 goes into freeze. Amarok is intending to move very soon, though.

Speaking of Amarok, we’ve been sitting in one of the labs discussing things since the Git BoF. It’s amazing how much gets decided how quickly, especially when you’re used to deciding things on mailing lists. This is particularly true for user interface decisions. I’m expecting great things from the next release of Amarok.

What’s Playing, Doc?

17th May 2009

So, I was round at a friend’s, and he was playing with Amarok and the Plasma widgets screensaver, and wanted a way to see what was currently playing when his computer was locked.  Of course, I pointed him towards the Now Playing applet, but it was overkill for what he wanted – he didn’t want the buttons, or the sliders – just a display of the current information.  Well, I was in the mood for playing, and it took me about 15 minutes to construct a javascript plasmoid, most of which was spent researching how to use data engines from them.

The code I left him with was:

layout = new LinearLayout(plasmoid);
layout.setOrientation(QtVertical);

label = new Label();
layout.addItem(label);
label.text = 'No player'

plasmoid.dataUpdate = function(name, data) {
    label.text = 'Info:\n';
    for (var key in data) {
        label.text += key + ': ' + data[key] + '\n';
    }
}

plasmoid.dataEngine("nowplaying").connectSource("org.mpris.amarok", plasmoid, 500);

This was in a file called “main.js” in a subfolder called “contents”. In the plasmoid folder (the folder containing the contents folder), I also put in a metadata.desktop file:


[Desktop Entry]
Name=Simple Now Playing
Comment=Now Playing as Matt likes it
Icon=applications-multimedia
Type=Service
X-Plasma-API=javascript
X-Plasma-MainScript=main.js
X-Plasma-DefaultSize=200,100
X-KDE-ServiceTypes=Plasma/Applet
X-KDE-PluginInfo-Author=Alex Merry
X-KDE-PluginInfo-Email=alex.merry [SPAMNO]@[SPAMNO] kdemail.org
X-KDE-PluginInfo-Name=simplenowplaying
X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Website=http://plasma.kde.org/
X-KDE-PluginInfo-Category=Multimedia
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true

Now it was just a case of calling plasmapkg -i . from within the plasmoid directory, and the simple widget was installed. This produces the following:

simple-now-playing

Of course, this will only work with Amarok, and doesn’t respond to Amarok being started or quit. That’s OK, it’s not much more work to deal with that:


layout = new LinearLayout(plasmoid);
layout.setOrientation(QtVertical);

label = new Label();
layout.addItem(label);
label.text = "No player found";

function firstSource() {
	var sources = plasmoid.dataEngine("nowplaying").sources;
	if (sources.length) {
		return sources[0];
	} else {
		label.text = "No player found";
		return '';
	}
}

plasmoid.dataUpdate = function(name, data) {
	if (source == name) {
		label.text = 'Info:\n';
		for (var key in data) {
			label.text += key + ': ' + data[key] + '\n';	
		}
	}
}

source = firstSource();

npDataEngine = plasmoid.dataEngine("nowplaying");

npDataEngine.sourceRemoved.connect(function(name) {
	if (name == source) {
		source = firstSource();
		if (source) {
			npDataEngine.connect(source, plasmoid, 500);
		}
	}
});

npDataEngine.sourceAdded.connect(function(name) {
	if (!source) {
		source = name;
		npDataEngine.connect(source, plasmoid, 500);
	}
});

if (source) {
	npDataEngine.connectSource(source, plasmoid, 500);
}

Of course, it’s not much work to only show the things you’re interested in, but this covers the interesting parts of the plasmoid.

Interesting note: it appears that if you replace the line label.text = 'Info:\n'; with label.text = '';, the subsequent calls to label.text += ... don’t work – you just end up with a blank label.

Running out of original titles on the subject of replay gain

31st January 2009

Now that Amarok can read replay gain tags from almost all the files that it can read metadata from, I feel replay gain support in Amarok is pretty much there.  And, yes, all this will be in Amarok 2.1.

A small caveat: the reading of replay gain tags from MP4 files only works if Amarok was built against libMP4v2.  Amarok uses its own (home-brewed?) code for reading MP4 tags when it can’t find libMP4v2 at build-time, but it doesn’t support freeform tags and I don’t see the point of implementing that support when libMP4v2 does the job.

Amarok 2.1 will support all the file formats that Amarok 1.4′s replay gain script did (and more), apart from Musepack (mpc).  Ironically, this is because of Musepack’s native support for replay gain – rather than abusing metadata tags to store the replay gain information, Musepack has a special field in the file header to specify the values.  However, TagLib doesn’t let us at this field, so we’re a bit stuck.

So the state of replay gain support in Amarok is that, for files in the main collection or elsewhere on your computer’s filesystem (so not streaming media and not portable music players), replay gain tags (both album and track mode) will be read from the following formats:

  • MP3 (as written by mp3gain/aacgain, Foobar2000 and Mutagen/Quod Libet – yes, there are three different ways of writing the tags to MP3 files)
  • OGG Vorbis (as written by vorbisgain and anything compatible)
  • OGG FLAC (assuming the tags are stored in the same way as in OGG Vorbis)
  • OGG Speex (assuming the tags are stored in the same way as in OGG Vorbis)
  • FLAC (as written by flac [with it's --replay-gain switch] and anything compatible)
  • WMA/ASF (as written by the Amarok 1.4 replay gain script)
  • MP4 (as written by aacgain and anything compatible, providing Amarok was built with libMP4v2)
  • WavPack (assuming the tags are stored in a similar way to how mp3gain stores them in MP3 files)
  • TrueAudio (assuming the tags are stored in a similar way to how Foobar2000 or QuodLibet/Mutagen store the tags in MP3 files)

In addition, Amarok will use the track mode tags (or the album mode ones if there aren’t any track mode tags) to adjust the gain during playback.  I haven’t added an option to switch to album mode (or even disable it, if you really want to) yet, but that will come.  I just have to figure out where it should go in the user interface…

Of course, this is only half the functionality provided by the Amarok 1.4 replay gain script.  That also parsed and tagged files in the playlist.  This could be implemented nicely as a script (I certainly don’t believe it belongs in the core of Amarok).  But that’s for another day, and probably another person.

Replay This

18th January 2009

So, after some trial-and-error, I’ve cracked the RVA2 frame format as written by Quod Libet / Mutagen.

  • All values are stored big-endian (most significant byte first).  This tripped me up, because this is not true for all data stored in all tag formats.
  • If we think of the peak value in terms of the “units, tens, thousands” format you learned at school (only, of course, in binary this is “units, twos, fours”), the first bit is the units, the second is the halves, the third quarters and so on.  Simple, yes?  However, it’s not so easy to infer from the code that parses it.  So I’ve put plenty of comments in the amarokcollectionscanner code.
  • Everything else you could wish to know about the format is in the ID3v2.4 frames list, section 4.11.

So Amarok’s collection now has complete support for replay gain tags in Ogg Vorbis (as written by vorbisgain), FLAC (as written by flacenc) and MP3 (as written by Foobar2000 [ID3v2.3 TXXX comments], Quod Libet / Mutagen [ID3v2.4 RVA2] or mp3gain [APEv2]).

Other formats will follow.  As will replay gain support for files that aren’t in the collection, but are on the local machine.

Reverse Engineering Datatypes

17th January 2009

Amarok now reads the tags written by Foobar2000 and by mp3gain (written when you call mp3gain without the -a or -r options) from MP3 files.  However, the final part of MP3 support is tricker: the RVA2 tag in the ID3v2.4 spec.

Naturally, the specification leaves out an all-important detail: the format of the peak volume field.  It tells you which bits represent the peak volume, but not how to interpret them.

Luckily, mutagen, a Python audio metadata library, supports this tag, so it’s implementation can serve as a reference.  However, they try to be clever with their implementation, so reverse-engineering it to arrive at the format of the original data requires some work.

The documentation on the Python class implementing the RVA2 frame support says that the peak volume is a float between 0 and 1.  So 0 is silent, 1 is full volume (digital full scale).  This doesn’t seem right to me, because the replay gain specification points out that it is possible to have a peak volume over 1 in some circumstances in a compressed audio file.  But we’ll leave that aside for the moment.

Let’s start with the code.  data contains the raw bytes, the first of which is a number specifying how many bits (not bytes) of the remaining data is occupied by the number representing the peak volume.

        peak = 0
        bits = ord(data[0])
        bytes = min(4, (bits + 7) >> 3)
        # not enough frame data
        if bytes + 1 > len(data): raise ID3JunkFrameError
        shift = ((8 - (bits & 7)) & 7) + (4 - bytes) * 8
        for i in range(1, bytes+1):
            peak *= 256
            peak += ord(data[i])
        peak *= 2**shift
        return (float(peak) / (2**31-1))

Let’s start with bytes.  This is simply bits (the number of bits representing the peak volume) rounded up to the nearest 8, then divided by 8.  So if bits is 8n + k, bytes is n in the case that k = 0 and (n+1) in the case that k > 0.

The next variable is the shift.  This is the first bit of clever magic, and it takes some time spent staring at it (preferably with a pad and paper to hand) to arrive at the following conclusion:

  • if k = 0, shift is 8(4 – n)
  • if k > 0, shift is 8(4 – (n + 1))

Then we read the bits into peak.  Remember that if k > 0, the last (8 – k) bits will be junk.  Now we shift it right (shift is always at least 0, because of our contraint on bytes to be at least 4) so that the first 32 bits are all that remains (I assume here that Python is treating peak as an integer).  Then we turn peak into a float and divide it by (231) – 1.  This contant is a magic number, being the largest value that can be stored in a signed 32-bit integer.

Something that might shed light on this is that, when it writes the peak volume out, it simply writes the value multiplied by 215 as a 16-bit unsigned integer.  This would make interpreting the value as simple as placing a “decimal” point after the first binary digit (so we get 1 digit before the point and 15 after).  Note that this does indeed allow a peak volume greater than 1 (but less than 2).

I’m left with two questions:

  1. Why do we divide the number by MAX_INT_32, rather than simply 231? (I just made up that constant name now, don’t complain that it’s wrong.)
  2. Why does mutagen put a 32-bit minimum on the number, and then write a 16-bit number when it writes out RVA2 tags?

Answers on a postcard (or just in the comments).

Gaining Gain for Personal Gain

15th January 2009

SVN commit 911684 by alexmerry:

ReplayGain FTW!

Make replay gain support actually do something by
(a) getting the data we stored out of the collection database
(b) using it when the track changes

Also, improve the storage of replay gain tags by storing NULL when they weren’t present on the original track metadata. This allows us to substitute the track gain for the album gain when the latter is requested but doesn’t exist.

This closes the most popular feature request on bugzilla.

Of course, there’s still work to be done.

  • Currently, it’s fixed in track mode.
  • It only works on files in the collection – it should be possible to extend it to other files on the local computer at least.
  • Finally, it only works on Ogg Vorbis and FLAC files (mp3gain and aacgain modify MP3/MP4 files in such a way that we don’t need to do anything special in Amarok for them to sound right, providing you pass either the “-r” or “-a” option). Adding other file types is simply a case of adding the relevant code to amarokcollectionscanner.

But, right now, I’m listening to my music all playing at the right volume (apart from the WMA files, but what can you do?).

[edit] I should point out that it works without moving your volume slider up and down like the Amarok 1.4 script did.  It just works magically and invisibly, like it should.  This part of the implementation (actually changing the volume) was a doddle, thanks to the wonder of Phonon[/edit]

The Gains of Replay Gain

11th January 2009

So… no-one responded with anything helpful on my last post.  There was a Dot article about the semantic desktop (and hence Nepomuk), however.

On to more interesting things.  I’ve got fed up recently with changing volumes for different songs.  I actually notice it more on my portable MP3 player, but Amarok 2 has long bugged me by not having any Replay Gain support.  And I’ve been thinking about implementing it for about the same amount of time.  Yesterday, I finally got around to starting it.

The first hurdle I came up against was how to get the information I needed from the files.  If you run mp3gain or aacgain on your MP3 or MP4 files, or if you have MusePack files, there’s no problem since the information about changing the volume is stored in such a way that it is interpreted by the decoder, and so Amarok doesn’t need to do anything.  But with other file formats, and when you use other tools for calculating the gain for MP3 and MP4 files, the information is stored as tags.  Fine, we can read tags.  We can store the information we read in the database, and then access it later.  But we’re left with the question of what tags we’re looking for.

The official Replay Gain standard is incredibly unhelpful in this regard.  It gives you all sorts of details about the mathematics behind calculating how much gain to apply to a track, but almost nothing about how to store or retreive that value.  On top of that, it is woefully out of date, not having been updated since 2001.

OK, so over to the Replay Gain page on the slightly more active Hydrogenaudio Knowledgebase wiki.  However, which tag format (ID3v2 or APE, say) the relevant tags are stored in by various tools and for various formats is about as detailed as it gets.  None of the web pages for the various tools are any more helpful.

The vorbisgain man page is more forthcoming: the relevant tags are REPLAYGAIN_TRACK_GAIN, REPLAYGAIN_TRACK_PEAK, REPLAYGAIN_ALBUM_GAIN and REPLAYGAIN_ALBUM_PEAK. The REPLAYGAIN_*_GAIN values are in decibels and have ” dB” appended to the end. The REPLAYGAIN_*_PEAK values are floats (no further information given).

So I turned to the old Amarok 1.4 Replay Gain script.  This is what I found:

  • Vorbis and FLAC works as described above. To get the peak gain relative to the adjusted gain, you apparently need to take the log of the float, base 10, and multiply the result by 20.
  • MP3s can have the data stored in one of several ways (apart from the method detailed above, which we don’t need to concern ourselves with):
    • In the APEv2 tags in the same manner as with Vorbis (possibly not all uppercase)
    • In the ID3v2 TXXX (user comment) tags in the same manner as Vorbis (possibly not all uppercase) – the TXXX frames have a description field and value field, which are used for the tag name and the tag value. Total of 4 TXXX tags to store all the values. These are written by Foobar2000.
    • In the ID3v2.4 RVA2 tags (only Quod Libet does this AFAIK). Each tag has an identification string, into which Quod Libet writes the values “album” and “track”. The format of the rest of the frame is specified in section 4.11 of the ID3v2.4 frames specification.
  • MP4s are done in the same way as Vorbis.
  • AAC (not in an MP4 container): APEv2, presumeably in the same manner as MP3 APE tags (the ReplayGain script doesn’t support AAC files).
  • MusePack: values are stored in the header in fields designated for the purpose. Should be supported natively by the decoder, according to the Hydrogenaudio Knowledgebase, but the 1.4 ReplayGain script stil reads these values. The peak value apparently needs to be multiplied by 2 to get the peak gain relative to the adjusted gain.
  • WMA: same as Vorbis, stored in the ASF tags (this may well be only files tagged by the ReplayGain script itself).
  • WavPack: stored in APEv2 tags, presumeably in the same manner as MP3 APE tags (the ReplayGain script doesn’t support WavPack files).
  • MOD files: Foobar2000 stores these in APEv2 format, presumeably in the same manner as MP3 APE tags (the ReplayGain script doesn’t support MOD files).

Don’t you feel all enlightened now?


Follow

Get every new post delivered to your Inbox.