/* wave resampler helper */ private static void FOFGeneratorStereo( FOFStateRec State, int nActualFrames, float[] workspace, int lOffset, int rOffset, SynthParamRec SynthParams) { /* see if we shouldn't be here */ if (!State.WaveTableWasDefined) { return; } /* do some setup */ bool EnvelopeFinished = IsEnvelopeAtEnd(State.WaveTableLoudnessEnvelope); int LocalSamplePositionMask = State.FramesPerTable - 1; /* iterate over samples */ for (int Scan = 0; Scan < nActualFrames; Scan++) { /* check for launching */ GrainLaunchCheck( State, EnvelopeFinished, SynthParams); /* prepare point */ float CurrentLeft = workspace[Scan + lOffset]; float CurrentRight = workspace[Scan + rOffset]; /* generate sounds for all registered grains */ FOFGrainRec GrainScan = State.ActiveGrainList; FOFGrainRec GrainLag = null; while (GrainScan != null) { /* wave generation */ float RightWeight = GrainScan.FOFSamplePosition.FracF; int ArraySubscript = GrainScan.FOFSamplePosition.Int & LocalSamplePositionMask; /* L+F(R-L) */ float Left0Value = GrainScan.WaveData0[ArraySubscript]; float Right0Value = GrainScan.WaveData0[ArraySubscript + 1]; float CombinedValue = Left0Value + (RightWeight * (Right0Value - Left0Value)); if ((GrainScan.WaveData1 != null) && State.Template.EnableCrossWaveTableInterpolation) { /* L+F(R-L) -- applied twice */ float Left1Value = GrainScan.WaveData1[ArraySubscript]; float Right1Value = GrainScan.WaveData1[ArraySubscript + 1]; float Wave0Temp = CombinedValue; CombinedValue = Wave0Temp + (GrainScan.Wave1Weight * (Left1Value + (RightWeight * (Right1Value - Left1Value)) - Wave0Temp)); } /* else index == 1, so no right hand wave to use */ CurrentLeft += GrainScan.LeftLoudness * CombinedValue; CurrentRight += GrainScan.RightLoudness * CombinedValue; /* increment phase */ bool Advance = true; GrainScan.FOFSamplePosition += GrainScan.FOFSamplePositionDifferential; if (GrainScan.FOFSamplePosition.Int >= State.FramesPerTable) { GrainScan.FOFSamplePosition.MaskInt64HighHalf(LocalSamplePositionMask); if (State.Template.FOFExpansion == OscFOFExpandType.eOscFOFSilenceFill) { /* silence fill. */ /* for silence fill, we now terminate this oscillator. */ Advance = false; FOFGrainRec Temp = GrainScan; GrainScan = GrainScan.Next; if (GrainLag != null) { GrainLag.Next = GrainScan; } else { State.ActiveGrainList = GrainScan; } Free(ref SynthParams.freelists.FOFGrainRecFreeList, ref Temp); } else { /* loop around. */ /* we only terminate the oscillator if it's not the first */ /* one, otherwise we allow it to restart */ if (GrainLag != null) { /* not the first one */ Advance = false; FOFGrainRec Temp = GrainScan; GrainScan = GrainScan.Next; GrainLag.Next = GrainScan; Free(ref SynthParams.freelists.FOFGrainRecFreeList, ref Temp); } } } if (Advance) { GrainLag = GrainScan; GrainScan = GrainScan.Next; } } /* send value back */ workspace[Scan + lOffset] = CurrentLeft; workspace[Scan + rOffset] = CurrentRight; } }
/* fof pitch start pulse: */ /* | | | | | | */ /* 0.0 1.1 2.1 3.2 0.3 1.4 2.4 3.5 0.6 1.6 2.7 3.7 0.8 1.9 2.9 0.0 1.1 2.1 3.2 0.3 */ /* sampling period pulse: */ /* * * * * * * * * * * * * * * * * * * * * */ /* no interpolation of grains: */ /* 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 */ /* with interpolation of grains: */ /* 0.0 1.0 2.0 0.3 1.3 2.3 0.5 1.5 2.5 0.7 1.7 2.7 0.0 1.0 2.0 0.3 */ /* pitch start pulse and grain sampling pulse have same units to allow */ /* start pulse fraction to be used for initial grain fraction. */ /* check to see if we should launch a new grain */ private static void GrainLaunchCheck( FOFStateRec State, bool EnvelopeFinished, SynthParamRec SynthParams) { /* first, handle grain launching. do this by adding the pitch differential */ /* to the accumulator and checking for overflow. */ State.WaveTableSamplePosition += State.WaveTableSamplePositionDifferential; /* if this overflows, then truncate it and launch a new grain */ if (State.WaveTableSamplePosition.Int >= State.FramesPerTable) { /* time to launch a new grain */ /* truncate grain pitch index to keep wave table index inside */ State.WaveTableSamplePosition.MaskInt64HighHalf(State.FramesPerTable - 1); /* check for discard mode */ if (State.Template.FOFCompression == OscFOFCompressType.eOscFOFDiscard) { /* if we are in discard mode, then dump any active grains */ FOFGrainRec Scan = State.ActiveGrainList; while (Scan != null) { FOFGrainRec one = Scan; Scan = Scan.Next; Free(ref SynthParams.freelists.FOFGrainRecFreeList, ref one); } State.ActiveGrainList = null; } /* creating new grain, but only if the envelope generator hasn't terminated */ if (!EnvelopeFinished) { /* initialize grain differential to the sampling rate */ /* note that grain differential is independent of the */ /* number of frames in the table. */ Fixed64 FOFSamplePositionDifferential = new Fixed64( State.FOFSamplingRateContour * State.Template.FOFSamplingRate / SynthParams.dSamplingRate); // create new grain, but only if differential isn't really close to zero if (!((FOFSamplePositionDifferential.FracI < 0x00010000) && (FOFSamplePositionDifferential.Int == 0))) { FOFGrainRec NewGrain = New(ref SynthParams.freelists.FOFGrainRecFreeList); // must initialize all fields: NewGrain /* set up grain's phase index */ NewGrain.FOFSamplePosition = State.WaveTableSamplePosition; /* set up wave data pointers using current wave table index */ #if DEBUG if ((State.WaveTableIndex < 0) || (State.WaveTableIndex > State.NumberOfTablesMinus1)) { // table index out of range Debug.Assert(false); throw new InvalidOperationException(); } #endif NewGrain.WaveData0 = State.WaveTableMatrix[(int)(State.WaveTableIndex)]; NewGrain.WaveData1 = null; NewGrain.Wave1Weight = 0; if ((int)(State.WaveTableIndex) < State.NumberOfTablesMinus1) { NewGrain.WaveData1 = State.WaveTableMatrix[(int)(State.WaveTableIndex) + 1]; NewGrain.Wave1Weight = (float)(State.WaveTableIndex - (int)(State.WaveTableIndex)); } /* set up amplitudes */ NewGrain.LeftLoudness = State.LeftLoudness; NewGrain.RightLoudness = State.RightLoudness; NewGrain.FOFSamplePositionDifferential = FOFSamplePositionDifferential; NewGrain.Next = null; /* inserted at front of list (important later) */ NewGrain.Next = State.ActiveGrainList; State.ActiveGrainList = NewGrain; } } } }