1
0

ParamEnvelope.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. // ParamEnvelope.cpp: implementation of the CParamEnvelope class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. #include "AudioPlugIn.h"
  6. #include "ParamEnvelope.h"
  7. #include <math.h>
  8. ////////////////////////////////////////////////////////////////////////////////
  9. // ParamInfo
  10. ////////////////////////////////////////////////////////////////////////////////
  11. float ParamInfo::MapToInternal( float fValue ) const
  12. {
  13. // Convert a user-supplied parameter value to one that is for internal
  14. // use by the plug-in's processing code.
  15. if (MPT_FLOAT == mppi.mpType)
  16. {
  17. // Map floats to the internal range, using a linear mapping
  18. double dDelta = (fValue - mppi.mpdMinValue) / (mppi.mpdMaxValue - mppi.mpdMinValue);
  19. return float( fInternalMin + dDelta * (fInternalMax - fInternalMin) );
  20. }
  21. else if (MPT_BOOL == mppi.mpType)
  22. {
  23. // Map booleans to 0.0 or 1.0
  24. return float( (fValue < 0.5) ? MPBOOL_FALSE : MPBOOL_TRUE );
  25. }
  26. else // (MPT_ENUM == mppi.mpType || MPT_INT == mppi.mpType)
  27. {
  28. // Map integers to the internal range, using a linear mapping, and then
  29. // round to the nearest value.
  30. double dDelta = (fValue - mppi.mpdMinValue) / (mppi.mpdMaxValue - mppi.mpdMinValue);
  31. double dMapped = fInternalMin + dDelta * (fInternalMax - fInternalMin);
  32. return static_cast<float>( floor( dMapped + 0.5 ) );
  33. }
  34. }
  35. ////////////////////////////////////////////////////////////////////////////////
  36. float ParamInfo::MapToExternal( float fValue ) const
  37. {
  38. // Convert an internal processing value to value in the user's input range
  39. if (MPT_FLOAT == mppi.mpType)
  40. {
  41. // Map floats to the external range, using a linear mapping
  42. double dDelta = (fValue - fInternalMin) / (fInternalMax - fInternalMin);
  43. return float( mppi.mpdMinValue + dDelta * (mppi.mpdMaxValue - mppi.mpdMinValue) );
  44. }
  45. else if (MPT_BOOL == mppi.mpType)
  46. {
  47. // Booleans are already in a suitable range; no mapping required.
  48. return fValue;
  49. }
  50. else // (MPT_ENUM == mppi.mpType || MPT_INT == mppi.mpType)
  51. {
  52. // Map integers to the external range, using a linear mapping
  53. double dDelta = (fValue - fInternalMin) / (fInternalMax - fInternalMin);
  54. return float( mppi.mpdMinValue + dDelta * (mppi.mpdMaxValue - mppi.mpdMinValue) );
  55. }
  56. }
  57. ////////////////////////////////////////////////////////////////////////////////
  58. // CParamEnvelope
  59. ////////////////////////////////////////////////////////////////////////////////
  60. //------------------------------------------------------------------------------
  61. // Ctors
  62. CParamEnvelope::CParamEnvelope() :
  63. m_bOverride( FALSE ),
  64. m_bCaptured( FALSE ),
  65. m_fOverrideValue( 0 ),
  66. m_fEnvelopeValue( 0 ),
  67. m_dEnvelopeDelta1( 0 ),
  68. m_dEnvelopeDelta2( 0 ),
  69. m_bValidDeltas( TRUE ),
  70. m_rtRendered( 0 )
  71. {
  72. }
  73. CParamEnvelope::~CParamEnvelope()
  74. {
  75. }
  76. //----------------------------------------------------------------------------
  77. // Phase 2 of construction
  78. void CParamEnvelope::SetParamInfo( const ParamInfo& info )
  79. {
  80. m_info = info;
  81. m_fEnvelopeValue = m_info.MapToInternal( m_info.mppi.mpdNeutralValue );
  82. cleanup();
  83. }
  84. //----------------------------------------------------------------------------
  85. // Find the index for data on or after rt
  86. int CParamEnvelope::IndexForRefTime( REFERENCE_TIME rt ) const
  87. {
  88. CAutoLock lock( const_cast<CParamEnvelope*>( this ) );
  89. int const nLength = GetCount();
  90. // Fail gracefully if the list is empty
  91. if (0 == nLength)
  92. return -1;
  93. // Special case for position after the last segment
  94. if (rt >= m_envSegs[ nLength - 1 ].rtEnd)
  95. return nLength - 1;
  96. int ixMin = 0;
  97. int ixMax = nLength;
  98. int ix = ( ixMin + ixMax ) / 2;
  99. // Binary search for the shape which starts on or before the given time
  100. do
  101. {
  102. REFERENCE_TIME rtShape = m_envSegs[ ix ].rtStart;
  103. // We've made an exact match
  104. if (rtShape == rt)
  105. return ix;
  106. // No match was found, so update search indices
  107. else if (rt < rtShape)
  108. ixMax = ix;
  109. else if (rt > rtShape)
  110. ixMin = ix;
  111. ix = (ixMin + ixMax) / 2;
  112. }
  113. while (ix != ixMin);
  114. // The search may have left us at a shape after the desired time, so
  115. // scan back if necessary
  116. while (ix >= 0 && m_envSegs[ ix ].rtStart > rt)
  117. --ix;
  118. return ix;
  119. }
  120. //------------------------------------------------------------------------------
  121. // Set the current position, updating current envelope value and deltas. This
  122. // method is called repeatedly by the streaming code, to update parameter values
  123. // as they evolve along the duration of the envelope.
  124. HRESULT CParamEnvelope::UpdateValuesForRefTime( REFERENCE_TIME rt, long lSampleRate )
  125. {
  126. CAutoLock lock( this );
  127. int const nLength = GetCount();
  128. if (0 == nLength)
  129. return S_OK; // nothing to do
  130. int const ix = IndexForRefTime( rt );
  131. const MP_ENVELOPE_SEGMENT* pmpseg = (ix < 0 || ix >= nLength) ? NULL : &m_envSegs[ ix ];
  132. // Assume deltas are valid. We'll make them invalid if we encounter a SIN curve.
  133. m_bValidDeltas = TRUE;
  134. if (NULL == pmpseg || rt < pmpseg->rtStart || rt > pmpseg->rtEnd)
  135. {
  136. // The seek position is between 2 segments, so do not modify the current envelope
  137. // value. The envelope will either latch the previous value, or continue to obey
  138. // any intervening override value, until we hit the next segment boundary.
  139. if (NULL != pmpseg)
  140. m_fEnvelopeValue = m_info.MapToInternal( pmpseg->valEnd );
  141. m_dEnvelopeDelta1 = m_dEnvelopeDelta2 = 0;
  142. }
  143. else
  144. {
  145. // We're dealing with point directly over a shape. Stop any override value.
  146. stopOverride();
  147. // Compute the time delta between this vector and the next, as value
  148. // between 0..1. We use to interpolate between points.
  149. double dx = double(pmpseg->rtEnd - pmpseg->rtStart) / UNITS;
  150. double y0 = m_info.MapToInternal( pmpseg->valStart );
  151. double y1 = m_info.MapToInternal( pmpseg->valEnd );
  152. double dy = y1 - y0;
  153. double x = (double(rt - pmpseg->rtStart) / UNITS) / dx;
  154. // Convert dx to units per sample, before computing deltas
  155. dx = dx * lSampleRate;
  156. // Interpolate between times
  157. if (MP_CURVE_JUMP == pmpseg->iCurve)
  158. {
  159. m_dEnvelopeDelta2 = 0;
  160. m_dEnvelopeDelta1 = 0;
  161. m_fEnvelopeValue = static_cast<float>( y0 );
  162. }
  163. else if (MP_CURVE_LINEAR == pmpseg->iCurve)
  164. {
  165. m_dEnvelopeDelta2 = 0;
  166. m_dEnvelopeDelta1 = dy / dx;
  167. m_fEnvelopeValue = static_cast<float>( y0 + dy * x );
  168. }
  169. else if (MP_CURVE_SQUARE == pmpseg->iCurve || MP_CURVE_INVSQUARE == pmpseg->iCurve)
  170. {
  171. double A;
  172. double B;
  173. if (MP_CURVE_SQUARE == pmpseg->iCurve)
  174. {
  175. A = y0;
  176. B = dy;
  177. }
  178. else
  179. {
  180. x = x - 1;
  181. A = y1;
  182. B = -dy;
  183. }
  184. m_dEnvelopeDelta2 = 2.0 * B / (dx * dx);
  185. m_dEnvelopeDelta1 = (B / dx) * (2.0 * x + (1.0 / dx));
  186. m_fEnvelopeValue = static_cast<float>( A + B * x * x );
  187. }
  188. else if (MP_CURVE_SINE)
  189. {
  190. static const double dPI = 3.14159265358979323846264338327950288419716939937510;
  191. double dTheta = dPI * (x - 0.5);
  192. m_bValidDeltas = FALSE;
  193. m_fEnvelopeValue = float( dy * ( sin( dTheta ) + 0.5 ) );
  194. }
  195. }
  196. // Keep track of the latest time rendered so far
  197. m_rtRendered = max( m_rtRendered, rt );
  198. return S_OK;
  199. }
  200. //------------------------------------------------------------------------------
  201. // If the list is empty, make sure we get an override value.
  202. void CParamEnvelope::cleanup()
  203. {
  204. if (0 == GetCount() && !IsOverrideActive())
  205. {
  206. m_fOverrideValue = m_fEnvelopeValue;
  207. m_bOverride = TRUE;
  208. }
  209. }
  210. //---------------------------------------------------------------------------
  211. // Set the value of our parameter, overriding any segment in effect.
  212. HRESULT CParamEnvelope::SetParam( float fValue )
  213. {
  214. m_bOverride = TRUE;
  215. m_fOverrideValue = m_info.MapToInternal( fValue );
  216. m_fEnvelopeValue = m_fOverrideValue;
  217. m_dEnvelopeDelta1 = m_dEnvelopeDelta2 = 0;
  218. return S_OK;
  219. }
  220. //------------------------------------------------------------------------------
  221. // Get the value of the parameter, either overriden on an a segment
  222. HRESULT CParamEnvelope::GetParam( float* pfValue )
  223. {
  224. if (NULL == pfValue)
  225. return E_POINTER;
  226. *pfValue = m_info.MapToExternal( GetCurrentValue() );
  227. return S_OK;
  228. }
  229. //------------------------------------------------------------------------------
  230. // Add segments to this envelope
  231. static bool compareEnvSeg( const MP_ENVELOPE_SEGMENT& a, const MP_ENVELOPE_SEGMENT& b )
  232. {
  233. return a.rtStart < b.rtStart;
  234. }
  235. static bool operator==( const MP_ENVELOPE_SEGMENT& a, const MP_ENVELOPE_SEGMENT& b )
  236. {
  237. return 0 == memicmp( &a, &b, sizeof(MP_ENVELOPE_SEGMENT) );
  238. }
  239. HRESULT CParamEnvelope::AddEnvelope( DWORD cSegments, MP_ENVELOPE_SEGMENT* pmpes, double dSamplesPerRefTime )
  240. {
  241. CAutoLock lock( this );
  242. // Make room for what we are going to add
  243. m_envSegs.reserve( m_envSegs.size() + cSegments );
  244. // Add each segment, noting which one is earliest in time
  245. REFERENCE_TIME rtMin = _I64_MAX;
  246. for (int ix = 0; ix < cSegments; ix++)
  247. {
  248. // Round reference times to sample boundaries
  249. MP_ENVELOPE_SEGMENT mpes = pmpes[ ix ];
  250. mpes.rtStart = REFERENCE_TIME(mpes.rtStart * dSamplesPerRefTime) / dSamplesPerRefTime + 0.5;
  251. mpes.rtEnd = REFERENCE_TIME(mpes.rtEnd * dSamplesPerRefTime) / dSamplesPerRefTime + 0.5;
  252. m_envSegs.push_back( pmpes[ ix ] );
  253. if (mpes.rtStart < rtMin)
  254. rtMin = mpes.rtStart;
  255. }
  256. // Flush all segments prior to the first newly added one
  257. ix = IndexForRefTime( rtMin );
  258. if (ix > 0 && rtMin < m_rtRendered)
  259. m_envSegs.erase( m_envSegs.begin(), m_envSegs.begin() + ix );
  260. // Sort them
  261. std::sort( m_envSegs.begin(), m_envSegs.end(), compareEnvSeg );
  262. // Remove duplicates
  263. EnvelopeSegs::iterator it = m_envSegs.begin();
  264. while (it != m_envSegs.end())
  265. {
  266. EnvelopeSegs::iterator itBegin = it + 1;
  267. EnvelopeSegs::iterator itEnd = itBegin;
  268. while (itEnd != m_envSegs.end() && *itEnd == *it)
  269. itEnd++;
  270. if (itEnd != itBegin)
  271. it = m_envSegs.erase( itBegin, itEnd );
  272. else
  273. it++;
  274. }
  275. return S_OK;
  276. }
  277. //------------------------------------------------------------------------------
  278. // Flush segments within the specified time range. The rules for flushing are
  279. // described as follows in the documentation for IMediaParams:
  280. //
  281. // If the time span specified by refTimeStart and refTimeEnd overlaps an envelope
  282. // segment, the entire segment is flushed. On the other hand, if it falls on
  283. // the boundary of an envelope segment, the entire segment is retained. Thus:
  284. //
  285. // [] If the start time falls inside an envelope segment, the segment is flushed.
  286. // [] If the end time falls inside an envelope segment, the segment is flushed.
  287. // [] If the start time equals the end time of an envelope segment, the segment is retained.
  288. // [] If the end time equals the start time of an envelope segment, the segment is retained.
  289. HRESULT CParamEnvelope::FlushEnvelope( REFERENCE_TIME rtStart, REFERENCE_TIME rtEnd, double dSamplesPerRefTime )
  290. {
  291. CAutoLock lock( this );
  292. // Round reference times to sample boundaries
  293. if (rtStart != _I64_MIN && rtStart != _I64_MAX)
  294. rtStart = REFERENCE_TIME( REFERENCE_TIME(rtStart / dSamplesPerRefTime) * dSamplesPerRefTime + 0.5 );
  295. if (rtEnd != _I64_MIN && rtEnd != _I64_MAX)
  296. rtEnd = REFERENCE_TIME( REFERENCE_TIME(rtEnd / dSamplesPerRefTime) * dSamplesPerRefTime + 0.5 );
  297. EnvelopeSegs::iterator it = m_envSegs.begin();
  298. while (it != m_envSegs.end())
  299. {
  300. if (!(rtStart >= it->rtEnd || rtEnd <= it->rtStart))
  301. it = m_envSegs.erase( it );
  302. else
  303. it++;
  304. }
  305. // Once envelopes get thrown away, we need to redetermine our max render time
  306. m_rtRendered = 0;
  307. cleanup();
  308. return S_OK;
  309. }
  310. //------------------------------------------------------------------------------
  311. // The parameter pcSegments passes values both ways. The caller needs to pass in the
  312. // size of the segment array. GetEnvelope() then uses pcSegments to return the number
  313. // of segments it has placed in the array.
  314. HRESULT CParamEnvelope::GetEnvelope( DWORD *pcSegments, MP_ENVELOPE_SEGMENT *pmpes )
  315. {
  316. CAutoLock lock( this );
  317. ASSERT( pcSegments );
  318. DWORD ix = 0;
  319. for (EnvelopeSegs::iterator it = m_envSegs.begin();
  320. it != m_envSegs.end() && ix < *pcSegments;
  321. it++, ix++)
  322. {
  323. pmpes[ ix ] = *it;
  324. }
  325. *pcSegments = ix;
  326. return S_OK;
  327. }