Main Page   Modules   Class Hierarchy   Compound List   File List   Compound Members   File Members  

out_vorbis.cpp

00001 /* MuSE - Multiple Streaming Engine
00002  * Copyright (C) 2000-2003 Denis Rojo aka jaromil <jaromil@dyne.org>
00003  *
00004  * This source code is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Public License as published 
00006  * by the Free Software Foundation; either version 2 of the License,
00007  * or (at your option) any later version.
00008  *
00009  * This source code is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00012  * Please refer to the GNU Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Public License along with
00015  * this source code; if not, write to:
00016  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  *
00018  * "$Id: out_vorbis.cpp,v 1.4 2004/03/09 21:44:26 jaromil Exp $"
00019  *
00020  */
00021 
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <inttypes.h>
00025 #include <out_vorbis.h>
00026 #ifdef HAVE_VORBIS
00027 
00028 #include <jutils.h>
00029 #include <generic.h>
00030 #include <config.h>
00031 
00032 OutVorbis::OutVorbis()
00033   : OutChannel("vorbis") {
00034   func("OutVorbis::OutVorbis() %p",this);
00035 
00036   headersize = 0;
00037 
00038   sprintf(name,"Ogg/Vorbis encoder");
00039   sprintf(version,"version unknown");
00040 
00041   tipo = OGG;
00042 
00043   /* initialize resampler */
00044 
00045   pcm = (float*) malloc(INBUF);
00046   rsmpled = (float*) malloc(RSMPBUF);
00047   rsmp_state = NULL;
00048 }
00049 
00050 
00051 bool OutVorbis::init() {
00052   func("OutVorbis::init() %p",this);
00053 
00054   vorbis_info_init(&vi);  
00055 
00056   act("initializing %s %i",name,vi.version);
00057 
00058 
00059   initialized = true;
00060 
00061 
00062   
00063   if(!apply_profile()) {
00064     error("problems in setting up codec parameters");
00065     //    vorbis_info_clear(&vi);
00066     return false;
00067   }
00068 
00069   vorbis_encode_setup_init(&vi);
00070 
00071   /* Now, set up the analysis engine, stream encoder, and other
00072      preparation before the encoding begins. */
00073   vorbis_analysis_init(&vd,&vi);
00074   vorbis_block_init(&vd,&vb);
00075   ogg_stream_init(&os, time(NULL));
00076 
00077   /* sets up our comments */
00078   {
00079     char tmp[128];
00080     sprintf(tmp,"%s version %s",PACKAGE,VERSION);
00081     vorbis_comment_init(&vc);
00082     vorbis_comment_add_tag(&vc,"Streamed with",tmp);
00083   }
00084 
00085   /* Now, build the three header packets and send through to the stream 
00086      output stage (but defer actual file output until the main encode loop) */
00087 
00088   /* Build the packets */
00089   vorbis_analysis_headerout
00090     (&vd,&vc,&header_main,&header_comments,&header_codebooks);
00091 
00092   /* And stream them out */
00093   ogg_stream_packetin(&os,&header_main);
00094   ogg_stream_packetin(&os,&header_comments);
00095   ogg_stream_packetin(&os,&header_codebooks);
00096   
00097   /* write out headers */
00098   headersize = 0;
00099   _pbyte = (int8_t*)header; 
00100   while(ogg_stream_flush(&os,&og)) {
00101     memcpy(_pbyte,og.header,og.header_len);
00102     _pbyte += og.header_len;
00103     memcpy(_pbyte,og.body,og.body_len);
00104     _pbyte += og.body_len;
00105     headersize += og.header_len + og.body_len;
00106   }
00107 
00108   vorbis_comment_clear(&vc);
00109 
00110   initialized = true;
00111   return initialized;
00112 }
00113 
00114 /* note: num is number of samples in the L (or R) channel
00115    not the total number of samples in pcm[] */
00116 int OutVorbis::encode() {  
00117   int num = 0, samples = 0;
00118   int rsmperr, res;
00119   _pbyte = (int8_t*)buffer;
00120 
00121   if(headersize) {
00122     memcpy(buffer,header,headersize);
00123     encoded = headersize;
00124     shout(); dump();
00125   }
00126   
00127   encoded = headersize = 0;
00128 
00129   if(erbapipa->size() < out_chunk_len) { // bytes, not samples, 16bit stereo stuff
00130     //    func("OutVorbis::encode : not enough data in pipe");
00131     return -1;
00132   }
00133 
00134   /* this takes SAMPLES and returns SAMPLES */
00135   num = erbapipa->read_float_intl(OUT_CHUNK,pcm,channels());
00136 
00137   if(num<OUT_CHUNK)
00138     func("OutVorbis::encode() : erbapipa->read_float_bidi reads %i samples instead of %i",
00139          num,OUT_CHUNK);
00140   if(num<1) return num;
00141   
00142   rsmp_data.data_in = pcm;
00143   rsmp_data.input_frames = num;
00144   rsmp_data.data_out = rsmpled;
00145   rsmp_data.output_frames = RSMPBUF;
00146 
00147   /* do the resampling */
00148   rsmperr = src_process(rsmp_state,&rsmp_data);
00149   if(rsmperr) {
00150     error("error in ogg resampler: %s",src_strerror(rsmperr));
00151     if(rsmperr==6) error("resampling ratio is %.4f",rsmp_data.src_ratio);
00152   }
00153 
00154   samples = rsmp_data.output_frames_gen;
00155   //  func("%i frames resampled in %i with ratio %.4f",
00156   //  rsmp_data.input_frames_used, samples, rsmp_data.src_ratio);
00157 
00158   /* initialize the vorbis encoder to work on the resampled size */
00159   _intbuf = vorbis_analysis_buffer(&vd,samples);
00160   /* move resampled pcm into the vorbis float array 
00161      i tryed to avoid in every way this extra mov of memory
00162      but there is no way to predict the resampled buffer size exactly
00163      before actually doing the resampling. this prevents from initializing
00164      the vorbis buffer BEFORE doing the resampling, to store the resampled
00165      pcm directly into it (both the output of the resampler and the input
00166      of the vorbis encoder take a bidimensional float array).
00167      currently this causes a bit of overhead, but is the only way. */
00168   prepare(rsmpled,_intbuf,samples);
00169   res = vorbis_analysis_wrote(&vd,samples);
00170   if(res) { /* nonzero is error */
00171     error("OutVorbis::encode : error %i from vorbis_analysis_wrote");
00172     return 0; }
00173   
00174   /* encode one block at a time... */
00175   while(vorbis_analysis_blockout(&vd,&vb)==1) {
00176     /* Do the main analysis, creating a packet */
00177     res = vorbis_analysis(&vb, NULL); /* here as a second argument darkice uses 
00178                                          &op while oggenc uses NULL */
00179     if(res) { /* nonzero is error */
00180       error("OutVorbis::encode : error %i from vorbis_analysis_wrote");
00181       return 0;
00182     }
00183   
00184     res = vorbis_bitrate_addblock(&vb);
00185     if(res) { /* nonzero is error */
00186       error("OutVorbis::encode : error %i from vorbis_bitrate_addblock");
00187       return 0;
00188     }
00189 
00190     while(vorbis_bitrate_flushpacket(&vd, &op)) {
00191       /* Add packet to bitstream */
00192       res = ogg_stream_packetin(&os,&op);
00193       if(res) { /* nonzero is error */
00194         error("OutVorbis::encode : error %i from ogg_stream_packetin");
00195         return 0;
00196       }
00197 
00198       while(ogg_stream_pageout(&os,&og)) { 
00199         memcpy(_pbyte,og.header,og.header_len);
00200         _pbyte += og.header_len;
00201         memcpy(_pbyte,og.body,og.body_len);
00202         _pbyte += og.body_len;
00203         
00204         encoded += og.header_len + og.body_len;
00205 
00206         /* TODO: use throw / catch mechanism for EOS */
00207         //      if(ogg_page_eos(&og)) { func("Ogg encoder EOS!"); return encoded; }
00208       }
00209       
00210     }
00211   }
00212   return encoded;
00213 }
00214 
00215 int OutVorbis::prepare(float *buf, float **fbuf, int num) {
00216   int i=0;
00217   switch(channels()) {
00218   case 1:
00219     for(i=num; i>0; i--)
00220       fbuf[0][i] = buf[i]; // short buf: (buf[i*2] + buf[i*2+1]) / 65536.0f; // /2 /32768.0f;
00221     break;
00222   case 2:
00223     for(i=0; i<num; i++) {
00224       fbuf[0][i] = buf[i<<1]; // short buf: buf[i*2] /32768.0f;
00225       fbuf[1][i] = buf[(i<<1)+1]; // short buf: buf[i*2+1] /32768.0f;
00226     }
00227     break;
00228   default:
00229     error("error in OutVorbis::prepare");
00230     error("no more than 2 audio channels are supported");
00231     break;
00232   }
00233 
00234   return i;
00235 
00236   /*
00237     the way oggenc does this is by using a 16bit large pointer and shifting around: 
00238     fbuf[0][i] = ( ( p[i*4+1] << 8 ) | ( p[i*4] & 0xff ) ) / 32768.0f;
00239     fbuf[1][i] = ( ( p[i*4+3] << 8 ) | ( p[i*4+4] & 0xff ) ) / 32768.0f;
00240     (or something like this, check it out in oggenc/audio.c to be sure)
00241   */
00242 }
00243 
00244 
00245 void OutVorbis::flush() {
00246   //  lock();
00247   vorbis_analysis_wrote(&vd,0); /* oggenc L255 */
00248   //  unlock();
00249 }
00250 
00251 bool OutVorbis::apply_profile() {
00252   func("OutVorbis::apply_profile() q%.4f r%i b%i c%i",
00253        quality(),freq(),bps(),channels());
00254   bool res = true;
00255   int rsmp_err = 0;
00256 
00257   if(rsmp_state) src_delete(rsmp_state);
00258   rsmp_state = src_new(SRC_SINC_BEST_QUALITY, channels() ,&rsmp_err);
00259   if(!rsmp_state)
00260     error("Ogg/Vorbis can't initialize resampler: %s",src_strerror(rsmp_err));
00261   else func("ogg resampler %s initialized",src_get_name(SRC_SINC_BEST_QUALITY));
00262   
00263   /* set ratio for resampling with ogg vorbis */
00264   double ratio = (double)((float)freq() / 44100.0f);
00265   //  src_set_ratio(rsmp_state, ratio);
00266   rsmp_data.src_ratio = ratio;
00267  
00268   if(!src_is_valid_ratio(rsmp_data.src_ratio))
00269     error("invalid resampling ratio: %.4f", ratio);
00270   func("resample ratio for freq %i is %.4f", 
00271        freq(), ratio);
00272 
00273   if( vorbis_encode_setup_vbr
00274       (&vi, channels(), freq(), quality()/10.0f) ) {
00275     error("vorbis_encode_setup_vbr failed: invalid parameters");
00276     error("apply vorbis_encode_setup_vbr(%p,%u,%u,%f)",
00277           &vi, channels(), freq(), quality()/10.0f);
00278     res = false;
00279   }
00280 
00281   Shouter *ice = (Shouter*)icelist.begin();
00282   while(ice) {
00283     ice->bps( bps() );
00284     ice = (Shouter*)ice->next;
00285   }
00286 
00287   /* to save this calculation */
00288   out_chunk_len = OUT_CHUNK * sizeof(float) * 2;
00289 
00290   /* Turn off management entirely (if it was turned on) */
00291   //  vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_SET, NULL);
00292   
00293   //  if( vorbis_encode_setup_managed
00294   //      (&vi, channels(), freq(), -1, bps()*1000, -1 ) ) {
00295   //    (&vi, 2, SAMPLE_RATE, -1, bps()*1000, -1 ) ) {
00296   //    error("vorbis_encode_setup_managed failed: invalid bitrate %i",bps());
00297   //    res = false;
00298   //  }
00299   
00300   return res;
00301 }
00302   
00303 OutVorbis::~OutVorbis() {
00304   func("OutVorbis::~OutVorbis() %p",this);
00305   //  if(running) flush();
00306   act("closing ogg/vorbis encoder");
00307 
00308   if(rsmp_state) src_delete(rsmp_state);
00309   free(pcm);
00310   free(rsmpled);
00311 
00312   ogg_stream_clear(&os);
00313   
00314   vorbis_block_clear(&vb);
00315   vorbis_dsp_clear(&vd);
00316   vorbis_info_clear(&vi);
00317 }
00318 
00319 #endif

Generated on Sat Apr 17 17:38:48 2004 for MuSE by doxygen1.3