678 lines
19 KiB
C
678 lines
19 KiB
C
|
/* Copyright (c) 2002-2007 Jean-Marc Valin
|
||
|
Copyright (c) 2008 CSIRO
|
||
|
Copyright (c) 2007-2009 Xiph.Org Foundation
|
||
|
File: celtdec.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.
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
# include "config.h"
|
||
|
#endif
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#if !defined WIN32 && !defined _WIN32
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
#ifdef HAVE_GETOPT_H
|
||
|
#include <getopt.h>
|
||
|
#endif
|
||
|
#ifndef HAVE_GETOPT_LONG
|
||
|
#include "getopt_win.h"
|
||
|
#endif
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <celt.h>
|
||
|
#include <ogg/ogg.h>
|
||
|
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
#include "wave_out.h"
|
||
|
/* We need the following two to set stdout to binary */
|
||
|
#include <io.h>
|
||
|
#include <fcntl.h>
|
||
|
#endif
|
||
|
#include <math.h>
|
||
|
|
||
|
#ifdef __MINGW32__
|
||
|
#include "wave_out.c"
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_SYS_SOUNDCARD_H
|
||
|
#include <sys/soundcard.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
|
||
|
#elif defined HAVE_SYS_AUDIOIO_H
|
||
|
#include <sys/types.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <sys/audioio.h>
|
||
|
#ifndef AUDIO_ENCODING_SLINEAR
|
||
|
#define AUDIO_ENCODING_SLINEAR AUDIO_ENCODING_LINEAR /* Solaris */
|
||
|
#endif
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#include <string.h>
|
||
|
#include "wav_io.h"
|
||
|
#include <celt_header.h>
|
||
|
|
||
|
#define MAX_FRAME_SIZE 2048
|
||
|
|
||
|
#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
|
||
|
((buf[base+2]<<16)&0xff0000)| \
|
||
|
((buf[base+1]<<8)&0xff00)| \
|
||
|
(buf[base]&0xff))
|
||
|
|
||
|
static void print_comments(char *comments, int length)
|
||
|
{
|
||
|
char *c=comments;
|
||
|
int len, i, nb_fields;
|
||
|
char *end;
|
||
|
|
||
|
if (length<8)
|
||
|
{
|
||
|
fprintf (stderr, "Invalid/corrupted comments\n");
|
||
|
return;
|
||
|
}
|
||
|
end = c+length;
|
||
|
len=readint(c, 0);
|
||
|
c+=4;
|
||
|
if (len < 0 || c+len>end)
|
||
|
{
|
||
|
fprintf (stderr, "Invalid/corrupted comments\n");
|
||
|
return;
|
||
|
}
|
||
|
fwrite(c, 1, len, stderr);
|
||
|
c+=len;
|
||
|
fprintf (stderr, "\n");
|
||
|
if (c+4>end)
|
||
|
{
|
||
|
fprintf (stderr, "Invalid/corrupted comments\n");
|
||
|
return;
|
||
|
}
|
||
|
nb_fields=readint(c, 0);
|
||
|
c+=4;
|
||
|
for (i=0;i<nb_fields;i++)
|
||
|
{
|
||
|
if (c+4>end)
|
||
|
{
|
||
|
fprintf (stderr, "Invalid/corrupted comments\n");
|
||
|
return;
|
||
|
}
|
||
|
len=readint(c, 0);
|
||
|
c+=4;
|
||
|
if (len < 0 || c+len>end)
|
||
|
{
|
||
|
fprintf (stderr, "Invalid/corrupted comments\n");
|
||
|
return;
|
||
|
}
|
||
|
fwrite(c, 1, len, stderr);
|
||
|
c+=len;
|
||
|
fprintf (stderr, "\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FILE *out_file_open(char *outFile, int rate, int *channels)
|
||
|
{
|
||
|
FILE *fout=NULL;
|
||
|
/*Open output file*/
|
||
|
if (strlen(outFile)==0)
|
||
|
{
|
||
|
#if defined HAVE_SYS_SOUNDCARD_H
|
||
|
int audio_fd, format, stereo;
|
||
|
audio_fd=open("/dev/dsp", O_WRONLY);
|
||
|
if (audio_fd<0)
|
||
|
{
|
||
|
perror("Cannot open /dev/dsp");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
format=AFMT_S16_NE;
|
||
|
if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
|
||
|
{
|
||
|
perror("SNDCTL_DSP_SETFMT");
|
||
|
close(audio_fd);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
stereo=0;
|
||
|
if (*channels==2)
|
||
|
stereo=1;
|
||
|
if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
|
||
|
{
|
||
|
perror("SNDCTL_DSP_STEREO");
|
||
|
close(audio_fd);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (stereo!=0)
|
||
|
{
|
||
|
if (*channels==1)
|
||
|
fprintf (stderr, "Cannot set mono mode, will decode in stereo\n");
|
||
|
*channels=2;
|
||
|
}
|
||
|
|
||
|
if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate)==-1)
|
||
|
{
|
||
|
perror("SNDCTL_DSP_SPEED");
|
||
|
close(audio_fd);
|
||
|
exit(1);
|
||
|
}
|
||
|
fout = fdopen(audio_fd, "w");
|
||
|
#elif defined HAVE_SYS_AUDIOIO_H
|
||
|
audio_info_t info;
|
||
|
int audio_fd;
|
||
|
|
||
|
audio_fd = open("/dev/audio", O_WRONLY);
|
||
|
if (audio_fd<0)
|
||
|
{
|
||
|
perror("Cannot open /dev/audio");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
AUDIO_INITINFO(&info);
|
||
|
#ifdef AUMODE_PLAY /* NetBSD/OpenBSD */
|
||
|
info.mode = AUMODE_PLAY;
|
||
|
#endif
|
||
|
info.play.encoding = AUDIO_ENCODING_SLINEAR;
|
||
|
info.play.precision = 16;
|
||
|
info.play.sample_rate = rate;
|
||
|
info.play.channels = *channels;
|
||
|
|
||
|
if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0)
|
||
|
{
|
||
|
perror ("AUDIO_SETINFO");
|
||
|
exit(1);
|
||
|
}
|
||
|
fout = fdopen(audio_fd, "w");
|
||
|
#elif defined WIN32 || defined _WIN32
|
||
|
{
|
||
|
unsigned int celt_channels = *channels;
|
||
|
if (Set_WIN_Params (INVALID_FILEDESC, rate, SAMPLE_SIZE, celt_channels))
|
||
|
{
|
||
|
fprintf (stderr, "Can't access %s\n", "WAVE OUT");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
fprintf (stderr, "No soundcard support\n");
|
||
|
exit(1);
|
||
|
#endif
|
||
|
} else {
|
||
|
if (strcmp(outFile,"-")==0)
|
||
|
{
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
_setmode(_fileno(stdout), _O_BINARY);
|
||
|
#endif
|
||
|
fout=stdout;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fout = fopen(outFile, "wb");
|
||
|
if (!fout)
|
||
|
{
|
||
|
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, 0, 0);
|
||
|
}
|
||
|
}
|
||
|
return fout;
|
||
|
}
|
||
|
|
||
|
void usage(void)
|
||
|
{
|
||
|
printf ("Usage: celtdec [options] input_file.oga [output_file]\n");
|
||
|
printf ("\n");
|
||
|
printf ("Decodes a CELT file and produce a WAV file or raw file\n");
|
||
|
printf ("\n");
|
||
|
printf ("input_file can be:\n");
|
||
|
printf (" filename.oga regular CELT file\n");
|
||
|
printf (" - stdin\n");
|
||
|
printf ("\n");
|
||
|
printf ("output_file can be:\n");
|
||
|
printf (" filename.wav Wav file\n");
|
||
|
printf (" filename.* Raw PCM file (any extension other that .wav)\n");
|
||
|
printf (" - stdout\n");
|
||
|
printf (" (nothing) Will be played to soundcard\n");
|
||
|
printf ("\n");
|
||
|
printf ("Options:\n");
|
||
|
printf (" --mono Force decoding in mono\n");
|
||
|
printf (" --stereo Force decoding in stereo\n");
|
||
|
printf (" --rate n Force decoding at sampling rate n Hz\n");
|
||
|
printf (" --packet-loss n Simulate n %% random packet loss\n");
|
||
|
printf (" -V Verbose mode (show bit-rate)\n");
|
||
|
printf (" -h, --help This help\n");
|
||
|
printf (" -v, --version Version information\n");
|
||
|
printf ("\n");
|
||
|
}
|
||
|
|
||
|
void version(void)
|
||
|
{
|
||
|
printf ("celtenc (CELT %s encoder)\n",CELT_VERSION);
|
||
|
printf ("Copyright (C) 2008 Jean-Marc Valin\n");
|
||
|
}
|
||
|
|
||
|
void version_short(void)
|
||
|
{
|
||
|
printf ("celtenc (CELT %s encoder)\n",CELT_VERSION);
|
||
|
printf ("Copyright (C) 2008 Jean-Marc Valin\n");
|
||
|
}
|
||
|
|
||
|
static CELTDecoder *process_header(ogg_packet *op, celt_int32 enh_enabled, celt_int32 *frame_size, int *granule_frame_size, celt_int32 *rate, int *nframes, int forceMode, int *channels, int *overlap, int *extra_headers, int quiet, CELTMode **mode)
|
||
|
{
|
||
|
CELTDecoder *st;
|
||
|
CELTHeader header;
|
||
|
int bitstream;
|
||
|
|
||
|
celt_header_from_packet(op->packet, op->bytes, &header);
|
||
|
|
||
|
if (header.nb_channels>2 || header.nb_channels<1)
|
||
|
{
|
||
|
fprintf (stderr, "Unsupported number of channels: %d\n", header.nb_channels);
|
||
|
return NULL;
|
||
|
}
|
||
|
*mode = celt_mode_create(header.sample_rate, header.frame_size, NULL);
|
||
|
if (*mode == NULL)
|
||
|
{
|
||
|
fprintf (stderr, "Mode initialization failed.\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
celt_mode_info(*mode, CELT_GET_BITSTREAM_VERSION, &bitstream);
|
||
|
if (bitstream!=header.version_id)
|
||
|
fprintf(stderr, "WARNING: Input was encoded with a CELT bitstream version %d. This decoder uses %d. Output will probably be corrupted.\n",header.version_id,bitstream);
|
||
|
|
||
|
*channels = header.nb_channels;
|
||
|
*overlap=header.overlap;
|
||
|
st = celt_decoder_create_custom(*mode, header.nb_channels, NULL);
|
||
|
if (!st)
|
||
|
{
|
||
|
fprintf (stderr, "Decoder initialization failed.\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*celt_mode_info(*mode, CELT_GET_FRAME_SIZE, frame_size);*/
|
||
|
*frame_size = header.frame_size;
|
||
|
*granule_frame_size = *frame_size;
|
||
|
|
||
|
if (!*rate)
|
||
|
*rate = header.sample_rate;
|
||
|
|
||
|
*nframes = 1;
|
||
|
|
||
|
if (!quiet)
|
||
|
{
|
||
|
fprintf (stderr, "Decoding %d Hz audio in", *rate);
|
||
|
|
||
|
if (*channels==1)
|
||
|
fprintf (stderr, " (mono");
|
||
|
else
|
||
|
fprintf (stderr, " (stereo");
|
||
|
fprintf(stderr, ")\n");
|
||
|
}
|
||
|
|
||
|
*extra_headers = header.extra_headers;
|
||
|
|
||
|
return st;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
int c;
|
||
|
int option_index = 0;
|
||
|
char *inFile, *outFile;
|
||
|
FILE *fin, *fout=NULL;
|
||
|
short out[MAX_FRAME_SIZE];
|
||
|
short output[MAX_FRAME_SIZE];
|
||
|
int frame_size=0, granule_frame_size=0;
|
||
|
void *st=NULL;
|
||
|
CELTMode *mode=NULL;
|
||
|
int packet_count=0;
|
||
|
int stream_init = 0;
|
||
|
int quiet = 0;
|
||
|
ogg_int64_t page_granule=0, last_granule=0;
|
||
|
int skip_samples=0, page_nb_packets;
|
||
|
struct option long_options[] =
|
||
|
{
|
||
|
{"help", no_argument, NULL, 0},
|
||
|
{"quiet", no_argument, NULL, 0},
|
||
|
{"version", no_argument, NULL, 0},
|
||
|
{"version-short", no_argument, NULL, 0},
|
||
|
{"rate", required_argument, NULL, 0},
|
||
|
{"mono", no_argument, NULL, 0},
|
||
|
{"stereo", no_argument, NULL, 0},
|
||
|
{"packet-loss", required_argument, NULL, 0},
|
||
|
{0, 0, 0, 0}
|
||
|
};
|
||
|
ogg_sync_state oy;
|
||
|
ogg_page og;
|
||
|
ogg_packet op;
|
||
|
ogg_stream_state os;
|
||
|
int enh_enabled;
|
||
|
int nframes=2;
|
||
|
int print_bitrate=0;
|
||
|
int close_in=0;
|
||
|
int eos=0;
|
||
|
int forceMode=-1;
|
||
|
int audio_size=0;
|
||
|
float loss_percent=-1;
|
||
|
int channels=-1;
|
||
|
int rate=0;
|
||
|
int extra_headers=0;
|
||
|
int wav_format=0;
|
||
|
int lookahead=0;
|
||
|
int celt_serialno = -1;
|
||
|
int firstpacket = 1;
|
||
|
|
||
|
enh_enabled = 1;
|
||
|
|
||
|
/*Process options*/
|
||
|
while(1)
|
||
|
{
|
||
|
c = getopt_long (argc, argv, "hvV",
|
||
|
long_options, &option_index);
|
||
|
if (c==-1)
|
||
|
break;
|
||
|
|
||
|
switch(c)
|
||
|
{
|
||
|
case 0:
|
||
|
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();
|
||
|
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,"mono")==0)
|
||
|
{
|
||
|
channels=1;
|
||
|
} else if (strcmp(long_options[option_index].name,"stereo")==0)
|
||
|
{
|
||
|
channels=2;
|
||
|
} else if (strcmp(long_options[option_index].name,"rate")==0)
|
||
|
{
|
||
|
rate=atoi (optarg);
|
||
|
} else if (strcmp(long_options[option_index].name,"packet-loss")==0)
|
||
|
{
|
||
|
loss_percent = atof(optarg);
|
||
|
}
|
||
|
break;
|
||
|
case 'h':
|
||
|
usage();
|
||
|
exit(0);
|
||
|
break;
|
||
|
case 'v':
|
||
|
version();
|
||
|
exit(0);
|
||
|
break;
|
||
|
case 'V':
|
||
|
print_bitrate=1;
|
||
|
break;
|
||
|
case '?':
|
||
|
usage();
|
||
|
exit(1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (argc-optind!=2 && argc-optind!=1)
|
||
|
{
|
||
|
usage();
|
||
|
exit(1);
|
||
|
}
|
||
|
inFile=argv[optind];
|
||
|
|
||
|
if (argc-optind==2)
|
||
|
outFile=argv[optind+1];
|
||
|
else
|
||
|
outFile = "";
|
||
|
wav_format = strlen(outFile)>=4 && (
|
||
|
strcmp(outFile+strlen(outFile)-4,".wav")==0
|
||
|
|| strcmp(outFile+strlen(outFile)-4,".WAV")==0);
|
||
|
/*Open input file*/
|
||
|
if (strcmp(inFile, "-")==0)
|
||
|
{
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
_setmode(_fileno(stdin), _O_BINARY);
|
||
|
#endif
|
||
|
fin=stdin;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fin = fopen(inFile, "rb");
|
||
|
if (!fin)
|
||
|
{
|
||
|
perror(inFile);
|
||
|
exit(1);
|
||
|
}
|
||
|
close_in=1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*Init Ogg data struct*/
|
||
|
ogg_sync_init(&oy);
|
||
|
|
||
|
/*Main decoding loop*/
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
char *data;
|
||
|
int i, nb_read;
|
||
|
/*Get the ogg buffer for writing*/
|
||
|
data = ogg_sync_buffer(&oy, 200);
|
||
|
/*Read bitstream from input file*/
|
||
|
nb_read = fread(data, sizeof(char), 200, fin);
|
||
|
ogg_sync_wrote(&oy, nb_read);
|
||
|
|
||
|
/*Loop for all complete pages we got (most likely only one)*/
|
||
|
while (ogg_sync_pageout(&oy, &og)==1)
|
||
|
{
|
||
|
if (stream_init == 0) {
|
||
|
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));
|
||
|
}
|
||
|
/*Add page to the bitstream*/
|
||
|
ogg_stream_pagein(&os, &og);
|
||
|
page_granule = ogg_page_granulepos(&og);
|
||
|
page_nb_packets = ogg_page_packets(&og);
|
||
|
if (page_granule>0 && frame_size)
|
||
|
{
|
||
|
/* FIXME: shift the granule values if --force-* is specified */
|
||
|
skip_samples = frame_size*(page_nb_packets*granule_frame_size*nframes - (page_granule-last_granule))/granule_frame_size;
|
||
|
if (ogg_page_eos(&og))
|
||
|
skip_samples = -skip_samples;
|
||
|
/*else if (!ogg_page_bos(&og))
|
||
|
skip_samples = 0;*/
|
||
|
} else
|
||
|
{
|
||
|
skip_samples = 0;
|
||
|
}
|
||
|
/*printf ("page granulepos: %d %d %d\n", skip_samples, page_nb_packets, (int)page_granule);*/
|
||
|
last_granule = page_granule;
|
||
|
/*Extract all available packets*/
|
||
|
while (!eos && ogg_stream_packetout(&os, &op) == 1)
|
||
|
{
|
||
|
if (op.bytes>=8 && !memcmp(op.packet, "CELT ", 8)) {
|
||
|
celt_serialno = os.serialno;
|
||
|
}
|
||
|
if (celt_serialno == -1 || os.serialno != celt_serialno)
|
||
|
break;
|
||
|
/*If first packet, process as CELT header*/
|
||
|
if (packet_count==0)
|
||
|
{
|
||
|
st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, forceMode, &channels, &lookahead, &extra_headers, quiet, &mode);
|
||
|
if (!st)
|
||
|
exit(1);
|
||
|
if (!nframes)
|
||
|
nframes=1;
|
||
|
fout = out_file_open(outFile, rate, &channels);
|
||
|
|
||
|
} else if (packet_count==1)
|
||
|
{
|
||
|
if (!quiet)
|
||
|
print_comments((char*)op.packet, op.bytes);
|
||
|
} else if (packet_count<=1+extra_headers)
|
||
|
{
|
||
|
/* Ignore extra headers */
|
||
|
} else {
|
||
|
int lost=0;
|
||
|
if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
|
||
|
lost=1;
|
||
|
|
||
|
/*End of stream condition*/
|
||
|
if (op.e_o_s && os.serialno == celt_serialno) /* don't care for anything except celt eos */
|
||
|
eos=1;
|
||
|
|
||
|
{
|
||
|
int ret;
|
||
|
/*Decode frame*/
|
||
|
if (!lost)
|
||
|
ret = celt_decode(st, (unsigned char*)op.packet, op.bytes, output, frame_size);
|
||
|
else
|
||
|
ret = celt_decode(st, NULL, 0, output, frame_size);
|
||
|
|
||
|
/*for (i=0;i<frame_size*channels;i++)
|
||
|
printf ("%d\n", (int)output[i]);*/
|
||
|
|
||
|
if (ret!=0)
|
||
|
{
|
||
|
fprintf (stderr, "Decoding error: corrupted stream?\n");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (print_bitrate) {
|
||
|
celt_int32 tmp=op.bytes;
|
||
|
char ch=13;
|
||
|
fputc (ch, stderr);
|
||
|
fprintf (stderr, "Bitrate in use: %d bytes/packet ", tmp);
|
||
|
}
|
||
|
/*Convert to short and save to output file*/
|
||
|
if (strlen(outFile)!=0)
|
||
|
{
|
||
|
for (i=0;i<frame_size*channels;i++)
|
||
|
out[i]=le_short(output[i]);
|
||
|
} else {
|
||
|
for (i=0;i<frame_size*channels;i++)
|
||
|
out[i]=output[i];
|
||
|
}
|
||
|
{
|
||
|
int frame_offset = 0;
|
||
|
int new_frame_size = frame_size;
|
||
|
/*printf ("packet %d %d\n", packet_no, skip_samples);*/
|
||
|
/*fprintf (stderr, "packet %d %d %d\n", packet_no, skip_samples, lookahead);*/
|
||
|
if (firstpacket == 1)
|
||
|
{
|
||
|
/*printf ("chopping first packet\n");*/
|
||
|
new_frame_size -= lookahead;
|
||
|
frame_offset = lookahead;
|
||
|
firstpacket = 0;
|
||
|
}
|
||
|
if (new_frame_size>0)
|
||
|
{
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
if (strlen(outFile)==0)
|
||
|
WIN_Play_Samples (out+frame_offset*channels, sizeof(short) * new_frame_size*channels);
|
||
|
else
|
||
|
#endif
|
||
|
fwrite(out+frame_offset*channels, sizeof(short), new_frame_size*channels, fout);
|
||
|
|
||
|
audio_size+=sizeof(short)*new_frame_size*channels;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
packet_count++;
|
||
|
}
|
||
|
}
|
||
|
if (feof(fin))
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (fout && wav_format)
|
||
|
{
|
||
|
if (fseek(fout,4,SEEK_SET)==0)
|
||
|
{
|
||
|
int tmp;
|
||
|
tmp = le_int(audio_size+36);
|
||
|
fwrite(&tmp,4,1,fout);
|
||
|
if (fseek(fout,32,SEEK_CUR)==0)
|
||
|
{
|
||
|
tmp = le_int(audio_size);
|
||
|
fwrite(&tmp,4,1,fout);
|
||
|
} else
|
||
|
{
|
||
|
fprintf (stderr, "First seek worked, second didn't\n");
|
||
|
}
|
||
|
} else {
|
||
|
fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (st)
|
||
|
{
|
||
|
celt_decoder_destroy(st);
|
||
|
celt_mode_destroy(mode);
|
||
|
} else {
|
||
|
fprintf (stderr, "This doesn't look like a CELT file\n");
|
||
|
}
|
||
|
if (stream_init)
|
||
|
ogg_stream_clear(&os);
|
||
|
ogg_sync_clear(&oy);
|
||
|
|
||
|
#if defined WIN32 || defined _WIN32
|
||
|
if (strlen(outFile)==0)
|
||
|
WIN_Audio_close ();
|
||
|
#endif
|
||
|
|
||
|
if (close_in)
|
||
|
fclose(fin);
|
||
|
if (fout != NULL)
|
||
|
fclose(fout);
|
||
|
|
||
|
return 0;
|
||
|
}
|