[SCM] bs1770gain/master: Imported Upstream version 0.4.6~beta2

pere at users.alioth.debian.org pere at users.alioth.debian.org
Sun Sep 13 21:44:35 UTC 2015


The following commit has been merged in the master branch:
commit 0789de89c070fc5a3237553d16c51890bb879bf8
Author: Petter Reinholdtsen <pere at hungry.com>
Date:   Thu Sep 10 19:18:26 2015 +0200

    Imported Upstream version 0.4.6~beta2

diff --git a/bs1770gain/Makefile.am b/bs1770gain/Makefile.am
index a91ec5d..3323821 100755
--- a/bs1770gain/Makefile.am
+++ b/bs1770gain/Makefile.am
@@ -7,9 +7,10 @@ CFLAGS+=-Wcast-qual
 CFLAGS+=-Wreturn-type
 CFLAGS+=-Wextra
 CFLAGS+=-Wstrict-prototypes
+CFLAGS+=-Wno-unused-parameter
 #CFLAGS+=-Wmissing-declarations
 #CFLAGS+=-Wmissing-prototypes
-CFLAGS+=-Wno-unused-variable
+#CFLAGS+=-Wno-unused-variable
 
 CPPFLAGS+=-I$(top_srcdir)/libpbutil
 CPPFLAGS+=-I$(top_srcdir)/lib1770-2
@@ -56,6 +57,8 @@ noinst_LIBRARIES=libbs1770gain.a
 libbs1770gain_a_SOURCES=
 libbs1770gain_a_SOURCES+=bs1770gain.h
 libbs1770gain_a_SOURCES+=bs1770gain_priv.h
+libbs1770gain_a_SOURCES+=bs1770gain_print_xml.c
+libbs1770gain_a_SOURCES+=bs1770gain_print_classic.c
 libbs1770gain_a_SOURCES+=bs1770gain_aggregate.c
 libbs1770gain_a_SOURCES+=bs1770gain_album.c
 libbs1770gain_a_SOURCES+=bs1770gain_opath.c
diff --git a/bs1770gain/Makefile.in b/bs1770gain/Makefile.in
index a743543..f4cb73c 100755
--- a/bs1770gain/Makefile.in
+++ b/bs1770gain/Makefile.in
@@ -66,10 +66,11 @@ AR = ar
 ARFLAGS = cru
 libbs1770gain_a_AR = $(AR) $(ARFLAGS)
 libbs1770gain_a_LIBADD =
-am_libbs1770gain_a_OBJECTS = bs1770gain_aggregate.$(OBJEXT) \
-	bs1770gain_album.$(OBJEXT) bs1770gain_opath.$(OBJEXT) \
-	bs1770gain_parse_time.$(OBJEXT) bs1770gain_transcode.$(OBJEXT) \
-	bs1770gain_tree.$(OBJEXT)
+am_libbs1770gain_a_OBJECTS = bs1770gain_print_xml.$(OBJEXT) \
+	bs1770gain_print_classic.$(OBJEXT) \
+	bs1770gain_aggregate.$(OBJEXT) bs1770gain_album.$(OBJEXT) \
+	bs1770gain_opath.$(OBJEXT) bs1770gain_parse_time.$(OBJEXT) \
+	bs1770gain_transcode.$(OBJEXT) bs1770gain_tree.$(OBJEXT)
 libbs1770gain_a_OBJECTS = $(am_libbs1770gain_a_OBJECTS)
 am__installdirs = "$(DESTDIR)$(bindir)"
 PROGRAMS = $(bin_PROGRAMS)
@@ -109,12 +110,13 @@ BS1770GAIN_TOOLS_INSTALL = @BS1770GAIN_TOOLS_INSTALL@
 BS1770GAIN_TOOLS_UNINSTALL = @BS1770GAIN_TOOLS_UNINSTALL@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
-#CFLAGS+=-Wmissing-declarations
-#CFLAGS+=-Wmissing-prototypes
 CFLAGS = @CFLAGS@ -Werror -Wall -Wformat -Wpointer-arith -Wcast-align \
 	-Wcast-qual -Wreturn-type -Wextra -Wstrict-prototypes \
-	-Wno-unused-variable
+	-Wno-unused-parameter
 CPP = @CPP@
+#CFLAGS+=-Wmissing-declarations
+#CFLAGS+=-Wmissing-prototypes
+#CFLAGS+=-Wno-unused-variable
 CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir)/libpbutil \
 	-I$(top_srcdir)/lib1770-2 -I$(top_srcdir)/libffsox-2
 CYGPATH_W = @CYGPATH_W@
@@ -218,6 +220,7 @@ bs1770gain_LDADD = libbs1770gain.a \
 #bs1770hello_LDADD+=-lm
 noinst_LIBRARIES = libbs1770gain.a
 libbs1770gain_a_SOURCES = bs1770gain.h bs1770gain_priv.h \
+	bs1770gain_print_xml.c bs1770gain_print_classic.c \
 	bs1770gain_aggregate.c bs1770gain_album.c bs1770gain_opath.c \
 	bs1770gain_parse_time.c bs1770gain_transcode.c \
 	bs1770gain_tree.c
@@ -318,6 +321,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/bs1770gain_album.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/bs1770gain_opath.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/bs1770gain_parse_time.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/bs1770gain_print_classic.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/bs1770gain_print_xml.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/bs1770gain_transcode.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/bs1770gain_tree.Po at am__quote@
 
diff --git a/bs1770gain/bs1770gain.c b/bs1770gain/bs1770gain.c
index 1bfda59..afc4272 100755
--- a/bs1770gain/bs1770gain.c
+++ b/bs1770gain/bs1770gain.c
@@ -25,23 +25,23 @@
 #define CLOCKS_PER_MILLIS (CLOCKS_PER_SEC/1000)
 
 #define BS1770GAIN_AGGREGATE_METHOD ( \
-  AGGREGATE_MOMENTARY_MAXIMUM \
-  &AGGREGATE_MOMENTARY_MEAN \
-  &AGGREGATE_SHORTTERM_MAXIMUM \
-  &AGGREGATE_SHORTTERM_MEAN \
+  FFSOX_AGGREGATE_MOMENTARY_MAXIMUM \
+  &FFSOX_AGGREGATE_MOMENTARY_MEAN \
+  &FFSOX_AGGREGATE_SHORTTERM_MAXIMUM \
+  &FFSOX_AGGREGATE_SHORTTERM_MEAN \
 )
 
 #define BS1770GAIN_AGGREGATE_RANGE_PEAK ( \
-  AGGREGATE_MOMENTARY_RANGE \
-  &AGGREGATE_SHORTTERM_RANGE \
-  &AGGREGATE_SAMPLEPEAK \
-  &AGGREGATE_TRUEPEAK \
+  FFSOX_AGGREGATE_MOMENTARY_RANGE \
+  &FFSOX_AGGREGATE_SHORTTERM_RANGE \
+  &FFSOX_AGGREGATE_SAMPLEPEAK \
+  &FFSOX_AGGREGATE_TRUEPEAK \
 )
 
 #define BS1770GAIN_OPTIONS_NO_METHOD(o) \
-  (0==((o)->flags&BS1770GAIN_AGGREGATE_METHOD))
+  (0==((o)->aggregate&BS1770GAIN_AGGREGATE_METHOD))
 #define BS1770GAIN_OPTIONS_NO_RANGE_PEAK(o) \
-  (0==((o)->flags&BS1770GAIN_AGGREGATE_RANGE_PEAK))
+  (0==((o)->aggregate&BS1770GAIN_AGGREGATE_RANGE_PEAK))
 
 ///////////////////////////////////////////////////////////////////////////////
 void bs1770gain_usage(char **argv, int code)
@@ -87,12 +87,14 @@ void bs1770gain_usage(char **argv, int code)
       "   or apply the EBU/ATSC/RG gain, respectively,\n"
       "   and output to folder\n");
   fprintf(stderr," -f <file>,--file <file>:  write analysis to file\n");
-  fprintf(stderr," -x,--extensions:  enable following extensions\n"
+  fprintf(stderr," -x,--extensions:  enable following extensions/defaults:\n"
       "   1) rename files according to TITLE tag\n"
       "   2) read metadata from per-folder CSV file \"folder.csv\"\n"
       "   3) copy file \"folder.jpg\" from source to destination\n"
       "      folder\n"
-      "   4) automatically add the TRACK and DISC tags\n");
+      "   4) automatically add the TRACK and DISC tags\n"
+      "   5) calculate maximum true peak\n"
+      "   6) convert to stereo\n");
   fprintf(stderr," -l,--list:  print FFmpeg format/stream information\n");
   /////////////////////////////////////////////////////////////////////////////
   fprintf(stderr," --ebu:  calculate replay gain according to EBU R128\n"
@@ -103,6 +105,15 @@ void bs1770gain_usage(char **argv, int code)
       "   ReplayGain 2.0 (-18.0 LUFS)\n");
   fprintf(stderr," --track-tags:  write track tags\n");
   fprintf(stderr," --album-tags:  write album tags\n");
+#if defined (BS1770GAIN_TAG_PREFIX) // {
+#if 0 // {
+  fprintf(stderr," --tag-prefix <prefix>:  use <prefix> as tag prefix\n"
+      "   instead of \"REPLAYGAIN\"\n");
+#else // } {
+  fprintf(stderr," --tag-prefix <prefix>:  instead of \"REPLAYGAIN\",\n"
+      "   use <prefix> as RG tag prefix\n");
+#endif // }
+#endif // }
   fprintf(stderr," --unit <unit>:  write tags with <unit>\n");
   fprintf(stderr," --apply <q>:  apply the EBU/ATSC/RG gain to the output\n"
       "   (in conjunction with the -o/--output option),\n"
@@ -112,6 +123,7 @@ void bs1770gain_usage(char **argv, int code)
       "   [0:<index>] in FFmpeg listing, cf. -l/--list option)\n");
   fprintf(stderr," --video <index>:  select video index (corresponds to\n"
       "   [0:<index>] in FFmpeg listing, cf. -l/--list option)\n");
