예제 #1
0
 /* restart portamento cycle prior to full restart for tie continuation */
 /* only the portamento stuff from FrozenNote is used */
 public static void RestartOscBankStatePortamento(
     OscStateBankRec State,
     ref FrozenNoteRec FrozenNote)
 {
     if (FrozenNote.PortamentoDuration > 0)
     {
         State.PortamentoCounter    = FrozenNote.PortamentoDuration;
         State.TotalPortamentoTicks = FrozenNote.PortamentoDuration;
         State.InitialFrequency     = State.CurrentFrequency; /* save current pitch */
         State.FinalFrequency       = FrozenNote.NominalFrequency;
         State.PortamentoHertz      = ((FrozenNote.OriginalNote.Flags & NoteFlags.ePortamentoUnitsHertzNotHalfsteps) != 0);
     }
     else
     {
         State.PortamentoCounter = 0;
         State.CurrentFrequency  = FrozenNote.NominalFrequency;
     }
 }
        /* build a new note object with all parameters determined.  *StartAdjustOut */
        /* indicates how many ticks before (negative) or after (positive) now that */
        /* the key-down should occur.  this is added to the scanning gap size and envelope */
        /* origins to figure out how to schedule the note */
        public static void FixNoteParameters(
            IncrParamUpdateRec GlobalParamSource,
            NoteNoteObjectRec Note,
            out int StartAdjustOut,
            double EnvelopeTicksPerDurationTick,
            short PitchIndexAdjust,
            ref FrozenNoteRec FrozenNote,
            SynthParamRec SynthParams)
        {
            // must assign all fields

            /* reference to the note that defines this note. */
            FrozenNote.OriginalNote = Note;

            /* frequency determined by pitch index + detuning, in Hertz */
            double NominalFrequency;
            {
                int i = Note._Pitch
                        + GlobalParamSource.TransposeHalfsteps
                        + PitchIndexAdjust;
                if (i < 0)
                {
                    i = 0;
                }
                else if (i > Constants.NUMNOTES - 1)
                {
                    i = Constants.NUMNOTES - 1;
                }
                /* compute frequency from index */
#if DEBUG
                if ((Constants.CENTERNOTE % 12) != 0)
                {
                    // CENTERNOTE multiple of 12
                    Debug.Assert(false);
                    throw new ArgumentException();
                }
#endif
                double d = GlobalParamSource.FrequencyTable[i % 12].nd.Current;
                i = (i / 12) - (Constants.CENTERNOTE / 12);
                d = d * Math.Exp(i * Constants.LOG2) * Constants.MIDDLEC;
                /* apply detuning */
                double e;
                switch (Note.Flags & NoteFlags.eDetuningModeMask)
                {
                default:
                    Debug.Assert(false);
                    throw new ArgumentException();

                case NoteFlags.eDetuningModeDefault:
                    e = (double)Note._Detuning * GlobalParamSource.Detune.nd.Current;
                    if (GlobalParamSource.DetuneHertz)
                    {
                        goto DetuneHertzPoint;
                    }
                    else
                    {
                        goto DetuneHalfStepsPoint;
                    }

                case NoteFlags.eDetuningModeHalfSteps:
                    e = (double)(Note._Detuning) + GlobalParamSource.Detune.nd.Current;
DetuneHalfStepsPoint:
                    NominalFrequency = d * Math.Exp((e / 12) * Constants.LOG2);
                    break;

                case NoteFlags.eDetuningModeHertz:
                    e = (double)Note._Detuning + GlobalParamSource.Detune.nd.Current;
DetuneHertzPoint:
                    NominalFrequency = d + e;
                    break;
                }
            }
            FrozenNote.NominalFrequency = NominalFrequency;

            /* frequency used for doing multisampling, in Hertz */
            if (Note._MultisamplePitchAsIf != -1)
            {
                /* compute frequency from index */
                int i = Note._MultisamplePitchAsIf;
#if DEBUG
                if ((Constants.CENTERNOTE % 12) != 0)
                {
                    Debug.Assert(false);
                    throw new ArgumentException();
                }
#endif
                double d = GlobalParamSource.FrequencyTable[i % 12].nd.Current;
                i = (i / 12) - (Constants.CENTERNOTE / 12);
                d = d * Math.Exp(i * Constants.LOG2) * Constants.MIDDLEC;
                FrozenNote.MultisampleFrequency = d;
            }
            else
            {
                FrozenNote.MultisampleFrequency = NominalFrequency;
            }

            /* acceleration of envelopes */
            FrozenNote.HurryUpFactor = (double)Note._HurryUpFactor * GlobalParamSource.HurryUp.nd.Current;

            /* duration, in envelope ticks */
            int Duration;
            {
                int i;
                switch (Note.Flags & NoteFlags.eDurationMask)
                {
                default:
                    Debug.Assert(false);
                    throw new ArgumentException();

                case NoteFlags.e64thNote:
                    i = DURATIONUPDATECLOCKRESOLUTION / 64;
                    break;

                case NoteFlags.e32ndNote:
                    i = DURATIONUPDATECLOCKRESOLUTION / 32;
                    break;

                case NoteFlags.e16thNote:
                    i = DURATIONUPDATECLOCKRESOLUTION / 16;
                    break;

                case NoteFlags.e8thNote:
                    i = DURATIONUPDATECLOCKRESOLUTION / 8;
                    break;

                case NoteFlags.e4thNote:
                    i = DURATIONUPDATECLOCKRESOLUTION / 4;
                    break;

                case NoteFlags.e2ndNote:
                    i = DURATIONUPDATECLOCKRESOLUTION / 2;
                    break;

                case NoteFlags.eWholeNote:
                    i = DURATIONUPDATECLOCKRESOLUTION;
                    break;

                case NoteFlags.eDoubleNote:
                    i = DURATIONUPDATECLOCKRESOLUTION * 2;
                    break;

                case NoteFlags.eQuadNote:
                    i = DURATIONUPDATECLOCKRESOLUTION * 4;
                    break;
                }
                switch (Note.Flags & NoteFlags.eDivisionMask)
                {
                default:
                    Debug.Assert(false);
                    throw new ArgumentException();

                case NoteFlags.eDiv1Modifier:
                    break;

                case NoteFlags.eDiv3Modifier:
                    i = i / 3;
                    break;

                case NoteFlags.eDiv5Modifier:
                    i = i / 5;
                    break;

                case NoteFlags.eDiv7Modifier:
                    i = i / 7;
                    break;
                }
                if ((Note.Flags & NoteFlags.eDotModifier) != 0)
                {
                    i = (i * 3) / 2;
                }
                double d = i;
                switch (Note.Flags & NoteFlags.eDurationAdjustMask)
                {
                default:
                    Debug.Assert(false);
                    throw new ArgumentException();

                case NoteFlags.eDurationAdjustDefault:
                    if (GlobalParamSource.DurationAdjustAdditive)
                    {
                        goto DurationAdjustAddPoint;
                    }
                    else
                    {
                        goto DurationAdjustMultPoint;
                    }

                case NoteFlags.eDurationAdjustAdditive:
DurationAdjustAddPoint:
                    d = d + (double)Note._DurationAdjust * (DURATIONUPDATECLOCKRESOLUTION / 4);
                    break;

                case NoteFlags.eDurationAdjustMultiplicative:
DurationAdjustMultPoint:
                    d = d * (double)Note._DurationAdjust;
                    break;
                }
                if (GlobalParamSource.DurationAdjustAdditive)
                {
                    d = d + GlobalParamSource.DurationAdjust.nd.Current * (DURATIONUPDATECLOCKRESOLUTION / 4);
                }
                else
                {
                    d = d * GlobalParamSource.DurationAdjust.nd.Current;
                }
                /* this line is what converts from duration update ticks to envelope ticks */
                Duration = (int)(d * EnvelopeTicksPerDurationTick);
            }
            FrozenNote.Duration = Duration;

            /* portamento duration, in envelope ticks */
            FrozenNote.PortamentoDuration = (int)(((double)Note._PortamentoDuration + GlobalParamSource.Portamento.nd.Current)
                                                  * (DURATIONUPDATECLOCKRESOLUTION / 4) * EnvelopeTicksPerDurationTick);

            /* see if portamento occurs before note retrigger */
            FrozenNote.PortamentoBeforeNote = ((Note.Flags & NoteFlags.ePortamentoLeadsNote) != 0);

            /* first release point, in envelope ticks after start of note */
            switch (Note.Flags & NoteFlags.eRelease1OriginMask)
            {
            default:
                Debug.Assert(false);
                throw new ArgumentException();

            case NoteFlags.eRelease1FromStart:
                FrozenNote.ReleasePoint1     = (int)((double)Note._ReleasePoint1 * Duration);
                FrozenNote.Release1FromStart = true;
                break;

            case NoteFlags.eRelease1FromEnd:
                FrozenNote.ReleasePoint1     = (int)((1 - (double)Note._ReleasePoint1) * Duration);
                FrozenNote.Release1FromStart = false;
                break;

            case NoteFlags.eRelease1FromDefault:
                if (GlobalParamSource.ReleasePoint1FromStart)
                {
                    FrozenNote.ReleasePoint1 = (int)(((double)Note._ReleasePoint1
                                                      + GlobalParamSource.ReleasePoint1.nd.Current)
                                                     * Duration);
                    FrozenNote.Release1FromStart = true;
                }
                else
                {
                    FrozenNote.ReleasePoint1 = (int)((1 - ((double)Note._ReleasePoint1
                                                           + GlobalParamSource.ReleasePoint1.nd.Current))
                                                     * Duration);
                    FrozenNote.Release1FromStart = false;
                }
                break;
            }

            /* second release point, in envelope ticks after start of note */
            switch (Note.Flags & NoteFlags.eRelease2OriginMask)
            {
            default:
                Debug.Assert(false);
                throw new ArgumentException();

            case NoteFlags.eRelease2FromStart:
                FrozenNote.ReleasePoint2     = (int)((double)Note._ReleasePoint2 * Duration);
                FrozenNote.Release2FromStart = true;
                break;

            case NoteFlags.eRelease2FromEnd:
                FrozenNote.ReleasePoint2     = (int)((1 - (double)Note._ReleasePoint2) * Duration);
                FrozenNote.Release2FromStart = false;
                break;

            case NoteFlags.eRelease2FromDefault:
                if (GlobalParamSource.ReleasePoint2FromStart)
                {
                    FrozenNote.ReleasePoint2 = (int)(((double)Note._ReleasePoint2
                                                      + GlobalParamSource.ReleasePoint2.nd.Current)
                                                     * Duration);
                    FrozenNote.Release2FromStart = true;
                }
                else
                {
                    FrozenNote.ReleasePoint2 = (int)((1 - ((double)Note._ReleasePoint2
                                                           + GlobalParamSource.ReleasePoint2.nd.Current))
                                                     * Duration);
                    FrozenNote.Release2FromStart = false;
                }
                break;
            }

            /* third release point, in envelope ticks after start of note */
            if ((Note.Flags & NoteFlags.eRelease3FromStartNotEnd) != 0)
            {
                FrozenNote.ReleasePoint3     = 0;
                FrozenNote.Release3FromStart = true;
            }
            else
            {
                FrozenNote.ReleasePoint3     = Duration;
                FrozenNote.Release3FromStart = false;
            }

            /* overall loudness adjustment for envelopes, including global volume scaling */
            FrozenNote.LoudnessAdjust = (double)Note._OverallLoudnessAdjustment * GlobalParamSource.Volume.nd.Current;

            /* stereo positioning for note */
            {
                double d = (double)Note._StereoPositionAdjustment + GlobalParamSource.StereoPosition.nd.Current;
                if (d < -1)
                {
                    d = -1;
                }
                else if (d > 1)
                {
                    d = 1;
                }
                FrozenNote.StereoPosition = d;
            }

            /* accent values for controlling envelopes */
            InitializeAccent(
                ref FrozenNote.Accents,
                (double)Note._Accent1 + GlobalParamSource.Accent1.nd.Current,
                (double)Note._Accent2 + GlobalParamSource.Accent2.nd.Current,
                (double)Note._Accent3 + GlobalParamSource.Accent3.nd.Current,
                (double)Note._Accent4 + GlobalParamSource.Accent4.nd.Current,
                (double)Note._Accent5 + GlobalParamSource.Accent5.nd.Current,
                (double)Note._Accent6 + GlobalParamSource.Accent6.nd.Current,
                (double)Note._Accent7 + GlobalParamSource.Accent7.nd.Current,
                (double)Note._Accent8 + GlobalParamSource.Accent8.nd.Current);

            /* pitch displacement maximum depth, in tonal Hertz */
            FrozenNote.PitchDisplacementDepthLimit = (double)Note._PitchDisplacementDepthAdjustment
                                                     * GlobalParamSource.PitchDisplacementDepthLimit.nd.Current;

            /* pitch displacement maximum rate, in LFO Hertz */
            FrozenNote.PitchDisplacementRateLimit = (double)Note._PitchDisplacementRateAdjustment
                                                    * GlobalParamSource.PitchDisplacementRateLimit.nd.Current;

            /* pitch displacement start point, in envelope clocks after start of note */
            switch (Note.Flags & NoteFlags.ePitchDisplacementStartOriginMask)
            {
            default:
                Debug.Assert(false);
                throw new ArgumentException();

            case NoteFlags.ePitchDisplacementStartFromStart:
                FrozenNote.PitchDisplacementStartPoint = (int)(Duration
                                                               * (double)Note._PitchDisplacementStartPoint);
                break;

            case NoteFlags.ePitchDisplacementStartFromEnd:
                FrozenNote.PitchDisplacementStartPoint = (int)(Duration
                                                               * (1 - (double)Note._PitchDisplacementStartPoint));
                break;

            case NoteFlags.ePitchDisplacementStartFromDefault:
                if (GlobalParamSource.PitchDisplacementStartPointFromStart)
                {
                    FrozenNote.PitchDisplacementStartPoint = (int)(Duration
                                                                   * ((double)Note._PitchDisplacementStartPoint
                                                                      + GlobalParamSource.PitchDisplacementStartPoint.nd.Current));
                }
                else
                {
                    FrozenNote.PitchDisplacementStartPoint = (int)(Duration
                                                                   * (1 - ((double)Note._PitchDisplacementStartPoint
                                                                           + GlobalParamSource.PitchDisplacementStartPoint.nd.Current)));
                }
                break;
            }

            StartAdjustOut = (int)(((double)Note._EarlyLateAdjust
                                    + GlobalParamSource.EarlyLateAdjust.nd.Current) * Duration);
        }
