out_vorbis.cpp Source File

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