shithub: opus-tools

Download patch

ref: 14f650f64260115098d55fb91e1f83110cb628d3
parent: e112f60ecb9e65729e07f86140fe757186c98f65
author: Timothy B. Terriberry <tterribe@xiph.org>
date: Sat Jul 7 18:48:41 EDT 2012

Initial version of proper multichannel WAV output.

--- a/src/audio-in.c
+++ b/src/audio-in.c
@@ -68,6 +68,7 @@
 #include "opusenc.h"
 #include "speex_resampler.h"
 #include "lpc.h"
+#include "opus_header.h"
 
 #ifdef HAVE_LIBFLAC
 #include "flac.h"
@@ -430,18 +431,6 @@
 
     return 1;
 }
-
-static int wav_permute_matrix[8][8] =
-{
-  {0},              /* 1.0 mono   */
-  {0,1},            /* 2.0 stereo */
-  {0,2,1},          /* 3.0 channel ('wide') stereo */
-  {0,1,2,3},        /* 4.0 discrete quadraphonic */
-  {0,2,1,3,4},      /* 5.0 surround */
-  {0,2,1,4,5,3},    /* 5.1 surround */
-  {0,2,1,5,6,4,3},  /* 6.1 surround */
-  {0,2,1,6,7,4,5,3} /* 7.1 surround (classic theater 8-track) */
-};
 
 int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *oldbuf, int buflen)
 {
--- a/src/opus_header.c
+++ b/src/opus_header.c
@@ -271,3 +271,16 @@
    return p.pos;
 }
 
+/* This is just here because it's a convenient file linked by both opusenc and
+   opusdec (to guarantee this maps stays in sync). */
+const int wav_permute_matrix[8][8] =
+{
+  {0},              /* 1.0 mono   */
+  {0,1},            /* 2.0 stereo */
+  {0,2,1},          /* 3.0 channel ('wide') stereo */
+  {0,1,2,3},        /* 4.0 discrete quadraphonic */
+  {0,2,1,3,4},      /* 5.0 surround */
+  {0,2,1,4,5,3},    /* 5.1 surround */
+  {0,2,1,5,6,4,3},  /* 6.1 surround */
+  {0,2,1,6,7,4,5,3} /* 7.1 surround (classic theater 8-track) */
+};
--- a/src/opus_header.h
+++ b/src/opus_header.h
@@ -46,4 +46,6 @@
 int opus_header_parse(const unsigned char *header, int len, OpusHeader *h);
 int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len);
 
+extern const int wav_permute_matrix[8][8];
+
 #endif
--- a/src/opusdec.c
+++ b/src/opusdec.c
@@ -250,7 +250,7 @@
    }
 }
 
-FILE *out_file_open(char *outFile, int rate, int *channels)
+FILE *out_file_open(char *outFile, int *wav_format, int rate, int mapping_family, int *channels)
 {
    FILE *fout=NULL;
    /*Open output file*/
@@ -361,8 +361,15 @@
             perror(outFile);
             exit(1);
          }
-         if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(outFile+strlen(outFile)-4,".WAV")==0)
-            write_wav_header(fout, rate, *channels);
+         if (*wav_format)
+         {
+            *wav_format = write_wav_header(fout, rate, mapping_family, *channels);
+            if (*wav_format < 0)
+            {
+               fprintf (stderr, "Error writing WAV header.\n");
+               exit(1);
+            }
+         }
       }
    }
    return fout;
@@ -409,7 +416,7 @@
    printf("Copyright (C) 2008-2012 Xiph.Org Foundation\n");
 }
 
-static OpusMSDecoder *process_header(ogg_packet *op, opus_int32 *rate, int *channels, int *preskip, float *gain, int *streams, int quiet)
+static OpusMSDecoder *process_header(ogg_packet *op, opus_int32 *rate, int *mapping_family, int *channels, int *preskip, float *gain, int *streams, int wav_format, int quiet)
 {
    int err;
    OpusMSDecoder *st;
@@ -421,8 +428,9 @@
       return NULL;
    }
 
