1 /* Copyright (C) 2002-2006 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
43 #ifndef HAVE_GETOPT_LONG
44 #include "getopt_win.h"
50 #include <speex/speex.h>
51 #include <ext/libogg/ogg.h>
53 #include <speex/speex_header.h>
54 #include <speex/speex_stereo.h>
55 #include <speex/speex_preprocess.h>
57 #if defined WIN32 || defined _WIN32
58 /* We need the following two to set stdout to binary */
66 void comment_init(char **comments, int* length, char *vendor_string);
67 void comment_add(char **comments, int* length, char *tag, char *val);
70 /*Write an Ogg page to a file pointer*/
71 int oe_write_page(ogg_page *page, FILE *fp)
74 written = fwrite(page->header,1,page->header_len, fp);
75 written += fwrite(page->body,1,page->body_len, fp);
80 #define MAX_FRAME_SIZE 2000
81 #define MAX_FRAME_BYTES 2000
83 /* Convert input audio bits, endians and channels */
84 static int read_samples(FILE *fin,int frame_size, int bits, int channels, int lsb, short * input, char *buff, spx_int32_t *size)
86 unsigned char in[MAX_FRAME_BYTES*2];
97 *size -= bits/8*channels*frame_size;
102 nb_read = fread(in+12,1,bits/8*channels*frame_size-12, fin) + 12;
106 nb_read = fread(in,1,bits/8*channels* frame_size, fin);
108 nb_read /= bits/8*channels;
110 /*fprintf (stderr, "%d\n", nb_read);*/
117 /* Convert 8->16 bits */
118 for(i=frame_size*channels-1;i>=0;i--)
120 s[i]=(in[i]<<8)^0x8000;
124 /* convert to our endian format */
125 for(i=0;i<frame_size*channels;i++)
134 /* FIXME: This is probably redundent now */
135 /* copy to float input buffer */
136 for (i=0;i<frame_size*channels;i++)
138 input[i]=(short)s[i];
141 for (i=nb_read*channels;i<frame_size*channels;i++)
150 void add_fishead_packet (ogg_stream_state *os) {
154 memset(&fp, 0, sizeof(fp));
160 add_fishead_to_stream(os, &fp);
164 * Adds the fishead packets in the skeleton output stream along with the e_o_s packet
166 void add_fisbone_packet (ogg_stream_state *os, spx_int32_t serialno, SpeexHeader *header) {
170 memset(&fp, 0, sizeof(fp));
171 fp.serial_no = serialno;
172 fp.nr_header_packet = 2 + header->extra_headers;
173 fp.granule_rate_n = header->rate;
174 fp.granule_rate_d = 1;
175 fp.start_granule = 0;
177 fp.granule_shift = 0;
179 add_message_header_field(&fp, "Content-Type", "audio/x-speex");
181 add_fisbone_to_stream(os, &fp);
186 const char* speex_version;
187 speex_lib_ctl(SPEEX_LIB_GET_VERSION_STRING, (void*)&speex_version);
188 printf ("speexenc (Speex encoder) version %s (compiled " __DATE__ ")\n", speex_version);
189 printf ("Copyright (C) 2002-2006 Jean-Marc Valin\n");
194 const char* speex_version;
195 speex_lib_ctl(SPEEX_LIB_GET_VERSION_STRING, (void*)&speex_version);
196 printf ("speexenc version %s\n", speex_version);
197 printf ("Copyright (C) 2002-2006 Jean-Marc Valin\n");
202 printf ("Usage: speexenc [options] input_file output_file\n");
204 printf ("Encodes input_file using Speex. It can read the WAV or raw files.\n");
206 printf ("input_file can be:\n");
207 printf (" filename.wav wav file\n");
208 printf (" filename.* Raw PCM file (any extension other than .wav)\n");
209 printf (" - stdin\n");
211 printf ("output_file can be:\n");
212 printf (" filename.spx Speex file\n");
213 printf (" - stdout\n");
215 printf ("Options:\n");
216 printf (" -n, --narrowband Narrowband (8 kHz) input file\n");
217 printf (" -w, --wideband Wideband (16 kHz) input file\n");
218 printf (" -u, --ultra-wideband \"Ultra-wideband\" (32 kHz) input file\n");
219 printf (" --quality n Encoding quality (0-10), default 8\n");
220 printf (" --bitrate n Encoding bit-rate (use bit-rate n or lower)\n");
221 printf (" --vbr Enable variable bit-rate (VBR)\n");
222 printf (" --vbr-max-bitrate Set max VBR bit-rate allowed\n");
223 printf (" --abr rate Enable average bit-rate (ABR) at rate bps\n");
224 printf (" --vad Enable voice activity detection (VAD)\n");
225 printf (" --dtx Enable file-based discontinuous transmission (DTX)\n");
226 printf (" --comp n Set encoding complexity (0-10), default 3\n");
227 printf (" --nframes n Number of frames per Ogg packet (1-10), default 1\n");
228 printf (" --denoise Denoise the input before encoding\n");
229 printf (" --agc Apply adaptive gain control (AGC) before encoding\n");
230 printf (" --skeleton Outputs ogg skeleton metadata (may cause incompatibilities)\n");
231 printf (" --comment Add the given string as an extra comment. This may be\n");
232 printf (" used multiple times\n");
233 printf (" --author Author of this track\n");
234 printf (" --title Title for this track\n");
235 printf (" -h, --help This help\n");
236 printf (" -v, --version Version information\n");
237 printf (" -V Verbose mode (show bit-rate)\n");
238 printf ("Raw input options:\n");
239 printf (" --rate n Sampling rate for raw input\n");
240 printf (" --stereo Consider raw input as stereo\n");
241 printf (" --le Raw input is little-endian\n");
242 printf (" --be Raw input is big-endian\n");
243 printf (" --8bit Raw input is 8-bit unsigned\n");
244 printf (" --16bit Raw input is 16-bit signed\n");
245 printf ("Default raw PCM input is 16-bit, little-endian, mono\n");
247 printf ("More information is available from the Speex site: http://www.speex.org\n");
249 printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n");
253 int main(int argc, char **argv)
255 int nb_samples, total_samples=0, nb_encoded;
257 int option_index = 0;
258 char *inFile, *outFile;
260 short input[MAX_FRAME_SIZE];
261 spx_int32_t frame_size;
263 spx_int32_t vbr_enabled=0;
264 spx_int32_t vbr_max=0;
266 spx_int32_t vad_enabled=0;
267 spx_int32_t dtx_enabled=0;
269 const SpeexMode *mode=NULL;
273 char cbits[MAX_FRAME_BYTES];
274 int with_skeleton = 0;
275 struct option long_options[] =
277 {"wideband", no_argument, NULL, 0},
278 {"ultra-wideband", no_argument, NULL, 0},
279 {"narrowband", no_argument, NULL, 0},
280 {"vbr", no_argument, NULL, 0},
281 {"vbr-max-bitrate", required_argument, NULL, 0},
282 {"abr", required_argument, NULL, 0},
283 {"vad", no_argument, NULL, 0},
284 {"dtx", no_argument, NULL, 0},
285 {"quality", required_argument, NULL, 0},
286 {"bitrate", required_argument, NULL, 0},
287 {"nframes", required_argument, NULL, 0},
288 {"comp", required_argument, NULL, 0},
289 {"denoise", no_argument, NULL, 0},
290 {"agc", no_argument, NULL, 0},
291 {"skeleton",no_argument,NULL, 0},
292 {"help", no_argument, NULL, 0},
293 {"quiet", no_argument, NULL, 0},
294 {"le", no_argument, NULL, 0},
295 {"be", no_argument, NULL, 0},
296 {"8bit", no_argument, NULL, 0},
297 {"16bit", no_argument, NULL, 0},
298 {"stereo", no_argument, NULL, 0},
299 {"rate", required_argument, NULL, 0},
300 {"version", no_argument, NULL, 0},
301 {"version-short", no_argument, NULL, 0},
302 {"comment", required_argument, NULL, 0},
303 {"author", required_argument, NULL, 0},
304 {"title", required_argument, NULL, 0},
312 spx_int32_t quality=-1;
313 float vbr_quality=-1;
316 ogg_stream_state so; /* ogg stream for skeleton bitstream */
319 int bytes_written=0, ret, result;
323 spx_int32_t complexity=3;
324 const char* speex_version;
325 char vendor_string[64];
328 int close_in=0, close_out=0;
330 spx_int32_t bitrate=0;
331 double cumul_bits=0, enc_frames=0;
332 char first_bytes[12];
335 SpeexPreprocessState *preprocess = NULL;
336 int denoise_enabled=0, agc_enabled=0;
337 spx_int32_t lookahead = 0;
339 speex_lib_ctl(SPEEX_LIB_GET_VERSION_STRING, (void*)&speex_version);
340 snprintf(vendor_string, sizeof(vendor_string), "Encoded with Speex %s", speex_version);
342 comment_init(&comments, &comments_length, vendor_string);
344 /*Process command-line options*/
347 c = getopt_long (argc, argv, "nwuhvV",
348 long_options, &option_index);
355 if (strcmp(long_options[option_index].name,"narrowband")==0)
357 modeID = SPEEX_MODEID_NB;
358 } else if (strcmp(long_options[option_index].name,"wideband")==0)
360 modeID = SPEEX_MODEID_WB;
361 } else if (strcmp(long_options[option_index].name,"ultra-wideband")==0)
363 modeID = SPEEX_MODEID_UWB;
364 } else if (strcmp(long_options[option_index].name,"vbr")==0)
367 } else if (strcmp(long_options[option_index].name,"vbr-max-bitrate")==0)
369 vbr_max=atoi(optarg);
372 fprintf (stderr, "Invalid VBR max bit-rate value: %d\n", vbr_max);
375 } else if (strcmp(long_options[option_index].name,"abr")==0)
377 abr_enabled=atoi(optarg);
380 fprintf (stderr, "Invalid ABR value: %d\n", abr_enabled);
383 } else if (strcmp(long_options[option_index].name,"vad")==0)
386 } else if (strcmp(long_options[option_index].name,"dtx")==0)
389 } else if (strcmp(long_options[option_index].name,"quality")==0)
391 quality = atoi (optarg);
392 vbr_quality=atof(optarg);
393 } else if (strcmp(long_options[option_index].name,"bitrate")==0)
395 bitrate = atoi (optarg);
396 } else if (strcmp(long_options[option_index].name,"nframes")==0)
398 nframes = atoi (optarg);
403 } else if (strcmp(long_options[option_index].name,"comp")==0)
405 complexity = atoi (optarg);
406 } else if (strcmp(long_options[option_index].name,"denoise")==0)
409 } else if (strcmp(long_options[option_index].name,"agc")==0)
412 } else if (strcmp(long_options[option_index].name,"skeleton")==0)
415 } else if (strcmp(long_options[option_index].name,"help")==0)
419 } else if (strcmp(long_options[option_index].name,"quiet")==0)
422 } else if (strcmp(long_options[option_index].name,"version")==0)
426 } else if (strcmp(long_options[option_index].name,"version-short")==0)
430 } else if (strcmp(long_options[option_index].name,"le")==0)
433 } else if (strcmp(long_options[option_index].name,"be")==0)
436 } else if (strcmp(long_options[option_index].name,"8bit")==0)
439 } else if (strcmp(long_options[option_index].name,"16bit")==0)
442 } else if (strcmp(long_options[option_index].name,"stereo")==0)
445 } else if (strcmp(long_options[option_index].name,"rate")==0)
448 } else if (strcmp(long_options[option_index].name,"comment")==0)
450 if (!strchr(optarg, '='))
452 fprintf (stderr, "Invalid comment: %s\n", optarg);
453 fprintf (stderr, "Comments must be of the form name=value\n");
456 comment_add(&comments, &comments_length, NULL, optarg);
457 } else if (strcmp(long_options[option_index].name,"author")==0)
459 comment_add(&comments, &comments_length, "author=", optarg);
460 } else if (strcmp(long_options[option_index].name,"title")==0)
462 comment_add(&comments, &comments_length, "title=", optarg);
467 modeID = SPEEX_MODEID_NB;
481 modeID = SPEEX_MODEID_WB;
484 modeID = SPEEX_MODEID_UWB;
498 outFile=argv[optind+1];
500 /*Initialize Ogg stream struct*/
502 if (ogg_stream_init(&os, rand())==-1)
504 fprintf(stderr,"Error: stream init failed\n");
507 if (with_skeleton && ogg_stream_init(&so, rand())==-1)
509 fprintf(stderr,"Error: stream init failed\n");
513 if (strcmp(inFile, "-")==0)
515 #if defined WIN32 || defined _WIN32
516 _setmode(_fileno(stdin), _O_BINARY);
518 _fsetmode(stdin,"b");
524 fin = fopen(inFile, "rb");
534 fread(first_bytes, 1, 12, fin);
535 if (strncmp(first_bytes,"RIFF",4)==0 && strncmp(first_bytes,"RIFF",4)==0)
537 if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
540 lsb=1; /* CHECK: exists big-endian .wav ?? */
544 if (modeID==-1 && !rate)
546 /* By default, use narrowband/8 kHz */
547 modeID = SPEEX_MODEID_NB;
549 } else if (modeID!=-1 && rate)
551 mode = speex_lib_get_mode (modeID);
554 fprintf (stderr, "Error: sampling rate too high: %d Hz, try down-sampling\n", rate);
556 } else if (rate>25000)
558 if (modeID != SPEEX_MODEID_UWB)
560 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);
562 } else if (rate>12500)
564 if (modeID != SPEEX_MODEID_WB)
566 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);
568 } else if (rate>=6000)
570 if (modeID != SPEEX_MODEID_NB)
572 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);
575 fprintf (stderr, "Error: sampling rate too low: %d Hz\n", rate);
578 } else if (modeID==-1)
582 fprintf (stderr, "Error: sampling rate too high: %d Hz, try down-sampling\n", rate);
584 } else if (rate>25000)
586 modeID = SPEEX_MODEID_UWB;
587 } else if (rate>12500)
589 modeID = SPEEX_MODEID_WB;
590 } else if (rate>=6000)
592 modeID = SPEEX_MODEID_NB;
594 fprintf (stderr, "Error: Sampling rate too low: %d Hz\n", rate);
599 if (modeID == SPEEX_MODEID_NB)
601 else if (modeID == SPEEX_MODEID_WB)
603 else if (modeID == SPEEX_MODEID_UWB)
608 if (rate!=8000 && rate!=16000 && rate!=32000)
609 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);
612 mode = speex_lib_get_mode (modeID);
614 speex_init_header(&header, rate, 1, mode);
615 header.frames_per_packet=nframes;
616 header.vbr=vbr_enabled;
617 header.nb_channels = chan;
620 char *st_string="mono";
624 fprintf (stderr, "Encoding %d Hz audio using %s mode (%s)\n",
625 header.rate, mode->modeName, st_string);
627 /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n",
628 header.rate, mode->bitrate, mode->modeName);*/
630 /*Initialize Speex encoder*/
631 st = speex_encoder_init(mode);
633 if (strcmp(outFile,"-")==0)
635 #if defined WIN32 || defined _WIN32
636 _setmode(_fileno(stdout), _O_BINARY);
642 fout = fopen(outFile, "wb");
651 speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
652 speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity);
653 speex_encoder_ctl(st, SPEEX_SET_SAMPLING_RATE, &rate);
660 speex_encoder_ctl(st, SPEEX_SET_VBR_MAX_BITRATE, &vbr_max);
661 speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &vbr_quality);
664 speex_encoder_ctl(st, SPEEX_SET_QUALITY, &quality);
668 if (quality >= 0 && vbr_enabled)
669 fprintf (stderr, "Warning: --bitrate option is overriding --quality\n");
670 speex_encoder_ctl(st, SPEEX_SET_BITRATE, &bitrate);
675 speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
676 } else if (vad_enabled)
679 speex_encoder_ctl(st, SPEEX_SET_VAD, &tmp);
682 speex_encoder_ctl(st, SPEEX_SET_DTX, &tmp);
683 if (dtx_enabled && !(vbr_enabled || abr_enabled || vad_enabled))
685 fprintf (stderr, "Warning: --dtx is useless without --vad, --vbr or --abr\n");
686 } else if ((vbr_enabled || abr_enabled) && (vad_enabled))
688 fprintf (stderr, "Warning: --vad is already implied by --vbr or --abr\n");
691 fprintf (stderr, "Warning: Enabling skeleton output may cause some decoders to fail.\n");
696 speex_encoder_ctl(st, SPEEX_SET_ABR, &abr_enabled);
699 speex_encoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead);
701 if (denoise_enabled || agc_enabled)
703 preprocess = speex_preprocess_state_init(frame_size, rate);
704 speex_preprocess_ctl(preprocess, SPEEX_PREPROCESS_SET_DENOISE, &denoise_enabled);
705 speex_preprocess_ctl(preprocess, SPEEX_PREPROCESS_SET_AGC, &agc_enabled);
706 lookahead += frame_size;
709 /* first packet should be the skeleton header. */
712 add_fishead_packet(&so);
713 if ((ret = flush_ogg_stream_to_file(&so, fout))) {
714 fprintf (stderr,"Error: failed skeleton (fishead) header to output stream\n");
717 bytes_written += ret;
723 op.packet = (unsigned char *)speex_header_to_packet(&header, &packet_size);
724 op.bytes = packet_size;
729 ogg_stream_packetin(&os, &op);
732 while((result = ogg_stream_flush(&os, &og)))
735 ret = oe_write_page(&og, fout);
736 if(ret != og.header_len + og.body_len)
738 fprintf (stderr,"Error: failed writing header to output stream\n");
742 bytes_written += ret;
745 op.packet = (unsigned char *)comments;
746 op.bytes = comments_length;
751 ogg_stream_packetin(&os, &op);
754 /* fisbone packet should be write after all bos pages */
756 add_fisbone_packet(&so, os.serialno, &header);
757 if ((ret = flush_ogg_stream_to_file(&so, fout))) {
758 fprintf (stderr,"Error: failed writing skeleton (fisbone )header to output stream\n");
761 bytes_written += ret;
764 /* writing the rest of the speex header packets */
765 while((result = ogg_stream_flush(&os, &og)))
768 ret = oe_write_page(&og, fout);
769 if(ret != og.header_len + og.body_len)
771 fprintf (stderr,"Error: failed writing header to output stream\n");
775 bytes_written += ret;
780 /* write the skeleton eos packet */
782 add_eos_packet_to_stream(&so);
783 if ((ret = flush_ogg_stream_to_file(&so, fout))) {
784 fprintf (stderr,"Error: failed writing skeleton header to output stream\n");
787 bytes_written += ret;
791 speex_bits_init(&bits);
795 nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL);
797 nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
801 total_samples += nb_samples;
802 nb_encoded = -lookahead;
803 /*Main encoding loop (one frame per iteration)*/
804 while (!eos || total_samples>nb_encoded)
807 /*Encode current frame*/
809 speex_encode_stereo_int(input, frame_size, &bits);
812 speex_preprocess(preprocess, input, NULL);
814 speex_encode_int(st, input, &bits);
816 nb_encoded += frame_size;
820 speex_encoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
826 if (vad_enabled || vbr_enabled || abr_enabled)
827 fprintf (stderr, "Bitrate is use: %d bps (average %d bps) ", tmp, (int)(cumul_bits/enc_frames));
829 fprintf (stderr, "Bitrate is use: %d bps ", tmp);
836 nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
838 nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL);
844 if (eos && total_samples<=nb_encoded)
848 total_samples += nb_samples;
850 if ((id+1)%nframes!=0)
852 speex_bits_insert_terminator(&bits);
853 nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
854 speex_bits_reset(&bits);
855 op.packet = (unsigned char *)cbits;
858 /*Is this redundent?*/
859 if (eos && total_samples<=nb_encoded)
863 op.granulepos = (id+1)*frame_size-lookahead;
864 if (op.granulepos>total_samples)
865 op.granulepos = total_samples;
866 /*printf ("granulepos: %d %d %d %d %d %d\n", (int)op.granulepos, id, nframes, lookahead, 5, 6);*/
867 op.packetno = 2+id/nframes;
868 ogg_stream_packetin(&os, &op);
870 /*Write all new pages (most likely 0 or 1)*/
871 while (ogg_stream_pageout(&os,&og))
873 ret = oe_write_page(&og, fout);
874 if(ret != og.header_len + og.body_len)
876 fprintf (stderr,"Error: failed writing header to output stream\n");
880 bytes_written += ret;
883 if ((id+1)%nframes!=0)
885 while ((id+1)%nframes!=0)
888 speex_bits_pack(&bits, 15, 5);
890 nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
891 op.packet = (unsigned char *)cbits;
895 op.granulepos = (id+1)*frame_size-lookahead;
896 if (op.granulepos>total_samples)
897 op.granulepos = total_samples;
899 op.packetno = 2+id/nframes;
900 ogg_stream_packetin(&os, &op);
902 /*Flush all pages left to be written*/
903 while (ogg_stream_flush(&os, &og))
905 ret = oe_write_page(&og, fout);
906 if(ret != og.header_len + og.body_len)
908 fprintf (stderr,"Error: failed writing header to output stream\n");
912 bytes_written += ret;
915 speex_encoder_destroy(st);
916 speex_bits_destroy(&bits);
917 ogg_stream_clear(&os);
927 Comments will be stored in the Vorbis style.
928 It is describled in the "Structure" section of
929 http://www.xiph.org/ogg/vorbis/doc/v-comment.html
931 The comment header is decoded as follows:
932 1) [vendor_length] = read an unsigned integer of 32 bits
933 2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
934 3) [user_comment_list_length] = read an unsigned integer of 32 bits
935 4) iterate [user_comment_list_length] times {
936 5) [length] = read an unsigned integer of 32 bits
937 6) this iteration's user comment = read a UTF-8 vector as [length] octets
939 7) [framing_bit] = read a single bit as boolean
940 8) if ( [framing_bit] unset or end of packet ) then ERROR
943 If you have troubles, please write to ymnk@jcraft.com.
946 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
947 ((buf[base+2]<<16)&0xff0000)| \
948 ((buf[base+1]<<8)&0xff00)| \
950 #define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
951 buf[base+2]=((val)>>16)&0xff; \
952 buf[base+1]=((val)>>8)&0xff; \
953 buf[base]=(val)&0xff; \
956 void comment_init(char **comments, int* length, char *vendor_string)
958 int vendor_length=strlen(vendor_string);
959 int user_comment_list_length=0;
960 int len=4+vendor_length+4;
961 char *p=(char*)malloc(len);
963 fprintf (stderr, "malloc failed in comment_init()\n");
966 writeint(p, 0, vendor_length);
967 memcpy(p+4, vendor_string, vendor_length);
968 writeint(p, 4+vendor_length, user_comment_list_length);
972 void comment_add(char **comments, int* length, char *tag, char *val)
975 int vendor_length=readint(p, 0);
976 int user_comment_list_length=readint(p, 4+vendor_length);
977 int tag_len=(tag?strlen(tag):0);
978 int val_len=strlen(val);
979 int len=(*length)+4+tag_len+val_len;
981 p=(char*)realloc(p, len);
983 fprintf (stderr, "realloc failed in comment_add()\n");
987 writeint(p, *length, tag_len+val_len); /* length of comment */
988 if(tag) memcpy(p+*length+4, tag, tag_len); /* comment */
989 memcpy(p+*length+4+tag_len, val, val_len); /* comment */
990 writeint(p, 4+vendor_length, user_comment_list_length+1);