ref: 1cea8f6bf1e3ffec1a408b3ead5e7ca2febf7419
parent: 5a2543d273ded24ae67066d43ade313525e215d7
author: Timothy B. Terriberry <tterribe@xiph.org>
date: Wed Jan 9 13:38:55 EST 2013
Make opusfile_example output WAV.
--- a/examples/opusfile_example.c
+++ b/examples/opusfile_example.c
@@ -24,14 +24,6 @@
#endif
#include <opusfile.h>
-#if defined(OP_FIXED_POINT)
-typedef opus_int16 op_sample;
-# define op_read_native_stereo op_read_stereo
-#else
-typedef float op_sample;
-# define op_read_native_stereo op_read_float_stereo
-#endif
-
static void print_duration(FILE *_fp,ogg_int64_t _nsamples,int _frac){
ogg_int64_t seconds;
ogg_int64_t minutes;
@@ -97,15 +89,50 @@
else fprintf(_fp,"%li%s%c",(long)val,_spacer,SUFFIXES[shift]);
}
+static void put_le32(unsigned char *_dst,opus_uint32 _x){
+ _dst[0]=(unsigned char)(_x&0xFF);
+ _dst[1]=(unsigned char)(_x>>8&0xFF);
+ _dst[2]=(unsigned char)(_x>>16&0xFF);
+ _dst[3]=(unsigned char)(_x>>24&0xFF);
+}
+
+/*Make a header for a 48 kHz, stereo, signed, 16-bit little-endian PCM WAV.*/
+static void make_wav_header(unsigned char _dst[44],ogg_int64_t _duration){
+ /*The chunk sizes are set to 0x7FFFFFFF by default.
+ Many, though not all, programs will interpret this to mean the duration is
+ "undefined", and continue to read from the file so long as there is actual
+ data.*/
+ static const unsigned char WAV_HEADER_TEMPLATE[44]={
+ 'R','I','F','F',0xFF,0xFF,0xFF,0x7F,
+ 'W','A','V','E','f','m','t',' ',
+ 0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00,
+ 0x80,0xBB,0x00,0x00,0x00,0xEE,0x02,0x00,
+ 0x04,0x00,0x10,0x00,'d','a','t','a',
+ 0xFF,0xFF,0xFF,0x7F
+ };
+ memcpy(_dst,WAV_HEADER_TEMPLATE,sizeof(WAV_HEADER_TEMPLATE));
+ if(_duration>0){
+ if(_duration>0x1FFFFFF6){
+ fprintf(stderr,"WARNING: WAV output would be larger than 2 GB.\n");
+ fprintf(stderr,
+ "Writing non-standard WAV header with invalid chunk sizes.\n");
+ }
+ else{
+ opus_uint32 audio_size;
+ audio_size=(opus_uint32)(_duration*4);
+ put_le32(_dst+4,audio_size+36);
+ put_le32(_dst+40,audio_size);
+ }
+ }
+}
+
int main(int _argc,const char **_argv){
- OggOpusFile *of;
- ogg_int64_t pcm_offset;
- ogg_int64_t pcm_print_offset;
- ogg_int64_t nsamples;
- opus_int32 bitrate;
- int ret;
- int prev_li;
- int is_ssl;
+ OggOpusFile *of;
+ ogg_int64_t duration;
+ unsigned char wav_header[44];
+ int ret;
+ int is_ssl;
+ int output_seekable;
#if defined(_WIN32)
# undef fileno
# define fileno _fileno
@@ -150,8 +177,9 @@
fprintf(stderr,"Failed to open file '%s': %i\n",_argv[1],ret);
return EXIT_FAILURE;
}
+ duration=0;
+ output_seekable=fseek(stdout,0,SEEK_CUR)!=-1;
if(op_seekable(of)){
- ogg_int64_t duration;
opus_int64 size;
fprintf(stderr,"Total number of links: %i\n",op_link_count(of));
duration=op_pcm_total(of,-1);
@@ -163,104 +191,144 @@
print_size(stderr,size,0,"");
fprintf(stderr,"\n");
}
- prev_li=-1;
- nsamples=0;
- pcm_offset=op_pcm_tell(of);
- if(pcm_offset!=0){
- fprintf(stderr,"Non-zero starting PCM offset: %li\n",(long)pcm_offset);
+ else if(!output_seekable){
+ fprintf(stderr,"WARNING: Neither input nor output are seekable.\n");
+ fprintf(stderr,
+ "Writing non-standard WAV header with invalid chunk sizes.\n");
}
- pcm_print_offset=pcm_offset-48000;
- bitrate=0;
- for(;;){
- ogg_int64_t next_pcm_offset;
- op_sample pcm[120*48*2];
- int li;
- ret=op_read_native_stereo(of,pcm,sizeof(pcm)/sizeof(*pcm));
- if(ret<0){
- fprintf(stderr,"\nError decoding '%s': %i\n",_argv[1],ret);
- if(is_ssl)fprintf(stderr,"Possible truncation attack?\n");
- ret=EXIT_FAILURE;
- break;
+ make_wav_header(wav_header,duration);
+ if(!fwrite(wav_header,sizeof(wav_header),1,stdout)){
+ fprintf(stderr,"Error writing WAV header: %s\n",strerror(errno));
+ ret=EXIT_FAILURE;
+ }
+ else{
+ ogg_int64_t pcm_offset;
+ ogg_int64_t pcm_print_offset;
+ ogg_int64_t nsamples;
+ opus_int32 bitrate;
+ int prev_li;
+ prev_li=-1;
+ nsamples=0;
+ pcm_offset=op_pcm_tell(of);
+ if(pcm_offset!=0){
+ fprintf(stderr,"Non-zero starting PCM offset: %li\n",(long)pcm_offset);
}
- li=op_current_link(of);
- if(li!=prev_li){
- const OpusHead *head;
- const OpusTags *tags;
- int ci;
- /*We found a new link.
- Print out some information.*/
- fprintf(stderr,"Decoding link %i: \n",li);
- head=op_head(of,li);
- fprintf(stderr," Channels: %i\n",head->channel_count);
- if(op_seekable(of)){
- ogg_int64_t duration;
- opus_int64 size;
- duration=op_pcm_total(of,li);
- fprintf(stderr," Duration: ");
- print_duration(stderr,duration,3);
- fprintf(stderr," (%li samples @ 48 kHz)\n",(long)duration);
- size=op_raw_total(of,li);
- fprintf(stderr," Size: ");
- print_size(stderr,size,0,"");
+ pcm_print_offset=pcm_offset-48000;
+ bitrate=0;
+ for(;;){
+ ogg_int64_t next_pcm_offset;
+ opus_int16 pcm[120*48*2];
+ unsigned char out[120*48*2*2];
+ int li;
+ int si;
+ /*Although we would generally prefer to use the float interface, WAV
+ files with signed, 16-bit little-endian samples are far more
+ universally supported, so that's what we output.*/
+ ret=op_read_stereo(of,pcm,sizeof(pcm)/sizeof(*pcm));
+ if(ret<0){
+ fprintf(stderr,"\nError decoding '%s': %i\n",_argv[1],ret);
+ if(is_ssl)fprintf(stderr,"Possible truncation attack?\n");
+ ret=EXIT_FAILURE;
+ break;
+ }
+ li=op_current_link(of);
+ if(li!=prev_li){
+ const OpusHead *head;
+ const OpusTags *tags;
+ int ci;
+ /*We found a new link.
+ Print out some information.*/
+ fprintf(stderr,"Decoding link %i: \n",li);
+ head=op_head(of,li);
+ fprintf(stderr," Channels: %i\n",head->channel_count);
+ if(op_seekable(of)){
+ ogg_int64_t duration;
+ opus_int64 size;
+ duration=op_pcm_total(of,li);
+ fprintf(stderr," Duration: ");
+ print_duration(stderr,duration,3);
+ fprintf(stderr," (%li samples @ 48 kHz)\n",(long)duration);
+ size=op_raw_total(of,li);
+ fprintf(stderr," Size: ");
+ print_size(stderr,size,0,"");
+ fprintf(stderr,"\n");
+ }
+ if(head->input_sample_rate){
+ fprintf(stderr," Original sampling rate: %lu Hz\n",
+ (unsigned long)head->input_sample_rate);
+ }
+ tags=op_tags(of,li);
+ fprintf(stderr," Encoded by: %s\n",tags->vendor);
+ for(ci=0;ci<tags->comments;ci++){
+ fprintf(stderr," %s\n",tags->user_comments[ci]);
+ }
fprintf(stderr,"\n");
+ if(!op_seekable(of)){
+ pcm_offset=op_pcm_tell(of)-ret;
+ if(pcm_offset!=0){
+ fprintf(stderr,"Non-zero starting PCM offset in link %i: %li\n",
+ li,(long)pcm_offset);
+ }
+ }
}
- if(head->input_sample_rate){
- fprintf(stderr," Original sampling rate: %lu Hz\n",
- (unsigned long)head->input_sample_rate);
+ if(li!=prev_li||pcm_offset>=pcm_print_offset+48000){
+ opus_int32 next_bitrate;
+ opus_int64 raw_offset;
+ next_bitrate=op_bitrate_instant(of);
+ if(next_bitrate>=0)bitrate=next_bitrate;
+ raw_offset=op_raw_tell(of);
+ fprintf(stderr,"\r ");
+ print_size(stderr,raw_offset,0,"");
+ fprintf(stderr," ");
+ print_duration(stderr,pcm_offset,0);
+ fprintf(stderr," (");
+ print_size(stderr,bitrate,1," ");
+ fprintf(stderr,"bps) \r");
+ pcm_print_offset=pcm_offset;
}
- tags=op_tags(of,li);
- fprintf(stderr," Encoded by: %s\n",tags->vendor);
- for(ci=0;ci<tags->comments;ci++){
- fprintf(stderr," %s\n",tags->user_comments[ci]);
+ next_pcm_offset=op_pcm_tell(of);
+ if(pcm_offset+ret!=next_pcm_offset){
+ fprintf(stderr,"\nPCM offset gap! %li+%i!=%li\n",
+ (long)pcm_offset,ret,(long)next_pcm_offset);
}
- fprintf(stderr,"\n");
- if(!op_seekable(of)){
- pcm_offset=op_pcm_tell(of)-ret;
- if(pcm_offset!=0){
- fprintf(stderr,"Non-zero starting PCM offset in link %i: %li\n",
- li,(long)pcm_offset);
- }
+ pcm_offset=next_pcm_offset;
+ if(ret<=0){
+ ret=EXIT_SUCCESS;
+ break;
}
+ /*Ensure the data is little-endian before writing it out.*/
+ for(si=0;si<2*ret;si++){
+ out[2*si+0]=(unsigned char)(pcm[si]&0xFF);
+ out[2*si+1]=(unsigned char)(pcm[si]>>8&0xFF);
+ }
+ if(!fwrite(out,sizeof(*out)*4,ret,stdout)){
+ fprintf(stderr,"\nError writing decoded audio data: %s\n",
+ strerror(errno));
+ ret=EXIT_FAILURE;
+ break;
+ }
+ nsamples+=ret;
+ prev_li=li;
}
- if(li!=prev_li||pcm_offset>=pcm_print_offset+48000){
- opus_int32 next_bitrate;
- opus_int64 raw_offset;
- next_bitrate=op_bitrate_instant(of);
- if(next_bitrate>=0)bitrate=next_bitrate;
- raw_offset=op_raw_tell(of);
- fprintf(stderr,"\r ");
- print_size(stderr,raw_offset,0,"");
- fprintf(stderr," ");
- print_duration(stderr,pcm_offset,0);
- fprintf(stderr," (");
- print_size(stderr,bitrate,1," ");
- fprintf(stderr,"bps) \r");
- pcm_print_offset=pcm_offset;
+ if(ret==EXIT_SUCCESS){
+ fprintf(stderr,"\nDone: played ");
+ print_duration(stderr,nsamples,3);
+ fprintf(stderr," (%li samples @ 48 kHz).\n",(long)nsamples);
}
- next_pcm_offset=op_pcm_tell(of);
- if(pcm_offset+ret!=next_pcm_offset){
- fprintf(stderr,"\nPCM offset gap! %li+%i!=%li\n",
- (long)pcm_offset,ret,(long)next_pcm_offset);
+ if(op_seekable(of)&&nsamples!=duration){
+ fprintf(stderr,"\nWARNING: "
+ "Number of output samples does not match declared file duration.\n");
+ if(!output_seekable)fprintf(stderr,"Output WAV file will be corrupt.\n");
}
- pcm_offset=next_pcm_offset;
- if(ret<=0){
- ret=EXIT_SUCCESS;
- break;
+ if(output_seekable&&nsamples!=duration){
+ make_wav_header(wav_header,nsamples);
+ if(fseek(stdout,0,SEEK_SET)||
+ !fwrite(wav_header,sizeof(wav_header),1,stdout)){
+ fprintf(stderr,"Error rewriting WAV header: %s\n",strerror(errno));
+ ret=EXIT_FAILURE;
+ }
}
- if(!fwrite(pcm,sizeof(*pcm)*2,ret,stdout)){
- fprintf(stderr,"\nError writing decoded audio data: %s\n",
- strerror(errno));
- ret=EXIT_FAILURE;
- break;
- }
- nsamples+=ret;
- prev_li=li;
}
op_free(of);
- if(ret==EXIT_SUCCESS){
- fprintf(stderr,"\nDone: played ");
- print_duration(stderr,nsamples,3);
- fprintf(stderr," (%li samples @ 48 kHz).\n",(long)nsamples);
- }
return ret;
}