public bool IsGBInstrument() { if (Type == (int)M4AVoiceFlags.KeySplit || Type == (int)M4AVoiceFlags.Drum) { return(false); } M4AVoiceType vType = (M4AVoiceType)(Type & 0x7); return(vType >= M4AVoiceType.Square1 && vType <= M4AVoiceType.Noise); }
public Channel PlayNote(Track track, sbyte note, byte velocity, int duration) { int shift = note + track.KeyShift; note = (sbyte)(shift.Clamp(0, 0x7F)); track.PrevNote = note; if (!track.Ready) { return(null); } var owner = track.Index; WrappedVoice voice = null; bool fromDrum = false; try { voice = Song.VoiceTable.GetVoiceFromNote(track.Voice, note, out fromDrum); } catch { System.Console.WriteLine("Track {0} tried to play a bad note... Voice {1} Note {2}", owner, track.Voice, note); return(null); } var aNote = new Note { Duration = duration, Velocity = velocity, OriginalKey = note, Key = fromDrum ? voice.Voice.GetRootNote() : note }; if (voice.Voice is M4AVoiceEntry m4a) { M4AVoiceType type = (M4AVoiceType)(m4a.Type & 0x7); switch (type) { case M4AVoiceType.Direct: bool bFixed = (m4a.Type & (int)M4AVoiceFlags.Fixed) == (int)M4AVoiceFlags.Fixed; return(SoundMixer.Instance.NewDSNote(owner, m4a.ADSR, aNote, track.GetVolume(), track.GetPan(), track.GetPitch(), bFixed, ((M4AWrappedDirect)voice).Sample.GetSample(), tracks)); case M4AVoiceType.Square1: case M4AVoiceType.Square2: return(SoundMixer.Instance.NewGBNote(owner, m4a.ADSR, aNote, track.GetVolume(), track.GetPan(), track.GetPitch(), type, m4a.SquarePattern)); case M4AVoiceType.Wave: return(SoundMixer.Instance.NewGBNote(owner, m4a.ADSR, aNote, track.GetVolume(), track.GetPan(), track.GetPitch(), type, m4a.Address - ROM.Pak)); case M4AVoiceType.Noise: return(SoundMixer.Instance.NewGBNote(owner, m4a.ADSR, aNote, track.GetVolume(), track.GetPan(), track.GetPitch(), type, m4a.NoisePattern)); } } else if (voice.Voice is MLSSVoice mlssvoice) { MLSSVoiceEntry entry; bool bFixed = false; WrappedSample sample = null; try { entry = mlssvoice.GetEntryFromNote(note); bFixed = entry.IsFixedFrequency == 0x80; sample = ((MLSSVoiceTable)Song.VoiceTable).Samples[entry.Sample].GetSample(); } catch { System.Console.WriteLine("Track {0} tried to play a bad note... Voice {1} Note {2}", owner, track.Voice, note); return(null); } if (sample != null) { return(SoundMixer.Instance.NewDSNote(owner, new ADSR { A = 0xFF, S = 0xFF }, aNote, track.GetVolume(), track.GetPan(), track.GetPitch(), bFixed, sample, tracks)); } } return(null); }
public GBChannel NewGBNote(byte owner, ADSR env, Note note, byte vol, sbyte pan, int pitch, M4AVoiceType type, object arg) { GBChannel nChn; switch (type) { case M4AVoiceType.Square1: nChn = sq1; if (nChn.State < ADSRState.Releasing && nChn.OwnerIdx < owner) { return(null); } sq1.Init(owner, note, env, (SquarePattern)arg); break; case M4AVoiceType.Square2: nChn = sq2; if (nChn.State < ADSRState.Releasing && nChn.OwnerIdx < owner) { return(null); } sq2.Init(owner, note, env, (SquarePattern)arg); break; case M4AVoiceType.Wave: nChn = wave; if (nChn.State < ADSRState.Releasing && nChn.OwnerIdx < owner) { return(null); } wave.Init(owner, note, env, (int)arg); break; case M4AVoiceType.Noise: nChn = noise; if (nChn.State < ADSRState.Releasing && nChn.OwnerIdx < owner) { return(null); } noise.Init(owner, note, env, (NoisePattern)arg); break; default: return(null); } nChn.SetVolume(vol, pan); nChn.SetPitch(pitch); return(nChn); }
void AddPSG(M4AVoiceEntry entry, byte low = 0, byte high = 0x7F) { int sample; M4AVoiceType type = (M4AVoiceType)(entry.Type & 0x7); if (type == M4AVoiceType.Square1 || type == M4AVoiceType.Square2) { sample = (int)entry.SquarePattern; } else if (type == M4AVoiceType.Wave) { sample = AddWave(entry.Address - ROM.Pak); } else if (type == M4AVoiceType.Noise) { sample = (int)entry.NoisePattern + 4; } else { return; } sf2.AddInstrumentBag(); high = Math.Min((byte)0x7F, high); if (!(low == 0 && high == 0x7F)) { sf2.AddInstrumentGenerator(SF2Generator.KeyRange, new SF2GeneratorAmount { LowByte = low, HighByte = high }); } // ADSR if (entry.ADSR.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 = entry.ADSR.A / 5d; double att = 1200 * Math.Log(att_time, 2); sf2.AddInstrumentGenerator(SF2Generator.AttackVolEnv, new SF2GeneratorAmount { Amount = (short)att }); } if (entry.ADSR.S != 15) { double sus; // Compute attenuation in cB if sustain is non-zero if (entry.ADSR.S != 0) { sus = 100 * Math.Log(15d / entry.ADSR.S); } // Special case where attenuation is infinite -> use max value else { sus = 1000; } sf2.AddInstrumentGenerator(SF2Generator.SustainVolEnv, new SF2GeneratorAmount { Amount = (short)sus }); double dec_time = entry.ADSR.D / 5d; double dec = 1200 * Math.Log(dec_time + 1, 2); sf2.AddInstrumentGenerator(SF2Generator.DecayVolEnv, new SF2GeneratorAmount { Amount = (short)dec }); } if (entry.ADSR.R != 0) { double rel_time = entry.ADSR.R / 5d; double rel = 1200 * Math.Log(rel_time, 2); sf2.AddInstrumentGenerator(SF2Generator.ReleaseVolEnv, new SF2GeneratorAmount { Amount = (short)rel }); } if (type == M4AVoiceType.Noise && entry.Panpot != 0) { sf2.AddInstrumentGenerator(SF2Generator.Pan, new SF2GeneratorAmount { Amount = (short)((entry.Panpot - 0xC0) * (500d / 0x80)) }); } sf2.AddInstrumentGenerator(SF2Generator.SampleModes, new SF2GeneratorAmount { Amount = 1 }); sf2.AddInstrumentGenerator(SF2Generator.SampleID, new SF2GeneratorAmount { Amount = (short)sample }); }