CLAM-Development
1.1
|
00001 /* 00002 * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG) 00003 * UNIVERSITAT POMPEU FABRA 00004 * 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00019 * 00020 */ 00021 00022 #include "OggVorbisAudioStream.hxx" 00023 #include "AudioFile.hxx" 00024 #include <cstdio> 00025 #include <ctime> 00026 #include <cstdlib> 00027 #include <vorbis/codec.h> 00028 #include <iostream> 00029 #include <algorithm> 00030 00031 #if defined ( __powerpc__ ) || defined ( __POWERPC__ ) 00032 #define HOST_ENDIANESS 1 00033 #else 00034 #define HOST_ENDIANESS 0 00035 #endif 00036 00037 namespace CLAM 00038 { 00039 00040 namespace AudioCodecs 00041 { 00042 const TSize OggVorbisAudioStream::mMaxBlockSize = 4096 / sizeof(TInt16); // Seems to be the 'reference' value 00043 const TSize OggVorbisAudioStream::mAnalysisWindowSize = 1024; 00044 00045 OggVorbisAudioStream::OggVorbisAudioStream() 00046 : mFileHandle( NULL ), mValidFileParams( false ), mEncoding( false ) 00047 { 00048 mBlockBuffer.Resize( mMaxBlockSize ); 00049 mBlockBuffer.SetSize( mMaxBlockSize ); 00050 00051 } 00052 00053 OggVorbisAudioStream::OggVorbisAudioStream( const AudioFile& file ) 00054 : mFileHandle( NULL ), mValidFileParams( false ), mEncoding( false ) 00055 { 00056 SetFOI( file ); 00057 mBlockBuffer.Resize( mMaxBlockSize ); 00058 mBlockBuffer.SetSize( mMaxBlockSize ); 00059 00060 } 00061 00062 OggVorbisAudioStream::~OggVorbisAudioStream() 00063 { 00064 if ( mValidFileParams ) 00065 Dispose(); 00066 } 00067 00068 void OggVorbisAudioStream::SetFOI( const AudioFile& file ) 00069 { 00070 if ( mValidFileParams ) 00071 Dispose(); 00072 AudioFileToNative( file ); 00073 00074 } 00075 00076 void OggVorbisAudioStream::AudioFileToNative( const AudioFile& file ) 00077 { 00078 mName = file.GetLocation(); 00079 mEncodedSampleRate = (int)file.GetHeader().GetSampleRate(); 00080 mEncodedChannels = (int)file.GetHeader().GetChannels(); 00081 00082 mEncodeBuffer.resize( mEncodedChannels ); // as many stream buffers as channels 00083 00084 } 00085 00086 00087 void OggVorbisAudioStream::PrepareReading() 00088 { 00089 if ( ( mFileHandle = fopen( mName.c_str(), "rb" ) ) == NULL ) 00090 { 00091 std::string msgString = "Could not open "; 00092 msgString += mName; 00093 msgString +=" for reading!"; 00094 CLAM_ASSERT( false, msgString.c_str() ); 00095 } 00096 00097 if ( ov_open( mFileHandle, &mNativeFileParams, NULL, 0 ) < 0 ) 00098 { 00099 fclose( mFileHandle ); 00100 std::string msgString = mName; 00101 msgString += " is not a valid Ogg/Vorbis file!"; 00102 00103 CLAM_ASSERT( false, msgString.c_str() ); 00104 } 00105 00106 vorbis_info* info = ov_info( &mNativeFileParams, -1 ); 00107 00108 SetChannels( info->channels ); 00109 MarkAllChannelsAsConsumed(); 00110 00111 mValidFileParams = true; 00112 mCurrentSection = 0; 00113 00114 // MRJ: Seen on Audacity sources. It seems that 00115 // not all encoders respect the specs right: sometimes 00116 // one might stumble on a file with poorly encoded headers 00117 // having this the effect of reading several frames of zeros 00118 // at the beginning 00119 ov_pcm_seek( &mNativeFileParams, 0 ); 00120 } 00121 00122 void OggVorbisAudioStream::PrepareWriting() 00123 { 00124 if ( ( mFileHandle = fopen( mName.c_str(), "wb" ) ) == NULL ) 00125 { 00126 std::string msgString = "Could not open "; 00127 msgString += mName; 00128 msgString +=" for writing!"; 00129 CLAM_ASSERT( false, msgString.c_str() ); 00130 } 00131 00132 VorbisI_EncoderSetup(); 00133 MarkAllChannelsAsProduced(); 00134 mEncoding = true; 00135 00136 } 00137 00138 void OggVorbisAudioStream::VorbisI_EncoderSetup() 00139 { 00140 00141 vorbis_info_init( &mStreamInfo ); 00142 00143 // encoding mode choosing 00144 00145 int retValue = vorbis_encode_init_vbr( &mStreamInfo, 00146 mEncodedChannels, 00147 mEncodedSampleRate, 00148 0.5 ); 00149 00150 CLAM_ASSERT( retValue == 0, "Error trying to initialize Vorbis encoder!" ); 00151 00152 // We add to the comment section who we are 00153 vorbis_comment_init( &mFileComments ); 00154 vorbis_comment_add_tag( &mFileComments, "ENCODER", "CLAM" ); 00155 00156 // analysis state and auxiliary encoding state storage setup 00157 vorbis_analysis_init( &mDSPState, &mStreamInfo ); 00158 vorbis_block_init( &mDSPState, &mVorbisBlock ); 00159 00160 // packet->stream encoder setup 00161 // pick random serial number 00162 // :TODO: this random number thing might be really important... 00163 ogg_stream_init( &mOggStreamState, rand() ); 00164 00165 WriteBitstreamHeader(); 00166 00167 SetChannels( mEncodedChannels ); 00168 } 00169 00170 void OggVorbisAudioStream::WriteBitstreamHeader() 00171 { 00172 // Every Vorbis stream begins with 3 headers: 00173 // + the initial header ( with codec setup params ) 00174 // + the header with the comment fields 00175 // + the header with the code books 00176 00177 ogg_packet header_codec_setup; 00178 ogg_packet header_comments; 00179 ogg_packet header_codebooks; 00180 00181 // We make the headers from the current Vorbis DSP module state 00182 // and file comments 00183 vorbis_analysis_headerout( &mDSPState, &mFileComments, 00184 &header_codec_setup, 00185 &header_comments, 00186 &header_codebooks ); 00187 00188 // We 'push' each header one at a time into the stream 00189 ogg_stream_packetin( &mOggStreamState, &header_codec_setup ); 00190 ogg_stream_packetin( &mOggStreamState, &header_comments ); 00191 ogg_stream_packetin( &mOggStreamState, &header_codebooks ); 00192 00193 // Now we ensure that the audio data will begin on a new 00194 // 'page' as the specs require 00195 00196 while( ogg_stream_flush( &mOggStreamState, &mOggPage ) > 0 ) 00197 { 00198 fwrite( mOggPage.header, 1, mOggPage.header_len, mFileHandle ); 00199 fwrite( mOggPage.body, 1, mOggPage.body_len, mFileHandle ); 00200 } 00201 00202 00203 } 00204 00205 void OggVorbisAudioStream::PrepareReadWrite() 00206 { 00207 CLAM_ASSERT( false, "Cannot be done!" ); 00208 } 00209 00210 void OggVorbisAudioStream::Dispose() 00211 { 00212 if ( !mEncoding ) 00213 { 00214 ov_clear( &mNativeFileParams ); 00215 mValidFileParams = false; 00216 } 00217 else 00218 { 00219 // if there are yet samples to be processed we assure 00220 // they are encoded 00221 if ( !mEncodeBuffer[0].empty() ) 00222 DoVorbisAnalysis(); 00223 00224 // We tell the Vorbis encoder that we are 00225 // finished with encoding frames 00226 vorbis_analysis_wrote( &mDSPState, 0 ); 00227 // push blocks generated by the last call 00228 // onto the Ogg stream 00229 PushAnalysisBlocksOntoOggStream(); 00230 00231 // Encoder cleaning up 00232 ogg_stream_clear( &mOggStreamState ); 00233 vorbis_block_clear( &mVorbisBlock ); 00234 vorbis_dsp_clear( &mDSPState ); 00235 vorbis_comment_clear( &mFileComments ); 00236 vorbis_info_clear( &mStreamInfo ); 00237 00238 fclose( mFileHandle ); 00239 00240 mEncoding = false; 00241 00242 } 00243 } 00244 00245 void OggVorbisAudioStream::ConsumeDecodedSamples() 00246 { 00247 CLAM_ASSERT( mDecodeBuffer.size() >= unsigned(mInterleavedData.Size()), 00248 "This method cannot be called if the decode buffer" 00249 " has less samples than requested by the upper level"); 00250 00251 static const TData norm = 1.0 / 32768.0; 00252 00253 TData* pSamples = mInterleavedData.GetPtr(); 00254 const TData* pSamplesEnd = mInterleavedData.GetPtr() + mInterleavedData.Size(); 00255 for( std::deque<TInt16>::iterator i = mDecodeBuffer.begin(); 00256 pSamples < pSamplesEnd; i++, pSamples++ ) 00257 *pSamples = TData(*i)*norm; 00258 00259 mDecodeBuffer.erase( mDecodeBuffer.begin(), 00260 mDecodeBuffer.begin()+mInterleavedData.Size() ); 00261 00262 } 00263 00264 void OggVorbisAudioStream::DiskToMemoryTransfer() 00265 { 00266 00267 //Unused variable: TSize nBytes = 0; 00268 unsigned samplesRead = 0; 00269 00270 while ( mDecodeBuffer.size() < unsigned(mInterleavedData.Size()) ) 00271 { 00272 mLastBytesRead = ov_read( &mNativeFileParams, 00273 (char*)mBlockBuffer.GetPtr(), 00274 mMaxBlockSize*sizeof(TInt16), 00275 HOST_ENDIANESS, 00276 2, 1, &mCurrentSection ); 00277 00278 CLAM_ASSERT( mLastBytesRead >= 0, "Malformed OggVorbis file!" ); 00279 CLAM_ASSERT( mLastBytesRead % mEncodedChannels == 0, "BIG Whoops!" ); 00280 00281 if ( mLastBytesRead == 0 ) break; 00282 00283 samplesRead = mLastBytesRead / sizeof(TInt16 ); 00284 00285 00286 mDecodeBuffer.insert( mDecodeBuffer.end(), 00287 mBlockBuffer.GetPtr(), 00288 mBlockBuffer.GetPtr() + samplesRead); 00289 } 00290 00291 mFramesLastRead = mDecodeBuffer.size(); 00292 00293 if ( !mDecodeBuffer.empty() ) 00294 { 00295 00296 if ( mDecodeBuffer.size() < unsigned(mInterleavedData.Size()) ) 00297 { 00298 mDecodeBuffer.insert( mDecodeBuffer.end(), 00299 mInterleavedData.Size() - mDecodeBuffer.size(), 00300 0); 00301 } 00302 00303 ConsumeDecodedSamples(); 00304 } 00305 00306 mEOFReached = ( mLastBytesRead == 0) && (mDecodeBuffer.empty()); 00307 00308 } 00309 00310 void OggVorbisAudioStream::MemoryToDiskTransfer() 00311 { 00312 // Yahoo! The vorbis encoder wants the samples 00313 // as floats! 00314 00315 // We expose the buffer for submitting data to the encoder 00316 00317 TIndex currentOffset = 0; 00318 int i; 00319 00320 do 00321 { 00322 for ( i = mEncodeBuffer[0].size(); 00323 i < mAnalysisWindowSize && currentOffset < mInterleavedDataOut.Size(); 00324 i++ ) 00325 { 00326 for ( int j = 0; j < mEncodedChannels; j++ ) 00327 mEncodeBuffer[j].push_front( mInterleavedDataOut[ currentOffset + j ] ); 00328 00329 currentOffset += mEncodedChannels; 00330 } 00331 00332 if ( i == mAnalysisWindowSize ) // enough samples acquired 00333 DoVorbisAnalysis(); 00334 00335 } while ( currentOffset < mInterleavedDataOut.Size() ); 00336 00337 } 00338 00339 void OggVorbisAudioStream::PushAnalysisBlocksOntoOggStream() 00340 { 00341 int eos = 0; 00342 00343 while( vorbis_analysis_blockout( &mDSPState, &mVorbisBlock ) == 1 && !eos ) 00344 { 00345 // we assume we want bitrate management 00346 00347 vorbis_analysis( &mVorbisBlock, NULL ); 00348 vorbis_bitrate_addblock( &mVorbisBlock ); 00349 00350 while( vorbis_bitrate_flushpacket( &mDSPState, &mOggPacket ) ) 00351 { 00352 // We push the packet into the bitstream 00353 ogg_stream_packetin( &mOggStreamState, &mOggPacket ); 00354 00355 // page writeout 00356 00357 00358 while( ogg_stream_pageout( &mOggStreamState, &mOggPage ) > 0 00359 && !eos) 00360 { 00361 fwrite( mOggPage.header, 1, mOggPage.header_len, mFileHandle ); 00362 fwrite( mOggPage.body, 1, mOggPage.body_len, mFileHandle ); 00363 00364 eos = ( ogg_page_eos( &mOggPage ) )? 1 : 0; 00365 00366 } 00367 00368 } 00369 } 00370 00371 } 00372 00373 void OggVorbisAudioStream::DoVorbisAnalysis() 00374 { 00375 00376 float** encBuffer = vorbis_analysis_buffer( &mDSPState, 00377 mAnalysisWindowSize); 00378 00379 //Unused variable: int samplesWrote = 0; 00380 00381 for ( int j = 0; j < mEncodedChannels; j++ ) 00382 { 00383 int i = 0; 00384 00385 while( !mEncodeBuffer[j].empty() ) 00386 { 00387 00388 encBuffer[j][i] = mEncodeBuffer[j].back(); 00389 mEncodeBuffer[j].pop_back(); 00390 i++; 00391 } 00392 00393 // Zero padding 00394 while( i < mAnalysisWindowSize ) 00395 { 00396 encBuffer[j][i] = 0.0; 00397 i++; 00398 } 00399 00400 } 00401 00402 vorbis_analysis_wrote( &mDSPState, mAnalysisWindowSize ); 00403 00404 PushAnalysisBlocksOntoOggStream(); 00405 00406 00407 } 00408 } 00409 00410 } 00411