Example #1
0
tbool CWaveEncoder::Process_Init_Descendant()
{
	if (!mbWroteHeader) {
		if (!mbIgnoreOutputFileNull) {
			if (mpfOutputStream == NULL) {
				AppendErrMsg("CWaveEncoder::Process_Init_Descendant, internal error: Output file NULL");
				return false;
			}
			CDecoder::OutDummyPCMHeader(mpfOutputStream, miOutputBitWidth, miOutputChannels, miOutputSampleFreq);
		}
		mbWroteHeader = true;
	}

	return true;
} // Process_Init_Descendant
tbool CVorbisEncoder::Process_Init_Descendant()
{
	// Setup encoder
	vorbis_info_init(&vi);
	//mfQuality = -0.1;
	if (vorbis_encode_init_vbr(&vi, miOutputChannels, miInputSampleFreq, mfQuality) != 0) {
		AppendErrMsg("Function vorbis_encode_init_vbr failed for unknown reason.");
		return false;
	}
	// Note: Using vorbis_encode_init rather than vorbis_encode_init_vbr makes encoding 4 times slower
	// because it will try harder at targetting the bitrate you ask of it.
	// Also I think the guy with the unpronouncable name that tuned vorbis (the aoTuV versions)
	// only bothered to optimize the "_vbr" method.. and that's quite ok.
	// Lasse
	//
	//if (vorbis_encode_init(&vi, miOutputChannels, miInputSampleFreq, 256000, 192000, 32000) != 0) {
	//	AppendErrMsg("Function vorbis_encode_init(..) failed for unknown reason.");
	//	return false;
	//}

	// TODO: add comments
	vorbis_comment_init(&vc);
	vorbis_comment_add_tag(&vc,"ENCODER","Koblo 1.0 // aoTuV b5");
	vorbis_comment_add_tag(&vc,"KOBLO_ENC_QUAL", (char*)GetQualityName(meQuality));
	
	/* set up the analysis state and auxiliary encoding storage */
	vorbis_analysis_init(&vd,&vi);
	vorbis_block_init(&vd,&vb);
	
	/* set up our packet->stream encoder */
	/* pick a random serial number; that way we can more likely build
		chained streams just by concatenation */
	srand((unsigned)time(NULL));
	ogg_stream_init(&os,rand());
	
	/* Vorbis streams begin with three headers; the initial header (with
		most of the codec setup parameters) which is mandated by the Ogg
		bitstream spec.  The second header holds any comment fields.  The
		third header holds the bitstream codebook.  We merely need to
		make the headers, then pass them to libvorbis one at a time;
		libvorbis handles the additional Ogg bitstream constraints */
	
	{
		ogg_packet header;
		ogg_packet header_comm;
		ogg_packet header_code;
		
		vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
		ogg_stream_packetin(&os,&header); /* automatically placed in its own
			page */
		ogg_stream_packetin(&os,&header_comm);
		ogg_stream_packetin(&os,&header_code);
		
		/* This ensures the actual
			* audio data will start on a new page, as per spec
			*/
		while(1){
			int result=ogg_stream_flush(&os,&og);
			if(result==0)break;
			//fwrite(og.header,1,og.header_len,stdout);
			//fwrite(og.body,1,og.body_len,stdout);
			WriteOutput((char*)og.header, og.header_len);
			WriteOutput((char*)og.body, og.body_len);
		}
	}

	return true;
} // Process_Init_Secendant
tbool CWaveDecoder::Process(IFile* pfInputWaveStream, tint32 iSampleIxToProcess /*= 0*/, tint32 iNbOfSamplesToProcess /*= -1*/, tint32* piSamplesProcessed /*= NULL*/)
{
	if (pfInputWaveStream == NULL) {
		AppendErrMsg("Input wave stream is NULL.");
		return false;
	}

	tint32 iSamplesReturnedThisCall = 0;
	if (piSamplesProcessed)
		*piSamplesProcessed = 0;

	tbool bSameFile = false;
	if (!Process_Init(pfInputWaveStream, 0, 0, 0, &bSameFile))
		return false;
	if (bSameFile) {
		// Should we move read position?
		if (iSampleIxToProcess != muiInputFileSampleIx) {
			// Yes, we must move
			if ((iSampleIxToProcess < 0) || (iSampleIxToProcess >= miIn_Samples)) {
				// ... but we can't - that's an illegal index
				tchar psz[128];
				sprintf(psz, "Can't seek to frame %d (out of range)", iSampleIxToProcess);
				AppendErrMsg(psz);
				return false;
			}
			// Calculate how to skip
			tint32 iSkipFrames = iSampleIxToProcess - miSampleIxOutputBuffs;
			tint32 iSkipBytes = iSkipFrames * miIn_BlockAlign;
			// Skip
			muiInputFilePos += iSkipBytes;
			muiInputFileSampleIx = iSampleIxToProcess;
		}
		pfInputWaveStream->Seek(muiInputFilePos);
	}
	if (!bSameFile) {
		miLastInputBitWidth = 0;
		miIn_Channels = 0;
		miIn_BlockAlign = -1;
		miIn_SampleRate = 0;
		miIn_Samples = 0;

		// Use the open file functionality of one of the encoders to parse the wave file header
		// We pick the wave encoder (but it could just as well have been the Vorbis or LameEncoder)
		IEncoder* pEncoder = IEncoder::Create(geAudioCodecWave);
		CEncoder* pEncoder2 = NULL;
		if (pEncoder)
			pEncoder2 = dynamic_cast<CEncoder*>(pEncoder);
		if (pEncoder2 == NULL) {
			AppendErrMsg("Unable to allocate IEncoder for parsing wave (out of memory?)");
			return false;
		}
		// Set flag to allow Init(NULL)
		pEncoder2->mbIgnoreOutputFileNull = true;
		if (
			(!pEncoder2->Init(NULL, geQualityLossless24))
			||
			(!pEncoder2->Process_InitFile(pfInputWaveStream, NULL, iSampleIxToProcess, &iNbOfSamplesToProcess))
		) {
			tchar pszErr[512];
			pEncoder2->GetErrMsg(pszErr, 512, true);
			AppendErrMsg(pszErr);
			pEncoder2->Destroy();
			pEncoder2 = NULL;
			pEncoder = NULL;
			return false;
		}
		if (1) {
			tchar pszWarnings[1024];
			if (pEncoder2->GetWarnMsg(pszWarnings, 1024, true)) {
				if (*pszWarnings)
					AppendWarnMsg(pszWarnings);
			}
		}
		// Save the info from headers
		miLastInputBitWidth = pEncoder2->miInputBitWidth;
		miIn_Channels = pEncoder2->miInputChannels;
		miIn_BlockAlign = pEncoder2->miInputBlockAlign;
		miIn_SampleRate = pEncoder2->miInputSampleFreq;
		miIn_Samples = pEncoder2->miInputSamples_IncludingSkipped;
		EQuality eQuality = pEncoder2->meQuality;
		if (eQuality < meLowestInputQuality) {
			meLowestInputQuality = eQuality;
		}
		// Save position of data chunk
		muiInputFilePos = pfInputWaveStream->GetCurrentFilePosition();
	}

	tint32 iSamplesDecodedCurrInput = 0;
	//tint32 iPctDisplayed = -1;

	tint32 iSamplesToRead_CurrInput;
	tint32 iSamplesLeft_CurrInput = miIn_Samples - iSampleIxToProcess;
	if ((iNbOfSamplesToProcess >= 0) && (iNbOfSamplesToProcess <= iSamplesLeft_CurrInput)) {
		// Plenty of samples - take all we need
		iSamplesToRead_CurrInput = iNbOfSamplesToProcess;
	}
	else {
		// Insufficient or unspecified samples - limit return to what's there
		iSamplesToRead_CurrInput = iSamplesLeft_CurrInput;
	}

	tint32 iSamplesToRead_Now = 2 * 1024;
	tint32 iBytesToRead_Now = iSamplesToRead_Now * miIn_BlockAlign;

	tbool bFirstDecode = !bSameFile;
	tbool bSuccess = false;
	tbool bExit = false;
	while (!bExit) {
		if (iSamplesToRead_CurrInput < iSamplesToRead_Now) {
			// Last portion
			iSamplesToRead_Now = iSamplesToRead_CurrInput;
			iBytesToRead_Now = iSamplesToRead_Now * miIn_BlockAlign;
		}
		tchar* pcInBuff = PrepareInputBuff(iBytesToRead_Now);
		tint64 iRead = pfInputWaveStream->Read(pcInBuff, iBytesToRead_Now);
		
		if (iRead < 0) {
			AppendErrMsg("input wave error");
			return false;
		}
		if (iRead == 0) {
			// All of wave was read
			
			// Exit with success
			bSuccess = true;
			bExit = true;
			//return true;
		}
		else {
			muiInputFilePos = pfInputWaveStream->GetCurrentFilePosition();
			tint32 iSamplesDecodedNow = (tint32)iRead / miIn_BlockAlign;
			if (iSamplesDecodedNow > 0) {
				if (bFirstDecode) {
					// Only go here once
					bFirstDecode = false;
					
					miLastInputSamples = 0;
					miLastInputChannels = miIn_Channels;
					
					tbool bError = false;
					if ((miIn_Channels != 1) && (miIn_Channels != 2)) {
						tchar psz[128];
						sprintf(psz, "Can only read mono or stereo streams, not %d channels", miIn_Channels);
						AppendErrMsg(psz);
						bError = true;
					}
					
					if ((miOutputSampleFreq > -1) && (miOutputSampleFreq != miIn_SampleRate)) {
						tchar psz[128];
						sprintf(psz, "Sample freq of new wave (%d Hz) differs from first wave freq (%d Hz)", miIn_SampleRate, miOutputSampleFreq);
						AppendErrMsg(psz);
						bError = true;
					}
					else if ((miIn_SampleRate != 44100) && (miIn_SampleRate != 48000) && (miIn_SampleRate != 88200) && (miIn_SampleRate != 96000)) {
						tchar psz[128];
						sprintf(psz, "Sample freq of wave (%d Hz) can't be used", miIn_SampleRate);
						AppendErrMsg(psz);
						bError = true;
					}
					else {
						miOutputSampleFreq = miIn_SampleRate;
#ifndef AC_IDECODER_NOT_BACK_COMPAT
						miSampleFreq = miIn_SampleRate;
#endif // AC_IDECODER_NOT_BACK_COMPAT
					}
					
					if (bError) {
						bExit = true;
						bSuccess = false;
						//return false;
					}
					
					if (miOutputChannels == -1) {
						miOutputChannels = miIn_Channels;
					}
					miLastInputChannels = miIn_Channels;
					
					miOutputBlockAlign = (miOutputBitWidth / 8) * (mbSplit ? 1 : miOutputChannels);
					
					// First time here? Write the RIFF/Wave header
					if ((mbRIFFWaveHeader) && (mpfOutWaveStream1->GetCurrentFilePosition() == 0)) {
						if (!mbSplit) {
							// A single output wave file receives both channels
							OutDummyPCMHeader(mpfOutWaveStream1, miOutputBitWidth, miOutputChannels, miOutputSampleFreq);
						}
						else {
							// Must split data into two mono wave files
							OutDummyPCMHeader(mpfOutWaveStream1, miOutputBitWidth, 1, miOutputSampleFreq);
							OutDummyPCMHeader(mpfOutWaveStream2, miOutputBitWidth, 1, miOutputSampleFreq);
						}
					}

					// End of "Only go here once"
				}

				muiInputFileSampleIx += iSamplesDecodedNow;
				iSamplesDecodedCurrInput += iSamplesDecodedNow;
				iSamplesToRead_CurrInput -= iSamplesDecodedNow;
				
				// This points to the buffer(s) we're gonna write
				mpcBufferToWriteNowL = NULL;
				mpcBufferToWriteNowR = NULL;
				tint32 iBytesOutputBuffs = 0;
				miSamplesOutputBuffs = iSamplesDecodedNow * miOutputBlockAlign;
				
				if (miLastInputBitWidth == 16) {
					if (miOutputBitWidth == 16) {
						// output as 16 bit
						if (miIn_Channels == 1) {
							// input is mono
							if (miOutputChannels == 1) {
								// output 16 bit mono
								
								// Use buffer as it is
								mpcBufferToWriteNowL = pcInBuff;
								iBytesOutputBuffs = (tint32)iRead;
							}
							else {
								// output 16 bit upmix stereo
								st::Xlat::PCM16Mono_PCM16Stereo(pcInBuff, (tint32)iRead,
									&iBytesOutputBuffs, &mpcOutputBuffL, &miSizeOutputBuffL);
								mpcBufferToWriteNowL = mpcOutputBuffL;
							}
						}
						else {
							// input is stereo
							if (miOutputChannels == 1) {
								// output 16 bit downmix mono

								st::Xlat::PCM16Stereo_PCM16Mono_InPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs);
								mpcBufferToWriteNowL = pcInBuff;							
							}
							else if (mbSplit) {
								// output 16 bit dual mono

								st::Xlat::PCM16Stereo_DualPCM16Mono_PartialInPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs, &mpcOutputBuffR, &miSizeOutputBuffR);
								mpcBufferToWriteNowL = pcInBuff;
								mpcBufferToWriteNowR = mpcOutputBuffR;
							}
							else {
								// output 16 bit stereo
								
								// Use buffer as it is
								mpcBufferToWriteNowL = pcInBuff;
								iBytesOutputBuffs = (tint32)iRead;
							}
						}
					}
					else {
						// input = 16 bits, output as 24 bit
						if (miIn_Channels == 1) {
							// input is mono
							if (miOutputChannels == 1) {
								// output 16 => 24 bit mono
								
								st::Xlat::PCM16Mono_PCM24Mono(pcInBuff, (tint32)iRead, &iBytesOutputBuffs, &mpcOutputBuffL, &miSizeOutputBuffL);
								mpcBufferToWriteNowL = mpcOutputBuffL;
							}
							else {
								// output 16 => 24 bit upmix stereo
								
								// Double every sample
								st::Xlat::PCM16Mono_PCM24Stereo(pcInBuff, (tint32)iRead, &iBytesOutputBuffs, &mpcOutputBuffL, &miSizeOutputBuffL);
								mpcBufferToWriteNowL = mpcOutputBuffL;
							}
						}
						else {
							// input is stereo
							if (mbSplit) {
								// Split 16 bit stereo wave into two 24 bit mono wave files

								st::Xlat::PCM16Stereo_DualPCM24Mono_PartialInPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs, &mpcOutputBuffR, &miSizeOutputBuffR);
								mpcBufferToWriteNowL = pcInBuff;
								mpcBufferToWriteNowR = mpcOutputBuffR;
							}
							else {
								if (miOutputChannels == 1) {
									// output 16 => 24 bit downmix mono

									st::Xlat::PCM16Stereo_PCM24Mono_InPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs);
									mpcBufferToWriteNowL = pcInBuff;
								}
								else {
									// output 16 => 24 bit stereo

									st::Xlat::PCM16Stereo_PCM24Stereo(pcInBuff, (tint32)iRead, &iBytesOutputBuffs, &mpcOutputBuffL, &miSizeOutputBuffL);
									mpcBufferToWriteNowL = mpcOutputBuffL;
								}
							}
						}
					}
				}
				else {
					// input is 24 bit
					if (miOutputBitWidth == 16) {
						// Output 24 => 16 bit

						if (miIn_Channels == 1) {
							if (miOutputChannels == 1) {
								// 24 mono => 16 mono
								st::Xlat::PCM24Mono_PCM16Mono_InPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs);
								mpcBufferToWriteNowL = pcInBuff;
							}
							else {
								// 24 mono => 16 stereo
								st::Xlat::PCM24Mono_PCM16Stereo(pcInBuff, (tint32)iRead, &iBytesOutputBuffs, &mpcOutputBuffL, &miSizeOutputBuffL);
								mpcBufferToWriteNowL = mpcOutputBuffL;
							}
						}
						else if (mbSplit) {
							// 24 stereo => dual 16 mono
							st::Xlat::PCM24Stereo_DualPCM16Mono_PartialInPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs, &mpcOutputBuffR, &miSizeOutputBuffR);
							mpcBufferToWriteNowL = pcInBuff;
							mpcBufferToWriteNowR = mpcOutputBuffR;
						}
						else {
							if (miOutputChannels == 1) {
								// 24 stereo => 16 mono
								st::Xlat::PCM24Stereo_PCM16Mono_InPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs);
								mpcBufferToWriteNowL = pcInBuff;
							}
							else {
								// 24 stereo => 16 stereo
								st::Xlat::PCM24Stereo_PCM16Stereo_InPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs);
								mpcBufferToWriteNowL = pcInBuff;
							}
						}
					}
					else {
						// Both input and output is 24 bits
						if (miIn_Channels == 1) {
							if (miOutputChannels == 1) {
								// 24 mono (Use data as is)
								mpcBufferToWriteNowL = pcInBuff;
								iBytesOutputBuffs = (tint32)iRead;
							}
							else {
								// 24 mono => 24 stereo
								st::Xlat::PCM24Mono_PCM24Stereo(pcInBuff, (tint32)iRead, &iBytesOutputBuffs, &mpcOutputBuffL, &miSizeOutputBuffL);
								mpcBufferToWriteNowL = mpcOutputBuffL;
							}
						}
						else {
							// input is stereo
							if (mbSplit) {
								// 24 stereo => dual 24 mono
								st::Xlat::PCM24Stereo_DualPCM24Mono_PartialInPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs, &mpcOutputBuffR, &miSizeOutputBuffR);
								mpcBufferToWriteNowL = pcInBuff;
								mpcBufferToWriteNowR = mpcOutputBuffR;
							}
							else if (miOutputChannels == 1) {
								// 24 stereo => 24 mono
								st::Xlat::PCM24Stereo_PCM24Mono_InPlace(pcInBuff, (tint32)iRead, &iBytesOutputBuffs);
								mpcBufferToWriteNowL = pcInBuff;
							}
							else {
								// 24 stereo (Use data as is)
								mpcBufferToWriteNowL = pcInBuff;
								iBytesOutputBuffs = (tint32)iRead;
							}
						}
					}
				}
				
				// Write the converted output
				if (!mbAllowNoOutputFile) {
					if (mpcBufferToWriteNowL == NULL) {
						// We've made an error
						AppendErrMsg("Internal error: mpcBufferToWriteNowL == NULL");
						return false;
					}

					if (!mbSplit) {
						// Write to one (stereo or mono) wave file
						mpfOutWaveStream1->Write((const char*)mpcBufferToWriteNowL, iBytesOutputBuffs);
					}
					else {
						// Split into two (mono) wave files
						if (mpcBufferToWriteNowR == NULL) {
							// We've made an error
							AppendErrMsg("Internal error: mpcBufferToWriteNowR == NULL");
							return false;
						}

						mpfOutWaveStream1->Write((const char*)mpcBufferToWriteNowL, iBytesOutputBuffs);
						mpfOutWaveStream2->Write((const char*)mpcBufferToWriteNowR, iBytesOutputBuffs);
					}
				}
				
				miLastInputSamples += iSamplesDecodedNow;
				miSamplesTotal += iSamplesDecodedNow;
				iSamplesReturnedThisCall += iSamplesDecodedNow;
			}
		}

		tint64 iPct = miLastInputSamples * 100;
		tint64 iTotal = miIn_Samples;
		if (iTotal > 0) {
			iPct /= iTotal;
			if (iPct != miPctDisplayed) {
				tchar psz[32];
				sprintf(psz, "\r%3d%% ", (tint32)iPct);
				std::cerr << psz;
				miPctDisplayed = (tint32)iPct;
			}
		}
		
	}
	
	// Did we succeed?
	if (piSamplesProcessed)
		*piSamplesProcessed = iSamplesReturnedThisCall;
	return bSuccess;
} // Process