/// <summary> /// Make sure the envelope parameters are valid /// </summary> /// <param name="m">Envelope segment to check</param> /// <returns>E_INVALIDARG if parameters are incorrect, else S_OK</returns> public int ValidateEnvelopeSegment(MPEnvelopeSegment m) { int hr; // The start time cannot be MAX_TIME // The end time must be >= start time // The iCurve must be one of the recognized formats // The iCurve must be one of the supported formats // The iCurve can only be one format // The flags must be one of the recognized formats // The flags can only be one value if (m.rtStart == MaxTime || m.rtEnd < m.rtStart || (m.iCurve & ~(MPCaps.InvSquare | MPCaps.Jump | MPCaps.Linear | MPCaps.Sine | MPCaps.Square)) > 0 || ((int)m.iCurve & ~this.validCaps) > 0 || CountBits((int)m.iCurve) > 1 || (m.flags & (~(MPFlags.BeginCurrentVal | MPFlags.BeginNeutralVal | MPFlags.Standard))) > 0 || CountBits((int)m.flags) > 1) { hr = EINVALIDARG; } else { hr = SOk; } return(hr); }
/////////////////////////////////////////////////////////////////////////////// // Construction and Initializing methods // /////////////////////////////////////////////////////////////////////////////// #region CONSTRUCTION /// <summary> /// Initializes a new instance of the MPEnvelopes class. /// </summary> /// <param name="newDefaultValue">An <see cref="MPData"/> with the default value</param> /// <param name="newValidCaps">The valid capabilities</param> /// <param name="newMPType">The <see cref="MPType"/> of the envelope.</param> /// <param name="newMinValue">The <see cref="MPData"/> with the minimal value for the envelope.</param> /// <param name="newMaxValue">The <see cref="MPData"/> with the maximal value for the envelope.</param> public MPEnvelopes( MPData newDefaultValue, MPCaps newValidCaps, MPType newMPType, MPData newMinValue, MPData newMaxValue) { // Store the neutralValue, min/max value range, data type and supported curve types this.defaultValue = newDefaultValue; this.validCaps = (int)newValidCaps; this.dataType = newMPType; this.minValue = newMinValue; this.maxValue = newMaxValue; // Create an array to hold the segments (size = 3 was chosen arbitrarily) this.envelope = new ArrayList(3); // Create one segment that spans all time containing the default values MPEnvelopeSegment e = new MPEnvelopeSegment(); e.flags = MPFlags.BeginNeutralVal; e.iCurve = MPCaps.Jump; e.rtStart = 0; e.rtEnd = MaxTime; e.valStart = this.defaultValue; e.valEnd = this.defaultValue; this.envelope.Add(e); }
private void TestAddEnvelope() { int hr; MPEnvelopeSegment [] pSeg = new MPEnvelopeSegment[2]; MPData mp1 = new MPData(); MPData mp2 = new MPData(); mp1.vFloat = 40; mp2.vFloat = 60; pSeg[0].flags = MPFlags.Standard; pSeg[0].iCurve = MPCaps.Jump; pSeg[0].rtStart = 0; pSeg[0].rtEnd = 1000000; pSeg[0].valStart = mp1; pSeg[0].valEnd = mp2; pSeg[1].flags = MPFlags.Standard; pSeg[1].iCurve = MPCaps.Jump; pSeg[1].rtStart = 1000001; pSeg[1].rtEnd = 2000000; pSeg[1].valStart = mp2; pSeg[1].valEnd = mp1; hr = m_imp.AddEnvelope(0, 2, pSeg); DMOError.ThrowExceptionForHR(hr); }
/// <summary> /// Debug utility to dump an envelope /// </summary> public void DumpEnvelope() { int c = this.envelope.Count; Debug.WriteLine("----------------------------"); for (int x = 0; x < c; x++) { MPEnvelopeSegment oldSegment = (MPEnvelopeSegment)this.envelope[x]; Debug.WriteLine(string.Format("{0}: {1}-{2} {3}-{4}", x, oldSegment.rtStart, oldSegment.rtEnd, oldSegment.valStart.vInt, oldSegment.valEnd.vInt)); } }
/// <summary> /// Updates the given <see cref="MPEnvelopeSegment"/> /// with the new value /// </summary> /// <param name="y">The new value</param> /// <param name="m">Ref. The <see cref="MPEnvelopeSegment"/> /// to be updated.</param> private void UpdateSegment(int y, ref MPEnvelopeSegment m) { if ((m.flags & MPFlags.BeginNeutralVal) > 0) { m.valStart = this.defaultValue; } else if ((m.flags & MPFlags.BeginCurrentVal) > 0) { if (y > 0) { MPEnvelopeSegment oldSegment = (MPEnvelopeSegment)this.envelope[y - 1]; m.valStart = oldSegment.valEnd; } else { m.valStart = this.defaultValue; } } }
/////////////////////////////////////////////////////////////////////////////// // Inherited methods // /////////////////////////////////////////////////////////////////////////////// #region OVERRIDES #endregion //OVERRIDES /////////////////////////////////////////////////////////////////////////////// // Eventhandler // /////////////////////////////////////////////////////////////////////////////// #region EVENTS /////////////////////////////////////////////////////////////////////////////// // Eventhandler for UI, Menu, Buttons, Toolbars etc. // /////////////////////////////////////////////////////////////////////////////// #region WINDOWSEVENTHANDLER #endregion //WINDOWSEVENTHANDLER /////////////////////////////////////////////////////////////////////////////// // Eventhandler for Custom Defined Events // /////////////////////////////////////////////////////////////////////////////// #region CUSTOMEVENTHANDLER #endregion //CUSTOMEVENTHANDLER #endregion //EVENTS /////////////////////////////////////////////////////////////////////////////// // Methods and Eventhandling for Background tasks // /////////////////////////////////////////////////////////////////////////////// #region BACKGROUNDWORKER #endregion //BACKGROUNDWORKER /////////////////////////////////////////////////////////////////////////////// // Methods for doing main class job // /////////////////////////////////////////////////////////////////////////////// #region PRIVATEMETHODS /// <summary> /// Utility function called from AddEnvelope. When adding an envelope segment, /// it could happen that the new segment overlaps multiple existing segments. /// This routine walks starting at the specified segment. It either deletes /// the segment or adjusts it until it reaches the ending time of the new /// segment. /// </summary> /// <param name="z">Starting segment</param> /// <param name="end">Ending segment</param> private void DeleteRest(int z, long end) { // Don't walk past the end of the array while (z < this.envelope.Count) { MPEnvelopeSegment oldSegment = (MPEnvelopeSegment)this.envelope[z]; // Have we found the end time of the current segment? if (oldSegment.rtEnd <= end) { // Nope. Delete this segment. this.envelope.RemoveAt(z); } else { // Yes, this is the end. Split the old segment oldSegment.rtStart = end + 1; this.envelope[z] = oldSegment; break; } } }
/// <summary> /// Calculate the parameter value at a specified time /// <para></para> /// While there are routines written for all the curve types, I'm not enough /// of a math whiz to feel comfortable that I got it right. I stole the code /// from elsewhere and converted it to c#, so there's a chance I messed up. /// </summary> /// <param name="rt">Time at which to calculate</param> /// <returns>MPData value for that time based in the specified Curve</returns> public MPData CalcValueForTime(long rt) { long ir, ic; float p; MPData ret; MPEnvelopeSegment m = this.FindEnvelopeForTime(rt); switch (m.iCurve) { case MPCaps.Jump: // Not quite sure how I want to do this. Consider an envelope // that goes from time 0 to 10, and value 55 to 99. Obviously // at time 0, you return 55. At times 1 thru 9, I assume you // also return 55 (although one could argue they should return // 99). At time 10, you return 99. // If you never have a timestamp that exactly equals 10, you // would never get the new value. Seems odd. Well, that's // how I've written it, anyway. if (rt < m.rtEnd) { ret = m.valStart; } else { ret = m.valEnd; } break; case MPCaps.Linear: ir = m.rtEnd - m.rtStart; ic = rt - m.rtStart; p = ic / ir; ret = new MPData(); if (this.dataType == MPType.FLOAT) { ret.vFloat = (m.valEnd.vFloat - m.valStart.vFloat) * p; } else { ret.vInt = (int)((m.valEnd.vInt - m.valStart.vInt) * p); } break; case MPCaps.InvSquare: ir = m.rtEnd - m.rtStart; ic = rt - m.rtStart; p = ic / ir; p = (float)Math.Sqrt(p); ret = new MPData(); if (this.dataType == MPType.FLOAT) { ret.vFloat = (m.valEnd.vFloat - m.valStart.vFloat) * p; } else { ret.vInt = (int)((m.valEnd.vInt - m.valStart.vInt) * p); } break; case MPCaps.Sine: ir = m.rtEnd - m.rtStart; ic = rt - m.rtStart; p = ic / ir; p = (float)((Math.Sin((p * Math.PI) - (Math.PI / 2)) + 1) / 2); ret = new MPData(); if (this.dataType == MPType.FLOAT) { ret.vFloat = (m.valEnd.vFloat - m.valStart.vFloat) * p; } else { ret.vInt = (int)((m.valEnd.vInt - m.valStart.vInt) * p); } break; case MPCaps.Square: ir = m.rtEnd - m.rtStart; ic = rt - m.rtStart; p = ic / ir; p = p * p; ret = new MPData(); if (this.dataType == MPType.FLOAT) { ret.vFloat = (m.valEnd.vFloat - m.valStart.vFloat) * p; } else { ret.vInt = (int)((m.valEnd.vInt - m.valStart.vInt) * p); } break; default: Debug.Assert(false, "Invalid flag!"); ret = new MPData(); break; } return(ret); }
/////////////////////////////////////////////////////////////////////////////// // Defining Enumerations // /////////////////////////////////////////////////////////////////////////////// #region ENUMS #endregion ENUMS /////////////////////////////////////////////////////////////////////////////// // Defining Properties // /////////////////////////////////////////////////////////////////////////////// #region PROPERTIES #endregion //PROPERTIES /////////////////////////////////////////////////////////////////////////////// // Public methods // /////////////////////////////////////////////////////////////////////////////// #region PUBLICMETHODS /// <summary> /// Add a segment to the envelope. If this segment overlaps other segments, /// the other segments are deleted or shortened, and this segment is inserted /// </summary> /// <param name="newSegment">The segment to add</param> /// <returns>The HRESULT of the method.</returns> public int AddSegment(MPEnvelopeSegment newSegment) { int hr; int y; MPEnvelopeSegment oldSegment; hr = this.ValidateEnvelopeSegment(newSegment); if (hr >= 0) { // Find the first record to modify. There is always // at least one record, and ValidateEnvelopeSegment ensures // that the start time is less than the endtime of the last // record (MAX_TIME) y = 0; do { oldSegment = (MPEnvelopeSegment)this.envelope[y]; if (newSegment.rtStart <= oldSegment.rtEnd) { break; } y++; }while (true); // Process the flags on mNew this.UpdateSegment(y, ref newSegment); // Depending on how the new segment overlaps, adjust the // other segments and add it if (newSegment.rtStart <= oldSegment.rtStart) { if (newSegment.rtEnd >= oldSegment.rtEnd) { // Existing segment is completely replaced this.envelope.RemoveAt(y); this.envelope.Insert(y, newSegment); // Subsequent records may need to be deleted/adjusted if (newSegment.rtEnd > oldSegment.rtEnd) { this.DeleteRest(y + 1, newSegment.rtEnd); } } else { // Existing segment is shortened from the left oldSegment.rtStart = newSegment.rtEnd + 1; this.envelope[y] = oldSegment; this.envelope.Insert(y, newSegment); } } else { if (newSegment.rtEnd >= oldSegment.rtEnd) { // Existing segment is truncated oldSegment.rtEnd = newSegment.rtStart - 1; this.envelope[y] = oldSegment; this.envelope.Insert(y + 1, newSegment); if (newSegment.rtEnd > oldSegment.rtEnd) { this.DeleteRest(y + 2, newSegment.rtEnd); } } else { // Split a segment MPEnvelopeSegment appendSegment = oldSegment; appendSegment.rtStart = newSegment.rtEnd + 1; oldSegment.rtEnd = newSegment.rtStart - 1; this.envelope[y] = oldSegment; this.envelope.Insert(y + 1, newSegment); this.envelope.Insert(y + 2, appendSegment); } } } return(hr); }