id3v2mux: write RVA2 frames containing peak/gain volume data
authorJonathan Matthew <jonathan@d14n.org>
Wed, 1 Apr 2009 23:20:02 +0000 (00:20 +0100)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Wed, 1 Apr 2009 23:20:02 +0000 (00:20 +0100)
ext/taglib/gstid3v2mux.cc
tests/check/elements/id3v2mux.c

index fd15836..ac84a53 100644 (file)
@@ -55,6 +55,7 @@
 #include <textidentificationframe.h>
 #include <uniquefileidentifierframe.h>
 #include <attachedpictureframe.h>
+#include <relativevolumeframe.h>
 #include <commentsframe.h>
 #include <unknownframe.h>
 #include <id3v2synchdata.h>
@@ -560,6 +561,71 @@ add_uri_tag (ID3v2::Tag * id3v2tag, const GstTagList * list,
   }
 }
 
+static void
+add_relative_volume_tag (ID3v2::Tag * id3v2tag, const GstTagList * list,
+    const gchar * tag, guint num_tags, const gchar * frame_id)
+{
+  const char *gain_tag_name;
+  const char *peak_tag_name;
+  gdouble peak_val;
+  gdouble gain_val;
+  ID3v2::RelativeVolumeFrame * frame;
+
+  frame = new ID3v2::RelativeVolumeFrame ();
+
+  /* figure out tag names and the identification string to use */
+  if (strcmp (tag, GST_TAG_TRACK_PEAK) == 0 ||
+      strcmp (tag, GST_TAG_TRACK_GAIN) == 0) {
+    gain_tag_name = GST_TAG_TRACK_GAIN;
+    peak_tag_name = GST_TAG_TRACK_PEAK;
+    frame->setIdentification ("track");
+    GST_DEBUG ("adding track relative-volume frame");
+  } else {
+    gain_tag_name = GST_TAG_ALBUM_GAIN;
+    peak_tag_name = GST_TAG_ALBUM_PEAK;
+    frame->setIdentification ("album");
+    GST_DEBUG ("adding album relative-volume frame");
+  }
+  
+  /* find the value for the paired tag (gain, if this is peak, and
+   * vice versa).  if both tags exist, only write the frame when
+   * we're processing the peak tag.
+   */
+  if (strcmp (tag, GST_TAG_TRACK_PEAK) == 0 ||
+      strcmp (tag, GST_TAG_ALBUM_PEAK) == 0) {
+    ID3v2::RelativeVolumeFrame::PeakVolume encoded_peak;
+    short peak_int;
+
+    gst_tag_list_get_double (list, tag, &peak_val);
+
+    if (gst_tag_list_get_tag_size (list, gain_tag_name) > 0) {
+      gst_tag_list_get_double (list, gain_tag_name, &gain_val);
+      GST_DEBUG ("setting volume adjustment %g", gain_val);
+      frame->setVolumeAdjustment (gain_val);
+    }
+
+    /* copying mutagen: always write as 16 bits for sanity. */
+    peak_int = (short)(peak_val * G_MAXSHORT);
+    encoded_peak.bitsRepresentingPeak = 16;
+    encoded_peak.peakVolume = ByteVector::fromShort(peak_int, true);
+    GST_DEBUG ("setting peak value %g", peak_val);
+    frame->setPeakVolume(encoded_peak);
+
+  } else {
+    gst_tag_list_get_double (list, tag, &gain_val);
+    GST_DEBUG ("setting volume adjustment %g", gain_val);
+    frame->setVolumeAdjustment (gain_val);
+
+    if (gst_tag_list_get_tag_size (list, peak_tag_name) != 0) {
+      GST_DEBUG ("both gain and peak tags exist, not adding frame this time around");
+      delete frame;
+      return;
+    }
+  }
+
+  id3v2tag->addFrame (frame);
+}
+
 /* id3demux produces these for frames it cannot parse */
 #define GST_ID3_DEMUX_TAG_ID3V2_FRAME "private-id3v2-frame"
 
@@ -599,7 +665,11 @@ static const struct
   GST_TAG_ENCODER, add_encoder_tag, ""}, {
   GST_TAG_ENCODER_VERSION, add_encoder_tag, ""}, {
   GST_TAG_COPYRIGHT_URI, add_uri_tag, "WCOP"}, {
-  GST_TAG_LICENSE_URI, add_uri_tag, "WCOP"}
+  GST_TAG_LICENSE_URI, add_uri_tag, "WCOP"}, {
+  GST_TAG_TRACK_PEAK, add_relative_volume_tag, ""}, {
+  GST_TAG_TRACK_GAIN, add_relative_volume_tag, ""}, {
+  GST_TAG_ALBUM_PEAK, add_relative_volume_tag, ""}, {
+  GST_TAG_ALBUM_GAIN, add_relative_volume_tag, ""}
 };
 
 
