diff --git a/VG Music Studio/Core/GBA/MP2K/Channel.cs b/VG Music Studio/Core/GBA/MP2K/Channel.cs index 669bb46..faa606d 100644 --- a/VG Music Studio/Core/GBA/MP2K/Channel.cs +++ b/VG Music Studio/Core/GBA/MP2K/Channel.cs @@ -11,6 +11,7 @@ internal abstract class Channel public Note Note; // Must be a struct & field protected ADSR _adsr; + protected int _instPan; protected byte _velocity; protected int _pos; @@ -83,7 +84,7 @@ internal class PCM8Channel : Channel private sbyte[] _decompressedSample; public PCM8Channel(Mixer mixer) : base(mixer) { } - public void Init(Track owner, Note note, ADSR adsr, int sampleOffset, byte vol, sbyte pan, int pitch, bool bFixed, bool bCompressed) + public void Init(Track owner, Note note, ADSR adsr, int sampleOffset, byte vol, sbyte pan, int instPan, int pitch, bool bFixed, bool bCompressed) { State = EnvelopeState.Initializing; _pos = 0; _interPos = 0; @@ -95,6 +96,7 @@ public void Init(Track owner, Note note, ADSR adsr, int sampleOffset, byte vol, Owner.Channels.Add(this); Note = note; _adsr = adsr; + _instPan = instPan; _sampleHeader = _mixer.Config.Reader.ReadObject(sampleOffset); _sampleOffset = sampleOffset + 0x10; _bFixed = bFixed; @@ -120,12 +122,21 @@ public override ChannelVolume GetVolume() } public override void SetVolume(byte vol, sbyte pan) { + int combinedPan = pan + _instPan; + if (combinedPan > 63) + { + combinedPan = 63; + } + else if (combinedPan < -64) + { + combinedPan = -64; + } const int fix = 0x2000; if (State < EnvelopeState.Releasing) { int a = Note.Velocity * vol; - _leftVol = (byte)(a * (-pan + 0x40) / fix); - _rightVol = (byte)(a * (pan + 0x40) / fix); + _leftVol = (byte)(a * (-combinedPan + 0x40) / fix); + _rightVol = (byte)(a * (combinedPan + 0x40) / fix); } } public override void SetPitch(int pitch) @@ -346,7 +357,7 @@ protected enum GBPan : byte protected GBPan _panpot = GBPan.Center; public PSGChannel(Mixer mixer) : base(mixer) { } - protected void Init(Track owner, Note note, ADSR env) + protected void Init(Track owner, Note note, ADSR env, int instPan) { State = EnvelopeState.Initializing; if (Owner != null) @@ -360,6 +371,7 @@ protected void Init(Track owner, Note note, ADSR env) _adsr.D = (byte)(env.D & 0x7); _adsr.S = (byte)(env.S & 0xF); _adsr.R = (byte)(env.R & 0x7); + _instPan = instPan; } public override void Release() @@ -424,9 +436,18 @@ public override ChannelVolume GetVolume() } public override void SetVolume(byte vol, sbyte pan) { + int combinedPan = pan + _instPan; + if (combinedPan > 63) + { + combinedPan = 63; + } + else if (combinedPan < -64) + { + combinedPan = -64; + } if (State < EnvelopeState.Releasing) { - _panpot = pan < -21 ? GBPan.Left : pan > 20 ? GBPan.Right : GBPan.Center; + _panpot = combinedPan < -21 ? GBPan.Left : combinedPan > 20 ? GBPan.Right : GBPan.Center; _peakVelocity = (byte)((Note.Velocity * vol) >> 10); _sustainVelocity = (byte)(((_peakVelocity * _adsr.S) + 0xF) >> 4); // TODO if (State == EnvelopeState.Playing) @@ -609,9 +630,9 @@ internal class SquareChannel : PSGChannel private float[] _pat; public SquareChannel(Mixer mixer) : base(mixer) { } - public void Init(Track owner, Note note, ADSR env, SquarePattern pattern) + public void Init(Track owner, Note note, ADSR env, int instPan, SquarePattern pattern) { - Init(owner, note, env); + Init(owner, note, env, instPan); switch (pattern) { default: _pat = Utils.SquareD12; break; @@ -657,9 +678,9 @@ internal class PCM4Channel : PSGChannel private float[] _sample; public PCM4Channel(Mixer mixer) : base(mixer) { } - public void Init(Track owner, Note note, ADSR env, int sampleOffset) + public void Init(Track owner, Note note, ADSR env, int instPan, int sampleOffset) { - Init(owner, note, env); + Init(owner, note, env, instPan); _sample = Utils.PCM4ToFloat(sampleOffset); } @@ -699,9 +720,9 @@ internal class NoiseChannel : PSGChannel private BitArray _pat; public NoiseChannel(Mixer mixer) : base(mixer) { } - public void Init(Track owner, Note note, ADSR env, NoisePattern pattern) + public void Init(Track owner, Note note, ADSR env, int instPan, NoisePattern pattern) { - Init(owner, note, env); + Init(owner, note, env, instPan); _pat = pattern == NoisePattern.Fine ? Utils.NoiseFine : Utils.NoiseRough; } diff --git a/VG Music Studio/Core/GBA/MP2K/Mixer.cs b/VG Music Studio/Core/GBA/MP2K/Mixer.cs index 2ee4fc7..738859f 100644 --- a/VG Music Studio/Core/GBA/MP2K/Mixer.cs +++ b/VG Music Studio/Core/GBA/MP2K/Mixer.cs @@ -62,7 +62,7 @@ public override void Dispose() CloseWaveWriter(); } - public PCM8Channel AllocPCM8Channel(Track owner, ADSR env, Note note, byte vol, sbyte pan, int pitch, bool bFixed, bool bCompressed, int sampleOffset) + public PCM8Channel AllocPCM8Channel(Track owner, ADSR env, Note note, byte vol, sbyte pan, int instPan, int pitch, bool bFixed, bool bCompressed, int sampleOffset) { PCM8Channel nChn = null; IOrderedEnumerable byOwner = _pcm8Channels.OrderByDescending(c => c.Owner == null ? 0xFF : c.Owner.Index); @@ -106,11 +106,11 @@ public PCM8Channel AllocPCM8Channel(Track owner, ADSR env, Note note, byte vol, } if (nChn != null) // Could still be null from the above if { - nChn.Init(owner, note, env, sampleOffset, vol, pan, pitch, bFixed, bCompressed); + nChn.Init(owner, note, env, sampleOffset, vol, pan, instPan, pitch, bFixed, bCompressed); } return nChn; } - public PSGChannel AllocPSGChannel(Track owner, ADSR env, Note note, byte vol, sbyte pan, int pitch, VoiceType type, object arg) + public PSGChannel AllocPSGChannel(Track owner, ADSR env, Note note, byte vol, sbyte pan, int instPan, int pitch, VoiceType type, object arg) { PSGChannel nChn; switch (type) @@ -122,7 +122,7 @@ public PSGChannel AllocPSGChannel(Track owner, ADSR env, Note note, byte vol, sb { return null; } - _sq1.Init(owner, note, env, (SquarePattern)arg); + _sq1.Init(owner, note, env, instPan, (SquarePattern)arg); break; } case VoiceType.Square2: @@ -132,7 +132,7 @@ public PSGChannel AllocPSGChannel(Track owner, ADSR env, Note note, byte vol, sb { return null; } - _sq2.Init(owner, note, env, (SquarePattern)arg); + _sq2.Init(owner, note, env, instPan, (SquarePattern)arg); break; } case VoiceType.PCM4: @@ -142,7 +142,7 @@ public PSGChannel AllocPSGChannel(Track owner, ADSR env, Note note, byte vol, sb { return null; } - _pcm4.Init(owner, note, env, (int)arg); + _pcm4.Init(owner, note, env, instPan, (int)arg); break; } case VoiceType.Noise: @@ -152,7 +152,7 @@ public PSGChannel AllocPSGChannel(Track owner, ADSR env, Note note, byte vol, sb { return null; } - _noise.Init(owner, note, env, (NoisePattern)arg); + _noise.Init(owner, note, env, instPan, (NoisePattern)arg); break; } default: return null; diff --git a/VG Music Studio/Core/GBA/MP2K/Player.cs b/VG Music Studio/Core/GBA/MP2K/Player.cs index 954b860..e00bd21 100644 --- a/VG Music Studio/Core/GBA/MP2K/Player.cs +++ b/VG Music Studio/Core/GBA/MP2K/Player.cs @@ -1043,6 +1043,8 @@ private void PlayNote(Track track, byte key, byte velocity, byte addedDuration) Key = fromDrum ? v.RootKey : key }; var type = (VoiceType)(v.Type & 0x7); + int instPan = v.Pan; + instPan = (instPan & 0x80) != 0 ? instPan - 0xC0 : 0; switch (type) { case VoiceType.PCM8: @@ -1050,7 +1052,7 @@ private void PlayNote(Track track, byte key, byte velocity, byte addedDuration) bool bFixed = (v.Type & (int)VoiceFlags.Fixed) != 0; bool bCompressed = _config.HasPokemonCompression && ((v.Type & (int)VoiceFlags.Compressed) != 0); _mixer.AllocPCM8Channel(track, v.ADSR, note, - track.GetVolume(), track.GetPanpot(), track.GetPitch(), + track.GetVolume(), track.GetPanpot(), instPan, track.GetPitch(), bFixed, bCompressed, v.Int4 - GBA.Utils.CartridgeOffset); return; } @@ -1058,21 +1060,21 @@ private void PlayNote(Track track, byte key, byte velocity, byte addedDuration) case VoiceType.Square2: { _mixer.AllocPSGChannel(track, v.ADSR, note, - track.GetVolume(), track.GetPanpot(), track.GetPitch(), + track.GetVolume(), track.GetPanpot(), instPan, track.GetPitch(), type, (SquarePattern)v.Int4); return; } case VoiceType.PCM4: { _mixer.AllocPSGChannel(track, v.ADSR, note, - track.GetVolume(), track.GetPanpot(), track.GetPitch(), + track.GetVolume(), track.GetPanpot(), instPan, track.GetPitch(), type, v.Int4 - GBA.Utils.CartridgeOffset); return; } case VoiceType.Noise: { _mixer.AllocPSGChannel(track, v.ADSR, note, - track.GetVolume(), track.GetPanpot(), track.GetPitch(), + track.GetVolume(), track.GetPanpot(), instPan, track.GetPitch(), type, (NoisePattern)v.Int4); return; } diff --git a/VG Music Studio/Core/GBA/MP2K/Structs.cs b/VG Music Studio/Core/GBA/MP2K/Structs.cs index 19502c6..2da7d2c 100644 --- a/VG Music Studio/Core/GBA/MP2K/Structs.cs +++ b/VG Music Studio/Core/GBA/MP2K/Structs.cs @@ -24,8 +24,7 @@ internal class VoiceEntry public byte Type { get; set; } // 0 public byte RootKey { get; set; } // 1 public byte Unknown { get; set; } // 2 - /// Panpot for PCM8/Noise, Sweep for Square1 - public byte Byte3 { get; set; } // 3 + public byte Pan { get; set; } // 3 /// SquarePattern for Square1/Square2, NoisePattern for Noise, Address for PCM8/PCM4/KeySplit/Drum public int Int4 { get; set; } // 4 /// ADSR for PCM8/Square1/Square2/PCM4/Noise, KeysAddress for KeySplit