ref: 6dd8086d4d1bed7dcd5948651d24649809f90e1f
parent: 5be25490b689959d4c9fc62074e8be2f8eb666ab
author: Gregory Maxwell <[email protected]>
date: Tue Nov 22 13:17:36 EST 2011
First cut at working multichannel support.
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,5 +1,5 @@
CC=gcc
-CFLAGS=-DHAVE_SYS_SOUNDCARD_H -O2 -g -fstack-protector-all -c -Wall -Wextra -DHAVE_GETOPT_H -DEXPORT= -DRANDOM_PREFIX=opustools -DOUTSIDE_SPEEX -DFLOATING_POINT
+CFLAGS=-DHAVE_SYS_SOUNDCARD_H -DVALGRIND -O2 -g -fstack-protector-all -c -Wall -Wextra -DHAVE_GETOPT_H -DEXPORT= -DRANDOM_PREFIX=opustools -DOUTSIDE_SPEEX -DFLOATING_POINT
INCLUDES=-I../../opus/include
all: opusenc opusdec
@@ -7,8 +7,8 @@
.c.o:
$(CC) $(CFLAGS) $(INCLUDES) $< -o $@
-opusenc: opus_header.o wav_io.o opusenc.o resample.o
- gcc opus_header.o wav_io.o opusenc.o resample.o -o opusenc ../../opus/.libs/libopus.a -lm -logg
+opusenc: opus_header.o opusenc.o resample.o audio-in.o
+ gcc opus_header.o audio-in.o opusenc.o resample.o -o opusenc ../../opus/.libs/libopus.a -lm -logg
opusdec: opus_header.o wav_io.o wave_out.o opusdec.o resample.o
gcc wave_out.o opus_header.o wav_io.o opusdec.o resample.o -o opusdec ../../opus/.libs/libopus.a -lm -logg
--- /dev/null
+++ b/src/audio-in.c
@@ -1,0 +1,917 @@
+/* Copyright 2000-2002, Michael Smith <[email protected]>
+ 2010, Monty <[email protected]>
+ AIFF/AIFC support from OggSquish, (c) 1994-1996 Monty <[email protected]>
+ (From GPL code in oggenc relicensed by permission from Monty and Msmith)
+ File: audio-in.c
+
+ 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
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <math.h>
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(X) gettext(X)
+#else
+#define _(X) (X)
+#define textdomain(X)
+#define bindtextdomain(X, Y)
+#endif
+#ifdef gettext_noop
+#define N_(X) gettext_noop(X)
+#else
+#define N_(X) (X)
+#endif
+
+#include "opusenc.h"
+#include "speex_resampler.h"
+
+#ifdef HAVE_LIBFLAC
+#include "flac.h"
+#endif
+
+/* Macros to read header data */
+#define READ_U32_LE(buf) \
+ (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U16_LE(buf) \
+ (((buf)[1]<<8)|((buf)[0]&0xff))
+
+#define READ_U32_BE(buf) \
+ (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
+
+#define READ_U16_BE(buf) \
+ (((buf)[0]<<8)|((buf)[1]&0xff))
+
+/* Define the supported formats here */
+input_format formats[] = {
+ {wav_id, 12, wav_open, wav_close, "wav", N_("WAV file reader")},
+ {aiff_id, 12, aiff_open, wav_close, "aiff", N_("AIFF/AIFC file reader")},
+#ifdef HAVE_LIBFLAC
+ {flac_id, 4, flac_open, flac_close, "flac", N_("FLAC file reader")},
+ {oggflac_id, 32, flac_open, flac_close, "ogg", N_("Ogg FLAC file reader")},
+#endif
+ {NULL, 0, NULL, NULL, NULL, NULL}
+};
+
+input_format *open_audio_file(FILE *in, oe_enc_opt *opt)
+{
+ int j=0;
+ unsigned char *buf=NULL;
+ int buf_size=0, buf_filled=0;
+ int size,ret;
+
+ while(formats[j].id_func)
+ {
+ size = formats[j].id_data_len;
+ if(size >= buf_size)
+ {
+ buf = realloc(buf, size);
+ buf_size = size;
+ }
+
+ if(size > buf_filled)
+ {
+ ret = fread(buf+buf_filled, 1, buf_size-buf_filled, in);
+ buf_filled += ret;
+
+ if(buf_filled < size)
+ { /* File truncated */
+ j++;
+ continue;
+ }
+ }
+
+ if(formats[j].id_func(buf, buf_filled))
+ {
+ /* ok, we now have something that can handle the file */
+ if(formats[j].open_func(in, opt, buf, buf_filled)) {
+ free(buf);
+ return &formats[j];
+ }
+ }
+ j++;
+ }
+
+ free(buf);
+
+ return NULL;
+}
+
+static int seek_forward(FILE *in, unsigned int length)
+{
+ if(fseek(in, length, SEEK_CUR))
+ {
+ /* Failed. Do it the hard way. */
+ unsigned char buf[1024];
+ unsigned int seek_needed = length;
+ int seeked;
+ while(seek_needed > 0)
+ {
+ seeked = fread(buf, 1, seek_needed>1024?1024:seek_needed, in);
+ if(!seeked)
+ return 0; /* Couldn't read more, can't read file */
+ else
+ seek_needed -= seeked;
+ }
+ }
+ return 1;
+}
+
+static int find_wav_chunk(FILE *in, char *type, unsigned int *len)
+{
+ unsigned char buf[8];
+
+ while(1)
+ {
+ if(fread(buf,1,8,in) < 8) /* Suck down a chunk specifier */
+ {
+ fprintf(stderr, _("Warning: Unexpected EOF in reading WAV header\n"));
+ return 0; /* EOF before reaching the appropriate chunk */
+ }
+
+ if(memcmp(buf, type, 4))
+ {
+ *len = READ_U32_LE(buf+4);
+ if(!seek_forward(in, *len))
+ return 0;
+
+ buf[4] = 0;
+ fprintf(stderr, _("Skipping chunk of type \"%s\", length %d\n"), buf, *len);
+ }
+ else
+ {
+ *len = READ_U32_LE(buf+4);
+ return 1;
+ }
+ }
+}
+
+static int find_aiff_chunk(FILE *in, char *type, unsigned int *len)
+{
+ unsigned char buf[8];
+ int restarted = 0;
+
+ while(1)
+ {
+ if(fread(buf,1,8,in) <8)
+ {
+ if(!restarted) {
+ /* Handle out of order chunks by seeking back to the start
+ * to retry */
+ restarted = 1;
+ fseek(in, 12, SEEK_SET);
+ continue;
+ }
+ fprintf(stderr, _("Warning: Unexpected EOF in AIFF chunk\n"));
+ return 0;
+ }
+
+ *len = READ_U32_BE(buf+4);
+
+ if(memcmp(buf,type,4))
+ {
+ if((*len) & 0x1)
+ (*len)++;
+
+ if(!seek_forward(in, *len))
+ return 0;
+ }
+ else
+ return 1;
+ }
+}
+
+double read_IEEE80(unsigned char *buf)
+{
+ int s=buf[0]&0xff;
+ int e=((buf[0]&0x7f)<<8)|(buf[1]&0xff);
+ double f=((unsigned long)(buf[2]&0xff)<<24)|
+ ((buf[3]&0xff)<<16)|
+ ((buf[4]&0xff)<<8) |
+ (buf[5]&0xff);
+
+ if(e==32767)
+ {
+ if(buf[2]&0x80)
+ return HUGE_VAL; /* Really NaN, but this won't happen in reality */
+ else
+ {
+ if(s)
+ return -HUGE_VAL;
+ else
+ return HUGE_VAL;
+ }
+ }
+
+ f=ldexp(f,32);
+ f+= ((buf[6]&0xff)<<24)|
+ ((buf[7]&0xff)<<16)|
+ ((buf[8]&0xff)<<8) |
+ (buf[9]&0xff);
+
+ return ldexp(f, e-16446);
+}
+
+/* AIFF/AIFC support adapted from the old OggSQUISH application */
+int aiff_id(unsigned char *buf, int len)
+{
+ if(len<12) return 0; /* Truncated file, probably */
+
+ if(memcmp(buf, "FORM", 4))
+ return 0;
+
+ if(memcmp(buf+8, "AIF",3))
+ return 0;
+
+ if(buf[11]!='C' && buf[11]!='F')
+ return 0;
+
+ return 1;
+}
+
+static int aiff_permute_matrix[6][6] =
+{
+ {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 (WARN) */
+ {0,2,1,3,4}, /* 5.0 surround (WARN) */
+ {0,1,2,3,4,5}, /* 5.1 surround (WARN)*/
+};
+
+int aiff_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen)
+{
+ int aifc; /* AIFC or AIFF? */
+ unsigned int len;
+ unsigned char *buffer;
+ unsigned char buf2[8];
+ aiff_fmt format;
+ aifffile *aiff = malloc(sizeof(aifffile));
+ int i;
+ (void)buflen;/*unused*/
+
+ if(buf[11]=='C')
+ aifc=1;
+ else
+ aifc=0;
+
+ if(!find_aiff_chunk(in, "COMM", &len))
+ {
+ fprintf(stderr, _("Warning: No common chunk found in AIFF file\n"));
+ return 0; /* EOF before COMM chunk */
+ }
+
+ if(len < 18)
+ {
+ fprintf(stderr, _("Warning: Truncated common chunk in AIFF header\n"));
+ return 0; /* Weird common chunk */
+ }
+
+ buffer = alloca(len);
+
+ if(fread(buffer,1,len,in) < len)
+ {
+ fprintf(stderr, _("Warning: Unexpected EOF in reading AIFF header\n"));
+ return 0;
+ }
+
+ format.channels = READ_U16_BE(buffer);
+ format.totalframes = READ_U32_BE(buffer+2);
+ format.samplesize = READ_U16_BE(buffer+6);
+ format.rate = (int)read_IEEE80(buffer+8);
+
+ aiff->bigendian = 1;
+
+ if(aifc)
+ {
+ if(len < 22)
+ {
+ fprintf(stderr, _("Warning: AIFF-C header truncated.\n"));
+ return 0;
+ }
+
+ if(!memcmp(buffer+18, "NONE", 4))
+ {
+ aiff->bigendian = 1;
+ }
+ else if(!memcmp(buffer+18, "sowt", 4))
+ {
+ aiff->bigendian = 0;
+ }
+ else
+ {
+ fprintf(stderr, _("Warning: Can't handle compressed AIFF-C (%c%c%c%c)\n"), *(buffer+18), *(buffer+19), *(buffer+20), *(buffer+21));
+ return 0; /* Compressed. Can't handle */
+ }
+ }
+
+ if(!find_aiff_chunk(in, "SSND", &len))
+ {
+ fprintf(stderr, _("Warning: No SSND chunk found in AIFF file\n"));
+ return 0; /* No SSND chunk -> no actual audio */
+ }
+
+ if(len < 8)
+ {
+ fprintf(stderr, _("Warning: Corrupted SSND chunk in AIFF header\n"));
+ return 0;
+ }
+
+ if(fread(buf2,1,8, in) < 8)
+ {
+ fprintf(stderr, _("Warning: Unexpected EOF reading AIFF header\n"));
+ return 0;
+ }
+
+ format.offset = READ_U32_BE(buf2);
+ format.blocksize = READ_U32_BE(buf2+4);
+
+ if( format.blocksize == 0 &&
+ (format.samplesize == 16 || format.samplesize == 8))
+ {
+ /* From here on, this is very similar to the wav code. Oh well. */
+
+ opt->rate = format.rate;
+ opt->channels = format.channels;
+ opt->read_samples = wav_read; /* Similar enough, so we use the same */
+ opt->total_samples_per_channel = format.totalframes;
+
+ aiff->f = in;
+ aiff->samplesread = 0;
+ aiff->channels = format.channels;
+ aiff->samplesize = format.samplesize;
+ aiff->totalsamples = format.totalframes;
+
+ if(aiff->channels>3)
+ fprintf(stderr,"WARNING: AIFF[-C] files with greater than three channels use\n"
+ "speaker locations incompatable with Vorbis suppound definitions.\n"
+ "Not performaing channel location mapping.\n");
+
+ opt->readdata = (void *)aiff;
+
+ aiff->channel_permute = malloc(aiff->channels * sizeof(int));
+ if (aiff->channels <= 6)
+ /* Where we know the mappings, use them. */
+ memcpy(aiff->channel_permute, aiff_permute_matrix[aiff->channels-1],
+ sizeof(int) * aiff->channels);
+ else
+ /* Use a default 1-1 mapping */
+ for (i=0; i < aiff->channels; i++)
+ aiff->channel_permute[i] = i;
+
+ seek_forward(in, format.offset); /* Swallow some data */
+ return 1;
+ }
+ else
+ {
+ fprintf(stderr,
+ _("Warning: OggEnc does not support this type of AIFF/AIFC file\n"
+ " Must be 8 or 16 bit PCM.\n"));
+ return 0;
+ }
+}
+
+int wav_id(unsigned char *buf, int len)
+{
+ unsigned int flen;
+
+ if(len<12) return 0; /* Something screwed up */
+
+ if(memcmp(buf, "RIFF", 4))
+ return 0; /* Not wave */
+
+ flen = READ_U32_LE(buf+4); /* We don't use this */
+
+ if(memcmp(buf+8, "WAVE",4))
+ return 0; /* RIFF, but not wave */
+
+ 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)
+{
+ unsigned char buf[40];
+ unsigned int len;
+ int samplesize;
+ wav_fmt format;
+ wavfile *wav = malloc(sizeof(wavfile));
+ int i;
+ (void)buflen;/*unused*/
+ (void)oldbuf;/*unused*/
+
+ /* Ok. At this point, we know we have a WAV file. Now we have to detect
+ * whether we support the subtype, and we have to find the actual data
+ * We don't (for the wav reader) need to use the buffer we used to id this
+ * as a wav file (oldbuf)
+ */
+
+ if(!find_wav_chunk(in, "fmt ", &len))
+ return 0; /* EOF */
+
+ if(len < 16)
+ {
+ fprintf(stderr, _("Warning: Unrecognised format chunk in WAV header\n"));
+ return 0; /* Weird format chunk */
+ }
+
+ /* A common error is to have a format chunk that is not 16, 18 or
+ * 40 bytes in size. This is incorrect, but not fatal, so we only
+ * warn about it instead of refusing to work with the file.
+ * Please, if you have a program that's creating format chunks of
+ * sizes other than 16 or 18 bytes in size, report a bug to the
+ * author.
+ */
+ if(len!=16 && len!=18 && len!=40)
+ fprintf(stderr,
+ _("Warning: INVALID format chunk in wav header.\n"
+ " Trying to read anyway (may not work)...\n"));
+
+ if(len>40)len=40;
+
+ if(fread(buf,1,len,in) < len)
+ {
+ fprintf(stderr, _("Warning: Unexpected EOF in reading WAV header\n"));
+ return 0;
+ }
+
+ format.format = READ_U16_LE(buf);
+ format.channels = READ_U16_LE(buf+2);
+ format.samplerate = READ_U32_LE(buf+4);
+ format.bytespersec = READ_U32_LE(buf+8);
+ format.align = READ_U16_LE(buf+12);
+ format.samplesize = READ_U16_LE(buf+14);
+
+ if(format.format == -2) /* WAVE_FORMAT_EXTENSIBLE */
+ {
+ if(len<40)
+ {
+ fprintf(stderr,"ERROR: Extended WAV format header invalid (too small)\n");
+ return 0;
+ }
+
+ format.mask = READ_U32_LE(buf+20);
+ /* warn the user if the format mask is not a supported/expected type */
+ switch(format.mask){
+ case 1539: /* 4.0 using side surround instead of back */
+ fprintf(stderr,"WARNING: WAV file uses side surround instead of rear for quadraphonic;\n"
+ "remapping side speakers to rear in encoding.\n");
+ break;
+ case 1551: /* 5.1 using side instead of rear */
+ fprintf(stderr,"WARNING: WAV file uses side surround instead of rear for 5.1;\n"
+ "remapping side speakers to rear in encoding.\n");
+ break;
+ case 319: /* 6.1 using rear instead of side */
+ fprintf(stderr,"WARNING: WAV file uses rear surround instead of side for 6.1;\n"
+ "remapping rear speakers to side in encoding.\n");
+ break;
+ case 255: /* 7.1 'Widescreen' */
+ fprintf(stderr,"WARNING: WAV file is a 7.1 'Widescreen' channel mapping;\n"
+ "remapping speakers to Vorbis 7.1 format.\n");
+ break;
+ case 0: /* default/undeclared */
+ case 1: /* mono */
+ case 3: /* stereo */
+ case 51: /* quad */
+ case 55: /* 5.0 */
+ case 63: /* 5.1 */
+ case 1807: /* 6.1 */
+ case 1599: /* 7.1 */
+ break;
+ default:
+ fprintf(stderr,"WARNING: Unknown WAV surround channel mask: %d\n"
+ "blindly mapping speakers using default SMPTE/ITU ordering.\n",
+ format.mask);
+ break;
+ }
+ format.format = READ_U16_LE(buf+24);
+ }
+
+ if(!find_wav_chunk(in, "data", &len))
+ return 0; /* EOF */
+
+ if(format.format == 1)
+ {
+ samplesize = format.samplesize/8;
+ opt->read_samples = wav_read;
+ }
+ else if(format.format == 3)
+ {
+ samplesize = 4;
+ opt->read_samples = wav_ieee_read;
+ }
+ else
+ {
+ fprintf(stderr,
+ _("ERROR: Wav file is unsupported type (must be standard PCM\n"
+ " or type 3 floating point PCM\n"));
+ return 0;
+ }
+
+ if(format.align != format.channels * samplesize) {
+ /* This is incorrect according to the spec. Warn loudly, then ignore
+ * this value.
+ */
+ fprintf(stderr, _("Warning: WAV 'block alignment' value is incorrect, "
+ "ignoring.\n"
+ "The software that created this file is incorrect.\n"));
+ }
+
+ if(format.samplesize == samplesize*8 &&
+ (format.samplesize == 24 || format.samplesize == 16 ||
+ format.samplesize == 8 ||
+ (format.samplesize == 32 && format.format == 3)))
+ {
+ /* OK, good - we have the one supported format,
+ now we want to find the size of the file */
+ opt->rate = format.samplerate;
+ opt->channels = format.channels;
+
+ wav->f = in;
+ wav->samplesread = 0;
+ wav->bigendian = 0;
+ wav->channels = format.channels; /* This is in several places. The price
+ of trying to abstract stuff. */
+ wav->samplesize = format.samplesize;
+
+ if(len)
+ {
+ opt->total_samples_per_channel = len/(format.channels*samplesize);
+ }
+ else
+ {
+ long pos;
+ pos = ftell(in);
+ if(fseek(in, 0, SEEK_END) == -1)
+ {
+ opt->total_samples_per_channel = 0; /* Give up */
+ }
+ else
+ {
+ opt->total_samples_per_channel = (ftell(in) - pos)/
+ (format.channels*samplesize);
+ fseek(in,pos, SEEK_SET);
+ }
+ }
+ wav->totalsamples = opt->total_samples_per_channel;
+
+ opt->readdata = (void *)wav;
+
+ wav->channel_permute = malloc(wav->channels * sizeof(int));
+ if (wav->channels <= 8)
+ /* Where we know the mappings, use them. */
+ memcpy(wav->channel_permute, wav_permute_matrix[wav->channels-1],
+ sizeof(int) * wav->channels);
+ else
+ /* Use a default 1-1 mapping */
+ for (i=0; i < wav->channels; i++)
+ wav->channel_permute[i] = i;
+
+ return 1;
+ }
+ else
+ {
+ fprintf(stderr,
+ _("ERROR: Wav file is unsupported subformat (must be 8,16, or 24 bit PCM\n"
+ "or floating point PCM\n"));
+ return 0;
+ }
+}
+
+long wav_read(void *in, float *buffer, int samples)
+{
+ wavfile *f = (wavfile *)in;
+ int sampbyte = f->samplesize / 8;
+ signed char *buf = alloca(samples*sampbyte*f->channels);
+ long bytes_read = fread(buf, 1, samples*sampbyte*f->channels, f->f);
+ int i,j;
+ long realsamples;
+ int *ch_permute = f->channel_permute;
+
+ if(f->totalsamples && f->samplesread +
+ bytes_read/(sampbyte*f->channels) > f->totalsamples) {
+ bytes_read = sampbyte*f->channels*(f->totalsamples - f->samplesread);
+ }
+
+ realsamples = bytes_read/(sampbyte*f->channels);
+ f->samplesread += realsamples;
+
+ if(f->samplesize==8)
+ {
+ unsigned char *bufu = (unsigned char *)buf;
+ for(i = 0; i < realsamples; i++)
+ {
+ for(j=0; j < f->channels; j++)
+ {
+ buffer[i*f->channels+j]=((int)(bufu[i*f->channels + ch_permute[j]])-128)/128.0f;
+ }
+ }
+ }
+ else if(f->samplesize==16)
+ {
+ if(!f->bigendian)
+ {
+ for(i = 0; i < realsamples; i++)
+ {
+ for(j=0; j < f->channels; j++)
+ {
+ buffer[i*f->channels+j] = ((buf[i*2*f->channels + 2*ch_permute[j] + 1]<<8) |
+ (buf[i*2*f->channels + 2*ch_permute[j]] & 0xff))/32768.0f;
+ }
+ }
+ }
+ else
+ {
+ for(i = 0; i < realsamples; i++)
+ {
+ for(j=0; j < f->channels; j++)
+ {
+ buffer[i*f->channels+j]=((buf[i*2*f->channels + 2*ch_permute[j]]<<8) |
+ (buf[i*2*f->channels + 2*ch_permute[j] + 1] & 0xff))/32768.0f;
+ }
+ }
+ }
+ }
+ else if(f->samplesize==24)
+ {
+ if(!f->bigendian) {
+ for(i = 0; i < realsamples; i++)
+ {
+ for(j=0; j < f->channels; j++)
+ {
+ buffer[i*f->channels+j] = ((buf[i*3*f->channels + 3*ch_permute[j] + 2] << 16) |
+ (((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j] + 1] << 8) |
+ (((unsigned char *)buf)[i*3*f->channels + 3*ch_permute[j]] & 0xff))
+ / 8388608.0f;
+
+ }
+ }
+ }
+ else {
+ fprintf(stderr, _("Big endian 24 bit PCM data is not currently "
+ "supported, aborting.\n"));
+ return 0;
+ }
+ }
+ else {
+ fprintf(stderr, _("Internal error: attempt to read unsupported "
+ "bitdepth %d\n"), f->samplesize);
+ return 0;
+ }
+
+ return realsamples;
+}
+
+long wav_ieee_read(void *in, float *buffer, int samples)
+{
+ wavfile *f = (wavfile *)in;
+ float *buf = alloca(samples*4*f->channels); /* de-interleave buffer */
+ long bytes_read = fread(buf,1,samples*4*f->channels, f->f);
+ int i,j;
+ long realsamples;
+
+ if(f->totalsamples && f->samplesread +
+ bytes_read/(4*f->channels) > f->totalsamples)
+ bytes_read = 4*f->channels*(f->totalsamples - f->samplesread);
+ realsamples = bytes_read/(4*f->channels);
+ f->samplesread += realsamples;
+
+ for(i=0; i < realsamples; i++)
+ for(j=0; j < f->channels; j++)
+ buffer[i*f->channels+j] = buf[i*f->channels + f->channel_permute[j]];
+
+ return realsamples;
+}
+
+void wav_close(void *info)
+{
+ wavfile *f = (wavfile *)info;
+ free(f->channel_permute);
+
+ free(f);
+}
+
+int raw_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen)
+{
+ wav_fmt format; /* fake wave header ;) */
+ wavfile *wav = malloc(sizeof(wavfile));
+ int i;
+ (void)buf;/*unused*/
+ (void)buflen;/*unused*/
+
+ /* construct fake wav header ;) */
+ format.format = 2;
+ format.channels = opt->channels;
+ format.samplerate = opt->rate;
+ format.samplesize = opt->samplesize;
+ format.bytespersec = opt->channels * opt->rate * opt->samplesize / 8;
+ format.align = format.bytespersec;
+ wav->f = in;
+ wav->samplesread = 0;
+ wav->bigendian = opt->endianness;
+ wav->channels = format.channels;
+ wav->samplesize = opt->samplesize;
+ wav->totalsamples = 0;
+ wav->channel_permute = malloc(wav->channels * sizeof(int));
+ for (i=0; i < wav->channels; i++)
+ wav->channel_permute[i] = i;
+
+ opt->read_samples = wav_read;
+ opt->readdata = (void *)wav;
+ opt->total_samples_per_channel = 0; /* raw mode, don't bother */
+ return 1;
+}
+
+typedef struct {
+ audio_read_func real_reader;
+ void *real_readdata;
+ int channels;
+ float scale_factor;
+} scaler;
+
+static long read_scaler(void *data, float *buffer, int samples) {
+ scaler *d = data;
+ long in_samples = d->real_reader(d->real_readdata, buffer, samples);
+ int i;
+
+ for(i=0; i < d->channels*in_samples; i++) {
+ buffer[i] *= d->scale_factor;
+ }
+
+ return in_samples;
+}
+
+void setup_scaler(oe_enc_opt *opt, float scale) {
+ scaler *d = calloc(1, sizeof(scaler));
+
+ d->real_reader = opt->read_samples;
+ d->real_readdata = opt->readdata;
+
+ opt->read_samples = read_scaler;
+ opt->readdata = d;
+ d->channels = opt->channels;
+ d->scale_factor = scale;
+}
+
+typedef struct {
+ audio_read_func real_reader;
+ void *real_readdata;
+ int channels;
+ int *extra_samples;
+} padder;
+
+static long read_padder(void *data, float *buffer, int samples) {
+ padder *d = data;
+ long in_samples = d->real_reader(d->real_readdata, buffer, samples);
+ int i, extra=0;
+
+ if(in_samples<samples){
+ extra=samples-in_samples;
+ if(extra>*d->extra_samples)extra=*d->extra_samples;
+ *d->extra_samples-=extra;
+ }
+ for(i=0;i<extra*d->channels;i++)buffer[(in_samples*d->channels)+i]=0;
+ return in_samples+extra;
+}
+
+void setup_padder(oe_enc_opt *opt) {
+ padder *d = calloc(1, sizeof(padder));
+
+ d->real_reader = opt->read_samples;
+ d->real_readdata = opt->readdata;
+
+ opt->read_samples = read_padder;
+ opt->readdata = d;
+ d->channels = opt->channels;
+ d->extra_samples = &opt->extraout;
+}
+
+void clear_padder(oe_enc_opt *opt) {
+ padder *d = opt->readdata;
+
+ opt->read_samples = d->real_reader;
+ opt->readdata = d->real_readdata;
+
+ free(d);
+}
+
+typedef struct {
+ SpeexResamplerState *resampler;
+ audio_read_func real_reader;
+ void *real_readdata;
+ float *bufs;
+ int channels;
+ int bufpos;
+ int bufsize;
+ int done;
+} resampler;
+
+static long read_resampled(void *d, float *buffer, int samples)
+{
+ resampler *rs = d;
+ int out_samples=0;
+ float *pcmbuf;
+ int *inbuf;
+ pcmbuf=rs->bufs;
+ inbuf=&rs->bufpos;
+ while(out_samples<samples){
+ int i;
+ int reading, ret;
+ unsigned in_len, out_len;
+ reading=1024-*inbuf;
+ ret=rs->real_reader(rs->real_readdata, pcmbuf+*inbuf*rs->channels, reading);
+ *inbuf+=ret;
+ in_len=*inbuf;
+ out_len=samples-out_samples;
+ speex_resampler_process_interleaved_float(rs->resampler, pcmbuf, &in_len, buffer+out_samples*rs->channels, &out_len);
+ if(ret==0&&in_len==0){
+ for(i=out_samples*rs->channels;i<samples*rs->channels;i++)buffer[i]=0;
+ return out_samples;
+ }
+ out_samples+=out_len;
+ for(i=0;i<rs->channels*(*inbuf-(long int)in_len);i++)pcmbuf[i]=pcmbuf[i+rs->channels*in_len];
+ *inbuf-=in_len;
+ }
+ return out_samples;
+}
+
+int setup_resample(oe_enc_opt *opt, int complexity, long outfreq) {
+ resampler *rs = calloc(1, sizeof(resampler));
+ int err;
+
+ rs->bufsize = 2048; /* Shrug */
+ rs->bufpos = 0;
+
+ rs->real_reader = opt->read_samples;
+ rs->real_readdata = opt->readdata;
+ rs->channels = opt->channels;
+ rs->done = 0;
+ rs->resampler = speex_resampler_init(rs->channels, opt->rate, outfreq, complexity, &err);
+ if(err!=0)fprintf(stderr, _("resampler error: %s\n"), speex_resampler_strerror(err));
+
+ opt->skip+=speex_resampler_get_output_latency(rs->resampler);
+
+ rs->bufs = malloc(sizeof(float) * rs->bufsize * opt->channels);
+
+ opt->read_samples = read_resampled;
+ opt->readdata = rs;
+ if(opt->total_samples_per_channel)
+ opt->total_samples_per_channel = (int)((float)opt->total_samples_per_channel *
+ ((float)outfreq/(float)opt->rate));
+ opt->rate = outfreq;
+
+ return 0;
+}
+
+void clear_resample(oe_enc_opt *opt) {
+ resampler *rs = opt->readdata;
+
+ opt->read_samples = rs->real_reader;
+ opt->readdata = rs->real_readdata;
+ speex_resampler_destroy(rs->resampler);
+
+ free(rs->bufs);
+
+ free(rs);
+}
--- a/src/opus_header.c
+++ b/src/opus_header.c
@@ -9,9 +9,9 @@
- Pre-skip (16 bits)
- Sampling rate (32 bits)
- Gain in dB (16 bits, S7.8)
- - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping,
+ - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping,
2..254: reserved, 255: multistream with no mapping)
-
+
- if (mapping != 0)
- N = totel number of streams (8 bits)
- M = number of paired streams (8 bits)
@@ -23,7 +23,7 @@
else
- right
- else
- - stream = byte-2*M
+ - stream = byte-M
*/
typedef struct {
@@ -109,7 +109,7 @@
ROPacket p;
unsigned char ch;
opus_uint16 shortval;
-
+
p.data = packet;
p.maxlen = len;
p.pos = 0;
@@ -117,11 +117,11 @@
read_chars(&p, (unsigned char*)str, 8);
if (strcmp(str, "OpusHead")!=0)
return 0;
-
+
if (!read_chars(&p, &ch, 1))
return 0;
h->version = ch;
-
+
if (!read_chars(&p, &ch, 1))
return 0;
h->channels = ch;
@@ -142,17 +142,17 @@
if (!read_chars(&p, &ch, 1))
return 0;
h->channel_mapping = ch;
-
+
if (h->channel_mapping != 0)
{
if (!read_chars(&p, &ch, 1))
return 0;
h->nb_streams = ch;
-
+
if (!read_chars(&p, &ch, 1))
return 0;
h->nb_coupled = ch;
-
+
/* Multi-stream support */
for (i=0;i<h->channels;i++)
{
@@ -159,6 +159,11 @@
if (!read_chars(&p, &h->stream_map[i], 1))
return 0;
}
+ } else {
+ h->nb_streams = 1;
+ h->nb_coupled = h->channels>1;
+ h->stream_map[0]=0;
+ h->stream_map[1]=1;
}
if (h->version==0 && p.pos != len)
return 0;
@@ -170,7 +175,7 @@
int i;
Packet p;
unsigned char ch;
-
+
p.data = packet;
p.maxlen = len;
p.pos = 0;
@@ -180,11 +185,11 @@
ch = 0;
if (!write_chars(&p, &ch, 1))
return 0;
-
+
ch = h->channels;
if (!write_chars(&p, &ch, 1))
return 0;
-
+
if (!write_uint16(&p, h->preskip))
return 0;
@@ -203,7 +208,7 @@
ch = h->nb_streams;
if (!write_chars(&p, &ch, 1))
return 0;
-
+
ch = h->nb_coupled;
if (!write_chars(&p, &ch, 1))
return 0;
--- a/src/opusdec.c
+++ b/src/opusdec.c
@@ -94,7 +94,7 @@
#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
((buf[base+2]<<16)&0xff0000)| \
((buf[base+1]<<8)&0xff00)| \
- (buf[base]&0xff))
+ (buf[base]&0xff))
typedef struct shapestate shapestate;
struct shapestate {
@@ -394,7 +394,6 @@
int err;
OpusMSDecoder *st;
OpusHeader header;
- unsigned char mapping[256] = {0,1};
if (opus_header_parse(op->packet, op->bytes, &header)==0)
{
@@ -402,12 +401,6 @@
return NULL;
}
- if (header.channels>2 || header.channels<1)
- {
- fprintf (stderr, "Unsupported number of channels: %d\n", header.channels);
- return NULL;
- }
-
*channels = header.channels;
if(!*rate)*rate=header.input_sample_rate;
@@ -419,7 +412,7 @@
}
*preskip = header.preskip;
- st = opus_multistream_decoder_create(48000, header.channels, 1, header.channels==2 ? 1 : 0, mapping, &err);
+ st = opus_multistream_decoder_create(48000, header.channels, header.nb_streams, header.nb_coupled, header.stream_map, &err);
if(err != OPUS_OK){
fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err));
return NULL;
@@ -438,11 +431,7 @@
{
fprintf (stderr, "Decoding %d Hz audio", *rate);
- if (*channels==1)
- fprintf (stderr, " (mono");
- else
- fprintf (stderr, " (stereo");
- fprintf(stderr, ")\n");
+ fprintf(stderr, " (%d channel%s)\n",*channels,*channels>1?"s":"");
}
return st;
@@ -452,8 +441,8 @@
{
int i,tmp_skip;
unsigned out_len;
- short out[MAX_FRAME_SIZE*2];
- float buf[MAX_FRAME_SIZE*2];
+ short out[MAX_FRAME_SIZE*channels];
+ float buf[MAX_FRAME_SIZE*channels];
float *output;
do {
@@ -500,7 +489,7 @@
int option_index = 0;
char *inFile, *outFile;
FILE *fin, *fout=NULL;
- float output[MAX_FRAME_SIZE*2];
+ float *output;
int frame_size=0;
OpusMSDecoder *st=NULL;
int packet_count=0;
@@ -540,6 +529,7 @@
SpeexResamplerState *resampler=NULL;
float gain=1;
+ output=0;
shapemem.a_buf=0;
shapemem.b_buf=0;
shapemem.mute=960;
@@ -661,10 +651,10 @@
ogg_stream_init(&os, ogg_page_serialno(&og));
stream_init = 1;
}
- if (ogg_page_serialno(&og) != os.serialno) {
- /* so all streams are read. */
- ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
- }
+ if (ogg_page_serialno(&og) != os.serialno) {
+ /* so all streams are read. */
+ ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
+ }
/*Add page to the bitstream*/
ogg_stream_pagein(&os, &og);
page_granule = ogg_page_granulepos(&og);
@@ -671,11 +661,11 @@
/*Extract all available packets*/
while (!eos && ogg_stream_packetout(&os, &op) == 1)
{
- if (op.bytes>=8 && !memcmp(op.packet, "OpusHead", 8)) {
- opus_serialno = os.serialno;
- }
- if (opus_serialno == -1 || os.serialno != opus_serialno)
- break;
+ if (op.bytes>=8 && !memcmp(op.packet, "OpusHead", 8)) {
+ opus_serialno = os.serialno;
+ }
+ if (opus_serialno == -1 || os.serialno != opus_serialno)
+ break;
/*If first packet, process as OPUS header*/
if (packet_count==0)
{
@@ -687,6 +677,9 @@
shapemem.a_buf=calloc(channels,sizeof(float)*4);
shapemem.b_buf=calloc(channels,sizeof(float)*4);
shapemem.fs=rate;
+ if(output)
+ free(output);
+ output=malloc(sizeof(float)*MAX_FRAME_SIZE*channels);
/* Converting preskip to output sampling rate */
preskip = preskip*(rate/48000.);
if (!st)
@@ -822,6 +815,8 @@
if(shapemem.a_buf)free(shapemem.a_buf);
if(shapemem.b_buf)free(shapemem.b_buf);
+
+ if(output)free(output);
if (close_in)
fclose(fin);
--- a/src/opusenc.c
+++ b/src/opusenc.c
@@ -39,9 +39,6 @@
#include <sys/time.h>
#include <math.h>
-#include "opus_header.h"
-#include "speex_resampler.h"
-
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
@@ -52,6 +49,9 @@
#include <ogg/ogg.h>
#include "wav_io.h"
+#include "opus_header.h"
+#include "opusenc.h"
+
#if defined WIN32 || defined _WIN32
/* We need the following two to set stdout to binary */
#include <io.h>
@@ -58,6 +58,15 @@
#include <fcntl.h>
#endif
+#ifdef VALGRIND
+#include <valgrind/memcheck.h>
+#define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
+#define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
+#else
+#define VG_UNDEF(x,y)
+#define VG_CHECK(x,y)
+#endif
+
void comment_init(char **comments, int* length, char *vendor_string);
void comment_add(char **comments, int* length, char *tag, char *val);
@@ -70,103 +79,10 @@
return written;
}
-#define MAX_FRAME_SIZE (960*6)
#define MAX_FRAME_BYTES 61295
#define IMIN(a,b) ((a) < (b) ? (a) : (b)) /**< Minimum int value. */
#define IMAX(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum int value. */
-/* Convert input audio bits, endians and channels */
-static int read_samples_pcm(FILE *fin,int frame_size, int bits, int channels,
- int lsb, float * input, char *buff, opus_int32 *size,
- int *extra_samples)
-{
- short s[MAX_FRAME_SIZE*2];
- unsigned char *in=(unsigned char*)s;
- int i;
- int nb_read;
- int extra=0;
-
- /*Read input audio*/
- if(buff){
- for(i=0;i<12;i++)in[i]=buff[i];
- nb_read=fread(in+12, 1, bits/8*channels*frame_size-12, fin) + 12;
- if(size)*size+=12;
- }else{
- int tmp=frame_size;
- if(size&&tmp>*size)tmp=*size;
- nb_read=fread(in, 1, bits/8*channels* tmp, fin);
- }
- nb_read/=bits/8*channels;
-
- /* Make sure to return *extra_samples zeros at the end */
- if(nb_read<frame_size){
- extra=frame_size-nb_read;
- if(extra > *extra_samples)
- extra=*extra_samples;
- *extra_samples-=extra;
- }
- /*fprintf(stderr, "%d\n", nb_read);*/
- if(nb_read==0 && extra==0)return 0;
-
- if(bits==8){
- /* Convert 8->16 bits */
- for(i=frame_size*channels-1;i>=0;i--){
- s[i]=(in[i]<<8)^0x8000;
- }
- }else{
- /* convert to our endian format */
- for(i=0;i<frame_size*channels;i++){
- if(lsb)s[i]=le_short(s[i]);
- else s[i]=be_short(s[i]);
- }
- }
-
- /* copy to float input buffer */
- for(i=0;i<frame_size*channels;i++)input[i]=s[i]*(1/32768.f);
- for(i=nb_read*channels;i<frame_size*channels;i++)input[i]=0;
-
- nb_read+=extra;
- if(size){
- int tmp=bits/8*channels*nb_read;
- if(tmp>*size)*size=0;
- else *size-=tmp;
- }
- return nb_read;
-}
-
-static int read_samples(FILE *fin,int frame_size, int bits, int channels,
- int lsb, float * input, char *buff, opus_int32 *size,
- SpeexResamplerState *resampler, int *extra_samples)
-{
- if(resampler){
- /* FIXME: This is a hack, get rid of these static variables */
- static float pcmbuf[2048];
- static int inbuf=0;
- int out_samples=0;
- while(out_samples<frame_size){
- int i;
- int reading, ret;
- unsigned in_len, out_len;
- reading=1024-inbuf;
- ret=read_samples_pcm(fin, reading, bits, channels, lsb, pcmbuf+inbuf*channels, buff, size, extra_samples);
- inbuf+=ret;
- in_len=inbuf;
- out_len=frame_size-out_samples;
- speex_resampler_process_interleaved_float(resampler, pcmbuf, &in_len, input+out_samples*channels, &out_len);
- if(ret==0&&in_len==0){
- for(i=out_samples*channels;i<frame_size*channels;i++)input[i]=0;
- return out_samples;
- }
- out_samples+=out_len;
- for(i=0;i<channels*(inbuf-in_len);i++)pcmbuf[i]=pcmbuf[i+channels*in_len];
- inbuf-=in_len;
- }
- return out_samples;
- }else{
- return read_samples_pcm(fin, frame_size, bits, channels, lsb, input, buff, size, extra_samples);
- }
-}
-
void version(const char *version)
{
printf("opusenc (using %s)\n",version);
@@ -183,7 +99,7 @@
{
printf("Usage: opusenc [options] input_file output_file.oga\n");
printf("\n");
- printf("Encodes input_file using Opus. It can read the WAV or raw files.\n");
+ printf("Encodes input_file using Opus. It can read the WAV, AIFF, or raw files.\n");
printf("\nGeneral options:\n");
printf(" -h, --help This help\n");
printf(" -v, --version Version information\n");
@@ -190,8 +106,7 @@
printf(" --quiet Quiet mode\n");
printf("\n");
printf("input_file can be:\n");
- printf(" filename.wav wav file\n");
- printf(" filename.* Raw PCM file (any extension other than .wav)\n");
+ printf(" filename.wav file\n");
printf(" - stdin\n");
printf("\n");
printf("output_file can be:\n");
@@ -208,7 +123,8 @@
printf(" --framesize n Maximum frame size in milliseconds\n");
printf(" (2.5, 5, 10, 20, 40, 60, default: 20)\n");
printf(" --expect-loss Percentage packet loss to expect (default: 0)\n");
- printf(" --force-mono Force mono output\n");
+ printf(" --downmix-mono Downmix to mono\n");
+ printf(" --downmix-stereo Downmix to to stereo (if >2 channels)\n");
printf(" --max-ogg-delay n Maximum container delay in milliseconds\n");
printf(" (0-1000, default: 1000)\n");
printf("\nDiagnostic options:\n");
@@ -215,20 +131,18 @@
printf(" --save-range file Saves check values for every frame to a file\n");
printf(" --set-ctl-int x=y Pass the encoder control x with value y (advanced)\n");
printf(" This may be used multiple times\n");
+ printf(" --uncoupled Use one mono stream per channel\n");
printf("\nMetadata options:\n");
printf(" --comment Add the given string as an extra comment\n");
printf(" This may be used multiple times\n");
printf(" --author Author of this track\n");
printf(" --title Title for this track\n");
- printf("\nRaw input options:\n");
- printf(" --rate n Sampling rate for raw input\n");
- printf(" --mono Consider raw input as mono\n");
- printf(" --stereo Consider raw input as stereo\n");
- printf(" --le Raw input is little-endian\n");
- printf(" --be Raw input is big-endian\n");
- printf(" --8bit Raw input is 8-bit unsigned\n");
- printf(" --16bit Raw input is 16-bit signed\n");
- printf("Default raw PCM input is 48kHz, 16-bit, little-endian, stereo\n");
+ printf("\nInput options:\n");
+ printf(" --raw Raw input\n");
+ printf(" --raw-bits n Set bits/sample for raw input (default: 16)\n");
+ printf(" --raw-rate n Set sampling rate for raw input (default: 48000)\n");
+ printf(" --raw-chan n Set number of channels for raw input (default: 2)\n");
+ printf(" --raw-endianness n 1 for bigendian, 0 for little (defaults to 0)\n");
}
static inline void print_time(double seconds)
@@ -247,32 +161,10 @@
int main(int argc, char **argv)
{
- int i, c, nb_samples;
int option_index=0;
- char *inFile, *outFile;
- FILE *fin, *fout, *frange;
- char *range_file;
- float input[MAX_FRAME_SIZE*2];
- opus_int32 frame_size=960;
- opus_int64 nb_encoded, bytes_written=0, pages_out=0, total_bytes=0, total_samples=0;
- struct timeval start_time, stop_time;
- int last_spin_len=0;
- time_t last_spin=0;
- int quiet=0;
- int nbBytes;
- OpusMSEncoder *st;
- unsigned char *packet;
- int with_hard_cbr=0;
- int with_cvbr=0;
- opus_int32 peak_bytes=0;
- opus_int32 min_bytes=MAX_FRAME_BYTES;
- int expect_loss=0;
- int force_mono=0;
- int *opt_ctls_ctlval;
- int opt_ctls=0;
- int max_ogg_delay=48000;
struct option long_options[] =
{
+ {"quiet", no_argument, NULL, 0},
{"bitrate", required_argument, NULL, 0},
{"hard-cbr",no_argument,NULL, 0},
{"vbr",no_argument,NULL, 0},
@@ -280,18 +172,18 @@
{"comp", required_argument, NULL, 0},
{"framesize", required_argument, NULL, 0},
{"expect-loss", required_argument, NULL, 0},
- {"force-mono",no_argument,NULL, 0},
+ {"downmix-mono",no_argument,NULL, 0},
+ {"downmix-stereo",no_argument,NULL, 0},
{"max-ogg-delay", required_argument, NULL, 0},
{"save-range", required_argument, NULL, 0},
{"set-ctl-int", required_argument, NULL, 0},
+ {"uncoupled", no_argument, NULL, 0},
{"help", no_argument, NULL, 0},
- {"quiet", no_argument, NULL, 0},
- {"le", no_argument, NULL, 0},
- {"be", no_argument, NULL, 0},
- {"8bit", no_argument, NULL, 0},
- {"16bit", no_argument, NULL, 0},
- {"mono", no_argument, NULL, 0},
- {"stereo", no_argument, NULL, 0},
+ {"raw", no_argument, NULL, 0},
+ {"raw-bits", required_argument, NULL, 0},
+ {"raw-rate", required_argument, NULL, 0},
+ {"raw-chan", required_argument, NULL, 0},
+ {"raw-endianness", required_argument, NULL, 0},
{"rate", required_argument, NULL, 0},
{"music", no_argument, NULL, 0},
{"speech", no_argument, NULL, 0},
@@ -302,40 +194,77 @@
{"title", required_argument, NULL, 0},
{0, 0, 0, 0}
};
- opus_int32 rate=48000;
- opus_int32 coding_rate=48000;
- opus_int32 size;
- int chan=1;
- int fmt=16;
- int lsb=1;
+ int i, ret;
+ OpusMSEncoder *st;
+ const char *opus_version;
+ unsigned char *packet;
+ float *input;
+ /*I/O*/
+ oe_enc_opt inopt;
+ input_format *in_format;
+ char *inFile;
+ char *outFile;
+ char *range_file;
+ FILE *fin;
+ FILE *fout;
+ FILE *frange;
ogg_stream_state os;
- ogg_page og;
- ogg_packet op;
- ogg_int64_t last_granulepos=0;
- int ret, result;
- int id=-1;
- OpusHeader header;
- char vendor_string[64];
- char *comments;
- int comments_length;
- int close_in=0, close_out=0;
- int eos=0;
- opus_int32 bitrate=-1;
- char first_bytes[12];
- int wave_input=0;
- opus_int32 lookahead=0;
- int bytes_per_packet=-1;
- int complexity=10;
- const char *opus_version;
- SpeexResamplerState *resampler=NULL;
- int extra_samples;
- int signal=OPUS_AUTO;
- unsigned char mapping[256]={0, 1};
- int err;
+ ogg_page og;
+ ogg_packet op;
+ ogg_int64_t last_granulepos=0;
+ ogg_int32_t id=-1;
+ int eos=0;
+ OpusHeader header;
+ int comments_length;
+ char vendor_string[64];
+ char *comments;
+ /*Counters*/
+ opus_int64 nb_encoded=0;
+ opus_int64 bytes_written=0;
+ opus_int64 pages_out=0;
+ opus_int64 total_bytes=0;
+ opus_int64 total_samples=0;
+ opus_int32 nbBytes;
+ opus_int32 nb_samples;
+ opus_int32 peak_bytes=0;
+ opus_int32 min_bytes=MAX_FRAME_BYTES;
+ struct timeval start_time;
+ struct timeval stop_time;
+ time_t last_spin=0;
+ int last_spin_len=0;
+ /*Settings*/
+ int quiet=0;
+ opus_int32 bitrate=-1;
+ opus_int32 rate=48000;
+ opus_int32 coding_rate=48000;
+ opus_int32 frame_size=960;
+ int chan=2;
+ int with_hard_cbr=0;
+ int with_cvbr=0;
+ int signal=OPUS_AUTO;
+ int expect_loss=0;
+ int complexity=10;
+ int downmix=0;
+ int uncoupled=0;
+ int *opt_ctls_ctlval;
+ int opt_ctls=0;
+ int max_ogg_delay=48000; /*@48kHz*/
+ opus_int32 lookahead=0;
+ unsigned char mapping[256];
+ int force_narrow=0;
opt_ctls_ctlval=NULL;
frange=NULL;
range_file=NULL;
+ in_format=NULL;
+ inopt.channels=chan;
+ inopt.rate=coding_rate=rate;
+ inopt.samplesize=16;
+ inopt.endianness=0;
+ inopt.rawmode=0;
+
+ for(i=0;i<256;i++)mapping[i]=i;
+
opus_version=opus_get_version_string();
snprintf(vendor_string, sizeof(vendor_string), "%s\n",opus_version);
comment_init(&comments, &comments_length, vendor_string);
@@ -348,6 +277,7 @@
/*Process command-line options*/
while(1){
+ int c;
c=getopt_long(argc, argv, "hv",
long_options, &option_index);
if(c==-1)
@@ -355,7 +285,9 @@
switch(c){
case 0:
- if(strcmp(long_options[option_index].name,"bitrate")==0){
+ if(strcmp(long_options[option_index].name,"quiet")==0){
+ quiet=1;
+ }else if(strcmp(long_options[option_index].name,"bitrate")==0){
bitrate=atof(optarg)*1000.;
}else if(strcmp(long_options[option_index].name,"hard-cbr")==0){
with_hard_cbr=1;
@@ -369,8 +301,6 @@
}else if(strcmp(long_options[option_index].name,"help")==0){
usage();
exit(0);
- }else if(strcmp(long_options[option_index].name,"quiet")==0){
- quiet=1;
}else if(strcmp(long_options[option_index].name,"version")==0){
version(opus_version);
exit(0);
@@ -377,26 +307,33 @@
}else if(strcmp(long_options[option_index].name,"version-short")==0){
version_short(opus_version);
exit(0);
- }else if(strcmp(long_options[option_index].name,"le")==0){
- lsb=1;
- }else if(strcmp(long_options[option_index].name,"be")==0){
- lsb=0;
- }else if(strcmp(long_options[option_index].name,"8bit")==0){
- fmt=8;
- }else if(strcmp(long_options[option_index].name,"16bit")==0){
- fmt=16;
- }else if(strcmp(long_options[option_index].name,"stereo")==0){
- chan=2;
- }else if(strcmp(long_options[option_index].name,"mono")==0){
- chan=1;
- } else if(strcmp(long_options[option_index].name,"rate")==0){
- rate=atoi(optarg);
+ }else if(strcmp(long_options[option_index].name,"raw")==0){
+ inopt.rawmode=1;
+ }else if(strcmp(long_options[option_index].name,"raw-bits")==0){
+ inopt.rawmode=1;
+ inopt.samplesize=atoi(optarg);
+ if(inopt.samplesize!=8&&inopt.samplesize!=16&&inopt.samplesize!=24){
+ fprintf(stderr,"Invalid bit-depth: %s\n",optarg);
+ fprintf(stderr,"--raw-bits must be one of 8,16, or 24\n");
+ exit(1);
+ }
+ }else if(strcmp(long_options[option_index].name,"raw-rate")==0){
+ inopt.rawmode=1;
+ inopt.rate=atoi(optarg);
+ }else if(strcmp(long_options[option_index].name,"raw-chan")==0){
+ inopt.rawmode=1;
+ inopt.channels=atoi(optarg);
+ }else if(strcmp(long_options[option_index].name,"raw-endianness")==0){
+ inopt.rawmode=1;
+ inopt.endianness=atoi(optarg);
}else if(strcmp(long_options[option_index].name,"music")==0){
signal=OPUS_SIGNAL_MUSIC;
}else if(strcmp(long_options[option_index].name,"speech")==0){
signal=OPUS_SIGNAL_VOICE;
- }else if(strcmp(long_options[option_index].name,"force-mono")==0){
- force_mono=1;
+ }else if(strcmp(long_options[option_index].name,"downmix-mono")==0){
+ downmix=1;
+ }else if(strcmp(long_options[option_index].name,"downmix-stereo")==0){
+ downmix=2;
}else if(strcmp(long_options[option_index].name,"expect-loss")==0){
expect_loss=atoi(optarg);
if(expect_loss>100||expect_loss<0){
@@ -458,6 +395,8 @@
exit(1);
}
range_file=optarg;
+ }else if(strcmp(long_options[option_index].name,"uncoupled")==0){
+ uncoupled=1;
}else if(strcmp(long_options[option_index].name,"comment")==0){
if(!strchr(optarg,'=')){
fprintf(stderr, "Invalid comment: %s\n", optarg);
@@ -513,16 +452,26 @@
perror(inFile);
exit(1);
}
- close_in=1;
}
- err=fread(first_bytes, 1, 12, fin);
- if(strncmp(first_bytes,"RIFF",4)==0 && strncmp(first_bytes,"RIFF",4)==0){
- if(read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)exit(1);
- wave_input=1;
- lsb=1; /* CHECK: exists big-endian .wav ?? */
+ if(inopt.rawmode){
+ static input_format raw_format = {NULL, 0, raw_open, wav_close, "raw",N_("RAW file reader")};
+ in_format = &raw_format;
+ in_format->open_func(fin, &inopt, NULL, 0);
+ }else in_format=open_audio_file(fin,&inopt);
+
+ if(!in_format){
+ fprintf(stderr,"Error parsing input file: %s\n",inFile);
+ exit(1);
}
+ rate=inopt.rate;
+ chan=inopt.channels;
+ inopt.skip=0;
+
+ /*In order to code the complete length we'll need to do a little padding*/
+ setup_padder(&inopt);
+
if(rate>24000)coding_rate=48000;
else if(rate>16000)coding_rate=24000;
else if(rate>12000)coding_rate=12000;
@@ -530,105 +479,126 @@
frame_size=frame_size/(48000/coding_rate);
- bitrate=bitrate>0?bitrate:32000*(force_mono?1:chan)+32000;
- bytes_per_packet=MAX_FRAME_BYTES;
- if(bitrate>2048000||bitrate<500){
- fprintf(stderr,"Error: Bitrate %d bits/sec is insane.\nDid you mistake bits for kilobits?\n",bitrate);
- fprintf(stderr,"--bitrate values from 6-256 kbit/sec per channel are meaningful.\n");
- exit(1);
- }
- bitrate=IMIN((force_mono?1:chan)*256000,bitrate);
+ /*Scale the resampler complexity, but only for 48000 output because
+ the near-cutoff behavior matters a lot more at lower rates.*/
+ if(rate!=coding_rate)setup_resample(&inopt,coding_rate==48000?complexity/2:5,coding_rate);
- if(rate!=coding_rate){
- /*if(!quiet)fprintf(stderr, "Resampling from %dHz to %dHz before encoding\n", rate, coding_rate);*/
- resampler=speex_resampler_init(chan, rate, coding_rate, 5, &err);
- if(err!=0)fprintf(stderr, "resampler error: %s\n", speex_resampler_strerror(err));
- /* Using pre-skip to skip the zeros */
- /*speex_resampler_skip_zeros(resampler);*/
+ /*OggOpus headers*/ /*FIXME: broke forcemono*/
+ header.channels=chan;
+ header.nb_streams=header.channels;
+ header.nb_coupled=0;
+ if(header.channels<=8&&!uncoupled){
+ static const unsigned char opusenc_streams[8][10]={
+ /*Coupled, NB_bitmap, mapping...*/
+ /*1*/ {0, 0, 0},
+ /*2*/ {1, 0, 0,1},
+ /*3*/ {1, 0, 0,2,1},
+ /*4*/ {2, 0, 0,1,2,3},
+ /*5*/ {2, 0, 0,4,1,2,3},
+ /*6*/ {2,1<<5, 0,4,1,2,3,5},
+ /*7*/ {2,1<<6, 0,4,1,2,3,5,6},
+ /*6*/ {3,1<<7, 0,6,1,2,3,4,5,7}
+ };
+ for(i=0;i<header.channels;i++)mapping[i]=opusenc_streams[header.channels-1][i+2];
+ force_narrow=opusenc_streams[header.channels-1][1];
+ header.nb_coupled=opusenc_streams[header.channels-1][0];
+ header.nb_streams=header.channels-header.nb_coupled;
}
+ header.channel_mapping=header.channels>8?255:header.nb_streams>1;
+ if(header.channel_mapping>0)for(i=0;i<header.channels;i++)header.stream_map[i]=mapping[i];
+ /* 0 dB gain is the recommended unless you know what you're doing */
+ header.gain=0;
+ header.input_sample_rate=rate;
-
/*Initialize OPUS encoder*/
- st=opus_multistream_encoder_create(coding_rate, chan, 1, chan==2, mapping, OPUS_APPLICATION_AUDIO, &err);
- if(err!=OPUS_OK){
- fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err));
+ st=opus_multistream_encoder_create(coding_rate, chan, header.nb_streams, header.nb_coupled, mapping, OPUS_APPLICATION_AUDIO, &ret);
+ if(ret!=OPUS_OK){
+ fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(ret));
exit(1);
}
- err=opus_multistream_encoder_ctl(st, OPUS_SET_SIGNAL(signal));
- if(err!=OPUS_OK){
- fprintf(stderr,"OPUS_SET_SIGNAL returned: %s\n",opus_strerror(err));
+ ret=opus_multistream_encoder_ctl(st, OPUS_SET_SIGNAL(signal));
+ if(ret!=OPUS_OK){
+ fprintf(stderr,"OPUS_SET_SIGNAL returned: %s\n",opus_strerror(ret));
exit(1);
}
- header.channels=force_mono?1:chan;
- header.channel_mapping=0;
- header.nb_streams=1;
- header.nb_coupled=1;
- /* 0 dB gain is the recommended unless you know what you're doing */
- header.gain=0;
- header.input_sample_rate=rate;
- if(force_mono){
- OpusEncoder *oe;
- opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(0,&oe));
- err=opus_encoder_ctl(oe, OPUS_SET_FORCE_CHANNELS(1));
- if(err!=OPUS_OK){
- fprintf(stderr,"OPUS_SET_FORCE_CHANNELS returned: %s\n",opus_strerror(err));
- exit(1);
+ if(force_narrow!=0){
+ for(i=0;i<header.nb_streams;i++){
+ if(force_narrow&(1<<i)){
+ OpusEncoder *oe;
+ opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(i,&oe));
+ ret=opus_encoder_ctl(oe, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
+ if(ret!=OPUS_OK){
+ fprintf(stderr,"OPUS_SET_MAX_BANDWIDTH on stream %d returned: %s\n",i,opus_strerror(ret));
+ exit(1);
+ }
+ }
}
}
- err=opus_multistream_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
- if(err!=OPUS_OK){
- fprintf(stderr,"OPUS_SET_BITRATE returned: %s\n",opus_strerror(err));
+ bitrate=bitrate>0?bitrate:64000*header.nb_streams+32000*header.nb_coupled;
+
+ if(bitrate>2048000||bitrate<500){
+ fprintf(stderr,"Error: Bitrate %d bits/sec is insane.\nDid you mistake bits for kilobits?\n",bitrate);
+ fprintf(stderr,"--bitrate values from 6-256 kbit/sec per channel are meaningful.\n");
exit(1);
}
+ bitrate=IMIN(chan*256000,bitrate);
- err=opus_multistream_encoder_ctl(st, OPUS_SET_VBR(!with_hard_cbr));
- if(err!=OPUS_OK){
- fprintf(stderr,"OPUS_SET_VBR returned: %s\n",opus_strerror(err));
+ ret=opus_multistream_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
+ if(ret!=OPUS_OK){
+ fprintf(stderr,"OPUS_SET_BITRATE returned: %s\n",opus_strerror(ret));
exit(1);
}
+ ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR(!with_hard_cbr));
+ if(ret!=OPUS_OK){
+ fprintf(stderr,"OPUS_SET_VBR returned: %s\n",opus_strerror(ret));
+ exit(1);
+ }
+
if(!with_hard_cbr){
- err=opus_multistream_encoder_ctl(st, OPUS_SET_VBR_CONSTRAINT(with_cvbr));
- if(err!=OPUS_OK){
- fprintf(stderr,"OPUS_SET_VBR_CONSTRAINT returned: %s\n",opus_strerror(err));
+ ret=opus_multistream_encoder_ctl(st, OPUS_SET_VBR_CONSTRAINT(with_cvbr));
+ if(ret!=OPUS_OK){
+ fprintf(stderr,"OPUS_SET_VBR_CONSTRAINT returned: %s\n",opus_strerror(ret));
exit(1);
}
}
- err=opus_multistream_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
- if(err!=OPUS_OK){
- fprintf(stderr,"OPUS_SET_COMPLEXITY returned: %s\n",opus_strerror(err));
+ ret=opus_multistream_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
+ if(ret!=OPUS_OK){
+ fprintf(stderr,"OPUS_SET_COMPLEXITY returned: %s\n",opus_strerror(ret));
exit(1);
}
- err=opus_multistream_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(expect_loss));
- if(err!=OPUS_OK){
- fprintf(stderr,"OPUS_SET_PACKET_LOSS_PERC returned: %s\n",opus_strerror(err));
+ ret=opus_multistream_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(expect_loss));
+ if(ret!=OPUS_OK){
+ fprintf(stderr,"OPUS_SET_PACKET_LOSS_PERC returned: %s\n",opus_strerror(ret));
exit(1);
}
for(i=0;i<opt_ctls;i++){
- err=opus_multistream_encoder_ctl(st,opt_ctls_ctlval[i<<1],opt_ctls_ctlval[(i<<1)+1]);
- if(err!=OPUS_OK){
- fprintf(stderr,"opus_multistream_encoder_ctl(st,%d,%d) returned: %s\n",opt_ctls_ctlval[i<<1],opt_ctls_ctlval[(i<<1)+1],opus_strerror(err));
+ ret=opus_multistream_encoder_ctl(st,opt_ctls_ctlval[i<<1],opt_ctls_ctlval[(i<<1)+1]);
+ if(ret!=OPUS_OK){
+ fprintf(stderr,"opus_multistream_encoder_ctl(st,%d,%d) returned: %s\n",opt_ctls_ctlval[i<<1],opt_ctls_ctlval[(i<<1)+1],opus_strerror(ret));
exit(1);
}
}
/*We do the lookahead check late so user CTLs can change it*/
- err=opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&lookahead));
- if(err!=OPUS_OK){
- fprintf(stderr,"OPUS_GET_LOOKAHEAD returned: %s\n",opus_strerror(err));
+ ret=opus_multistream_encoder_ctl(st, OPUS_GET_LOOKAHEAD(&lookahead));
+ if(ret!=OPUS_OK){
+ fprintf(stderr,"OPUS_GET_LOOKAHEAD returned: %s\n",opus_strerror(ret));
exit(1);
}
- header.preskip=lookahead*(48000/coding_rate);
- if(resampler)header.preskip+=speex_resampler_get_output_latency(resampler)*(48000/coding_rate);
+ inopt.skip+=lookahead;
+ /*Regardless of the rate we're coding at the ogg timestamping/skip is
+ always timed at 48000.*/
+ header.preskip=inopt.skip*(48000./coding_rate);
/* Extra samples that need to be read to compensate for the pre-skip */
- extra_samples=(int)header.preskip*(rate/48000.);
+ inopt.extraout=(int)header.preskip*(rate/48000.);
if(!quiet){
int opus_app;
@@ -641,8 +611,12 @@
fprintf(stderr,"-----------------------------------------------------\n");
fprintf(stderr," Input: %0.6gkHz %d channel%s\n",
header.input_sample_rate/1000.,chan,chan<2?"":"s");
- fprintf(stderr," Output: %d channel%s, %0.2gms packets, %0.6gkbit/sec%s\n",
- force_mono?1:chan,(force_mono?1:chan)<2?"":"s",
+ fprintf(stderr," Output: %d channel%s (",header.channels,header.channels<2?"":"s");
+ if(header.nb_coupled>0)fprintf(stderr,"%d coupled",header.nb_coupled*2);
+ if(header.nb_streams-header.nb_coupled>0)fprintf(stderr,
+ "%s%d uncoupled",header.nb_coupled>0?", ":"",
+ header.nb_streams-header.nb_coupled);
+ fprintf(stderr,")\n %0.2gms packets, %0.6gkbit/sec%s\n",
frame_size/(coding_rate/1000.), bitrate/1000.,
with_hard_cbr?" CBR":with_cvbr?" CVBR":" VBR");
fprintf(stderr," Pregap: %d\n",header.preskip);
@@ -662,7 +636,6 @@
perror(outFile);
exit(1);
}
- close_out=1;
}
/*Write header*/
@@ -677,8 +650,8 @@
op.packetno=0;
ogg_stream_packetin(&os, &op);
- while((result=ogg_stream_flush(&os, &og))){
- if(!result)break;
+ while((ret=ogg_stream_flush(&os, &og))){
+ if(!ret)break;
ret=oe_write_page(&og, fout);
if(ret!=og.header_len+og.body_len){
fprintf(stderr,"Error: failed writing header to output stream\n");
@@ -698,8 +671,8 @@
}
/* writing the rest of the opus header packets */
- while((result=ogg_stream_flush(&os, &og))){
- if(!result)break;
+ while((ret=ogg_stream_flush(&os, &og))){
+ if(!ret)break;
ret=oe_write_page(&og, fout);
if(ret!=og.header_len + og.body_len){
fprintf(stderr,"Error: failed writing header to output stream\n");
@@ -711,9 +684,14 @@
free(comments);
- if(!wave_input)nb_samples=read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL, resampler, &extra_samples);
- else nb_samples=read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size, resampler, &extra_samples);
+ input=malloc(sizeof(float)*frame_size*chan);
+ if(input==NULL){
+ fprintf(stderr,"Error: couldn't allocate sample buffer.\n");
+ exit(1);
+ }
+ nb_samples = inopt.read_samples(inopt.readdata,input,frame_size);
+
if(nb_samples==0)eos=1;
total_samples+=nb_samples;
nb_encoded=-header.preskip;
@@ -722,11 +700,21 @@
id++;
/*Encode current frame*/
- nbBytes=opus_multistream_encode_float(st, input, frame_size, packet, bytes_per_packet);
+ if(nb_samples<frame_size){
+ /*FIXME*/
+ //printf("X: %d %d\n",nb_samples,frame_size);
+ for(i=nb_samples*chan;i<frame_size*chan;i++)input[i]=0;
+ }
+
+ VG_UNDEF(packet,MAX_FRAME_BYTES);
+ VG_CHECK(input,sizeof(float)*chan*frame_size);
+ nbBytes=opus_multistream_encode_float(st, input, frame_size, packet, MAX_FRAME_BYTES);
if(nbBytes<0){
fprintf(stderr, "Encoding failed: %s. Aborting.\n", opus_strerror(nbBytes));
break;
}
+ VG_CHECK(packet,nbBytes);
+ VG_UNDEF(input,sizeof(float)*chan*frame_size);
nb_encoded+=frame_size;
total_bytes+=nbBytes;
peak_bytes=IMAX(nbBytes,peak_bytes);
@@ -743,14 +731,13 @@
bw_strings[opus_packet_get_bandwidth(packet)-OPUS_BANDWIDTH_NARROWBAND],
packet[0]&4?'S':'M',opus_packet_get_samples_per_frame(packet,48000));
for(i=0;i<streams;i++){
- err=opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(i,&oe));
- err=opus_encoder_ctl(oe,OPUS_GET_FINAL_RANGE(&rng));
+ ret=opus_multistream_encoder_ctl(st,OPUS_MULTISTREAM_GET_ENCODER_STATE(i,&oe));
+ ret=opus_encoder_ctl(oe,OPUS_GET_FINAL_RANGE(&rng));
fprintf(frange,"%llu%c",(unsigned long long)rng,i+1==streams?'\n':' ');
}
}
- if(wave_input)nb_samples=read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size, resampler, &extra_samples);
- else nb_samples=read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL, resampler, &extra_samples);
+ nb_samples = inopt.read_samples(inopt.readdata,input,frame_size);
if(nb_samples==0)eos=1;
if(eos && total_samples<=nb_encoded)op.e_o_s=1;
else op.e_o_s=0;
@@ -763,6 +750,7 @@
/*Is this redundent?*/
if(eos && total_samples<=nb_encoded)op.e_o_s=1;
else op.e_o_s=0;
+ /*FIXME: this doesn't cope with the frame size changing*/
op.granulepos=(id+1)*frame_size*(48000/coding_rate);
if(op.granulepos>total_samples)op.granulepos=total_samples*(48000/coding_rate);
op.packetno=2+id;
@@ -810,8 +798,15 @@
bitrate*(1.-tweight);
}else estbitrate=nbBytes*8*((double)coding_rate/frame_size);
for(i=0;i<last_spin_len;i++)fprintf(stderr," ");
- snprintf(sbuf,54,"\r[%c] %02d:%02d:%02d.%02d %4.3gx realtime, %5.4gkbit/s\r",
- spinner[last_spin&3],
+ if(inopt.total_samples_per_channel>0 && inopt.total_samples_per_channel<nb_encoded){
+ snprintf(sbuf,54,"\r[%c] %02d%% ",spinner[last_spin&3],
+ (int)floor(nb_encoded/(double)(inopt.total_samples_per_channel+inopt.skip)*100.));
+ }else{
+ snprintf(sbuf,54,"\r[%c] ",spinner[last_spin&3]);
+ }
+ last_spin_len=strlen(sbuf);
+ snprintf(sbuf+last_spin_len,54-last_spin_len,
+ "%02d:%02d:%02d.%02d %4.3gx realtime, %5.4gkbit/s\r",
(int)(coded_seconds/3600),(int)(coded_seconds/60)%60,
(int)(coded_seconds)%60,(int)(coded_seconds*100)%100,
coded_seconds/wall_time,
@@ -849,7 +844,7 @@
fprintf(stderr,"\n Runtime:");
print_time(wall_time);
fprintf(stderr,"\n (%0.4gx realtime)\n",coded_seconds/wall_time);
- fprintf(stderr," Wrote: %lld bytes, %d packets, %lld pages\n",total_bytes,id,pages_out);
+ fprintf(stderr," Wrote: %lld bytes, %d packets, %lld pages\n",total_bytes,id+1,pages_out);
fprintf(stderr," Bitrate: %0.6gkbit/s (without overhead)\n",
total_bytes*8.0/(coded_seconds)/1000.0);
fprintf(stderr," Rate range: %0.6gkbit/s to %0.6gkbit/s\n (%d to %d bytes per packet)\n",
@@ -859,15 +854,20 @@
#ifdef OLD_LIBOGG
if(max_ogg_delay>(frame_size*(48000/coding_rate)*4))fprintf(stderr," (use libogg 1.2.2 or later for lower overhead)\n");
#endif
+ fprintf(stderr,"\n");
}
opus_multistream_encoder_destroy(st);
ogg_stream_clear(&os);
free(packet);
+ free(input);
if(opt_ctls)free(opt_ctls_ctlval);
- if(rate!=coding_rate)speex_resampler_destroy(resampler);
- if(close_in)fclose(fin);
- if(close_out)fclose(fout);
+
+ if(rate!=coding_rate)clear_resample(&inopt);
+ clear_padder(&inopt);
+ in_format->close_func(inopt.readdata);
+ if(fin)fclose(fin);
+ if(fout)fclose(fout);
if(frange)fclose(frange);
return 0;
}
--- /dev/null
+++ b/src/opusenc.h
@@ -1,0 +1,97 @@
+#ifndef __OPUSENC_H
+#define __OPUSENC_H
+
+#ifdef ENABLE_NLS
+#include <libintl.h>
+#define _(X) gettext(X)
+#else
+#define _(X) (X)
+#define textdomain(X)
+#define bindtextdomain(X, Y)
+#endif
+#ifdef gettext_noop
+#define N_(X) gettext_noop(X)
+#else
+#define N_(X) (X)
+#endif
+
+typedef long (*audio_read_func)(void *src, float *buffer, int samples);
+
+typedef struct
+{
+ audio_read_func read_samples;
+ void *readdata;
+ long total_samples_per_channel;
+ int rawmode;
+ int channels;
+ long rate;
+ int samplesize;
+ int endianness;
+ char *infilename;
+ int ignorelength;
+ int skip;
+ int extraout;
+} oe_enc_opt;
+
+void setup_scaler(oe_enc_opt *opt, float scale);
+void clear_scaler(oe_enc_opt *opt);
+void setup_padder(oe_enc_opt *opt);
+void clear_padder(oe_enc_opt *opt);
+
+typedef struct
+{
+ int (*id_func)(unsigned char *buf, int len); /* Returns true if can load file */
+ int id_data_len; /* Amount of data needed to id whether this can load the file */
+ int (*open_func)(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+ void (*close_func)(void *);
+ char *format;
+ char *description;
+} input_format;
+
+typedef struct {
+ short format;
+ short channels;
+ int samplerate;
+ int bytespersec;
+ short align;
+ short samplesize;
+ unsigned int mask;
+} wav_fmt;
+
+typedef struct {
+ short channels;
+ short samplesize;
+ long totalsamples;
+ long samplesread;
+ FILE *f;
+ short bigendian;
+ int *channel_permute;
+} wavfile;
+
+typedef struct {
+ short channels;
+ int totalframes;
+ short samplesize;
+ int rate;
+ int offset;
+ int blocksize;
+} aiff_fmt;
+
+typedef wavfile aifffile; /* They're the same */
+
+input_format *open_audio_file(FILE *in, oe_enc_opt *opt);
+
+int raw_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+int wav_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+int aiff_open(FILE *in, oe_enc_opt *opt, unsigned char *buf, int buflen);
+int wav_id(unsigned char *buf, int len);
+int aiff_id(unsigned char *buf, int len);
+void wav_close(void *);
+void raw_close(void *);
+int setup_resample(oe_enc_opt *opt, int complexity, long outfreq);
+void clear_resample(oe_enc_opt *opt);
+
+long wav_read(void *, float *buffer, int samples);
+long wav_ieee_read(void *, float *buffer, int samples);
+
+#endif /* __OPUSENC_H */
--- a/src/wav_io.c
+++ b/src/wav_io.c
@@ -35,150 +35,6 @@
#include "opus_types.h"
#include "wav_io.h"
-
-int read_wav_header(FILE *file, int *rate, int *channels, int *format, opus_int32 *size)
-{
- char ch[5];
- opus_int32 itmp;
- opus_int16 stmp;
- opus_int32 bpersec;
- opus_int16 balign;
- int skip_bytes;
- int i;
- int ret;
-
- ch[4]=0;
-#if 0
- fread(ch, 1, 4, file);
- if (strcmp(ch, "RIFF")!=0)
- {
- fseek(file, 0, SEEK_SET);
- return 0;
- }
-
- fread(&itmp, 4, 1, file);
- *size = le_int(itmp-36);
-
- fread(ch, 1, 4, file);
- if (strcmp(ch, "WAVE")!=0)
- {
- fprintf (stderr, "RIFF file is not a WAVE file\n");
- return -1;
- }
-#endif
- ret = fread(ch, 1, 4, file);
- while (strcmp(ch, "fmt ")!=0)
- {
- ret = fread(&itmp, 4, 1, file);
- itmp = le_int(itmp);
- /*fprintf (stderr, "skip=%d\n", itmp);*/
- /*strange way of seeking, but it works even for pipes*/
- for (i=0;i<itmp;i++)
- fgetc(file);
- /*fseek(file, itmp, SEEK_CUR);*/
- ret = fread(ch, 1, 4, file);
- if (feof(file))
- {
- fprintf (stderr, "Corrupted WAVE file: no \"fmt \"\n");
- return -1;
- }
- }
- /*if (strcmp(ch, "fmt ")!=0)
- {
- fprintf (stderr, "Corrupted WAVE file: no \"fmt \"\n");
- return -1;
- }*/
-
- ret = fread(&itmp, 4, 1, file);
- itmp = le_int(itmp);
- skip_bytes=itmp-16;
- /*fprintf (stderr, "skip=%d\n", skip_bytes);*/
-
- ret = fread(&stmp, 2, 1, file);
- stmp = le_short(stmp);
- if (stmp!=1)
- {
- fprintf (stderr, "Only PCM encoding is supported\n");
- return -1;
- }
-
- ret = fread(&stmp, 2, 1, file);
- stmp = le_short(stmp);
- *channels = stmp;
-
- if (stmp>2)
- {
- fprintf (stderr, "Only mono and (intensity) stereo supported\n");
- return -1;
- }
-
- ret = fread(&itmp, 4, 1, file);
- itmp = le_int(itmp);
- *rate = itmp;
-
- ret = fread(&itmp, 4, 1, file);
- bpersec = le_int(itmp);
-
- ret = fread(&stmp, 2, 1, file);
- balign = le_short(stmp);
-
- ret = fread(&stmp, 2, 1, file);
- stmp = le_short(stmp);
- if (stmp!=16 && stmp!=8)
- {
- fprintf (stderr, "Only 8/16-bit linear supported\n");
- return -1;
- }
- *format=stmp;
-
- if (bpersec!=*rate**channels*stmp/8)
- {
- fprintf (stderr, "Corrupted header: ByteRate mismatch\n");
- return -1;
- }
-
- if (balign!=*channels*stmp/8)
- {
- fprintf (stderr, "Corrupted header: BlockAlign mismatch\n");
- return -1;
- }
-
-
- /*strange way of seeking, but it works even for pipes*/
- if (skip_bytes>0)
- for (i=0;i<skip_bytes;i++)
- fgetc(file);
-
- /*fseek(file, skip_bytes, SEEK_CUR);*/
-
- ret = fread(ch, 1, 4, file);
- while (strcmp(ch, "data")!=0)
- {
- ret = fread(&itmp, 4, 1, file);
- itmp = le_int(itmp);
- /*strange way of seeking, but it works even for pipes*/
- for (i=0;i<itmp;i++)
- fgetc(file);
- /*fseek(file, itmp, SEEK_CUR);*/
- ret = fread(ch, 1, 4, file);
- if (feof(file))
- {
- fprintf (stderr, "Corrupted WAVE file: no \"data\"\n");
- return -1;
- }
- }
-
- /*Ignore this for now*/
- ret = fread(&itmp, 4, 1, file);
- itmp = le_int(itmp);
-
- *size=itmp;
-
- return 1;
-}
-
-
-
void write_wav_header(FILE *file, int rate, int channels, int format, int size)
{
char ch[5];
--- a/src/wav_io.h
+++ b/src/wav_io.h
@@ -55,8 +55,6 @@
#endif
}
-int read_wav_header(FILE *file, int *rate, int *channels, int *format, opus_int32 *size);
-
void write_wav_header(FILE *file, int rate, int channels, int format, int size);
#endif