www.pudn.com > VirtualVCR-src-v2.6.9.zip > AudioResample.cpp


/* 
	Virtual VCR 
    Copyright (C) 2002  Shaun Faulds 
 
    This program is free software; you can redistribute it and/or modify 
    it under the terms of the GNU General Public License as published by 
    the Free Software Foundation; either version 2 of the License, or 
    (at your option) any later version. 
 
    This program is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    GNU General Public License for more details. 
 
    You should have received a copy of the GNU General Public License 
    along with this program; if not, write to the Free Software 
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 
	Acknowledgments: 
	This application and associated filters are based on the examples 
	from the Microsoft DirectX DirectShow SDK. 
*/ 
 
#include  
#include  
#include  
#include  
 
#if (1100 > _MSC_VER) 
#include  
#else 
#include  
#endif 
 
#include "AudioResampleUIDs.h" 
#include "iAudioResample.h" 
#include "AudioResample.h" 
#include "AudioResampleProp.h" 
#include "resource.h" 
 
#include "src_filter_asm.h" 
#include "src_tables.c" 
 
#include "AudioResample.version" 
 
////////////////////////////////////////////////////////////// 
// 
// Override CTransformFilter method. 
// Verifies that the input pin supports the media type. 
// Part of the Connect process. 
// Ensure that we do not get connected to formats that we can't handle. 
// We only work for wave audio, 8 or 16 bit, uncompressed. 
// 
HRESULT CAudioResample::CheckInputType(const CMediaType* pmt) 
{ 
    WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmt->pbFormat; 
 
    // Reject non-Audio types. 
    // 
    if (pmt->majortype != MEDIATYPE_Audio) 
	{ 
        return VFW_E_TYPE_NOT_ACCEPTED; 
    } 
 
    // Reject invalid format blocks 
    // 
    if (pmt->formattype != FORMAT_WaveFormatEx) 
	{ 
        return VFW_E_TYPE_NOT_ACCEPTED; 
	} 
 
    // Reject compressed audio 
    // 
    if (pwfx->wFormatTag != WAVE_FORMAT_PCM) 
	{ 
        return VFW_E_TYPE_NOT_ACCEPTED; 
    } 
 
    // Accept only 8 or 16 bit 
    // 
    if (pwfx->wBitsPerSample != 8 && pwfx->wBitsPerSample != 16) 
	{ 
        return VFW_E_TYPE_NOT_ACCEPTED; 
    } 
 
    return NOERROR; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Verifies that the output pin supports the media type. 
// 
HRESULT CAudioResample::CheckOutputType(const CMediaType* mediaType) 
{ 
	// assertion 
	CheckPointer (mediaType, E_POINTER); 
 
	// major type supported? 
	if (!IsEqualGUID (*mediaType->Type (), MEDIATYPE_Audio)) 
	{ 
		return E_FAIL; 
	} 
 
	// we can support this connection 
	return S_OK; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Verifies that the input and output pins support the media type.  
// Calls CheckOutputType and CheckInputType depending on 
// the pin direction. 
// 
HRESULT CAudioResample::CheckTransform (const CMediaType* inputMediaType, const CMediaType* outputMediaType) 
{ 
 
	HRESULT result = S_OK; 
 
	// check input media 
	if (inputMediaType != 0) 
	{ 
		result = this->CheckInputType(inputMediaType); 
		if (result != S_OK) 
		{ 
			return result; 
		} 
	} 
 
	// check output media 
	if (outputMediaType != 0) 
	{ 
		result = this->CheckOutputType(outputMediaType); 
		if (result != S_OK) 
		{ 
			return result; 
		} 
	} 
 
	// Found a vaild match 
	return S_OK; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Sets the number and size of buffers required for the transfer. 
// Works out the buffer size by multiplying the width by the height 
// of the output video frame by the colour format bit byte size. 
// BufferSize = Width * Height * (Colour Bit Depth in bytes) 
// 
HRESULT CAudioResample::DecideBufferSize ( IMemAllocator* allocator, ALLOCATOR_PROPERTIES* properties ) 
{ 
 
    // Is the input pin connected 
 
    if (m_pInput->IsConnected() == FALSE) 
	{ 
        return E_UNEXPECTED; 
    } 
 
    ASSERT(allocator); 
    ASSERT(properties); 
    HRESULT hr = NOERROR; 
 
	// Reserve one 500 Kb buffer, this should be enough 
    properties->cBuffers = 1; 
	properties->cbBuffer = 500000; 
    //properties->cbBuffer = m_pInput->CurrentMediaType().GetSampleSize(); 
    ASSERT(properties->cbBuffer); 
	//MsgBOX(TEXT("Number of buffers:%d  Size of Buffers:%d"), properties->cBuffers, properties->cbBuffer); 
 
    // Ask the allocator to reserve us some sample memory, NOTE the function 
    // can succeed (that is return NOERROR) but still not have allocated the 
    // memory that we requested, so we must check we got whatever we wanted 
 
    ALLOCATOR_PROPERTIES Actual; 
    hr = allocator->SetProperties(properties,&Actual); 
    if (FAILED(hr)) 
	{ 
        return hr; 
    } 
 
    ASSERT( Actual.cBuffers == 1 ); 
 
    if (properties->cBuffers > Actual.cBuffers || 
            properties->cbBuffer > Actual.cbBuffer) 
	{ 
		return E_FAIL; 
    } 
    return NOERROR; 
} 
 
 
////////////////////////////////////////////////////////////// 
// 
// Returns one of the media types that the output pin supports 
// 
HRESULT CAudioResample::GetMediaType(int position, CMediaType* mediaType) 
{ 
	// make sure the input pin is connected 
	if (this->m_pInput->IsConnected () == FALSE) 
	{ 
		return E_UNEXPECTED; 
	} 
 
	// assertion 
	if (mediaType == 0) 
	{ 
		return E_POINTER; 
	} 
	if (position < 0) 
	{ 
		return E_INVALIDARG; 
	} 
	if (position > 1) 
	{ 
		return VFW_S_NO_MORE_ITEMS; 
	} 
 
	*mediaType = m_pInput->CurrentMediaType(); 
 
	return S_OK; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Sets the input media type, this is called when SetMediaType 
// is called with a pin direction of INPUT 
// 
HRESULT CAudioResample::SetInputMediaType(const CMediaType* pmt) 
{ 
    // Record what we need for doing the actual transform 
 
    WAVEFORMATEX *pwfx = (WAVEFORMATEX *) pmt->Format(); 
 
    m_Channels =  pwfx->nChannels; 
    m_SamplesPerSec = pwfx->nSamplesPerSec; 
    // Ignored: pwfx->nAvgBytesPerSec; 
    // Ignored: pwfx->nBlockAlign; 
    m_BytesPerSample = pwfx->wBitsPerSample/8; 
 
    // Call the base class to do its thing 
    CTransformFilter::SetMediaType(PINDIR_INPUT, pmt); 
 
	//Reconnect the output pins to pass the new size downstream 
//	ReconnectOutputPins(); 
 
	//MsgBOX(TEXT("%d"), m_BytesPerSample); 
 
	return S_OK; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Sets the output media type, this is called when SetMediaType 
// is called with a pin direction of OUTPUT 
// 
HRESULT CAudioResample::SetOutputMediaType (const CMediaType* mediaType) 
{ 
	// copy media type 
	this->outputMediaType = *mediaType; 
 
	return S_OK; 
} 
 
 
////////////////////////////////////////////////////////////// 
// 
// Informs when the media type is established for the connection.  
// This then calls the SetInputMediaType and SetOutputMediaType 
// for each pin direction. 
// 
HRESULT CAudioResample::SetMediaType(PIN_DIRECTION direction, const CMediaType* pmt) 
{ 
	// assertion 
	CheckPointer (pmt, E_POINTER); 
 
	// Call super method, pass it what we got here. 
	HRESULT result = this->CTransformFilter::SetMediaType (direction, pmt); 
	if (result != S_OK) 
	{ 
		return result; 
	} 
 
	// set media type for input pin? 
	if (direction == PINDIR_INPUT) 
	{ 
		this->SetInputMediaType(pmt); 
	} 
 
	// set media type for the output pin 
	else if (direction == PINDIR_OUTPUT) 
	{ 
		this->SetOutputMediaType(pmt); 
	} 
 
	// unrecognized pin direction 
	else 
	{ 
		return E_FAIL; 
	} 
 
	return S_OK; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Performs transform operations of the filter. 
// Used PrepareOutputSample to copy the properties from 
// the input sample to the output sample then does 
// processes the video data by copying the input 
// data from the input sample to the output sample. 
// Only the data that is in the cropped are is copied! 
// 
HRESULT CAudioResample::Transform(IMediaSample* inSample,  IMediaSample* outSample) 
{ 
	// 
	// Perpare output sample. 
	// Use the local method to copy sample times etc 
	// from input sample to the output sample 
	// 
	HRESULT result = this->PrepareOutputSample(inSample, outSample); 
	if (result != S_OK) return result; 
 
	// Now do the data copy 
	BYTE *srcBuffer = 0; 
	BYTE *dstBuffer = 0; 
	inSample->GetPointer (&srcBuffer); 
	outSample->GetPointer (&dstBuffer); 
 
	int samplesUpdated = 0; 
 
	long lSourceSize = inSample->GetActualDataLength(); 
//MsgBOX(TEXT("%d"), lSourceSize); 
 
	// Stereo 16 Bit 
	if(m_BytesPerSample == 2 && m_Channels == 2) 
	{  
//MsgBOX(TEXT("Stereo 16 bit")); 
		long currentSamplesNo = (lSourceSize / 4); // for Stereo 16 bit samples are 4 bytes 
 
		totalRaw = totalRaw + currentSamplesNo; // keep a running tally of all raw data 
 
		double toADD = (percent * currentSamplesNo) + carryOverAdd; // samples to add plus the carry over from last update 
 
		long samplesToAdd = (long)toADD; // Just want the full sample number 
 
		carryOverAdd = (toADD - samplesToAdd); // store the carry over for next update 
 
		samplesUpdated = samplesToAdd; 
 
		sample_rate_conversion_HQ_16bit_stereo( 
			(long *)srcBuffer, 
			currentSamplesNo, 
			(long *)dstBuffer, 
			(currentSamplesNo + samplesToAdd), 
			SRC_H); 
 
		outSample->SetActualDataLength((currentSamplesNo + samplesToAdd) * 4);	 
 
	} 
	// Mono 16 Bit 
	else if(m_BytesPerSample == 2 && m_Channels == 1) 
	{ 
//MsgBOX(TEXT("Mono 16 bit")); 
		long currentSamplesNo = (lSourceSize / 2); // for Stereo 16 bit samples are 4 bytes 
 
		totalRaw = totalRaw + currentSamplesNo; // keep a running tally of all raw data 
 
		double toADD = (percent * currentSamplesNo) + carryOverAdd; // samples to add plus the carry over from last update 
 
		long samplesToAdd = (long)toADD; // Just want the full sample number 
 
		carryOverAdd = (toADD - samplesToAdd); // store the carry over for next update 
 
		samplesUpdated = samplesToAdd; 
 
		sample_rate_conversion_HQ_16bit_mono( 
			(short *)srcBuffer, 
			currentSamplesNo, 
			(short *)dstBuffer, 
			(currentSamplesNo + samplesToAdd), 
			SRC_H); 
 
		outSample->SetActualDataLength((currentSamplesNo + samplesToAdd) * 2);	 
 
	} 
	// We can not do this format 16 Bit mono and stereo only 
	else 
	{ 
		if(!seen) 
		{ 
			MsgBOX(TEXT("This Audio Resample Filter can only resample 16 bit audio!\nYour audio will be copied without resampling.")); 
			seen = true; 
		} 
		CopyMemory((PVOID)dstBuffer,(PVOID)srcBuffer, lSourceSize); 
		outSample->SetActualDataLength(lSourceSize);	 
	} 
 
	totalAdded += samplesUpdated; 
 
	int totalNow = (int)(totalAdded + totalRaw); 
	actualPercent = (double)((double)totalNow / (double)(int)totalRaw) * 100.0; 
 
	return S_OK; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Prepares the output sample. 
// This copies the property from the 
// input sample to the output sample. 
// 
HRESULT CAudioResample::PrepareOutputSample (IMediaSample* inSample, IMediaSample* outSample) 
{ 
	// assertions 
	CheckPointer(inSample, E_POINTER); 
	CheckPointer(outSample, E_POINTER);	 
 
	//.1 
	// set media type 
	// 
	//outSample->SetMediaType(&this->outputMediaType); 
 
	//.2 
	// copy the sample times 
	// 
 
	REFERENCE_TIME timeStart = 0; 
	REFERENCE_TIME timeEnd = 0; 
	if (inSample->GetTime (&timeStart, &timeEnd) == S_OK) 
	{ 
		outSample->SetTime (&timeStart, &timeEnd); 
	} 
 
	LONGLONG mediaStart = 0; 
	LONGLONG mediaEnd = 0; 
	if (inSample->GetMediaTime (&mediaStart, &mediaEnd) == S_OK) 
	{ 
			outSample->SetMediaTime (&mediaStart,&mediaEnd); 
	} 
	 
	//.3 
	// copy the sync point property 
	// 
	HRESULT result = inSample->IsSyncPoint(); 
	if (result == S_OK) 
	{ 
		outSample->SetSyncPoint(TRUE); 
	} 
	else if (result == S_FALSE) 
	{ 
		outSample->SetSyncPoint(FALSE); 
	} 
	else 
	{ 
		return E_UNEXPECTED; 
	} 
 
	//.4 
	// copy the preroll property 
	// 
	result = inSample->IsPreroll (); 
	if (result == S_OK) 
	{ 
		outSample->SetPreroll (TRUE); 
	} 
	else if (result == S_FALSE) 
	{ 
		outSample->SetPreroll (FALSE); 
	} 
	else 
	{ 
		return E_UNEXPECTED; 
	} 
 
	//.5 
	// copy the discontinuity property 
	// 
	result = inSample->IsDiscontinuity (); 
	if (result == S_OK) 
	{ 
		outSample->SetDiscontinuity (TRUE); 
	} 
	else if (result == S_FALSE) 
	{ 
		outSample->SetDiscontinuity(FALSE); 
	} 
	else 
	{ 
		return E_UNEXPECTED; 
	} 
 
	return S_OK; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Interface Methods to get and set the crop data 
// 
 
// 
// Gets the samples per second data 
// 
STDMETHODIMP CAudioResample::get_Percent(double* data) 
{ 
	*data = percentChange; 
	return NOERROR; 
} 
 
STDMETHODIMP CAudioResample::set_Percent(double data) 
{ 
	percentChange = data; 
	calculateInterval(); 
	return NOERROR; 
} 
 
STDMETHODIMP CAudioResample::get_SamplesAdded(__int64* data) 
{ 
	*data = totalAdded; 
	return NOERROR; 
} 
 
STDMETHODIMP CAudioResample::get_RawTotal(__int64* data) 
{ 
	*data = totalRaw; 
	return NOERROR; 
} 
 
STDMETHODIMP CAudioResample::get_ActualPercent(double* data) 
{ 
	*data = actualPercent; 
	return NOERROR; 
} 
 
//This is for the common interface IFilterProperties 
// 
// Gets the data properties 
// 
STDMETHODIMP CAudioResample::get_Prop(char*& data) 
{ 
	return NOERROR; 
} 
 
STDMETHODIMP CAudioResample::set_Prop(char* data) 
{ 
	return NOERROR; 
} 
 
STDMETHODIMP CAudioResample::get_Version(char *data) 
{ 
	wsprintf(data, "%s", MAIN_VERSION); 
 
	return NOERROR; 
} 
 
////////////////////////////////////////////////////////////// 
// 
// Method to display a message box to show data 
// 
int CAudioResample::MsgBOX(LPTSTR sz,...)  
{ 
    static TCHAR tach[2000]; 
    va_list va; 
 
    va_start(va, sz); 
    wvsprintf(tach, sz, va); 
    va_end(va); 
    MessageBox(HWND_DESKTOP, tach, "Filter Message Box", MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL); 
    return FALSE; 
} 
 
////////////////////////////////////////////////////////////// 
// 
//Reconnect the output pins to pass the new size down the stream 
// 
BOOL CAudioResample::ReconnectOutputPins() 
{ 
 
	FILTER_INFO filerInfo; 
	HRESULT hr = this->QueryFilterInfo(&filerInfo); 
 
	if(filerInfo.pGraph) 
	{ 
		IPin *pP; 
		ULONG u; 
		IEnumPins *pins = NULL; 
 
		PIN_INFO pininfo; 
		hr = this->EnumPins(&pins); 
		pins->Reset(); 
 
		while(hr == NOERROR) 
		{ 
	 
			hr = pins->Next(1, &pP, &u);				 
 
			if(hr == S_OK && pP) 
			{ 
				hr = pP->QueryPinInfo(&pininfo); 
 
				if(hr == NOERROR) 
				{ 
					if(pininfo.dir == PINDIR_OUTPUT) 
					{ 
						filerInfo.pGraph->Reconnect(pP); 
					} 
 
					if(pininfo.pFilter) 
						pininfo.pFilter->Release(); 
				} 
 
				pP->Release(); 
			} 
		} 
 
		if(pins) 
			pins->Release(); 
	} 
	 
	if(filerInfo.pGraph) 
		filerInfo.pGraph->Release(); 
	filerInfo.pGraph = NULL; 
 
	return (hr == S_OK); 
} 
 
 
////////////////////////////////////////////////////////////// 
// 
// Media Type setup info 
// 
const AMOVIESETUP_MEDIATYPE sudPinTypes = 
{ 
    &MEDIATYPE_Audio,       // Major type 
    &MEDIASUBTYPE_NULL      // Minor type 
}; 
 
////////////////////////////////////////////////////////////// 
// 
// Pin Definition setup info 
// 
const AMOVIESETUP_PIN sudpPins[] = 
{ 
    { L"Input",             // Pins string name 
      FALSE,                // Is it rendered 
      FALSE,                // Is it an output 
      FALSE,                // Are we allowed none 
      FALSE,                // And allowed many 
      &CLSID_NULL,          // Connects to filter 
      NULL,                 // Connects to pin 
      1,                    // Number of types 
      &sudPinTypes          // Pin information 
    }, 
    { L"Output",            // Pins string name 
      FALSE,                // Is it rendered 
      TRUE,                 // Is it an output 
      FALSE,                // Are we allowed none 
      FALSE,                // And allowed many 
      &CLSID_NULL,          // Connects to filter 
      NULL,                 // Connects to pin 
      1,                    // Number of types 
      &sudPinTypes          // Pin information 
    } 
}; 
 
////////////////////////////////////////////////////////////// 
// 
// Filter Stup info 
// 
const AMOVIESETUP_FILTER sudHistogram = 
{ 
    &CLSID_AudioResample,         // Filter CLSID 
    L"Audio Resample Filter",       // String name 
    MERIT_DO_NOT_USE,       // Filter merit 
    2,                      // Number of pins 
    sudpPins                // Pin information 
}; 
 
////////////////////////////////////////////////////////////// 
// 
// List of class IDs and creator functions for the class factory. This 
// provides the link between the OLE entry point in the DLL and an object 
// being created. The class factory will call the static CreateInstance 
// 
CFactoryTemplate g_Templates[] = { 
    { L"Audio Resample" 
    , &CLSID_AudioResample 
    , CAudioResample::CreateInstance 
    , NULL 
    , &sudHistogram } 
  , 
    { L"Audio Resample" 
    , &CLSID_AudioResamplePropertyPage 
    , CAudioResampleProperties::CreateInstance } 
}; 
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); 
 
////////////////////////////////////////////////////////////// 
// 
// Constructor 
// 
CAudioResample::CAudioResample(TCHAR *tszName, 
                   LPUNKNOWN punk, 
                   HRESULT *phr) : 
    CTransformFilter(tszName, punk, CLSID_AudioResample) 
{ 
		 
	// Initialise the data 
 
	seen = false; 
 
	percentChange = 100.0; 
	percent = 0.0; 
	carryOverAdd = 0.0; 
	actualPercent = 0.0; 
 
	totalRaw = 0; 
	totalAdded = 0; 
 
	calculateInterval(); 
 
	m_SamplesPerSec = 0; 
	m_BytesPerSample = 0; 
	m_Channels = 0; 
 
	//MsgBOX(TEXT("Audio Resample Filter Constructor %s"), pathInfo); 
 
} // (Constructor) 
 
CAudioResample::calculateInterval() 
{ 
	if(percentChange == 100.0) 
	{ 
		percent = 0; 
	} 
	else 
	{ 
		percent = (percentChange - 100) / 100; 
	} 
} 
 
////////////////////////////////////////////////////////////// 
// 
// CreateInstance 
// 
// Provide the way for COM to create a object 
// 
CUnknown *CAudioResample::CreateInstance(LPUNKNOWN punk, HRESULT *phr) 
{ 
    CAudioResample *pNewObject = new CAudioResample(NAME("Audio Resample"), punk, phr); 
    if (pNewObject == NULL) 
	{ 
        *phr = E_OUTOFMEMORY; 
    } 
    return pNewObject; 
 
} // CreateInstance 
 
 
////////////////////////////////////////////////////////////// 
// 
// NonDelegatingQueryInterface 
// 
// Reveals IAudioResample 
// 
STDMETHODIMP CAudioResample::NonDelegatingQueryInterface(REFIID riid, void **ppv) 
{ 
    CheckPointer(ppv,E_POINTER); 
 
    if (riid == IID_IAudioResample) 
	{ 
        return GetInterface((IAudioResample *) this, ppv); 
    } 
	else if (riid == IID_IFilterProperties) 
	{ 
		return GetInterface((IFilterProperties *) this, ppv); 
	} 
	else if (riid == IID_ISpecifyPropertyPages) 
	{ 
        return GetInterface((ISpecifyPropertyPages *) this, ppv); 
    } 
	else 
	{ 
        return CTransformFilter::NonDelegatingQueryInterface(riid, ppv); 
    } 
 
} // NonDelegatingQueryInterface 
 
////////////////////////////////////////////////////////////// 
// 
// GetPages 
// 
// Returns the clsid's of the property pages we support 
// 
STDMETHODIMP CAudioResample::GetPages(CAUUID *pPages) 
{ 
    pPages->cElems = 1; 
    pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID)); 
    if (pPages->pElems == NULL) 
	{ 
        return E_OUTOFMEMORY; 
    } 
 
    *(pPages->pElems) = CLSID_AudioResamplePropertyPage; 
 
    return NOERROR; 
 
} // GetPages 
 
////////////////////////////////////////////////////////////// 
// 
// DllRegisterServer 
// 
// Handles sample registry and unregistry 
// 
STDAPI DllRegisterServer() 
{ 
    return AMovieDllRegisterServer2( TRUE ); 
 
} // DllRegisterServer 
////////////////////////////////////////////////////////////// 
// 
// DllUnregisterServer 
// 
STDAPI DllUnregisterServer() 
{ 
    return AMovieDllRegisterServer2( FALSE ); 
 
} // DllUnregisterServer