/* find out if the wave table oscillator has finished */
            public bool IsItFinished()
            {
                WaveTableStateRec State = this;

                /* we are finished when one of the following conditions is met: */
                /*  - output volume is zero AND loudness envelope is finished */
                /*  - we are not generating any signal */
                if (!State.WaveTableWasDefined)
                {
                    return(true);
                }
                return(IsEnvelopeAtEnd(State.WaveTableLoudnessEnvelope));
            }
            /* fix up pre-origin time for the wave table state object */
            public void FixUpPreOrigin(
                int ActualPreOrigin)
            {
                WaveTableStateRec State = this;

                EnvelopeStateFixUpInitialDelay(
                    State.WaveTableIndexEnvelope,
                    ActualPreOrigin);
                EnvelopeStateFixUpInitialDelay(
                    State.WaveTableLoudnessEnvelope,
                    ActualPreOrigin);
                LFOGeneratorFixEnvelopeOrigins(
                    State.IndexLFOGenerator,
                    ActualPreOrigin);
                LFOGeneratorFixEnvelopeOrigins(
                    State.LoudnessLFOGenerator,
                    ActualPreOrigin);
                LFOGeneratorFixEnvelopeOrigins(
                    State.PitchLFO,
                    ActualPreOrigin);
                if (State.OscEffectGenerator != null)
                {
                    FixUpOscEffectGeneratorPreOrigin(
                        State.OscEffectGenerator,
                        ActualPreOrigin);
                }

                State.PreStartCountdown += ActualPreOrigin;

                // initial value for envelope smoothing
                State.Loudness = (float)(State.NoteLoudnessScaling * LFOGenInitialValue(
                                             State.LoudnessLFOGenerator,
                                             EnvelopeInitialValue(
                                                 State.WaveTableLoudnessEnvelope)));

                // initial value for envelope smoothing
                double waveTableIndex = State.NumberOfTablesMinus1 * LFOGenInitialValue(
                    State.IndexLFOGenerator,
                    EnvelopeInitialValue(
                        State.WaveTableIndexEnvelope));

                if (waveTableIndex < 0)
                {
                    waveTableIndex = 0;
                }
                else if (waveTableIndex > State.NumberOfTablesMinus1)
                {
                    waveTableIndex = State.NumberOfTablesMinus1;
                }
                State.WaveTableIndex = waveTableIndex;
            }
            /* send a key-up signal to one of the oscillators */
            public void KeyUpSustain3()
            {
                WaveTableStateRec State = this;

                LFOGeneratorKeyUpSustain3(
                    State.IndexLFOGenerator);
                LFOGeneratorKeyUpSustain3(
                    State.LoudnessLFOGenerator);
                EnvelopeKeyUpSustain3(
                    State.WaveTableIndexEnvelope);
                EnvelopeKeyUpSustain3(
                    State.WaveTableLoudnessEnvelope);
                LFOGeneratorKeyUpSustain3(
                    State.PitchLFO);
                if (State.OscEffectGenerator != null)
                {
                    OscEffectKeyUpSustain3(
                        State.OscEffectGenerator);
                }
            }
            /* finalize before termination */
            public void Finalize(
                SynthParamRec SynthParams,
                bool writeOutputLogs)
            {
                WaveTableStateRec State = this;

                if (State.OscEffectGenerator != null)
                {
                    FinalizeOscEffectGenerator(
                        State.OscEffectGenerator,
                        SynthParams,
                        writeOutputLogs);
                }

                FreeEnvelopeStateRecord(
                    ref State.WaveTableLoudnessEnvelope,
                    SynthParams);
                FreeLFOGenerator(
                    ref State.LoudnessLFOGenerator,
                    SynthParams);

                FreeEnvelopeStateRecord(
                    ref State.WaveTableIndexEnvelope,
                    SynthParams);
                FreeLFOGenerator(
                    ref State.IndexLFOGenerator,
                    SynthParams);

                FreeLFOGenerator(
                    ref State.PitchLFO,
                    SynthParams);

                Free(
                    ref SynthParams.freelists.waveTableStateFreeList,
                    ref State);
            }
            private static void Wave_Stereo(
                WaveTableStateRec State,
                int nActualFrames,
                float[] workspace,
                int lOffset,
                int rOffset,
                int loudnessWorkspaceOffset,
                double[] indexWorkspace,
                int indexWorkspaceOffset)
            {
#if DEBUG
                if ((State.WaveTableIndex < 0) || (State.WaveTableIndex > State.NumberOfTablesMinus1))
                {
                    // wave index out of range
                    Debug.Assert(false);
                    throw new InvalidOperationException();
                }
#endif

                /* load local values */
                float   LocalLoudness = State.Loudness;
                float   LeftPan       = .5f - .5f * State.Panning; // (1 - State.Panning) / 2
                float   RightPan      = .5f + .5f * State.Panning; // (1 + State.Panning) / 2
                Fixed64 LocalWaveTableSamplePosition             = State.WaveTableSamplePosition;
                Fixed64 LocalWaveTableSamplePositionDifferential = State.WaveTableSamplePositionDifferential;
                int     LocalSamplePositionMask = State.FramesPerTable - 1;

                if (Program.Config.EnableEnvelopeSmoothing
                    // case of no motion in either smoothed axes can use fast code path
                    && ((State.Loudness != State.PreviousLoudness) ||
                        (State.WaveTableIndex != State.PreviousWaveTableIndex)))
                {
                    // envelope smoothing

                    float LocalPreviousLoudness = State.PreviousLoudness;

                    // intentional discretization by means of sample-and-hold lfo should not be smoothed.
                    if (IsLFOSampleAndHold(State.LoudnessLFOGenerator))
                    {
                        LocalPreviousLoudness = LocalLoudness;
                    }

                    if (!EnvelopeCurrentSegmentExponential(State.WaveTableLoudnessEnvelope))
                    {
                        FloatVectorAdditiveRecurrence(
                            workspace,
                            loudnessWorkspaceOffset,
                            LocalPreviousLoudness,
                            LocalLoudness,
                            nActualFrames);
                    }
                    else
                    {
                        FloatVectorMultiplicativeRecurrence(
                            workspace,
                            loudnessWorkspaceOffset,
                            LocalPreviousLoudness,
                            LocalLoudness,
                            nActualFrames);
                    }

                    /* select method */
                    if (((State.WaveTableIndex == State.NumberOfTablesMinus1) &&
                         (State.PreviousWaveTableIndex == State.NumberOfTablesMinus1)) ||
                        !State.EnableCrossWaveTableInterpolation)
                    {
                        /* process at end of table, where there is no extra table for interpolation */

                        /* load local values */
                        float[] WaveData = State.WaveTableMatrix[(int)(State.WaveTableIndex)];

                        /* process */
                        for (int i = 0; i < nActualFrames; i++)
                        {
                            /* load outside buffer values */
                            float OrigLeft  = workspace[i + lOffset];
                            float OrigRight = workspace[i + rOffset];

                            /* compute weighting and subscript */
                            float RightWeight    = LocalWaveTableSamplePosition.FracF;
                            int   ArraySubscript = LocalWaveTableSamplePosition.Int & LocalSamplePositionMask;

                            /* L+F(R-L) */
                            float LeftValue     = WaveData[ArraySubscript];
                            float RightValue    = WaveData[ArraySubscript + 1];
                            float CombinedValue = LeftValue + (RightWeight * (RightValue - LeftValue));

                            /* increment pitch differential */
                            LocalWaveTableSamplePosition += LocalWaveTableSamplePositionDifferential;

                            /* generate output */
                            LocalLoudness          = workspace[i + loudnessWorkspaceOffset];
                            workspace[i + lOffset] = OrigLeft + LeftPan * LocalLoudness * CombinedValue;
                            workspace[i + rOffset] = OrigRight + RightPan * LocalLoudness * CombinedValue;
                        }
                    }
                    else
                    {
                        double LocalPreviousWaveTableIndex = State.PreviousWaveTableIndex;
                        double LocalWaveTableIndex         = State.WaveTableIndex;

                        // intentional discretization by means of sample-and-hold lfo should not be smoothed.
                        if (IsLFOSampleAndHold(State.IndexLFOGenerator))
                        {
                            LocalPreviousWaveTableIndex = LocalWaveTableIndex;
                        }

                        if (!EnvelopeCurrentSegmentExponential(State.WaveTableLoudnessEnvelope))
                        {
                            FloatVectorAdditiveRecurrenceFixed(
                                indexWorkspace,
                                indexWorkspaceOffset,
                                LocalPreviousWaveTableIndex,
                                LocalWaveTableIndex,
                                nActualFrames);
                        }
                        else
                        {
                            FloatVectorMultiplicativeRecurrenceFixed(
                                indexWorkspace,
                                indexWorkspaceOffset,
                                LocalPreviousWaveTableIndex,
                                LocalWaveTableIndex,
                                nActualFrames);
                        }

                        /* process */
                        for (int i = 0; i < nActualFrames; i++)
                        {
                            // unfortunately these are variant in this case
                            LocalWaveTableIndex = indexWorkspace[indexWorkspaceOffset + i];
                            float[] WaveData0 = State.WaveTableMatrix[(int)LocalWaveTableIndex];
                            // wave index can reach max value during iterations, constrain (Wave1Weight becomes unimportant)
                            float[] WaveData1   = State.WaveTableMatrix[(int)Math.Min(LocalWaveTableIndex + 1, State.NumberOfTablesMinus1)];
                            float   Wave1Weight = (float)(LocalWaveTableIndex - (int)LocalWaveTableIndex);

                            /* load outside buffer values */
                            float OrigLeft  = workspace[i + lOffset];
                            float OrigRight = workspace[i + rOffset];

                            /* compute weighting and subscript */
                            float RightWeight    = LocalWaveTableSamplePosition.FracF;
                            int   ArraySubscript = LocalWaveTableSamplePosition.Int & LocalSamplePositionMask;

                            /* L+F(R-L) -- applied twice */
                            float Left0Value  = WaveData0[ArraySubscript];
                            float Right0Value = WaveData0[ArraySubscript + 1];
                            float Left1Value  = WaveData1[ArraySubscript];
                            float Right1Value = WaveData1[ArraySubscript + 1];
                            float Wave0Temp   = Left0Value + (RightWeight * (Right0Value - Left0Value));
                            Wave0Temp = Wave0Temp + (Wave1Weight * (Left1Value + (RightWeight
                                                                                  * (Right1Value - Left1Value)) - Wave0Temp));

                            /* increment pitch differential */
                            LocalWaveTableSamplePosition += LocalWaveTableSamplePositionDifferential;

                            /* generate output */
                            LocalLoudness          = workspace[i + loudnessWorkspaceOffset];
                            workspace[i + lOffset] = OrigLeft + LeftPan * LocalLoudness * Wave0Temp;
                            workspace[i + rOffset] = OrigRight + RightPan * LocalLoudness * Wave0Temp;
                        }
                    }
                }
                else
                {
                    // non-smoothing case

                    float LocalLeftLoudness  = LocalLoudness * LeftPan;
                    float LocalRightLoudness = LocalLoudness * RightPan;

                    /* select method */
                    if ((State.WaveTableIndex == State.NumberOfTablesMinus1) ||
                        !State.EnableCrossWaveTableInterpolation)
                    {
                        /* process at end of table, where there is no extra table for interpolation */

                        /* load local values */
                        float[] WaveData = State.WaveTableMatrix[(int)(State.WaveTableIndex)];

                        /* process */
                        for (int i = 0; i < nActualFrames; i++)
                        {
                            /* load outside buffer values */
                            float OrigLeft  = workspace[i + lOffset];
                            float OrigRight = workspace[i + rOffset];

                            /* compute weighting and subscript */
                            float RightWeight    = LocalWaveTableSamplePosition.FracF;
                            int   ArraySubscript = LocalWaveTableSamplePosition.Int & LocalSamplePositionMask;

                            /* L+F(R-L) */
                            float LeftValue     = WaveData[ArraySubscript];
                            float RightValue    = WaveData[ArraySubscript + 1];
                            float CombinedValue = LeftValue + (RightWeight * (RightValue - LeftValue));

                            /* increment pitch differential */
                            LocalWaveTableSamplePosition += LocalWaveTableSamplePositionDifferential;

                            /* generate output */
                            workspace[i + lOffset] = OrigLeft + LocalLeftLoudness * CombinedValue;
                            workspace[i + rOffset] = OrigRight + LocalRightLoudness * CombinedValue;
                        }
                    }
                    else
                    {
                        /* load local values */
                        float[] WaveData0   = State.WaveTableMatrix[(int)(State.WaveTableIndex)];
                        float[] WaveData1   = State.WaveTableMatrix[(int)(State.WaveTableIndex) + 1];
                        float   Wave1Weight = (float)(State.WaveTableIndex - (int)(State.WaveTableIndex));

                        /* process */
                        for (int i = 0; i < nActualFrames; i++)
                        {
                            /* load outside buffer values */
                            float OrigLeft  = workspace[i + lOffset];
                            float OrigRight = workspace[i + rOffset];

                            /* compute weighting and subscript */
                            float RightWeight    = LocalWaveTableSamplePosition.FracF;
                            int   ArraySubscript = LocalWaveTableSamplePosition.Int & LocalSamplePositionMask;

                            /* L+F(R-L) -- applied twice */
                            float Left0Value  = WaveData0[ArraySubscript];
                            float Right0Value = WaveData0[ArraySubscript + 1];
                            float Left1Value  = WaveData1[ArraySubscript];
                            float Right1Value = WaveData1[ArraySubscript + 1];
                            float Wave0Temp   = Left0Value + (RightWeight * (Right0Value - Left0Value));
                            Wave0Temp = Wave0Temp + (Wave1Weight * (Left1Value + (RightWeight
                                                                                  * (Right1Value - Left1Value)) - Wave0Temp));

                            /* increment pitch differential */
                            LocalWaveTableSamplePosition += LocalWaveTableSamplePositionDifferential;

                            /* generate output */
                            workspace[i + lOffset] = OrigLeft + LocalLeftLoudness * Wave0Temp;
                            workspace[i + rOffset] = OrigRight + LocalRightLoudness * Wave0Temp;
                        }
                    }
                }

                /* save local state back to record */
                State.WaveTableSamplePosition = LocalWaveTableSamplePosition;
            }
            /* generate one sequence of samples */
            public SynthErrorCodes Generate(
                int nActualFrames,
                float[] workspace,
                int RawBufferLOffset,
                int RawBufferROffset,
                int PrivateWorkspaceLOffset,
                int PrivateWorkspaceROffset,
                SynthParamRec SynthParams)
            {
                SynthErrorCodes error = SynthErrorCodes.eSynthDone;

                WaveTableStateRec State = this;

#if DEBUG
                Debug.Assert(!SynthParams.ScratchWorkspace1InUse);
                SynthParams.ScratchWorkspace1InUse = true;
                Debug.Assert(!SynthParams.ScratchWorkspace3InUse);
                SynthParams.ScratchWorkspace3InUse = true;
#endif
                int      loudnessWorkspaceOffset = SynthParams.ScratchWorkspace1LOffset;
                int      indexWorkspaceOffset    = SynthParams.ScratchWorkspace3Offset;
                double[] indexWorkspace          = UnsafeArrayCastLong.AsDoubles(SynthParams.ScratchWorkspace3);

                if (State.PreStartCountdown <= 0)
                {
                    if (State.OscEffectGenerator == null)
                    {
                        /* normal case */
                        if (State.WaveTableWasDefined)
                        {
                            Wave_Stereo(
                                State,
                                nActualFrames,
                                workspace,
                                RawBufferLOffset,
                                RawBufferROffset,
                                loudnessWorkspaceOffset,
                                indexWorkspace,
                                indexWorkspaceOffset);
                        }
                    }
                    else
                    {
                        /* effect postprocessing case */

                        /* initialize private storage */
                        FloatVectorZero(
                            workspace,
                            PrivateWorkspaceLOffset,
                            nActualFrames);
                        FloatVectorZero(
                            workspace,
                            PrivateWorkspaceROffset,
                            nActualFrames);

                        /* generate waveform */
                        if (State.WaveTableWasDefined)
                        {
                            Wave_Stereo(
                                State,
                                nActualFrames,
                                workspace,
                                PrivateWorkspaceLOffset,
                                PrivateWorkspaceROffset,
                                loudnessWorkspaceOffset,
                                indexWorkspace,
                                indexWorkspaceOffset);
                        }

#if DEBUG
                        SynthParams.ScratchWorkspace1InUse = false;
                        SynthParams.ScratchWorkspace3InUse = false;
#endif

                        /* apply processor to it */
                        error = ApplyOscEffectGenerator(
                            State.OscEffectGenerator,
                            workspace,
                            PrivateWorkspaceLOffset,
                            PrivateWorkspaceROffset,
                            nActualFrames,
                            SynthParams);
                        if (error != SynthErrorCodes.eSynthDone)
                        {
                            goto Error;
                        }

                        /* copy out data */
                        FloatVectorAcc(
                            workspace,
                            PrivateWorkspaceLOffset,
                            workspace,
                            RawBufferLOffset,
                            nActualFrames);
                        FloatVectorAcc(
                            workspace,
                            PrivateWorkspaceROffset,
                            workspace,
                            RawBufferROffset,
                            nActualFrames);
                    }
                }


Error:

#if DEBUG
                SynthParams.ScratchWorkspace1InUse = false;
                SynthParams.ScratchWorkspace3InUse = false;
#endif

                return(error);
            }
            /* restart a wave table oscillator.  this is used for tie continuations */
            public void Restart(
                ref AccentRec NewAccents,
                double NewLoudness,
                double NewHurryUp,
                bool RetriggerEnvelopes,
                double NewStereoPosition,
                double NewInitialFrequency,
                double PitchDisplacementDepthLimit,
                double PitchDisplacementRateLimit,
                SynthParamRec SynthParams)
            {
                WaveTableStateRec State = this;

                NewStereoPosition += State.Template.StereoBias;
                if (NewStereoPosition < -1)
                {
                    NewStereoPosition = -1;
                }
                else if (NewStereoPosition > 1)
                {
                    NewStereoPosition = 1;
                }
                State.Panning = (float)NewStereoPosition;

                State.NoteLoudnessScaling = NewLoudness * State.Template.OverallOscillatorLoudness;

                EnvelopeRetriggerFromOrigin(
                    State.WaveTableIndexEnvelope,
                    ref NewAccents,
                    NewInitialFrequency,
                    1,
                    NewHurryUp,
                    RetriggerEnvelopes,
                    SynthParams);
                EnvelopeRetriggerFromOrigin(
                    State.WaveTableLoudnessEnvelope,
                    ref NewAccents,
                    NewInitialFrequency,
                    1,
                    NewHurryUp,
                    RetriggerEnvelopes,
                    SynthParams);
                LFOGeneratorRetriggerFromOrigin(
                    State.IndexLFOGenerator,
                    ref NewAccents,
                    NewInitialFrequency,
                    NewHurryUp,
                    1,
                    1,
                    RetriggerEnvelopes,
                    SynthParams);
                LFOGeneratorRetriggerFromOrigin(
                    State.LoudnessLFOGenerator,
                    ref NewAccents,
                    NewInitialFrequency,
                    NewHurryUp,
                    1,
                    1,
                    RetriggerEnvelopes,
                    SynthParams);
                LFOGeneratorRetriggerFromOrigin(
                    State.PitchLFO,
                    ref NewAccents,
                    NewInitialFrequency,
                    NewHurryUp,
                    PitchDisplacementDepthLimit,
                    PitchDisplacementRateLimit,
                    RetriggerEnvelopes,
                    SynthParams);
                if (State.OscEffectGenerator != null)
                {
                    OscEffectGeneratorRetriggerFromOrigin(
                        State.OscEffectGenerator,
                        ref NewAccents,
                        NewInitialFrequency,
                        NewHurryUp,
                        RetriggerEnvelopes,
                        SynthParams);
                }
                /* do not reset PitchLFOStartCountdown since we can't give it a proper value */
                /* to do the expected thing, and we'll be interrupting the phase of the LFO */
                /* wave generator */
            }
            /* perform one envelope update cycle, and set a new frequency for a wave table */
            /* state object.  used for portamento and modulation of frequency (vibrato) */
            public SynthErrorCodes UpdateEnvelopes(
                double NewFrequencyHertz,
                SynthParamRec SynthParams)
            {
                SynthErrorCodes   error;
                WaveTableStateRec State = this;

                if (State.PitchLFOStartCountdown > 0)
                {
                    State.PitchLFOStartCountdown -= 1;
                }
                else
                {
                    /* do some pitch stuff */
                    error             = SynthErrorCodes.eSynthDone;
                    NewFrequencyHertz = LFOGenUpdateCycle(
                        State.PitchLFO,
                        NewFrequencyHertz,
                        NewFrequencyHertz,
                        SynthParams,
                        ref error);
                    if (error != SynthErrorCodes.eSynthDone)
                    {
                        return(error);
                    }
                }
                NewFrequencyHertz = NewFrequencyHertz * State.Template.FrequencyMultiplier + State.Template.FrequencyAdder;
                double Differential = NewFrequencyHertz * State.FramesPerTableOverFinalOutputSamplingRate;

                State.WaveTableSamplePositionDifferential = new Fixed64(Differential);

                /* this is for the benefit of resampling only -- envelope generators do their */
                /* own pre-origin sequencing */
                if (State.PreStartCountdown > 0)
                {
                    State.PreStartCountdown -= 1;
                }

                error = SynthErrorCodes.eSynthDone;
                double waveTableIndex = State.NumberOfTablesMinus1 *
                                        LFOGenUpdateCycle(
                    State.IndexLFOGenerator,
                    EnvelopeUpdate(
                        State.WaveTableIndexEnvelope,
                        NewFrequencyHertz,
                        SynthParams,
                        ref error),
                    NewFrequencyHertz,
                    SynthParams,
                    ref error);

                if (error != SynthErrorCodes.eSynthDone)
                {
                    return(error);
                }
                if (waveTableIndex < 0)
                {
                    waveTableIndex = 0;
                }
                else if (waveTableIndex > State.NumberOfTablesMinus1)
                {
                    waveTableIndex = State.NumberOfTablesMinus1;
                }
                State.PreviousWaveTableIndex = State.WaveTableIndex;
                State.WaveTableIndex         = waveTableIndex;

                error = SynthErrorCodes.eSynthDone;
                State.PreviousLoudness = State.Loudness;
                State.Loudness         = (float)(State.NoteLoudnessScaling *
                                                 LFOGenUpdateCycle(
                                                     State.LoudnessLFOGenerator,
                                                     EnvelopeUpdate(
                                                         State.WaveTableLoudnessEnvelope,
                                                         NewFrequencyHertz,
                                                         SynthParams,
                                                         ref error),
                                                     NewFrequencyHertz,
                                                     SynthParams,
                                                     ref error));
                if (error != SynthErrorCodes.eSynthDone)
                {
                    return(error);
                }

                if (State.OscEffectGenerator != null)
                {
                    error = OscEffectGeneratorUpdateEnvelopes(
                        State.OscEffectGenerator,
                        NewFrequencyHertz,
                        SynthParams);
                    if (error != SynthErrorCodes.eSynthDone)
                    {
                        return(error);
                    }
                }

                return(SynthErrorCodes.eSynthDone);
            }
            /* create a new wave table state object. */
            public SynthErrorCodes NewState(
                double FreqForMultisampling,
                ref AccentRec Accents,
                double Loudness,
                double HurryUp,
                out int PreOriginTimeOut,
                double StereoPosition,
                double InitialFrequency,
                double PitchDisplacementDepthLimit,
                double PitchDisplacementRateLimit,
                int PitchDisplacementStartPoint,
                PlayTrackInfoRec TrackInfo,
                SynthParamRec SynthParams,
                out IOscillator StateOut)
            {
                WaveTableTemplateRec Template = this;

                int OnePreOrigin;

                PreOriginTimeOut = 0;
                StateOut         = null;

                WaveTableStateRec State = New(ref SynthParams.freelists.waveTableStateFreeList);

                // all fields must be assigned: State

                // conservative zero-initialization
                State.WaveTableSamplePositionDifferential = new Fixed64(0);

                State.Template = Template;

                int MaxPreOrigin = 0;

                State.WaveTableSamplePosition = new Fixed64(0);
                /* State.WaveTableSamplePositionDifferential specified in separate call */

                State.NoteLoudnessScaling = Loudness * Template.OverallOscillatorLoudness;

                int NumberOfTables;

                State.WaveTableWasDefined = GetMultiWaveTableReference(
                    Template.WaveTableSourceSelector,
                    FreqForMultisampling,
                    out State.WaveTableMatrix,
                    out State.FramesPerTable,
                    out NumberOfTables);
                State.NumberOfTablesMinus1 = NumberOfTables - 1;

                State.FramesPerTableOverFinalOutputSamplingRate
                    = (double)State.FramesPerTable / SynthParams.dSamplingRate;

                /* State.FramesPerTable > 0: */
                /*   if the wave table is empty, then we don't do any work (and we must not, */
                /*   since array accesses would cause a crash) */
                if (State.WaveTableWasDefined)
                {
                    if (!(State.FramesPerTable > 0))
                    {
                        State.WaveTableWasDefined = false;
                    }
                }

                State.PreStartCountdown = (int)((Template.TimeDisplacement
                                                 * SynthParams.dEnvelopeRate) + 0.5);
                if (-State.PreStartCountdown > MaxPreOrigin)
                {
                    MaxPreOrigin = -State.PreStartCountdown;
                }

                /* State.WaveTableIndex determined by envelope update */
                State.WaveTableIndexEnvelope = NewEnvelopeStateRecord(
                    Template.IndexEnvelopeTemplate,
                    ref Accents,
                    InitialFrequency,
                    1,
                    HurryUp,
                    out OnePreOrigin,
                    _PlayTrackParamGetter,
                    TrackInfo,
                    SynthParams);
                if (OnePreOrigin > MaxPreOrigin)
                {
                    MaxPreOrigin = OnePreOrigin;
                }
                State.IndexLFOGenerator = NewLFOGenerator(
                    Template.IndexLFOTemplate,
                    out OnePreOrigin,
                    ref Accents,
                    InitialFrequency,
                    HurryUp,
                    1,
                    1,
                    FreqForMultisampling,
                    _PlayTrackParamGetter,
                    TrackInfo,
                    SynthParams);
                if (OnePreOrigin > MaxPreOrigin)
                {
                    MaxPreOrigin = OnePreOrigin;
                }
                State.PreviousWaveTableIndex = 0;
                State.WaveTableIndex         = 0;

                /* State.MonoLoudness, State.LeftLoudness, State.RightLoudness */
                /* are determined by the envelope update */
                StereoPosition += Template.StereoBias;
                if (StereoPosition < -1)
                {
                    StereoPosition = -1;
                }
                else if (StereoPosition > 1)
                {
                    StereoPosition = 1;
                }
                State.Panning = (float)StereoPosition;
                State.WaveTableLoudnessEnvelope = NewEnvelopeStateRecord(
                    Template.LoudnessEnvelopeTemplate,
                    ref Accents,
                    InitialFrequency,
                    1,
                    HurryUp,
                    out OnePreOrigin,
                    _PlayTrackParamGetter,
                    TrackInfo,
                    SynthParams);
                if (OnePreOrigin > MaxPreOrigin)
                {
                    MaxPreOrigin = OnePreOrigin;
                }
                State.LoudnessLFOGenerator = NewLFOGenerator(
                    Template.LoudnessLFOTemplate,
                    out OnePreOrigin,
                    ref Accents,
                    InitialFrequency,
                    HurryUp,
                    1,
                    1,
                    FreqForMultisampling,
                    _PlayTrackParamGetter,
                    TrackInfo,
                    SynthParams);
                if (OnePreOrigin > MaxPreOrigin)
                {
                    MaxPreOrigin = OnePreOrigin;
                }
                State.PreviousLoudness = 0;
                State.Loudness         = 0;

                State.PitchLFO = NewLFOGenerator(
                    Template.PitchLFOTemplate,
                    out OnePreOrigin,
                    ref Accents,
                    InitialFrequency,
                    HurryUp,
                    PitchDisplacementDepthLimit,
                    PitchDisplacementRateLimit,
                    FreqForMultisampling,
                    _PlayTrackParamGetter,
                    TrackInfo,
                    SynthParams);
                if (OnePreOrigin > MaxPreOrigin)
                {
                    MaxPreOrigin = OnePreOrigin;
                }
                State.PitchLFOStartCountdown = PitchDisplacementStartPoint;

                State.OscEffectGenerator = null;
                if (Template.OscEffectTemplate != null)
                {
                    SynthErrorCodes Result = NewOscEffectGenerator(
                        Template.OscEffectTemplate,
                        ref Accents,
                        HurryUp,
                        InitialFrequency,
                        FreqForMultisampling,
                        out OnePreOrigin,
                        TrackInfo,
                        SynthParams,
                        out State.OscEffectGenerator);
                    if (Result != SynthErrorCodes.eSynthDone)
                    {
                        return(Result);
                    }
                    if (OnePreOrigin > MaxPreOrigin)
                    {
                        MaxPreOrigin = OnePreOrigin;
                    }
                }

                State.EnableCrossWaveTableInterpolation = Template.EnableCrossWaveTableInterpolation;

                PreOriginTimeOut = MaxPreOrigin;
                StateOut         = State;
                return(SynthErrorCodes.eSynthDone);
            }