vorbisencode.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033 
00034 #include "vorbisencode.h"
00035 #include "vorbis/vorbisenc.h"
00036 #include "llerror.h"
00037 #include "llrand.h"
00038 #include "llmath.h"
00039 #include "llapr.h"
00040 
00041 //#if LL_DARWIN
00042 // MBW -- XXX -- Getting rid of SecondLifeVorbis for now -- no fmod means no name collisions.
00043 #if 0
00044 #include "VorbisFramework.h"
00045 
00046 #define vorbis_analysis                         mac_vorbis_analysis
00047 #define vorbis_analysis_headerout       mac_vorbis_analysis_headerout
00048 #define vorbis_analysis_init            mac_vorbis_analysis_init
00049 #define vorbis_encode_ctl                       mac_vorbis_encode_ctl
00050 #define vorbis_encode_setup_init        mac_vorbis_encode_setup_init
00051 #define vorbis_encode_setup_managed     mac_vorbis_encode_setup_managed
00052 
00053 #define vorbis_info_init                        mac_vorbis_info_init
00054 #define vorbis_info_clear                       mac_vorbis_info_clear
00055 #define vorbis_comment_init                     mac_vorbis_comment_init
00056 #define vorbis_comment_clear            mac_vorbis_comment_clear
00057 #define vorbis_block_init                       mac_vorbis_block_init
00058 #define vorbis_block_clear                      mac_vorbis_block_clear
00059 #define vorbis_dsp_clear                        mac_vorbis_dsp_clear
00060 #define vorbis_analysis_buffer          mac_vorbis_analysis_buffer
00061 #define vorbis_analysis_wrote           mac_vorbis_analysis_wrote
00062 #define vorbis_analysis_blockout        mac_vorbis_analysis_blockout
00063 
00064 #define ogg_stream_packetin                     mac_ogg_stream_packetin
00065 #define ogg_stream_init                         mac_ogg_stream_init
00066 #define ogg_stream_flush                        mac_ogg_stream_flush
00067 #define ogg_stream_pageout                      mac_ogg_stream_pageout
00068 #define ogg_page_eos                            mac_ogg_page_eos
00069 #define ogg_stream_clear                        mac_ogg_stream_clear
00070 
00071 #endif
00072 
00073 S32 check_for_invalid_wav_formats(const char *in_fname, char *error_msg)
00074 {
00075         U16 num_channels = 0;
00076         U32 sample_rate = 0;
00077         U32 bits_per_sample = 0;
00078         U32 physical_file_size = 0;
00079         U32 chunk_length = 0;
00080         U32 raw_data_length = 0;
00081         U32 bytes_per_sec = 0;
00082         BOOL uncompressed_pcm = FALSE;
00083 
00084         unsigned char wav_header[44];           /*Flawfinder: ignore*/
00085 
00086         error_msg[0] = '\0';
00087 
00088     apr_file_t* infp = ll_apr_file_open(in_fname,LL_APR_RB);
00089         if (!infp)
00090         {
00091                 strcpy(error_msg, "CannotUploadSoundFile");     /*Flawfinder: ignore*/
00092                 return(LLVORBISENC_SOURCE_OPEN_ERR);
00093         }
00094 
00095         ll_apr_file_read(infp, wav_header, 44);
00096         physical_file_size = ll_apr_file_seek(infp,APR_END,0);
00097 
00098         if (strncmp((char *)&(wav_header[0]),"RIFF",4))
00099         {
00100                 strcpy(error_msg, "SoundFileNotRIFF");  /*Flawfinder: ignore*/
00101                 apr_file_close(infp);
00102             return(LLVORBISENC_WAV_FORMAT_ERR);
00103         }
00104 
00105         if (strncmp((char *)&(wav_header[8]),"WAVE",4))
00106         {
00107                 strcpy(error_msg, "SoundFileNotRIFF");  /*Flawfinder: ignore*/
00108                 apr_file_close(infp);
00109             return(LLVORBISENC_WAV_FORMAT_ERR);
00110         }
00111         
00112         // parse the chunks
00113         
00114         U32 file_pos = 12;  // start at the first chunk (usually fmt but not always)
00115         
00116         while ((file_pos + 8)< physical_file_size)
00117         {
00118                 ll_apr_file_seek(infp,APR_SET,file_pos);
00119                 ll_apr_file_read(infp, wav_header, 44);
00120 
00121                 chunk_length = ((U32) wav_header[7] << 24) 
00122                         + ((U32) wav_header[6] << 16) 
00123                         + ((U32) wav_header[5] << 8) 
00124                         + wav_header[4];
00125 
00126 //              llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
00127 
00128                 if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
00129                 {
00130                         if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00))
00131                         {
00132                                 uncompressed_pcm = TRUE;
00133                         }
00134                         num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
00135                         sample_rate = ((U32) wav_header[15] << 24) 
00136                                 + ((U32) wav_header[14] << 16) 
00137                                 + ((U32) wav_header[13] << 8) 
00138                                 + wav_header[12];
00139                         bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
00140                         bytes_per_sec = ((U32) wav_header[19] << 24) 
00141                                 + ((U32) wav_header[18] << 16) 
00142                                 + ((U32) wav_header[17] << 8) 
00143                                 + wav_header[16];
00144                 }
00145                 else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
00146                 {
00147                         raw_data_length = chunk_length;                 
00148                 }
00149                 file_pos += (chunk_length + 8);
00150                 chunk_length = 0;
00151         } 
00152 
00153         apr_file_close(infp);   
00154 
00155         if (!uncompressed_pcm)
00156         {       
00157                  strcpy(error_msg, "SoundFileNotPCM");          /*Flawfinder: ignore*/
00158                   return(LLVORBISENC_PCM_FORMAT_ERR);
00159         }
00160         
00161         if ((num_channels < 1) || (num_channels > 2))
00162         {       
00163                 strcpy(error_msg, "SoundFileInvalidChannelCount");      /*Flawfinder: ignore*/
00164                 return(LLVORBISENC_MULTICHANNEL_ERR);
00165         }
00166 
00167         if (sample_rate != 44100)
00168         {       
00169                 strcpy(error_msg, "SoundFileInvalidSampleRate");                /*Flawfinder: ignore*/
00170                 return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE);
00171         }
00172         
00173         if ((bits_per_sample != 16) && (bits_per_sample != 8))
00174         {                
00175                 strcpy(error_msg, "SoundFileInvalidWordSize");          /*Flawfinder: ignore*/
00176                 return(LLVORBISENC_UNSUPPORTED_WORD_SIZE);
00177         }
00178 
00179         if (!raw_data_length)
00180         {
00181                 strcpy(error_msg, "SoundFileInvalidHeader");                    /*Flawfinder: ignore*/
00182                 return(LLVORBISENC_CLIP_TOO_LONG);               
00183         }
00184 
00185         F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec;
00186                 
00187         if (clip_length > 10.0f)
00188         {
00189                 strcpy(error_msg, "SoundFileInvalidTooLong");                   /*Flawfinder: ignore*/
00190                 return(LLVORBISENC_CLIP_TOO_LONG);               
00191         }
00192 
00193     return(LLVORBISENC_NOERR);
00194 }
00195 
00196 S32 encode_vorbis_file(const char *in_fname, const char *out_fname)
00197 {
00198         return(encode_vorbis_file_at(in_fname,out_fname, 128000));
00199 }
00200 
00201 S32 encode_vorbis_file_at(const char *in_fname, const char *out_fname, S32 bitrate)
00202 {
00203 #define READ_BUFFER 1024
00204         unsigned char readbuffer[READ_BUFFER*4+44];   /* out of the data segment, not the stack */      /*Flawfinder: ignore*/
00205 
00206         ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
00207         ogg_page         og; /* one Ogg bitstream page.  Vorbis packets are inside */
00208         ogg_packet       op; /* one raw packet of data for decode */
00209         
00210         vorbis_info      vi; /* struct that stores all the static vorbis bitstream settings */
00211         vorbis_comment   vc; /* struct that stores all the user comments */
00212         
00213         vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
00214         vorbis_block     vb; /* local working space for packet->PCM decode */
00215         
00216         int eos=0;
00217         int result;
00218 
00219         U16 num_channels = 0;
00220         U32 sample_rate = 0;
00221         U32 bits_per_sample = 0;
00222 
00223         S32 format_error = 0;
00224         char error_msg[MAX_STRING];     /*Flawfinder: ignore*/
00225         if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg)))
00226         {
00227                 llwarns << error_msg << ": " << in_fname << llendl;
00228                 return(format_error);
00229         }
00230 
00231 #if 1
00232         unsigned char wav_header[44];   /*Flawfinder: ignore*/
00233 
00234         S32 data_left = 0;
00235 
00236         apr_file_t* infp = ll_apr_file_open(in_fname,LL_APR_RB);
00237         if (!infp)
00238         {
00239                 llwarns << "Couldn't open temporary ogg file for writing: " << in_fname
00240                         << llendl;
00241                 return(LLVORBISENC_SOURCE_OPEN_ERR);
00242         }
00243         apr_file_t* outfp = ll_apr_file_open(out_fname,LL_APR_WPB);
00244         if (!outfp)
00245         {
00246                 llwarns << "Couldn't open upload sound file for reading: " << in_fname
00247                         << llendl;
00248                 apr_file_close (infp);
00249                 return(LLVORBISENC_DEST_OPEN_ERR);
00250         }
00251         
00252          // parse the chunks
00253          U32 chunk_length = 0;
00254          U32 file_pos = 12;  // start at the first chunk (usually fmt but not always)
00255          
00256          while (apr_file_eof(infp) != APR_EOF)
00257          {
00258                  ll_apr_file_seek(infp,APR_SET,file_pos);
00259                  ll_apr_file_read(infp, wav_header, 44);
00260                  
00261                  chunk_length = ((U32) wav_header[7] << 24) 
00262                          + ((U32) wav_header[6] << 16) 
00263                          + ((U32) wav_header[5] << 8) 
00264                          + wav_header[4];
00265                  
00266 //               llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
00267                  
00268                  if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
00269                  {
00270                          num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
00271                          sample_rate = ((U32) wav_header[15] << 24) 
00272                                  + ((U32) wav_header[14] << 16) 
00273                                  + ((U32) wav_header[13] << 8) 
00274                                  + wav_header[12];
00275                          bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
00276                  }
00277                  else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
00278                  {
00279                          ll_apr_file_seek(infp,APR_SET,file_pos+8);
00280                          // leave the file pointer at the beginning of the data chunk data
00281                          data_left = chunk_length;                      
00282                          break;
00283                  }
00284                  file_pos += (chunk_length + 8);
00285                  chunk_length = 0;
00286          } 
00287          
00288 //       apr_file_close(infp);  
00289 
00290          /********** Encode setup ************/
00291          
00292          /* choose an encoding mode */
00293          /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
00294          vorbis_info_init(&vi);
00295 
00296          // always encode to mono
00297 //       vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1);
00298 //       if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
00299 
00300 //       F32 quality = 0;
00301 //       quality = (bitrate==128000 ? 0.4f : 0.1);
00302 
00303          if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
00304 //       if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality))
00305 //       if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) ||
00306 //              vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) ||
00307 //              vorbis_encode_setup_init(&vi))
00308         {
00309 //              llwarns << "unable to initialize vorbis codec at quality " << quality << llendl;
00310                 llwarns << "unable to initialize vorbis codec at bitrate " << bitrate << llendl;
00311                 return(LLVORBISENC_DEST_OPEN_ERR);
00312         }
00313          
00314          /* add a comment */
00315          vorbis_comment_init(&vc);
00316 //       vorbis_comment_add(&vc,"Linden");
00317          
00318          /* set up the analysis state and auxiliary encoding storage */
00319          vorbis_analysis_init(&vd,&vi);
00320          vorbis_block_init(&vd,&vb);
00321          
00322          /* set up our packet->stream encoder */
00323          /* pick a random serial number; that way we can more likely build
00324                 chained streams just by concatenation */
00325          ogg_stream_init(&os, ll_rand());
00326          
00327          /* Vorbis streams begin with three headers; the initial header (with
00328                 most of the codec setup parameters) which is mandated by the Ogg
00329                 bitstream spec.  The second header holds any comment fields.  The
00330                 third header holds the bitstream codebook.  We merely need to
00331                 make the headers, then pass them to libvorbis one at a time;
00332                 libvorbis handles the additional Ogg bitstream constraints */
00333          
00334          {
00335                  ogg_packet header;
00336                  ogg_packet header_comm;
00337                  ogg_packet header_code;
00338                  
00339                  vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
00340                  ogg_stream_packetin(&os,&header); /* automatically placed in its own
00341                                                                                           page */
00342                  ogg_stream_packetin(&os,&header_comm);
00343                  ogg_stream_packetin(&os,&header_code);
00344                  
00345                  /* We don't have to write out here, but doing so makes streaming 
00346                   * much easier, so we do, flushing ALL pages. This ensures the actual
00347                   * audio data will start on a new page
00348                   */
00349                  while(!eos){
00350                          int result=ogg_stream_flush(&os,&og);
00351                          if(result==0)break;
00352                          ll_apr_file_write(outfp, og.header, og.header_len);
00353                          ll_apr_file_write(outfp, og.body, og.body_len);
00354                  }
00355                  
00356          }
00357          
00358          
00359          while(!eos)
00360          {
00361                  long bytes_per_sample = bits_per_sample/8;
00362 
00363                  long bytes=(long)ll_apr_file_read(infp, readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */
00364                  
00365                  if (bytes==0)
00366                  {
00367                          /* end of file.  this can be done implicitly in the mainline,
00368                                 but it's easier to see here in non-clever fashion.
00369                                 Tell the library we're at end of stream so that it can handle
00370                                 the last frame and mark end of stream in the output properly */
00371 
00372                          vorbis_analysis_wrote(&vd,0);
00373 //                       eos = 1;
00374                          
00375                  }
00376                  else
00377                  {
00378                          long i;
00379                          long samples;
00380                          int temp;
00381 
00382                          data_left -= bytes;
00383              /* data to encode */
00384                          
00385                          /* expose the buffer to submit data */
00386                          float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER);
00387                         
00388                          i = 0;
00389                          samples = bytes / (num_channels * bytes_per_sample);
00390 
00391                          if (num_channels == 2)
00392                          {
00393                                  if (bytes_per_sample == 2)
00394                                  {
00395                                          /* uninterleave samples */
00396                                          for(i=0; i<samples ;i++)
00397                                          {
00398                                                  temp =  ((signed char *)readbuffer)[i*4+1];    /*Flawfinder: ignore*/
00399                                                  temp += ((signed char *)readbuffer)[i*4+3];    /*Flawfinder: ignore*/
00400                                                  temp <<= 8;
00401                                                  temp += readbuffer[i*4];
00402                                                  temp += readbuffer[i*4+2];
00403 
00404                                                  buffer[0][i] = ((float)temp) / 65536.f;
00405                                          }
00406                                  }
00407                                  else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
00408                                  {
00409                                          /* uninterleave samples */
00410                                          for(i=0; i<samples ;i++)
00411                                          {
00412                                                  temp  = readbuffer[i*2+0];
00413                                                  temp += readbuffer[i*2+1];
00414                                                  temp -= 256;
00415                                                  buffer[0][i] = ((float)temp) / 256.f;
00416                                          }
00417                                  } 
00418                          }
00419                          else if (num_channels == 1)
00420                          {
00421                                  if (bytes_per_sample == 2)
00422                                  {
00423                                          for(i=0; i < samples ;i++)
00424                                          {
00425                                                  temp = ((signed char*)readbuffer)[i*2+1];
00426                                                  temp <<= 8;
00427                                                  temp += readbuffer[i*2];
00428                                                  buffer[0][i] = ((float)temp) / 32768.f;
00429                                          }
00430                                  }
00431                                  else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
00432                                  {
00433                                          for(i=0; i < samples ;i++)
00434                                          {
00435                                                  temp = readbuffer[i];
00436                                                  temp -= 128;
00437                                                  buffer[0][i] = ((float)temp) / 128.f;
00438                                          }
00439                                  }
00440                          }
00441                                 
00442                          /* tell the library how much we actually submitted */
00443                          vorbis_analysis_wrote(&vd,i);
00444                  }
00445                          
00446                  /* vorbis does some data preanalysis, then divvies up blocks for
00447                         more involved (potentially parallel) processing.  Get a single
00448                         block for encoding now */
00449                  while(vorbis_analysis_blockout(&vd,&vb)==1)
00450                  {
00451                          
00452                          /* analysis */
00453                         /* Do the main analysis, creating a packet */
00454                         vorbis_analysis(&vb, NULL);
00455                         vorbis_bitrate_addblock(&vb);
00456 
00457                         while(vorbis_bitrate_flushpacket(&vd, &op)) 
00458                         {
00459                          
00460                          /* weld the packet into the bitstream */
00461                          ogg_stream_packetin(&os,&op);
00462                          
00463                          /* write out pages (if any) */
00464                          while(!eos)
00465                          {
00466                                  result = ogg_stream_pageout(&os,&og);
00467 
00468                                  if(result==0)
00469                                         break;
00470 
00471                                  ll_apr_file_write(outfp, og.header, og.header_len);
00472                                  ll_apr_file_write(outfp, og.body, og.body_len);
00473                                  
00474                                  /* this could be set above, but for illustrative purposes, I do
00475                                         it here (to show that vorbis does know where the stream ends) */
00476                                  
00477                                  if(ogg_page_eos(&og))
00478                                         eos=1;
00479                                  
00480                          }
00481                         }
00482                  }
00483          }
00484          
00485          
00486          
00487          /* clean up and exit.  vorbis_info_clear() must be called last */
00488          
00489          ogg_stream_clear(&os);
00490          vorbis_block_clear(&vb);
00491          vorbis_dsp_clear(&vd);
00492          vorbis_comment_clear(&vc);
00493          vorbis_info_clear(&vi);
00494          
00495          /* ogg_page and ogg_packet structs always point to storage in
00496                 libvorbis.  They're never freed or manipulated directly */
00497          
00498 //       fprintf(stderr,"Vorbis encoding: Done.\n");
00499          llinfos << "Vorbis encoding: Done." << llendl;
00500          apr_file_close(outfp);
00501          apr_file_close(infp);
00502          
00503 #endif
00504          return(LLVORBISENC_NOERR);
00505          
00506 }

Generated on Thu Jul 1 06:10:03 2010 for Second Life Viewer by  doxygen 1.4.7