Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PSF Engine #61

Draft
wants to merge 25 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion VG Music Studio/Core/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ public enum EngineType : byte
GBA_AlphaDream,
GBA_MP2K,
NDS_DSE,
NDS_SDAT
NDS_SDAT,
PSX_PSF
}

public static Engine Instance { get; private set; }
Expand Down Expand Up @@ -72,6 +73,16 @@ public Engine(EngineType type, object playerArg)
Player = new NDS.SDAT.Player(mixer, config);
break;
}
case EngineType.PSX_PSF:
{
string bgmPath = (string)playerArg;
var config = new PSX.PSF.Config(bgmPath);
Config = config;
var mixer = new PSX.PSF.Mixer();
Mixer = mixer;
Player = new PSX.PSF.Player(mixer, config);
break;
}
default: throw new ArgumentOutOfRangeException(nameof(type));
}
Type = type;
Expand Down
136 changes: 136 additions & 0 deletions VG Music Studio/Core/PSX/PSF/Channel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
namespace Kermalis.VGMusicStudio.Core.PSX.PSF
{
internal class Channel
{
public readonly byte Index;

public Track Owner;
public ushort BaseTimer = NDS.Utils.ARM7_CLOCK / 44100;
public ushort Timer;
public byte NoteVelocity;
public byte Volume = 0x7F;
public sbyte Pan = 0x40;
public byte BaseKey, Key;
public byte PitchTune;

private int _pos;
private short _prevLeft;
private short _prevRight;

private long _dataOffset;
private long _loopOffset;
private short[] _decompressedSample;

public Channel(byte i)
{
Index = i;
}

private static readonly float[][] _idk = new float[5][]
{
new float[2] { 0f, 0f },
new float[2] { 60f / 64f, 0f },
new float[2] { 115f / 64f, 52f / 64f },
new float[2] { 98f / 64f, 55f / 64f },
new float[2] { 122f / 64f, 60f / 64f }
};
public void Start(long sampleOffset, long sampleSize, byte[] exeBuffer)
{
Stop();
//State = EnvelopeState.Attack;
//Velocity = -92544;
_pos = 0;
_prevLeft = _prevRight = 0;
_loopOffset = 0;
_dataOffset = 0;
float prev1 = 0, prev2 = 0;
_decompressedSample = new short[0x50000];
for (long i = 0; i < sampleSize; i += 16)
{
byte b0 = exeBuffer[sampleOffset + i];
byte b1 = exeBuffer[sampleOffset + i + 1];
int range = b0 & 0xF;
int filter = (b0 & 0xF0) >> 4;
bool end = (b1 & 0x1) != 0;
bool looping = (b1 & 0x2) != 0;
bool loop = (b1 & 0x4) != 0;

// Decomp
long pi = i * 28 / 16;
int shift = range + 16;
for (int j = 0; j < 14; j++)
{
sbyte bj = (sbyte)exeBuffer[sampleOffset + i + 2 + j];
_decompressedSample[pi + (j * 2)] = (short)((bj << 28) >> shift);
_decompressedSample[pi + (j * 2) + 1] = (short)(((bj & 0xF0) << 24) >> shift);
}
if (filter == 0)
{
prev1 = _decompressedSample[pi + 27];
prev2 = _decompressedSample[pi + 26];
}
else
{
float f1 = _idk[filter][0];
float f2 = _idk[filter][1];
float p1 = prev1;
float p2 = prev2;
for (int j = 0; j < 28; j++)
{
float t = _decompressedSample[pi + j] + (p1 * f1) - (p2 * f2);
_decompressedSample[pi + j] = (short)t;
p2 = p1;
p1 = t;
}
prev1 = p1;
prev2 = p2;
}
}
}

public void Stop()
{
if (Owner != null)
{
Owner.Channels.Remove(this);
}
Owner = null;
Volume = 0;
}

public void Process(out short left, out short right)
{
if (Timer != 0)
{
int numSamples = (_pos + 0x100) / Timer;
_pos = (_pos + 0x100) % Timer;
// prevLeft and prevRight are stored because numSamples can be 0.
for (int i = 0; i < numSamples; i++)
{
short samp;
// If hit end
if (_dataOffset >= _decompressedSample.Length)
{
if (true)
//if (swav.DoesLoop)
{
_dataOffset = _loopOffset;
}
else
{
left = right = _prevLeft = _prevRight = 0;
Stop();
return;
}
}
samp = _decompressedSample[_dataOffset++];
samp = (short)(samp * Volume / 0x7F);
_prevLeft = (short)(samp * (-Pan + 0x40) / 0x80);
_prevRight = (short)(samp * (Pan + 0x40) / 0x80);
}
}
left = _prevLeft;
right = _prevRight;
}
}
}
54 changes: 54 additions & 0 deletions VG Music Studio/Core/PSX/PSF/Commands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Drawing;

namespace Kermalis.VGMusicStudio.Core.PSX.PSF
{
internal class ControllerCommand : ICommand
{
public Color Color => Color.MediumVioletRed;
public string Label => "Controller";
public string Arguments => $"{Controller}, {Value}";

public byte Controller { get; set; }
public byte Value { get; set; }
}
internal class FinishCommand : ICommand
{
public Color Color => Color.MediumSpringGreen;
public string Label => "Finish";
public string Arguments => string.Empty;
}
internal class NoteCommand : ICommand
{
public Color Color => Color.SkyBlue;
public string Label => $"Note {(Velocity == 0 ? "Off" : "On")}";
public string Arguments => $"{Util.Utils.GetNoteName(Key)}{(Velocity == 0 ? string.Empty : $", {Velocity}")}";

public byte Key { get; set; }
public byte Velocity { get; set; }
}
internal class PitchBendCommand : ICommand
{
public Color Color => Color.MediumPurple;
public string Label => "Pitch Bend";
public string Arguments => $"0x{Bend1:X} 0x{Bend2:X}";

public byte Bend1 { get; set; }
public byte Bend2 { get; set; }
}
internal class TempoCommand : ICommand
{
public Color Color => Color.DeepSkyBlue;
public string Label => "Tempo";
public string Arguments => Tempo.ToString();

public uint Tempo { get; set; }
}
internal class VoiceCommand : ICommand
{
public Color Color => Color.DarkSalmon;
public string Label => "Voice";
public string Arguments => Voice.ToString();

public byte Voice { get; set; }
}
}
37 changes: 37 additions & 0 deletions VG Music Studio/Core/PSX/PSF/Config.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Kermalis.VGMusicStudio.Properties;
using System;
using System.IO;
using System.Linq;

namespace Kermalis.VGMusicStudio.Core.PSX.PSF
{
internal class Config : Core.Config
{
public readonly string BGMPath;
public readonly string[] BGMFiles;

public Config(string bgmPath)
{
BGMPath = bgmPath;
BGMFiles = Directory.EnumerateFiles(bgmPath).Where(f => f.EndsWith(".minipsf", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".psf", StringComparison.OrdinalIgnoreCase)).ToArray();
if (BGMFiles.Length == 0)
{
throw new Exception(Strings.ErrorDSENoSequences);
}
var songs = new Song[BGMFiles.Length];
for (int i = 0; i < BGMFiles.Length; i++)
{
// TODO: Read title from tag
songs[i] = new Song(i, Path.GetFileNameWithoutExtension(BGMFiles[i]));
}
Playlists.Add(new Playlist(Strings.PlaylistMusic, songs));
}

public override string GetSongName(long index)
{
return index < 0 || index >= BGMFiles.Length
? index.ToString()
: '\"' + BGMFiles[index] + '\"';
}
}
}
Loading