1
/* Copyright (C) 2002-2008 Jean-Marc Valin
4
Redistribution and use in source and binary forms, with or without
5
modification, are permitted provided that the following conditions
8
- Redistributions of source code must retain the above copyright
9
notice, this list of conditions and the following disclaimer.
11
- Redistributions in binary form must reproduce the above copyright
12
notice, this list of conditions and the following disclaimer in the
13
documentation and/or other materials provided with the distribution.
15
- Neither the name of the Xiph.org Foundation nor the names of its
16
contributors may be used to endorse or promote products derived from
17
this software without specific prior written permission.
19
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
23
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
#if !defined WIN32 && !defined _WIN32
45
#ifndef HAVE_GETOPT_LONG
46
#include "getopt_win.h"
54
#include "celt_header.h"
58
#if defined WIN32 || defined _WIN32
59
/* We need the following two to set stdout to binary */
67
void comment_init(char **comments, int* length, char *vendor_string);
68
void comment_add(char **comments, int* length, char *tag, char *val);
71
/*Write an Ogg page to a file pointer*/
72
int oe_write_page(ogg_page *page, FILE *fp)
75
written = fwrite(page->header,1,page->header_len, fp);
76
written += fwrite(page->body,1,page->body_len, fp);
81
#define MAX_FRAME_SIZE 2000
82
#define MAX_FRAME_BYTES 2000
84
/* Convert input audio bits, endians and channels */
85
static int read_samples(FILE *fin,int frame_size, int bits, int channels, int lsb, short * input, char *buff, celt_int32_t *size)
87
unsigned char in[MAX_FRAME_BYTES*2];
98
*size -= bits/8*channels*frame_size;
103
nb_read = fread(in+12,1,bits/8*channels*frame_size-12, fin) + 12;
107
nb_read = fread(in,1,bits/8*channels* frame_size, fin);
109
nb_read /= bits/8*channels;
111
/*fprintf (stderr, "%d\n", nb_read);*/
118
/* Convert 8->16 bits */
119
for(i=frame_size*channels-1;i>=0;i--)
121
s[i]=(in[i]<<8)^0x8000;
125
/* convert to our endian format */
126
for(i=0;i<frame_size*channels;i++)
135
/* FIXME: This is probably redundent now */
136
/* copy to float input buffer */
137
for (i=0;i<frame_size*channels;i++)
139
input[i]=(short)s[i];
142
for (i=nb_read*channels;i<frame_size*channels;i++)
151
void add_fishead_packet (ogg_stream_state *os) {
155
memset(&fp, 0, sizeof(fp));
161
add_fishead_to_stream(os, &fp);
165
* Adds the fishead packets in the skeleton output stream along with the e_o_s packet
167
void add_fisbone_packet (ogg_stream_state *os, celt_int32_t serialno, CELT051Header *header) {
171
memset(&fp, 0, sizeof(fp));
172
fp.serial_no = serialno;
173
fp.nr_header_packet = 2 + header->extra_headers;
174
fp.granule_rate_n = header->sample_rate;
175
fp.granule_rate_d = 1;
176
fp.start_granule = 0;
178
fp.granule_shift = 0;
180
add_message_header_field(&fp, "Content-Type", "audio/x-celt");
182
add_fisbone_to_stream(os, &fp);
187
printf ("celtenc (CELT encoder)\n");
188
printf ("Copyright (C) 2008 Jean-Marc Valin\n");
191
void version_short(void)
193
printf ("celtenc (CELT encoder)\n");
194
printf ("Copyright (C) 2008 Jean-Marc Valin\n");
199
printf ("Usage: celtenc [options] input_file output_file.oga\n");
201
printf ("Encodes input_file using CELT. It can read the WAV or raw files.\n");
203
printf ("input_file can be:\n");
204
printf (" filename.wav wav file\n");
205
printf (" filename.* Raw PCM file (any extension other than .wav)\n");
206
printf (" - stdin\n");
208
printf ("output_file can be:\n");
209
printf (" filename.oga compressed file\n");
210
printf (" - stdout\n");
212
printf ("Options:\n");
213
printf (" --bitrate n Encoding bit-rate\n");
214
printf (" --comp n Encoding complexity (0-10)\n");
215
printf (" --skeleton Outputs ogg skeleton metadata (may cause incompatibilities)\n");
216
printf (" --comment Add the given string as an extra comment. This may be\n");
217
printf (" used multiple times\n");
218
printf (" --author Author of this track\n");
219
printf (" --title Title for this track\n");
220
printf (" -h, --help This help\n");
221
printf (" -v, --version Version information\n");
222
printf (" -V Verbose mode (show bit-rate)\n");
223
printf ("Raw input options:\n");
224
printf (" --rate n Sampling rate for raw input\n");
225
printf (" --mono Consider raw input as mono\n");
226
printf (" --stereo Consider raw input as stereo\n");
227
printf (" --le Raw input is little-endian\n");
228
printf (" --be Raw input is big-endian\n");
229
printf (" --8bit Raw input is 8-bit unsigned\n");
230
printf (" --16bit Raw input is 16-bit signed\n");
231
printf ("Default raw PCM input is 16-bit, little-endian, mono\n");
235
int main(int argc, char **argv)
237
int nb_samples, total_samples=0, nb_encoded;
239
int option_index = 0;
240
char *inFile, *outFile;
242
short input[MAX_FRAME_SIZE];
243
celt_int32_t frame_size;
248
unsigned char bits[MAX_FRAME_BYTES];
249
int with_skeleton = 0;
250
struct option long_options[] =
252
{"bitrate", required_argument, NULL, 0},
253
{"comp", required_argument, NULL, 0},
254
{"skeleton",no_argument,NULL, 0},
255
{"help", no_argument, NULL, 0},
256
{"quiet", no_argument, NULL, 0},
257
{"le", no_argument, NULL, 0},
258
{"be", no_argument, NULL, 0},
259
{"8bit", no_argument, NULL, 0},
260
{"16bit", no_argument, NULL, 0},
261
{"mono", no_argument, NULL, 0},
262
{"stereo", no_argument, NULL, 0},
263
{"rate", required_argument, NULL, 0},
264
{"version", no_argument, NULL, 0},
265
{"version-short", no_argument, NULL, 0},
266
{"comment", required_argument, NULL, 0},
267
{"author", required_argument, NULL, 0},
268
{"title", required_argument, NULL, 0},
272
celt_int32_t rate=44100;
278
ogg_stream_state so; /* ogg stream for skeleton bitstream */
281
int bytes_written=0, ret, result;
283
CELT051Header header;
284
char vendor_string[64];
287
int close_in=0, close_out=0;
289
celt_int32_t bitrate=-1;
290
char first_bytes[12];
292
celt_int32_t lookahead = 0;
293
int bytes_per_packet=48;
296
snprintf(vendor_string, sizeof(vendor_string), "Encoded with CELT\n");
298
comment_init(&comments, &comments_length, vendor_string);
300
/*Process command-line options*/
303
c = getopt_long (argc, argv, "hvV",
304
long_options, &option_index);
311
if (strcmp(long_options[option_index].name,"bitrate")==0)
313
bitrate = atoi (optarg);
314
} else if (strcmp(long_options[option_index].name,"skeleton")==0)
317
} else if (strcmp(long_options[option_index].name,"help")==0)
321
} else if (strcmp(long_options[option_index].name,"quiet")==0)
324
} else if (strcmp(long_options[option_index].name,"version")==0)
328
} else if (strcmp(long_options[option_index].name,"version-short")==0)
332
} else if (strcmp(long_options[option_index].name,"le")==0)
335
} else if (strcmp(long_options[option_index].name,"be")==0)
338
} else if (strcmp(long_options[option_index].name,"8bit")==0)
341
} else if (strcmp(long_options[option_index].name,"16bit")==0)
344
} else if (strcmp(long_options[option_index].name,"stereo")==0)
347
} else if (strcmp(long_options[option_index].name,"mono")==0)
350
} else if (strcmp(long_options[option_index].name,"rate")==0)
353
} else if (strcmp(long_options[option_index].name,"comp")==0)
355
complexity=atoi (optarg);
356
} else if (strcmp(long_options[option_index].name,"comment")==0)
358
if (!strchr(optarg, '='))
360
fprintf (stderr, "Invalid comment: %s\n", optarg);
361
fprintf (stderr, "Comments must be of the form name=value\n");
364
comment_add(&comments, &comments_length, NULL, optarg);
365
} else if (strcmp(long_options[option_index].name,"author")==0)
367
comment_add(&comments, &comments_length, "author=", optarg);
368
} else if (strcmp(long_options[option_index].name,"title")==0)
370
comment_add(&comments, &comments_length, "title=", optarg);
392
fprintf(stderr,"\nWARNING: This encoder is a CELT *PRERELEASE*. It produces streams that are\n"
393
" not decodable by ANY OTHER VERSION. These streams will NOT be\n"
394
" supported or decodable by any future CELT release.\n\n");
402
outFile=argv[optind+1];
404
/*Initialize Ogg stream struct*/
406
if (ogg_stream_init(&os, rand())==-1)
408
fprintf(stderr,"Error: stream init failed\n");
411
if (with_skeleton && ogg_stream_init(&so, rand())==-1)
413
fprintf(stderr,"Error: stream init failed\n");
417
if (strcmp(inFile, "-")==0)
419
#if defined WIN32 || defined _WIN32
420
_setmode(_fileno(stdin), _O_BINARY);
422
_fsetmode(stdin,"b");
428
fin = fopen(inFile, "rb");
438
fread(first_bytes, 1, 12, fin);
439
if (strncmp(first_bytes,"RIFF",4)==0 && strncmp(first_bytes,"RIFF",4)==0)
441
if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
444
lsb=1; /* CHECK: exists big-endian .wav ?? */
466
fprintf (stderr, "Only mono and stereo are supported\n");
470
mode = celt051_mode_create(rate, chan, 256, NULL);
473
celt051_mode_info(mode, CELT_GET_FRAME_SIZE, &frame_size);
475
bytes_per_packet = (bitrate*1000*frame_size/rate+4)/8;
477
celt051_header_init(&header, mode);
478
header.nb_channels = chan;
481
char *st_string="mono";
485
fprintf (stderr, "Encoding %d Hz audio using %s (%d bytes per packet)\n",
486
header.sample_rate, st_string, bytes_per_packet);
488
/*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n",
489
header.rate, mode->bitrate, mode->modeName);*/
491
/*Initialize CELT encoder*/
492
st = celt051_encoder_create(mode);
494
if (complexity!=-127) {
495
if (celt051_encoder_ctl(st, CELT_SET_COMPLEXITY(complexity)) != CELT_OK)
497
fprintf (stderr, "Only complexity 0 through 10 is supported\n");
502
if (strcmp(outFile,"-")==0)
504
#if defined WIN32 || defined _WIN32
505
_setmode(_fileno(stdout), _O_BINARY);
511
fout = fopen(outFile, "wb");
521
fprintf (stderr, "Warning: Enabling skeleton output may cause some decoders to fail.\n");
524
/* first packet should be the skeleton header. */
526
add_fishead_packet(&so);
527
if ((ret = flush_ogg_stream_to_file(&so, fout))) {
528
fprintf (stderr,"Error: failed skeleton (fishead) header to output stream\n");
531
bytes_written += ret;
536
unsigned char header_data[100];
537
int packet_size = celt051_header_to_packet(&header, header_data, 100);
538
op.packet = header_data;
539
op.bytes = packet_size;
544
ogg_stream_packetin(&os, &op);
546
while((result = ogg_stream_flush(&os, &og)))
549
ret = oe_write_page(&og, fout);
550
if(ret != og.header_len + og.body_len)
552
fprintf (stderr,"Error: failed writing header to output stream\n");
556
bytes_written += ret;
559
op.packet = (unsigned char *)comments;
560
op.bytes = comments_length;
565
ogg_stream_packetin(&os, &op);
568
/* fisbone packet should be write after all bos pages */
570
add_fisbone_packet(&so, os.serialno, &header);
571
if ((ret = flush_ogg_stream_to_file(&so, fout))) {
572
fprintf (stderr,"Error: failed writing skeleton (fisbone )header to output stream\n");
575
bytes_written += ret;
578
/* writing the rest of the celt header packets */
579
while((result = ogg_stream_flush(&os, &og)))
582
ret = oe_write_page(&og, fout);
583
if(ret != og.header_len + og.body_len)
585
fprintf (stderr,"Error: failed writing header to output stream\n");
589
bytes_written += ret;
594
/* write the skeleton eos packet */
596
add_eos_packet_to_stream(&so);
597
if ((ret = flush_ogg_stream_to_file(&so, fout))) {
598
fprintf (stderr,"Error: failed writing skeleton header to output stream\n");
601
bytes_written += ret;
607
nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL);
609
nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
613
total_samples += nb_samples;
614
nb_encoded = -lookahead;
615
/*Main encoding loop (one frame per iteration)*/
616
while (!eos || total_samples>nb_encoded)
619
/*Encode current frame*/
621
nbBytes = celt051_encode(st, input, NULL, bits, bytes_per_packet);
624
fprintf(stderr, "Got error %d while encoding. Aborting.\n", nbBytes);
627
nb_encoded += frame_size;
631
nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
633
nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL);
639
if (eos && total_samples<=nb_encoded)
643
total_samples += nb_samples;
645
op.packet = (unsigned char *)bits;
648
/*Is this redundent?*/
649
if (eos && total_samples<=nb_encoded)
653
op.granulepos = (id+1)*frame_size-lookahead;
654
if (op.granulepos>total_samples)
655
op.granulepos = total_samples;
656
/*printf ("granulepos: %d %d %d %d %d %d\n", (int)op.granulepos, id, nframes, lookahead, 5, 6);*/
658
ogg_stream_packetin(&os, &op);
660
/*Write all new pages (most likely 0 or 1)*/
661
while (ogg_stream_pageout(&os,&og))
663
ret = oe_write_page(&og, fout);
664
if(ret != og.header_len + og.body_len)
666
fprintf (stderr,"Error: failed writing header to output stream\n");
670
bytes_written += ret;
673
/*Flush all pages left to be written*/
674
while (ogg_stream_flush(&os, &og))
676
ret = oe_write_page(&og, fout);
677
if(ret != og.header_len + og.body_len)
679
fprintf (stderr,"Error: failed writing header to output stream\n");
683
bytes_written += ret;
686
celt051_encoder_destroy(st);
687
celt051_mode_destroy(mode);
688
ogg_stream_clear(&os);
698
Comments will be stored in the Vorbis style.
699
It is describled in the "Structure" section of
700
http://www.xiph.org/ogg/vorbis/doc/v-comment.html
702
The comment header is decoded as follows:
703
1) [vendor_length] = read an unsigned integer of 32 bits
704
2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
705
3) [user_comment_list_length] = read an unsigned integer of 32 bits
706
4) iterate [user_comment_list_length] times {
707
5) [length] = read an unsigned integer of 32 bits
708
6) this iteration's user comment = read a UTF-8 vector as [length] octets
710
7) [framing_bit] = read a single bit as boolean
711
8) if ( [framing_bit] unset or end of packet ) then ERROR
714
If you have troubles, please write to ymnk@jcraft.com.
717
#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
718
((buf[base+2]<<16)&0xff0000)| \
719
((buf[base+1]<<8)&0xff00)| \
721
#define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
722
buf[base+2]=((val)>>16)&0xff; \
723
buf[base+1]=((val)>>8)&0xff; \
724
buf[base]=(val)&0xff; \
727
void comment_init(char **comments, int* length, char *vendor_string)
729
int vendor_length=strlen(vendor_string);
730
int user_comment_list_length=0;
731
int len=4+vendor_length+4;
732
char *p=(char*)malloc(len);
734
fprintf (stderr, "malloc failed in comment_init()\n");
737
writeint(p, 0, vendor_length);
738
memcpy(p+4, vendor_string, vendor_length);
739
writeint(p, 4+vendor_length, user_comment_list_length);
743
void comment_add(char **comments, int* length, char *tag, char *val)
746
int vendor_length=readint(p, 0);
747
int user_comment_list_length=readint(p, 4+vendor_length);
748
int tag_len=(tag?strlen(tag):0);
749
int val_len=strlen(val);
750
int len=(*length)+4+tag_len+val_len;
752
p=(char*)realloc(p, len);
754
fprintf (stderr, "realloc failed in comment_add()\n");
758
writeint(p, *length, tag_len+val_len); /* length of comment */
759
if(tag) memcpy(p+*length+4, tag, tag_len); /* comment */
760
memcpy(p+*length+4+tag_len, val, val_len); /* comment */
761
writeint(p, 4+vendor_length, user_comment_list_length+1);