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