From c7de4cbd687ca11a6458e5200168d34740a34c6b Mon Sep 17 00:00:00 2001 From: bree Date: Wed, 26 Feb 2025 03:06:51 +0100 Subject: [PATCH] Basic FluidSynth support (NOT STABLE YET), added small set of new features, etc etc --- .gitmodules | 3 + MIDIModificationFramework | 2 +- OmniConverter.sln | 41 +++ .../Extensions/Audio/AudioCodecType.cs | 66 ++++ OmniConverter/Extensions/Audio/AudioEngine.cs | 3 +- .../Audio/Renderers/BASSRenderer.cs | 2 +- .../Audio/Renderers/FluidSynthRenderer.cs | 309 ++++++++++++++++++ OmniConverter/Extensions/MIDI/FLP.cs | 4 +- OmniConverter/Extensions/MIDI/MIDI.cs | 7 +- .../Extensions/MIDI/MIDIConverter.cs | 158 ++++----- OmniConverter/Extensions/Settings.cs | 6 +- OmniConverter/Forms/InfoWindow.axaml.cs | 4 +- OmniConverter/Forms/MainWindow.axaml | 2 +- OmniConverter/Forms/MainWindow.axaml.cs | 2 +- OmniConverter/Forms/SettingsWindow.axaml | 1 + OmniConverter/Forms/SettingsWindow.axaml.cs | 27 +- .../Forms/SoundFontsManager.axaml.cs | 8 +- OmniConverter/OmniConverter.csproj | 2 +- 18 files changed, 517 insertions(+), 130 deletions(-) create mode 100644 OmniConverter/Extensions/Audio/Renderers/FluidSynthRenderer.cs diff --git a/.gitmodules b/.gitmodules index 3ab8bdc..9821e6b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ url=https://github.com/KaleidonKep99/ManagedBass [submodule "MIDIModificationFramework"] path = MIDIModificationFramework url = https://github.com/KaleidonKep99/MIDIModificationFramework +[submodule "nfluidsynth"] + path = nfluidsynth + url = https://github.com/KaleidonKep99/nfluidsynth diff --git a/MIDIModificationFramework b/MIDIModificationFramework index 9d8d07c..2d966bd 160000 --- a/MIDIModificationFramework +++ b/MIDIModificationFramework @@ -1 +1 @@ -Subproject commit 9d8d07ce485a900bb6da1af9018528bb84f01c22 +Subproject commit 2d966bdfb03d5ebf53e4ddd04a13b0203b03f080 diff --git a/OmniConverter.sln b/OmniConverter.sln index 3ce0927..1c73ab3 100644 --- a/OmniConverter.sln +++ b/OmniConverter.sln @@ -5,6 +5,7 @@ VisualStudioVersion = 17.9.34728.123 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniConverter", "OmniConverter\OmniConverter.csproj", "{B8123F7B-5CDA-48AD-804B-ACF334188F11}" ProjectSection(ProjectDependencies) = postProject + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB} = {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB} {5147DAD3-EB9D-466E-B787-5867CF977377} = {5147DAD3-EB9D-466E-B787-5867CF977377} {A622A3B1-C497-4B14-A61D-BD115CD82769} = {A622A3B1-C497-4B14-A61D-BD115CD82769} {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A} = {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A} @@ -25,62 +26,102 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NFluidsynth", "nfluidsynth\NFluidsynth\NFluidsynth.csproj", "{1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Debug|ARM64.ActiveCfg = Debug|ARM64 {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Debug|ARM64.Build.0 = Debug|ARM64 {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Debug|x64.ActiveCfg = Debug|x64 {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Debug|x64.Build.0 = Debug|x64 + {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Release|Any CPU.Build.0 = Release|Any CPU {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Release|ARM64.ActiveCfg = Release|ARM64 {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Release|ARM64.Build.0 = Release|ARM64 {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Release|x64.ActiveCfg = Release|x64 {B8123F7B-5CDA-48AD-804B-ACF334188F11}.Release|x64.Build.0 = Release|x64 + {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Debug|ARM64.ActiveCfg = Debug|Any CPU {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Debug|ARM64.Build.0 = Debug|Any CPU {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Debug|x64.ActiveCfg = Debug|Any CPU {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Debug|x64.Build.0 = Debug|Any CPU + {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Release|Any CPU.Build.0 = Release|Any CPU {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Release|ARM64.ActiveCfg = Release|Any CPU {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Release|ARM64.Build.0 = Release|Any CPU {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Release|x64.ActiveCfg = Release|Any CPU {A5EBE366-17DF-44A9-A870-FB274EA299AE}.Release|x64.Build.0 = Release|Any CPU + {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Debug|Any CPU.Build.0 = Debug|Any CPU {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Debug|ARM64.ActiveCfg = Debug|Any CPU {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Debug|ARM64.Build.0 = Debug|Any CPU {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Debug|x64.ActiveCfg = Debug|Any CPU {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Debug|x64.Build.0 = Debug|Any CPU + {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Release|Any CPU.Build.0 = Release|Any CPU {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Release|ARM64.ActiveCfg = Release|Any CPU {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Release|ARM64.Build.0 = Release|Any CPU {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Release|x64.ActiveCfg = Release|Any CPU {EAC480C1-E85D-40FB-8B23-9A1D7F050C2A}.Release|x64.Build.0 = Release|Any CPU + {A622A3B1-C497-4B14-A61D-BD115CD82769}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A622A3B1-C497-4B14-A61D-BD115CD82769}.Debug|Any CPU.Build.0 = Debug|Any CPU {A622A3B1-C497-4B14-A61D-BD115CD82769}.Debug|ARM64.ActiveCfg = Debug|Any CPU {A622A3B1-C497-4B14-A61D-BD115CD82769}.Debug|ARM64.Build.0 = Debug|Any CPU {A622A3B1-C497-4B14-A61D-BD115CD82769}.Debug|x64.ActiveCfg = Debug|Any CPU {A622A3B1-C497-4B14-A61D-BD115CD82769}.Debug|x64.Build.0 = Debug|Any CPU + {A622A3B1-C497-4B14-A61D-BD115CD82769}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A622A3B1-C497-4B14-A61D-BD115CD82769}.Release|Any CPU.Build.0 = Release|Any CPU {A622A3B1-C497-4B14-A61D-BD115CD82769}.Release|ARM64.ActiveCfg = Release|Any CPU {A622A3B1-C497-4B14-A61D-BD115CD82769}.Release|ARM64.Build.0 = Release|Any CPU {A622A3B1-C497-4B14-A61D-BD115CD82769}.Release|x64.ActiveCfg = Release|Any CPU {A622A3B1-C497-4B14-A61D-BD115CD82769}.Release|x64.Build.0 = Release|Any CPU + {5147DAD3-EB9D-466E-B787-5867CF977377}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5147DAD3-EB9D-466E-B787-5867CF977377}.Debug|Any CPU.Build.0 = Debug|Any CPU {5147DAD3-EB9D-466E-B787-5867CF977377}.Debug|ARM64.ActiveCfg = Debug|Any CPU {5147DAD3-EB9D-466E-B787-5867CF977377}.Debug|ARM64.Build.0 = Debug|Any CPU {5147DAD3-EB9D-466E-B787-5867CF977377}.Debug|x64.ActiveCfg = Debug|Any CPU {5147DAD3-EB9D-466E-B787-5867CF977377}.Debug|x64.Build.0 = Debug|Any CPU + {5147DAD3-EB9D-466E-B787-5867CF977377}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5147DAD3-EB9D-466E-B787-5867CF977377}.Release|Any CPU.Build.0 = Release|Any CPU {5147DAD3-EB9D-466E-B787-5867CF977377}.Release|ARM64.ActiveCfg = Release|Any CPU {5147DAD3-EB9D-466E-B787-5867CF977377}.Release|ARM64.Build.0 = Release|Any CPU {5147DAD3-EB9D-466E-B787-5867CF977377}.Release|x64.ActiveCfg = Release|Any CPU {5147DAD3-EB9D-466E-B787-5867CF977377}.Release|x64.Build.0 = Release|Any CPU + {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Debug|Any CPU.Build.0 = Debug|Any CPU {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Debug|ARM64.ActiveCfg = Debug|Any CPU {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Debug|ARM64.Build.0 = Debug|Any CPU {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Debug|x64.ActiveCfg = Debug|x64 {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Debug|x64.Build.0 = Debug|x64 + {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Release|Any CPU.Build.0 = Release|Any CPU {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Release|ARM64.ActiveCfg = Release|Any CPU {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Release|ARM64.Build.0 = Release|Any CPU {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Release|x64.ActiveCfg = Release|x64 {4783C7B9-31B4-4BBB-9AC2-0EADB1139528}.Release|x64.Build.0 = Release|x64 + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Debug|ARM64.Build.0 = Debug|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Debug|x64.ActiveCfg = Debug|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Debug|x64.Build.0 = Debug|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Release|Any CPU.Build.0 = Release|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Release|ARM64.ActiveCfg = Release|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Release|ARM64.Build.0 = Release|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Release|x64.ActiveCfg = Release|Any CPU + {1BCEB4D4-E062-4A84-8810-9CF63B9AF0DB}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/OmniConverter/Extensions/Audio/AudioCodecType.cs b/OmniConverter/Extensions/Audio/AudioCodecType.cs index df762f2..37e94ca 100644 --- a/OmniConverter/Extensions/Audio/AudioCodecType.cs +++ b/OmniConverter/Extensions/Audio/AudioCodecType.cs @@ -42,6 +42,72 @@ public static string ToExtension(this AudioCodecType codec) }; } + public static bool CanHandleFloatingPoint(this AudioCodecType codec) + { + switch (codec) + { + case AudioCodecType.FLAC: + case AudioCodecType.LAME: + return false; + + default: + return true; + } + } + + public static bool OffersBitrateSetting(this AudioCodecType codec) + { + switch (codec) + { + case AudioCodecType.LAME: + case AudioCodecType.Vorbis: + return true; + + default: + return false; + } + } + + public static bool IsValidFormat(this AudioCodecType codec, int sampleRate, int bitrate, out string Reason) + { + bool checkFailed = false; + string error = string.Empty; + int maxSampleRate = 0; + int maxBitrate = 0; + + switch (codec) + { + case AudioCodecType.FLAC: + maxSampleRate = 384000; + maxBitrate = int.MaxValue; + break; + + case AudioCodecType.LAME: + maxSampleRate = 48000; + maxBitrate = 320; + break; + + case AudioCodecType.Vorbis: + maxSampleRate = 48000; + maxBitrate = 480; + break; + + case AudioCodecType.PCM: + default: + Reason = string.Empty; + return true; + } + + if (sampleRate > maxSampleRate) + error += $"{codec.ToExtension()} does not support sample rates above {maxSampleRate / 1000}kHz."; + + if (bitrate > maxBitrate) + error += $"{(string.IsNullOrEmpty(error) ? "" : "\n\n")}{codec.ToExtension()} does not support bitrates above {maxBitrate}kbps."; + + Reason = error; + return !checkFailed; + } + // Hacky shit public static string? CheckFFMpegDirectory() { diff --git a/OmniConverter/Extensions/Audio/AudioEngine.cs b/OmniConverter/Extensions/Audio/AudioEngine.cs index de18351..6a4a3d4 100644 --- a/OmniConverter/Extensions/Audio/AudioEngine.cs +++ b/OmniConverter/Extensions/Audio/AudioEngine.cs @@ -8,7 +8,8 @@ public enum EngineID Unknown = -1, BASS = 0, XSynth = 1, - MAX = XSynth + FluidSynth = 2, + MAX = FluidSynth } public abstract class AudioEngine : IDisposable diff --git a/OmniConverter/Extensions/Audio/Renderers/BASSRenderer.cs b/OmniConverter/Extensions/Audio/Renderers/BASSRenderer.cs index f7f0400..056501b 100644 --- a/OmniConverter/Extensions/Audio/Renderers/BASSRenderer.cs +++ b/OmniConverter/Extensions/Audio/Renderers/BASSRenderer.cs @@ -59,7 +59,7 @@ . .#+ -+- .LimitToRange((int)GlobalSynthSettings.InterpolationType.None, (int)GlobalSynthSettings.InterpolationType.Max) - 1; - Bass.Configure(Configuration.MidiVoices, CachedSettings.BASS.MaxVoices); + Bass.Configure(Configuration.MidiVoices, CachedSettings.Synth.MaxVoices); Bass.Configure(Configuration.SRCQuality, interp); Bass.Configure(Configuration.SampleSRCQuality, interp); diff --git a/OmniConverter/Extensions/Audio/Renderers/FluidSynthRenderer.cs b/OmniConverter/Extensions/Audio/Renderers/FluidSynthRenderer.cs new file mode 100644 index 0000000..90e64a6 --- /dev/null +++ b/OmniConverter/Extensions/Audio/Renderers/FluidSynthRenderer.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Media; +using NFluidsynth; + +namespace OmniConverter +{ + public class FluidSynthEngine : AudioEngine + { + private List _managedSfArray = []; + private NFluidsynth.Settings _fluidSynthSettings; + private Settings _cachedSettings; + + public unsafe FluidSynthEngine(CSCore.WaveFormat waveFormat, Settings settings) : base(waveFormat, settings, false) + { + Debug.PrintToConsole(Debug.LogType.Message, $"Preparing FluidSynth settings..."); + + Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); + + _fluidSynthSettings = new(); + + _fluidSynthSettings[ConfigurationKeys.SynthAudioChannels].IntValue = 1; + _fluidSynthSettings[ConfigurationKeys.SynthSampleRate].DoubleValue = settings.Synth.SampleRate; + _fluidSynthSettings[ConfigurationKeys.SynthPolyphony].IntValue = settings.Synth.MaxVoices.LimitToRange(1, 65535); + _fluidSynthSettings[ConfigurationKeys.SynthThreadSafeApi].IntValue = 1; + _fluidSynthSettings[ConfigurationKeys.SynthMinNoteLength].IntValue = 0; + + _cachedSettings = settings; + + Initialized = true; + + Debug.PrintToConsole(Debug.LogType.Message, $"FluidSynth settings prepared..."); + + return; + } + + protected override void Dispose(bool disposing) + { + if (_disposed) + return; + + if (Initialized) + { + _fluidSynthSettings.Dispose(); + } + + _disposed = true; + } + + public NFluidsynth.Settings GetFluidSynthSettings() => _fluidSynthSettings; + public Settings GetConverterSettings() => _cachedSettings; + } + + public class FluidSynthRenderer : MIDIRenderer + { + public Synth? handle { get; private set; } = null; + private long length = 0; + private ulong sfCount = 0; + private List _managedSfArray = []; + + private float[]? outL = null, outR = null; + private FluidSynthEngine reference; + + public FluidSynthRenderer(FluidSynthEngine fluidsynth) : base(fluidsynth.WaveFormat, fluidsynth.CachedSettings.Synth.Volume, false) + { + reference = fluidsynth; + + if (UniqueID == string.Empty) + return; + + if (fluidsynth == null) + return; + + Debug.PrintToConsole(Debug.LogType.Message, $"Stream unique ID: {UniqueID}"); + + handle = new(reference.GetFluidSynthSettings()); + var tmp = reference.GetConverterSettings(); + + foreach (var sf in tmp.SoundFontsList) + { + var sfhandle = handle.LoadSoundFont(sf.SoundFontPath, true); + + if (sfhandle != 0) + _managedSfArray.Add(sfhandle); + } + + if (_managedSfArray.Count > 0) + { + Debug.PrintToConsole(Debug.LogType.Message, $"{UniqueID} - Stream is open."); + + Initialized = true; + } + } + + private bool IsError(string Error) + { + return false; + } + + public override unsafe int Read(float[] buffer, int offset, long delta, int count) + { + if (handle == null) + return 0; + + if (outL == null) + outL = new float[count / 2]; + + if (outR == null) + outR = new float[count / 2]; + + lock (Lock) + { + fixed (float* buff = buffer) + { + fixed (float* toutL = outL) + { + fixed (float* toutR = outR) + { + var offsetBuff = buff + offset; + + // Zero out the buffer + for (int i = 0; i < count / 2; i++) + { + toutL[i] = 0.0f; + toutR[i] = 0.0f; + } + + float*[] whatisthis = { toutL, toutR }; + + fixed (float** hell = whatisthis) + handle.Process(count / 2, 2, hell, 2, hell); + + // Copy it in a way that makes F*****G SENSE, + // CHRIST FLUIDSYNTH, CAN'T YOU JUST BE F*****G NORMAL????? + for (int i = 0; i < count / 2; i++) + { + offsetBuff[i * 2] = toutL[i]; + offsetBuff[i * 2 + 1] = toutR[i]; + } + } + + } + } + } + + length += count; + return count; + } + + public override void SystemReset() + { + if (handle == null) + return; + + handle.SystemReset(); + } + + public override bool SendCustomFXEvents(int channel, short reverb, short chorus) + { + return true; + } + + public override void SendEvent(byte[] data) + { + if (handle == null) + return; + + var status = data[0]; + var chan = status & 0xF; + var param1 = data[1]; + var param2 = data.Length >= 3 ? data[2] : (byte)0; + + switch ((MIDIEventType)(status & 0xF0)) + { + case MIDIEventType.NoteOn: + if (reference.CachedSettings.Event.FilterVelocity && param2 >= reference.CachedSettings.Event.VelocityLow && param2 <= reference.CachedSettings.Event.VelocityHigh) + return; + if (reference.CachedSettings.Event.FilterKey && (param1 < reference.CachedSettings.Event.KeyLow || param1 > reference.CachedSettings.Event.KeyHigh)) + return; + + if (param1 == 0) + { + handle.NoteOff(chan, param1); + } + else handle.NoteOn(chan, param1, param2); + return; + + case MIDIEventType.NoteOff: + if (reference.CachedSettings.Event.FilterKey && (param1 < reference.CachedSettings.Event.KeyLow || param1 > reference.CachedSettings.Event.KeyHigh)) + return; + + handle.NoteOff(chan, param1); + return; + + case MIDIEventType.PatchChange: + handle.ProgramChange(chan, param1); + return; + + case MIDIEventType.ChannelPressure: + handle.ChannelPressure(chan, param1); + return; + + case MIDIEventType.Aftertouch: + handle.KeyPressure(chan, param1, param2); + return; + + case MIDIEventType.CC: + handle.CC(chan, param1, param2); + return; + + case MIDIEventType.PitchBend: + handle.PitchBend(chan, (param2 << 7) | param1); + return; + + case MIDIEventType.SystemMessageStart: + { + switch ((MIDIEventType)status) + { + case MIDIEventType.SystemMessageStart: + { + string sysexbuf = string.Empty; + + foreach (byte ch in data) + sysexbuf += $"{ch:X}"; + + try + { + if (handle.Sysex(data, null, false)) + Debug.PrintToConsole(Debug.LogType.Message, $"SysEx parsed! >> {sysexbuf}"); + } + catch + { + + Debug.PrintToConsole(Debug.LogType.Error, $"Invalid SysEx! >> {sysexbuf}"); + } + + } + return; + + default: + break; + } + + break; + } + + default: + break; + } + } + + public override void RefreshInfo() + { + if (handle == null) + return; + + ActiveVoices = (ulong)handle.ActiveVoiceCount; + } + + public override void SendEndEvent() + { + if (handle == null) + return; + + for (int i = 0; i < 16; i++) + handle.AllNotesOff(i); + + SystemReset(); + } + + public override long Position + { + get { return length; } + set { throw new NotSupportedException("Can't set position."); } + } + + public override long Length + { + get { return length; } + } + + protected override void Dispose(bool disposing) + { + if (Disposed) + return; + + if (_managedSfArray.Count > 0) + { + for (int i = 0; i < _managedSfArray.Count; i++) + handle?.UnloadSoundFont(_managedSfArray[i], false); + } + + if (handle != null) + handle.Dispose(); + + UniqueID = string.Empty; + CanSeek = false; + + Initialized = false; + Disposed = true; + } + } + +} diff --git a/OmniConverter/Extensions/MIDI/FLP.cs b/OmniConverter/Extensions/MIDI/FLP.cs index 5250acf..0ec44f1 100644 --- a/OmniConverter/Extensions/MIDI/FLP.cs +++ b/OmniConverter/Extensions/MIDI/FLP.cs @@ -49,7 +49,7 @@ public class FLP : MIDI static bool ForceColor { get; set; } static bool DisableEcho { get; set; } - public FLP(MidiFile? loadedFile, IEnumerable metaEvent, string name, long id, string path, TimeSpan timeLength, int tracks, long noteCount, ulong fileSize, ulong[] eventCounts) : base(loadedFile, metaEvent, name, id, path, timeLength, tracks, noteCount, fileSize, eventCounts) + public FLP(MidiFile? loadedFile, IEnumerable metaEvent, string name, long id, string path, TimeSpan timeLength, int tracks, long noteCount, ulong fileSize, ulong[] eventCounts, double ppqn) : base(loadedFile, metaEvent, name, id, path, timeLength, tracks, noteCount, fileSize, eventCounts, ppqn) { } public FLP(string test) : base(test) @@ -243,7 +243,7 @@ private static MemoryStream ConvertToMemoryMID(string filePath, ParallelOptions seconds += e.DeltaTime; } - return new FLP(midi, mergedMetaEvents, name, id, filePath, TimeSpan.FromSeconds(seconds), midi.TrackCount, noteCount, (ulong)(new FileInfo(filePath).Length), eventCounts); + return new FLP(midi, mergedMetaEvents, name, id, filePath, TimeSpan.FromSeconds(seconds), midi.TrackCount, noteCount, (ulong)(new FileInfo(filePath).Length), eventCounts, midi.PPQ); } catch (OperationCanceledException) { } catch (Exception e) diff --git a/OmniConverter/Extensions/MIDI/MIDI.cs b/OmniConverter/Extensions/MIDI/MIDI.cs index f227621..91f8ae3 100644 --- a/OmniConverter/Extensions/MIDI/MIDI.cs +++ b/OmniConverter/Extensions/MIDI/MIDI.cs @@ -62,6 +62,7 @@ public class MIDI : ObservableObject public string HumanReadableSize { get => MiscFunctions.BytesToHumanReadableSize(_fileSize); } public MidiFile? LoadedFile { get => _loadedFile; } public ulong[] EventCounts { get => _eventCounts; } + public double PPQ { get => _ppqn; } public ulong TotalEventCount { get { @@ -85,8 +86,9 @@ public ulong TotalEventCount { private long _noteCount; private ulong _fileSize; private ulong[] _eventCounts; + private double _ppqn; - public MIDI(MidiFile? loadedFile, IEnumerable metaEvent, string name, long id, string path, TimeSpan timeLength, int tracks, long noteCount, ulong fileSize, ulong[] eventCounts) + public MIDI(MidiFile? loadedFile, IEnumerable metaEvent, string name, long id, string path, TimeSpan timeLength, int tracks, long noteCount, ulong fileSize, ulong[] eventCounts, double ppqn) { _loadedFile = loadedFile; _metaEvent = metaEvent; @@ -98,6 +100,7 @@ public MIDI(MidiFile? loadedFile, IEnumerable metaEvent, string name, _noteCount = noteCount; _fileSize = fileSize; _eventCounts = eventCounts; + _ppqn = ppqn; } public MIDI(string test) @@ -227,7 +230,7 @@ public static List> GetMetaEvents(IEnumerable= 10000000) + if (_cachedSettings.Renderer == EngineID.BASS && _cachedSettings.Synth.MaxVoices >= 10000000) { var biggestMidi = _midis.MaxBy(x => x.Tracks); - var v = _cachedSettings.BASS.MaxVoices; + var v = _cachedSettings.Synth.MaxVoices; var mem = ((ulong)v * 312) * (ulong)_cachedSettings.Render.ThreadsCount; var memusage = MiscFunctions.BytesToHumanReadableSize(mem); @@ -129,22 +139,16 @@ public override bool StartWork() } } - switch (_cachedSettings.Encoder.AudioCodec) + string Reason = string.Empty; + bool codecCheck = _cachedSettings.Encoder.AudioCodec.IsValidFormat(_cachedSettings.Synth.SampleRate, _cachedSettings.Encoder.AudioBitrate, out Reason); + if (!codecCheck) { - case AudioCodecType.LAME: - if (IsInvalidFormat(AudioCodecType.LAME, 48000, 320)) - return false; + if (_cachedSettings.Program.AudioEvents) + MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Error, true); - break; - - case AudioCodecType.Vorbis: - if (IsInvalidFormat(AudioCodecType.Vorbis, 48000, 480)) - return false; + MessageBox.Show(_winRef, Reason, "OmniConverter - Error", MsBox.Avalonia.Enums.ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error); - break; - - default: - break; + return false; } } @@ -265,18 +269,24 @@ private void ConversionFunc() _audioRenderer = new XSynthEngine(_waveFormat, _cachedSettings); break; + case EngineID.FluidSynth: + _audioRenderer = new FluidSynthEngine(_waveFormat, _cachedSettings); + break; + case EngineID.BASS: default: - // do this hacky crap to get the voice change to work - _audioRenderer = new BASSEngine(_waveFormat, _cachedSettings); - _audioRenderer.Dispose(); - _audioRenderer = new BASSEngine(_waveFormat, _cachedSettings); break; } if (_audioRenderer.Initialized) { + // Cache variables + _autoDev = _audioRenderer is BASSEngine; + _separateTrackFiles = _cachedSettings.Render.PerTrackFile; + _audCodec = _cachedSettings.Encoder.AudioCodec; + _audLimiter = !_audCodec.CanHandleFloatingPoint() || _cachedSettings.Synth.AudioLimiter; + if (_cachedSettings.Render.PerTrackMode) { if (_midis.Count > 1) @@ -323,57 +333,16 @@ private void GetTotalEventsCount() _validator.SetTotalEventsCount(totalEvents); } - private bool IsInvalidFormat(AudioCodecType codec, int maxSampleRate, int maxBitrate) - { - string error = string.Empty; - - if (_cachedSettings.Synth.SampleRate > maxSampleRate || _cachedSettings.Encoder.AudioBitrate > maxBitrate) - { - if (_cachedSettings.Program.AudioEvents) - MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Error, true); - - if (_cachedSettings.Synth.SampleRate > maxSampleRate) - error += $"{codec.ToExtension()} does not support sample rates above {maxSampleRate / 1000}kHz."; - - if (_cachedSettings.Encoder.AudioBitrate > maxBitrate) - error += $"{(string.IsNullOrEmpty(error) ? "" : "\n\n")}{codec.ToExtension()} does not support bitrates above {maxBitrate}kbps."; - - - MessageBox.Show(_winRef, error, "OmniConverter - Error", MsBox.Avalonia.Enums.ButtonEnum.Ok, MsBox.Avalonia.Enums.Icon.Error); - - return true; - } - - return false; - } - - private bool CheckIfCodecCanFloat(AudioCodecType codec) - { - switch (codec) - { - case AudioCodecType.LAME: - return false; - - default: - return true; - } - } - private void PerMIDIConversion() { if (_audioRenderer == null) return; - // Cache settings - var autoDevice = _audioRenderer is BASSEngine; - var codec = _cachedSettings.Encoder.AudioCodec; - var audioLimiter = !CheckIfCodecCanFloat(codec) || _cachedSettings.Synth.AudioLimiter; - AutoFillInfo(ConvStatus.Prep); GetTotalEventsCount(); if (_cachedSettings.Program.AudioEvents) - MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Start, !autoDevice); + MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Start, !_autoDev); _convElapsedTime.Reset(); _convElapsedTime.Start(); @@ -393,8 +362,8 @@ private void PerMIDIConversion() // Prepare the filename string fallbackFile = GetOutputFilename(midi.Name, AudioCodecType.PCM, false); - string outputFile1 = GetOutputFilename(midi.Name, codec, true); - string outputFile2 = GetOutputFilename(midi.Name, codec, false); + string outputFile1 = GetOutputFilename(midi.Name, _audCodec, true); + string outputFile2 = GetOutputFilename(midi.Name, _audCodec, false); Debug.PrintToConsole(Debug.LogType.Message, $"Output file: {outputFile1}"); @@ -458,7 +427,7 @@ private void PerMIDIConversion() IWaveSource MStream; Limiter BAC; - if (audioLimiter && _waveFormat.BitsPerSample == 32) + if (_audLimiter && _waveFormat.BitsPerSample == 32) { Debug.PrintToConsole(Debug.LogType.Message, "LoudMax enabled."); BAC = new Limiter(msm, 0.1); @@ -481,13 +450,13 @@ private void PerMIDIConversion() FDestination.Dispose(); FOpen.Dispose(); - if (codec != AudioCodecType.PCM) + if (_audCodec != AudioCodecType.PCM) { try { AutoFillInfo(ConvStatus.EncodingAudio); - var ffcodec = codec.ToFFMpegCodec(); + var ffcodec = _audCodec.ToFFMpegCodec(); if (ffcodec == null) throw new Exception(); @@ -522,11 +491,11 @@ private void PerMIDIConversion() if (!_cancToken.IsCancellationRequested) { if (_cachedSettings.Program.AudioEvents) - MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Finish, !autoDevice); + MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Finish, !_autoDev); MiscFunctions.PerformShutdownCheck(_convElapsedTime); } - else MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Error, !autoDevice); + else MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Error, !_autoDev); Dispatcher.UIThread.Post(_winRef.Close); } @@ -536,14 +505,8 @@ private void PerTrackConversion() if (_audioRenderer == null) return; - // Cache settings - var autoDevice = _audioRenderer is BASSEngine; - var perTrackFile = _cachedSettings.Render.PerTrackFile; - var codec = _cachedSettings.Encoder.AudioCodec; - var audioLimiter = !CheckIfCodecCanFloat(codec) || _cachedSettings.Synth.AudioLimiter; - if (_cachedSettings.Program.AudioEvents) - MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Start, !autoDevice); + MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Start, !_autoDev); GetTotalEventsCount(); @@ -568,9 +531,8 @@ private void PerTrackConversion() using (MultiStreamMerger msm = new(_waveFormat)) { - // Per track! - if (perTrackFile) + if (_separateTrackFiles) { // We do, create folder folder += string.Format("/{0}", Path.GetFileNameWithoutExtension(midi.Name)); @@ -583,7 +545,7 @@ private void PerTrackConversion() folder += "/"; - Parallel.For(midiData.Count(), _parallelOptions, track => + Parallel.For(_validator.GetTotalTracks(), _parallelOptions, track => { try { @@ -612,15 +574,15 @@ private void PerTrackConversion() Debug.PrintToConsole(Debug.LogType.Message, $"ConvertWorker => T{track}, {midi.Length.TotalSeconds}"); // Per track! - if (perTrackFile) + if (_separateTrackFiles) { // Prepare the filename fallbackFile = outputFile2 = string.Format("{0}Track {1}{2}", folder, track, AudioCodecType.PCM.ToExtension()); outputFile1 = string.Format("{0}Track {1}{2}{3}", - folder, track, codec.ToExtension(), codec != AudioCodecType.PCM ? ".swp" : ""); + folder, track, _audCodec.ToExtension(), _audCodec != AudioCodecType.PCM ? ".swp" : ""); outputFile2 = string.Format("{0}Track {1}{2}", - folder, track, codec.ToExtension()); + folder, track, _audCodec.ToExtension()); // Check if file already exists if (File.Exists(outputFile1)) @@ -630,9 +592,9 @@ private void PerTrackConversion() fallbackFile = outputFile2 = string.Format("{0}Track {1} - {2}{3}", folder, track, date, AudioCodecType.PCM.ToExtension()); outputFile1 = string.Format("{0}Track {1} - {2}{3}{4}", - folder, track, date, codec.ToExtension(), codec != AudioCodecType.PCM ? ".swp" : ""); + folder, track, date, _audCodec.ToExtension(), _audCodec != AudioCodecType.PCM ? ".swp" : ""); outputFile2 = string.Format("{0}Track {1} - {2}{3}", - folder, track, date, codec.ToExtension()); + folder, track, date, _audCodec.ToExtension()); } sampleWriter = trackMsm.GetWriter(); @@ -675,13 +637,13 @@ private void PerTrackConversion() Dispatcher.UIThread.Post(() => trackPanel?.Dispose()); } - if (perTrackFile) + if (_separateTrackFiles) { // Reset MSM position trackMsm.Position = 0; IWaveSource exportSource; - if (audioLimiter && _waveFormat.BitsPerSample == 32) + if (_audLimiter && _waveFormat.BitsPerSample == 32) { Debug.PrintToConsole(Debug.LogType.Message, "LoudMax enabled."); var BAC = new Limiter(trackMsm, 0.1); @@ -707,13 +669,13 @@ private void PerTrackConversion() fileWriter.Dispose(); } - if (codec != AudioCodecType.PCM) + if (_audCodec != AudioCodecType.PCM) { try { Debug.PrintToConsole(Debug.LogType.Message, $"Converting {outputFile1} to final user selected codec..."); - var ffcodec = codec.ToFFMpegCodec(); + var ffcodec = _audCodec.ToFFMpegCodec(); if (ffcodec == null) throw new Exception(); @@ -749,21 +711,21 @@ private void PerTrackConversion() try { - if (!perTrackFile) + if (!_separateTrackFiles) { // Reset MSM position msm.Position = 0; // Time to save the file string fallbackFile = GetOutputFilename(midi.Name, AudioCodecType.PCM, false); - var outputFile1 = GetOutputFilename(midi.Name, codec, true); - var outputFile2 = GetOutputFilename(midi.Name, codec, false); + var outputFile1 = GetOutputFilename(midi.Name, _audCodec, true); + var outputFile2 = GetOutputFilename(midi.Name, _audCodec, false); Debug.PrintToConsole(Debug.LogType.Message, $"Output file: {outputFile1}"); // Prepare wave source IWaveSource? MStream = null; - if (audioLimiter && _waveFormat.BitsPerSample == 32) + if (_audLimiter && _waveFormat.BitsPerSample == 32) { Debug.PrintToConsole(Debug.LogType.Message, "LoudMax enabled."); var BAC = new Limiter(msm, 0.1); @@ -792,13 +754,13 @@ private void PerTrackConversion() fileWriter.Dispose(); } - if (codec != AudioCodecType.PCM) + if (_audCodec != AudioCodecType.PCM) { try { AutoFillInfo(ConvStatus.EncodingAudio); - var ffcodec = codec.ToFFMpegCodec(); + var ffcodec = _audCodec.ToFFMpegCodec(); if (ffcodec == null) throw new Exception(); @@ -836,11 +798,11 @@ private void PerTrackConversion() if (!_cancToken.IsCancellationRequested) { if (_cachedSettings.Program.AudioEvents) - MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Finish, !autoDevice); + MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Finish, !_autoDev); MiscFunctions.PerformShutdownCheck(_convElapsedTime); } - else MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Error, !autoDevice); + else MiscFunctions.PlaySound(MiscFunctions.ConvSounds.Error, !_autoDev); Dispatcher.UIThread.Post(_winRef.Close); } @@ -940,6 +902,10 @@ public void Process(ISampleWriter output, WaveFormat waveFormat, CancellationTok _midiRenderer = new BASSRenderer(bass); break; + case FluidSynthEngine fluidsynth: + _midiRenderer = new FluidSynthRenderer(fluidsynth); + break; + default: break; } diff --git a/OmniConverter/Extensions/Settings.cs b/OmniConverter/Extensions/Settings.cs index c8ef9b1..61280f4 100644 --- a/OmniConverter/Extensions/Settings.cs +++ b/OmniConverter/Extensions/Settings.cs @@ -18,6 +18,9 @@ public enum InterpolationType Max = Point64 } + [JsonProperty] + public int MaxVoices = 2048; + [JsonProperty] public double Volume = 1.0; @@ -51,9 +54,6 @@ public object Clone() [JsonObject] public class BASSSettings : ICloneable { - [JsonProperty] - public int MaxVoices = 2048; - [JsonProperty] public bool DisableEffects = true; diff --git a/OmniConverter/Forms/InfoWindow.axaml.cs b/OmniConverter/Forms/InfoWindow.axaml.cs index d925074..2694b65 100644 --- a/OmniConverter/Forms/InfoWindow.axaml.cs +++ b/OmniConverter/Forms/InfoWindow.axaml.cs @@ -30,9 +30,9 @@ public InfoWindow() try { bmidiVer = BassMidi.Version; } catch { } try { xsynthVer = XSynth.Version; } catch { } - if (Environment.ProcessPath != null) + if (pp != null) { - var fcv = FileVersionInfo.GetVersionInfo(Environment.ProcessPath); + var fcv = FileVersionInfo.GetVersionInfo(pp); if (fcv.ProductVersion != null && fcv.ProductVersion.Contains("dev")) { diff --git a/OmniConverter/Forms/MainWindow.axaml b/OmniConverter/Forms/MainWindow.axaml index d86814c..b284fe5 100644 --- a/OmniConverter/Forms/MainWindow.axaml +++ b/OmniConverter/Forms/MainWindow.axaml @@ -101,7 +101,7 @@ - + diff --git a/OmniConverter/Forms/MainWindow.axaml.cs b/OmniConverter/Forms/MainWindow.axaml.cs index d34301e..73fdfab 100644 --- a/OmniConverter/Forms/MainWindow.axaml.cs +++ b/OmniConverter/Forms/MainWindow.axaml.cs @@ -149,7 +149,7 @@ public void SelectedMIDIChanged(object? sender, SelectionChangedEventArgs e) InfoFullPath.Content = item.Path; InfoName.Content = item.Name; InfoNoteCount.Content = item.Notes.ToString("N0"); - InfoLength.Content = item.HumanReadableTime; + InfoLength.Content = $"{item.HumanReadableTime} ({item.PPQ}PPQN)"; InfoTracks.Content = item.Tracks; InfoSize.Content = item.HumanReadableSize; return; diff --git a/OmniConverter/Forms/SettingsWindow.axaml b/OmniConverter/Forms/SettingsWindow.axaml index 6a602a1..683ddb1 100644 --- a/OmniConverter/Forms/SettingsWindow.axaml +++ b/OmniConverter/Forms/SettingsWindow.axaml @@ -24,6 +24,7 @@ BASS XSynth + FluidSynth diff --git a/OmniConverter/Forms/SettingsWindow.axaml.cs b/OmniConverter/Forms/SettingsWindow.axaml.cs index f754a72..7678daa 100644 --- a/OmniConverter/Forms/SettingsWindow.axaml.cs +++ b/OmniConverter/Forms/SettingsWindow.axaml.cs @@ -62,7 +62,7 @@ private void CheckSettings(object? sender, RoutedEventArgs e) XSynth_ThreadingSelection.SelectedIndex = ((int)Program.Settings.XSynth.Threading) .LimitToRange(XSynthSettings.ThreadingType.None, XSynthSettings.ThreadingType.Max); - BASS_MaxVoices.Value = Program.Settings.BASS.MaxVoices; + BASS_MaxVoices.Value = Program.Settings.Synth.MaxVoices; XSynth_MaxLayers.Value = Program.Settings.XSynth.MaxLayers; BASS_DisableFX.IsChecked = Program.Settings.BASS.DisableEffects; BASS_NoteOff1.IsChecked = Program.Settings.BASS.NoteOff1; @@ -163,22 +163,14 @@ private void AudioCodecChanged(object? sender, SelectionChangedEventArgs e) { if (AudioCodec != null) { - AudioBitrate.IsEnabled = AudioCodec.SelectedIndex > 1; + var audCodec = (AudioCodecType)AudioCodec.SelectedIndex; + var canHandleFP = audCodec.CanHandleFloatingPoint(); - switch ((AudioCodecType)AudioCodec.SelectedIndex) - { - case AudioCodecType.LAME: - ForceLimitAudio = true; - AudioLimiter.IsChecked = ForceLimitAudio; - break; + ForceLimitAudio = !canHandleFP; - default: - ForceLimitAudio = false; - AudioLimiter.IsChecked = Program.Settings.Synth.AudioLimiter; - break; - } - - AudioLimiter.IsEnabled = !ForceLimitAudio; + AudioBitrate.IsEnabled = audCodec.OffersBitrateSetting(); + AudioLimiter.IsChecked = canHandleFP ? Program.Settings.Synth.AudioLimiter : true; + AudioLimiter.IsEnabled = canHandleFP; } } @@ -202,7 +194,7 @@ private void AudioRendererChanged(object? sender, SelectionChangedEventArgs e) switch ((EngineID)SelectedRenderer.SelectedIndex) { case EngineID.BASS: - BASS_MaxVoices.Value = Program.Settings.BASS.MaxVoices; + BASS_MaxVoices.Value = Program.Settings.Synth.MaxVoices; BASSSettingsPanel.IsVisible = true; XSynthSettingsPanel.IsVisible = false; @@ -214,6 +206,7 @@ private void AudioRendererChanged(object? sender, SelectionChangedEventArgs e) BASSSettingsPanel.IsVisible = false; XSynthSettingsPanel.IsVisible = true; + break; default: @@ -331,7 +324,7 @@ private void ApplySettings(object? sender, RoutedEventArgs e) Program.Settings.Synth.SampleRate = Convert.ToInt32(((ComboBoxItem)item).Content); if (BASS_MaxVoices.Value != null) - Program.Settings.BASS.MaxVoices = (int)BASS_MaxVoices.Value; + Program.Settings.Synth.MaxVoices = (int)BASS_MaxVoices.Value; if (XSynth_MaxLayers.Value != null) Program.Settings.XSynth.MaxLayers = (ulong)XSynth_MaxLayers.Value; diff --git a/OmniConverter/Forms/SoundFontsManager.axaml.cs b/OmniConverter/Forms/SoundFontsManager.axaml.cs index 5ea5138..7883be1 100644 --- a/OmniConverter/Forms/SoundFontsManager.axaml.cs +++ b/OmniConverter/Forms/SoundFontsManager.axaml.cs @@ -16,12 +16,16 @@ public SoundFontsManager() { InitializeComponent(); - SoundFontListView.ItemsSource = Program.SoundFontsManager.GetSoundFontList(); - AddHandler(DragDrop.DropEvent, FileDropInit); AddHandler(DragDrop.DragEnterEvent, FileDropEnter); // AddHandler(DragDrop.DragLeaveEvent, FileDropLeave); + try + { + SoundFontListView.ItemsSource = Program.SoundFontsManager.GetSoundFontList(); + } + catch { } + switch (Program.Settings.Renderer) { case EngineID.BASS: diff --git a/OmniConverter/OmniConverter.csproj b/OmniConverter/OmniConverter.csproj index c570349..da030e4 100644 --- a/OmniConverter/OmniConverter.csproj +++ b/OmniConverter/OmniConverter.csproj @@ -38,7 +38,6 @@ - @@ -47,6 +46,7 @@ +