예제 #3
0
        /* this is used for resetting a note for a tie */
        /* the FrozenNote object is NOT disposed */
        public static void ResetOscBankState(
            OscStateBankRec State,
            ref FrozenNoteRec FrozenNote,
            SynthParamRec SynthParams)
        {
            bool RetriggerEnvelopes = ((FrozenNote.OriginalNote.Flags & NoteFlags.eRetriggerEnvelopesOnTieFlag) != 0);

            /* go through the oscillators and retrigger them */
            OscStateRec OneState = State.OscillatorList;

            while (OneState != null)
            {
                OneState.StateReference.Restart(
                    ref FrozenNote.Accents,
                    FrozenNote.LoudnessAdjust * State.BankTemplate.InstrOverallLoudness,
                    FrozenNote.HurryUpFactor,
                    RetriggerEnvelopes,
                    FrozenNote.StereoPosition,
                    FrozenNote.NominalFrequency,
                    FrozenNote.PitchDisplacementDepthLimit,
                    FrozenNote.PitchDisplacementRateLimit,
                    SynthParams);
                OneState = OneState.Next;
            }

            LFOGeneratorRetriggerFromOrigin(
                State.PitchLFO,
                ref FrozenNote.Accents,
                FrozenNote.NominalFrequency,
                FrozenNote.HurryUpFactor,
                FrozenNote.PitchDisplacementDepthLimit,
                FrozenNote.PitchDisplacementRateLimit,
                RetriggerEnvelopes,
                SynthParams);

            if (State.CombinedOscEffectGenerator != null)
            {
                OscEffectGeneratorRetriggerFromOrigin(
                    State.CombinedOscEffectGenerator,
                    ref FrozenNote.Accents,
                    FrozenNote.NominalFrequency,
                    FrozenNote.HurryUpFactor,
                    RetriggerEnvelopes,
                    SynthParams);
            }

            /* if this object ties to a note, then this is the note to tie to.  this is */
            /* used for finding existing oscillators for tie continuations. */
            State.TieToNote = FrozenNote.OriginalNote._Tie;

            /* portamento control parameters */
            if (!FrozenNote.PortamentoBeforeNote)
            {
                /* if PortamentoBeforeNote is not set, then we have to restart the portamento */
                /* with the current note, otherwise it has already been restarted earlier */
                RestartOscBankStatePortamento(State, ref FrozenNote);
            }

            /* various counters (in terms of envelope ticks) */
            if (State.TieToNote == null)
            {
                State.Release1Countdown = FrozenNote.ReleasePoint1;
                State.Release2Countdown = FrozenNote.ReleasePoint2;
                State.Release3Countdown = FrozenNote.ReleasePoint3;
            }
            else
            {
                /* for ties, only honor releases from start */
                if (FrozenNote.Release1FromStart)
                {
                    State.Release1Countdown = FrozenNote.ReleasePoint1;
                }
                else
                {
                    State.Release1Countdown = -1;
                }
                if (FrozenNote.Release2FromStart)
                {
                    State.Release2Countdown = FrozenNote.ReleasePoint2;
                }
                else
                {
                    State.Release2Countdown = -1;
                }
                if (FrozenNote.Release3FromStart)
                {
                    State.Release3Countdown = FrozenNote.ReleasePoint3;
                }
                else
                {
                    State.Release3Countdown = -1;
                }
            }
            /* do not reset PitchLFOStartCountdown since we can't give it a proper value */
            /* to do the expected thing, and we'd be interrupting the phase of the LFO */
            /* wave generator */
        }
