Lerp() private method

private Lerp ( double value1, double value2, double amount ) : double
value1 double
value2 double
amount double
return double
Exemplo n.º 1
0
        public static readonly short[] amdPushupRatio = new short[1024];  //Amount used to push a value scaled by the above table to a positive range.

        static Tables()
        {
            for(int i=0; i<short2float.Length; i++)
            {
                short2float[i] = (float) (i / 32767.5) - 1;
            }


            for(int i=1; i<dutyRatio.Length; i++)  //Start at 1 to avoid divide by zero!
            {
                dutyRatio[i] = (float) (0xFFFF / (double)i) ;
            }


            //Transpose
            for(int i=0; i<transpose.Length; i++)
            {
                transpose[i] = Math.Pow(2, (i * 0.01)/12.0);
            }

            //sin
            for(int i=0; i<sin.Length; i++)
            {
                // sin[i] =  (short) Math.Round(Math.Sin(TAU * (i/(double)sin.Length) )*short.MaxValue);
                sin[i] =  unchecked( (ushort) Math.Round( -Tools.Log2(Math.Sin((i+0.5) * Math.PI/sin.Length/2.0)) * 256) );
            }
            
            //tri
            for (int i=0; i<tri.Length; i++)
            {
                var inc = ((i+0.5)/(double)(tri.Length)) ;
                // saw[i] = (ushort)(Tools.ToFixedPoint( inc, 8) | (uint)inc  & (ushort.MaxValue>>0));
                tri[i] = (ushort) ( (float) Math.Round(-Tools.Log2(inc*2) * 256));
            }

            //saw table
            for (int i=0; i<saw.Length; i++)
            {
                var inc = ((i+0.5)/(double)(saw.Length + 1)) ;
                // saw[i] = (ushort)(Tools.ToFixedPoint( inc, 8) | (uint)inc  & (ushort.MaxValue>>0));
                saw[i] = (ushort) ( (float) Math.Round(-Tools.Log2(inc*2) * 256));
            }


            //LFOs, 16kb tables etc
            for (int i=1; i<8192; i++)
            {
                vol2pitchDown[i] = Tools.Lerp(1, 0.5f, i/8192.0f);
                vol2pitchUp[i]   = Tools.Lerp(1, 2, i/8192.0f);
            }

            for (int i=0; i<amdScaleRatio.Length; i++)
            {
                amdScaleRatio[i] = 1.0f - i / (float)Envelope.L_MAX;  // Value from 1.0f-0 representing how much to scale volume by from the raw AMD value
                amdPushupRatio[i] = (short)(0x1FFF * amdScaleRatio[i]); 
            }


            //Volume to attenuation table
            var attVol = new ushort[2048];
            for(ushort i=0; i<attVol.Length; i++)
            {
                attVol[i] = attenuation_to_volume(i);
                vol2attenuation[attVol[i]] = i;
            }
            //Above we calculated out all of the reasonable values from the known table, now we'll fill all the other values in between with similar attenuations.
            //We added one to vol2attenuation's array length to accomodate possible overflows but don't want it offsetting the calcs, so subtract 1 in the loop.
            ushort lastVal = 2047;
            int first_instance=0;
            for(ushort i=0; i<vol2attenuation.Length-1; i++)  
            {
                if(vol2attenuation[i] == 0) 
                    vol2attenuation[i] = lastVal; 
                else if (first_instance==0)
                    {
                        first_instance = i;
                        lastVal = vol2attenuation[i];
                    }
                else
                    lastVal = vol2attenuation[i];
            }

            for (ushort i=0; i<first_instance; i++)  //FIXME:  This block may not be necessary.  Check against very slow LFOs with saw waves.
            {
                vol2attenuation[i] = (ushort)Tools.Lerp(2047, vol2attenuation[first_instance], i/(first_instance-1));
            }



            // Stopwatch sw = new Stopwatch();
            // bool flip=false;
            // long inc2= (long)(Global.FRAC_SIZE << 2);
            // double inc3 = (double)inc2;
            // sw.Start();
            // for (uint i=0; i<48000*24*6; i++)  
            //     Oscillator.Saw(i, 32767, ref flip, __makeref(inc2));
            // sw.Stop();
            // Console.WriteLine("Saw1 Elapsed={0}ms",sw.Elapsed.Milliseconds);

            // sw.Restart();
            // for (uint i=0; i<48000*24*6; i++)  Oscillator.Saw2(i, 32768, ref flip, __makeref(inc3));
            // sw.Stop();
            // Console.WriteLine("Saw2 Elapsed={0}ms",sw.Elapsed.Milliseconds);

            // System.Diagnostics.Debug.Print("Shornlf");

        }