+  fprintf(stderr," --stereo:  convert to stereo\n");
   fprintf(stderr," --drc <drc>:  set AC3 dynamic range compression (DRC)\n");
   fprintf(stderr," --extension <extension>:  enable extension out of\n"
       "   \"rename\":  rename files according to TITLE tag\n"
@@ -123,10 +135,11 @@ void bs1770gain_usage(char **argv, int code)
   fprintf(stderr," --loglevel <level>:  set loglevel,\n"
       "   level:  \"quiet\", \"panic\", \"fatal\", \"error\", \"warning\",\n"
       "   \"info\", \"verbose\", \"debug\"\n");
+  fprintf(stderr," --xml:  print results in xml format\n");
   fprintf(stderr," --time:  print out duration of program invocation\n");
   //fprintf(stderr," --level:\n");
   //fprintf(stderr," --preamp <preamp>:\n");
-  //fprintf(stderr," --mono2stero:\n");
+  //fprintf(stderr," --stero:\n");
   //fprintf(stderr," --rg-tags:\n");
   //fprintf(stderr," --bwf-tags:\n");
   /////////////////////////////////////////////////////////////////////////////
@@ -195,12 +208,16 @@ enum {
   ATSC,
   AUDIO,
   VIDEO,
-  MONO2STEREO,
+  STEREO,
   REPLAYGAIN,
   RG_TAGS,
   BWF_TAGS,
   TRACK_TAGS,
   ALBUM_TAGS,
+  XML,
+#if defined (BS1770GAIN_TAG_PREFIX) // {
+  TAG_PREFIX,
+#endif // }
   ////////////////
   MOMENTARY_RANGE,
   MOMENTARY_LENGTH,
@@ -252,12 +269,16 @@ static struct option bs1770gain_opts[]={
   { "time",no_argument,NULL,TIME },
   { "ebu",no_argument,NULL,EBU },
   { "atsc",no_argument,NULL,ATSC },
-  { "mono2stereo",no_argument,NULL,MONO2STEREO },
+  { "stereo",no_argument,NULL,STEREO },
   { "replaygain",no_argument,NULL,REPLAYGAIN },
   { "rg-tags",no_argument,NULL,RG_TAGS },
   { "bwf-tags",no_argument,NULL,BWF_TAGS },
   { "track-tags",no_argument,NULL,TRACK_TAGS },
   { "album-tags",no_argument,NULL,ALBUM_TAGS },
+  { "xml",no_argument,NULL,XML },
+#if defined (BS1770GAIN_TAG_PREFIX) // {
+  { "tag-prefix",required_argument,NULL,TAG_PREFIX },
+#endif // }
   ////
   { "momentary-mean",no_argument,NULL,'i' },
   { "momentary-maximum",no_argument,NULL,'m' },
@@ -300,12 +321,13 @@ static struct option bs1770gain_opts[]={
 int main(int argc, char **argv)
 {
   options_t options;
+  FILE *f=stdout;
   tree_t root;
   char *fpath=NULL;
   char *odirname=NULL;
   int loglevel=AV_LOG_QUIET;
   double overlap;
-  clock_t t1,t2;
+  double t1,t2;
   int c;
 
   if (1==argc)
@@ -315,13 +337,15 @@ int main(int argc, char **argv)
 
   //setlocale(LC_ALL,"C");
   memset(&options,0,sizeof options);
-  options.f=stdout;
   options.unit="LU";
   options.audio=-1;
   options.video=-1;
   options.level=-23.0;
   options.audio_ext="flac";
   options.video_ext="mkv";
+#if defined (BS1770GAIN_TAG_PREFIX) // {
+  options.tag_prefix="REPLAYGAIN";
+#endif // }
 
   options.momentary.ms=400.0;
   options.momentary.partition=4;
@@ -363,21 +387,21 @@ int main(int argc, char **argv)
     case 'u':
       if (0==strcasecmp("integrated",optarg)
           ||0==strcasecmp("momentary-mean",optarg)) {
-        options.flags|=AGGREGATE_MOMENTARY_MEAN;
+        options.aggregate|=FFSOX_AGGREGATE_MOMENTARY_MEAN;
         options.method=BS1770GAIN_METHOD_MOMENTARY_MEAN;
       }
       else if (0==strcasecmp("momentary",optarg)
           ||0==strcasecmp("momentary-maximum",optarg)) {
-        options.flags|=AGGREGATE_MOMENTARY_MAXIMUM;
+        options.aggregate|=FFSOX_AGGREGATE_MOMENTARY_MAXIMUM;
         options.method=BS1770GAIN_METHOD_MOMENTARY_MAXIMUM;
       }
       else if (0==strcasecmp("shortterm-mean",optarg)) {
-        options.flags|=AGGREGATE_SHORTTERM_MEAN;
+        options.aggregate|=FFSOX_AGGREGATE_SHORTTERM_MEAN;
         options.method=BS1770GAIN_METHOD_SHORTTERM_MEAN;
       }
       else if (0==strcasecmp("shortterm",optarg)
           ||0==strcasecmp("shortterm-maximum",optarg)) {
-        options.flags|=AGGREGATE_SHORTTERM_MAXIMUM;
+        options.aggregate|=FFSOX_AGGREGATE_SHORTTERM_MAXIMUM;
         options.method=BS1770GAIN_METHOD_SHORTTERM_MAXIMUM;
       }
       else {
@@ -399,25 +423,27 @@ int main(int argc, char **argv)
       options.dump=1;
       break;
     case 'i':
-      options.flags|=AGGREGATE_MOMENTARY_MEAN;
+      options.aggregate|=FFSOX_AGGREGATE_MOMENTARY_MEAN;
       break;
     case 's':
-      options.flags|=AGGREGATE_SHORTTERM_MAXIMUM;
+      options.aggregate|=FFSOX_AGGREGATE_SHORTTERM_MAXIMUM;
       break;
     case 'm':
-      options.flags|=AGGREGATE_MOMENTARY_MAXIMUM;
+      options.aggregate|=FFSOX_AGGREGATE_MOMENTARY_MAXIMUM;
       break;
     case 'r':
-      options.flags|=AGGREGATE_SHORTTERM_RANGE;
+      options.aggregate|=FFSOX_AGGREGATE_SHORTTERM_RANGE;
       break;
     case 'p':
-      options.flags|=AGGREGATE_SAMPLEPEAK;
+      options.aggregate|=FFSOX_AGGREGATE_SAMPLEPEAK;
       break;
     case 't':
-      options.flags|=AGGREGATE_TRUEPEAK;
+      options.aggregate|=FFSOX_AGGREGATE_TRUEPEAK;
       break;
     case 'x':
       options.extensions=BS1770GAIN_EXTENSION_ALL;
+      options.aggregate|=FFSOX_AGGREGATE_TRUEPEAK;
+      options.stereo=1;
       break;
     /// without flag //////////////////////////////////////////////////////////
     case AUDIO:
@@ -448,6 +474,11 @@ int main(int argc, char **argv)
       else
         bs1770gain_usage(argv,-1);
       break;
+#if defined (BS1770GAIN_TAG_PREFIX) // {
+    case TAG_PREFIX:
+      options.tag_prefix=optarg;
+      break;
+#endif // }
     case FORMAT:
       options.format=optarg;
       break;
@@ -486,7 +517,7 @@ int main(int argc, char **argv)
       break;
     case REPLAYGAIN:
       options.level=-18.0;
-    options.unit="dB";
+      options.unit="dB";
       break;
     case RG_TAGS:
       options.mode|=BS1770GAIN_MODE_RG_TAGS;
@@ -500,15 +531,18 @@ int main(int argc, char **argv)
     case ALBUM_TAGS:
       options.mode|=BS1770GAIN_MODE_ALBUM_TAGS;
       break;
+    case XML:
+      options.xml=1;
+      break;
     case TIME:
       options.time=1;
       break;
-    case MONO2STEREO:
-      options.mono2stereo=1;
+    case STEREO:
+      options.stereo=1;
       break;
     ///////////////////////////////////////////////////////////////////////////
     case MOMENTARY_RANGE:
-      options.flags|=AGGREGATE_MOMENTARY_RANGE;
+      options.aggregate|=FFSOX_AGGREGATE_MOMENTARY_RANGE;
       break;
     ////////
     case MOMENTARY_LENGTH:
@@ -540,7 +574,7 @@ int main(int argc, char **argv)
       break;
     ////////
     case SHORTTERM_MEAN:
-      options.flags|=AGGREGATE_SHORTTERM_MEAN;
+      options.aggregate|=FFSOX_AGGREGATE_SHORTTERM_MEAN;
       break;
     ////////
     case SHORTTERM_LENGTH:
@@ -615,7 +649,7 @@ int main(int argc, char **argv)
 
   if (BS1770GAIN_OPTIONS_NO_METHOD(&options)) {
     if (NULL!=odirname||BS1770GAIN_OPTIONS_NO_RANGE_PEAK(&options))
-      options.flags|=AGGREGATE_MOMENTARY_MEAN;
+      options.aggregate|=FFSOX_AGGREGATE_MOMENTARY_MEAN;
   }
 
   // load the FFmpeg and SoX libraries from "bs1770gain-tools".
@@ -630,10 +664,17 @@ int main(int argc, char **argv)
   if (0==options.dump||av_log_get_level()<loglevel)
     av_log_set_level(loglevel);
 
-  if (NULL!=fpath&&NULL==(options.f=fopen(fpath,"w")))
+  if (fpath&&NULL==(f=fopen(fpath,"w")))
     goto file;
 
   bs1770gain_tree_cli_init(&root,argc,argv,optind);
+
+  if (options.xml)
+    bs1770gain_print_xml(&options.p,f);
+  else
+    bs1770gain_print_classic(&options.p,f);
+
+  options.p.vmt->session.head(&options.p);
   t1=clock();
   bs1770gain_tree_analyze(&root,odirname,&options);
   t2=clock();
@@ -641,17 +682,18 @@ int main(int argc, char **argv)
   if (0==root.cli.count)
     fprintf(stderr,"Warning: No valid input files/folders given.\n");
 
+  options.p.vmt->session.tail(&options.p);
   root.vmt->cleanup(&root);
 
   if (options.time)
-    fprintf(stderr, "Duration: %ld ms.\n",(t2-t1)/CLOCKS_PER_MILLIS);
+    fprintf(stderr, "Duration: %.0f ms.\n",(t2-t1)/CLOCKS_PER_MILLIS);
 // cleanup:
   sox_quit();
   // still reachable: 9,689 bytes in 51 blocks
   // TODO: Cleanup FFmpeg. But how?
 dynload:
   if (NULL!=fpath)
-    fclose(options.f);
+    fclose(f);
 file:
   TRACE_POP();
   PBU_HEAP_PRINT();
diff --git a/bs1770gain/bs1770gain.h b/bs1770gain/bs1770gain.h
index de7023c..9be50fc 100755
--- a/bs1770gain/bs1770gain.h
+++ b/bs1770gain/bs1770gain.h
@@ -17,8 +17,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  * MA  02110-1301  USA
  */
-#ifndef __BS1770GAIN_H__
-#define __BS1770GAIN_H__ // {
+#ifndef __BS1770GAIN_H__ // {
+#define __BS1770GAIN_H__
 #include <ffsox.h>
 #include <dirent.h>
 #ifdef __cpluplus
@@ -26,6 +26,9 @@ extern "C" {
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
+//#define BS1770GAIN_TAG_PREFIX
+
+///////////////////////////////////////////////////////////////////////////////
 typedef struct bs1770gain_block_options bs1770gain_block_options_t;
 typedef struct bs1770gain_options bs1770gain_options_t;
 typedef struct bs1770gain_tag bs1770gain_tag_t;
@@ -33,6 +36,8 @@ typedef struct bs1770gain_tree_vmt bs1770gain_tree_vmt_t;
 typedef struct bs1770gain_tree bs1770gain_tree_t;
 typedef struct bs1770gain_album bs1770gain_album_t;
 typedef struct bs1770gain_track bs1770gain_track_t;
+typedef struct bs1770gain_print_vmt bs1770gain_print_vmt_t;
+typedef struct bs1770gain_print bs1770gain_print_t;
 
 ///////////////////////////////////////////////////////////////////////////////
 char *bs1770gain_opath(const char *ipath, const char *odirname,
@@ -41,7 +46,7 @@ char *bs1770gain_opathx(int n, const char *title, const char *odirname,
     const char *oext);
 
 int bs1770gain_transcode(bs1770gain_track_t *track,
-    const bs1770gain_options_t *options);
+    bs1770gain_options_t *options);
 // parse time in microseconds.
 int64_t bs1770gain_parse_time(const char *s);
 
@@ -51,11 +56,45 @@ double bs1770gain_aggregate_get_loudness(const ffsox_aggregate_t *aggregate,
     const bs1770gain_options_t *options);
 
 ///////////////////////////////////////////////////////////////////////////////
-#define BS1770GAIN_MODE_APPLY                 1
-#define BS1770GAIN_MODE_RG_TAGS               2
-#define BS1770GAIN_MODE_BWF_TAGS              4
-#define BS1770GAIN_MODE_TRACK_TAGS            8
-#define BS1770GAIN_MODE_ALBUM_TAGS            16
+struct bs1770gain_print_vmt {
+  const char *name;
+
+  struct {
+    void (*head)(bs1770gain_print_t *p);
+    FILE *(*file)(bs1770gain_print_t *p);
+    void (*tail)(bs1770gain_print_t *p);
+  } session;
+
+  struct {
+    void (*head)(bs1770gain_print_t *p, bs1770gain_album_t *a,
+        const char *ibasename);
+    void (*tail)(bs1770gain_print_t *p);
+  } album;
+
+  struct {
+    void (*head)(bs1770gain_print_t *p, bs1770gain_track_t *t);
+    void (*body)(bs1770gain_print_t *p, ffsox_aggregate_t *aggregate,
+        const bs1770gain_options_t *options);
+    void (*tail)(bs1770gain_print_t *p);
+  } track;
+};
+
+struct bs1770gain_print {
+  const bs1770gain_print_vmt_t *vmt;
+  FILE *f;
+  bs1770gain_album_t *a;
+  bs1770gain_track_t *t;
+};
+
+void bs1770gain_print_classic(bs1770gain_print_t *p, FILE *f);
+void bs1770gain_print_xml(bs1770gain_print_t *p, FILE *f);
+
+///////////////////////////////////////////////////////////////////////////////
+#define BS1770GAIN_MODE_APPLY                 (1<<0)
+#define BS1770GAIN_MODE_RG_TAGS               (1<<1)
+#define BS1770GAIN_MODE_BWF_TAGS              (1<<2)
+#define BS1770GAIN_MODE_TRACK_TAGS            (1<<3)
+#define BS1770GAIN_MODE_ALBUM_TAGS            (1<<4)
 
 #define BS1770GAIN_MODE_RG_BWF_TAGS \
     (BS1770GAIN_MODE_RG_TAGS|BS1770GAIN_MODE_BWF_TAGS)
@@ -63,20 +102,20 @@ double bs1770gain_aggregate_get_loudness(const ffsox_aggregate_t *aggregate,
     (BS1770GAIN_MODE_TRACK_TAGS|BS1770GAIN_MODE_ALBUM_TAGS)
 
 #define BS1770GAIN_IS_MODE_RG_TAGS(mode) \
-	(0!=(BS1770GAIN_MODE_RG_TAGS&(mode)))
+    (0!=(BS1770GAIN_MODE_RG_TAGS&(mode)))
 #define BS1770GAIN_IS_MODE_BWF_TAGS(mode) \
-	(0!=(BS1770GAIN_MODE_BWF_TAGS&(mode)))
+    (0!=(BS1770GAIN_MODE_BWF_TAGS&(mode)))
 #define BS1770GAIN_IS_MODE_APPLY(mode) \
-	(0!=(BS1770GAIN_MODE_APPLY&(mode)))
+    (0!=(BS1770GAIN_MODE_APPLY&(mode)))
 #define BS1770GAIN_IS_MODE_TRACK_TAGS(mode) \
-	(0!=(BS1770GAIN_MODE_TRACK_TAGS&(mode)))
+    (0!=(BS1770GAIN_MODE_TRACK_TAGS&(mode)))
 #define BS1770GAIN_IS_MODE_ALBUM_TAGS(mode) \
-	(0!=(BS1770GAIN_MODE_ALBUM_TAGS&(mode)))
+    (0!=(BS1770GAIN_MODE_ALBUM_TAGS&(mode)))
 
 #define BS1770GAIN_IS_MODE_RG_BWF_TAGS(mode) \
-	(0!=(BS1770GAIN_MODE_RG_BWF_TAGS&(mode)))
+    (0!=(BS1770GAIN_MODE_RG_BWF_TAGS&(mode)))
 #define BS1770GAIN_IS_MODE_TRACK_ALBUM_TAGS(mode) \
-	(0!=(BS1770GAIN_MODE_TRACK_ALBUM_TAGS&(mode)))
+    (0!=(BS1770GAIN_MODE_TRACK_ALBUM_TAGS&(mode)))
 
 // *must* begin with 0 (defines default)
 #define BS1770GAIN_METHOD_MOMENTARY_MEAN      0
@@ -106,7 +145,8 @@ struct bs1770gain_block_options {
 };
 
 struct bs1770gain_options {
-  FILE *f;
+  bs1770gain_print_t p;
+  int xml;
   const char *unit;
   double level;
   double preamp;
@@ -115,12 +155,10 @@ struct bs1770gain_options {
   int64_t duration;
   int audio;
   int video;
-  int flags;
+  int aggregate;
   bs1770gain_block_options_t momentary;
   bs1770gain_block_options_t shortterm;
-  int truepeak;
-  int samplepeak;
-  int mono2stereo;
+  int stereo;
   int mode;
   int method;
   int extensions;
@@ -130,6 +168,9 @@ struct bs1770gain_options {
   const char *format;
   const char *video_ext;
   const char *audio_ext;
+#if defined (BS1770GAIN_TAG_PREFIX) // {
+  const char *tag_prefix;
+#endif // }
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -138,7 +179,7 @@ struct bs1770gain_tag {
   char val[32];
 };
 
-extern bs1770gain_tag_t bs1770gain_tags[];
+//extern bs1770gain_tag_t bs1770gain_tags[];
 
 ///////////////////////////////////////////////////////////////////////////////
 #define BS1770GAIN_TREE_STATE_INV   0
@@ -180,11 +221,11 @@ bs1770gain_tree_t *bs1770gain_tree_init(bs1770gain_tree_t *tree,
     const bs1770gain_tree_t *parent);
 
 int bs1770gain_tree_analyze(bs1770gain_tree_t *tree, const char *odirname,
-    const bs1770gain_options_t *options);
+    bs1770gain_options_t *options);
 int bs1770gain_tree_track(bs1770gain_tree_t *tree, bs1770gain_album_t *album,
-    const bs1770gain_options_t *options);
+    bs1770gain_options_t *options);
 int bs1770gain_tree_album(const bs1770gain_tree_t *root, const char *odirname,
-    const bs1770gain_options_t *options);
+    bs1770gain_options_t *options);
 
 int bs1770gain_tree_stat(bs1770gain_tree_t *tree, char *path,
     const bs1770gain_options_t *options);
diff --git a/bs1770gain/bs1770gain_aggregate.c b/bs1770gain/bs1770gain_aggregate.c
index 0ded023..02039e9 100755
--- a/bs1770gain/bs1770gain_aggregate.c
+++ b/bs1770gain/bs1770gain_aggregate.c
@@ -19,146 +19,6 @@
  */
 #include <bs1770gain_priv.h>
 
-static int bs1770gain_aggregate_width(int flags)
-{
-  int width=0;
-  int len;
-
-  ////////
-  if (0!=(flags&AGGREGATE_MOMENTARY_MEAN)) {
-    if (width<(len=strlen("integrated")))
-      width=len;
-  }
-
-  if (0!=(flags&AGGREGATE_MOMENTARY_MAXIMUM)) {
-    if (width<(len=strlen("momentary maximum")))
-      width=len;
-  }
-
-  if (0!=(flags&AGGREGATE_MOMENTARY_RANGE)) {
-    if (width<(len=strlen("momentary range")))
-      width=len;
-  }
-
-  ////////
-  if (0!=(flags&AGGREGATE_SHORTTERM_MEAN)) {
-    if (width<(len=strlen("shortterm mean")))
-      width=len;
-  }
-
-  if (0!=(flags&AGGREGATE_SHORTTERM_MAXIMUM)) {
-    if (width<(len=strlen("shortterm maximum")))
-      width=len;
-  }
-
-  if (0!=(flags&AGGREGATE_SHORTTERM_RANGE)) {
-    if (width<(len=strlen("range")))
-      width=len;
-  }
-
-  ////////
-  if (0!=(flags&AGGREGATE_SAMPLEPEAK)) {
-    if (width<(len=strlen("sample peak")))
-      width=len;
-  }
-
-  if (0!=(flags&AGGREGATE_TRUEPEAK)) {
-    if (width<(len=strlen("true peak")))
-      width=len;
-  }
-
-  return width;
-}
-
-static void bs1770gain_aggregate_label(const char *label, int width, FILE *f)
-{
-  width+=6;
-
-  for (width-=strlen(label);0<width;--width)
-    fputc(' ',f);
-
-  fprintf(f,"%s:  ",label);
-}
-
-void bs1770gain_aggregate_print(aggregate_t *aggregate,
-    const options_t *options)
-{
-  int flags=aggregate->flags;
-  FILE *f=options->f;
-  double level=options->preamp+options->level;
-  int width=bs1770gain_aggregate_width(flags);
-  double q,db;
-
-  ////////
-  if (0!=(flags&AGGREGATE_MOMENTARY_MEAN)) {
-    db=lib1770_stats_get_mean(aggregate->momentary,
-        options->momentary.mean_gate);
-    //fprintf(f,"       integrated:  %.1f LUFS / %.1f LU\n",db,level-db);
-    bs1770gain_aggregate_label("integrated",width,f);
-    fprintf(f,"%.1f LUFS / %.1f LU\n",db,level-db);
-  }
-
-  if (0!=(flags&AGGREGATE_MOMENTARY_MAXIMUM)) {
-    db=lib1770_stats_get_max(aggregate->momentary);
-    //fprintf(f,"        momentary:  %.1f LUFS / %.1f LU\n",db,level-db);
-    bs1770gain_aggregate_label("momentary maximum",width,f);
-    fprintf(f,"%.1f LUFS / %.1f LU\n",db,level-db);
-  }
-
-  if (0!=(flags&AGGREGATE_MOMENTARY_RANGE)) {
-    db=lib1770_stats_get_range(aggregate->momentary,
-        options->momentary.range_gate,
-        options->momentary.range_lower_bound,
-        options->momentary.range_upper_bound);
-    //fprintf(f,"            range:  %.1f LUFS\n",db);
-    bs1770gain_aggregate_label("momentary range",width,f);
-    fprintf(f,"%.1f LUFS\n",db);
-  }
-
-  ////////
-  if (0!=(flags&AGGREGATE_SHORTTERM_MEAN)) {
-    db=lib1770_stats_get_mean(aggregate->shortterm,
-        options->shortterm.mean_gate);
-    //fprintf(f,"       integrated:  %.1f LUFS / %.1f LU\n",db,level-db);
-    bs1770gain_aggregate_label("shortterm mean",width,f);
-    fprintf(f,"%.1f LUFS / %.1f LU\n",db,level-db);
-  }
-
-  if (0!=(flags&AGGREGATE_SHORTTERM_MAXIMUM)) {
-    db=lib1770_stats_get_max(aggregate->shortterm);
-    //fprintf(f,"       shortterm:  %.1f LUFS / %.1f LU\n",db,level-db);
-    bs1770gain_aggregate_label("shortterm maximum",width,f);
-    fprintf(f,"%.1f LUFS / %.1f LU\n",db,level-db);
-  }
-
-  if (0!=(flags&AGGREGATE_SHORTTERM_RANGE)) {
-    db=lib1770_stats_get_range(aggregate->shortterm,
-        options->shortterm.range_gate,
-        options->shortterm.range_lower_bound,
-        options->shortterm.range_upper_bound);
-    //fprintf(f,"            range:  %.1f LUFS\n",db);
-    bs1770gain_aggregate_label("range",width,f);
-    fprintf(f,"%.1f LUFS\n",db);
-  }
-
-  ////////
-  if (0!=(flags&AGGREGATE_SAMPLEPEAK)) {
-    q=aggregate->samplepeak;
-    db=LIB1770_Q2DB(q);
-    //fprintf(f,"      sample peak:  %.1f SPFS / %f\n",db,q);
-    bs1770gain_aggregate_label("sample peak",width,f);
-    fprintf(f,"%.1f SPFS / %f\n",db,q);
-  }
-
-  if (0!=(flags&AGGREGATE_TRUEPEAK)) {
-    q=aggregate->truepeak;
-    db=LIB1770_Q2DB(q);
-    //fprintf(f,"        true peak:  %.1f TPFS / %f\n",db,q);
-    bs1770gain_aggregate_label("true peak",width,f);
-    fprintf(f,"%.1f TPFS / %f\n",db,q);
-  }
-}
-
 double bs1770gain_aggregate_get_loudness(const aggregate_t *aggregate,
     const options_t *options)
 {
diff --git a/bs1770gain/bs1770gain_album.c b/bs1770gain/bs1770gain_album.c
index f2cc8ad..86112a2 100755
--- a/bs1770gain/bs1770gain_album.c
+++ b/bs1770gain/bs1770gain_album.c
@@ -46,7 +46,7 @@ bs1770gain_album_t *bs1770gain_album_new(const char *ipath, const char *opath,
     goto opath;
   }
 
-  if (ffsox_aggregate_create(&album->aggregate,options->flags)<0) {
+  if (ffsox_aggregate_create(&album->aggregate,options->aggregate)<0) {
     DMESSAGE("creating album aggregator");
     goto aggregate;
   }
@@ -159,7 +159,7 @@ bs1770gain_track_t *bs1770gain_track_new(const char *ipath,
     goto ipath;
   }
 
-  if (ffsox_aggregate_create(&track->aggregate,options->flags)<0) {
+  if (ffsox_aggregate_create(&track->aggregate,options->aggregate)<0) {
     DMESSAGE("creating album aggregator");
     goto aggregate;
   }
diff --git a/bs1770gain/bs1770gain_opath.c b/bs1770gain/bs1770gain_opath.c
index 050b570..c8debb4 100755
--- a/bs1770gain/bs1770gain_opath.c
+++ b/bs1770gain/bs1770gain_opath.c
@@ -22,7 +22,7 @@
 
 static char *bs177gain_norm_title(char *tp)
 {
-  static const char tok[]=" /-_()[]{}?'`´";
+  static const char tok[]=" /-_()[]{}?'`´.";
   char *p,*saveptr;
 
   // TODO: unicode.
diff --git a/bs1770gain/bs1770gain_aggregate.c b/bs1770gain/bs1770gain_print_classic.c
similarity index 61%
copy from bs1770gain/bs1770gain_aggregate.c
copy to bs1770gain/bs1770gain_print_classic.c
index 0ded023..3eeb673 100755
--- a/bs1770gain/bs1770gain_aggregate.c
+++ b/bs1770gain/bs1770gain_print_classic.c
@@ -1,6 +1,6 @@
 /*
- * bs1770gain_aggregate.c
- * Copyright (C) 2015 Peter Belkner <pbelkner at users.sf.net>
+ * bs1770gain_print_classic.c
+ * Copyright (C) 2014 Peter Belkner <pbelkner at users.sf.net>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public
@@ -19,7 +19,19 @@
  */
 #include <bs1770gain_priv.h>
 
-static int bs1770gain_aggregate_width(int flags)
+///////////////////////////////////////////////////////////////////////////////
+static const bs1770gain_print_vmt_t *get_vmt(void);
+
+///////////////////////////////////////////////////////////////////////////////
+void bs1770gain_print_classic(bs1770gain_print_t *p, FILE *f)
+{
+  p->vmt=get_vmt();
+  p->f=f;
+  p->a=NULL;
+  p->t=NULL;
+}
+
+static int bs1770gain_print_width(int flags)
 {
   int width=0;
   int len;
@@ -70,7 +82,7 @@ static int bs1770gain_aggregate_width(int flags)
   return width;
 }
 
-static void bs1770gain_aggregate_label(const char *label, int width, FILE *f)
+static void bs1770gain_print_label(const char *label, int width, FILE *f)
 {
   width+=6;
 
@@ -80,28 +92,75 @@ static void bs1770gain_aggregate_label(const char *label, int width, FILE *f)
   fprintf(f,"%s:  ",label);
 }
 
-void bs1770gain_aggregate_print(aggregate_t *aggregate,
+///////////////////////////////////////////////////////////////////////////////
+static void session_head(bs1770gain_print_t *p)
+{
+}
+
+static FILE *session_file(bs1770gain_print_t *p)
+{
+  return stdout==p->f?p->f:NULL;
+}
+
+static void session_tail(bs1770gain_print_t *p)
+{
+}
+
+////////
+static void album_head(bs1770gain_print_t *p, bs1770gain_album_t *a,
+    const char *ibasename)
+{
+  p->a=a;
+
+  if (NULL==ibasename||0==*ibasename)
+    fprintf(p->f,"analyzing ...\n");
+  else
+    fprintf(p->f,"analyzing \"%s\" ...\n",ibasename);
+}
+
+static void album_tail(bs1770gain_print_t *p)
+{
+  p->a=NULL;
+}
+
+////////
+static void track_head(bs1770gain_print_t *p, bs1770gain_track_t *t)
+{
+  p->t=t;
+
+  if (NULL==t)
+    fprintf(p->f,"  [ALBUM]:");
+  else {
+    fprintf(p->f,"  [%d/%d] \"%s\"",t->n,p->a->n,pbu_basename(t->ipath));
+    fprintf(p->f,": ");
+    fflush(p->f);
+  }
+}
+
+static void track_body(bs1770gain_print_t *p, aggregate_t *aggregate,
     const options_t *options)
 {
   int flags=aggregate->flags;
-  FILE *f=options->f;
+  FILE *f=p->f;
   double level=options->preamp+options->level;
-  int width=bs1770gain_aggregate_width(flags);
+  int width=bs1770gain_print_width(flags);
   double q,db;
 
+  fprintf(p->f,p->t&&p->vmt->session.file(p)?"        \n":"\n");
+
   ////////
   if (0!=(flags&AGGREGATE_MOMENTARY_MEAN)) {
     db=lib1770_stats_get_mean(aggregate->momentary,
         options->momentary.mean_gate);
     //fprintf(f,"       integrated:  %.1f LUFS / %.1f LU\n",db,level-db);
-    bs1770gain_aggregate_label("integrated",width,f);
+    bs1770gain_print_label("integrated",width,f);
     fprintf(f,"%.1f LUFS / %.1f LU\n",db,level-db);
   }
 
   if (0!=(flags&AGGREGATE_MOMENTARY_MAXIMUM)) {
     db=lib1770_stats_get_max(aggregate->momentary);
     //fprintf(f,"        momentary:  %.1f LUFS / %.1f LU\n",db,level-db);
-    bs1770gain_aggregate_label("momentary maximum",width,f);
+    bs1770gain_print_label("momentary maximum",width,f);
     fprintf(f,"%.1f LUFS / %.1f LU\n",db,level-db);
   }
 
@@ -111,7 +170,7 @@ void bs1770gain_aggregate_print(aggregate_t *aggregate,
         options->momentary.range_lower_bound,
         options->momentary.range_upper_bound);
     //fprintf(f,"            range:  %.1f LUFS\n",db);
-    bs1770gain_aggregate_label("momentary range",width,f);
+    bs1770gain_print_label("momentary range",width,f);
     fprintf(f,"%.1f LUFS\n",db);
   }
 
@@ -120,14 +179,14 @@ void bs1770gain_aggregate_print(aggregate_t *aggregate,
     db=lib1770_stats_get_mean(aggregate->shortterm,
         options->shortterm.mean_gate);
     //fprintf(f,"       integrated:  %.1f LUFS / %.1f LU\n",db,level-db);
-    bs1770gain_aggregate_label("shortterm mean",width,f);
+    bs1770gain_print_label("shortterm mean",width,f);
     fprintf(f,"%.1f LUFS / %.1f LU\n",db,level-db);
   }
 
   if (0!=(flags&AGGREGATE_SHORTTERM_MAXIMUM)) {
     db=lib1770_stats_get_max(aggregate->shortterm);
     //fprintf(f,"       shortterm:  %.1f LUFS / %.1f LU\n",db,level-db);
-    bs1770gain_aggregate_label("shortterm maximum",width,f);
+    bs1770gain_print_label("shortterm maximum",width,f);
     fprintf(f,"%.1f LUFS / %.1f LU\n",db,level-db);
   }
 
@@ -137,7 +196,7 @@ void bs1770gain_aggregate_print(aggregate_t *aggregate,
         options->shortterm.range_lower_bound,
         options->shortterm.range_upper_bound);
     //fprintf(f,"            range:  %.1f LUFS\n",db);
-    bs1770gain_aggregate_label("range",width,f);
+    bs1770gain_print_label("range",width,f);
     fprintf(f,"%.1f LUFS\n",db);
   }
 
@@ -146,7 +205,7 @@ void bs1770gain_aggregate_print(aggregate_t *aggregate,
     q=aggregate->samplepeak;
     db=LIB1770_Q2DB(q);
     //fprintf(f,"      sample peak:  %.1f SPFS / %f\n",db,q);
-    bs1770gain_aggregate_label("sample peak",width,f);
+    bs1770gain_print_label("sample peak",width,f);
     fprintf(f,"%.1f SPFS / %f\n",db,q);
   }
 
@@ -154,34 +213,32 @@ void bs1770gain_aggregate_print(aggregate_t *aggregate,
     q=aggregate->truepeak;
     db=LIB1770_Q2DB(q);
     //fprintf(f,"        true peak:  %.1f TPFS / %f\n",db,q);
-    bs1770gain_aggregate_label("true peak",width,f);
+    bs1770gain_print_label("true peak",width,f);
     fprintf(f,"%.1f TPFS / %f\n",db,q);
   }
 }
 
-double bs1770gain_aggregate_get_loudness(const aggregate_t *aggregate,
-    const options_t *options)
+static void track_tail(bs1770gain_print_t *p)
+{
+  p->t=NULL;
+}
+
+////////
+static const bs1770gain_print_vmt_t *get_vmt(void)
 {
-  switch (options->method) {
-  case BS1770GAIN_METHOD_MOMENTARY_MEAN:
-    return NULL==aggregate->momentary
-        ?LIB1770_SILENCE
-        :lib1770_stats_get_mean(aggregate->momentary,
-            options->momentary.mean_gate);
-  case BS1770GAIN_METHOD_MOMENTARY_MAXIMUM:
-    return NULL==aggregate->momentary
-        ?LIB1770_SILENCE
-        :lib1770_stats_get_max(aggregate->momentary);
-  case BS1770GAIN_METHOD_SHORTTERM_MEAN:
-    return NULL==aggregate->shortterm
-        ?LIB1770_SILENCE
-        :lib1770_stats_get_mean(aggregate->shortterm,
-            options->shortterm.mean_gate);
-  case BS1770GAIN_METHOD_SHORTTERM_MAXIMUM:
-    return NULL==aggregate->shortterm
-        ?LIB1770_SILENCE
-        :lib1770_stats_get_max(aggregate->shortterm);
-  default:
-    return LIB1770_SILENCE;
+  static bs1770gain_print_vmt_t vmt;
+
+  if (NULL==vmt.name) {
+    vmt.name="bs1770gain_print_classic";
+    vmt.session.head=session_head;
+    vmt.session.file=session_file;
+    vmt.session.tail=session_tail;
+    vmt.album.head=album_head;
+    vmt.album.tail=album_tail;
+    vmt.track.head=track_head;
+    vmt.track.body=track_body;
+    vmt.track.tail=track_tail;
   }
+
+  return &vmt;
 }
diff --git a/bs1770gain/bs1770gain_print_xml.c b/bs1770gain/bs1770gain_print_xml.c
new file mode 100755
index 0000000..8f9fba1
--- /dev/null
+++ b/bs1770gain/bs1770gain_print_xml.c
@@ -0,0 +1,169 @@
+/*
+ * bs1770gain_print_xml.c
+ * Copyright (C) 2014 Peter Belkner <pbelkner at users.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301  USA
+ */
+#include <bs1770gain_priv.h>
+
+///////////////////////////////////////////////////////////////////////////////
+static const bs1770gain_print_vmt_t *get_vmt(void);
+
+///////////////////////////////////////////////////////////////////////////////
+void bs1770gain_print_xml(bs1770gain_print_t *p, FILE *f)
+{
+  p->vmt=get_vmt();
+  p->f=f;
+  p->a=NULL;
+  p->t=NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static void session_head(bs1770gain_print_t *p)
+{
+  fprintf(p->f,"<bs1770gain>\n");
+}
+
+static FILE *session_file(bs1770gain_print_t *p)
+{
+  return NULL;
+}
+
+static void session_tail(bs1770gain_print_t *p)
+{
+  fprintf(p->f,"</bs1770gain>\n");
+}
+
+////////
+static void album_head(bs1770gain_print_t *p, bs1770gain_album_t *a,
+    const char *ibasename)
+{
+  p->a=a;
+
+  if (NULL==ibasename||0==*ibasename)
+    fprintf(p->f,"  <album>\n");
+  else
+    fprintf(p->f,"  <album folder=\"%s\">\n",ibasename);
+}
+
+static void album_tail(bs1770gain_print_t *p)
+{
+  p->a=NULL;
+  fprintf(p->f,"  </album>\n");
+}
+
+////////
+static void track_head(bs1770gain_print_t *p, bs1770gain_track_t *t)
+{
+  p->t=t;
+
+  if (NULL==t)
+    fprintf(p->f,"    <summary total=\"%d\">\n",p->a->n);
+  else {
+    fprintf(p->f,"    <track total=\"%d\" number=\"%d\" file=\"%s\">\n",
+        p->a->n,t->n,pbu_basename(t->ipath));
+  }
+}
+
+static void track_body(bs1770gain_print_t *p, aggregate_t *aggregate,
+    const options_t *options)
+{
+  int flags=aggregate->flags;
+  FILE *f=p->f;
+  double level=options->preamp+options->level;
+  double q,db;
+
+  ////////
+  if (0!=(flags&AGGREGATE_MOMENTARY_MEAN)) {
+    db=lib1770_stats_get_mean(aggregate->momentary,
+        options->momentary.mean_gate);
+    fprintf(f,"      <integrated lufs=\"%.1f\" lu=\"%.1f\" />\n",db,level-db);
+  }
+
+  if (0!=(flags&AGGREGATE_MOMENTARY_MAXIMUM)) {
+    db=lib1770_stats_get_max(aggregate->momentary);
+    fprintf(f,"      <momentary lufs=\"%.1f\" lu=\"%.1f\" />\n",db,level-db);
+  }
+
+  if (0!=(flags&AGGREGATE_MOMENTARY_RANGE)) {
+    db=lib1770_stats_get_range(aggregate->momentary,
+        options->momentary.range_gate,
+        options->momentary.range_lower_bound,
+        options->momentary.range_upper_bound);
+    fprintf(f,"      <momentary-range lufs=\"%.1f\" />\n",db);
+  }
+
+  ////////
+  if (0!=(flags&AGGREGATE_SHORTTERM_MEAN)) {
+    db=lib1770_stats_get_mean(aggregate->shortterm,
+        options->shortterm.mean_gate);
+    fprintf(f,"      <shortterm-mean lufs=\"%.1f\" lu=\"%.1f\" />\n",
+        db,level-db);
+  }
+
+  if (0!=(flags&AGGREGATE_SHORTTERM_MAXIMUM)) {
+    db=lib1770_stats_get_max(aggregate->shortterm);
+    fprintf(f,"      <shortterm-maximum lufs=\"%.1f\" lu=\"%.1f\" />\n",
+        db,level-db);
+  }
+
+  if (0!=(flags&AGGREGATE_SHORTTERM_RANGE)) {
+    db=lib1770_stats_get_range(aggregate->shortterm,
+        options->shortterm.range_gate,
+        options->shortterm.range_lower_bound,
+        options->shortterm.range_upper_bound);
+    fprintf(f,"      <range lufs=\"%.1f\" />\n",db);
+  }
+
+  ////////
+  if (0!=(flags&AGGREGATE_SAMPLEPEAK)) {
+    q=aggregate->samplepeak;
+    db=LIB1770_Q2DB(q);
+    fprintf(f,"      <sample-peak spfs=\"%.1f\" factor=\"%f\" />\n",db,q);
+  }
+
+  if (0!=(flags&AGGREGATE_TRUEPEAK)) {
+    q=aggregate->truepeak;
+    db=LIB1770_Q2DB(q);
+    fprintf(f,"      <true-peak tpfs=\"%.1f\" factor=\" %f\" />\n",db,q);
+  }
+}
+
+static void track_tail(bs1770gain_print_t *p)
+{
+  fprintf(p->f,p->t?"    </track>\n":"    </summary>\n");
+  p->t=NULL;
+}
+
+////////
+static const bs1770gain_print_vmt_t *get_vmt(void)
+{
+  static bs1770gain_print_vmt_t vmt;
+
+  if (NULL==vmt.name) {
+    vmt.name="bs1770gain_print_xml";
+    vmt.session.head=session_head;
+    vmt.session.file=session_file;
+    vmt.session.tail=session_tail;
+    vmt.album.head=album_head;
+    vmt.album.tail=album_tail;
+    vmt.track.head=track_head;
+    vmt.track.body=track_body;
+    vmt.track.tail=track_tail;
+  }
+
+  return &vmt;
+}
diff --git a/bs1770gain/bs1770gain_transcode.c b/bs1770gain/bs1770gain_transcode.c
index 2e17a07..cfc1e2d 100755
--- a/bs1770gain/bs1770gain_transcode.c
+++ b/bs1770gain/bs1770gain_transcode.c
@@ -18,6 +18,7 @@
  * MA  02110-1301  USA
  */
 #include <bs1770gain_priv.h>
+#include <ctype.h>
 
 static void bs1770gain_tags_rg(tag_t *tags, const aggregate_t *track,
     const aggregate_t *album, const options_t *options)
@@ -130,6 +131,113 @@ static void bs1770gain_tags_bwf(tag_t *tags, const aggregate_t *aggregate,
   }
 }
 
+#if defined (BS1770GAIN_TAG_PREFIX) // {
+static const char *bs1770gain_convert_key(tag_t *t, char *key, size_t size,
+    const options_t *options)
+{
+  const char *p1,*p2,*rp;
+  char *wp;
+
+    p1=t->key;
+    p2="REPLAYGAIN";
+
+    while (*p2) {
+      if (*p1++!=*p2++) {
+        // no RG tag, copy it into the fresh dictionanry.
+        return t->key;
+      }
+    }
+
+    if (size<strlen(options->tag_prefix)+strlen(p1))
+      return NULL;
+
+    wp=key;
+
+    for (rp=options->tag_prefix;*rp;++rp)
+      *wp++=toupper(*rp);
+
+    for (rp=p1;*rp;++rp)
+      *wp++=*rp;
+
+    *wp=0;
+
+    // RG tag, copy it into the fresh dictionanry.
+    return key;
+}
+
+static void bs1770gain_clone_dict(track_t *track, AVDictionary **ometadata,
+    AVDictionary *imetadata, tag_t *tags, const options_t *options)
+{
+  enum {
+    TRACK=1<<1,
+    DISC=1<<2
+  };
+
+#if 0 // {
+  album_t *album=track->album;
+#endif // }
+  AVDictionaryEntry *de=NULL;
+  tag_t *t;
+  int flags=0;
+  char value[32];
+  char buf[128];
+  const char *key;
+
+  // for each tag ...
+  while (NULL!=(de=av_dict_get(imetadata,"",de,AV_DICT_IGNORE_SUFFIX))) {
+    // ... filter out RG/BWF tags
+    for (t=tags;NULL!=t->key;++t) {
+      if (NULL==(key=bs1770gain_convert_key(t,buf,sizeof buf,options)))
+        goto next_de;
+      else if (0==strcasecmp(key,de->key))
+        goto next_de;
+    }
+
+    // ... copy it into the fresh dictionary.
+    if (0==strcasecmp("TRACK",de->key))
+      flags|=TRACK;
+    else if (0==strcasecmp("DISC",de->key))
+      flags|=DISC;
+
+    av_dict_set(ometadata,de->key,de->value,0);
+  next_de:
+    continue;
+  }
+
+  if (0!=(EXTENSION_TAGS&options->extensions)) {
+    if (0==(TRACK&flags)) {
+#if 0 // {
+      sprintf(value,"%d/%d",track->n,album->n);
+#else // } {
+      sprintf(value,"%d",track->n);
+#endif // }
+      av_dict_set(ometadata,"TRACK",value,0);
+    }
+
+    if (0==(DISC&flags)) {
+      sprintf(value,"%d",1);
+      av_dict_set(ometadata,"DISC",value,0);
+    }
+  }
+}
+
+static void bs1770gain_write_dict(AVDictionary **ometadata, tag_t *tags,
+    const options_t *options)
+{
+  tag_t *t;
+  char buf[128];
+  const char *key;
+
+  // for each RG/BWF tag ...
+  for (t=tags;NULL!=t->key;++t) {
+    if (0==*t->val)
+      continue;
+
+    if (NULL!=(key=bs1770gain_convert_key(t,buf,sizeof buf,options)))
+      av_dict_set(ometadata,key,t->val,0);
+  }
+}
+#else // } {
 static void bs1770gain_clone_dict(track_t *track, AVDictionary **ometadata,
     AVDictionary *imetadata, tag_t *tags, const options_t *options)
 {
@@ -193,6 +301,7 @@ static void bs1770gain_write_dict(AVDictionary **ometadata, tag_t *tags)
       av_dict_set(ometadata,t->key,t->val,0);
   }
 }
+#endif // }
 
 // copy all tags into the fresh dictionary except the RG/BWF ones.
 static void bs1770gain_clone_tags(tag_t *tags, sink_t *so, source_t *si,
@@ -223,7 +332,7 @@ static void bs1770gain_clone_tags(tag_t *tags, sink_t *so, source_t *si,
   }
 }
 
-int bs1770gain_transcode(track_t *t, const options_t *options)
+int bs1770gain_transcode(track_t *t, options_t *options)
 {
   enum { CODEC_ID=AV_CODEC_ID_FLAC,SAMPLE_FMT=AV_SAMPLE_FMT_S32 };
 
@@ -246,8 +355,10 @@ int bs1770gain_transcode(track_t *t, const options_t *options)
   };
 
   int code=-1;
-  FILE *f=options->f;
-  source_cb_t progress=stdout==f?ffsox_source_progress:NULL;
+  bs1770gain_print_t *p=&options->p;
+  FILE *f=p->vmt->session.file(p);
+  source_cb_t progress=f?ffsox_source_progress:NULL;
+  int stereo=options->stereo;
   double drc=options->drc;
   int ai=options->audio;
   int vi=options->video;
@@ -297,7 +408,7 @@ int bs1770gain_transcode(track_t *t, const options_t *options)
     sample_fmt=-1;
   }
 
-  if (ffsox_source_link_create(&si,&so,drc,CODEC_ID,sample_fmt,q)<0) {
+  if (ffsox_source_link_create(&si,&so,stereo,drc,CODEC_ID,sample_fmt,q)<0) {
     DMESSAGE("creating link");
     goto link;
   }
@@ -309,7 +420,11 @@ int bs1770gain_transcode(track_t *t, const options_t *options)
 
   if (!BS1770GAIN_IS_MODE_APPLY(options->mode)) {
     // set the RG/BWF tags.
+#if defined (BS1770GAIN_TAG_PREFIX) // {
+    bs1770gain_write_dict(&so.f.fc->metadata,tags,options);
+#else // } {
     bs1770gain_write_dict(&so.f.fc->metadata,tags);
+#endif // }
   }
 
   if (ffsox_source_seek(&si,options->begin)<0) {
diff --git a/bs1770gain/bs1770gain_tree.c b/bs1770gain/bs1770gain_tree.c
index cc2a37c..449bf71 100755
--- a/bs1770gain/bs1770gain_tree.c
+++ b/bs1770gain/bs1770gain_tree.c
@@ -59,12 +59,13 @@ ifc:
 }
 
 int bs1770gain_tree_analyze(tree_t *tree, const char *odirname,
-    const options_t *options)
+    options_t *options)
 {
   int code=-1;
   const bs1770gain_tree_t *parent=tree->parent;
   const char *ibasename=NULL==parent?NULL:parent->basename;
-  FILE *f=options->f;
+  bs1770gain_print_t *p=&options->p;
+  FILE *f=p->vmt->session.file(p);
   bs1770gain_album_t *album;
   bs1770gain_track_t *track;
   const char *label;
@@ -104,26 +105,20 @@ int bs1770gain_tree_analyze(tree_t *tree, const char *odirname,
   if (NULL==album->head)
     ;
   else if (0==options->dump) {
-    if (NULL==ibasename||0==*ibasename)
-      fprintf(f,"analyzing ...\n");
-    else
-      fprintf(f,"analyzing \"%s\" ...\n",ibasename);
+    p->vmt->album.head(p,album,ibasename);
 
     for (track=album->head;NULL!=track;track=track->next) {
-      fprintf(f,"  [%d/%d] \"%s\"",track->n,album->n,
-          pbu_basename(track->ipath));
-      fprintf(f,": ");
-      fflush(f);
+      p->vmt->track.head(p,track);
 
       memset(&ac,0,sizeof ac);
       ac.path=track->ipath;
       ac.aggregate=&track->aggregate;
+      ac.stereo=options->stereo;
       ac.drc=options->drc;
       ac.momentary.ms=options->momentary.ms;
       ac.momentary.partition=options->momentary.partition;
       ac.shortterm.ms=options->shortterm.ms;
-      ac.shortterm.partition=options->shortterm.partition;
-      ac.f=stdout==f?f:NULL;
+      ac.f=f;
       ac.dump=0;
 
       if (ffsox_analyze(&ac)<0) {
@@ -131,22 +126,24 @@ int bs1770gain_tree_analyze(tree_t *tree, const char *odirname,
         goto analyze;
       }
       else {
-        fprintf(f,stdout==f?"        \n":"\n");
-        bs1770gain_aggregate_print(&track->aggregate,options);
+        p->vmt->track.body(p,&track->aggregate,options);
         ffsox_aggregate_merge(&album->aggregate,&track->aggregate);
       }
+
+      p->vmt->track.tail(p);
     }
 
     if (BS1770GAIN_IS_MODE_ALBUM_TAGS(options->mode)) {
-      fprintf(f,"  [ALBUM]:\n");
-      bs1770gain_aggregate_print(&album->aggregate,options);
+      p->vmt->track.head(p,NULL);
+      p->vmt->track.body(p,&album->aggregate,options);
+      p->vmt->track.tail(p);
     }
 
     if (NULL!=odirname) {
       label=BS1770GAIN_IS_MODE_APPLY(options->mode)?"transcoding":"remuxing";
 
       // print a massage.
-      if (stdout==f) {
+      if (f) {
         if (NULL==ibasename||0==*ibasename)
           fprintf(f,"%s ...\n",label);
         else
@@ -165,7 +162,7 @@ int bs1770gain_tree_analyze(tree_t *tree, const char *odirname,
         bs1770gain_album_copy_file(album,"folder.jpg");
     }
 
-    fprintf(f,"done.\n");
+    p->vmt->album.tail(p);
   }
   else {
     for (track=album->head;NULL!=track;track=track->next)
@@ -183,7 +180,7 @@ album:
 }
 
 int bs1770gain_tree_track(bs1770gain_tree_t *tree, bs1770gain_album_t *album,
-    const bs1770gain_options_t *options)
+    bs1770gain_options_t *options)
 {
   int code =-1;
 
@@ -203,14 +200,16 @@ track:
 }
 
 int bs1770gain_tree_album(const bs1770gain_tree_t *root, const char *odirname,
-    const bs1770gain_options_t *options)
+    bs1770gain_options_t *options)
 {
   int code;
   char *path;
   bs1770gain_tree_t tree;
+  FILE *f;
 
   TRACE_PUSH();
   code =-1;
+  f=options->p.vmt->session.file(&options->p);
 
   if (NULL!=odirname) {
     if (NULL==(path=pbu_extend_path(odirname,root->basename))) {
@@ -226,7 +225,9 @@ int bs1770gain_tree_album(const bs1770gain_tree_t *root, const char *odirname,
     goto dir;
   }
 
-  fprintf(options->f,"%s\n",root->path);
+  if (f)
+    fprintf(f,"%s\n",root->path);
+
   bs1770gain_tree_analyze(&tree,path,options);
   code=0;
 // cleanp:
diff --git a/configure b/configure
index 8aeca8b..5c31da0 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for bs1770gain 0.4.5.
+# Generated by GNU Autoconf 2.69 for bs1770gain 0.4.6-beta2.
 #
 # Report bugs to <pbelkner at users.sf.net>.
 #
@@ -580,8 +580,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='bs1770gain'
 PACKAGE_TARNAME='bs1770gain'
-PACKAGE_VERSION='0.4.5'
-PACKAGE_STRING='bs1770gain 0.4.5'
+PACKAGE_VERSION='0.4.6-beta2'
+PACKAGE_STRING='bs1770gain 0.4.6-beta2'
 PACKAGE_BUGREPORT='pbelkner at users.sf.net'
 PACKAGE_URL='http://bs1770gain.sourceforge.net/'
 
@@ -1278,7 +1278,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures bs1770gain 0.4.5 to adapt to many kinds of systems.
+\`configure' configures bs1770gain 0.4.6-beta2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1344,7 +1344,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of bs1770gain 0.4.5:";;
+     short | recursive ) echo "Configuration of bs1770gain 0.4.6-beta2:";;
    esac
   cat <<\_ACEOF
 
@@ -1440,7 +1440,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-bs1770gain configure 0.4.5
+bs1770gain configure 0.4.6-beta2
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1696,7 +1696,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by bs1770gain $as_me 0.4.5, which was
+It was created by bs1770gain $as_me 0.4.6-beta2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2511,7 +2511,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='bs1770gain'
- VERSION='0.4.5'
+ VERSION='0.4.6-beta2'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -5167,7 +5167,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by bs1770gain $as_me 0.4.5, which was
+This file was extended by bs1770gain $as_me 0.4.6-beta2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -5234,7 +5234,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-bs1770gain config.status 0.4.5
+bs1770gain config.status 0.4.6-beta2
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index 95e8c0d..7201296 100755
--- a/configure.ac
+++ b/configure.ac
@@ -26,7 +26,7 @@
 # AC_CHECK_HEADER (header-file, [action-if-found], [action-if-not-found], [includes = `default-includes'])
 # AC_CHECK_LIB (library, function, [action-if-found], [action-if-not-found], [other-libraries])
 
-AC_INIT([bs1770gain], [0.4.5], [pbelkner at users.sf.net], [], [http://bs1770gain.sourceforge.net/])
+AC_INIT([bs1770gain], [0.4.6-beta2], [pbelkner at users.sf.net], [], [http://bs1770gain.sourceforge.net/])
 AM_INIT_AUTOMAKE([-Wall -Werror foreign])
 AC_PROG_CC
 AC_PROG_RANLIB
diff --git a/lib1770-2/lib1770_pre.c b/lib1770-2/lib1770_pre.c
index 380ffb9..b69fc57 100755
--- a/lib1770-2/lib1770_pre.c
+++ b/lib1770-2/lib1770_pre.c
@@ -20,17 +20,17 @@
 #include <lib1770.h>
 
 ///////////////////////////////////////////////////////////////////////////////
-#if defined (_MSC_VER) // {
+//#if defined (_MSC_VER) // {
 #define lib1770_get(offs,i) \
   ((offs)+(i)<0?LIB1770_BUF_SIZE+(offs)+(i):(offs)+(i))
-#else // } {
-inline int lib1770_get(int offs, int i)
-{
-  int j=offs+i;
-
-  return j<0?LIB1770_BUF_SIZE+j:j;
-}
-#endif // }
+//#else // } {
+//inline int lib1770_get(int offs, int i)
+//{
+//  int j=offs+i;
+//
+//  return j<0?LIB1770_BUF_SIZE+j:j;
+//}
+//#endif // }
 
 #define LIB1770_GET(buf,offs,i) \
     ((buf)[lib1770_get(offs,i)])
diff --git a/lib1770-2/lib1770_stats.c b/lib1770-2/lib1770_stats.c
index c3895ff..84f8032 100755
--- a/lib1770-2/lib1770_stats.c
+++ b/lib1770-2/lib1770_stats.c
@@ -130,8 +130,27 @@ void lib1770_stats_add_sqs(lib1770_stats_t *stats, double wmsq)
 
   if (NULL!=bin) {
     // cumulative moving average.
+    // https://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average
+#if 1 // {
+	//                       x (n) - CMA (n)
+	// CMA (n+1) = CMA (n) + ---------------
+	//                          n + 1
     stats->hist.pass1.wmsq+=(wmsq-stats->hist.pass1.wmsq)
         /(double)(++stats->hist.pass1.count);
+#else // } {
+	//             x + n * CMA (n)
+    // CMA (n+1) = ---------------
+	//                n + 1
+	//
+	//           = x / (n + 1) + CMA (n) * n / (n+1)
+	//
+	double n=stats->hist.pass1.count;
+	double m=n+1;
+
+    stats->hist.pass1.wmsq*=n/m;
+    stats->hist.pass1.wmsq+=wmsq/m;
+	++stats->hist.pass1.count;
+#endif // }
     ++bin->count;
   }
 }
diff --git a/libffsox-2/ffsox.h b/libffsox-2/ffsox.h
index 186fcba..fdc08f1 100755
--- a/libffsox-2/ffsox.h
+++ b/libffsox-2/ffsox.h
@@ -218,14 +218,14 @@ void ffsox_sink_close(ffsox_sink_t *s);
 
 ///////////////////////////////////////////////////////////////////////////////
 enum {
-  FFSOX_AGGREGATE_MOMENTARY_MAXIMUM=1<<1,
-  FFSOX_AGGREGATE_MOMENTARY_MEAN=1<<2,
-  FFSOX_AGGREGATE_MOMENTARY_RANGE=1<<3,
-  FFSOX_AGGREGATE_SHORTTERM_MAXIMUM=1<<4,
-  FFSOX_AGGREGATE_SHORTTERM_MEAN=1<<5,
-  FFSOX_AGGREGATE_SHORTTERM_RANGE=1<<6,
-  FFSOX_AGGREGATE_SAMPLEPEAK=1<<7,
-  FFSOX_AGGREGATE_TRUEPEAK=1<<8,
+  FFSOX_AGGREGATE_MOMENTARY_MAXIMUM=1<<0,
+  FFSOX_AGGREGATE_MOMENTARY_MEAN=1<<1,
+  FFSOX_AGGREGATE_MOMENTARY_RANGE=1<<2,
+  FFSOX_AGGREGATE_SHORTTERM_MAXIMUM=1<<3,
+  FFSOX_AGGREGATE_SHORTTERM_MEAN=1<<4,
+  FFSOX_AGGREGATE_SHORTTERM_RANGE=1<<5,
+  FFSOX_AGGREGATE_SAMPLEPEAK=1<<6,
+  FFSOX_AGGREGATE_TRUEPEAK=1<<7,
   FFSOX_AGGREGATE_MOMENTARY
       =FFSOX_AGGREGATE_MOMENTARY_MAXIMUM
       |FFSOX_AGGREGATE_MOMENTARY_MEAN
@@ -293,6 +293,7 @@ void ffsox_collect_truepeak(void *data, double x);
 struct ffsox_analyze_config {
   const char *path;
   ffsox_aggregate_t *aggregate;
+  int stereo;
   double drc;
   ffsox_block_config_t momentary;
   ffsox_block_config_t shortterm;
@@ -406,7 +407,7 @@ int ffsox_source_append(ffsox_source_t *si, ffsox_packet_consumer_t *pc);
 int ffsox_source_seek(ffsox_source_t *n, int64_t ts);
 
 int ffsox_source_link_create(ffsox_source_t *si, ffsox_sink_t *so,
-    double drc, int codec_id, int sample_fmt, double q);
+    int stereo, double drc, int codec_id, int sample_fmt, double q);
 void ffsox_source_link_cleanup(ffsox_source_t *si);
 void ffsox_source_progress(const ffsox_source_t *si, /* FILE */void *data);
 
@@ -523,9 +524,9 @@ struct ffsox_frame_reader {
 };
 
 int ffsox_frame_reader_create(ffsox_frame_reader_t *fr, ffsox_source_t *si,
-    int stream_index, double drc);
+    int stream_index, int stereo, double drc);
 ffsox_frame_reader_t *ffsox_frame_reader_new(ffsox_source_t *si,
-    int stream_index, double drc);
+    int stream_index, int stereo, double drc);
 const ffsox_frame_reader_vmt_t *ffsox_frame_reader_get_vmt(void);
 
 /// frame_consumer ////////////////////////////////////////////////////////////
diff --git a/libffsox-2/ffsox_analyze.c b/libffsox-2/ffsox_analyze.c
index f7361f0..b9b9213 100755
--- a/libffsox-2/ffsox_analyze.c
+++ b/libffsox-2/ffsox_analyze.c
@@ -52,7 +52,7 @@ int ffsox_analyze(analyze_config_t *ac)
     av_dump_format(si.f.fc,0,si.f.path,0);
 
   // create a frame reader.
-  if (NULL==(fr=ffsox_frame_reader_new(&si,si.ai,ac->drc))) {
+  if (NULL==(fr=ffsox_frame_reader_new(&si,si.ai,ac->stereo,ac->drc))) {
     DMESSAGE("creating frame reader");
     goto fr;
   }
diff --git a/libffsox-2/ffsox_frame_reader.c b/libffsox-2/ffsox_frame_reader.c
index 5565e32..9dd8415 100755
--- a/libffsox-2/ffsox_frame_reader.c
+++ b/libffsox-2/ffsox_frame_reader.c
@@ -22,7 +22,7 @@
 static frame_reader_vmt_t vmt;
 
 int ffsox_frame_reader_create(frame_reader_t *fr, source_t *si,
-    int stream_index, double drc)
+    int stream_index, int stereo, double drc)
 {
   AVDictionary *opts=NULL;
   char buf[32];
@@ -42,9 +42,9 @@ int ffsox_frame_reader_create(frame_reader_t *fr, source_t *si,
     goto find;
   }
 
-  // we want to have stereo.
-  // TODO: should be an option.
-  fr->si.cc->request_channel_layout=AV_CH_LAYOUT_STEREO;
+  // if we want to have stereo:
+  if (stereo)
+    fr->si.cc->request_channel_layout=AV_CH_LAYOUT_STEREO;
 
   if (AV_CODEC_ID_AC3==fr->si.cc->codec_id) {
     // avoid dynamic range compression.
@@ -79,7 +79,7 @@ base:
 }
 
 frame_reader_t *ffsox_frame_reader_new(source_t *si, int stream_index,
-    double drc)
+    int stereo, double drc)
 {
   frame_reader_t *fr;
 
@@ -88,7 +88,7 @@ frame_reader_t *ffsox_frame_reader_new(source_t *si, int stream_index,
     goto malloc;
   }
 
-  if (ffsox_frame_reader_create(fr,si,stream_index,drc)<0) {
+  if (ffsox_frame_reader_create(fr,si,stream_index,stereo,drc)<0) {
     DMESSAGE("creating frame reader");
     goto create;
   }
diff --git a/libffsox-2/ffsox_source_link.c b/libffsox-2/ffsox_source_link.c
index fe459bb..d8d3d25 100755
--- a/libffsox-2/ffsox_source_link.c
+++ b/libffsox-2/ffsox_source_link.c
@@ -36,12 +36,12 @@ pw:
 }
 
 static int ffsox_source_link_codec(source_t *si, sink_t *so, int stream_index,
-    double drc, int codec_id, int sample_fmt, double q)
+    int stereo, double drc, int codec_id, int sample_fmt, double q)
 {
   frame_reader_t *fr=NULL;
   frame_writer_t *fw=NULL;
 
-  if (NULL==(fr=ffsox_frame_reader_new(si,stream_index,drc))) {
+  if (NULL==(fr=ffsox_frame_reader_new(si,stream_index,stereo,drc))) {
     DMESSAGE("creating frame reader");
     goto read;
   }
@@ -63,8 +63,8 @@ read:
   return -1;
 }
 
-int ffsox_source_link_create(source_t *si, sink_t *so, double drc, int codec_id,
-    int sample_fmt, double q)
+int ffsox_source_link_create(source_t *si, sink_t *so, int stereo, double drc,
+    int codec_id, int sample_fmt, double q)
 {
   int i;
 
@@ -77,7 +77,7 @@ int ffsox_source_link_create(source_t *si, sink_t *so, double drc, int codec_id,
         }
       }
       else {
-        if (ffsox_source_link_codec(si,so,i,drc,codec_id,sample_fmt,q)<0) {
+        if (ffsox_source_link_codec(si,so,i,stereo,drc,codec_id,sample_fmt,q)<0) {
           DMESSAGE("codec linking");
           goto link;
         }

-- 
bs1770gain packaging



More information about the pkg-multimedia-commits mailing list