예제 #4
0
        /* construct a new oscillator bank state object based on the note.  the note is */
        /* assumed to start "now" in terms of the parameters in the ParameterUpdator.  */
        /* the ScanningGapWidth is the number of envelope clock ticks in the current scanning */
        /* gap.  this is used to determine how far later than "now" in terms of the back */
        /* edge of the scanning gap (different from above) the osc bank should start playing. */
        /* *WhenToStartPlayingOut returns the number of envelope ticks after the back edge */
        /* of the scanning gap that the note should be started. */
        /*     <already played>       |    <scanning gap>     |    <not yet analyzed> */
        /*   time ---.    time ---.    time ---.    time ---.    time ---.   time ---. */
        /*                            ^A                      ^B     */
        /* point A is the back edge of the scanning gap.  as this edge moves forward in time, */
        /*   oscillator bank state objects are removed from the queue and playback is commenced */
        /*   for them. */
        /* point B is the front edge of the scanning gap.  as this edge moves forward in time, */
        /*   notes are extracted from the track and state bank objects are created for them. */
        /*   ParameterUpdator always reflects parameters at this point in time. */
        public static SynthErrorCodes NewOscBankState(
            OscBankTemplateRec Template,
            out int WhenToStartPlayingOut,
            NoteNoteObjectRec Note,
            double EnvelopeTicksPerDurationTick,
            short PitchIndexAdjust,
            PlayTrackInfoRec TrackInfo,
            SynthParamRec SynthParams,
            out OscStateBankRec StateOut)
        {
            int ThisPreOriginTime;
            int StartPointAdjust;

            WhenToStartPlayingOut = 0;
            StateOut = null;

            int MaxOscillatorPreOriginTime = 0;

            OscStateBankRec State = New(ref SynthParams.freelists.oscStateBankFreeList);

            // all fields must be assigned: State

            State.PortamentoHertz      = false;
            State.TotalPortamentoTicks = 0;
            State.InitialFrequency     = 0;
            State.FinalFrequency       = 0;

            State.BankTemplate = Template;

            /* freeze the parameters */
            FrozenNoteRec FrozenNote = new FrozenNoteRec();

            FixNoteParameters(
                Template.ParamUpdator,
                Note,
                out StartPointAdjust,
                EnvelopeTicksPerDurationTick,
                PitchIndexAdjust,
                ref FrozenNote,
                SynthParams);


            /* this calculates the differential values for periodic pitch displacements */
            State.PitchLFO = NewLFOGenerator(
                Template.PitchLFOTemplate,
                out ThisPreOriginTime,
                ref FrozenNote.Accents,
                FrozenNote.NominalFrequency,
                FrozenNote.HurryUpFactor,
                FrozenNote.PitchDisplacementDepthLimit,
                FrozenNote.PitchDisplacementRateLimit,
                FrozenNote.MultisampleFrequency,
                _PlayTrackParamGetter,
                TrackInfo,
                SynthParams);
            if (ThisPreOriginTime > MaxOscillatorPreOriginTime)
            {
                MaxOscillatorPreOriginTime = ThisPreOriginTime;
            }

            /* list of oscillators that this oscillator bank is comprised of */
            State.OscillatorList = null;
            for (int i = 0; i < Template.NumOscillatorsInBank; i++)
            {
                OscStateRec OneState = New(ref SynthParams.freelists.oscStateFreeList);

                // all fields must be assigned: OneState

                /* link it in */
                OneState.Next        = State.OscillatorList;
                State.OscillatorList = OneState;

                /* copy over the function vectors */
                OneState.Template = Template.TemplateArray[i];

                /* create the oscillator */
                SynthErrorCodes Result = OneState.Template.TemplateReference.NewState(
                    FrozenNote.MultisampleFrequency,
                    ref FrozenNote.Accents,
                    FrozenNote.LoudnessAdjust * Template.InstrOverallLoudness,
                    FrozenNote.HurryUpFactor,
                    out ThisPreOriginTime,
                    FrozenNote.StereoPosition,
                    FrozenNote.NominalFrequency,
                    FrozenNote.PitchDisplacementDepthLimit,
                    FrozenNote.PitchDisplacementRateLimit,
                    FrozenNote.PitchDisplacementStartPoint,
                    TrackInfo,
                    SynthParams,
                    out OneState.StateReference);
                if (Result != SynthErrorCodes.eSynthDone)
                {
                    return(Result);
                }
                if (ThisPreOriginTime > MaxOscillatorPreOriginTime)
                {
                    MaxOscillatorPreOriginTime = ThisPreOriginTime;
                }
            }

            State.CombinedOscEffectGenerator = null;
            if ((Template.CombinedOscillatorEffects != null) && (GetEffectSpecListLength(Template.CombinedOscillatorEffects) > 0))
            {
                SynthErrorCodes Result = NewOscEffectGenerator(
                    Template.CombinedOscillatorEffects,
                    ref FrozenNote.Accents,
                    FrozenNote.HurryUpFactor,
                    FrozenNote.NominalFrequency,
                    FrozenNote.MultisampleFrequency,
                    out ThisPreOriginTime,
                    TrackInfo,
                    SynthParams,
                    out State.CombinedOscEffectGenerator);
                if (Result != SynthErrorCodes.eSynthDone)
                {
                    return(Result);
                }
                if (ThisPreOriginTime > MaxOscillatorPreOriginTime)
                {
                    MaxOscillatorPreOriginTime = ThisPreOriginTime;
                }
            }
            /* else no combined oscillator effects, State.CombinedOscEffectGenerator is null */

            /* if this object ties to a note, then this is the note */
            /* to tie to.  this is used for finding existing oscillators */
            /* for tie continuations. */
            State.TieToNote = Note._Tie;

            /* portamento control parameters */
            State.PortamentoCounter = 0;
            State.CurrentFrequency  = FrozenNote.NominalFrequency;


            /* fix up pre-origin times */
            OscStateRec StateScan = State.OscillatorList;

            while (StateScan != null)
            {
                StateScan.StateReference.FixUpPreOrigin(
                    MaxOscillatorPreOriginTime);
                StateScan = StateScan.Next;
            }
            LFOGeneratorFixEnvelopeOrigins(
                State.PitchLFO,
                MaxOscillatorPreOriginTime);
            if (State.CombinedOscEffectGenerator != null)
            {
                FixUpOscEffectGeneratorPreOrigin(
                    State.CombinedOscEffectGenerator,
                    MaxOscillatorPreOriginTime);
            }

            /* various counters (in terms of envelope ticks) */
            if (State.TieToNote == null)
            {
                State.Release1Countdown = FrozenNote.ReleasePoint1 + MaxOscillatorPreOriginTime;
                State.Release2Countdown = FrozenNote.ReleasePoint2 + MaxOscillatorPreOriginTime;
                State.Release3Countdown = FrozenNote.ReleasePoint3 + MaxOscillatorPreOriginTime;
            }
            else
            {
                /* for ties, only honor releases from start */
                if (FrozenNote.Release1FromStart)
                {
                    State.Release1Countdown = FrozenNote.ReleasePoint1 + MaxOscillatorPreOriginTime;
                }
                else
                {
                    State.Release1Countdown = -1;
                }
                if (FrozenNote.Release2FromStart)
                {
                    State.Release2Countdown = FrozenNote.ReleasePoint2 + MaxOscillatorPreOriginTime;
                }
                else
                {
                    State.Release2Countdown = -1;
                }
                if (FrozenNote.Release3FromStart)
                {
                    State.Release3Countdown = FrozenNote.ReleasePoint3 + MaxOscillatorPreOriginTime;
                }
                else
                {
                    State.Release3Countdown = -1;
                }
            }
            State.PitchLFOStartCountdown = FrozenNote.PitchDisplacementStartPoint
                                           /*+ MaxOscillatorPreOriginTime*/;
            /* pre origin relationship must be preserved for */
            /* pitch LFO trigger */

            /* done */
            WhenToStartPlayingOut = StartPointAdjust - MaxOscillatorPreOriginTime;
            StateOut = State;
            return(SynthErrorCodes.eSynthDone);
        }