From 53ec877dabff60b7561d32795ecf10b87429616a Mon Sep 17 00:00:00 2001 From: "rodgers.r" Date: Fri, 15 Mar 2019 11:02:05 +0200 Subject: [PATCH] Initialize. --- .gitignore | 4 + DssiDescriptor.cs | 91 +++++++++++++ DssiDescriptorStruct.cs | 37 ++++++ DssiInstance.cs | 194 +++++++++++++++++++++++++++ DssiMidiEvent.cs | 18 +++ DssiProgramDescriptorStruct.cs | 17 +++ LADSPA.NET.csproj | 69 ++++++++++ LADSPA.NET.sln | 37 ++++++ LadspaDescriptor.cs | 74 +++++++++++ LadspaDescriptorStruct.cs | 44 +++++++ LadspaInstance.cs | 232 +++++++++++++++++++++++++++++++++ LadspaLibraryContext.cs | 79 +++++++++++ LadspaPort.cs | 80 ++++++++++++ LadspaPortDescriptorEnum.cs | 18 +++ LadspaPortRangeHintsEnum.cs | 31 +++++ LadspaPortRangeHintsStruct.cs | 17 +++ LadspaPropertiesEnum.cs | 17 +++ Properties/AssemblyInfo.cs | 36 +++++ dist/LADSPA.NET.dll | Bin 0 -> 19968 bytes 19 files changed, 1095 insertions(+) create mode 100644 .gitignore create mode 100644 DssiDescriptor.cs create mode 100644 DssiDescriptorStruct.cs create mode 100644 DssiInstance.cs create mode 100644 DssiMidiEvent.cs create mode 100644 DssiProgramDescriptorStruct.cs create mode 100644 LADSPA.NET.csproj create mode 100644 LADSPA.NET.sln create mode 100644 LadspaDescriptor.cs create mode 100644 LadspaDescriptorStruct.cs create mode 100644 LadspaInstance.cs create mode 100644 LadspaLibraryContext.cs create mode 100644 LadspaPort.cs create mode 100644 LadspaPortDescriptorEnum.cs create mode 100644 LadspaPortRangeHintsEnum.cs create mode 100644 LadspaPortRangeHintsStruct.cs create mode 100644 LadspaPropertiesEnum.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 dist/LADSPA.NET.dll diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba88c78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vs +*.suo +bin/ +obj/ diff --git a/DssiDescriptor.cs b/DssiDescriptor.cs new file mode 100644 index 0000000..3edb479 --- /dev/null +++ b/DssiDescriptor.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + public class DssiDescriptor + { + public readonly LadspaLibraryContext Library; + public readonly LadspaDescriptor LadspaDescriptor; + public readonly bool IsSynth; + + internal readonly IntPtr DataHandle; + internal readonly DssiDescriptorConfigureCallback Configure; + internal readonly DssiDescriptorGetProgramCallback GetProgram; + internal readonly DssiDescriptorSelectProgramCallback SelectProgram; + internal readonly DssiDescriptorGetMidiControllerForPortCallback GetMidiControllerForPort; + internal readonly DssiDescriptorRunSynthCallback RunSynth; + internal readonly DssiDescriptorRunSynthCallback RunSynthAdding; + internal readonly DssiDescriptorRunMultiSynthCallback RunMultiSynth; + internal readonly DssiDescriptorRunMultiSynthCallback RunMultiSynthAdding; + + private DssiProgramDescriptor[] _programs; + public DssiProgramDescriptor[] Programs + { + get + { + lock (this) + { + if (_programs == null) + { + if (LadspaDescriptor.Instantiate != null && LadspaDescriptor.Cleanup != null) + { + IntPtr handle = LadspaDescriptor.Instantiate(LadspaDescriptor.DataHandle, 44100); + if (handle != IntPtr.Zero) + { + try { _programs = LoadProgramsFromHandle(handle); } + finally { LadspaDescriptor.Cleanup(handle); } + } + } + if (_programs == null) { _programs = new DssiProgramDescriptor[0]; } + } + return (_programs); + } + } + } + + internal DssiDescriptor(LadspaLibraryContext library, uint index, IntPtr dataPtr) + { + DssiDescriptorStruct data = (DssiDescriptorStruct)Marshal.PtrToStructure(dataPtr, typeof(DssiDescriptorStruct)); + Library = library; + DataHandle = dataPtr; + LadspaDescriptor = new LadspaDescriptor(library, index, data.LadspaDescriptor); + if (data.Configure != IntPtr.Zero) { Configure = (DssiDescriptorConfigureCallback)Marshal.GetDelegateForFunctionPointer(data.Configure, typeof(DssiDescriptorConfigureCallback)); } + if (data.GetProgram != IntPtr.Zero) { GetProgram = (DssiDescriptorGetProgramCallback)Marshal.GetDelegateForFunctionPointer(data.GetProgram, typeof(DssiDescriptorGetProgramCallback)); } + if (data.SelectProgram != IntPtr.Zero) { SelectProgram = (DssiDescriptorSelectProgramCallback)Marshal.GetDelegateForFunctionPointer(data.SelectProgram, typeof(DssiDescriptorSelectProgramCallback)); } + if (data.GetMidiControllerForPort != IntPtr.Zero) { GetMidiControllerForPort = (DssiDescriptorGetMidiControllerForPortCallback)Marshal.GetDelegateForFunctionPointer(data.GetMidiControllerForPort, typeof(DssiDescriptorGetMidiControllerForPortCallback)); } + if (data.RunSynth != IntPtr.Zero) { RunSynth = (DssiDescriptorRunSynthCallback)Marshal.GetDelegateForFunctionPointer(data.RunSynth, typeof(DssiDescriptorRunSynthCallback)); } + if (data.RunSynthAdding != IntPtr.Zero) { RunSynthAdding = (DssiDescriptorRunSynthCallback)Marshal.GetDelegateForFunctionPointer(data.RunSynthAdding, typeof(DssiDescriptorRunSynthCallback)); } + if (data.RunMultiSynth != IntPtr.Zero) { RunMultiSynth = (DssiDescriptorRunMultiSynthCallback)Marshal.GetDelegateForFunctionPointer(data.RunMultiSynth, typeof(DssiDescriptorRunMultiSynthCallback)); } + if (data.RunMultiSynthAdding != IntPtr.Zero) { RunMultiSynthAdding = (DssiDescriptorRunMultiSynthCallback)Marshal.GetDelegateForFunctionPointer(data.RunMultiSynthAdding, typeof(DssiDescriptorRunMultiSynthCallback)); } + IsSynth = data.RunSynth != null; + } + + public DssiInstance CreateInstance(int sampleRate, int bufferSize = 1024) + { + IntPtr handle = LadspaDescriptor.Instantiate != null ? LadspaDescriptor.Instantiate(LadspaDescriptor.DataHandle, (uint)sampleRate) : IntPtr.Zero; + if (handle == IntPtr.Zero) { throw new Exception("Failed to create instance."); } + lock (this) { if (_programs == null) { _programs = LoadProgramsFromHandle(handle); } } + return (new DssiInstance(this, handle, sampleRate, bufferSize)); + } + + private DssiProgramDescriptor[] LoadProgramsFromHandle(IntPtr handle) + { + List programs = new List(); + if (GetProgram != null) + { + for (uint index = 0; true; index++) + { + IntPtr programPtr = GetProgram(handle, index); + if (programPtr == IntPtr.Zero) { break; } + programs.Add((DssiProgramDescriptor)Marshal.PtrToStructure(programPtr, typeof(DssiProgramDescriptor))); + } + } + return (programs.ToArray()); + } + } +} diff --git a/DssiDescriptorStruct.cs b/DssiDescriptorStruct.cs new file mode 100644 index 0000000..7c12e87 --- /dev/null +++ b/DssiDescriptorStruct.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + [StructLayout(LayoutKind.Sequential)] + internal struct DssiDescriptorStruct + { + public uint APIVersion; + public IntPtr LadspaDescriptor; // LadspaDescriptorStruct + public IntPtr Configure; // string (IntPtr handle, string key, string value) + public IntPtr GetProgram; // IntPtr (IntPtr handle, uint index) + public IntPtr SelectProgram; // void (IntPtr handle, uint bank, uint program) + public IntPtr GetMidiControllerForPort; // int (IntPtr handle, uint port) + public IntPtr RunSynth; // void (IntPtr thisPtr, uint sampleCount, IntPtr events, uint eventCount) + public IntPtr RunSynthAdding; // void (IntPtr thisPtr, uint sampleCount, IntPtr events, uint eventCount) + public IntPtr RunMultiSynth; // void (uint thisCount, IntPtr thisPtrs, uint sampleCount, IntPtr events, IntPtr eventCounts) + public IntPtr RunMultiSynthAdding; // void (uint thisCount, IntPtr thisPtrs, uint sampleCount, IntPtr events, IntPtr eventCounts) + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate string DssiDescriptorConfigureCallback(IntPtr thisPtr, string key, string value); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate IntPtr DssiDescriptorGetProgramCallback(IntPtr thisPtr, uint index); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void DssiDescriptorSelectProgramCallback(IntPtr thisPtr, uint bank, uint program); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate int DssiDescriptorGetMidiControllerForPortCallback(IntPtr thisPtr, uint port); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void DssiDescriptorRunSynthCallback(IntPtr thisPtr, uint sampleCount, IntPtr events, uint eventCount); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void DssiDescriptorRunMultiSynthCallback(uint thisCount, IntPtr thisPtrs, uint sampleCount, IntPtr events, IntPtr eventCounts); +} diff --git a/DssiInstance.cs b/DssiInstance.cs new file mode 100644 index 0000000..1128bf7 --- /dev/null +++ b/DssiInstance.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + public class DssiInstance : LadspaInstance + { + public readonly DssiDescriptor DssiDescriptor; + + protected readonly List eventList = new List(); + protected IntPtr eventBuffer; + protected int eventBufferSize; + protected int eventBufferCount; + + internal DssiInstance(DssiDescriptor descriptor, IntPtr handle, int sampleRate, int bufferSize) + : base(descriptor.LadspaDescriptor, handle, sampleRate, bufferSize) + { + DssiDescriptor = descriptor; + } + + protected void PrepareEvents() + { + int i, offset, count = eventList.Count; + int size = count * 28; + if (eventBuffer == IntPtr.Zero || eventBufferSize < size) + { + if (eventBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(eventBuffer); eventBuffer = IntPtr.Zero; } + eventBuffer = Marshal.AllocHGlobal(eventBufferSize = size + 140); + } + for (i = 0, offset = 0, eventBufferCount = 0; i < count; i++) + { + DssiMidiEvent e = eventList[i]; + int command = e.MidiCommand & 0xf0; + switch (command) + { + case 0x80: // Note Off + case 0x90: // Note On + case 0xA0: // Aftertouch + int code = (command == 0x80 || (e.MidiData1 == 0 && command == 0x90)) ? 7 : command == 0x90 ? 6 : 8; + Marshal.WriteInt32(eventBuffer, offset, code); offset += 4; // Type code + Marshal.WriteInt32(eventBuffer, offset, e.SampleIndex); offset += 4; // Ticks + Marshal.WriteInt64(eventBuffer, offset, 0); offset += 8; // Realtime timestamp + addresses (not used) + Marshal.WriteByte(eventBuffer, offset, (byte)(e.MidiCommand & 0x0f)); offset++; // Channel + Marshal.WriteByte(eventBuffer, offset, e.MidiData0); offset++; // Pitch + Marshal.WriteByte(eventBuffer, offset, e.MidiData1 == 0 ? (byte)64 : e.MidiData1); offset++; // Velocity + Marshal.WriteByte(eventBuffer, offset, 0); offset++; // Off Velocity (not used) + Marshal.WriteInt64(eventBuffer, offset, 0); offset += 8; // Duration (not used) + eventBufferCount++; + break; + + case 0xB0: // Controller + Marshal.WriteInt32(eventBuffer, offset, 10); offset += 4; // Type code + Marshal.WriteInt32(eventBuffer, offset, e.SampleIndex); offset += 4; // Ticks + Marshal.WriteInt64(eventBuffer, offset, 0); offset += 8; // Realtime timestamp + addresses (not used) + Marshal.WriteInt32(eventBuffer, offset, e.MidiCommand & 0x0f); offset += 4; // Channel + Unused + Marshal.WriteInt32(eventBuffer, offset, e.MidiData0); offset += 4; // Control parameter + Marshal.WriteInt32(eventBuffer, offset, e.MidiData1); offset += 4; // Value + eventBufferCount++; + break; + + case 0xC0: // Change program + SelectProgram((uint)(e.MidiCommand & 0x0f), e.MidiData0); + break; + + case 0xD0: // Channel pressure + Marshal.WriteInt32(eventBuffer, offset, 12); offset += 4; // Type code + Marshal.WriteInt32(eventBuffer, offset, e.SampleIndex); offset += 4; // Ticks + Marshal.WriteInt64(eventBuffer, offset, 0); offset += 8; // Realtime timestamp + addresses (not used) + Marshal.WriteInt32(eventBuffer, offset, e.MidiCommand & 0x0f); offset += 4; // Channel + Unused + Marshal.WriteInt32(eventBuffer, offset, 0); offset += 4; // Control parameter + Marshal.WriteInt32(eventBuffer, offset, e.MidiData0); offset += 4; // Value + eventBufferCount++; + break; + + case 0xE0: // Pitchbend + Marshal.WriteInt32(eventBuffer, offset, 13); offset += 4; // Type code + Marshal.WriteInt32(eventBuffer, offset, e.SampleIndex); offset += 4; // Ticks + Marshal.WriteInt64(eventBuffer, offset, 0); offset += 8; // Realtime timestamp + addresses (not used) + Marshal.WriteInt32(eventBuffer, offset, e.MidiCommand & 0x0f); offset += 4; // Channel + Unused + Marshal.WriteInt32(eventBuffer, offset, 0); offset += 4; // Control parameter + Marshal.WriteInt32(eventBuffer, offset, (short)(((int)e.MidiData0 << 2) + ((int)e.MidiData1 << 9)) >> 2); offset += 4; // Value + eventBufferCount++; + break; + } + } + eventList.Clear(); + } + + public string Configure(string key, string value) + { + if (handle == IntPtr.Zero || DssiDescriptor.Configure == null) { return (null); } + return (DssiDescriptor.Configure(handle, key, value)); + } + + public void SelectProgram(uint bank, uint program) + { + if (handle == IntPtr.Zero || DssiDescriptor.SelectProgram == null) { return; } + DssiDescriptor.SelectProgram(handle, bank, program); + } + + public int GetMidiControllerForPort(uint port) + { + if (handle == IntPtr.Zero || DssiDescriptor.GetMidiControllerForPort == null) { return (0); } + return (DssiDescriptor.GetMidiControllerForPort(handle, port)); + } + + public void AddSynthEvents(params DssiMidiEvent[] events) + { + if (events != null && events.Length > 0) + { + eventList.AddRange(events); + } + } + + public bool Run(float[][] outputs) + { + if (handle != IntPtr.Zero) + { + if (DssiDescriptor.RunSynth != null) + { + PrepareEvents(); + DssiDescriptor.RunSynth(handle, (uint)bufferSize, eventBuffer, (uint)eventBufferCount); + CopyOutputs(outputs); + return (true); + } + else if (LadspaDescriptor.Run != null) + { + LadspaDescriptor.Run(handle, (uint)bufferSize); + CopyOutputs(outputs); + return (true); + } + } + if (eventList.Count > 0) { eventList.Clear(); } + return (false); + } + + public bool RunAdding(float[][] outputs) + { + if (handle != IntPtr.Zero) + { + if (DssiDescriptor.RunSynthAdding != null) + { + PrepareEvents(); + DssiDescriptor.RunSynthAdding(handle, (uint)bufferSize, eventBuffer, (uint)eventBufferCount); + CopyOutputs(outputs); + return (true); + } + else if (LadspaDescriptor.RunAdding != null) + { + LadspaDescriptor.RunAdding(handle, (uint)bufferSize); + CopyOutputs(outputs); + return (true); + } + } + if (eventList.Count > 0) { eventList.Clear(); } + return (false); + } + + public override bool Run(float[][] inputs, float[][] outputs) + { + if (handle != IntPtr.Zero) { CopyInputs(inputs); } + return (Run(outputs)); + } + + public override bool RunAdding(float[][] inputs, float[][] outputs) + { + if (handle != IntPtr.Zero) { CopyInputs(inputs); } + return (RunAdding(outputs)); + } + + public override LadspaInstance Clone(int sampleRate = 0, int bufferSize = 0) + { + if (sampleRate == 0) { sampleRate = this.sampleRate; } + if (bufferSize == 0) { bufferSize = this.bufferSize; } + DssiInstance instance = DssiDescriptor.CreateInstance(sampleRate, bufferSize); + for (int i = 0; i < LadspaDescriptor.Ports.Length; i++) { instance.SetParameter(i, GetParameter(i)); } + return (instance); + } + + public override void Dispose() + { + base.Dispose(); + if (eventBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(eventBuffer); + eventBuffer = IntPtr.Zero; + } + } + } +} diff --git a/DssiMidiEvent.cs b/DssiMidiEvent.cs new file mode 100644 index 0000000..b71ff80 --- /dev/null +++ b/DssiMidiEvent.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + public struct DssiMidiEvent + { + public int SampleIndex; + public byte MidiCommand; + public byte MidiData0; + public byte MidiData1; + public byte MidiData2; + } +} diff --git a/DssiProgramDescriptorStruct.cs b/DssiProgramDescriptorStruct.cs new file mode 100644 index 0000000..82ae8b6 --- /dev/null +++ b/DssiProgramDescriptorStruct.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + [StructLayout(LayoutKind.Sequential)] + public struct DssiProgramDescriptor + { + public uint Bank; + public uint Program; + public string Name; + } +} diff --git a/LADSPA.NET.csproj b/LADSPA.NET.csproj new file mode 100644 index 0000000..1a6c736 --- /dev/null +++ b/LADSPA.NET.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE} + Library + Properties + LADSPA.NET + LADSPA.NET + v4.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AnyCPU + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/LADSPA.NET.sln b/LADSPA.NET.sln new file mode 100644 index 0000000..cbbde35 --- /dev/null +++ b/LADSPA.NET.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27428.2043 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LADSPA.NET", "LADSPA.NET.csproj", "{4009C190-47E2-4132-9E6A-DF87BEF84FCE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Debug|x64.ActiveCfg = Debug|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Debug|x64.Build.0 = Debug|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Debug|x86.ActiveCfg = Debug|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Debug|x86.Build.0 = Debug|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Release|Any CPU.Build.0 = Release|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Release|x64.ActiveCfg = Release|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Release|x64.Build.0 = Release|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Release|x86.ActiveCfg = Release|Any CPU + {4009C190-47E2-4132-9E6A-DF87BEF84FCE}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CE0132EC-A75A-4BDA-ABCA-784D93C8B3FF} + EndGlobalSection +EndGlobal diff --git a/LadspaDescriptor.cs b/LadspaDescriptor.cs new file mode 100644 index 0000000..014d9b2 --- /dev/null +++ b/LadspaDescriptor.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + public class LadspaDescriptor + { + public readonly LadspaLibraryContext Library; + public readonly uint Index; + public readonly uint UniqueID; + public readonly LadspaPropertiesEnum Properties; + public readonly string Label; + public readonly string Name; + public readonly string Maker; + public readonly string Copyright; + public readonly LadspaPort[] Ports; + + internal readonly IntPtr DataHandle; + internal readonly LadspaDescriptorInstantiateCallback Instantiate; + internal readonly LadspaDescriptorConnectPortCallback ConnectPort; + internal readonly LadspaDescriptorCallback Activate; + internal readonly LadspaDescriptorRunCallback Run; + internal readonly LadspaDescriptorRunCallback RunAdding; + internal readonly LadspaDescriptorSetRunGainCallback SetRunAddingGain; + internal readonly LadspaDescriptorCallback Deactivate; + internal readonly LadspaDescriptorCallback Cleanup; + + internal LadspaDescriptor(LadspaLibraryContext library, uint index, IntPtr dataPtr) + { + LadspaDescriptorStruct data = (LadspaDescriptorStruct)Marshal.PtrToStructure(dataPtr, typeof(LadspaDescriptorStruct)); + Library = library; + DataHandle = dataPtr; + Index = index; + UniqueID = data.UniqueID; + Properties = data.Properties; + Label = data.Label; + Name = data.Name; + Maker = data.Maker; + Copyright = data.Copyright; + Ports = new LadspaPort[data.PortCount]; + int hintsSize = Marshal.SizeOf(typeof(LadspaPortRangeHintsStruct)); + int[] types = new int[data.PortCount]; + IntPtr[] names = new IntPtr[data.PortCount]; + if (data.PortDescriptors != IntPtr.Zero) { Marshal.Copy(data.PortDescriptors, types, 0, types.Length); } + if (data.PortNames != IntPtr.Zero) { Marshal.Copy(data.PortNames, names, 0, names.Length); } + for (int i = 0; i < data.PortCount; i++) + { + string name = names[i] != IntPtr.Zero ? Marshal.PtrToStringAnsi(names[i]) : null; + LadspaPortRangeHintsStruct hints = data.PortRangeHints == IntPtr.Zero ? new LadspaPortRangeHintsStruct() : + (LadspaPortRangeHintsStruct)Marshal.PtrToStructure(IntPtr.Add(data.PortRangeHints, hintsSize * (int)i), typeof(LadspaPortRangeHintsStruct)); + Ports[i] = new LadspaPort(this, i, (LadspaPortDescriptorEnum)types[i], hints.Hints, name, hints.LowerBound, hints.UpperBound); + } + if (data.Instantiate != IntPtr.Zero) { Instantiate = (LadspaDescriptorInstantiateCallback)Marshal.GetDelegateForFunctionPointer(data.Instantiate, typeof(LadspaDescriptorInstantiateCallback)); } + if (data.ConnectPort != IntPtr.Zero) { ConnectPort = (LadspaDescriptorConnectPortCallback)Marshal.GetDelegateForFunctionPointer(data.ConnectPort, typeof(LadspaDescriptorConnectPortCallback)); } + if (data.Activate != IntPtr.Zero) { Activate = (LadspaDescriptorCallback)Marshal.GetDelegateForFunctionPointer(data.Activate, typeof(LadspaDescriptorCallback)); } + if (data.Run != IntPtr.Zero) { Run = (LadspaDescriptorRunCallback)Marshal.GetDelegateForFunctionPointer(data.Run, typeof(LadspaDescriptorRunCallback)); } + if (data.RunAdding != IntPtr.Zero) { RunAdding = (LadspaDescriptorRunCallback)Marshal.GetDelegateForFunctionPointer(data.RunAdding, typeof(LadspaDescriptorRunCallback)); } + if (data.SetRunAddingGain != IntPtr.Zero) { SetRunAddingGain = (LadspaDescriptorSetRunGainCallback)Marshal.GetDelegateForFunctionPointer(data.SetRunAddingGain, typeof(LadspaDescriptorSetRunGainCallback)); } + if (data.Deactivate != IntPtr.Zero) { Deactivate = (LadspaDescriptorCallback)Marshal.GetDelegateForFunctionPointer(data.Deactivate, typeof(LadspaDescriptorCallback)); } + if (data.Cleanup != IntPtr.Zero) { Cleanup = (LadspaDescriptorCallback)Marshal.GetDelegateForFunctionPointer(data.Cleanup, typeof(LadspaDescriptorCallback)); } + } + + public LadspaInstance CreateInstance(int sampleRate, int bufferSize = 1024) + { + IntPtr handle = Instantiate != null ? Instantiate(DataHandle, (uint)sampleRate) : IntPtr.Zero; + if (handle == IntPtr.Zero) { throw new Exception("Failed to create instance."); } + return (new LadspaInstance(this, handle, sampleRate, bufferSize)); + } + } +} diff --git a/LadspaDescriptorStruct.cs b/LadspaDescriptorStruct.cs new file mode 100644 index 0000000..9f3b68e --- /dev/null +++ b/LadspaDescriptorStruct.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + [StructLayout(LayoutKind.Sequential)] + internal struct LadspaDescriptorStruct + { + public uint UniqueID; + public string Label; + public LadspaPropertiesEnum Properties; + public string Name; + public string Maker; + public string Copyright; + public uint PortCount; + public IntPtr PortDescriptors; // LadspaPortDescriptorEnum[] + public IntPtr PortNames; // string[] + public IntPtr PortRangeHints; // LadspaPortRangeHintsStruct[] + public IntPtr ImplementationData; + public IntPtr Instantiate; // IntPtr (LadspaDescriptorStruct* this, uint sampleRate) + public IntPtr ConnectPort; // void (IntPtr handle, uint port, float* data) + public IntPtr Activate; // void (IntPtr handle) + public IntPtr Run; // void (IntPtr handle, uint sampleCount) + public IntPtr RunAdding; // void (IntPtr handle, uint sampleCount) + public IntPtr SetRunAddingGain; // void (IntPtr handle, float gain) + public IntPtr Deactivate; // void (IntPtr handle) + public IntPtr Cleanup; // void (IntPtr handle) + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate IntPtr LadspaDescriptorInstantiateCallback(IntPtr thisPtr, uint sampleRate); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void LadspaDescriptorConnectPortCallback(IntPtr handle, uint port, IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void LadspaDescriptorCallback(IntPtr handle); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void LadspaDescriptorRunCallback(IntPtr handle, uint sampleCount); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void LadspaDescriptorSetRunGainCallback(IntPtr handle, float gain); +} diff --git a/LadspaInstance.cs b/LadspaInstance.cs new file mode 100644 index 0000000..af8161b --- /dev/null +++ b/LadspaInstance.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + public class LadspaInstance : IDisposable + { + [DllImport("kernel32.dll")] + protected static extern void RtlZeroMemory(IntPtr dst, int length); + + public readonly LadspaDescriptor LadspaDescriptor; + + protected IntPtr handle; + protected IntPtr data; + protected bool active; + protected int sampleRate; + protected readonly int[] dataOffsets; + protected readonly int[] inputOffsets; + protected readonly int[] outputOffsets; + protected readonly float[] paramBuffer; + protected readonly int bufferSize; + protected float[] mixBuffer; + + public readonly int InputCount; + public readonly int OutputCount; + + internal LadspaInstance(LadspaDescriptor descriptor, IntPtr handle, int sampleRate, int bufferSize) + { + if (bufferSize < 512) { bufferSize = 512; } + LadspaDescriptor = descriptor; + this.handle = handle; + this.bufferSize = bufferSize; + this.sampleRate = sampleRate; + int inputCount = 0, outputCount = 0; + for (int i = 0; i < descriptor.Ports.Length; i++) + { + if (descriptor.Ports[i].PortType != LadspaPortType.Audio) { continue; } + LadspaPortDirection dir = descriptor.Ports[i].PortDirection; + if (dir == LadspaPortDirection.Input) { inputCount++; } + else if (dir == LadspaPortDirection.Output) { outputCount++; } + } + InputCount = inputCount; + OutputCount = outputCount; + dataOffsets = new int[descriptor.Ports.Length]; + inputOffsets = new int[inputCount]; + outputOffsets = new int[outputCount]; + paramBuffer = new float[1]; + if (descriptor.ConnectPort != null) + { + int dataSize = 0, blockSize = (int)(bufferSize * 4); + inputCount = 0; outputCount = 0; + for (int i = 0; i < descriptor.Ports.Length; i++) + { + dataOffsets[i] = dataSize; + if (descriptor.Ports[i].PortType != LadspaPortType.Audio) { dataSize += 4; } + else + { + LadspaPortDirection dir = descriptor.Ports[i].PortDirection; + if (dir == LadspaPortDirection.Input) { inputOffsets[inputCount++] = dataSize; } + else if (dir == LadspaPortDirection.Output) { outputOffsets[outputCount++] = dataSize; } + dataSize += blockSize; + } + } + if (dataSize > 0) + { + data = Marshal.AllocHGlobal(dataSize); + RtlZeroMemory(data, dataSize); + ResetParameters(); + for (uint i = 0; i < descriptor.Ports.Length; i++) + { + descriptor.ConnectPort(handle, i, IntPtr.Add(data, dataOffsets[i])); + } + } + } + } + + protected void CopyInputs(float[][] inputs) + { + if (data == IntPtr.Zero) { return; } + for (int i = 0; i < inputOffsets.Length; i++) + { + int copy = inputs != null && inputs.Length > i && inputs[i] != null ? Math.Min(inputs[i].Length, bufferSize) : 0; + if (copy > 0) { Marshal.Copy(inputs[i], 0, IntPtr.Add(data, inputOffsets[i]), copy); } + if (copy < bufferSize) { RtlZeroMemory(IntPtr.Add(data, inputOffsets[i]), (bufferSize - copy) * 4); } + } + } + + protected void CopyOutputs(float[][] outputs) + { + if (data == IntPtr.Zero) { return; } + if (outputs == null) { return; } + int outMax = Math.Min(outputOffsets.Length, outputs.Length); + for (int i = 0; i < outMax; i++) + { + if (outputs[i] == null) { continue; } + int copy = Math.Min(outputs[i].Length, bufferSize); + if (copy > 0) { Marshal.Copy(IntPtr.Add(data, outputOffsets[i]), outputs[i], 0, copy); } + if (copy < outputs.Length) { Array.Clear(outputs, copy, outputs.Length - copy); } + } + if (outputs.Length == 1 && outputOffsets.Length > 1 && outputs[0] != null) + { + if (mixBuffer == null || mixBuffer.Length < bufferSize) { mixBuffer = new float[bufferSize]; } + Marshal.Copy(IntPtr.Add(data, outputOffsets[1]), mixBuffer, 0, bufferSize); + float[] buffer = outputs[0]; + int copy = Math.Min(buffer.Length, bufferSize); + for (int i = 0; i < copy; i++) { buffer[i] = (buffer[i] + mixBuffer[i]) * 0.5f; } + } + else + { + for (int i = outMax; i < outputs.Length; i++) + { + if (outputs[i] == null) { continue; } + if (i == 1 && outputs[0] != null) + { + Array.Copy(outputs[0], outputs[1], Math.Min(outputs[0].Length, outputs[1].Length)); + } + else + { + Array.Clear(outputs[i], 0, outputs[i].Length); + } + } + } + } + + public int GetSampleRate() + { + return (sampleRate); + } + + public int GetBufferSize() + { + return (bufferSize); + } + + public virtual LadspaInstance Clone(int sampleRate = 0, int bufferSize = 0) + { + if (sampleRate == 0) { sampleRate = this.sampleRate; } + if (bufferSize == 0) { bufferSize = this.bufferSize; } + LadspaInstance instance = LadspaDescriptor.CreateInstance(sampleRate, bufferSize); + for (int i = 0; i < LadspaDescriptor.Ports.Length; i++) { instance.SetParameter(i, GetParameter(i)); } + return (instance); + } + + public void Activate() + { + if (handle == IntPtr.Zero) { return; } + if (active) { return; } + if (LadspaDescriptor.Activate != null) { LadspaDescriptor.Activate(handle); } + active = true; + } + + public void Deactivate() + { + if (handle == IntPtr.Zero) { return; } + if (!active) { return; } + if (LadspaDescriptor.Deactivate != null) { LadspaDescriptor.Deactivate(handle); } + active = false; + } + + public void ResetParameters() + { + if (handle == IntPtr.Zero || data == IntPtr.Zero) { return; } + for (int i = 0; i < LadspaDescriptor.Ports.Length; i++) + { + if (LadspaDescriptor.Ports[i].PortType != LadspaPortType.Control) { continue; } + paramBuffer[0] = LadspaDescriptor.Ports[i].DefaultValue; + Marshal.Copy(paramBuffer, 0, IntPtr.Add(data, dataOffsets[i]), 1); + } + } + + public float GetParameter(int index) + { + if (handle == IntPtr.Zero) { return (0); } + if (index < 0 || index >= dataOffsets.Length) { return (0); } + if (LadspaDescriptor.Ports[index].PortType != LadspaPortType.Control) { return (0); } + Marshal.Copy(IntPtr.Add(data, dataOffsets[index]), paramBuffer, 0, 1); + return (paramBuffer[0]); + } + + public void SetParameter(int index, float value) + { + if (handle == IntPtr.Zero) { return; } + if (index < 0 || index >= dataOffsets.Length) { return; } + if (LadspaDescriptor.Ports[index].PortType != LadspaPortType.Control) { return; } + paramBuffer[0] = value; + Marshal.Copy(paramBuffer, 0, IntPtr.Add(data, dataOffsets[index]), 1); + } + + public void SetRunAddingGain(float gain) + { + if (handle == IntPtr.Zero) { return; } + if (LadspaDescriptor.SetRunAddingGain == null) { return; } + LadspaDescriptor.SetRunAddingGain(handle, gain); + } + + public virtual bool Run(float[][] inputs, float[][] outputs) + { + if (handle == IntPtr.Zero) { return (false); } + if (LadspaDescriptor.Run == null) { return (false); } + if (!active) { Activate(); } + CopyInputs(inputs); + LadspaDescriptor.Run(handle, (uint)bufferSize); + CopyOutputs(outputs); + return (true); + } + + public virtual bool RunAdding(float[][] inputs, float[][] outputs) + { + if (handle == IntPtr.Zero) { return (false); } + if (LadspaDescriptor.RunAdding == null) { return (false); } + if (!active) { Activate(); } + CopyInputs(inputs); + LadspaDescriptor.RunAdding(handle, (uint)bufferSize); + CopyOutputs(outputs); + return (true); + } + + public virtual void Dispose() + { + if (handle == IntPtr.Zero) { return; } + Deactivate(); + if (LadspaDescriptor.Cleanup != null) { LadspaDescriptor.Cleanup(handle); } + if (data != IntPtr.Zero) { Marshal.FreeHGlobal(data); } + data = IntPtr.Zero; + handle = IntPtr.Zero; + } + } +} diff --git a/LadspaLibraryContext.cs b/LadspaLibraryContext.cs new file mode 100644 index 0000000..eadeaed --- /dev/null +++ b/LadspaLibraryContext.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + public class LadspaLibraryContext : IDisposable + { + [DllImport("Kernel32.dll")] + private static extern IntPtr LoadLibrary(string path); + + [DllImport("Kernel32.dll")] + private static extern void FreeLibrary(IntPtr hModule); + + [DllImport("Kernel32.dll")] + private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate IntPtr LadspaDescriptorCallback(uint index); + + private IntPtr library; + + public readonly LadspaDescriptor[] LadspaDescriptors; + public readonly DssiDescriptor[] DssiDescriptors; + + public LadspaLibraryContext(string fileName) + { + library = LoadLibrary(fileName); + if (library == IntPtr.Zero) { throw new Exception("Failed to load library - file not found or not a valid library."); } + try + { + IntPtr func = GetProcAddress(library, "ladspa_descriptor"); + IntPtr dssiFunc = GetProcAddress(library, "dssi_descriptor"); + if (func == IntPtr.Zero && dssiFunc == IntPtr.Zero) + { + throw new Exception("Not a LADSPA plugin: ladspa_descriptor not found."); + } + List descriptors = new List(); + List dssiDescriptors = new List(); + if (func != IntPtr.Zero) + { + LadspaDescriptorCallback callback = (LadspaDescriptorCallback)Marshal.GetDelegateForFunctionPointer(func, typeof(LadspaDescriptorCallback)); + for (uint index = 0; true; index++) + { + IntPtr data = callback(index); + if (data == IntPtr.Zero) { break; } + descriptors.Add(new LadspaDescriptor(this, index, data)); + } + } + if (dssiFunc != IntPtr.Zero) + { + LadspaDescriptorCallback callback = (LadspaDescriptorCallback)Marshal.GetDelegateForFunctionPointer(dssiFunc, typeof(LadspaDescriptorCallback)); + for (uint index = 0; true; index++) + { + IntPtr data = callback(index); + if (data == IntPtr.Zero) { break; } + dssiDescriptors.Add(new DssiDescriptor(this, index, data)); + } + } + LadspaDescriptors = descriptors.ToArray(); + DssiDescriptors = dssiDescriptors.ToArray(); + } + catch + { + FreeLibrary(library); + throw; + } + } + + public void Dispose() + { + if (library == IntPtr.Zero) { return; } + FreeLibrary(library); + library = IntPtr.Zero; + } + } +} diff --git a/LadspaPort.cs b/LadspaPort.cs new file mode 100644 index 0000000..70e7f7d --- /dev/null +++ b/LadspaPort.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LADSPA.NET +{ + public class LadspaPort + { + public readonly LadspaDescriptor Descriptor; + public readonly int Index; + public readonly LadspaPortDescriptorEnum Type; + public readonly LadspaPortRangeHintsEnum Hints; + public readonly string Name; + + public readonly LadspaPortDirection PortDirection; + public readonly LadspaPortType PortType; + public readonly float DefaultValue; + public readonly float MinimumValue; + public readonly float MaximumValue; + public readonly bool IsLogarithmic; + public readonly bool IsInteger; + public readonly bool IsSampleRateMultiple; + public readonly bool IsToggled; + + internal LadspaPort(LadspaDescriptor descriptor, int index, + LadspaPortDescriptorEnum type, LadspaPortRangeHintsEnum hints, string name, + float minBound, float maxBound) + { + Descriptor = descriptor; + Index = index; + Type = type; + Name = name; + Hints = hints; + PortType = type.HasFlag(LadspaPortDescriptorEnum.LADSPA_PORT_CONTROL) ? LadspaPortType.Control : + type.HasFlag(LadspaPortDescriptorEnum.LADSPA_PORT_AUDIO) ? LadspaPortType.Audio : LadspaPortType.Unknown; + PortDirection = type.HasFlag(LadspaPortDescriptorEnum.LADSPA_PORT_INPUT) ? LadspaPortDirection.Input : + type.HasFlag(LadspaPortDescriptorEnum.LADSPA_PORT_OUTPUT) ? LadspaPortDirection.Output : LadspaPortDirection.Unknown; + IsLogarithmic = hints.HasFlag(LadspaPortRangeHintsEnum.LADSPA_HINT_LOGARITHMIC); + IsInteger = hints.HasFlag(LadspaPortRangeHintsEnum.LADSPA_HINT_INTEGER); + IsSampleRateMultiple = hints.HasFlag(LadspaPortRangeHintsEnum.LADSPA_HINT_SAMPLE_RATE); + IsToggled = hints.HasFlag(LadspaPortRangeHintsEnum.LADSPA_HINT_TOGGLED); + if (hints.HasFlag(LadspaPortRangeHintsEnum.LADSPA_HINT_BOUNDED_BELOW)) { MinimumValue = minBound; } else { MinimumValue = 0; } + if (hints.HasFlag(LadspaPortRangeHintsEnum.LADSPA_HINT_BOUNDED_ABOVE)) { MaximumValue = maxBound; } else { MaximumValue = 1; } + switch (hints & LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_MASK) + { + case LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_MINIMUM: DefaultValue = MinimumValue; break; + case LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_LOW: + DefaultValue = IsLogarithmic ? (float)Math.Exp((Math.Log(MinimumValue) * 3 + Math.Log(MaximumValue)) * 0.25) : + (MinimumValue * 3 + MaximumValue) * 0.25f; + break; + case LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_MIDDLE: + DefaultValue = IsLogarithmic ? (float)Math.Exp((Math.Log(MinimumValue) + Math.Log(MaximumValue)) * 0.5) : + (MinimumValue + MaximumValue) * 0.5f; + break; + case LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_HIGH: + DefaultValue = IsLogarithmic ? (float)Math.Exp((Math.Log(MinimumValue) + Math.Log(MaximumValue) * 3) * 0.25) : + (MinimumValue + MaximumValue * 3) * 0.25f; + break; + case LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_MAXIMUM: DefaultValue = MaximumValue; break; + case LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_0: DefaultValue = 0; break; + case LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_1: DefaultValue = 1; break; + case LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_100: DefaultValue = 100; break; + case LadspaPortRangeHintsEnum.LADSPA_HINT_DEFAULT_440: DefaultValue = 440; break; + default: break; + } + } + } + + public enum LadspaPortDirection + { + Unknown, Input, Output + } + + public enum LadspaPortType + { + Unknown, Audio, Control + } +} diff --git a/LadspaPortDescriptorEnum.cs b/LadspaPortDescriptorEnum.cs new file mode 100644 index 0000000..3558d06 --- /dev/null +++ b/LadspaPortDescriptorEnum.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + [Flags] + public enum LadspaPortDescriptorEnum : int + { + LADSPA_PORT_INPUT = 0x1, + LADSPA_PORT_OUTPUT = 0x2, + LADSPA_PORT_CONTROL = 0x4, + LADSPA_PORT_AUDIO = 0x8 + } +} diff --git a/LadspaPortRangeHintsEnum.cs b/LadspaPortRangeHintsEnum.cs new file mode 100644 index 0000000..02dcf30 --- /dev/null +++ b/LadspaPortRangeHintsEnum.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + [Flags] + public enum LadspaPortRangeHintsEnum : int + { + LADSPA_HINT_BOUNDED_BELOW = 0x1, + LADSPA_HINT_BOUNDED_ABOVE = 0x2, + LADSPA_HINT_TOGGLED = 0x4, + LADSPA_HINT_SAMPLE_RATE = 0x8, + LADSPA_HINT_LOGARITHMIC = 0x10, + LADSPA_HINT_INTEGER = 0x20, + LADSPA_HINT_DEFAULT_MASK = 0x3C0, + LADSPA_HINT_DEFAULT_NONE = 0x0, + LADSPA_HINT_DEFAULT_MINIMUM = 0x40, + LADSPA_HINT_DEFAULT_LOW = 0x80, + LADSPA_HINT_DEFAULT_MIDDLE = 0xC0, + LADSPA_HINT_DEFAULT_HIGH = 0x100, + LADSPA_HINT_DEFAULT_MAXIMUM = 0x140, + LADSPA_HINT_DEFAULT_0 = 0x200, + LADSPA_HINT_DEFAULT_1 = 0x240, + LADSPA_HINT_DEFAULT_100 = 0x280, + LADSPA_HINT_DEFAULT_440 = 0x2C0 + } +} diff --git a/LadspaPortRangeHintsStruct.cs b/LadspaPortRangeHintsStruct.cs new file mode 100644 index 0000000..2a0cc0c --- /dev/null +++ b/LadspaPortRangeHintsStruct.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + [StructLayout(LayoutKind.Sequential)] + internal struct LadspaPortRangeHintsStruct + { + public LadspaPortRangeHintsEnum Hints; + public float LowerBound; + public float UpperBound; + } +} diff --git a/LadspaPropertiesEnum.cs b/LadspaPropertiesEnum.cs new file mode 100644 index 0000000..ab1bd90 --- /dev/null +++ b/LadspaPropertiesEnum.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace LADSPA.NET +{ + [Flags] + public enum LadspaPropertiesEnum : int + { + LADSPA_PROPERTY_REALTIME = 0x1, + LADSPA_PROPERTY_INPLACE_BROKEN = 0x2, + LADSPA_PROPERTY_HARD_RT_CAPABLE = 0x4 + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3d1c659 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("LADSPA.NET")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("XTremeGaida")] +[assembly: AssemblyProduct("LADSPA.NET")] +[assembly: AssemblyCopyright("Copyright © XTremeGaida 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("487d3fbc-3722-4b8c-8380-5d98d336104b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dist/LADSPA.NET.dll b/dist/LADSPA.NET.dll new file mode 100644 index 0000000000000000000000000000000000000000..f30e8d5e4ab5b115709441d1d27af91da6f6a298 GIT binary patch literal 19968 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 g`AOjY^f>