+   *mapping_family = header.channel_mapping;
    *channels = header.channels;
-   if(*channels>2)fprintf (stderr, "Warning: Opusdec currently gets the channel order wrong for wav output with >2 channels.\n");
+   if(wav_format)adjust_wav_mapping(*mapping_family, *channels, header.stream_map);
 
    if(!*rate)*rate=header.input_sample_rate;
    /*If the rate is unspecified we decode to 48000*/
@@ -558,6 +566,7 @@
    ogg_int64_t audio_size=0;
    float loss_percent=-1;
    int channels=-1;
+   int mapping_family;
    int rate=0;
    int wav_format=0;
    int preskip=0;
@@ -725,7 +734,7 @@
             /*If first packet, process as OPUS header*/
             if (packet_count==0)
             {
-               st = process_header(&op, &rate, &channels, &preskip, &gain, &streams, quiet);
+               st = process_header(&op, &rate, &mapping_family, &channels, &preskip, &gain, &streams, wav_format, quiet);
                if (!st)
                   exit(1);
                gran_offset=preskip;
@@ -744,7 +753,7 @@
                      fprintf(stderr, "resampler error: %s\n", speex_resampler_strerror(err));
                   speex_resampler_skip_zeros(resampler);
                }
-               if(!fout)fout=out_file_open(outFile, rate, &channels);
+               if(!fout)fout=out_file_open(outFile, &wav_format, rate, mapping_family, &channels);
             } else if (packet_count==1)
             {
                if (!quiet)
@@ -828,14 +837,14 @@
          break;
    }
 
