00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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 unsigned OggVorbisAudioStream::mMaxBlockSize = 4096 / sizeof(TInt16);
00043 const unsigned OggVorbisAudioStream::mAnalysisWindowSize = 1024;
00044
00045 OggVorbisAudioStream::OggVorbisAudioStream( const AudioFile& file )
00046 : mFileHandle( NULL )
00047 , mEncoding( false )
00048 {
00049 mName = file.GetLocation();
00050 mEncodedSampleRate = (int)file.GetHeader().GetSampleRate();
00051 mChannels = (int)file.GetHeader().GetChannels();
00052 mEncodeBuffer.resize( mChannels );
00053 mBlockBuffer.resize( mMaxBlockSize );
00054 }
00055
00056 OggVorbisAudioStream::~OggVorbisAudioStream()
00057 {
00058 Dispose();
00059 }
00060
00061 void OggVorbisAudioStream::PrepareReading()
00062 {
00063 mFileHandle = fopen(mName.c_str(), "rb");
00064 if (mFileHandle == NULL)
00065 {
00066 std::string msgString = "Could not open ";
00067 msgString += mName;
00068 msgString +=" for reading!";
00069 CLAM_ASSERT( false, msgString.c_str() );
00070 }
00071 int error = ov_open(mFileHandle, &mNativeFileParams, NULL, 0);
00072 if ( error < 0 )
00073 {
00074 fclose( mFileHandle );
00075 mFileHandle=NULL;
00076 std::string msgString = mName;
00077 msgString += " is not a valid Ogg/Vorbis file!";
00078 CLAM_ASSERT( false, msgString.c_str() );
00079 }
00080
00081 vorbis_info* info = ov_info(&mNativeFileParams, -1);
00082 CLAM_ASSERT(mChannels==unsigned(info->channels),
00083 "OggVorbisAudioStream: channels info changed before opening");
00084
00085 mCurrentSection = 0;
00086 mFramePosition = 0;
00087
00088
00089
00090
00091
00092
00093 ov_pcm_seek( &mNativeFileParams, 0 );
00094 }
00095
00096 void OggVorbisAudioStream::PrepareWriting()
00097 {
00098 mFileHandle = fopen(mName.c_str(), "wb");
00099 if ( mFileHandle==NULL )
00100 {
00101 std::string msgString = "Could not open ";
00102 msgString += mName;
00103 msgString +=" for writing!";
00104 CLAM_ASSERT( false, msgString.c_str() );
00105 }
00106 VorbisI_EncoderSetup();
00107 mEncoding = true;
00108 }
00109
00110 void OggVorbisAudioStream::VorbisI_EncoderSetup()
00111 {
00112
00113 vorbis_info_init( &mStreamInfo );
00114
00115 int retValue = vorbis_encode_init_vbr(
00116 &mStreamInfo, mChannels, mEncodedSampleRate, 0.5 );
00117
00118 CLAM_ASSERT( retValue == 0, "Error trying to initialize Vorbis encoder!" );
00119
00120
00121 vorbis_comment_init( &mFileComments );
00122 vorbis_comment_add_tag( &mFileComments, "ENCODER", "CLAM" );
00123
00124
00125 vorbis_analysis_init( &mDSPState, &mStreamInfo );
00126 vorbis_block_init( &mDSPState, &mVorbisBlock );
00127
00128
00129
00130
00131 ogg_stream_init( &mOggStreamState, rand() );
00132
00133 WriteBitstreamHeader();
00134 }
00135
00136 void OggVorbisAudioStream::WriteBitstreamHeader()
00137 {
00138
00139
00140
00141
00142
00143 ogg_packet header_codec_setup;
00144 ogg_packet header_comments;
00145 ogg_packet header_codebooks;
00146
00147
00148
00149 vorbis_analysis_headerout( &mDSPState, &mFileComments,
00150 &header_codec_setup,
00151 &header_comments,
00152 &header_codebooks );
00153
00154
00155 ogg_stream_packetin( &mOggStreamState, &header_codec_setup );
00156 ogg_stream_packetin( &mOggStreamState, &header_comments );
00157 ogg_stream_packetin( &mOggStreamState, &header_codebooks );
00158
00159
00160
00161
00162 while( ogg_stream_flush( &mOggStreamState, &mOggPage ) > 0 )
00163 {
00164 fwrite( mOggPage.header, 1, mOggPage.header_len, mFileHandle );
00165 fwrite( mOggPage.body, 1, mOggPage.body_len, mFileHandle );
00166 }
00167 }
00168
00169 void OggVorbisAudioStream::Dispose()
00170 {
00171 if ( mFileHandle == NULL) return;
00172 if ( not mEncoding )
00173 {
00174 ov_clear( &mNativeFileParams );
00175 mFileHandle = NULL;
00176 return;
00177 }
00178
00179
00180
00181 if ( !mEncodeBuffer[0].empty() )
00182 DoVorbisAnalysis();
00183
00184
00185
00186 vorbis_analysis_wrote( &mDSPState, 0 );
00187
00188
00189 PushAnalysisBlocksOntoOggStream();
00190
00191
00192 ogg_stream_clear( &mOggStreamState );
00193 vorbis_block_clear( &mVorbisBlock );
00194 vorbis_dsp_clear( &mDSPState );
00195 vorbis_comment_clear( &mFileComments );
00196 vorbis_info_clear( &mStreamInfo );
00197
00198 fclose( mFileHandle );
00199 mFileHandle = NULL;
00200 mEncoding = false;
00201 }
00202
00203 void OggVorbisAudioStream::ConsumeDecodedSamples()
00204 {
00205 unsigned nItems = mInterleavedData.size();
00206 TData* pSamples = &mInterleavedData[0];
00207
00208 CLAM_ASSERT( mDecodeBuffer.size() >= nItems,
00209 "This method cannot be called if the decode buffer"
00210 " has less samples than requested by the upper level");
00211
00212 static const TData norm = 1.0 / 32768.0;
00213
00214 const TData* pSamplesEnd = pSamples + nItems;
00215 typedef std::deque<TInt16> sampleDeque;
00216 for( sampleDeque::iterator i = mDecodeBuffer.begin(); pSamples < pSamplesEnd; i++)
00217 *pSamples++ = TData(*i)*norm;
00218
00219 mDecodeBuffer.erase(
00220 mDecodeBuffer.begin(),
00221 mDecodeBuffer.begin()+nItems );
00222
00223 unsigned nFrames = nItems / mChannels;
00224 mFramePosition+=nFrames;
00225 }
00226
00227 void OggVorbisAudioStream::DiskToMemoryTransfer()
00228 {
00229
00230 unsigned samplesRead = 0;
00231
00232 while (mDecodeBuffer.size() < mInterleavedData.size())
00233 {
00234 mLastBytesRead = ov_read(
00235 &mNativeFileParams,
00236 (char*)&mBlockBuffer[0],
00237 mMaxBlockSize*sizeof(TInt16),
00238 HOST_ENDIANESS,
00239 2, 1, &mCurrentSection );
00240
00241 CLAM_ASSERT( mLastBytesRead >= 0, "Malformed OggVorbis file!" );
00242 CLAM_ASSERT( mLastBytesRead % mChannels == 0, "BIG Whoops!" );
00243
00244 if ( mLastBytesRead == 0 ) break;
00245
00246 samplesRead = mLastBytesRead / sizeof(TInt16 );
00247
00248 mDecodeBuffer.insert(
00249 mDecodeBuffer.end(),
00250 mBlockBuffer.begin(),
00251 mBlockBuffer.begin() + samplesRead);
00252 }
00253
00254 mFramesLastRead = mDecodeBuffer.size();
00255 mEOFReached = ( mLastBytesRead == 0) && (mDecodeBuffer.empty());
00256
00257 if (mDecodeBuffer.empty()) return;
00258
00259 if (mDecodeBuffer.size() < mInterleavedData.size())
00260 {
00261 mDecodeBuffer.insert(
00262 mDecodeBuffer.end(),
00263 mInterleavedData.size() - mDecodeBuffer.size(),
00264 0);
00265 }
00266 ConsumeDecodedSamples();
00267 }
00268
00269 void OggVorbisAudioStream::MemoryToDiskTransfer()
00270 {
00271
00272
00273
00274
00275
00276 unsigned currentOffset = 0;
00277 unsigned i;
00278 do
00279 {
00280 for ( i = mEncodeBuffer[0].size();
00281 i < mAnalysisWindowSize && currentOffset < mInterleavedData.size();
00282 i++ )
00283 {
00284 for (unsigned j=0; j<mChannels; j++)
00285 mEncodeBuffer[j].push_front( mInterleavedData[ currentOffset + j ] );
00286
00287 currentOffset += mChannels;
00288 }
00289
00290 if ( i == mAnalysisWindowSize )
00291 DoVorbisAnalysis();
00292
00293 } while ( currentOffset < mInterleavedData.size() );
00294
00295 }
00296
00297 void OggVorbisAudioStream::PushAnalysisBlocksOntoOggStream()
00298 {
00299 int eos = 0;
00300 while( vorbis_analysis_blockout( &mDSPState, &mVorbisBlock ) == 1 && !eos )
00301 {
00302
00303 vorbis_analysis( &mVorbisBlock, NULL );
00304 vorbis_bitrate_addblock( &mVorbisBlock );
00305
00306 while( vorbis_bitrate_flushpacket( &mDSPState, &mOggPacket ) )
00307 {
00308
00309 ogg_stream_packetin( &mOggStreamState, &mOggPacket );
00310
00311
00312 while( ogg_stream_pageout( &mOggStreamState, &mOggPage ) > 0
00313 && !eos)
00314 {
00315 fwrite( mOggPage.header, 1, mOggPage.header_len, mFileHandle );
00316 fwrite( mOggPage.body, 1, mOggPage.body_len, mFileHandle );
00317 eos = ( ogg_page_eos( &mOggPage ) )? 1 : 0;
00318 }
00319 }
00320 }
00321 }
00322
00323 void OggVorbisAudioStream::DoVorbisAnalysis()
00324 {
00325 float** encBuffer = vorbis_analysis_buffer(
00326 &mDSPState, mAnalysisWindowSize);
00327
00328 for (unsigned j = 0; j < mChannels; j++ )
00329 {
00330 unsigned i = 0;
00331 while( !mEncodeBuffer[j].empty() )
00332 {
00333 encBuffer[j][i] = mEncodeBuffer[j].back();
00334 mEncodeBuffer[j].pop_back();
00335 i++;
00336 }
00337
00338
00339 while( i < mAnalysisWindowSize )
00340 {
00341 encBuffer[j][i] = 0.0;
00342 i++;
00343 }
00344 }
00345 vorbis_analysis_wrote( &mDSPState, mAnalysisWindowSize );
00346 PushAnalysisBlocksOntoOggStream();
00347 }
00348 void OggVorbisAudioStream::SeekTo(unsigned long framePosition)
00349 {
00350 ov_pcm_seek( &mNativeFileParams, framePosition );
00351 mFramePosition = ov_pcm_tell(&mNativeFileParams);
00352 }
00353 }
00354
00355 }
00356