ChipDoc.cs
namespace PixelPusher;
public sealed class ChipDoc
{
public const int Channels = 4;
public const int Steps = 16;
public string Name { get; set; } = "cooler loop";
public int Bpm { get; set; } = 112;
public int Version { get; private set; }
public byte[] Notes { get; set; }
public ChipDoc()
{
Notes = new byte[Channels * Steps];
StockCoolerHook();
}
public byte Get( int ch, int step )
{
if ( (uint)ch >= Channels || (uint)step >= Steps )
return 0;
return Notes[ch * Steps + step];
}
public void Set( int ch, int step, byte note )
{
if ( (uint)ch >= Channels || (uint)step >= Steps )
return;
Notes[ch * Steps + step] = note;
Version++;
}
public void Toggle( int ch, int step )
{
var n = Get( ch, step );
Set( ch, step, n == 0 ? (byte)8 : (byte)0 );
}
public void Nudge( int ch, int step, int dir )
{
var n = Get( ch, step );
if ( n == 0 )
n = 8;
else
n = (byte)Math.Clamp( n + dir, 1, 12 );
Set( ch, step, n );
}
public void Clear()
{
Array.Clear( Notes );
Version++;
}
public ChipSave ToSave() => new()
{
Name = Name,
Bpm = Bpm,
Notes = PixelDoc.CopyBytes( Notes )
};
public static ChipDoc FromSave( ChipSave save )
{
var doc = new ChipDoc();
if ( save is null )
return doc;
if ( !string.IsNullOrWhiteSpace( save.Name ) )
doc.Name = save.Name;
if ( save.Bpm > 0 )
doc.Bpm = Math.Clamp( save.Bpm, 60, 240 );
if ( save.Notes is { Length: > 0 } )
{
var n = Math.Min( doc.Notes.Length, save.Notes.Length );
Array.Copy( save.Notes, doc.Notes, n );
}
return doc;
}
void StockCoolerHook()
{
// Pulse bass
Set( 0, 0, 5 );
Set( 0, 4, 5 );
Set( 0, 8, 8 );
Set( 0, 12, 5 );
// Triangle hook
Set( 1, 2, 10 );
Set( 1, 6, 12 );
Set( 1, 10, 10 );
Set( 1, 14, 8 );
// Noise hats
Set( 2, 4, 6 );
Set( 2, 12, 6 );
// Drum
Set( 3, 0, 8 );
Set( 3, 8, 8 );
Set( 3, 10, 4 );
}
}
public sealed class ChipSave
{
public string Name { get; set; }
public int Bpm { get; set; }
public byte[] Notes { get; set; }
}