Skip to content
This repository was archived by the owner on Sep 16, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions AutomationKeyframe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ public class AutomationKeyframe
public int Position { get; set; } = 0;
public double Value { get; set; } = 0;
public float Tension { get; set; } = 0;
public byte Mode { get; set; } = 0;
}
}
34 changes: 34 additions & 0 deletions Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,40 @@ public enum PluginType
Vst = 8
}

/*
* Modes:
* 0 - Single Curve
* 1 - Double Curve
* 2 - Hold
* 3 - Stairs
* 4 - Smooth Stairs
* 5 - Pulse
* 6 - Wave
* 7 - Single Curve 2
* 8 - Double Curve 2
* 9 - Half Sine
* 10 - Smooth
* 11 - Single Curve 3
* 12 - Double Curve 3
*/

public enum AutomationMode
{
SingleCurve = 0,
DoubleCurve = 1,
Hold = 2,
Stairs = 3,
SmoothStairs = 4,
Pulse = 5,
Wave = 6,
SingleCurve2 = 7,
DoubleCurve2 = 8,
HalfSine = 9,
Smooth = 10,
SingleCurve3 = 11,
DoubleCurve3 = 12
}

/*public enum FilterType
{
LowPass = 0,
Expand Down
3 changes: 3 additions & 0 deletions GeneratorData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ public class GeneratorData : IChannelData
public double Volume { get; set; } = 100;
public double Panning { get; set; } = 0;
public uint BaseNote { get; set; } = 57;
public byte Echo { get; set; } = 0;
public uint EchoFeed { get; set; } = 0;
public uint EchoTime { get; set; } = 0;
public int Insert { get; set; } = -1;
public int LayerParent { get; set; } = -1;

Expand Down
1 change: 1 addition & 0 deletions IPlaylistItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public interface IPlaylistItem
int Position { get; set; }
int Length { get; set; }
int StartOffset { get; set; }
bool Muted { get; set; }
int EndOffset { get; set; }
}
}
73 changes: 22 additions & 51 deletions Monad.FLParser.csproj
Original file line number Diff line number Diff line change
@@ -1,59 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CE83037E-41AA-4C72-BE2E-1DF90583AD17}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Monad.FLParser</RootNamespace>
<AssemblyName>Monad.FLParser</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFramework>netstandard2.0</TargetFramework>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="AutomationData.cs" />
<Compile Include="AutomationKeyframe.cs" />
<Compile Include="Channel.cs" />
<Compile Include="Enums.cs" />
<Compile Include="FlParseException.cs" />
<Compile Include="IChannelData.cs" />
<Compile Include="Insert.cs" />
<Compile Include="InsertSlot.cs" />
<Compile Include="IPlaylistItem.cs" />
<Compile Include="Track.cs" />
<Compile Include="Note.cs" />
<Compile Include="Pattern.cs" />
<Compile Include="PatternPlaylistItem.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="PluginIoInfo.cs" />
<Compile Include="Project.cs" />
<Compile Include="ProjectParser.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GeneratorData.cs" />
<Compile Include="ChannelPlaylistItem.cs" />
<Compile Remove="Properties\**" />
<EmbeddedResource Remove="Properties\**" />
<None Remove="Properties\**" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
1 change: 1 addition & 0 deletions Note.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public class Note
{
public int Position { get; set; }
public byte Color { get; set; }
public int Length { get; set; }
public byte Key { get; set; }
public ushort FinePitch { get; set; }
Expand Down
20 changes: 13 additions & 7 deletions Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Monad.FLParser
public class Project
{
public const int MaxInsertCount = 127;
public const int MaxTrackCount = 199;
//public const int MaxTrackCount = 499; //No longer used

public int MainVolume { get; set; } = 300;
public int MainPitch { get; set; } = 0;
Expand All @@ -19,24 +19,20 @@ public class Project
public string VersionString { get; set; } = string.Empty;
public int Version { get; set; } = 0x100;
public List<Channel> Channels { get; set; } = new List<Channel>();
public Track[] Tracks { get; set; } = new Track[MaxTrackCount];
public Track[] Tracks { get; set; }
public List<Pattern> Patterns = new List<Pattern>();
public Insert[] Inserts { get; set; } = new Insert[MaxInsertCount];
public bool PlayTruncatedNotes { get; set; } = false;

public Project()
{
for (var i = 0; i < MaxTrackCount; i++)
{
Tracks[i] = new Track();
}

for (var i = 0; i < MaxInsertCount; i++)
{
Inserts[i] = new Insert { Id = i, Name = $"Insert {i}" };
}

Inserts[0].Name = "Master";
InitTracks(199);
}

public static Project Load(string path, bool verbose)
Expand All @@ -60,5 +56,15 @@ public static Project Load(BinaryReader reader, bool verbose)
var parser = new ProjectParser(verbose);
return parser.Parse(reader);
}

internal void InitTracks(int count)
{
Tracks = new Track[count];

for(var i = 0; i < Tracks.Length; i++)
{
Tracks[i] = new Track();
}
}
}
}
78 changes: 62 additions & 16 deletions ProjectParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ private void ParseFldt(BinaryReader reader)
len = reader.ReadInt32();

// sanity check
if (len < 0 || len > 0x10000000)
throw new FlParseException($"Invalid chunk length: {len}", reader.BaseStream.Position);
//if (len < 0 || len > 0x10000000)
// throw new FlParseException($"Invalid chunk length: {len}", reader.BaseStream.Position);

} while (id != "FLdt");
}
Expand Down Expand Up @@ -243,13 +243,25 @@ private void ParseTextEvent(Enums.Event eventId, BinaryReader reader)
_project.Version = (int.Parse(numbers[0]) << 8) +
(int.Parse(numbers[1]) << 4) +
(int.Parse(numbers[2]) << 0);

var trackCount = _versionMajor <= 12 ? 199 : 500;
if(_project.Tracks.Length != trackCount)
_project.InitTracks(trackCount);
break;
case Enums.Event.GeneratorName:
if (genData != null) genData.GeneratorName = unicodeString;
break;
case Enums.Event.TextInsertName:
_curInsert.Name = unicodeString;
break;
case Enums.Event.TextDelay:
if(genData != null)
{
genData.Echo = dataBytes[12];
genData.EchoFeed = (uint)((dataBytes[1] << 8) | dataBytes[0]);
genData.EchoTime = (uint)((dataBytes[17] << 8) | dataBytes[16]);
}
break;
}
}

Expand Down Expand Up @@ -286,6 +298,7 @@ private void ParseDataEvent(Enums.Event eventId, BinaryReader reader)
case Enums.Event.DataChanParams:
{
if (genData == null) break;
//Console.WriteLine(reader.BaseStream.Position);
var unknown1 = reader.ReadBytes(40);
genData.ArpDir = (Enums.ArpDirection)reader.ReadInt32();
genData.ArpRange = reader.ReadInt32();
Expand Down Expand Up @@ -315,7 +328,8 @@ private void ParseDataEvent(Enums.Event eventId, BinaryReader reader)
var unknown3 = reader.ReadInt16();
var unknown4 = reader.ReadByte();
var finePitch = reader.ReadUInt16();
var release = reader.ReadUInt16();
var release = reader.ReadByte(); //Its actually a byte, next byte after is note channel
var color = reader.ReadByte(); //Used for map note color to midi channel
var pan = reader.ReadByte();
var velocity = reader.ReadByte();
var x1 = reader.ReadByte();
Expand All @@ -326,6 +340,7 @@ private void ParseDataEvent(Enums.Event eventId, BinaryReader reader)
_curPattern.Notes[channel].Add(new Note
{
Position = pos,
Color = color,
Length = length,
Key = key,
FinePitch = finePitch,
Expand Down Expand Up @@ -418,12 +433,24 @@ private void ParseDataEvent(Enums.Event eventId, BinaryReader reader)
var unknown2 = reader.ReadUInt32();
var unknown3 = reader.ReadByte();
var param = reader.ReadUInt16();
var paramDestination = reader.ReadInt16();
var paramDestination = reader.ReadUInt16();
var unknown4 = reader.ReadUInt64();

var channel = _project.Channels[automationChannel];

if ((paramDestination & 0x2000) == 0) // Automation on channel
if(paramDestination > _project.Channels.Count)
{
//Console.WriteLine("Tempo Automation Track found, but is currently not supported. Please use SAFC to merge the tempo back");
//break;
channel.Data = new AutomationData // automation on insert slot
{
Parameter = param & 0x7fff
//InsertId = (paramDestination & 0x0FF0) >> 6, // seems to be out by one
//SlotId = paramDestination & 0x003F
};
}

if ((paramDestination & 0x2000) == 0 && paramDestination < _project.Channels.Count) // Automation on channel
{
channel.Data = new AutomationData
{
Expand Down Expand Up @@ -451,10 +478,10 @@ private void ParseDataEvent(Enums.Event eventId, BinaryReader reader)
var patternId = reader.ReadUInt16();
var length = reader.ReadInt32();
var track = reader.ReadInt32();
if (_versionMajor == 20)
track = 501 - track;
if(_versionMajor == 20)
track = 499 - track;
else
track = 198 - track;
track = 198 - track;
var unknown1 = reader.ReadUInt16();
var itemFlags = reader.ReadUInt16();
var unknown3 = reader.ReadUInt32();
Expand All @@ -481,15 +508,28 @@ private void ParseDataEvent(Enums.Event eventId, BinaryReader reader)
var startOffset = reader.ReadInt32();
var endOffset = reader.ReadInt32();

_project.Tracks[track].Items.Add(new PatternPlaylistItem
if((patternId - patternBase - 1) > _project.Patterns.Count)
{
Console.WriteLine("Pattern found on track " + (track) + ", but it seems to be empty. Skipping...");
break;
}

try
{
_project.Tracks[track].Items.Add(new PatternPlaylistItem
{
Position = startTime,
Length = length,
StartOffset = startOffset,
EndOffset = endOffset,
Pattern = _project.Patterns[patternId - patternBase - 1],
Muted = muted
});
});
} catch(Exception e)
{
Console.WriteLine("Pattern found on track " + (track) + ", but it seems to be empty. Skipping...");
break;
}
}
}
break;
Expand All @@ -508,12 +548,15 @@ private void ParseDataEvent(Enums.Event eventId, BinaryReader reader)

for (var i = 0; i < keyCount; i++)
{
reader.ReadBytes(3);
var startPos = reader.BaseStream.Position;

var keyPos = reader.ReadDouble();
var keyVal = reader.ReadDouble();
var keyTension = reader.ReadSingle();
var unknown7 = reader.ReadUInt32(); // seems linked to tension?
var keyPos = reader.ReadUInt32(); //This is a UInt32
reader.ReadUInt32(); //Skip 4 bytes
var keyVal = reader.ReadUInt32(); //This is a UInt32
reader.ReadByte(); //Skip a byte, since it's useless
var keyTension = reader.ReadSingle(); // Tension is a single
var mode = reader.ReadByte(); // Seems to be the mode of the keyframe

var endPos = reader.BaseStream.Position;
reader.BaseStream.Position = startPos;
Expand All @@ -524,8 +567,10 @@ private void ParseDataEvent(Enums.Event eventId, BinaryReader reader)
{
Position = (int)(keyPos * _project.Ppq),
Tension = keyTension,
Value = keyVal
Value = keyVal,
Mode = mode
};
reader.ReadBytes(3);
}

// remaining data is unknown
Expand Down Expand Up @@ -563,7 +608,7 @@ private Plugin ParsePluginChunk(byte[] chunk)

if (pluginType != Enums.PluginType.Vst)
{
return null;
return null;
}

while (reader.BaseStream.Position < reader.BaseStream.Length)
Expand Down Expand Up @@ -591,6 +636,7 @@ private Plugin ParsePluginChunk(byte[] chunk)
}
}

//Console.WriteLine(reader.BaseStream.Position);
return plugin;
}
}
Expand Down
Loading