void AddPSG(M4AVoice voice, byte low = 0, byte high = 127) { dynamic v = voice; int sample; if (voice is M4APSG_Square_1 || voice is M4APSG_Square_2) { sample = AddSample(SongPlayer.Sounds[SongPlayer.SQUARE12_ID - v.Pattern], "Square Wave " + v.Pattern); } else if (voice is M4APSG_Wave wave) { sample = AddSample(SongPlayer.Sounds[wave.Address], string.Format("PSG Wave 0x{0:X}", wave.Address)); } else { sample = AddSample(SongPlayer.Sounds[SongPlayer.NOISE0_ID - v.Pattern], "Noise " + v.Pattern); } sf2.AddINSTBag(); // ADSR if (v.A != 0) { // Compute attack time - the sound engine is called 60 times per second // and adds "attack" to envelope every time the engine is called double att_time = v.A / 5.0; double att = 1200 * Math.Log(att_time, 2); sf2.AddINSTGenerator(SF2Generator.attackVolEnv, new GenAmountType((ushort)att)); } if (v.S != 15) { double sus; // Compute attenuation in cB if sustain is non-zero if (v.S != 0) { sus = 100 * Math.Log(15d / v.S); } // Special case where attenuation is infinite -> use max value else { sus = 1000; } sf2.AddINSTGenerator(SF2Generator.sustainVolEnv, new GenAmountType((ushort)sus)); double dec_time = v.D / 5d; double dec = 1200 * Math.Log(dec_time + 1, 2); sf2.AddINSTGenerator(SF2Generator.decayVolEnv, new GenAmountType((ushort)dec)); } if (v.R != 0) { double rel_time = v.R / 5d; double rel = 1200 * Math.Log(rel_time, 2); sf2.AddINSTGenerator(SF2Generator.releaseVolEnv, new GenAmountType((ushort)rel)); } high = Math.Min((byte)127, high); if (!(low == 0 && high == 127)) { sf2.AddINSTGenerator(SF2Generator.keyRange, new GenAmountType(low, high)); } if (voice is M4APSG_Noise noise && noise.Panpot != 0) { sf2.AddINSTGenerator(SF2Generator.pan, new GenAmountType((ushort)((noise.Panpot - 0xC0) * (500d / 0x80)))); } sf2.AddINSTGenerator(SF2Generator.sampleModes, new GenAmountType(1)); sf2.AddINSTGenerator(SF2Generator.sampleID, new GenAmountType((ushort)(sample))); }