-   if (fout && wav_format && audio_size<0x7FFFFFFF)
+   if (fout && wav_format>=0 && audio_size<0x7FFFFFFF)
    {
       if (fseek(fout,4,SEEK_SET)==0)
       {
          int tmp;
-         tmp = le_int(audio_size+36);
+         tmp = le_int(audio_size+20+wav_format);
          if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing end length.\n");
-         if (fseek(fout,32,SEEK_CUR)==0)
+         if (fseek(fout,16+wav_format,SEEK_CUR)==0)
          {
             tmp = le_int(audio_size);
             if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing header length.\n");
--- a/src/wav_io.c
+++ b/src/wav_io.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002 Jean-Marc Valin 
+/* Copyright (C) 2002 Jean-Marc Valin
    File: wav_io.c
    Routines to handle wav (RIFF) headers
 
@@ -5,14 +5,14 @@
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:
-   
+
    - Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
-   
+
    - Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
-   
+
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -33,44 +33,93 @@
 #include <stdio.h>
 #include <string.h>
 #include "wav_io.h"
+#include "opus_header.h"
 
-void write_wav_header(FILE *file, int rate, int channels)
+/* Adjust the stream->channel mapping to ensure the proper output order for
+   WAV files. */
+void adjust_wav_mapping(int mapping_family, int channels, unsigned char *stream_map)
 {
-   opus_int32 itmp;
-   opus_int16 stmp;
+   unsigned char new_stream_map[8];
+   int i;
+   /* If we aren't using one of the defined semantic channel maps, or we have
+      more channels than we know what to do with, use a default 1-1 mapping. */
+   if(mapping_family != 1 || channels > 8)
+      return;
+   for(i = 0; i < channels; i++)
+   {
+      new_stream_map[wav_permute_matrix[channels-1][i]] = stream_map[i];
+   }
+   memcpy(stream_map, new_stream_map, channels*sizeof(*stream_map));
+}
 
-   fprintf (file, "RIFF");
+static size_t fwrite_le32(opus_int32 i32, FILE *file)
+{
+   unsigned char buf[4];
+   buf[0]=(unsigned char)(i32&0xFF);
+   buf[1]=(unsigned char)(i32>>8&0xFF);
+   buf[2]=(unsigned char)(i32>>16&0xFF);
+   buf[3]=(unsigned char)(i32>>24&0xFF);
+   return fwrite(buf,4,1,file);
+}
 
-   itmp = 0x7fffffff;
-   fwrite(&itmp, 4, 1, file);
+static size_t fwrite_le16(int i16, FILE *file)
+{
+   unsigned char buf[2];
+   buf[0]=(unsigned char)(i16&0xFF);
+   buf[1]=(unsigned char)(i16>>8&0xFF);
+   return fwrite(buf,2,1,file);
+}
 
-   fprintf (file, "WAVEfmt ");
+int write_wav_header(FILE *file, int rate, int mapping_family, int channels)
+{
+   int ret;
+   int extensible;
 
-   itmp = le_int(16);
-   fwrite(&itmp, 4, 1, file);
+   /* Multichannel files require a WAVEFORMATEXTENSIBLE header to declare the
+      proper channel meanings. */
+   extensible = mapping_family == 1 && 3 <= channels && channels <= 8;
 
-   stmp = le_short(1);
-   fwrite(&stmp, 2, 1, file);
+   ret = fprintf (file, "RIFF") >= 0;
+   ret &= fwrite_le32 (0x7fffffff, file);
 
-   stmp = le_short(channels);
-   fwrite(&stmp, 2, 1, file);
+   ret &= fprintf (file, "WAVEfmt ") >= 0;
+   ret &= fwrite_le32 (extensible ? 40 : 16, file);
+   ret &= fwrite_le16 (extensible ? 0xfffe : 1, file);
+   ret &= fwrite_le16 (channels, file);
+   ret &= fwrite_le32 (rate, file);
+   ret &= fwrite_le32 (2*channels*rate, file);
+   ret &= fwrite_le16 (2*channels, file);
+   ret &= fwrite_le16 (16, file);
 
-   itmp = le_int(rate);
-   fwrite(&itmp, 4, 1, file);
+   if(extensible)
+   {
+      static const unsigned char ksdataformat_subtype_pcm[16]=
+      {
+        0x01, 0x00, 0x00, 0x00,
+        0x00, 0x00,
+        0x10, 0x00,
+        0x80, 0x00,
+        0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+      };
+      static const int wav_channel_masks[8] =
+      {
+         1,                      /* 1.0 mono */
+         1|2,                    /* 2.0 stereo */
+         1|2|4,                  /* 3.0 channel ('wide') stereo */
+         1|2|16|32,              /* 4.0 discrete quadrophonic */
+         1|2|4|16|32,            /* 5.0 */
+         1|2|4|8|16|32,          /* 5.1 */
+         1|2|4|8|256|512|1024,   /* 6.1 */
+         1|2|4|8|16|32|512|1024, /* 7.1 */
+      };
+      ret &= fwrite_le16 (22, file);
+      ret &= fwrite_le16 (16, file);
+      ret &= fwrite_le32 (wav_channel_masks[channels-1], file);
+      ret &= fwrite (ksdataformat_subtype_pcm, 16, 1, file);
+   }
 
-   itmp = le_int(rate*channels*2);
-   fwrite(&itmp, 4, 1, file);
+   ret &= fprintf (file, "data") >= 0;
+   ret &= fwrite_le32 (0x7fffffff, file);
 
-   stmp = le_short(2*channels);
-   fwrite(&stmp, 2, 1, file);
-
-   stmp = le_short(16);
-   fwrite(&stmp, 2, 1, file);
-
-   fprintf (file, "data");
-
-   itmp = le_int(0x7fffffff);
-   fwrite(&itmp, 4, 1, file);
-
-
+   return !ret ? -1 : extensible ? 40 : 16;
 }
--- a/src/wav_io.h
+++ b/src/wav_io.h
@@ -55,6 +55,8 @@
 #endif
 }
 
-void write_wav_header(FILE *file, int rate, int channels);
+void adjust_wav_mapping(int mapping_family, int channels, unsigned char *stream_map);
+
+int write_wav_header(FILE *file, int rate, int mapping_family, int channels);
 
 #endif