You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
810 lines
24 KiB
810 lines
24 KiB
14 years ago
|
/* Copyright (C) 2002-2003 Jean-Marc Valin
|
||
|
File: speexenc.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.
|
||
|
|
||
|
- Neither the name of the Xiph.org Foundation nor the names of its
|
||
|
contributors may be used to endorse or promote products derived from
|
||
|
this software without specific prior written permission.
|
||
|
|
||
|
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.
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#if !defined WIN32 && !defined _WIN32
|
||
|
#include <unistd.h>
|
||
|
#include <getopt.h>
|
||
|
#endif
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
#include "speex.h"
|
||
|
#include <ogg.h>
|
||
|
#include "wav_io.h"
|
||
|
#include "speex_header.h"
|
||
|
#include "speex_stereo.h"
|
||
|
#include "misc.h"
|
||
|
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
#include "getopt_win.h"
|
||
|
/* We need the following two to set stdout to binary */
|
||
|
#include <io.h>
|
||
|
#include <fcntl.h>
|
||
|
#endif
|
||
|
|
||
|
|
||
|
void comment_init(char **comments, int* length, char *vendor_string);
|
||
|
void comment_add(char **comments, int* length, char *tag, char *val);
|
||
|
|
||
|
|
||
|
/*Write an Ogg page to a file pointer*/
|
||
|
int oe_write_page(ogg_page *page, FILE *fp)
|
||
|
{
|
||
|
int written;
|
||
|
written = fwrite(page->header,1,page->header_len, fp);
|
||
|
written += fwrite(page->body,1,page->body_len, fp);
|
||
|
|
||
|
return written;
|
||
|
}
|
||
|
|
||
|
#define MAX_FRAME_SIZE 2000
|
||
|
#define MAX_FRAME_BYTES 2000
|
||
|
|
||
|
/* Convert input audio bits, endians and channels */
|
||
|
static int read_samples(FILE *fin,int frame_size, int bits, int channels, int lsb, float * input, char *buff, int *size)
|
||
|
{
|
||
|
unsigned char in[MAX_FRAME_BYTES*2];
|
||
|
int i;
|
||
|
short *s;
|
||
|
int nb_read;
|
||
|
|
||
|
if (size && *size<=0)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
/*Read input audio*/
|
||
|
if (size)
|
||
|
*size -= bits/8*channels*frame_size;
|
||
|
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 {
|
||
|
nb_read = fread(in,1,bits/8*channels* frame_size, fin);
|
||
|
}
|
||
|
nb_read /= bits/8*channels;
|
||
|
|
||
|
/*fprintf (stderr, "%d\n", nb_read);*/
|
||
|
if (nb_read==0)
|
||
|
return 1;
|
||
|
|
||
|
s=(short*)in;
|
||
|
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]=(short)s[i];
|
||
|
}
|
||
|
|
||
|
for (i=nb_read*channels;i<frame_size*channels;i++)
|
||
|
{
|
||
|
input[i]=0;
|
||
|
}
|
||
|
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void version()
|
||
|
{
|
||
|
printf ("speexenc (Speex encoder) version " VERSION " (compiled " __DATE__ ")\n");
|
||
|
printf ("Copyright (C) 2002-2003 Jean-Marc Valin\n");
|
||
|
}
|
||
|
|
||
|
void version_short()
|
||
|
{
|
||
|
printf ("speexenc version " VERSION "\n");
|
||
|
printf ("Copyright (C) 2002-2003 Jean-Marc Valin\n");
|
||
|
}
|
||
|
|
||
|
void usage()
|
||
|
{
|
||
|
printf ("Usage: speexenc [options] input_file output_file\n");
|
||
|
printf ("\n");
|
||
|
printf ("Encodes input_file using Speex. It can read the WAV or raw files.\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 (" - stdin\n");
|
||
|
printf ("\n");
|
||
|
printf ("output_file can be:\n");
|
||
|
printf (" filename.spx Speex file\n");
|
||
|
printf (" - stdout\n");
|
||
|
printf ("\n");
|
||
|
printf ("Options:\n");
|
||
|
printf (" -n, --narrowband Narrowband (8 kHz) input file\n");
|
||
|
printf (" -w, --wideband Wideband (16 kHz) input file\n");
|
||
|
printf (" -u, --ultra-wideband \"Ultra-wideband\" (32 kHz) input file\n");
|
||
|
printf (" --quality n Encoding quality (0-10), default 8\n");
|
||
|
printf (" --bitrate n Encoding bit-rate (use bit-rate n or lower)\n");
|
||
|
printf (" --vbr Enable variable bit-rate (VBR)\n");
|
||
|
printf (" --abr rate Enable average bit-rate (ABR) at rate bps\n");
|
||
|
printf (" --vad Enable voice activity detection (VAD)\n");
|
||
|
printf (" --dtx Enable file-based discontinuous transmission (DTX)\n");
|
||
|
printf (" --comp n Set encoding complexity (0-10), default 3\n");
|
||
|
printf (" --nframes n Number of frames per Ogg packet (1-10), default 1\n");
|
||
|
printf (" --comment Add the given string as an extra comment. This may be\n");
|
||
|
printf (" used multiple times.\n");
|
||
|
printf (" --author Author of this track.\n");
|
||
|
printf (" --title Title for this track.\n");
|
||
|
printf (" -h, --help This help\n");
|
||
|
printf (" -v, --version Version information\n");
|
||
|
printf (" -V Verbose mode (show bit-rate)\n");
|
||
|
printf ("Raw input options:\n");
|
||
|
printf (" --rate n Sampling rate for raw input\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 16-bit, little-endian, mono\n");
|
||
|
printf ("\n");
|
||
|
printf ("More information is available from the Speex site: http://www.speex.org\n");
|
||
|
printf ("\n");
|
||
|
printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
int c;
|
||
|
int option_index = 0;
|
||
|
char *inFile, *outFile;
|
||
|
FILE *fin, *fout;
|
||
|
float input[MAX_FRAME_SIZE];
|
||
|
int frame_size;
|
||
|
int vbr_enabled=0;
|
||
|
int abr_enabled=0;
|
||
|
int vad_enabled=0;
|
||
|
int dtx_enabled=0;
|
||
|
int nbBytes;
|
||
|
SpeexMode *mode=NULL;
|
||
|
void *st;
|
||
|
SpeexBits bits;
|
||
|
char cbits[MAX_FRAME_BYTES];
|
||
|
struct option long_options[] =
|
||
|
{
|
||
|
{"wideband", no_argument, NULL, 0},
|
||
|
{"ultra-wideband", no_argument, NULL, 0},
|
||
|
{"narrowband", no_argument, NULL, 0},
|
||
|
{"vbr", no_argument, NULL, 0},
|
||
|
{"abr", required_argument, NULL, 0},
|
||
|
{"vad", no_argument, NULL, 0},
|
||
|
{"dtx", no_argument, NULL, 0},
|
||
|
{"quality", required_argument, NULL, 0},
|
||
|
{"bitrate", required_argument, NULL, 0},
|
||
|
{"nframes", required_argument, NULL, 0},
|
||
|
{"comp", required_argument, NULL, 0},
|
||
|
{"help", no_argument, NULL, 0},
|
||
|
{"le", no_argument, NULL, 0},
|
||
|
{"be", no_argument, NULL, 0},
|
||
|
{"8bit", no_argument, NULL, 0},
|
||
|
{"16bit", no_argument, NULL, 0},
|
||
|
{"stereo", no_argument, NULL, 0},
|
||
|
{"rate", required_argument, NULL, 0},
|
||
|
{"version", no_argument, NULL, 0},
|
||
|
{"version-short", no_argument, NULL, 0},
|
||
|
{"comment", required_argument, NULL, 0},
|
||
|
{"author", required_argument, NULL, 0},
|
||
|
{"title", required_argument, NULL, 0},
|
||
|
{0, 0, 0, 0}
|
||
|
};
|
||
|
int print_bitrate=0;
|
||
|
int rate=0, size;
|
||
|
int chan=1;
|
||
|
int fmt=16;
|
||
|
int quality=-1;
|
||
|
float vbr_quality=-1;
|
||
|
int lsb=1;
|
||
|
ogg_stream_state os;
|
||
|
ogg_page og;
|
||
|
ogg_packet op;
|
||
|
int bytes_written=0, ret, result;
|
||
|
int id=-1;
|
||
|
SpeexHeader header;
|
||
|
int nframes=1;
|
||
|
int complexity=3;
|
||
|
char *vendor_string = "Encoded with Speex " VERSION;
|
||
|
char *comments;
|
||
|
int comments_length;
|
||
|
int close_in=0, close_out=0;
|
||
|
int eos=0;
|
||
|
int bitrate=0;
|
||
|
double cumul_bits=0, enc_frames=0;
|
||
|
char first_bytes[12];
|
||
|
int wave_input=0;
|
||
|
int tmp;
|
||
|
|
||
|
comment_init(&comments, &comments_length, vendor_string);
|
||
|
|
||
|
/*Process command-line options*/
|
||
|
while(1)
|
||
|
{
|
||
|
c = getopt_long (argc, argv, "nwuhvV",
|
||
|
long_options, &option_index);
|
||
|
if (c==-1)
|
||
|
break;
|
||
|
|
||
|
switch(c)
|
||
|
{
|
||
|
case 0:
|
||
|
if (strcmp(long_options[option_index].name,"narrowband")==0)
|
||
|
{
|
||
|
mode=&speex_nb_mode;
|
||
|
} else if (strcmp(long_options[option_index].name,"wideband")==0)
|
||
|
{
|
||
|
mode=&speex_wb_mode;
|
||
|
} else if (strcmp(long_options[option_index].name,"ultra-wideband")==0)
|
||
|
{
|
||
|
mode=&speex_uwb_mode;
|
||
|
} else if (strcmp(long_options[option_index].name,"vbr")==0)
|
||
|
{
|
||
|
vbr_enabled=1;
|
||
|
} else if (strcmp(long_options[option_index].name,"abr")==0)
|
||
|
{
|
||
|
abr_enabled=atoi(optarg);
|
||
|
if (!abr_enabled)
|
||
|
{
|
||
|
fprintf (stderr, "Invalid ABR value: %d\n", abr_enabled);
|
||
|
exit(1);
|
||
|
}
|
||
|
} else if (strcmp(long_options[option_index].name,"vad")==0)
|
||
|
{
|
||
|
vad_enabled=1;
|
||
|
} else if (strcmp(long_options[option_index].name,"dtx")==0)
|
||
|
{
|
||
|
dtx_enabled=1;
|
||
|
} else if (strcmp(long_options[option_index].name,"quality")==0)
|
||
|
{
|
||
|
quality = atoi (optarg);
|
||
|
vbr_quality=atof(optarg);
|
||
|
} else if (strcmp(long_options[option_index].name,"bitrate")==0)
|
||
|
{
|
||
|
bitrate = atoi (optarg);
|
||
|
} else if (strcmp(long_options[option_index].name,"nframes")==0)
|
||
|
{
|
||
|
nframes = atoi (optarg);
|
||
|
if (nframes<1)
|
||
|
nframes=1;
|
||
|
if (nframes>10)
|
||
|
nframes=10;
|
||
|
} else if (strcmp(long_options[option_index].name,"comp")==0)
|
||
|
{
|
||
|
complexity = atoi (optarg);
|
||
|
} else if (strcmp(long_options[option_index].name,"help")==0)
|
||
|
{
|
||
|
usage();
|
||
|
exit(0);
|
||
|
} else if (strcmp(long_options[option_index].name,"version")==0)
|
||
|
{
|
||
|
version();
|
||
|
exit(0);
|
||
|
} else if (strcmp(long_options[option_index].name,"version-short")==0)
|
||
|
{
|
||
|
version_short();
|
||
|
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,"rate")==0)
|
||
|
{
|
||
|
rate=atoi (optarg);
|
||
|
} else if (strcmp(long_options[option_index].name,"comment")==0)
|
||
|
{
|
||
|
comment_add(&comments, &comments_length, NULL, optarg);
|
||
|
} else if (strcmp(long_options[option_index].name,"author")==0)
|
||
|
{
|
||
|
comment_add(&comments, &comments_length, "author=", optarg);
|
||
|
} else if (strcmp(long_options[option_index].name,"title")==0)
|
||
|
{
|
||
|
comment_add(&comments, &comments_length, "title=", optarg);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
case 'n':
|
||
|
mode=&speex_nb_mode;
|
||
|
break;
|
||
|
case 'h':
|
||
|
usage();
|
||
|
exit(0);
|
||
|
break;
|
||
|
case 'v':
|
||
|
version();
|
||
|
exit(0);
|
||
|
break;
|
||
|
case 'V':
|
||
|
print_bitrate=1;
|
||
|
break;
|
||
|
case 'w':
|
||
|
mode=&speex_wb_mode;
|
||
|
break;
|
||
|
case 'u':
|
||
|
mode=&speex_uwb_mode;
|
||
|
break;
|
||
|
case '?':
|
||
|
usage();
|
||
|
exit(1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (argc-optind!=2)
|
||
|
{
|
||
|
usage();
|
||
|
exit(1);
|
||
|
}
|
||
|
inFile=argv[optind];
|
||
|
outFile=argv[optind+1];
|
||
|
|
||
|
/*Initialize Ogg stream struct*/
|
||
|
srand(time(NULL));
|
||
|
if (ogg_stream_init(&os, rand())==-1)
|
||
|
{
|
||
|
fprintf(stderr,"Error: stream init failed\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (strcmp(inFile, "-")==0)
|
||
|
{
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
_setmode(_fileno(stdin), _O_BINARY);
|
||
|
#endif
|
||
|
fin=stdin;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
fin = fopen(inFile, "rb");
|
||
|
#else
|
||
|
fin = fopen(inFile, "r");
|
||
|
#endif
|
||
|
if (!fin)
|
||
|
{
|
||
|
perror(inFile);
|
||
|
exit(1);
|
||
|
}
|
||
|
close_in=1;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
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 (!mode && !rate)
|
||
|
{
|
||
|
/* By default, use narrowband/8 kHz */
|
||
|
mode=&speex_nb_mode;
|
||
|
rate=8000;
|
||
|
} else if (mode && rate)
|
||
|
{
|
||
|
if (rate>48000)
|
||
|
{
|
||
|
fprintf (stderr, "Error: sampling rate too high: %d Hz, try down-sampling\n", rate);
|
||
|
exit(1);
|
||
|
} else if (rate>25000)
|
||
|
{
|
||
|
if (mode!=&speex_uwb_mode)
|
||
|
{
|
||
|
fprintf (stderr, "Warning: Trying to encode in %s at %d Hz. I'll do it but I suggest you try ultra-wideband instead\n", mode->modeName , rate);
|
||
|
}
|
||
|
} else if (rate>12500)
|
||
|
{
|
||
|
if (mode!=&speex_wb_mode)
|
||
|
{
|
||
|
fprintf (stderr, "Warning: Trying to encode in %s at %d Hz. I'll do it but I suggest you try wideband instead\n", mode->modeName , rate);
|
||
|
}
|
||
|
} else if (rate>=6000)
|
||
|
{
|
||
|
if (mode!=&speex_nb_mode)
|
||
|
{
|
||
|
fprintf (stderr, "Warning: Trying to encode in %s at %d Hz. I'll do it but I suggest you try narrowband instead\n", mode->modeName , rate);
|
||
|
}
|
||
|
} else {
|
||
|
fprintf (stderr, "Error: sampling rate too low: %d Hz\n", rate);
|
||
|
exit(1);
|
||
|
}
|
||
|
} else if (!mode)
|
||
|
{
|
||
|
if (rate>48000)
|
||
|
{
|
||
|
fprintf (stderr, "Error: sampling rate too high: %d Hz, try down-sampling\n", rate);
|
||
|
exit(1);
|
||
|
} else if (rate>25000)
|
||
|
{
|
||
|
mode=&speex_uwb_mode;
|
||
|
} else if (rate>12500)
|
||
|
{
|
||
|
mode=&speex_wb_mode;
|
||
|
} else if (rate>=6000)
|
||
|
{
|
||
|
mode=&speex_nb_mode;
|
||
|
} else {
|
||
|
fprintf (stderr, "Error: Sampling rate too low: %d Hz\n", rate);
|
||
|
exit(1);
|
||
|
}
|
||
|
} else if (!rate)
|
||
|
{
|
||
|
if (mode==&speex_nb_mode)
|
||
|
rate=8000;
|
||
|
else if (mode==&speex_wb_mode)
|
||
|
rate=16000;
|
||
|
else if (mode==&speex_uwb_mode)
|
||
|
rate=32000;
|
||
|
}
|
||
|
|
||
|
if (rate!=8000 && rate!=16000 && rate!=32000)
|
||
|
fprintf (stderr, "Warning: Speex is only optimized for 8, 16 and 32 kHz. It will still work at %d Hz but your mileage may vary\n", rate);
|
||
|
|
||
|
speex_init_header(&header, rate, 1, mode);
|
||
|
header.frames_per_packet=nframes;
|
||
|
header.vbr=vbr_enabled;
|
||
|
header.nb_channels = chan;
|
||
|
|
||
|
{
|
||
|
char *st_string="mono";
|
||
|
if (chan==2)
|
||
|
st_string="stereo";
|
||
|
fprintf (stderr, "Encoding %d Hz audio using %s mode (%s)\n",
|
||
|
header.rate, mode->modeName, st_string);
|
||
|
}
|
||
|
/*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n",
|
||
|
header.rate, mode->bitrate, mode->modeName);*/
|
||
|
|
||
|
/*Initialize Speex encoder*/
|
||
|
st = speex_encoder_init(mode);
|
||
|
|
||
|
if (strcmp(outFile,"-")==0)
|
||
|
{
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
_setmode(_fileno(stdout), _O_BINARY);
|
||
|
#endif
|
||
|
fout=stdout;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
fout = fopen(outFile, "wb");
|
||
|
#else
|
||
|
fout = fopen(outFile, "w");
|
||
|
#endif
|
||
|
if (!fout)
|
||
|
{
|
||
|
perror(outFile);
|
||
|
exit(1);
|
||
|
}
|
||
|
close_out=1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*Write header (format will change)*/
|
||
|
{
|
||
|
|
||
|
op.packet = (unsigned char *)speex_header_to_packet(&header, (int*)&(op.bytes));
|
||
|
op.b_o_s = 1;
|
||
|
op.e_o_s = 0;
|
||
|
op.granulepos = 0;
|
||
|
op.packetno = 0;
|
||
|
ogg_stream_packetin(&os, &op);
|
||
|
free(op.packet);
|
||
|
|
||
|
op.packet = (unsigned char *)comments;
|
||
|
op.bytes = comments_length;
|
||
|
op.b_o_s = 0;
|
||
|
op.e_o_s = 0;
|
||
|
op.granulepos = 0;
|
||
|
op.packetno = 1;
|
||
|
ogg_stream_packetin(&os, &op);
|
||
|
|
||
|
while((result = ogg_stream_flush(&os, &og)))
|
||
|
{
|
||
|
if(!result) 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");
|
||
|
exit(1);
|
||
|
}
|
||
|
else
|
||
|
bytes_written += ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free(comments);
|
||
|
|
||
|
speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
|
||
|
speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity);
|
||
|
speex_encoder_ctl(st, SPEEX_SET_SAMPLING_RATE, &rate);
|
||
|
|
||
|
if (quality >= 0)
|
||
|
{
|
||
|
if (vbr_enabled)
|
||
|
speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &vbr_quality);
|
||
|
else
|
||
|
speex_encoder_ctl(st, SPEEX_SET_QUALITY, &quality);
|
||
|
}
|
||
|
if (bitrate)
|
||
|
{
|
||
|
if (quality >= 0 && vbr_enabled)
|
||
|
fprintf (stderr, "Warning: --bitrate option is overriding --quality\n");
|
||
|
speex_encoder_ctl(st, SPEEX_SET_BITRATE, &bitrate);
|
||
|
}
|
||
|
if (vbr_enabled)
|
||
|
{
|
||
|
tmp=1;
|
||
|
speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
|
||
|
} else if (vad_enabled)
|
||
|
{
|
||
|
tmp=1;
|
||
|
speex_encoder_ctl(st, SPEEX_SET_VAD, &tmp);
|
||
|
}
|
||
|
if (dtx_enabled)
|
||
|
speex_encoder_ctl(st, SPEEX_SET_DTX, &tmp);
|
||
|
if (dtx_enabled && !(vbr_enabled || abr_enabled || vad_enabled))
|
||
|
{
|
||
|
fprintf (stderr, "Warning: --dtx is useless without --vad, --vbr or --abr\n");
|
||
|
} else if ((vbr_enabled || abr_enabled) && (vad_enabled))
|
||
|
{
|
||
|
fprintf (stderr, "Warning: --vad is already implied by --vbr or --abr\n");
|
||
|
}
|
||
|
|
||
|
if (abr_enabled)
|
||
|
{
|
||
|
speex_encoder_ctl(st, SPEEX_SET_ABR, &abr_enabled);
|
||
|
}
|
||
|
|
||
|
speex_bits_init(&bits);
|
||
|
|
||
|
if (!wave_input)
|
||
|
{
|
||
|
if (read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL))
|
||
|
eos=1;
|
||
|
} else {
|
||
|
if (read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size))
|
||
|
eos=1;
|
||
|
}
|
||
|
/*Main encoding loop (one frame per iteration)*/
|
||
|
while (!eos)
|
||
|
{
|
||
|
id++;
|
||
|
/*Encode current frame*/
|
||
|
if (chan==2)
|
||
|
speex_encode_stereo(input, frame_size, &bits);
|
||
|
speex_encode(st, input, &bits);
|
||
|
|
||
|
if (print_bitrate) {
|
||
|
int tmp;
|
||
|
char ch=13;
|
||
|
speex_encoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
|
||
|
fputc (ch, stderr);
|
||
|
cumul_bits += tmp;
|
||
|
enc_frames += 1;
|
||
|
if (vad_enabled || vbr_enabled || abr_enabled)
|
||
|
fprintf (stderr, "Bitrate is use: %d bps (average %d bps) ", tmp, (int)(cumul_bits/enc_frames));
|
||
|
else
|
||
|
fprintf (stderr, "Bitrate is use: %d bps ", tmp);
|
||
|
|
||
|
}
|
||
|
|
||
|
if (wave_input)
|
||
|
{
|
||
|
if (read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size))
|
||
|
{
|
||
|
eos=1;
|
||
|
op.e_o_s = 1;
|
||
|
}
|
||
|
} else {
|
||
|
if (read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL))
|
||
|
{
|
||
|
eos=1;
|
||
|
op.e_o_s = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((id+1)%nframes!=0)
|
||
|
continue;
|
||
|
speex_bits_insert_terminator(&bits);
|
||
|
nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
|
||
|
speex_bits_reset(&bits);
|
||
|
op.packet = (unsigned char *)cbits;
|
||
|
op.bytes = nbBytes;
|
||
|
op.b_o_s = 0;
|
||
|
if (eos)
|
||
|
op.e_o_s = 1;
|
||
|
else
|
||
|
op.e_o_s = 0;
|
||
|
op.granulepos = (id+nframes)*frame_size;
|
||
|
op.packetno = 2+id/nframes;
|
||
|
ogg_stream_packetin(&os, &op);
|
||
|
|
||
|
/*Write all new pages (most likely 0 or 1)*/
|
||
|
while (ogg_stream_pageout(&os,&og))
|
||
|
{
|
||
|
ret = oe_write_page(&og, fout);
|
||
|
if(ret != og.header_len + og.body_len)
|
||
|
{
|
||
|
fprintf (stderr,"Error: failed writing header to output stream\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
else
|
||
|
bytes_written += ret;
|
||
|
}
|
||
|
}
|
||
|
if ((id+1)%nframes!=0)
|
||
|
{
|
||
|
while ((id+1)%nframes!=0)
|
||
|
{
|
||
|
id++;
|
||
|
speex_bits_pack(&bits, 15, 5);
|
||
|
}
|
||
|
nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
|
||
|
op.packet = (unsigned char *)cbits;
|
||
|
op.bytes = nbBytes;
|
||
|
op.b_o_s = 0;
|
||
|
op.e_o_s = 1;
|
||
|
op.granulepos = (id+nframes)*frame_size;
|
||
|
op.packetno = 2+id/nframes;
|
||
|
ogg_stream_packetin(&os, &op);
|
||
|
}
|
||
|
/*Flush all pages left to be written*/
|
||
|
while (ogg_stream_flush(&os, &og))
|
||
|
{
|
||
|
ret = oe_write_page(&og, fout);
|
||
|
if(ret != og.header_len + og.body_len)
|
||
|
{
|
||
|
fprintf (stderr,"Error: failed writing header to output stream\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
else
|
||
|
bytes_written += ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
speex_encoder_destroy(st);
|
||
|
speex_bits_destroy(&bits);
|
||
|
ogg_stream_clear(&os);
|
||
|
|
||
|
if (close_in)
|
||
|
fclose(fin);
|
||
|
if (close_out)
|
||
|
fclose(fout);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Comments will be stored in the Vorbis style.
|
||
|
It is describled in the "Structure" section of
|
||
|
http://www.xiph.org/ogg/vorbis/doc/v-comment.html
|
||
|
|
||
|
The comment header is decoded as follows:
|
||
|
1) [vendor_length] = read an unsigned integer of 32 bits
|
||
|
2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
|
||
|
3) [user_comment_list_length] = read an unsigned integer of 32 bits
|
||
|
4) iterate [user_comment_list_length] times {
|
||
|
5) [length] = read an unsigned integer of 32 bits
|
||
|
6) this iteration's user comment = read a UTF-8 vector as [length] octets
|
||
|
}
|
||
|
7) [framing_bit] = read a single bit as boolean
|
||
|
8) if ( [framing_bit] unset or end of packet ) then ERROR
|
||
|
9) done.
|
||
|
|
||
|
If you have troubles, please write to ymnk@jcraft.com.
|
||
|
*/
|
||
|
|
||
|
#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
|
||
|
((buf[base+2]<<16)&0xff0000)| \
|
||
|
((buf[base+1]<<8)&0xff00)| \
|
||
|
(buf[base]&0xff))
|
||
|
#define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
|
||
|
buf[base+2]=((val)>>16)&0xff; \
|
||
|
buf[base+1]=((val)>>8)&0xff; \
|
||
|
buf[base]=(val)&0xff; \
|
||
|
}while(0)
|
||
|
|
||
|
void comment_init(char **comments, int* length, char *vendor_string)
|
||
|
{
|
||
|
int vendor_length=strlen(vendor_string);
|
||
|
int user_comment_list_length=0;
|
||
|
int len=4+vendor_length+4;
|
||
|
char *p=(char*)malloc(len);
|
||
|
if(p==NULL){
|
||
|
}
|
||
|
writeint(p, 0, vendor_length);
|
||
|
memcpy(p+4, vendor_string, vendor_length);
|
||
|
writeint(p, 4+vendor_length, user_comment_list_length);
|
||
|
*length=len;
|
||
|
*comments=p;
|
||
|
}
|
||
|
void comment_add(char **comments, int* length, char *tag, char *val)
|
||
|
{
|
||
|
char* p=*comments;
|
||
|
int vendor_length=readint(p, 0);
|
||
|
int user_comment_list_length=readint(p, 4+vendor_length);
|
||
|
int tag_len=(tag?strlen(tag):0);
|
||
|
int val_len=strlen(val);
|
||
|
int len=(*length)+4+tag_len+val_len;
|
||
|
|
||
|
p=(char*)realloc(p, len);
|
||
|
if(p==NULL){
|
||
|
}
|
||
|
|
||
|
writeint(p, *length, tag_len+val_len); /* length of comment */
|
||
|
if(tag) memcpy(p+*length+4, tag, tag_len); /* comment */
|
||
|
memcpy(p+*length+4+tag_len, val, val_len); /* comment */
|
||
|
writeint(p, 4+vendor_length, user_comment_list_length+1);
|
||
|
|
||
|
*comments=p;
|
||
|
*length=len;
|
||
|
}
|
||
|
#undef readint
|
||
|
#undef writeint
|