void Key()
        {
            float value    = (float)(file.read_double());
            float time     = (float)(file.read_double());
            Shape spantype = (Shape)file.read_int();
            float p1       = (float)(file.read_double());
            float p2       = (float)(file.read_double());
            float p3       = (float)(file.read_double());
            float p4       = (float)(file.read_double());
            float p5       = (float)(file.read_double());
            float p6       = (float)(file.read_double());

            var channel_key = new LWChannelKey(
                value,
                time,
                spantype,
                p1,
                p2,
                p3,
                p4,
                p5,
                p6
                );

            currentEnvelope.insert(channel_key);
        }
        //  Interpolate the value of a BEZ2 curve.
        public static float bez2(LWChannelKey key0, LWChannelKey key1, float time)
        {
            float t0 = 0.0f;
            float t1 = 1.0f;

            float x = key0.Time + ((key0.Shape == Shape.Bezier2D) ? key0.p3 : (key1.Time - key0.Time) / 3.0f);
            float t = bez2_time(key0.Time, x, key1.Time + key1.p1, key1.Time, key0.Time, ref t0, ref t1);
            float y = key0.Value + ((key0.Shape == Shape.Bezier2D) ? key0.p4 : key0.p2 / 3.0f);

            return(bezier(key0.Value, y, key1.p2 + key1.Value, key1.Value, t));
        }
 public void insert(LWChannelKey channel_key)
 {
     keys.Add(channel_key);
     steps++;
 }
        /*  Given a list of keys and a time, returns the interpolated value of the
         *  envelope at that time.
         */
        public float eval(float time)
        {
            if (time == last_time)
            {
                return(last_value);
            }
            else
            {
                last_time = time;
            }

            float t;
            float @in;
            float @out;
            float offset = 0.0f;
            int   noff;

            if (keys.Count == 0)
            {
                last_value = 0;
                return(last_value);
            }

            //  If there's only one key, the value is constant
            if (keys.Count == 1)
            {
                last_value = keys.First().Value;
                return(last_value);
            }

            //  Get the first key
            LWChannelKey skey = keys.First();
            LWChannelKey ekey = keys.Last();
            LWChannelKey next = (keys.Count > 1) ? keys[1] : null;
            LWChannelKey prev = (keys.Count > 1) ? keys[keys.Count - 2] : null;

            //  Use pre-behavior if time is before first key time
            if (time < skey.Time)
            {
                switch (pre_behavior)
                {
                case Behavior.Reset:
                {
                    last_value = 0;
                    return(last_value);
                }

                case Behavior.Constant:
                {
                    last_value = skey.Value;
                    return(last_value);
                }

                case Behavior.Repeat:
                {
                    int dummy;
                    last_time = time = range(time, skey.Time, ekey.Time, out dummy);
                    break;
                }

                case Behavior.Oscillate:
                {
                    last_time = time = range(time, skey.Time, ekey.Time, out noff);
                    if ((noff % 2) != 0)
                    {
                        last_time = time = ekey.Time - skey.Time - time;
                    }
                    break;
                }

                case Behavior.OffsetRepeat:
                {
                    last_time = time = range(time, skey.Time, ekey.Time, out noff);
                    offset    = noff * (ekey.Value - skey.Value);
                    break;
                }

                case Behavior.Linear:
                {
                    @out       = outgoing(null, skey, next) / (next.Time - skey.Time);
                    last_value = @out * (time - skey.Time) + skey.Value;;
                    return(last_value);
                }

                default:
                {
                    last_value = 0;
                    return(last_value);
                }
                }
            }

            //  Use post-behavior if time is after last key time
            else if (time > ekey.Time)
            {
                switch (post_behavior)
                {
                case Behavior.Reset:
                {
                    last_value = 0;
                    return(last_value);
                }

                case Behavior.Constant:
                {
                    last_value = ekey.Value;
                    return(last_value);
                }

                case Behavior.Repeat:
                {
                    int dummy;
                    time = range(time, skey.Time, ekey.Time, out dummy);
                    break;
                }

                case Behavior.Oscillate:
                {
                    last_time = time = range(time, skey.Time, ekey.Time, out noff);
                    if ((noff % 2) != 0)
                    {
                        last_time = time = ekey.Time - skey.Time - time;
                    }
                    break;
                }

                case Behavior.OffsetRepeat:
                {
                    last_time = time = range(time, skey.Time, ekey.Time, out noff);
                    offset    = noff * (ekey.Value - skey.Value);
                    break;
                }

                case Behavior.Linear:
                {
                    @in        = incoming(prev, ekey, null) / (ekey.Time - prev.Time);
                    last_value = @in * (time - ekey.Time) + ekey.Value;
                    return(last_value);
                }

                default:
                {
                    last_value = 0;
                    return(last_value);
                }
                }
            }

            //  Get the endpoints of the interval being evaluated
            //k_it = keys.begin();
            prev = null;
            LWChannelKey key0 = null;
            LWChannelKey key1 = null;

            next = null;
            int i;

            for (i = 0; i < keys.Count; ++i)
            {
                prev = key0;
                key0 = key1;
                key1 = keys[i];
                if (time <= key1.Time)
                {
                    if (key0 == null)
                    {
                        key0 = key1;
                        if (i + 1 < keys.Count)
                        {
                            key1 = keys[i + 1];
                        }
                    }
                    break;
                }
            }
            if (i + 1 < keys.Count)
            {
                next = keys[i + 1];
            }

            /*    debug_msg(
             *      "%9.4f <= %9.4f <= %9.4f",
             *      key0 != NULL ? key0.time : 0,
             *      time,
             *      key1 != NULL ? key1.time : 0
             *  );*/

            //  Check for singularities first
            if (time == key0.Time)
            {
                last_value = key0.Value + offset;
                return(last_value);
            }
            else if (time == key1.Time)
            {
                last_value = key1.Value + offset;
                return(last_value);
            }

            if ((key0 == null) || (key1 == null))
            {
                throw new System.Exception("Channel Interpolation error");
                //return 0;
            }

            //  Get interval length, time in [0, 1]
            t = (time - key0.Time) / (key1.Time - key0.Time);

            //  Interpolate
            switch (key1.Shape)
            {
            case Shape.TCB: goto case Shape.Hermite;

            case Shape.Bezier1D: goto case Shape.Hermite;

            case Shape.Hermite:
            {
                @out = outgoing(prev, key0, key1);
                @in  = incoming(key0, key1, next);
                float h1;
                float h2;
                float h3;
                float h4;
                hermite(t, out h1, out h2, out h3, out h4);
                last_value = h1 * key0.Value + h2 * key1.Value + h3 * @out + h4 * @in + offset;
                return(last_value);
            }

            case Shape.Bezier2D:
            {
                last_value = bez2(key0, key1, time) + offset;
                return(last_value);
            }

            case Shape.Linear:
            {
                last_value = key0.Value + t * (key1.Value - key0.Value) + offset;
                return(last_value);
            }

            case Shape.Stepped:
            {
                last_value = key0.Value + offset;
                return(last_value);
            }

            default:
            {
                last_value = offset;
                return(last_value);
            }
            }
        }
        /*  Return the incoming tangent to the curve at key1.  The value returned
         *  for the BEZ2 case is used when extrapolating a linear post behavior.
         */
        public static float incoming(LWChannelKey key0, LWChannelKey key1, LWChannelKey next)
        {
            float a;
            float b;
            float d;
            float t;
            float @in;

            switch (key1.Shape)
            {
            case Shape.Linear:
            {
                d = key1.Value - key0.Value;
                if (next != null)
                {
                    t   = (key1.Time - key0.Time) / (next.Time - key0.Time);
                    @in = t * (next.Value - key1.Value + d);
                }
                else
                {
                    @in = d;
                }
                break;
            }

            case Shape.TCB:
            {
                a = (1.0f - key1.Tension) * (1.0f - key1.Continuity) * (1.0f + key1.Bias);
                b = (1.0f - key1.Tension) * (1.0f + key1.Continuity) * (1.0f - key1.Bias);
                d = key1.Value - key0.Value;

                if (next != null)
                {
                    t   = (key1.Time - key0.Time) / (next.Time - key0.Time);
                    @in = t * (b * (next.Value - key1.Value) + a * d);
                }
                else
                {
                    @in = a * d;
                }
                break;
            }

            case Shape.Bezier1D: goto case Shape.Hermite;

            case Shape.Hermite:
            {
                @in = key1.p4;
                if (next != null)
                {
                    @in *= (key1.Time - key0.Time) / (next.Time - key0.Time);
                }
                break;
            }

            case Shape.Bezier2D:
            {
                @in = key1.p2 * (key1.Time - key0.Time);
                if (System.Math.Abs(key1.p1) > 1e-5f)
                {
                    @in /= key1.p1;
                }
                else
                {
                    @in *= 1e5f;
                }
                break;
            }

            case Shape.Stepped: goto default;

            default:
            {
                @in = 0.0f;
                break;
            }
            }

            return(@in);
        }
        /*  Return the outgoing tangent to the curve at key0.  The value returned
         *  for the BEZ2 case is used when extrapolating a linear pre behavior and
         *  when interpolating a non-BEZ2 span.
         */
        public float outgoing(LWChannelKey prev, LWChannelKey key0, LWChannelKey key1)
        {
            float a;
            float b;
            float d;
            float t;
            float @out;

            switch (key0.Shape)
            {
            case Shape.TCB:
            {
                a = (1.0f - key0.Tension) * (1.0f + key0.Continuity) * (1.0f + key0.Bias);
                b = (1.0f - key0.Tension) * (1.0f - key0.Continuity) * (1.0f - key0.Bias);
                d = key1.Value - key0.Value;

                if (prev != null)
                {
                    t    = (key1.Time - key0.Time) / (key1.Time - prev.Time);
                    @out = t * (a * (key0.Value - prev.Value) + b * d);
                }
                else
                {
                    @out = b * d;
                }
                break;
            }

            case Shape.Linear:
            {
                d = key1.Value - key0.Value;
                if (prev != null)
                {
                    t    = (key1.Time - key0.Time) / (key1.Time - prev.Time);
                    @out = t * (key0.Value - prev.Value + d);
                }
                else
                {
                    @out = d;
                }
                break;
            }

            case Shape.Bezier1D: goto case Shape.Hermite;

            case Shape.Hermite:
            {
                @out = key0.p5;
                if (prev != null)
                {
                    @out *= (key1.Time - key0.Time) / (key1.Time - prev.Time);
                }
                break;
            }

            case Shape.Bezier2D:
            {
                @out = key0.p4 * (key1.Time - key0.Time);
                if (System.Math.Abs(key0.p3) > 1e-5f)
                {
                    @out /= key0.p3;
                }
                else
                {
                    @out *= 1e5f;
                }
                break;
            }

            case Shape.Stepped: goto default;

            default:
            {
                @out = 0.0f;
                break;
            }
            }

            return(@out);
        }