aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--src/decoder/plugins/Mpg123DecoderPlugin.cxx91
2 files changed, 91 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index 40d9fcd7a..7cd991614 100644
--- a/NEWS
+++ b/NEWS
@@ -48,6 +48,7 @@ ver 0.19 (not yet released)
- dsf: support multi-channel files
- dsf: fix big-endian bugs
- dsf: fix noise at end of malformed file
+ - mpg123: support ID3v2, ReplayGain and MixRamp
- sndfile: support scanning remote files
- sndfile: support tags "comment", "album", "track", "genre"
- sndfile: native floating point playback
diff --git a/src/decoder/plugins/Mpg123DecoderPlugin.cxx b/src/decoder/plugins/Mpg123DecoderPlugin.cxx
index 1b28f4282..166529a4d 100644
--- a/src/decoder/plugins/Mpg123DecoderPlugin.cxx
+++ b/src/decoder/plugins/Mpg123DecoderPlugin.cxx
@@ -22,6 +22,9 @@
#include "../DecoderAPI.hxx"
#include "CheckAudioFormat.hxx"
#include "tag/TagHandler.hxx"
+#include "tag/TagBuilder.hxx"
+#include "tag/ReplayGain.hxx"
+#include "tag/MixRamp.hxx"
#include "fs/Path.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
@@ -102,6 +105,90 @@ mpd_mpg123_open(mpg123_handle *handle, const char *path_fs,
}
static void
+AddTagItem(TagBuilder &tag, TagType type, const mpg123_string &s)
+{
+ assert(s.p != nullptr);
+ assert(s.size >= s.fill);
+ assert(s.fill > 0);
+
+ tag.AddItem(type, s.p, s.fill - 1);
+}
+
+static void
+AddTagItem(TagBuilder &tag, TagType type, const mpg123_string *s)
+{
+ if (s != nullptr)
+ AddTagItem(tag, type, *s);
+}
+
+static void
+mpd_mpg123_id3v2_tag(Decoder &decoder, const mpg123_id3v2 &id3v2)
+{
+ TagBuilder tag;
+
+ AddTagItem(tag, TAG_TITLE, id3v2.title);
+ AddTagItem(tag, TAG_ARTIST, id3v2.artist);
+ AddTagItem(tag, TAG_ALBUM, id3v2.album);
+ AddTagItem(tag, TAG_DATE, id3v2.year);
+ AddTagItem(tag, TAG_GENRE, id3v2.genre);
+
+ for (size_t i = 0, n = id3v2.comments; i < n; ++i)
+ AddTagItem(tag, TAG_COMMENT, id3v2.comment_list[i].text);
+
+ decoder_tag(decoder, nullptr, tag.Commit());
+}
+
+static void
+mpd_mpg123_id3v2_extras(Decoder &decoder, const mpg123_id3v2 &id3v2)
+{
+ ReplayGainInfo replay_gain;
+ replay_gain.Clear();
+
+ MixRampInfo mix_ramp;
+
+ bool found_replay_gain = false, found_mixramp = false;
+
+ for (size_t i = 0, n = id3v2.extras; i < n; ++i) {
+ if (ParseReplayGainTag(replay_gain,
+ id3v2.extra[i].description.p,
+ id3v2.extra[i].text.p))
+ found_replay_gain = true;
+ else if (ParseMixRampTag(mix_ramp,
+ id3v2.extra[i].description.p,
+ id3v2.extra[i].text.p))
+ found_mixramp = true;
+ }
+
+ if (found_replay_gain)
+ decoder_replay_gain(decoder, &replay_gain);
+
+ if (found_mixramp)
+ decoder_mixramp(decoder, std::move(mix_ramp));
+}
+
+static void
+mpd_mpg123_id3v2(Decoder &decoder, const mpg123_id3v2 &id3v2)
+{
+ mpd_mpg123_id3v2_tag(decoder, id3v2);
+ mpd_mpg123_id3v2_extras(decoder, id3v2);
+}
+
+static void
+mpd_mpg123_meta(Decoder &decoder, mpg123_handle *const handle)
+{
+ if ((mpg123_meta_check(handle) & MPG123_NEW_ID3) == 0)
+ return;
+
+ mpg123_id3v1 *v1;
+ mpg123_id3v2 *v2;
+ if (mpg123_id3(handle, &v1, &v2) != MPG123_OK)
+ return;
+
+ if (v2 != nullptr)
+ mpd_mpg123_id3v2(decoder, *v2);
+}
+
+static void
mpd_mpg123_file_decode(Decoder &decoder, Path path_fs)
{
/* open the file */
@@ -148,9 +235,11 @@ mpd_mpg123_file_decode(Decoder &decoder, Path path_fs)
}
/* the decoder main loop */
-
DecoderCommand cmd;
do {
+ /* read metadata */
+ mpd_mpg123_meta(decoder, handle);
+
/* decode */
unsigned char buffer[8192];