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