index d824361..6b863f5 100644 (file)
 #define TEST_TRACK_COUNT      19
 #define TEST_VOLUME_NUMBER    2
 #define TEST_VOLUME_COUNT     3
-
-/* #define TEST_TRACK_GAIN      1.45  (not implemented yet) */
-/* #define TEST_ALBUM_GAIN      0.78  (not implemented yet) */
+#define TEST_TRACK_GAIN      1.45
+#define TEST_ALBUM_GAIN      0.78
+#define TEST_TRACK_PEAK      0.83
+#define TEST_ALBUM_PEAK      0.18
 
 /* for dummy mp3 frame sized MP3_FRAME_SIZE bytes,
  * start: ff fb b0 44 00 00 08 00  00 4b 00 00 00 00 00 00 */
@@ -45,6 +46,18 @@ static const guint8 mp3_dummyhdr[] = { 0xff, 0xfb, 0xb0, 0x44, 0x00, 0x00,
 
 #define MP3_FRAME_SIZE 626
 
+/* the peak and gain values are stored pretty roughly, so check that they're
+ * within 2% of the expected value.
+ */
+#define fail_unless_sorta_equals_float(a, b)                           \
+G_STMT_START {                                                         \
+  double first = a;                                                    \
+  double second = b;                                                   \
+  fail_unless(fabs (first - second) < (0.02 * fabs (first)),           \
+      "'" #a "' (%g) is not equal to '" #b "' (%g)", first, second);   \
+} G_STMT_END;
+
+
 static GstTagList *
 test_taglib_id3mux_create_tags (guint32 mask)
 {
@@ -88,12 +101,20 @@ test_taglib_id3mux_create_tags (guint32 mask)
         GST_TAG_ALBUM_VOLUME_COUNT, TEST_VOLUME_COUNT, NULL);
   }
   if (mask & (1 << 8)) {
+    gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+        GST_TAG_TRACK_GAIN, TEST_TRACK_GAIN, NULL);
   }
   if (mask & (1 << 9)) {
+    gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+        GST_TAG_ALBUM_GAIN, TEST_ALBUM_GAIN, NULL);
   }
   if (mask & (1 << 10)) {
+    gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+        GST_TAG_TRACK_PEAK, TEST_TRACK_PEAK, NULL);
   }
   if (mask & (1 << 11)) {
+    gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+        GST_TAG_ALBUM_PEAK, TEST_ALBUM_PEAK, NULL);
   }
   if (mask & (1 << 12)) {
   }
@@ -193,23 +214,29 @@ test_taglib_id3mux_check_tags (GstTagList * tags, guint32 mask)
             &count));
     fail_unless (count == TEST_VOLUME_COUNT);
   }
-#if 0
   if (mask & (1 << 8)) {
     gdouble gain;
 
     fail_unless (gst_tag_list_get_double (tags, GST_TAG_TRACK_GAIN, &gain));
-    fail_unless (gain == TEST_TRACK_GAIN);
+    fail_unless_sorta_equals_float (gain, TEST_TRACK_GAIN);
   }
   if (mask & (1 << 9)) {
     gdouble gain;
 
     fail_unless (gst_tag_list_get_double (tags, GST_TAG_ALBUM_GAIN, &gain));
-    fail_unless (gain == TEST_ALBUM_GAIN);
+    fail_unless_sorta_equals_float (gain, TEST_ALBUM_GAIN);
   }
-#endif
   if (mask & (1 << 10)) {
+    gdouble peak;
+
+    fail_unless (gst_tag_list_get_double (tags, GST_TAG_TRACK_PEAK, &peak));
+    fail_unless_sorta_equals_float (peak, TEST_TRACK_PEAK);
   }
   if (mask & (1 << 11)) {
+    gdouble peak;
+
+    fail_unless (gst_tag_list_get_double (tags, GST_TAG_ALBUM_PEAK, &peak));
+    fail_unless_sorta_equals_float (peak, TEST_ALBUM_PEAK);
   }
   if (mask & (1 << 12)) {
   }
@@ -269,6 +296,7 @@ got_buffer (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
     memcpy (GST_BUFFER_DATA (*p_buf) + off, GST_BUFFER_DATA (buf), size);
   }
 }
+
 static void
 demux_pad_added (GstElement * id3demux, GstPad * srcpad, GstBuffer ** p_outbuf)
 {