// Copyright (c) 1999-2001 Microsoft Corporation. All rights reserved. // // Declaration of CParamsManager. // #include "dmerror.h" #include "param.h" #include "math.h" #include "..\shared\validate.h" CParamsManager::CParamsManager() { m_fDirty = FALSE; m_fMusicTime = FALSE; m_cParams = 0; m_pCurveLists = NULL; m_pParamInfos = NULL; InitializeCriticalSection(&m_ParamsCriticalSection); // Note: on pre-Blackcomb OS's, this call can raise an exception; if it // ever pops in stress, we can add an exception handler and retry loop. } CParamsManager::~CParamsManager() { delete[] m_pCurveLists; delete[] m_pParamInfos; DeleteCriticalSection(&m_ParamsCriticalSection); } HRESULT CParamsManager::InitParams(DWORD cParams, ParamInfo *pParamInfo) { m_pCurveLists = new CCurveList[cParams]; if (!m_pCurveLists) return E_OUTOFMEMORY; // save the parameter info m_pParamInfos = new ParamInfo[cParams]; if (!m_pParamInfos) return E_OUTOFMEMORY; for (DWORD 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; // wcscpy will write into various points of the string, neatly terminating each with a null wcscpy(pwsz, pinfo.MParamInfo.szLabel); wcscpy(pwsz + iUnit, pinfo.MParamInfo.szUnitText); wcscpy(pwsz + iEnums, pinfo.pwchText); // 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 = 2; return S_OK; } HRESULT CParamsManager::GetSupportedTimeFormat(DWORD dwFormatIndex, GUID *pguidTimeFormat) { if (!pguidTimeFormat) { return E_POINTER; } if (dwFormatIndex == 0) { *pguidTimeFormat = GUID_TIME_REFERENCE; } else { *pguidTimeFormat = GUID_TIME_MUSIC; } return S_OK; } HRESULT CParamsManager::GetCurrentTimeFormat( GUID *pguidTimeFormat, MP_TIMEDATA *pTimeData) { return E_NOTIMPL; } HRESULT CParamsManager::CopyParamsFromSource( CParamsManager * pSource) { HRESULT hr = S_OK; hr = InitParams(pSource->m_cParams, pSource->m_pParamInfos); if (SUCCEEDED(hr)) { DWORD dwIndex; 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; } 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 *pCurve = pList->GetHead(); if (!pCurve) { *pval = pInfo->MParamInfo.mpdNeutralValue; LeaveCriticalSection(&m_ParamsCriticalSection); return S_OK; } // 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 (;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); return S_OK; } // 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 - pCurve->m_Envelope.valStart) * fltScalingY + pCurve->m_Envelope.valStart; 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. } } 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; 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); } 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) { if (guidTimeFormat == GUID_TIME_REFERENCE) { m_fMusicTime = FALSE; } else if (guidTimeFormat == GUID_TIME_MUSIC) { m_fMusicTime = TRUE; } else { return E_INVALIDARG; } return S_OK; }