using Jacobi.Vst.Core; using Jacobi.Vst.Host.Interop; using NAudio.Midi; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text.Json.Serialization; using System.Windows; using System.Windows.Media.Media3D; using Windows.UI.Core; using WinRT; using static QuikDawEditor.EDITING.MidiMethods; using static QuikDawEditor.EDITING.MiscMethods; using static QuikDawEditor.EDITING.StaticProperties; namespace QuikDawEditor.VST; public class ActiveVstPlugin : VstPluginReference { //[JsonConstructor] //public ActiveVstPlugin(ObservableCollection VstPluginChannels) { this.VstPluginChannels = VstPluginChannels; } public ActiveVstPlugin() { } //public ActiveVstPlugin() //{ // for (int i = 0; i < 16; i++) // { // VstPluginChannels.Add(new GMChannel() { GMChannelNo = i}); // VstPluginChannels[i].chunkUpdated += ActiveVstPlugin_chunkUpdated; // } //} //private void ActiveVstPlugin_chunkUpdated(byte[] updateBytes, int updatePointer) //{ // byte[] chunkbytes = this.myContext.PluginCommandStub.Commands.GetChunk(true); // Array.Copy(updateBytes, 0, chunkbytes, updatePointer, updateBytes.Length); // myContext.PluginCommandStub.Commands.SetChunk(chunkbytes, true); // this.PluginChunk = chunkbytes; //} public int Channel = 1; private void UpdateChunk(byte[] updateBytes, int updatePointer) { if (myContext != null) { Array.Copy(updateBytes, 0, this.PluginChunk, updatePointer, updateBytes.Length); myContext.PluginCommandStub.Commands.SetChunk(this.PluginChunk, true); } } public bool VolumeAdjustable { get { return IsGeneralMidi; } } public bool HasEditor { get { return myContext == null || IsGeneralMidi ? false : myContext.PluginInfo.Flags.HasFlag(VstPluginFlags.HasEditor); } } //public ObservableCollection VstPluginChannels { get; set; } = new ObservableCollection(); private bool _IsActive = false; public bool IsActive { get { return _IsActive && ReturnedWithoutError; } set { _IsActive = value; NotifyPropertyChanged(nameof(IsActive)); } } private Thickness _Margin; [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public Thickness Margin { get { return _Margin; } set { _Margin = value; NotifyPropertyChanged(nameof(Margin)); } } private bool _IsEnabled = true; [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public bool IsEnabled { get { return _IsEnabled; } set { _IsEnabled = value; NotifyPropertyChanged(nameof(IsEnabled)); } } private float _Volume = 1; public float Volume { get { return _Volume; } set { if (Math.Abs(_Volume - value) > 0.1 || (value == 1 & _Volume != 1) || (value == 0 & _Volume != 0)) { _Volume = value; int offset = this.Channel == 10 ? 9 * 0x28 : 0; UpdateChunk(new byte[] { (byte)(value * 0x64) }, 0xA83 + offset); } } } internal List midEventsToSend = new List(); public bool IsEditorOpen { get; set; } = false; public bool causedRunningError = false; public string GetProgName { get { return myContext == null ? "" : myContext.PluginCommandStub.Commands.GetProgramName(); } } public void SelectedProgramChanged() { NotifyPropertyChanged(nameof(SelectedProgramName)); } public bool IsGeneralMidi { get { return this.PluginName == "Reality GM Instruments"; } } public Point pluginWindowLoc { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public ObservableCollection ActiveVstPrograms { get { return this.IsGeneralMidi ? GeneralMidiPrograms : VstPrograms; } } private int _SelectedVstProgramIndex = 0; public int SelectedVstProgramIndex { get { return _SelectedVstProgramIndex; } set { _SelectedVstProgramIndex = value; if (VstPrograms.Count > value) VstPrograms[value].IsSelected = true; NotifyPropertyChanged(nameof(SelectedVstProgramIndex)); } } internal void UpdateGetProgName() { NotifyPropertyChanged(nameof(SelectedProgramName)); } public string SelectedProgramName { get { return (this.IsGeneralMidi ? ActiveVstPrograms[SelectedVstProgramIndex].ProgramName : (ActiveVstPrograms.Count == 0 || SelectedVstProgramIndex == -1) ? GetProgName : ActiveVstPrograms[SelectedVstProgramIndex].ProgramName); } } public static VstAudioBufferManager inputMgr; public static VstAudioBufferManager outputMgr; public VstAudioBuffer[] inputBuffers; public VstAudioBuffer[] outputBuffers; public void OpenActivePluginContext() { try { myHostCmdStub = new HostCommandStub(); myContext = VstPluginContext.Create(VstDllFileFullPath, myHostCmdStub); myHostCmdStub.PluginCalled += new EventHandler(HostCmdStub_PluginCalled); myHostCmdStub.WindowSizeChanged += HostCmdStub_WindowSizeChanged; //Console.WriteLine("Opened context from: " + VstDllFilePath); // add custom data to the context myContext.Set("PluginPath", VstDllFileFullPath); myContext.Set("HostCmdStub", myHostCmdStub); myContext.PluginCommandStub.Commands.Open(); InitializeVstAudioBuffers(); } catch (Exception e) { MessageBox.Show(e.ToString(), "Error reading in vst plugin . . . ", MessageBoxButton.OK, MessageBoxImage.Exclamation, MessageBoxResult.OK, MessageBoxOptions.DefaultDesktopOnly); } } double prevTempo; int prevBeatsPerMeasure; internal void HostCmdStub_WindowSizeChanged(int width, int height) { edHostWin.WinFormsHost.Width = width; edHostWin.WinFormsHost.Height = height; } internal void HostCmdStub_PluginCalled(object sender, PluginCalledEventArgs e) { HostCommandStub hostCmdStub = (HostCommandStub)sender; //if (e.Message.StartsWith("GetPluginInfo")) { MessageBox.Show("got plugininfo"); } if (e.Message.StartsWith("GetTimeInfo")) { if (prevTempo != projPlayer.BeatsPerMinuteProject) { hostCmdStub.myVstTimeInfo.Tempo = projPlayer.BeatsPerMinuteProject; prevTempo = projPlayer.BeatsPerMinuteProject; } if (prevBeatsPerMeasure != editingProject.BeatsPerMeasure) { hostCmdStub.myVstTimeInfo.TimeSignatureNumerator = editingProject.BeatsPerMeasure; prevBeatsPerMeasure = editingProject.BeatsPerMeasure; } hostCmdStub.myVstTimeInfo.PpqPosition = (projPlayer.CurrentPlayingPosMS % MillisecondsPerBeat) / MillisecondsPerBeat; hostCmdStub.myVstTimeInfo.BarStartPosition = 1; hostCmdStub.myVstTimeInfo.SampleRate = 44100; if (projPlayer.IsProjectPlaying) hostCmdStub.myVstTimeInfo.Flags = playingFlags; else hostCmdStub.myVstTimeInfo.Flags = nonPlayingFlags; } //Debug.WriteLine("ppqpos=" + hostCmdStub.myVstTimeInfo.PpqPosition.ToString()); //Debug.WriteLine("message=" + e.Message); //Debug.WriteLine ("flags=" + hostCmdStub.myVstTimeInfo.Flags.ToString()); } private void InitializeVstAudioBuffers() { int inputCount = myContext.PluginInfo.AudioInputCount; int outputCount = myContext.PluginInfo.AudioOutputCount; /// wrap these in using statements to automatically call Dispose and cleanup the unmanaged memory. inputMgr = new VstAudioBufferManager(inputCount, projPlayer.blockSize); outputMgr = new VstAudioBufferManager(outputCount, projPlayer.blockSize); myContext.PluginCommandStub.Commands.SetBlockSize(projPlayer.blockSize); myContext.PluginCommandStub.Commands.SetSampleRate(44100f); myContext.PluginCommandStub.Commands.MainsChanged(true); myContext.PluginCommandStub.Commands.StartProcess(); inputBuffers = inputMgr.Buffers.ToArray(); outputBuffers = outputMgr.Buffers.ToArray(); } public void clearBuffers() { inputMgr.ClearAllBuffers(); } public void clearInputBuffers() { Array.Clear(inputBuffers); } public void Close() { if (myContext != null && edHostWin != null) { edHostWin.LocationChanged -= EdHostWin_LocationChanged; myHostCmdStub.PluginCalled -= new EventHandler(HostCmdStub_PluginCalled); myContext.PluginCommandStub.Commands.MainsChanged(false); if (IsEditorOpen) edHostWin?.Close(); myContext.PluginCommandStub.Commands.Close(); //myContext.Dispose(); } if (myContext !=null) { myContext.Remove("PluginPath"); myContext.Remove("HostCmdStub"); myContext = null; } } public EditorHostWindow edHostWin; public void OpenEditor(Window ownerWindow) { if (myContext != null) { edHostWin = new EditorHostWindow(pluginWindowLoc, myContext.PluginCommandStub, this, pluginID) { Owner = (EditorWindow)App.Current.MainWindow, ShowInTaskbar = false}; edHostWin.LocationChanged += EdHostWin_LocationChanged; edHostWin.Closed += EdHostWin_Closed; edHostWin.Show(); ////myContext.PluginCommandStub.Commands.MainsChanged(false); IsEditorOpen = true; //if (myContext.PluginCommandStub.Commands.GetChunk(true) != null) // File.WriteAllBytes(@"D:\chunk" + DateTime.Now.ToShortTimeString().Replace("/", "-").Replace(":", "-"), myContext.PluginCommandStub.Commands.GetChunk(true)); } else IsEditorOpen = false; } private void EdHostWin_Closed(object sender, EventArgs e) { IsEditorOpen = false; } private void EdHostWin_LocationChanged(object sender, EventArgs e) { this.pluginWindowLoc = new System.Windows.Point(edHostWin.Left, edHostWin.Top); editingProject.NeedsSaving = true; } internal void SendPlayNoteMessage(int noteno, int velocity) { MemoryStream noteOnStream = new MemoryStream(); BinaryWriter noteOnDatax = new BinaryWriter(noteOnStream); noteOnDatax.Write(MidiMessage.StartNote(noteno, velocity, this.Channel).RawData); VstMidiEvent vstMEvent = new VstMidiEvent(0, 0, 0, noteOnStream.ToArray(), 0, 0, true); VstMidiEvent[] vmEvents = new VstMidiEvent[] { vstMEvent }; myContext.PluginCommandStub.Commands.ProcessEvents(vmEvents); noteOnStream.Close(); noteOnDatax.Close(); } internal void SendStopNoteMessage(int noteno) { MemoryStream noteOffStream = new MemoryStream(); BinaryWriter noteOffDatax = new BinaryWriter(noteOffStream); noteOffDatax.Write(MidiMessage.StopNote(noteno, 0, this.Channel).RawData); VstMidiEvent vstMEvent = new VstMidiEvent(0, 0, 0, noteOffStream.ToArray(), 0, 0, true); VstMidiEvent[] vmEvents = new VstMidiEvent[] { vstMEvent }; myContext.PluginCommandStub.Commands.ProcessEvents(vmEvents); noteOffStream.Close(); noteOffDatax.Close(); } internal void SendStopNotes(List stopNoteNos, int channel) { List vmEvents = new List(); foreach (int snoteno in stopNoteNos) { MemoryStream noteOffStream = new MemoryStream(); BinaryWriter noteOffDatax = new BinaryWriter(noteOffStream); noteOffDatax.Write(MidiMessage.StopNote(snoteno, 0, 1).RawData); vmEvents.Add(new VstMidiEvent(0, 0, 0, noteOffStream.ToArray(), 0, 0, true)); noteOffStream.Close(); noteOffDatax.Close(); } vmEvents.Add(CreateControllerMidiEvent(SUSTAIN_CONTROLLER_CODE, 0, channel)); vmEvents.Add(CreateControllerMidiEvent(MOD_CONTROLLER_CODE, 0, channel)); vmEvents.Add(CreatePitchWheelChangeMidiEvent(PITCHWHEEL_OFF_VALUE, channel)); myContext.PluginCommandStub.Commands.ProcessEvents(vmEvents.ToArray()); } }