Exemplo n.º 2
0
        // const float RATIO_RR = 1.0f;


        public override IOErrorFlags Load(string path)
        {
            //Update the clock multiplier, in case sample rate changed....
            ClockMult = 55930.0 / Global.MixRate * Global.ClockMult;

            IOErrorFlags err = IOErrorFlags.OK;

            try
            {
                using (StreamReader sr = new StreamReader(path))
                {
                    string line;
                    line = sr.ReadLine();
                    //Check header for validity.  TODO:  Check to see if there exists other formats created in other drivers
                    if (!line.StartsWith("//MiOPMdrv"))
                    {
                        throw new PE_ImportException(IOErrorFlags.UnrecognizedFormat);
                    }

                    //Move forward to the instrument blocks.
                    // while (!line.StartsWith("@:"))  line=sr.ReadLine();


                    //OPM files all have 128 banks.  Initialize bank.
                    bank = new string[128];

                    //Prepare to process banks.
                    // var egs = new Envelope[4]; egs.InitArray();  //Envelope used to generate partial JSON.
                    // var pgs = new Increments[4]; for(int i=0; i<pgs.Length; i++) pgs[i] = Increments.Prototype();
                    var v = new Voice(4);

                    // for(int i=0; i<bank.Length; i++)
                    while (!sr.EndOfStream)
                    {
                        //Use our envelope proto as a way to store voice data as a json string.
                        //Assume the format of each block corresponds to the MiOPMdrv specification:
                        //@:[Num] [Name]
                        //LFO: FRQ AMD PMD WAV NFRQ   //Where NFRQ = Noise Frequency (Duty in PhaseEngine Noise1 or Noise2 mode)
                        //CH:  PAN	FB ALG AMS PMS SLOT NE  //Where FB = Feedback of M1 (first op) and NE = Noise override (Change all waveforms to Noise1)
                        //[OPname]: AR DR  SR  RR  DL   TL  KS MUL DT1 DT2 AMS-EN
                        //
                        // CH SLOT is the mute mask, where  M1=8, C1=16, M2=32, C2=64. It's currently unknown if flags 1, 2, 4 are used. Normal mask:  120
                        // AMS-EN is AMS enable, which only appears to have 0 and 128 as values.  Treat DT1 as normal detune and DT2 as coarse (Cents mult).

                        var p = new JSONObject();

                        //Get the first instrument.
                        var l = NextValidLine(sr);
                        while (!l.StartsWith("@:"))
                        {
                            l = NextValidLine(sr);
                        }

ProcessNextVoice:
                        //Currently, l should be the instrument header line. Process every instrument in the bank now.
                        l = l.Substring(2).Trim();                                                            //Prep for split.
                        string[] splitLine = { l.Substring(0, l.IndexOf(" ")), l.Substring(l.IndexOf(" ")) }; //Split in two at first space
                        // var splitLine = l.Split(" ", StringSplitOptions.RemoveEmptyEntries);  //Should have a length of 2.
                        var slot = Convert.ToInt32(splitLine[0]);                                             //Will be used to assign the correct bank once we have built our voice proto.
                        // p.AddPrim("name", splitLine[1]);
                        v.name = splitLine[1].Trim();

                        l = NextValidLine(sr);  //Next line should be LFO.  However, order could be anything....

                        int amd, ams = 0, pmd = 0, pms = 0, nFrq = 0;


ProcessNextLine:
                        splitLine = l.Split(" ", StringSplitOptions.RemoveEmptyEntries);
                        switch (splitLine[0].Trim())
                        {
                        case "LFO:":     //LFO configuration options.
                            //Estimate LFO speed from OPM manual. Not very accurate -- FIXME.
                            v.lfo.pg = Increments.FromFreq(Tools.Lerp(0.008, Global.MixRate, Convert.ToByte(splitLine[1]) / 255.0));
                            // v.lfo.pg.FreqSelect( Tools.Lerp(0.008, Global.MixRate, Convert.ToByte(splitLine[1]) / 255.0) );
                            v.lfo.pg.Recalc();
                            amd = (Convert.ToByte(splitLine[2])) << 3; //Max value:  1016.
                            pmd = (Convert.ToByte(splitLine[3]));      //Max value:  127.

                            v.lfo.invert = true;

                            //Determine LFO oscillator.
                            Oscillator.oscTypes osc = Oscillator.oscTypes.Saw;
                            switch (Convert.ToByte(splitLine[4]))
                            {
                            case 0:
                                osc = Oscillator.oscTypes.Saw;
                                break;

                            case 1:
                                osc = Oscillator.oscTypes.Pulse;
                                break;

                            case 2:
                                osc = Oscillator.oscTypes.Triangle;
                                break;

                            case 3:
                                osc = Oscillator.oscTypes.Noise2;
                                break;
                            }
                            v.lfo.SetOscillatorType(osc);

                            //Determine noise frequency, if the noise generator is active.
                            nFrq = Convert.ToByte(splitLine[5]);
                            break;

                        case "CH:":                                                            //Voice configuration options.
                            v.Pan             = Convert.ToByte(splitLine[1]) / 128.0f * 2 - 1; //VOPM appears to set PAN to 64 by default, so we want to make sure 0 is our default
                            v.egs[0].feedback = Convert.ToByte(splitLine[2]);                  //Set operator 1 to the feedback level specified.
                            var algNum = Convert.ToByte(splitLine[3]);
                            v.alg = Algorithm.FromPreset(algNum, Algorithm.PresetType.OPM);
                            ams   = Convert.ToByte(splitLine[4]) << 1; //Max value:  6
                            pms   = Convert.ToByte(splitLine[5]);      //Max value:  7

                            //Process the mute mask.
                            var mute = Convert.ToByte(splitLine[6]) >> 3;      //Default mute mask is 120.  Remove 3 LSBs.
                            for (int i = 0; i < 4; i++)
                            {
                                v.egs[i].mute = (mute >> i & 1) == 0;
                            }

                            //Determine whether the waveform should be noise.
                            if (Convert.ToByte(splitLine[7]) > 0)
                            {
                                for (int i = 0; i < 4; i++)
                                {
                                    v.oscType[i]  = (byte)Oscillator.oscTypes.Noise2;
                                    v.egs[i].duty = (ushort)(31 - nFrq);
                                }
                            }
                            else
                            {
                                for (int i = 0; i < 4; i++)
                                {
                                    v.oscType[i]  = (byte)Oscillator.oscTypes.Sine;
                                    v.egs[i].duty = 32767;
                                }
                            }

                            break;

                        default:                                                                                //One of the 4 operators.  Translate from OpNames to their correct values to assign the correct envelope.
                            OpNames opName;
                            if (Enum.TryParse <OpNames>(splitLine[0].Trim().Substring(0, 2), true, out opName)) //Only executes on success
                            {
                                var opNum = (int)opName;
                                var e     = v.egs[opNum];  //Select the envelope.
                                //[OPname]: AR DR  SR  RR  DL   TL  KS MUL DT1 DT2 AMS-EN

                                e.ar = (byte)Math.Round(Convert.ToByte(splitLine[1]) * RATIO_AR);
                                e.dr = (byte)Math.Round(Convert.ToByte(splitLine[2]) * RATIO_DR);
                                e.sr = (byte)Math.Round(Convert.ToByte(splitLine[3]) * RATIO_SR);
                                e.rr = (byte)Math.Round(Convert.ToByte(splitLine[4]) * RATIO_RR);

                                e.dl = (ushort)Math.Min(Math.Round(Convert.ToUInt16(splitLine[5]) * RATIO_DL), Envelope.L_MAX);
                                e.sl = e.sr > 0? Envelope.L_MAX: e.dl;
                                e.tl = (ushort)Math.Min(Math.Round(Convert.ToUInt16(splitLine[6]) * RATIO_TL), Envelope.L_MAX);

                                //Assign envelope RateTable to a default preset and scale the max application.
                                e.ksr = new RateTable();  e.ksr.ceiling = (float)(Convert.ToUInt16(splitLine[7]) * 25 / ClockMult);     //FIXME:  Check accuracy

                                var dt2 = Convert.ToUInt16(splitLine[10]);
                                v.pgs[opNum].mult   = Convert.ToUInt16(splitLine[8]);
                                v.pgs[opNum].Detune = dt1_ratios[Convert.ToUInt16(splitLine[9])]; //DT1
                                v.pgs[opNum].coarse = dt2_coarse_ratios[dt2];                     //DT2
                                v.pgs[opNum].fine   = dt2_fine_ratios[dt2];

                                //Determine AMS.
                                e.ams = Convert.ToByte(splitLine[11]) > 0?  (byte)ams : (byte)0;
                            }
                            break;
                        }

                        l = NextValidLine(sr);
                        if (l == null || l.StartsWith("@:"))
                        {
                            //Final prep of instrument that has all the values it's going to have plugged into it.  Now to merge PMS/PMD.
                            //OPM LFO is not exactly linear in the pitch range from base note to min/max, so we use an estimate based on max,
                            //where a PMD of 127 translates to a PhaseEngine PMD of ~±0.58_6363 repeating.
                            v.lfo.pmd = Tools.Lerp(0, 0.586363f, pmd / 127.0f) * (pms / 7.0f);

                            //TODO:  Consider whether recalcing the PG increments are necessary.

                            // bank[slot] = //TODO:  Convert voice to string here...  Or, change bank type to Voice[] from string[]....
                            bank[slot] = v.ToJSONString();

                            if (l == null)
                            {
                                break;           //End of File
                            }
                            else
                            {
                                goto ProcessNextVoice;
                            }
                        }
                        else
                        {
                            goto ProcessNextLine;
                        }
                    }
                }
            }
            catch (FileNotFoundException) { err |= IOErrorFlags.NotFound; }
            catch (PE_ImportException e) { err |= e.flags; }
            // catch //Anything else
            // { err |= IOErrorFlags.Failed | IOErrorFlags.Corrupt; }

            return(err);
        }
Exemplo n.º 3
0
 //Applies a random detune value based on the current randomness parameter and the max specified detune level.
 public void ApplyDetuneRandomness()
 {
     detune_current = Tools.Lerp(_detune, 1.0, (XorShift64Star.NextDouble()) * detune_randomness);
 }