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.6 2004/12/15 18:18:06 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," ");
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   encoder_initialized = false;
00050 }
00051 
00052 
00053 bool OutVorbis::init() {
00054   func("OutVorbis::init() %p",this);
00055 
00056   //  vorbis_info_init(&vi);  
00057 
00058   //  act("initializing %s %i",name,vi.version);
00059 
00060   
00061   if(!apply_profile()) {
00062     error("problems in setting up codec parameters");
00063     //    vorbis_info_clear(&vi);
00064     return false;
00065   }
00066 
00067   erbapipa->set_output_type("copy_int16_to_float");
00068 
00069   initialized = true;
00070   return initialized;
00071 }
00072 
00073 /* note: num is number of samples in the L (or R) channel
00074    not the total number of samples in pcm[] */
00075 int OutVorbis::encode() {  
00076   int num = 0, samples = 0;
00077   int rsmperr, res;
00078   _pbyte = (int8_t*)buffer;
00079 
00080   if(headersize) {
00081     memcpy(buffer,header,headersize);
00082     encoded = headersize;
00083     shout(); dump();
00084   }
00085   
00086   encoded = headersize = 0;
00087 
00088   if(erbapipa->size() < out_chunk_len) { // bytes, not samples, 16bit stereo stuff
00089     //    func("OutVorbis::encode : not enough data in pipe");
00090     return -1;
00091   }
00092 
00093   /* this now takes bytes and returns bytes */
00094   num = erbapipa->read(OUT_CHUNK,pcm);
00095   // QUAA or?:   num = erbapipa->read(OUT_CHUNK*2,pcm);
00096 
00097 
00098   if(num<OUT_CHUNK)
00099     func("OutVorbis::encode() : erbapipa->read_float_bidi reads %i samples instead of %i",
00100          num,OUT_CHUNK);
00101   if(num<1) return num;
00102   
00103   rsmp_data.data_in = pcm;
00104   rsmp_data.input_frames = num;
00105   rsmp_data.data_out = rsmpled;
00106   rsmp_data.output_frames = RSMPBUF;
00107 
00108   /* do the resampling */
00109   rsmperr = src_process(rsmp_state,&rsmp_data);
00110   if(rsmperr) {
00111     error("error in ogg resampler: %s",src_strerror(rsmperr));
00112     if(rsmperr==6) error("resampling ratio is %.4f",rsmp_data.src_ratio);
00113   }
00114 
00115   samples = rsmp_data.output_frames_gen;
00116   func("%i frames resampled in %i with ratio %.4f (num:%u OUT_CHUNK:%u)",
00117        rsmp_data.input_frames_used, samples, rsmp_data.src_ratio, num, OUT_CHUNK);
00118 
00119   /* initialize the vorbis encoder to work on the resampled size */
00120   _intbuf = vorbis_analysis_buffer(&vd,samples);
00121   /* move resampled pcm into the vorbis float array 
00122      i tryed to avoid in every way this extra mov of memory
00123      but there is no way to predict the resampled buffer size exactly
00124      before actually doing the resampling. this prevents from initializing
00125      the vorbis buffer BEFORE doing the resampling, to store the resampled
00126      pcm directly into it (both the output of the resampler and the input
00127      of the vorbis encoder take a bidimensional float array).
00128      currently this causes a bit of overhead, but is the only way. */
00129   prepare(rsmpled,_intbuf,samples);
00130   res = vorbis_analysis_wrote(&vd,samples);
00131   if(res) { /* nonzero is error */
00132     error("OutVorbis::encode : error %i from vorbis_analysis_wrote");
00133     return 0; }
00134   
00135   /* encode one block at a time... */
00136   while(vorbis_analysis_blockout(&vd,&vb)==1) {
00137     /* Do the main analysis, creating a packet */
00138     res = vorbis_analysis(&vb, NULL); /* here as a second argument darkice uses 
00139                                          &op while oggenc uses NULL */
00140     if(res) { /* nonzero is error */
00141       error("OutVorbis::encode : error %i from vorbis_analysis_wrote");
00142       return 0;
00143     }
00144   
00145     res = vorbis_bitrate_addblock(&vb);
00146     if(res) { /* nonzero is error */
00147       error("OutVorbis::encode : error %i from vorbis_bitrate_addblock");
00148       return 0;
00149     }
00150 
00151     while(vorbis_bitrate_flushpacket(&vd, &op)) {
00152       /* Add packet to bitstream */
00153       res = ogg_stream_packetin(&os,&op);
00154       if(res) { /* nonzero is error */
00155         error("OutVorbis::encode : error %i from ogg_stream_packetin");
00156         return 0;
00157       }
00158 
00159       while(ogg_stream_pageout(&os,&og)) { 
00160         memcpy(_pbyte,og.header,og.header_len);
00161         _pbyte += og.header_len;
00162         memcpy(_pbyte,og.body,og.body_len);
00163         _pbyte += og.body_len;
00164         
00165         encoded += og.header_len + og.body_len;
00166 
00167         /* TODO: use throw / catch mechanism for EOS */
00168         //      if(ogg_page_eos(&og)) { func("Ogg encoder EOS!"); return encoded; }
00169       }
00170       
00171     }
00172   }
00173   return encoded;
00174 }
00175 
00176 int OutVorbis::prepare(float *buf, float **fbuf, int num) {
00177   // this function distributes interlaced data in two l/r arrays
00178 
00179   int i=0;
00180   switch(channels()) {
00181   case 1:
00182     for(i=num; i>0; i--)
00183       fbuf[0][i] = buf[i]; // short buf: (buf[i*2] + buf[i*2+1]) / 65536.0f; // /2 /32768.0f;
00184     break;
00185   case 2:
00186     for(i=0; i<num; i++) {
00187       fbuf[0][i] = buf[i<<1]; // short buf: buf[i*2] /32768.0f;
00188       fbuf[1][i] = buf[(i<<1)+1]; // short buf: buf[i*2+1] /32768.0f;
00189     }
00190     break;
00191   default:
00192     error("error in OutVorbis::prepare");
00193     error("no more than 2 audio channels are supported");
00194     break;
00195   }
00196 
00197   return i;
00198 
00199   /*
00200     the way oggenc does this is by using a 16bit large pointer and shifting around: 
00201     fbuf[0][i] = ( ( p[i*4+1] << 8 ) | ( p[i*4] & 0xff ) ) / 32768.0f;
00202     fbuf[1][i] = ( ( p[i*4+3] << 8 ) | ( p[i*4+4] & 0xff ) ) / 32768.0f;
00203     (or something like this, check it out in oggenc/audio.c to be sure)
00204   */
00205 }
00206 
00207 
00208 void OutVorbis::flush() {
00209   //  lock();
00210   vorbis_analysis_wrote(&vd,0); /* oggenc L255 */
00211   //  unlock();
00212 }
00213 
00214 bool OutVorbis::apply_profile() {
00215   func("OutVorbis::apply_profile() q%.4f r%i b%i c%i",
00216        _quality,freq(),bps(),channels());
00217   bool res = true;
00218   int rsmp_err = 0;
00219 
00220   if(rsmp_state) src_delete(rsmp_state);
00221   // SRC_SINC_(BEST|MEDIUM)_QUALITY or SRC_SINC_FASTEST
00222   rsmp_state = src_new(SRC_SINC_FASTEST, channels() ,&rsmp_err);
00223   if(!rsmp_state)
00224     error("Ogg/Vorbis can't initialize resampler: %s",src_strerror(rsmp_err));
00225   else func("ogg resampler %s initialized",src_get_name(SRC_SINC_FASTEST));
00226   
00227   /* set ratio for resampling with ogg vorbis */
00228   {
00229     double ratio = (double)((float)freq() / 44100.0f);
00230     rsmp_data.src_ratio = ratio/2; // input is always stereo
00231   }
00232   if(!src_is_valid_ratio(rsmp_data.src_ratio))
00233     error("invalid resampling ratio: %.4f", rsmp_data.src_ratio);
00234   func("resample ratio for freq %i is %.4f", freq(), rsmp_data.src_ratio);
00235 
00236 
00239   if(encoder_initialized) {
00240     // clear the encoder for new initialization
00241     ogg_stream_clear(&os);
00242     vorbis_block_clear(&vb);
00243     vorbis_dsp_clear(&vd);
00244     vorbis_info_clear(&vi);
00245   } else encoder_initialized = true;
00246 
00247   vorbis_info_init(&vi);
00248   if( vorbis_encode_init
00249       (&vi, channels(), freq(), bps()*1000, bps()*1000, bps()*1000) ) {
00250     error("vorbis_encode_init failed: invalid parameters");
00251     res = false;
00252   }
00253   /* Now, set up the analysis engine, stream encoder, and other
00254      preparation before the encoding begins. */
00255   vorbis_analysis_init(&vd,&vi);
00256   vorbis_block_init(&vd,&vb);
00257   ogg_stream_init(&os, time(NULL));
00258 
00259   /* sets up our comments */
00260   {
00261     char tmp[128];
00262     sprintf(tmp,"%s version %s",PACKAGE,VERSION);
00263     vorbis_comment_init(&vc);
00264     vorbis_comment_add_tag(&vc,"Streamed with",tmp);
00265   }
00266 
00267   /* Now, build the three header packets and send through to the stream 
00268      output stage (but defer actual file output until the main encode loop) */
00269 
00270   /* Build the packets */
00271   vorbis_analysis_headerout
00272     (&vd,&vc,&header_main,&header_comments,&header_codebooks);
00273 
00274   /* And stream them out */
00275   ogg_stream_packetin(&os,&header_main);
00276   ogg_stream_packetin(&os,&header_comments);
00277   ogg_stream_packetin(&os,&header_codebooks);
00278   
00279   /* write out headers */
00280   headersize = 0;
00281   _pbyte = (int8_t*)header; 
00282   while(ogg_stream_flush(&os,&og)) {
00283     memcpy(_pbyte,og.header,og.header_len);
00284     _pbyte += og.header_len;
00285     memcpy(_pbyte,og.body,og.body_len);
00286     _pbyte += og.body_len;
00287     headersize += og.header_len + og.body_len;
00288   }
00289 
00290   vorbis_comment_clear(&vc);
00292 
00293 
00294 
00295   Shouter *ice = (Shouter*)icelist.begin();
00296   while(ice) {
00297     char tmp[256];
00298 
00299     snprintf(tmp,256,"%u",bps());       ice->bps( tmp );
00300     snprintf(tmp,256,"%u",freq());      ice->freq( tmp );
00301     snprintf(tmp,256,"%u",channels());  ice->channels( tmp );
00302     //    snprintf(tmp,256,"%.2f",_quality);  ice->quality( tmp );
00303     
00304     ice = (Shouter*)ice->next;
00305   }
00306 
00307   /* to save this calculation */
00308   out_chunk_len = OUT_CHUNK * sizeof(float) * 2;
00309 
00310   /* Turn off management entirely (if it was turned on) */
00311   //  vorbis_encode_ctl(&vi, OV_ECTL_RATEMANAGE_SET, NULL);
00312   
00313   //  if( vorbis_encode_setup_managed
00314   //      (&vi, channels(), freq(), -1, bps()*1000, -1 ) ) {
00315   //    (&vi, 2, SAMPLE_RATE, -1, bps()*1000, -1 ) ) {
00316   //    error("vorbis_encode_setup_managed failed: invalid bitrate %i",bps());
00317   //    res = false;
00318   //  }
00319   
00320   return res;
00321 }
00322   
00323 OutVorbis::~OutVorbis() {
00324   func("OutVorbis::~OutVorbis() %p",this);
00325   //  if(running) flush();
00326   act("closing ogg/vorbis encoder");
00327 
00328   if(rsmp_state) src_delete(rsmp_state);
00329   free(pcm);
00330   free(rsmpled);
00331 
00332   if(encoder_initialized) {
00333     ogg_stream_clear(&os);
00334     vorbis_block_clear(&vb);
00335     vorbis_dsp_clear(&vd);
00336     vorbis_info_clear(&vi);
00337   }
00338 }
00339 
00340 #endif

Generated on Thu Dec 16 12:28:21 2004 for MuSE by doxygen1.3