// Copyright (c) 1999 Microsoft Corporation. All rights reserved. // // Declaration of CParamControlTrack. // #include "dmime.h" #include "ParamTrk.h" #include "..\shared\Validate.h" #include "miscutil.h" #include "limits.h" #include "math.h" STDMETHODIMP CParamControlTrack::QueryInterface(const IID &iid, void **ppv) { V_INAME(CParamControlTrack::QueryInterface); V_PTRPTR_WRITE(ppv); V_REFGUID(iid); if (iid == IID_IUnknown || iid == IID_IDirectMusicTrack || iid == IID_IDirectMusicTrack8) *ppv = static_cast(this); else if (iid == IID_IPersistStream) *ppv = static_cast(this); else if (iid == IID_IPersist) *ppv = static_cast(this); else if (iid == IID_CParamControlTrack) { *ppv = static_cast(this); } else { *ppv = NULL; Trace(4,"Warning: Request to query unknown interface on Track\n"); return E_NOINTERFACE; } reinterpret_cast(this)->AddRef(); return S_OK; } STDMETHODIMP CParamControlTrack::Init(IDirectMusicSegment *pSegment) { V_INAME(CBasicTrack::Init); V_INTERFACE(pSegment); return S_OK; } STDMETHODIMP CParamControlTrack::Load(IStream* pIStream) { V_INAME(CPlayingTrack::Load); V_INTERFACE(pIStream); HRESULT hr = S_OK; SmartRef::CritSec CS(&m_CriticalSection); // Increment counter so the next play will update state data with the new list. ++m_dwValidate; // Clear the objects/params/curves in case we're being reloaded. m_listObjects.CleanUp(); m_cObjects = 0; m_cParams = 0; SmartRef::RiffIter ri(pIStream); if (!ri) return ri.hr(); // find hr = ri.FindRequired(SmartRef::RiffIter::List, DMUS_FOURCC_PARAMCONTROLTRACK_TRACK_LIST, DMUS_E_INVALID_PARAMCONTROLTRACK); if (FAILED(hr)) { #ifdef DBG if (hr == DMUS_E_INVALID_PARAMCONTROLTRACK) { Trace(1, "Error: Unable to load parameter control track: List 'prmt' not found.\n"); } #endif return hr; } SmartRef::RiffIter riTrack = ri.Descend(); // for each while (riTrack && riTrack.Find(SmartRef::RiffIter::List, DMUS_FOURCC_PARAMCONTROLTRACK_OBJECT_LIST)) { hr = this->LoadObject(riTrack.Descend()); if (FAILED(hr)) return hr; ++riTrack; } hr = riTrack.hr(); return hr; } STDMETHODIMP CParamControlTrack::InitPlay( IDirectMusicSegmentState *pSegmentState, IDirectMusicPerformance *pPerformance, void **ppStateData, DWORD dwTrackID, DWORD dwFlags) { V_INAME(CParamControlTrack::InitPlay); V_PTRPTR_WRITE(ppStateData); V_INTERFACE(pSegmentState); V_INTERFACE(pPerformance); SmartRef::CritSec CS(&m_CriticalSection); // Set up state data StateData *pStateData = new StateData; if (!pStateData) return E_OUTOFMEMORY; HRESULT hr = InitStateData(pStateData, pSegmentState); if (FAILED(hr)) { delete pStateData; } else { pStateData->dwValidate = m_dwValidate; *ppStateData = pStateData; } return hr; } STDMETHODIMP CParamControlTrack::EndPlay(void *pStateData) { V_INAME(CParamControlTrack::EndPlay); V_BUFPTR_WRITE(pStateData, sizeof(StateData)); SmartRef::CritSec CS(&m_CriticalSection); StateData *pSD = static_cast(pStateData); if (!pSD->fFlushInAbort) { // For each object, flush all curves on each parameter up to the start time of the last one we sent. // (This allows the DMO being controlled to free up memory associated with any previous curves // while still keeping the last one around so that the next thing played picks up that parameter // value how it was left.) // Then release the object's params interface. int iObj = 0; for (TListItem *pObject = m_listObjects.GetHead(); pObject && iObj < m_cObjects; pObject = pObject->GetNext(), ++iObj) { IMediaParams *pIMediaParams = pSD->prgpIMediaParams[iObj]; if (pIMediaParams) { ObjectInfo &obj = pObject->GetItemValue(); int iParam = 0; for (TListItem *pParam = obj.listParams.GetHead(); pParam && iParam < m_cParams; pParam = pParam->GetNext(), ++iParam) { ParamInfo ¶m = pParam->GetItemValue(); ParamState ¶mstate = pSD->prgParam[iParam]; if (paramstate.fLast) { HRESULT hrFlush = pIMediaParams->FlushEnvelope(param.header.dwIndex, _I64_MIN, paramstate.rtStartPointOfLastCurve); if (FAILED(hrFlush)) { assert(false); TraceI(1, "Unable to flush envelope information from an audio path object in parameter control track, HRESULT 0x%08x.\n", hrFlush); } } } } SafeRelease(pIMediaParams); } } delete[] pSD->prgpIMediaParams; delete[] pSD->prgParam; delete pSD; return S_OK; } HRESULT CParamControlTrack::OnSegmentEnd(REFERENCE_TIME rtEnd, void *pStateData) { SmartRef::CritSec CS(&m_CriticalSection); StateData *pSD = static_cast(pStateData); // For each object, flush all curves on each parameter up to the start time of the last one we sent // (if that started before segment end) or flush everything up to the last one to start before // segment end, and flush everything after segment end (if the start time was after segment end). // (This allows the DMO being controlled to free up memory associated with any previous curves // while still keeping the last one around so that the next thing played picks up that parameter // value how it was left.) // Then release the object's params interface. int iObj = 0; for (TListItem *pObject = m_listObjects.GetHead(); pObject && iObj < m_cObjects; pObject = pObject->GetNext(), ++iObj) { IMediaParams *pIMediaParams = pSD->prgpIMediaParams[iObj]; if (pIMediaParams) { ObjectInfo &obj = pObject->GetItemValue(); int iParam = 0; for (TListItem *pParam = obj.listParams.GetHead(); pParam && iParam < m_cParams; pParam = pParam->GetNext(), ++iParam) { ParamInfo ¶m = pParam->GetItemValue(); ParamState ¶mstate = pSD->prgParam[iParam]; if (paramstate.fLast) { HRESULT hrFlush = S_OK; if (paramstate.rtStartPointOfLastCurve < rtEnd) { hrFlush = pIMediaParams->FlushEnvelope(param.header.dwIndex, _I64_MIN, paramstate.rtStartPointOfLastCurve); } else { // first, look for the largest start time less than rtEnd and // flush up to there. The loop assumes the list is ordered largest to smallest. TListItem* pStartTime = paramstate.listStartTimes.GetHead(); for (; pStartTime; pStartTime = pStartTime->GetNext()) { if (pStartTime->GetItemValue() < rtEnd) { hrFlush = pIMediaParams->FlushEnvelope(param.header.dwIndex, _I64_MIN, pStartTime->GetItemValue()); break; } } // Then, flush from rtEnd on. if (SUCCEEDED(hrFlush)) { hrFlush = pIMediaParams->FlushEnvelope(param.header.dwIndex, rtEnd, _I64_MAX); } } if (FAILED(hrFlush)) { assert(false); TraceI(1, "Unable to flush envelope information from an audio path object in parameter control track, HRESULT 0x%08x.\n", hrFlush); } } } } SafeRelease(pIMediaParams); pSD->prgpIMediaParams[iObj] = NULL; } pSD->fFlushInAbort = true; return S_OK; } STDMETHODIMP CParamControlTrack::Clone(MUSIC_TIME mtStart, MUSIC_TIME mtEnd, IDirectMusicTrack** ppTrack) { // §§ Test more thoroughly when we have multiple working params/objects. V_INAME(CParamControlTrack::Clone); V_PTRPTR_WRITE(ppTrack); SmartRef::CritSec CS(&m_CriticalSection); HRESULT hr = S_OK; SmartRef::ComPtr scomTrack = new CParamControlTrack(&hr); if (FAILED(hr)) return hr; if (!scomTrack) return E_OUTOFMEMORY; scomTrack->AddRef(); // Copy each object for (TListItem *pObject = m_listObjects.GetHead(); pObject; pObject = pObject->GetNext()) { ObjectInfo &obj = pObject->GetItemValue(); TListItem *pNewObject = new TListItem; if (!pNewObject) return E_OUTOFMEMORY; ObjectInfo &newobj = pNewObject->GetItemValue(); newobj.header = obj.header; // Copy each parameter for (TListItem *pParam = obj.listParams.GetHead(); pParam; pParam = pParam->GetNext()) { ParamInfo ¶m = pParam->GetItemValue(); TListItem *pNewParam = new TListItem; if (!pNewParam) return E_OUTOFMEMORY; ParamInfo &newparam = pNewParam->GetItemValue(); newparam.header = param.header; // Copy the curves from mtStart to mtEnd // These should include curves that overlap the start and end, though this // leave some issues we still need to work out (what happens with overlapping curves?) // So, first find the first curve whose end time is at or after mtStart... for (DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pCurveStart = param.curves; (pCurveStart < param.curvesEnd) && (pCurveStart->mtEndTime < mtStart); ++pCurveStart) {} // Then, find the curve whose start time is after mtEnd. for (DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pCurveEnd = pCurveStart; (pCurveEnd < param.curvesEnd) && (pCurveEnd->mtStartTime < mtEnd); ++pCurveEnd) {} int cCurves = (int)(pCurveEnd - pCurveStart); newparam.curves = new DMUS_IO_PARAMCONTROLTRACK_CURVEINFO[cCurves]; if (!newparam.curves) return E_OUTOFMEMORY; memcpy(newparam.curves, pCurveStart, cCurves * sizeof(DMUS_IO_PARAMCONTROLTRACK_CURVEINFO)); newparam.curvesEnd = newparam.curves + cCurves; // Now, scan through the new curve array and adjust the times by subtracting mtStart from everything. for (pCurveStart = newparam.curves; pCurveStart < newparam.curvesEnd; pCurveStart++) { pCurveStart->mtStartTime -= mtStart; pCurveStart->mtEndTime -= mtStart; } newobj.listParams.AddHead(pNewParam); } newobj.listParams.Reverse(); // Technically, the order shouldn't matter. But this ensures that the cloned track will send curves to different parameters in the exact same order just in case. scomTrack->m_listObjects.AddHead(pNewObject); } scomTrack->m_listObjects.Reverse(); // Technically, the order shouldn't matter. But this ensures that the cloned track will send curves to different objects in the exact same order just in case. ++scomTrack->m_dwValidate; scomTrack->m_cObjects = m_cObjects; scomTrack->m_cParams = m_cParams; *ppTrack = scomTrack.disown(); return hr; } HRESULT CParamControlTrack::PlayMusicOrClock( void *pStateData, MUSIC_TIME mtStart, MUSIC_TIME mtEnd, MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, DWORD dwFlags, IDirectMusicPerformance* pPerf, IDirectMusicSegmentState* pSegSt, DWORD dwVirtualID, bool fClockTime) { V_INAME(CParamControlTrack::Play); V_BUFPTR_WRITE( pStateData, sizeof(StateData)); V_INTERFACE(pPerf); V_INTERFACE(pSegSt); if (dwFlags & DMUS_TRACKF_PLAY_OFF) return S_OK; SmartRef::CritSec CS(&m_CriticalSection); StateData *pSD = static_cast(pStateData); if (m_dwValidate != pSD->dwValidate) { HRESULT hr = InitStateData(pSD, pSegSt); if (FAILED(hr)) { return hr; } } // envelope structure we'll fill for sending each envelope segment. MP_ENVELOPE_SEGMENT envCurve; Zero(&envCurve); MP_ENVELOPE_SEGMENT *const penvCurve = &envCurve; bool fMoreCurves = false; // set to true by any parameter that has more curves to play // for each parameter... int iParam = 0; int iObject = 0; for (TListItem *pObject = m_listObjects.GetHead(); pObject && iObject < m_cObjects; pObject = pObject->GetNext(), ++iObject) { ObjectInfo &obj = pObject->GetItemValue(); IMediaParams *pIMediaParams = pSD->prgpIMediaParams[iObject]; bool fObjClockTime = !!(obj.header.guidTimeFormat == GUID_TIME_REFERENCE); if (!fObjClockTime && obj.header.guidTimeFormat != GUID_TIME_MUSIC) { // track can only handle music and clock time assert(false); // Only log this once at warning level one. Rest go to warning level three to avoid tons of identical trace messages during playback). TraceI( obj.fAlreadyTracedPlaybackError ? 3 : 1, "Parameter control track unable to control object -- unknown time format (must be GUID_TIME_MUSIC or GUID_TIME_REFERENCE).\n"); obj.fAlreadyTracedPlaybackError = true; continue; } for (TListItem *pParam = obj.listParams.GetHead(); pParam && iParam < m_cParams; pParam = pParam->GetNext(), ++iParam) { ParamInfo ¶m = pParam->GetItemValue(); ParamState ¶mstate = pSD->prgParam[iParam]; DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *&pCurrentCurve = paramstate.pCurrentCurve; // We're going to seek through the event list to find the proper next control curve for each parameter if // the track's data has been reloaded or if playback has made a jump to a different position in the track. if (m_dwValidate != pSD->dwValidate || dwFlags & (DMUS_TRACKF_SEEK | DMUS_TRACKF_LOOP | DMUS_TRACKF_FLUSH | DMUS_TRACKF_START)) { assert(m_dwValidate != pSD->dwValidate || dwFlags & DMUS_TRACKF_SEEK); // by contract SEEK should be set whenever the other dwFlags are // find first curve that begins at or after the start time we're currently playing for (pCurrentCurve = param.curves; pCurrentCurve < param.curvesEnd && pCurrentCurve->mtStartTime < mtStart; ++pCurrentCurve) {} if (pIMediaParams && pCurrentCurve > param.curves) { // check the previous curve to see if we ended up in the middle of it DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pPrevCurve = pCurrentCurve - 1; // Send a curve chopped off at the start time we're currently playing. // We can't send the whole curve because it would take effect too early. HRESULT hrEnv = this->PlayTruncatedEnvelope(mtStart, pIMediaParams, penvCurve, pPrevCurve, obj, param, paramstate, mtOffset, rtOffset, pPerf, fClockTime, fObjClockTime, dwFlags); if (FAILED(hrEnv)) { // Can't fail from Play. Just assert and print trace information. assert(false); // Only log this once at warning level one. Rest go to warning level three to avoid tons of identical trace messages during playback). TraceI( param.fAlreadyTracedPlaybackError ? 3 : 1, "Unable to send envelope information to an audio path object in parameter control track, HRESULT 0x%08x.\n", hrEnv); param.fAlreadyTracedPlaybackError = true; } } } // Send curves until the next curve is after mtEnd for ( ; pCurrentCurve < param.curvesEnd; ++pCurrentCurve ) { if (pCurrentCurve->mtStartTime < mtStart) // this can happen if DMUS_TRACKF_PLAY_OFF was set and the seek pointer remains at events from the past continue; if (pCurrentCurve->mtStartTime >= mtEnd) break; // send this curve if (pIMediaParams) { HRESULT hrEnv = this->PlayEnvelope(pIMediaParams, penvCurve, pCurrentCurve, obj, param, paramstate, mtOffset, rtOffset, pPerf, fClockTime, fObjClockTime); if (FAILED(hrEnv)) { // Can't fail from Play. Just assert and print trace information. assert(false); // Only log this once at warning level one. Rest go to warning level three to avoid tons of identical trace messages during playback). TraceI( param.fAlreadyTracedPlaybackError ? 3 : 1, "Unable to send envelope information to an audio path object in parameter control track, HRESULT 0x%08x.\n", hrEnv); param.fAlreadyTracedPlaybackError = true; } } } if (pCurrentCurve < param.curvesEnd) fMoreCurves = true; } assert(!pParam); // we should have gotten all the way through this param list } assert(!pObject && iParam == m_cParams && iObject == m_cObjects); // we should have gotten all the way through the object list and done the expected number of objects and parameters pSD->dwValidate = m_dwValidate; // if we weren't in sync with new track data before, we are now return fMoreCurves ? S_OK : DMUS_S_END; } HRESULT CParamControlTrack::LoadObject(SmartRef::RiffIter ri) { if (!ri) return ri.hr(); HRESULT hr = S_OK; SmartRef::Ptr > spItem = new TListItem; if (!spItem) return E_OUTOFMEMORY; ObjectInfo &ritem = spItem->GetItemValue(); // find hr = ri.FindRequired(SmartRef::RiffIter::Chunk, DMUS_FOURCC_PARAMCONTROLTRACK_OBJECT_CHUNK, DMUS_E_INVALID_PARAMCONTROLTRACK); if (FAILED(hr)) { #ifdef DBG if (hr == DMUS_E_INVALID_PARAMCONTROLTRACK) { Trace(1, "Error: Unable to load parameter control track: Chunk 'proh' not found.\n"); } #endif return hr; } hr = SmartRef::RiffIterReadChunk(ri, &ritem.header); if (FAILED(hr)) return hr; if (!(ritem.header.guidTimeFormat == GUID_TIME_MUSIC || ritem.header.guidTimeFormat == GUID_TIME_REFERENCE)) { Trace(1, "Error: Unable to load parameter control track: guidTimeFormat in chunk 'proh' must be either GUID_TIME_MUSIC or GUID_TIME_REFERENCE.\n"); return DMUS_E_INVALID_PARAMCONTROLTRACK; } // for each while (ri && ri.Find(SmartRef::RiffIter::List, DMUS_FOURCC_PARAMCONTROLTRACK_PARAM_LIST)) { hr = this->LoadParam(ri.Descend(), ritem.listParams); if (FAILED(hr)) return hr; ++ri; } hr = ri.hr(); if (SUCCEEDED(hr)) { m_listObjects.AddHead(spItem.disown()); ++m_cObjects; } return hr; } HRESULT CParamControlTrack::LoadParam(SmartRef::RiffIter ri, TList &listParams) { if (!ri) return ri.hr(); HRESULT hr = S_OK; SmartRef::Ptr > spItem = new TListItem; if (!spItem) return E_OUTOFMEMORY; ParamInfo &ritem = spItem->GetItemValue(); // find hr = ri.FindRequired(SmartRef::RiffIter::Chunk, DMUS_FOURCC_PARAMCONTROLTRACK_PARAM_CHUNK, DMUS_E_INVALID_PARAMCONTROLTRACK); if (FAILED(hr)) { #ifdef DBG if (hr == DMUS_E_INVALID_PARAMCONTROLTRACK) { Trace(1, "Error: Unable to load parameter control track: Chunk 'prph' not found.\n"); } #endif return hr; } hr = SmartRef::RiffIterReadChunk(ri, &ritem.header); if (FAILED(hr)) return hr; // find if (!ri.Find(SmartRef::RiffIter::Chunk, DMUS_FOURCC_PARAMCONTROLTRACK_CURVES_CHUNK)) { // It is OK if we read to the end without finding the chunk--we succeed without finding any curves. // Or it could be a failure because there was a problem reading from the stream. // The RiffIter's hr method reflects this. return ri.hr(); } // read the array of control curves int cRecords; hr = SmartRef::RiffIterReadArrayChunk(ri, &ritem.curves, &cRecords); if (FAILED(hr)) return hr; ritem.curvesEnd = ritem.curves + cRecords; listParams.AddHead(spItem.disown()); ++m_cParams; return hr; } HRESULT CParamControlTrack::TrackToObjectTime( MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, IDirectMusicPerformance* pPerf, bool fTrkClockTime, bool fObjClockTime, MUSIC_TIME mt, REFERENCE_TIME *rt) { HRESULT hr = S_OK; // set the time (reference time variable is used to hold either music or reference time in different contexts) REFERENCE_TIME rtEnv = mt; // add the correct offset and if necessary convert from millisecond time rtEnv = fTrkClockTime ? rtEnv * gc_RefPerMil + rtOffset : rtEnv = rtEnv + mtOffset; if (fTrkClockTime != fObjClockTime) { // need to convert between out track's time format and the audio object's time format if (fObjClockTime) { MUSIC_TIME mtEnv = static_cast(rtEnv); hr = pPerf->MusicToReferenceTime(mtEnv, &rtEnv); if (FAILED(hr)) return hr; } else { MUSIC_TIME mtEnv = 0; hr = pPerf->ReferenceToMusicTime(rtEnv, &mtEnv); rtEnv = mtEnv; if (FAILED(hr)) return hr; } } *rt = rtEnv; return hr; } HRESULT CParamControlTrack::PlayEnvelope( IMediaParams *pIMediaParams, MP_ENVELOPE_SEGMENT *pEnv, DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pPt, const ObjectInfo &obj, const ParamInfo ¶m, ParamState ¶mstate, MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, IDirectMusicPerformance* pPerf, bool fTrkClockTime, bool fObjClockTime) { HRESULT hr = S_OK; // set the curve type and flags pEnv->iCurve = static_cast(pPt->dwCurveType); pEnv->flags = pPt->dwFlags; pEnv->valEnd = pPt->fltEndValue; pEnv->valStart = pPt->fltStartValue; // set the time (used to hold either music or reference time in different contexts) REFERENCE_TIME &rtEnvStart = pEnv->rtStart; hr = this->TrackToObjectTime(mtOffset, rtOffset, pPerf, fTrkClockTime, fObjClockTime, pPt->mtStartTime, &rtEnvStart); if (FAILED(hr)) return hr; REFERENCE_TIME &rtEnvEnd = pEnv->rtEnd; hr = this->TrackToObjectTime(mtOffset, rtOffset, pPerf, fTrkClockTime, fObjClockTime, pPt->mtEndTime, &rtEnvEnd); if (FAILED(hr)) return hr; hr = pIMediaParams->AddEnvelope(param.header.dwIndex, 1, pEnv); if (SUCCEEDED(hr)) { paramstate.rtStartPointOfLastCurve = rtEnvStart; TListItem* pStartTime = new TListItem; if (pStartTime) { pStartTime->GetItemValue() = rtEnvStart; // Adding to the head maintains a largest-to-smallest ordering. paramstate.listStartTimes.AddHead(pStartTime); } paramstate.fLast = true; } return hr; } HRESULT CParamControlTrack::PlayTruncatedEnvelope( MUSIC_TIME mtTruncStart, IMediaParams *pIMediaParams, MP_ENVELOPE_SEGMENT *pEnv, DMUS_IO_PARAMCONTROLTRACK_CURVEINFO *pPt, const ObjectInfo &obj, const ParamInfo ¶m, ParamState ¶mstate, MUSIC_TIME mtOffset, REFERENCE_TIME rtOffset, IDirectMusicPerformance* pPerf, bool fTrkClockTime, bool fObjClockTime, DWORD dwFlags) { // Copy info from the curve DMUS_IO_PARAMCONTROLTRACK_CURVEINFO curveinfo = *pPt; // Cut the start to the designated time curveinfo.mtStartTime = mtTruncStart; bool fSkip = false; if (mtTruncStart >= curveinfo.mtEndTime) { // Curve happened in the past. Send a jump curve right at the current (truncate) time picking up with // that value. // if we're looping and we passed the end of this curve, just skip it. if ( (dwFlags & DMUS_TRACKF_LOOP) ) { fSkip = true; } else { curveinfo.mtEndTime = mtTruncStart; curveinfo.dwCurveType = MP_CURVE_JUMP; } } else if (pPt->dwCurveType != MP_CURVE_JUMP) { // Find the point at that time and pick up with a linear curve from there. // (For the nonlinear curves, there's no way to pick them up part-way along.) curveinfo.dwCurveType = MP_CURVE_LINEAR; MUSIC_TIME mtTimeChange = pPt->mtEndTime - pPt->mtStartTime; MUSIC_TIME mtTimeIntermediate = mtTruncStart - pPt->mtStartTime; float fltScalingX = static_cast(mtTimeIntermediate) / mtTimeChange; // 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 (pPt->dwCurveType) { 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; } // Apply that scaling to the range of the actual points curveinfo.fltStartValue = (pPt->fltEndValue - pPt->fltStartValue) * fltScalingY + pPt->fltStartValue; } if (fSkip) return S_OK; return this->PlayEnvelope(pIMediaParams, pEnv, &curveinfo, obj, param, paramstate, mtOffset, rtOffset, pPerf, fTrkClockTime, fObjClockTime); } HRESULT CParamControlTrack::InitStateData(StateData *pStateData, IDirectMusicSegmentState *pSegmentState) { if (pStateData->prgpIMediaParams) { delete [] pStateData->prgpIMediaParams; pStateData->prgpIMediaParams = NULL; } if (pStateData->prgParam) { delete [] pStateData->prgParam; pStateData->prgParam = NULL; } pStateData->prgpIMediaParams = new IMediaParams *[m_cObjects]; if (!pStateData->prgpIMediaParams) { return E_OUTOFMEMORY; } pStateData->prgParam = new ParamState[m_cParams]; if (!pStateData->prgParam) { delete [] pStateData->prgpIMediaParams; return E_OUTOFMEMORY; } // Get the IMediaParams interface for each object SmartRef::ComPtr scomSegSt8; HRESULT hr = pSegmentState->QueryInterface(IID_IDirectMusicSegmentState8, reinterpret_cast(&scomSegSt8)); if (FAILED(hr)) { delete [] pStateData->prgParam; delete [] pStateData->prgpIMediaParams; return hr; } int iObject = 0; for (TListItem *pObject = m_listObjects.GetHead(); pObject; pObject = pObject->GetNext(), ++iObject) { IMediaParams *pIMediaParams = NULL; ObjectInfo &rinfo = pObject->GetItemValue(); HRESULT hrObject = scomSegSt8->GetObjectInPath( rinfo.header.dwPChannel, rinfo.header.dwStage, rinfo.header.dwBuffer, rinfo.header.guidObject, rinfo.header.dwIndex, IID_IMediaParams, reinterpret_cast(&pIMediaParams)); if (FAILED(hrObject)) { // Can't fail from InitPlay (and this is called from there). // Just print trace information. TraceI(1, "Parameter control track was unable to find audio path object, HRESULT 0x%08x.\n", hrObject); } else { hrObject = pIMediaParams->SetTimeFormat(rinfo.header.guidTimeFormat, rinfo.header.guidTimeFormat == GUID_TIME_MUSIC ? 768 : 0); } if (FAILED(hrObject)) { // Can't fail from InitPlay (and this is called from there). // Just print trace information. Trace(1, "Unable to set time format of object in parameter control track, HRESULT 0x%08x.\n", hrObject); } if (FAILED(hrObject)) { SafeRelease(pIMediaParams); } pStateData->prgpIMediaParams[iObject] = pIMediaParams; } return S_OK; }