From aedbd7be0261b99a0fbf37c03d69f7ac33c55541 Mon Sep 17 00:00:00 2001 From: Kermalis <29823718+Kermalis@users.noreply.github.com> Date: Fri, 11 Sep 2020 08:41:47 -0400 Subject: [PATCH] Update after since ages since a long time --- SoundFont2.sln | 3 - SoundFont2/SF2.cs | 102 ++-- SoundFont2/SF2Chunks.cs | 897 +++++++++++++++++++++-------------- SoundFont2/SF2Types.cs | 45 +- SoundFont2/SF2Utils.cs | 15 - SoundFont2/SoundFont2.csproj | 19 +- 6 files changed, 618 insertions(+), 463 deletions(-) delete mode 100644 SoundFont2/SF2Utils.cs diff --git a/SoundFont2.sln b/SoundFont2.sln index 6a791d7..b8d3f51 100644 --- a/SoundFont2.sln +++ b/SoundFont2.sln @@ -7,12 +7,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoundFont2", "SoundFont2\So EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3C554511-03EA-41F0-AA5D-BABB9C330A33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3C554511-03EA-41F0-AA5D-BABB9C330A33}.Debug|Any CPU.Build.0 = Debug|Any CPU {3C554511-03EA-41F0-AA5D-BABB9C330A33}.Release|Any CPU.ActiveCfg = Release|Any CPU {3C554511-03EA-41F0-AA5D-BABB9C330A33}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection diff --git a/SoundFont2/SF2.cs b/SoundFont2/SF2.cs index 9db36da..1811dc1 100644 --- a/SoundFont2/SF2.cs +++ b/SoundFont2/SF2.cs @@ -1,16 +1,16 @@ -using System.IO; -using System.Text; +using Kermalis.EndianBinaryIO; +using System.IO; namespace Kermalis.SoundFont2 { public sealed class SF2 { - uint size; - public readonly InfoListChunk InfoChunk; - public readonly SdtaListChunk SoundChunk; - public readonly PdtaListChunk HydraChunk; + private uint _size; + public InfoListChunk InfoChunk { get; } + public SdtaListChunk SoundChunk { get; } + public PdtaListChunk HydraChunk { get; } - // For creating + /// For creating public SF2() { InfoChunk = new InfoListChunk(this); @@ -18,20 +18,22 @@ public SF2() HydraChunk = new PdtaListChunk(this); } - // For reading + /// For reading public SF2(string path) { - using (var reader = new BinaryReader(File.Open(path, FileMode.Open), Encoding.ASCII)) + using (var reader = new EndianBinaryReader(File.Open(path, FileMode.Open))) { - char[] chars = reader.ReadChars(4); - if (new string(chars) != "RIFF") + string str = reader.ReadString(4, false); + if (str != "RIFF") + { throw new InvalidDataException("RIFF header was not found at the start of the file."); - - size = reader.ReadUInt32(); - chars = reader.ReadChars(4); - if (new string(chars) != "sfbk") + } + _size = reader.ReadUInt32(); + str = reader.ReadString(4, false); + if (str != "sfbk") + { throw new InvalidDataException("sfbk header was not found at the expected offset."); - + } InfoChunk = new InfoListChunk(this, reader); SoundChunk = new SdtaListChunk(this, reader); HydraChunk = new PdtaListChunk(this, reader); @@ -40,13 +42,13 @@ public SF2(string path) public void Save(string path) { - using (var writer = new BinaryWriter(File.Open(path, FileMode.Create), Encoding.ASCII)) + using (var writer = new EndianBinaryWriter(File.Open(path, FileMode.Create))) { AddTerminals(); - writer.Write("RIFF".ToCharArray()); - writer.Write(size); - writer.Write("sfbk".ToCharArray()); + writer.Write("RIFF", 4); + writer.Write(_size); + writer.Write("sfbk", 4); InfoChunk.Write(writer); SoundChunk.Write(writer); @@ -55,7 +57,7 @@ public void Save(string path) } - // Returns sample index + /// Returns sample index public uint AddSample(short[] pcm16, string name, bool bLoop, uint loopPos, uint sampleRate, byte originalKey, sbyte pitchCorrection) { uint start = SoundChunk.SMPLSubChunk.AddSample(pcm16, bLoop, loopPos); @@ -76,14 +78,10 @@ public uint AddSample(short[] pcm16, string name, bool bLoop, uint loopPos, uint return AddSampleHeader(name, start, end, loopStart, loopEnd, sampleRate, originalKey, pitchCorrection); } - // Returns instrument index + /// Returns instrument index public uint AddInstrument(string name) { - return HydraChunk.INSTSubChunk.AddInstrument(new SF2Instrument(this) - { - InstrumentName = name, - InstrumentBagIndex = (ushort)HydraChunk.IBAGSubChunk.Count - }); + return HydraChunk.INSTSubChunk.AddInstrument(new SF2Instrument(name, (ushort)HydraChunk.IBAGSubChunk.Count)); } public void AddInstrumentBag() { @@ -91,29 +89,19 @@ public void AddInstrumentBag() } public void AddInstrumentModulator() { - HydraChunk.IMODSubChunk.AddModulator(new SF2ModulatorList(this)); + HydraChunk.IMODSubChunk.AddModulator(new SF2ModulatorList()); } public void AddInstrumentGenerator() { - HydraChunk.IGENSubChunk.AddGenerator(new SF2GeneratorList(this)); + HydraChunk.IGENSubChunk.AddGenerator(new SF2GeneratorList()); } public void AddInstrumentGenerator(SF2Generator generator, SF2GeneratorAmount amount) { - HydraChunk.IGENSubChunk.AddGenerator(new SF2GeneratorList(this) - { - Generator = generator, - GeneratorAmount = amount - }); + HydraChunk.IGENSubChunk.AddGenerator(new SF2GeneratorList(generator, amount)); } public void AddPreset(string name, ushort preset, ushort bank) { - HydraChunk.PHDRSubChunk.AddPreset(new SF2PresetHeader(this) - { - PresetName = name, - Preset = preset, - Bank = bank, - PresetBagIndex = (ushort)HydraChunk.PBAGSubChunk.Count - }); + HydraChunk.PHDRSubChunk.AddPreset(new SF2PresetHeader(name, preset, bank, (ushort)HydraChunk.PBAGSubChunk.Count)); } public void AddPresetBag() { @@ -121,36 +109,22 @@ public void AddPresetBag() } public void AddPresetModulator() { - HydraChunk.PMODSubChunk.AddModulator(new SF2ModulatorList(this)); + HydraChunk.PMODSubChunk.AddModulator(new SF2ModulatorList()); } public void AddPresetGenerator() { - HydraChunk.PGENSubChunk.AddGenerator(new SF2GeneratorList(this)); + HydraChunk.PGENSubChunk.AddGenerator(new SF2GeneratorList()); } public void AddPresetGenerator(SF2Generator generator, SF2GeneratorAmount amount) { - HydraChunk.PGENSubChunk.AddGenerator(new SF2GeneratorList(this) - { - Generator = generator, - GeneratorAmount = amount - }); + HydraChunk.PGENSubChunk.AddGenerator(new SF2GeneratorList(generator, amount)); } - uint AddSampleHeader(string name, uint start, uint end, uint loopStart, uint loopEnd, uint sampleRate, byte originalKey, sbyte pitchCorrection) + private uint AddSampleHeader(string name, uint start, uint end, uint loopStart, uint loopEnd, uint sampleRate, byte originalKey, sbyte pitchCorrection) { - return HydraChunk.SHDRSubChunk.AddSample(new SF2SampleHeader(this) - { - SampleName = name, - Start = start, - End = end, - LoopStart = loopStart, - LoopEnd = loopEnd, - SampleRate = sampleRate, - OriginalKey = originalKey, - PitchCorrection = pitchCorrection - }); - } - void AddTerminals() + return HydraChunk.SHDRSubChunk.AddSample(new SF2SampleHeader(name, start, end, loopStart, loopEnd, sampleRate, originalKey, pitchCorrection)); + } + private void AddTerminals() { AddSampleHeader("EOS", 0, 0, 0, 0, 0, 0, 0); AddInstrument("EOI"); @@ -166,8 +140,10 @@ void AddTerminals() internal void UpdateSize() { if (InfoChunk == null || SoundChunk == null || HydraChunk == null) + { return; - size = 4 + } + _size = 4 + InfoChunk.UpdateSize() + 8 + SoundChunk.UpdateSize() + 8 + HydraChunk.UpdateSize() + 8; diff --git a/SoundFont2/SF2Chunks.cs b/SoundFont2/SF2Chunks.cs index e1e6f22..9601304 100644 --- a/SoundFont2/SF2Chunks.cs +++ b/SoundFont2/SF2Chunks.cs @@ -1,277 +1,288 @@ -using System; +using Kermalis.EndianBinaryIO; +using System; using System.Collections.Generic; -using System.IO; -using System.Linq; namespace Kermalis.SoundFont2 { public class SF2Chunk { - protected readonly SF2 sf2; + protected readonly SF2 _sf2; - readonly char[] chunkName; // Length 4 - public string ChunkName => new string(chunkName); - public uint Size { get; protected set; } // Size in bytes + /// Length 4 + public string ChunkName { get; } + /// Size in bytes + public uint Size { get; protected set; } protected SF2Chunk(SF2 inSf2, string name) { - sf2 = inSf2; - chunkName = name.ToCharArray(); + _sf2 = inSf2; + ChunkName = name; } - protected SF2Chunk(SF2 inSf2, BinaryReader reader) + protected SF2Chunk(SF2 inSf2, EndianBinaryReader reader) { - sf2 = inSf2; - chunkName = reader.ReadChars(4); + _sf2 = inSf2; + ChunkName = reader.ReadString(4, false); Size = reader.ReadUInt32(); } - internal virtual void Write(BinaryWriter writer) + + internal virtual void Write(EndianBinaryWriter writer) { - writer.Write(chunkName); + writer.Write(ChunkName, 4); writer.Write(Size); } } public abstract class SF2ListChunk : SF2Chunk { - readonly char[] listChunkName; // Length 4 - public string ListChunkName => new string(listChunkName); + ///Length 4 + public string ListChunkName { get; } protected SF2ListChunk(SF2 inSf2, string name) : base(inSf2, "LIST") { - listChunkName = name.ToCharArray(); + ListChunkName = name; Size = 4; } - protected SF2ListChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + protected SF2ListChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { - listChunkName = reader.ReadChars(4); + ListChunkName = reader.ReadString(4, false); } - internal override void Write(BinaryWriter writer) + + internal abstract uint UpdateSize(); + + internal override void Write(EndianBinaryWriter writer) { base.Write(writer); - writer.Write(listChunkName); + writer.Write(ListChunkName, 4); } - - internal abstract uint UpdateSize(); } public sealed class SF2PresetHeader { public const uint Size = 38; - readonly SF2 sf2; - char[] presetName; // Length 20 - public string PresetName - { - get => new string(presetName).TrimEnd('\0'); - set => SF2Utils.TruncateOrNot(value, 20, ref presetName); - } - public ushort Preset, Bank, PresetBagIndex; + /// Length 20 + public string PresetName { get; set; } + public ushort Preset { get; set; } + public ushort Bank { get; set; } + public ushort PresetBagIndex { get; set; } // Reserved for future implementations - readonly uint library = 0, genre = 0, morphology = 0; + private readonly uint _library; + private readonly uint _genre; + private readonly uint _morphology; - internal SF2PresetHeader(SF2 inSf2) + internal SF2PresetHeader(string name, ushort preset, ushort bank, ushort index) { - sf2 = inSf2; - presetName = new char[20]; + PresetName = name; + Preset = preset; + Bank = bank; + PresetBagIndex = index; } - internal SF2PresetHeader(SF2 inSf2, BinaryReader reader) + internal SF2PresetHeader(EndianBinaryReader reader) { - sf2 = inSf2; - presetName = reader.ReadChars(20); + PresetName = reader.ReadString(20, true); Preset = reader.ReadUInt16(); Bank = reader.ReadUInt16(); PresetBagIndex = reader.ReadUInt16(); - library = reader.ReadUInt32(); - genre = reader.ReadUInt32(); - morphology = reader.ReadUInt32(); + _library = reader.ReadUInt32(); + _genre = reader.ReadUInt32(); + _morphology = reader.ReadUInt32(); } - internal void Write(BinaryWriter writer) + + internal void Write(EndianBinaryWriter writer) { - writer.Write(presetName); + writer.Write(PresetName, 20); writer.Write(Preset); writer.Write(Bank); writer.Write(PresetBagIndex); - writer.Write(library); - writer.Write(genre); - writer.Write(morphology); + writer.Write(_library); + writer.Write(_genre); + writer.Write(_morphology); } - public override string ToString() => $"Preset Header - Bank = {Bank}, " + - $"\nPreset = {Preset}, " + - $"\nName = \"{PresetName}\""; + public override string ToString() + { + return $"Preset Header - Bank = {Bank}" + + $",\nPreset = {Preset}" + + $",\nName = \"{PresetName}\""; + } } - // Covers sfPresetBag and sfInstBag + /// Covers sfPresetBag and sfInstBag public sealed class SF2Bag { public const uint Size = 4; - readonly SF2 sf2; - public ushort GeneratorIndex; // Index in list of generators - public ushort ModulatorIndex; // Index in list of modulators + /// Index in list of generators + public ushort GeneratorIndex { get; set; } + /// Index in list of modulators + public ushort ModulatorIndex { get; set; } - internal SF2Bag(SF2 inSf2, bool preset) + internal SF2Bag(SF2 inSf2, bool isPresetBag) { - sf2 = inSf2; - if (preset) + if (isPresetBag) { - GeneratorIndex = (ushort)sf2.HydraChunk.PGENSubChunk.Count; - ModulatorIndex = (ushort)sf2.HydraChunk.PMODSubChunk.Count; + GeneratorIndex = (ushort)inSf2.HydraChunk.PGENSubChunk.Count; + ModulatorIndex = (ushort)inSf2.HydraChunk.PMODSubChunk.Count; } else { - GeneratorIndex = (ushort)sf2.HydraChunk.IGENSubChunk.Count; - ModulatorIndex = (ushort)sf2.HydraChunk.IMODSubChunk.Count; + GeneratorIndex = (ushort)inSf2.HydraChunk.IGENSubChunk.Count; + ModulatorIndex = (ushort)inSf2.HydraChunk.IMODSubChunk.Count; } } - internal SF2Bag(SF2 inSf2, BinaryReader reader) + internal SF2Bag(EndianBinaryReader reader) { - sf2 = inSf2; GeneratorIndex = reader.ReadUInt16(); ModulatorIndex = reader.ReadUInt16(); } - internal void Write(BinaryWriter writer) + + internal void Write(EndianBinaryWriter writer) { writer.Write(GeneratorIndex); writer.Write(ModulatorIndex); } - public override string ToString() => $"Bag - Generator index = {GeneratorIndex}, " + - $"\nModulator index = {ModulatorIndex}"; + public override string ToString() + { + return $"Bag - Generator index = {GeneratorIndex}" + + $",\nModulator index = {ModulatorIndex}"; + } } - // Covers sfModList and sfInstModList + /// Covers sfModList and sfInstModList public sealed class SF2ModulatorList { public const uint Size = 10; - readonly SF2 sf2; - public SF2Modulator ModulatorSource; - public SF2Generator ModulatorDestination; - public short ModulatorAmount; - public SF2Modulator ModulatorAmountSource; - public SF2Transform ModulatorTransform; + public SF2Modulator ModulatorSource { get; set; } + public SF2Generator ModulatorDestination { get; set; } + public short ModulatorAmount { get; set; } + public SF2Modulator ModulatorAmountSource { get; set; } + public SF2Transform ModulatorTransform { get; set; } - internal SF2ModulatorList(SF2 inSf2) - { - sf2 = inSf2; - } - internal SF2ModulatorList(SF2 inSf2, BinaryReader reader) + internal SF2ModulatorList() { } + internal SF2ModulatorList(EndianBinaryReader reader) { - sf2 = inSf2; - ModulatorSource = (SF2Modulator)reader.ReadUInt16(); - ModulatorDestination = (SF2Generator)reader.ReadUInt16(); + ModulatorSource = reader.ReadEnum(); + ModulatorDestination = reader.ReadEnum(); ModulatorAmount = reader.ReadInt16(); - ModulatorAmountSource = (SF2Modulator)reader.ReadUInt16(); - ModulatorTransform = (SF2Transform)reader.ReadUInt16(); + ModulatorAmountSource = reader.ReadEnum(); + ModulatorTransform = reader.ReadEnum(); } - internal void Write(BinaryWriter writer) + + internal void Write(EndianBinaryWriter writer) { - writer.Write((ushort)ModulatorSource); - writer.Write((ushort)ModulatorDestination); + writer.Write(ModulatorSource); + writer.Write(ModulatorDestination); writer.Write(ModulatorAmount); - writer.Write((ushort)ModulatorAmountSource); - writer.Write((ushort)ModulatorTransform); + writer.Write(ModulatorAmountSource); + writer.Write(ModulatorTransform); } - public override string ToString() => $"Modulator List - Modulator source = {ModulatorSource}, " + - $"\nModulator destination = {ModulatorDestination}, " + - $"\nModulator amount = {ModulatorAmount}, " + - $"\nModulator amount source = {ModulatorAmountSource}, " + - $"\nModulator transform = {ModulatorTransform}"; + public override string ToString() + { + return $"Modulator List - Modulator source = {ModulatorSource}" + + $",\nModulator destination = {ModulatorDestination}" + + $",\nModulator amount = {ModulatorAmount}" + + $",\nModulator amount source = {ModulatorAmountSource}" + + $",\nModulator transform = {ModulatorTransform}"; + } } public sealed class SF2GeneratorList { public const uint Size = 4; - readonly SF2 sf2; - public SF2Generator Generator; - public SF2GeneratorAmount GeneratorAmount; + public SF2Generator Generator { get; set; } + public SF2GeneratorAmount GeneratorAmount { get; set; } - internal SF2GeneratorList(SF2 inSf2) + internal SF2GeneratorList() { } + internal SF2GeneratorList(SF2Generator generator, SF2GeneratorAmount amount) { - sf2 = inSf2; + Generator = generator; + GeneratorAmount = amount; } - internal SF2GeneratorList(SF2 inSf2, BinaryReader reader) + internal SF2GeneratorList(EndianBinaryReader reader) { - sf2 = inSf2; - Generator = (SF2Generator)reader.ReadUInt16(); - GeneratorAmount = new SF2GeneratorAmount { UAmount = reader.ReadUInt16() }; + Generator = reader.ReadEnum(); + GeneratorAmount = new SF2GeneratorAmount { Amount = reader.ReadInt16() }; } - public void Write(BinaryWriter writer) + + public void Write(EndianBinaryWriter writer) { - writer.Write((ushort)Generator); - writer.Write(GeneratorAmount.UAmount); + writer.Write(Generator); + writer.Write(GeneratorAmount.Amount); } - public override string ToString() => $"Generator List - Generator = {Generator}, " + - $"\nGenerator amount = \"{GeneratorAmount}\""; + public override string ToString() + { + return $"Generator List - Generator = {Generator}" + + $",\nGenerator amount = \"{GeneratorAmount}\""; + } } public sealed class SF2Instrument { public const uint Size = 22; - readonly SF2 sf2; - char[] instrumentName; // Length 20 - public string InstrumentName - { - get => new string(instrumentName).TrimEnd('\0'); - set => SF2Utils.TruncateOrNot(value, 20, ref instrumentName); - } - public ushort InstrumentBagIndex; + /// Length 20 + public string InstrumentName { get; set; } + public ushort InstrumentBagIndex { get; set; } - internal SF2Instrument(SF2 inSf2) + internal SF2Instrument(string name, ushort index) { - sf2 = inSf2; - instrumentName = new char[20]; + InstrumentName = name; + InstrumentBagIndex = index; } - internal SF2Instrument(SF2 inSf2, BinaryReader reader) + internal SF2Instrument(EndianBinaryReader reader) { - sf2 = inSf2; - instrumentName = reader.ReadChars(20); + InstrumentName = reader.ReadString(20, true); InstrumentBagIndex = reader.ReadUInt16(); } - internal void Write(BinaryWriter writer) + + internal void Write(EndianBinaryWriter writer) { - writer.Write(instrumentName); + writer.Write(InstrumentName, 20); writer.Write(InstrumentBagIndex); } - public override string ToString() => $"Instrument - Name = \"{InstrumentName}\""; + public override string ToString() + { + return $"Instrument - Name = \"{InstrumentName}\""; + } } public sealed class SF2SampleHeader { public const uint Size = 46; - readonly SF2 sf2; - - char[] sampleName; // Length 20 - public string SampleName - { - get => new string(sampleName).TrimEnd('\0'); - set => SF2Utils.TruncateOrNot(value, 20, ref sampleName); - } - public uint Start; - public uint End; - public uint LoopStart; - public uint LoopEnd; - public uint SampleRate; - public byte OriginalKey; - public sbyte PitchCorrection; - public ushort SampleLink; - public SF2SampleLink SampleType = SF2SampleLink.MonoSample; - internal SF2SampleHeader(SF2 inSf2) - { - sf2 = inSf2; - sampleName = new char[20]; - } - internal SF2SampleHeader(SF2 inSf2, BinaryReader reader) - { - sf2 = inSf2; - sampleName = reader.ReadChars(20); + /// Length 20 + public string SampleName { get; set; } + public uint Start { get; set; } + public uint End { get; set; } + public uint LoopStart { get; set; } + public uint LoopEnd { get; set; } + public uint SampleRate { get; set; } + public byte OriginalKey { get; set; } + public sbyte PitchCorrection { get; set; } + public ushort SampleLink { get; set; } + public SF2SampleLink SampleType { get; set; } + + internal SF2SampleHeader(string name, uint start, uint end, uint loopStart, uint loopEnd, uint sampleRate, byte originalKey, sbyte pitchCorrection) + { + SampleName = name; + Start = start; + End = end; + LoopStart = loopStart; + LoopEnd = loopEnd; + SampleRate = sampleRate; + OriginalKey = originalKey; + PitchCorrection = pitchCorrection; + SampleType = SF2SampleLink.MonoSample; + } + internal SF2SampleHeader(EndianBinaryReader reader) + { + SampleName = reader.ReadString(20, true); Start = reader.ReadUInt32(); End = reader.ReadUInt32(); LoopStart = reader.ReadUInt32(); @@ -280,11 +291,12 @@ internal SF2SampleHeader(SF2 inSf2, BinaryReader reader) OriginalKey = reader.ReadByte(); PitchCorrection = reader.ReadSByte(); SampleLink = reader.ReadUInt16(); - SampleType = (SF2SampleLink)reader.ReadUInt16(); + SampleType = reader.ReadEnum(); } - internal void Write(BinaryWriter writer) + + internal void Write(EndianBinaryWriter writer) { - writer.Write(sampleName); + writer.Write(SampleName, 20); writer.Write(Start); writer.Write(End); writer.Write(LoopStart); @@ -293,113 +305,117 @@ internal void Write(BinaryWriter writer) writer.Write(OriginalKey); writer.Write(PitchCorrection); writer.Write(SampleLink); - writer.Write((ushort)SampleType); + writer.Write(SampleType); } - public override string ToString() => $"Sample - Name = \"{SampleName}\", " + - $"\nType = {SampleType}"; + public override string ToString() + { + return $"Sample - Name = \"{SampleName}\"" + + $",\nType = {SampleType}"; + } } #region Sub-Chunks public sealed class VersionSubChunk : SF2Chunk { - public SF2VersionTag Version; + public SF2VersionTag Version { get; set; } internal VersionSubChunk(SF2 inSf2, string subChunkName) : base(inSf2, subChunkName) { Size = SF2VersionTag.Size; - sf2.UpdateSize(); + inSf2.UpdateSize(); } - internal VersionSubChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal VersionSubChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { - Version = new SF2VersionTag(reader.ReadUInt16(), reader.ReadUInt16()); + Version = new SF2VersionTag(reader); } - internal override void Write(BinaryWriter writer) + + internal override void Write(EndianBinaryWriter writer) { base.Write(writer); - writer.Write(Version.Major); - writer.Write(Version.Minor); + Version.Write(writer); } - public override string ToString() => $"Version Chunk - Revision = {Version}"; + public override string ToString() + { + return $"Version Chunk - Revision = {Version}"; + } } public sealed class HeaderSubChunk : SF2Chunk { - readonly int maxSize; - - char[] field; + public int MaxSize { get; } + private int _fieldTargetLength; + private string _field; + /// Length public string Field { - get => new string(field).TrimEnd('\0'); + get => _field; set { - var strAsList = value.ToCharArray().ToList(); - if (strAsList.Count >= maxSize) // Input too long; cut it down + if (value.Length >= MaxSize) // Input too long; cut it down { - strAsList = strAsList.Take(maxSize).ToList(); - strAsList[maxSize - 1] = '\0'; + _fieldTargetLength = MaxSize; } - else if (strAsList.Count % 2 == 0) // Even amount of characters + else if (value.Length % 2 == 0) // Even amount of characters { - strAsList.Add('\0'); // Add two null-terminators to keep the byte count even - strAsList.Add('\0'); + _fieldTargetLength = value.Length + 2; // Add two null-terminators to keep the byte count even } else // Odd amount of characters { - strAsList.Add('\0'); // Add one null-terminator since that would make byte the count even + _fieldTargetLength = value.Length + 1; // Add one null-terminator since that would make byte the count even } - field = strAsList.ToArray(); - Size = (uint)field.Length; - sf2.UpdateSize(); + _field = value; + Size = (uint)_fieldTargetLength; + _sf2.UpdateSize(); } } internal HeaderSubChunk(SF2 inSf2, string subChunkName, int maxSize = 0x100) : base(inSf2, subChunkName) { - this.maxSize = maxSize; + MaxSize = maxSize; } - internal HeaderSubChunk(SF2 inSf2, BinaryReader reader, int maxSize = 0x100) : base(inSf2, reader) + internal HeaderSubChunk(SF2 inSf2, EndianBinaryReader reader, int maxSize = 0x100) : base(inSf2, reader) { - this.maxSize = maxSize; - field = reader.ReadChars((int)Size); + MaxSize = maxSize; + _field = reader.ReadString((int)Size, true); } - internal override void Write(BinaryWriter writer) + + internal override void Write(EndianBinaryWriter writer) { base.Write(writer); - writer.Write(field); + writer.Write(_field, _fieldTargetLength); } - public override string ToString() => $"Header Chunk - Name = \"{ChunkName}\", " + - $"\nField Max Size = {maxSize}, " + - $"\nField = \"{Field}\""; + public override string ToString() + { + return $"Header Chunk - Name = \"{ChunkName}\"" + + $",\nField Max Size = {MaxSize}" + + $",\nField = \"{Field}\""; + } } public sealed class SMPLSubChunk : SF2Chunk { - List samples = new List(); // Block of sample data + private readonly List _samples = new List(); // Block of sample data internal SMPLSubChunk(SF2 inSf2) : base(inSf2, "smpl") { } - internal SMPLSubChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal SMPLSubChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { for (int i = 0; i < Size / sizeof(short); i++) - samples.Add(reader.ReadInt16()); - } - internal override void Write(BinaryWriter writer) - { - base.Write(writer); - foreach (short s in samples) - writer.Write(s); + { + _samples.Add(reader.ReadInt16()); + } } // Returns index of the start of the sample internal uint AddSample(short[] pcm16, bool bLoop, uint loopPos) { - uint start = (uint)samples.Count; + uint start = (uint)_samples.Count; // Write wave - samples.AddRange(pcm16); + _samples.AddRange(pcm16); // If looping is enabled, write 8 samples from the loop point if (bLoop) @@ -407,191 +423,253 @@ internal uint AddSample(short[] pcm16, bool bLoop, uint loopPos) // In case (loopPos + i) is greater than the sample length uint max = (uint)pcm16.Length - loopPos; for (uint i = 0; i < 8; i++) - samples.Add(pcm16[loopPos + (i % max)]); + { + _samples.Add(pcm16[loopPos + (i % max)]); + } } // Write 46 empty samples - samples.AddRange(new short[46]); + _samples.AddRange(new short[46]); - Size = (uint)samples.Count * sizeof(short); - sf2.UpdateSize(); + Size = (uint)_samples.Count * sizeof(short); + _sf2.UpdateSize(); return start; } - public override string ToString() => $"Sample Data Chunk"; + internal override void Write(EndianBinaryWriter writer) + { + base.Write(writer); + foreach (short s in _samples) + { + writer.Write(s); + } + } + + public override string ToString() + { + return $"Sample Data Chunk"; + } } public sealed class PHDRSubChunk : SF2Chunk { - readonly List presets = new List(); - public uint Count => (uint)presets.Count; + private readonly List _presets = new List(); + public uint Count => (uint)_presets.Count; internal PHDRSubChunk(SF2 inSf2) : base(inSf2, "phdr") { } - internal PHDRSubChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal PHDRSubChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { for (int i = 0; i < Size / SF2PresetHeader.Size; i++) - presets.Add(new SF2PresetHeader(inSf2, reader)); + { + _presets.Add(new SF2PresetHeader(reader)); + } + } + + internal void AddPreset(SF2PresetHeader preset) + { + _presets.Add(preset); + Size = Count * SF2PresetHeader.Size; + _sf2.UpdateSize(); } - internal override void Write(BinaryWriter writer) + + internal override void Write(EndianBinaryWriter writer) { base.Write(writer); for (int i = 0; i < Count; i++) - presets[i].Write(writer); + { + _presets[i].Write(writer); + } } - internal void AddPreset(SF2PresetHeader preset) + public override string ToString() { - presets.Add(preset); - Size = Count * SF2PresetHeader.Size; - sf2.UpdateSize(); + return $"Preset Header Chunk - Preset count = {Count}"; } - - public override string ToString() => $"Preset Header Chunk - Preset count = {Count}"; } public sealed class INSTSubChunk : SF2Chunk { - readonly List instruments = new List(); - public uint Count => (uint)instruments.Count; + private readonly List _instruments = new List(); + public uint Count => (uint)_instruments.Count; internal INSTSubChunk(SF2 inSf2) : base(inSf2, "inst") { } - internal INSTSubChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal INSTSubChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { for (int i = 0; i < Size / SF2Instrument.Size; i++) - instruments.Add(new SF2Instrument(inSf2, reader)); - } - internal override void Write(BinaryWriter writer) - { - base.Write(writer); - for (int i = 0; i < Count; i++) - instruments[i].Write(writer); + { + _instruments.Add(new SF2Instrument(reader)); + } } internal uint AddInstrument(SF2Instrument instrument) { - instruments.Add(instrument); + _instruments.Add(instrument); Size = Count * SF2Instrument.Size; - sf2.UpdateSize(); + _sf2.UpdateSize(); return Count - 1; } - public override string ToString() => $"Instrument Chunk - Instrument count = {Count}"; + internal override void Write(EndianBinaryWriter writer) + { + base.Write(writer); + for (int i = 0; i < Count; i++) + { + _instruments[i].Write(writer); + } + } + + public override string ToString() + { + return $"Instrument Chunk - Instrument count = {Count}"; + } } public sealed class BAGSubChunk : SF2Chunk { - readonly List bags = new List(); - public uint Count => (uint)bags.Count; + private readonly List _bags = new List(); + public uint Count => (uint)_bags.Count; internal BAGSubChunk(SF2 inSf2, bool preset) : base(inSf2, preset ? "pbag" : "ibag") { } - internal BAGSubChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal BAGSubChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { for (int i = 0; i < Size / SF2Bag.Size; i++) - bags.Add(new SF2Bag(inSf2, reader)); + { + _bags.Add(new SF2Bag(reader)); + } } - internal override void Write(BinaryWriter writer) + + internal void AddBag(SF2Bag bag) + { + _bags.Add(bag); + Size = Count * SF2Bag.Size; + _sf2.UpdateSize(); + } + + internal override void Write(EndianBinaryWriter writer) { base.Write(writer); for (int i = 0; i < Count; i++) - bags[i].Write(writer); + { + _bags[i].Write(writer); + } } - internal void AddBag(SF2Bag bag) + public override string ToString() { - bags.Add(bag); - Size = Count * SF2Bag.Size; - sf2.UpdateSize(); + return $"Bag Chunk - Name = \"{ChunkName}\"" + + $",\nBag count = {Count}"; } - - public override string ToString() => $"Bag Chunk - Name = \"{ChunkName}\", " + - $"\nBag count = {Count}"; } public sealed class MODSubChunk : SF2Chunk { - readonly List modulators = new List(); - public uint Count => (uint)modulators.Count; + private readonly List _modulators = new List(); + public uint Count => (uint)_modulators.Count; internal MODSubChunk(SF2 inSf2, bool preset) : base(inSf2, preset ? "pmod" : "imod") { } - internal MODSubChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal MODSubChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { for (int i = 0; i < Size / SF2ModulatorList.Size; i++) - modulators.Add(new SF2ModulatorList(inSf2, reader)); + { + _modulators.Add(new SF2ModulatorList(reader)); + } } - internal override void Write(BinaryWriter writer) + + internal void AddModulator(SF2ModulatorList modulator) + { + _modulators.Add(modulator); + Size = Count * SF2ModulatorList.Size; + _sf2.UpdateSize(); + } + + internal override void Write(EndianBinaryWriter writer) { base.Write(writer); for (int i = 0; i < Count; i++) - modulators[i].Write(writer); + { + _modulators[i].Write(writer); + } } - internal void AddModulator(SF2ModulatorList modulator) + public override string ToString() { - modulators.Add(modulator); - Size = Count * SF2ModulatorList.Size; - sf2.UpdateSize(); + return $"Modulator Chunk - Name = \"{ChunkName}\"" + + $",\nModulator count = {Count}"; } - - public override string ToString() => $"Modulator Chunk - Name = \"{ChunkName}\", " + - $"\nModulator count = {Count}"; } public sealed class GENSubChunk : SF2Chunk { - readonly List generators = new List(); - public uint Count => (uint)generators.Count; + private readonly List _generators = new List(); + public uint Count => (uint)_generators.Count; internal GENSubChunk(SF2 inSf2, bool preset) : base(inSf2, preset ? "pgen" : "igen") { } - internal GENSubChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal GENSubChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { for (int i = 0; i < Size / SF2GeneratorList.Size; i++) - generators.Add(new SF2GeneratorList(inSf2, reader)); + { + _generators.Add(new SF2GeneratorList(reader)); + } } - internal override void Write(BinaryWriter writer) + + internal void AddGenerator(SF2GeneratorList generator) + { + _generators.Add(generator); + Size = Count * SF2GeneratorList.Size; + _sf2.UpdateSize(); + } + + internal override void Write(EndianBinaryWriter writer) { base.Write(writer); for (int i = 0; i < Count; i++) - generators[i].Write(writer); + { + _generators[i].Write(writer); + } } - internal void AddGenerator(SF2GeneratorList generator) + public override string ToString() { - generators.Add(generator); - Size = Count * SF2GeneratorList.Size; - sf2.UpdateSize(); + return $"Generator Chunk - Name = \"{ChunkName}\"" + + $",\nGenerator count = {Count}"; } - - public override string ToString() => $"Generator Chunk - Name = \"{ChunkName}\", " + - $"\nGenerator count = {Count}"; } public sealed class SHDRSubChunk : SF2Chunk { - readonly List samples = new List(); - public uint Count => (uint)samples.Count; + private readonly List _samples = new List(); + public uint Count => (uint)_samples.Count; internal SHDRSubChunk(SF2 inSf2) : base(inSf2, "shdr") { } - internal SHDRSubChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal SHDRSubChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { for (int i = 0; i < Size / SF2SampleHeader.Size; i++) - samples.Add(new SF2SampleHeader(inSf2, reader)); - } - internal override void Write(BinaryWriter writer) - { - base.Write(writer); - for (int i = 0; i < Count; i++) - samples[i].Write(writer); + { + _samples.Add(new SF2SampleHeader(reader)); + } } internal uint AddSample(SF2SampleHeader sample) { - samples.Add(sample); + _samples.Add(sample); Size = Count * SF2SampleHeader.Size; - sf2.UpdateSize(); + _sf2.UpdateSize(); return Count - 1; } - public override string ToString() => $"Sample Header Chunk - Sample header count = {Count}"; + internal override void Write(EndianBinaryWriter writer) + { + base.Write(writer); + for (int i = 0; i < Count; i++) + { + _samples[i].Write(writer); + } + } + + public override string ToString() + { + return $"Sample Header Chunk - Sample header count = {Count}"; + } } #endregion @@ -600,210 +678,286 @@ internal uint AddSample(SF2SampleHeader sample) public sealed class InfoListChunk : SF2ListChunk { - readonly List subChunks = new List(); - - const string defaultEngine = "EMU8000"; + private readonly List _subChunks = new List(); + private const string DefaultEngine = "EMU8000"; public string Engine { get { - if (subChunks.Find(s => s.ChunkName == "isng") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "isng") is HeaderSubChunk chunk) + { return chunk.Field; + } else { - subChunks.Add(new HeaderSubChunk(sf2, "isng") { Field = defaultEngine }); - return defaultEngine; + _subChunks.Add(new HeaderSubChunk(_sf2, "isng") { Field = DefaultEngine }); + return DefaultEngine; } } set { - if (subChunks.Find(s => s.ChunkName == "isng") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "isng") is HeaderSubChunk chunk) + { chunk.Field = value; + } else - subChunks.Add(new HeaderSubChunk(sf2, "isng") { Field = value }); + { + _subChunks.Add(new HeaderSubChunk(_sf2, "isng") { Field = value }); + } } } - const string defaultBank = "General MIDI"; + + private const string DefaultBank = "General MIDI"; public string Bank { get { - if (subChunks.Find(s => s.ChunkName == "INAM") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "INAM") is HeaderSubChunk chunk) + { return chunk.Field; + } else { - subChunks.Add(new HeaderSubChunk(sf2, "INAM") { Field = defaultBank }); - return defaultBank; + _subChunks.Add(new HeaderSubChunk(_sf2, "INAM") { Field = DefaultBank }); + return DefaultBank; } } set { - if (subChunks.Find(s => s.ChunkName == "INAM") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "INAM") is HeaderSubChunk chunk) + { chunk.Field = value; + } else - subChunks.Add(new HeaderSubChunk(sf2, "INAM") { Field = value }); + { + _subChunks.Add(new HeaderSubChunk(_sf2, "INAM") { Field = value }); + } } } public string ROM { get { - if (subChunks.Find(s => s.ChunkName == "irom") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "irom") is HeaderSubChunk chunk) + { return chunk.Field; + } else + { return string.Empty; + } } set { - if (subChunks.Find(s => s.ChunkName == "irom") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "irom") is HeaderSubChunk chunk) + { chunk.Field = value; + } else - subChunks.Add(new HeaderSubChunk(sf2, "irom") { Field = value }); + { + _subChunks.Add(new HeaderSubChunk(_sf2, "irom") { Field = value }); + } } } public SF2VersionTag ROMVersion { get { - if (subChunks.Find(s => s.ChunkName == "iver") is VersionSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "iver") is VersionSubChunk chunk) + { return chunk.Version; + } else + { return null; + } } set { - if (subChunks.Find(s => s.ChunkName == "iver") is VersionSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "iver") is VersionSubChunk chunk) + { chunk.Version = value; + } else - subChunks.Add(new VersionSubChunk(sf2, "iver") { Version = value }); + { + _subChunks.Add(new VersionSubChunk(_sf2, "iver") { Version = value }); + } } } public string Date { get { - if (subChunks.Find(s => s.ChunkName == "ICRD") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "ICRD") is HeaderSubChunk chunk) + { return chunk.Field; + } else + { return string.Empty; + } } set { - if (subChunks.Find(s => s.ChunkName == "ICRD") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "ICRD") is HeaderSubChunk chunk) + { chunk.Field = value; + } else - subChunks.Add(new HeaderSubChunk(sf2, "ICRD") { Field = value }); + { + _subChunks.Add(new HeaderSubChunk(_sf2, "ICRD") { Field = value }); + } } } public string Designer { get { - if (subChunks.Find(s => s.ChunkName == "IENG") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "IENG") is HeaderSubChunk chunk) + { return chunk.Field; + } else + { return string.Empty; + } } set { - if (subChunks.Find(s => s.ChunkName == "IENG") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "IENG") is HeaderSubChunk chunk) + { chunk.Field = value; + } else - subChunks.Add(new HeaderSubChunk(sf2, "IENG") { Field = value }); + { + _subChunks.Add(new HeaderSubChunk(_sf2, "IENG") { Field = value }); + } } } public string Products { get { - if (subChunks.Find(s => s.ChunkName == "IPRD") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "IPRD") is HeaderSubChunk chunk) + { return chunk.Field; + } else + { return string.Empty; + } } set { - if (subChunks.Find(s => s.ChunkName == "IPRD") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "IPRD") is HeaderSubChunk chunk) + { chunk.Field = value; + } else - subChunks.Add(new HeaderSubChunk(sf2, "IPRD") { Field = value }); + { + _subChunks.Add(new HeaderSubChunk(_sf2, "IPRD") { Field = value }); + } } } public string Copyright { get { - if (subChunks.Find(s => s.ChunkName == "ICOP") is HeaderSubChunk icop) + if (_subChunks.Find(s => s.ChunkName == "ICOP") is HeaderSubChunk icop) + { return icop.Field; + } else + { return string.Empty; + } } set { - if (subChunks.Find(s => s.ChunkName == "ICOP") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "ICOP") is HeaderSubChunk chunk) + { chunk.Field = value; + } else - subChunks.Add(new HeaderSubChunk(sf2, "ICOP") { Field = value }); + { + _subChunks.Add(new HeaderSubChunk(_sf2, "ICOP") { Field = value }); + } } } - const int commentMaxSize = 0x10000; + + private const int CommentMaxSize = 0x10000; public string Comment { get { - if (subChunks.Find(s => s.ChunkName == "ICMT") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "ICMT") is HeaderSubChunk chunk) + { return chunk.Field; + } else + { return string.Empty; + } } set { - if (subChunks.Find(s => s.ChunkName == "ICMT") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "ICMT") is HeaderSubChunk chunk) + { chunk.Field = value; + } else - subChunks.Add(new HeaderSubChunk(sf2, "ICMT", commentMaxSize) { Field = value }); + { + _subChunks.Add(new HeaderSubChunk(_sf2, "ICMT", maxSize: CommentMaxSize) { Field = value }); + } } } public string Tools { get { - if (subChunks.Find(s => s.ChunkName == "ISFT") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "ISFT") is HeaderSubChunk chunk) + { return chunk.Field; + } else + { return string.Empty; + } } set { - if (subChunks.Find(s => s.ChunkName == "ISFT") is HeaderSubChunk chunk) + if (_subChunks.Find(s => s.ChunkName == "ISFT") is HeaderSubChunk chunk) + { chunk.Field = value; + } else - subChunks.Add(new HeaderSubChunk(sf2, "ISFT") { Field = value }); + { + _subChunks.Add(new HeaderSubChunk(_sf2, "ISFT") { Field = value }); + } } } - + internal InfoListChunk(SF2 inSf2) : base(inSf2, "INFO") { // Mandatory sub-chunks - subChunks.Add(new VersionSubChunk(inSf2, "ifil") { Version = new SF2VersionTag(2, 1) }); - subChunks.Add(new HeaderSubChunk(inSf2, "isng") { Field = defaultEngine }); - subChunks.Add(new HeaderSubChunk(inSf2, "INAM") { Field = defaultBank }); - sf2.UpdateSize(); + _subChunks.Add(new VersionSubChunk(inSf2, "ifil") { Version = new SF2VersionTag(2, 1) }); + _subChunks.Add(new HeaderSubChunk(inSf2, "isng") { Field = DefaultEngine }); + _subChunks.Add(new HeaderSubChunk(inSf2, "INAM") { Field = DefaultBank }); + inSf2.UpdateSize(); } - internal InfoListChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal InfoListChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { - var startOffset = reader.BaseStream.Position; + long startOffset = reader.BaseStream.Position; while (reader.BaseStream.Position < startOffset + Size - 4) // The 4 represents the INFO that was already read { // Peek 4 chars for the chunk name - char[] name = reader.ReadChars(4); + string name = reader.ReadString(4, false); reader.BaseStream.Position -= 4; - string strName = new string(name); - switch (strName) + switch (name) { - case "ICMT": subChunks.Add(new HeaderSubChunk(inSf2, reader, commentMaxSize)); break; + case "ICMT": _subChunks.Add(new HeaderSubChunk(inSf2, reader, maxSize: CommentMaxSize)); break; case "ifil": - case "iver": subChunks.Add(new VersionSubChunk(inSf2, reader)); break; + case "iver": _subChunks.Add(new VersionSubChunk(inSf2, reader)); break; case "isng": case "INAM": case "ICRD": @@ -811,48 +965,51 @@ internal InfoListChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) case "IPRD": case "ICOP": case "ISFT": - case "irom": subChunks.Add(new HeaderSubChunk(inSf2, reader)); break; - default: - throw new NotSupportedException($"Unsupported chunk name at 0x{reader.BaseStream.Position:X}: \"{strName}\""); + case "irom": _subChunks.Add(new HeaderSubChunk(inSf2, reader)); break; + default: throw new NotSupportedException($"Unsupported chunk name at 0x{reader.BaseStream.Position:X}: \"{name}\""); } } } - internal override void Write(BinaryWriter writer) - { - base.Write(writer); - foreach (var sub in subChunks) - sub.Write(writer); - } internal override uint UpdateSize() { Size = 4; - foreach (var sub in subChunks) + foreach (SF2Chunk sub in _subChunks) + { Size += sub.Size + 8; + } + return Size; } - public override string ToString() => $"Info List Chunk - Sub-chunk count = {subChunks.Count}"; + internal override void Write(EndianBinaryWriter writer) + { + base.Write(writer); + foreach (SF2Chunk sub in _subChunks) + { + sub.Write(writer); + } + } + + public override string ToString() + { + return $"Info List Chunk - Sub-chunk count = {_subChunks.Count}"; + } } public sealed class SdtaListChunk : SF2ListChunk { - public readonly SMPLSubChunk SMPLSubChunk; + public SMPLSubChunk SMPLSubChunk { get; } internal SdtaListChunk(SF2 inSf2) : base(inSf2, "sdta") { SMPLSubChunk = new SMPLSubChunk(inSf2); - sf2.UpdateSize(); + inSf2.UpdateSize(); } - internal SdtaListChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal SdtaListChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { SMPLSubChunk = new SMPLSubChunk(inSf2, reader); } - internal override void Write(BinaryWriter writer) - { - base.Write(writer); - SMPLSubChunk.Write(writer); - } internal override uint UpdateSize() { @@ -860,20 +1017,29 @@ internal override uint UpdateSize() + SMPLSubChunk.Size + 8; } - public override string ToString() => $"Sample Data List Chunk"; + internal override void Write(EndianBinaryWriter writer) + { + base.Write(writer); + SMPLSubChunk.Write(writer); + } + + public override string ToString() + { + return $"Sample Data List Chunk"; + } } public sealed class PdtaListChunk : SF2ListChunk { - public readonly PHDRSubChunk PHDRSubChunk; - public readonly BAGSubChunk PBAGSubChunk; - public readonly MODSubChunk PMODSubChunk; - public readonly GENSubChunk PGENSubChunk; - public readonly INSTSubChunk INSTSubChunk; - public readonly BAGSubChunk IBAGSubChunk; - public readonly MODSubChunk IMODSubChunk; - public readonly GENSubChunk IGENSubChunk; - public readonly SHDRSubChunk SHDRSubChunk; + public PHDRSubChunk PHDRSubChunk { get; } + public BAGSubChunk PBAGSubChunk { get; } + public MODSubChunk PMODSubChunk { get; } + public GENSubChunk PGENSubChunk { get; } + public INSTSubChunk INSTSubChunk { get; } + public BAGSubChunk IBAGSubChunk { get; } + public MODSubChunk IMODSubChunk { get; } + public GENSubChunk IGENSubChunk { get; } + public SHDRSubChunk SHDRSubChunk { get; } internal PdtaListChunk(SF2 inSf2) : base(inSf2, "pdta") { @@ -886,9 +1052,9 @@ internal PdtaListChunk(SF2 inSf2) : base(inSf2, "pdta") IMODSubChunk = new MODSubChunk(inSf2, false); IGENSubChunk = new GENSubChunk(inSf2, false); SHDRSubChunk = new SHDRSubChunk(inSf2); - sf2.UpdateSize(); + inSf2.UpdateSize(); } - internal PdtaListChunk(SF2 inSf2, BinaryReader reader) : base(inSf2, reader) + internal PdtaListChunk(SF2 inSf2, EndianBinaryReader reader) : base(inSf2, reader) { PHDRSubChunk = new PHDRSubChunk(inSf2, reader); PBAGSubChunk = new BAGSubChunk(inSf2, reader); @@ -915,7 +1081,7 @@ internal override uint UpdateSize() + SHDRSubChunk.Size + 8; } - internal override void Write(BinaryWriter writer) + internal override void Write(EndianBinaryWriter writer) { base.Write(writer); PHDRSubChunk.Write(writer); @@ -929,7 +1095,10 @@ internal override void Write(BinaryWriter writer) SHDRSubChunk.Write(writer); } - public override string ToString() => $"Hydra List Chunk"; + public override string ToString() + { + return $"Hydra List Chunk"; + } } #endregion diff --git a/SoundFont2/SF2Types.cs b/SoundFont2/SF2Types.cs index 9a5b153..b297d80 100644 --- a/SoundFont2/SF2Types.cs +++ b/SoundFont2/SF2Types.cs @@ -1,25 +1,40 @@ -using System.Runtime.InteropServices; +using Kermalis.EndianBinaryIO; +using System.Runtime.InteropServices; namespace Kermalis.SoundFont2 { - // SF2 v2.1 spec page 16 + /// SF2 v2.1 spec page 16 public sealed class SF2VersionTag { public const uint Size = 4; - public readonly ushort Major; - public readonly ushort Minor; + public ushort Major { get; } + public ushort Minor { get; } public SF2VersionTag(ushort major, ushort minor) { - Major = major; Minor = minor; + Major = major; + Minor = minor; + } + internal SF2VersionTag(EndianBinaryReader reader) + { + Major = reader.ReadUInt16(); + Minor = reader.ReadUInt16(); + } + + internal void Write(EndianBinaryWriter writer) + { + writer.Write(Major); + writer.Write(Minor); } - public override string ToString() => $"v{Major}.{Minor}"; + public override string ToString() + { + return $"v{Major}.{Minor}"; + } } - // SF2 spec v2.1 page 19 - // Two bytes that can handle either two 8-bit values or a single 16-bit value + /// SF2 spec v2.1 page 19 - Two bytes that can handle either two 8-bit values or a single 16-bit value [StructLayout(LayoutKind.Explicit)] public struct SF2GeneratorAmount { @@ -28,10 +43,13 @@ public struct SF2GeneratorAmount [FieldOffset(0)] public short Amount; [FieldOffset(0)] public ushort UAmount; - public override string ToString() => $"BLo = {LowByte}, BHi = {HighByte}, Sh = {Amount}, U = {UAmount}"; + public override string ToString() + { + return $"BLo = {LowByte}, BHi = {HighByte}, Sh = {Amount}, U = {UAmount}"; + } } - // SF2 v2.1 spec page 20 + /// SF2 v2.1 spec page 20 public enum SF2SampleLink : ushort { MonoSample = 1, @@ -44,7 +62,7 @@ public enum SF2SampleLink : ushort RomLinkedSample = 0x8008 } - // SF2 v2.1 spec page 38 + /// SF2 v2.1 spec page 38 public enum SF2Generator : ushort { StartAddrsOffset = 0, @@ -102,8 +120,7 @@ public enum SF2Generator : ushort EndOper = 60 } - // Modulator's internal enumeration class - // SF2 v2.1 spec page 50 + /// SF2 v2.1 spec page 50 public enum SF2Modulator : ushort { None = 0, @@ -115,7 +132,7 @@ public enum SF2Modulator : ushort PitchWheelSensivity = 16 } - // SF2 v2.1 spec page 52 + /// SF2 v2.1 spec page 52 public enum SF2Transform : ushort { Linear = 0, diff --git a/SoundFont2/SF2Utils.cs b/SoundFont2/SF2Utils.cs deleted file mode 100644 index da76c4d..0000000 --- a/SoundFont2/SF2Utils.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Linq; - -namespace Kermalis.SoundFont2 -{ - static class SF2Utils - { - public static void TruncateOrNot(string str, int length, ref char[] toArray) - { - toArray = new char[length]; - var strAsChars = str.ToCharArray().Take(length).ToArray(); - Buffer.BlockCopy(strAsChars, 0, toArray, 0, strAsChars.Length * 2); - } - } -} diff --git a/SoundFont2/SoundFont2.csproj b/SoundFont2/SoundFont2.csproj index ac2e748..3ed4200 100644 --- a/SoundFont2/SoundFont2.csproj +++ b/SoundFont2/SoundFont2.csproj @@ -2,14 +2,25 @@ netstandard2.0 - - - - ..\Build + Release + Kermalis + + SoundFont2 + SoundFont2 + SoundFont2 + Kermalis.SoundFont2 + 1.0.0.0 ..\Build + Auto + none + false + + + +