// Copyright (c) 1999 Microsoft Corporation. All rights reserved. // // Declaration of CParams. // #include "dmerror.h" #include "param.h" #include "math.h" #include "validate.h" #include "debug.h" #pragma warning(disable:4296) CCurveList::~CCurveList() { while(this->GetHead()) { delete this->RemoveHead(); } } CParamsManager::CParamsManager() { m_fDirty = FALSE; m_cTimeFormats = 0; m_pguidTimeFormats = NULL; m_guidCurrentTimeFormat = GUID_NULL; m_cParams = 0; m_pCurveLists = NULL; m_pParamInfos = NULL; m_dwActiveBits = 0; InitializeCriticalSection(&m_ParamsCriticalSection); } CParamsManager::~CParamsManager() { delete[] m_pguidTimeFormats; delete[] m_pCurveLists; delete[] m_pParamInfos; DeleteCriticalSection(&m_ParamsCriticalSection); } HRESULT CParamsManager::InitParams(DWORD cTimeFormats, const GUID *pguidTimeFormats, DWORD dwFormatIndex, MP_TIMEDATA mptdTimeData, DWORD cParams, ParamInfo *pParamInfo) { //check that the index is in a valid range if (0 > dwFormatIndex || dwFormatIndex >= cTimeFormats || cParams > sizeof(DWORD) * 8) return E_INVALIDARG; m_pCurveLists = new CCurveList[cParams]; if (!m_pCurveLists) return E_OUTOFMEMORY; // save the time formats m_pguidTimeFormats = new GUID[cTimeFormats]; if (!m_pguidTimeFormats) return E_OUTOFMEMORY; for (DWORD dwIndex = 0; dwIndex < cTimeFormats; dwIndex++) { memcpy(&m_pguidTimeFormats[dwIndex], &pguidTimeFormats[dwIndex], sizeof(*pguidTimeFormats)); } // save the count of formats m_cTimeFormats = cTimeFormats; // save the current time format m_guidCurrentTimeFormat = m_pguidTimeFormats[dwFormatIndex]; // save the TimeData m_mptdCurrentTimeData = mptdTimeData; // save the parameter info m_pParamInfos = new ParamInfo[cParams]; if (!m_pParamInfos) return E_OUTOFMEMORY; for (dwIndex = 0; dwIndex < cParams; dwIndex++) { if (pParamInfo[dwIndex].dwIndex < cParams) { memcpy(&m_pParamInfos[pParamInfo[dwIndex].dwIndex],&pParamInfo[dwIndex],sizeof(ParamInfo)); } } m_cParams = cParams; return S_OK; } HRESULT CParamsManager::GetParamCount(DWORD *pdwParams) { if (pdwParams == NULL) return E_POINTER; *pdwParams = m_cParams; return S_OK; } HRESULT CParamsManager::GetParamInfo(DWORD dwParamIndex,MP_PARAMINFO *pInfo) { if (!pInfo) { return E_POINTER; } if (dwParamIndex < m_cParams) { *pInfo = m_pParamInfos[dwParamIndex].MParamInfo; return S_OK; } else { return E_INVALIDARG; } } HRESULT CParamsManager::GetParamText(DWORD dwParamIndex,WCHAR **ppwchText) { if (!ppwchText) { return E_POINTER; } if (dwParamIndex < m_cParams) { // write string of format: "Label\0Unit\0Enums1\0Enum2\0...EnumN\0\0" ParamInfo &pinfo = m_pParamInfos[dwParamIndex]; int iUnit = wcslen(pinfo.MParamInfo.szLabel) + 1; // begin writing unit text here int iEnums = iUnit + wcslen(pinfo.MParamInfo.szUnitText) + 1; // begin writing enum text here int iEnd = iEnums + wcslen(pinfo.pwchText) + 1; // write the final (second) null terminator here WCHAR *pwsz = static_cast(CoTaskMemAlloc((iEnd + 1) * sizeof(WCHAR))); if (!pwsz) return E_OUTOFMEMORY; // StringCchPrintfW will write into various points of the string, neatly terminating each with a null StringCchPrintfW(pwsz,iEnd+1,L"%ls%lc%ls%lc%s%lc",pinfo.MParamInfo.szLabel,L'\0', pinfo.MParamInfo.szUnitText,L'\0', pinfo.pwchText,L'\0'); // The text field was defined with commas to separate the enum values. // Replace them with NULL characters now. for (WCHAR *pwch = pwsz + iEnums; *pwch; ++pwch) { if (*pwch == L',') *pwch = L'\0'; } pwsz[iEnd] = L'\0'; *ppwchText = pwsz; return S_OK; } else { return E_INVALIDARG; } } HRESULT CParamsManager::GetNumTimeFormats(DWORD *pdwNumTimeFormats) { if (!pdwNumTimeFormats) { return E_POINTER; } *pdwNumTimeFormats = m_cTimeFormats; return S_OK; } HRESULT CParamsManager::GetSupportedTimeFormat(DWORD dwFormatIndex,GUID *pguidTimeFormat) { if (!pguidTimeFormat) { return E_POINTER; } if (dwFormatIndex >= m_cTimeFormats) { return E_INVALIDARG; } *pguidTimeFormat = m_pguidTimeFormats[dwFormatIndex]; return S_OK; } HRESULT CParamsManager::GetCurrentTimeFormat( GUID *pguidTimeFormat,MP_TIMEDATA *pTimeData) { HRESULT hr=S_OK; // Parameter Validation if ((pguidTimeFormat == NULL) || (pTimeData == NULL)) { hr = E_POINTER; Trace(1,"ERROR: pGuidTimeFormat or pTimeData is NULL\n"); } // Return the values if (SUCCEEDED(hr)) { *pguidTimeFormat = m_guidCurrentTimeFormat; *pTimeData = m_mptdCurrentTimeData; } return hr; } HRESULT CParamsManager::CopyParamsFromSource( CParamsManager * pSource) { HRESULT hr = S_OK; DWORD dwIndex; for (dwIndex = 0; dwIndex < m_cParams; dwIndex++) { if (pSource->m_guidCurrentTimeFormat == m_pguidTimeFormats[dwIndex]) { break; } } hr = InitParams(pSource->m_cTimeFormats, pSource->m_pguidTimeFormats, dwIndex, pSource->m_mptdCurrentTimeData, pSource->m_cParams,pSource->m_pParamInfos); if (SUCCEEDED(hr)) { for (dwIndex = 0; dwIndex < m_cParams; dwIndex++) { CCurveItem *pCurve = pSource->m_pCurveLists[dwIndex].GetHead(); for (;pCurve;pCurve = pCurve->GetNext()) { CCurveItem *pNew = new CCurveItem; if (!pNew) { return E_OUTOFMEMORY; } pNew->m_Envelope = pCurve->m_Envelope; m_pCurveLists[dwIndex].AddTail(pNew); } } } return hr; } void CParamsManager ::UpdateActiveParams(REFERENCE_TIME rtTime, UpdateCallback &rThis) { if (!m_dwActiveBits) return; // nothing to recalc DWORD dwBit = 1; for (DWORD dwIndex = 0; dwIndex < m_cParams; dwIndex++, dwBit = dwBit << 1) { if (m_dwActiveBits & dwBit) { float fVal = 0; HRESULT hr = GetParamFloat(dwIndex, rtTime, &fVal); rThis.SetParamUpdate(dwIndex, fVal); if (hr == S_FALSE) m_dwActiveBits &= ~dwBit; // we're beyond the last curve, don't need to recalc next time TraceI(6, "DMO value: time %I64d, param #%d, current value %hf\n", rtTime, dwIndex, fVal); } } } inline float ValRange(float valToClip, float valMin, float valMax) { return valToClip < valMin ? valMin : (valToClip > valMax ? valMax : valToClip); } HRESULT CParamsManager::GetParamFloat(DWORD dwParamIndex,REFERENCE_TIME rtTime,float *pval) { HRESULT hr = S_OK; if (dwParamIndex >= m_cParams) return E_INVALIDARG; EnterCriticalSection(&m_ParamsCriticalSection); CCurveList *pList = &m_pCurveLists[dwParamIndex]; ParamInfo *pInfo = &m_pParamInfos[dwParamIndex]; // if no points, then neutral value CCurveItem *pCurveHead = pList->GetHead(); if (!pCurveHead) { *pval = pInfo->MParamInfo.mpdNeutralValue; LeaveCriticalSection(&m_ParamsCriticalSection); return S_FALSE; } // Find the curve during or before the requested time // If the time is during a curve, we will use that. // If not, we need the end value of the previous curve. // Our list keeps these in backwards order, so we are scanning from the // highest point in time backwards. for (CCurveItem *pCurve = pCurveHead; pCurve && pCurve->m_Envelope.rtStart > rtTime;pCurve = pCurve->GetNext()); // If there is no pCurve, there was no curve prior to or during rtTime. Give up. if (!pCurve) { *pval = pInfo->MParamInfo.mpdNeutralValue; LeaveCriticalSection(&m_ParamsCriticalSection); return S_OK; } // Now, if pCurve ends before the requested time, // return the final value of pCurve, since that will hold until the start of the next curve. if (pCurve->m_Envelope.rtEnd < rtTime) { *pval = pCurve->m_Envelope.valEnd; LeaveCriticalSection(&m_ParamsCriticalSection); if (pCurve == pCurveHead) return S_FALSE; // past last curve else return S_OK; // there are more curves ahead } // If we get this far, the curve must bound rtTime. if (pCurve->m_Envelope.iCurve & MP_CURVE_JUMP) { *pval = pCurve->m_Envelope.valEnd; LeaveCriticalSection(&m_ParamsCriticalSection); return S_OK; } REFERENCE_TIME rtTimeChange = pCurve->m_Envelope.rtEnd - pCurve->m_Envelope.rtStart; REFERENCE_TIME rtTimeIntermediate = rtTime - pCurve->m_Envelope.rtStart; float fltScalingX = static_cast(rtTimeIntermediate) / rtTimeChange; // horizontal distance along curve between 0 and 1 float fltScalingY; // height of curve at that point between 0 and 1 based on curve function switch (pCurve->m_Envelope.iCurve) { case MP_CURVE_SQUARE: fltScalingY = fltScalingX * fltScalingX; break; case MP_CURVE_INVSQUARE: fltScalingY = (float) sqrt(fltScalingX); break; case MP_CURVE_SINE: // §§ Maybe we should have a lookup table here? fltScalingY = (float) (sin(fltScalingX * 3.1415926535 - (3.1415926535/2)) + 1) / 2; break; case MP_CURVE_LINEAR: default: fltScalingY = fltScalingX; } // Find out if we need to pull the start point from the previous curve, // the default neutral value, or the current curve. float fStartVal = pCurve->m_Envelope.valStart; if (pCurve->m_Envelope.flags & MPF_ENVLP_BEGIN_NEUTRALVAL) { fStartVal = pInfo->MParamInfo.mpdNeutralValue; } // Currentval, if it exists, will override neutralval. if (pCurve->m_Envelope.flags & MPF_ENVLP_BEGIN_CURRENTVAL) { // Take advantage of the fact that these are inserted in backwards order. // Scan for the previous curve that ends before this time. CCurveItem *pPrevious = pCurve->GetNext(); for (;pPrevious && pPrevious->m_Envelope.rtEnd > rtTime;pPrevious = pPrevious->GetNext()); if (pPrevious) { fStartVal = pPrevious->m_Envelope.valEnd; } } // Apply that scaling to the range of the actual points *pval = (pCurve->m_Envelope.valEnd - fStartVal) * fltScalingY + fStartVal; LeaveCriticalSection(&m_ParamsCriticalSection); return hr; } HRESULT CParamsManager::GetParamInt(DWORD dwParamIndex,REFERENCE_TIME rt,long *pval) { HRESULT hr = E_POINTER; if (pval) { float fVal; hr = GetParamFloat(dwParamIndex,rt,&fVal); if (SUCCEEDED(hr)) { *pval = (long) (fVal + 1/2); // Round. } } else { Trace(1,"ERROR: pval is NULL\n"); } return hr; } ////////////////////////////////////////////////////////////////////// // IMediaParams HRESULT CParamsManager::GetParam(DWORD dwParamIndex,MP_DATA *pValue) { V_INAME(CParams::GetParam); V_PTR_WRITE(pValue, MP_DATA); if (dwParamIndex >= m_cParams) return E_INVALIDARG; EnterCriticalSection(&m_ParamsCriticalSection); CCurveList *pList = &m_pCurveLists[dwParamIndex]; ParamInfo *pInfo = &m_pParamInfos[dwParamIndex]; // if no points, then neutral value CCurveItem *pCurve = pList->GetHead(); if (pCurve) { *pValue = pCurve->m_Envelope.valEnd; } else { *pValue = pInfo->MParamInfo.mpdNeutralValue; } LeaveCriticalSection(&m_ParamsCriticalSection); return S_OK; } HRESULT CParamsManager::SetParam(DWORD dwParamIndex,MP_DATA value) { V_INAME(CParams::SetParam); if (dwParamIndex >= m_cParams) return E_INVALIDARG; EnterCriticalSection(&m_ParamsCriticalSection); m_fDirty = TRUE; CCurveList *pList = &m_pCurveLists[dwParamIndex]; ParamInfo *pInfo = &m_pParamInfos[dwParamIndex]; // If we've already got a list, just force the most recent curve item to this value. // Otherwise, create a node and add it. CCurveItem *pCurve = pList->GetHead(); if (!pCurve) { pCurve = new CCurveItem; if (pCurve) { pCurve->m_Envelope.rtStart = 0x8000000000000000; // Max negative. pCurve->m_Envelope.rtEnd = 0x7FFFFFFFFFFFFFFF; // Max positive. pCurve->m_Envelope.flags = 0; pList->AddHead(pCurve); } else { LeaveCriticalSection(&m_ParamsCriticalSection); return E_OUTOFMEMORY; } } pCurve->m_Envelope.valStart = value; pCurve->m_Envelope.valEnd = value; pCurve->m_Envelope.iCurve = MP_CURVE_JUMP; LeaveCriticalSection(&m_ParamsCriticalSection); return S_OK; } HRESULT CParamsManager::AddEnvelope( DWORD dwParamIndex, DWORD cPoints, MP_ENVELOPE_SEGMENT *ppEnvelope) { V_INAME(CParams::AddEnvelope); V_PTR_READ(ppEnvelope, *ppEnvelope); if (dwParamIndex >= m_cParams) return E_INVALIDARG; if (!m_pParamInfos) return DMUS_E_NOT_INIT; HRESULT hr = S_OK; EnterCriticalSection(&m_ParamsCriticalSection); m_fDirty = TRUE; CCurveList *pList = &m_pCurveLists[dwParamIndex]; ParamInfo *pInfo = &m_pParamInfos[dwParamIndex]; DWORD dwCount; for (dwCount = 0; dwCount < cPoints; dwCount++) { CCurveItem *pCurve = new CCurveItem; if (!pCurve) { hr = E_OUTOFMEMORY; Trace(1,"ERROR: Out of memory\n"); break; } pCurve->m_Envelope = ppEnvelope[dwCount]; pCurve->m_Envelope.valEnd = ValRange(pCurve->m_Envelope.valEnd, pInfo->MParamInfo.mpdMinValue, pInfo->MParamInfo.mpdMaxValue); pCurve->m_Envelope.valStart = ValRange(pCurve->m_Envelope.valStart, pInfo->MParamInfo.mpdMinValue, pInfo->MParamInfo.mpdMaxValue); pList->AddHead(pCurve); m_dwActiveBits |= 1 << dwParamIndex; // next call to UpdateActiveParams will ensure the parameter's value is recalculated TraceI(6, "DMO envelope: time %I64d-%I64d, param #%d, value %hf-%hf\n", pCurve->m_Envelope.rtStart, pCurve->m_Envelope.rtEnd, dwParamIndex, pCurve->m_Envelope.valStart, pCurve->m_Envelope.valEnd); } LeaveCriticalSection(&m_ParamsCriticalSection); return hr; } HRESULT CParamsManager::FlushEnvelope( DWORD dwParamIndex, REFERENCE_TIME refTimeStart, REFERENCE_TIME refTimeEnd) { if (dwParamIndex >= m_cParams) return E_INVALIDARG; if (!m_pParamInfos) return DMUS_E_NOT_INIT; if (refTimeStart >= refTimeEnd) return E_INVALIDARG; EnterCriticalSection(&m_ParamsCriticalSection); m_fDirty = TRUE; CCurveList *pList = &m_pCurveLists[dwParamIndex]; ParamInfo *pInfo = &m_pParamInfos[dwParamIndex]; CCurveList TempList; CCurveItem *pCurve; while (pCurve = pList->RemoveHead()) { if ((pCurve->m_Envelope.rtStart >= refTimeStart) && (pCurve->m_Envelope.rtEnd <= refTimeEnd)) { delete pCurve; } else { TempList.AddHead(pCurve); } } while (pCurve = TempList.RemoveHead()) { pList->AddHead(pCurve); } LeaveCriticalSection(&m_ParamsCriticalSection); return S_OK; } HRESULT CParamsManager::SetTimeFormat( GUID guidTimeFormat, MP_TIMEDATA mpTimeData) { for (DWORD dwIndex = 0; dwIndex < m_cTimeFormats; dwIndex++) { if (guidTimeFormat == m_pguidTimeFormats[dwIndex]) { m_guidCurrentTimeFormat = m_pguidTimeFormats[dwIndex]; return S_OK; } } return E_INVALIDARG; }