using System; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Windows.Threading; using static QuikDawEditor.EDITING.MiscMethods; namespace QuikDawEditor.EditingClasses; public partial class Track { internal int GetVstiSamples(ref float[] sourceBuffer, int samplesRead, int blockSize) { blockSize *= 2; //lock (sourceBuffer) //{ lock (TrackInstrumentVsts) { for (int vstno = 0; vstno < TrackInstrumentVsts.Count; vstno++) { if (TrackInstrumentVsts[vstno].IsActive) { //Debug.WriteLine("Vstisampsread: " + samplesRead + " ::: blockSize: " + blockSize + " :::tvstbuffers /o" + TrackInstrumentVsts[vstno].outputBuffers[0].SampleCount); //TrackInstrumentVsts[vstno].clearInputBuffers(); //for (int sampsSent = 0; sampsSent <= samplesRead - blockSize ; sampsSent += blockSize) for (int sampsSent = 0; sampsSent < samplesRead ; sampsSent += blockSize) { try { //wasReplaced = false; TrackInstrumentVsts[vstno].myContext.PluginCommandStub.Commands.ProcessReplacing(TrackInstrumentVsts[vstno].inputBuffers, TrackInstrumentVsts[vstno].outputBuffers); //if (myTrackSampleProvider.currClipGainFactor != 1) // Debug.WriteLine("currclipgainfac=" + myTrackSampleProvider.currClipGainFactor); //for (int n = 0; n < blockSize * 2; n += m_waveFormat.Channels) for (int n = 0; n < blockSize; n += 2) { //additive for layered multiple VSTi's on same track sourceBuffer[sampsSent + n] += TrackInstrumentVsts[vstno].outputBuffers[0][n / 2] * myTrackSampleProvider.currClipGainFactor; sourceBuffer[sampsSent + n + 1] += TrackInstrumentVsts[vstno].outputBuffers[1][n / 2] * myTrackSampleProvider.currClipGainFactor; //to avoid clipping??? //sourceBuffer[sampsSent + n] = Math.Min(sourceBuffer[sampsSent + n], 1); //sourceBuffer[sampsSent + n + 1] = Math.Min(sourceBuffer[sampsSent + n + 1], 1); } //wasReplaced = true; } //catch (Win32Exception ex) { ReleaseResources(); Debug.WriteLine("Error getting vsti samples:\n" + ex.Message); } catch (Exception ex) { Debug.WriteLine("unhandled::::::::::::::::\n" + ex.Message); } //catch (AccessViolationException ex) { Debug.WriteLine("unhandled::::::::::::::::\n" + ex.Message); TrackEffectVsts[vstno].causedRunningError = true; } //finally //{ // // This code always executed even if application crashes. // if (!wasReplaced) {ReleaseResources(); Debug.WriteLine("some error ocurred"); } //} } } } //} } return sourceBuffer.Length; } int counter = 0; internal double LastAutoRecMasterTime = 0; AutoPoint aupoint; internal void ProcessTrackSamples(ref float[] sourceBuffer, int offset, int samplesRead, int blockSize, double MasterTimeMs, bool fromClip) { //clicking when moving playPos occurs here blockSize *= 2; ////HAAS DELAY //if (IsHaasDelayActive) //{ //Produce Haas effect for track // for (int bno = 0; bno < samplesRead; bno += 2) // HaasBuffer.Add(sourceBuffer[bno + 1]); // try // { // for (int bno = 0; bno < sourceBuffer.Length; bno += 2) // { // sourceBuffer[bno + 1] = HaasBuffer[0]; // HaasBuffer.RemoveAt(0); // } // } // //catch { HaasBuffer.Clear(); } // catch { } //} //SEND TO VST PLUGINS for (int vstno = 0; vstno < TrackEffectVsts.Count; vstno++) { if (TrackEffectVsts[vstno].IsActive && !TrackEffectVsts[vstno].causedRunningError) { //for (int sampsSent = 0; sampsSent <= samplesRead - blockSize * 2; sampsSent += blockSize * 2) for (int sampsSent = 0; sampsSent < samplesRead; sampsSent += blockSize) { //for (int n = 0; n < blockSize * 2; n += 2) // n is incremented by the number of channels (always stereo) for (int n = 0; n < blockSize; n += 2) // n is incremented by the number of channels (always stereo) { TrackEffectVsts[vstno].inputBuffers[0][n / 2] = sourceBuffer[sampsSent + n]; TrackEffectVsts[vstno].inputBuffers[1][n / 2] = sourceBuffer[sampsSent + n + 1]; } try { TrackEffectVsts[vstno].myContext.PluginCommandStub.Commands.ProcessReplacing(TrackEffectVsts[vstno].inputBuffers, TrackEffectVsts[vstno].outputBuffers); } catch (Win32Exception ex) { Debug.WriteLine("unhandled::::::::::::::::\n" + ex.Message); TrackEffectVsts[vstno].causedRunningError = true; } //for (int n = 0; n < blockSize * 2; n += 2) // n is incremented by the number of channels (always stereo) for (int n = 0; n < blockSize; n += 2) // n is incremented by the number of channels (always stereo) { sourceBuffer[sampsSent + n] = TrackEffectVsts[vstno].outputBuffers[0][n / 2]; sourceBuffer[sampsSent + n + 1] = TrackEffectVsts[vstno].outputBuffers[1][n / 2]; } } } } //Record or process automation if (this.IsAutomationRecording && counter == 0) // only record automation every 5th cycle (50 ms) { //Record AutomationLane selAutoClip = AutomationLanes[this.SelectedAutomationLaneIndex]; selAutoClip.RecAnchorAutoPoint.sourcePointms = MasterTimeMs; float newValue = this.GetCurrentSourceValue; newValue = Math.Max(selAutoClip.MinValue, Math.Min(selAutoClip.MaxValue, newValue)); App.Current.Dispatcher.Invoke(() => { //Remove existing autopoints in recorded range foreach (AutoPoint ap in this.compareAutoPoints.Where(ap => ap.sourcePointms >= this.AutomationRecordingStartMs && ap.sourcePointms <= MasterTimeMs)) { if (!selAutoClip.AutoRemovedPoints.Contains(ap)) selAutoClip.AutoRemovedPoints.Add(ap); selAutoClip.autoPoints.Remove(ap); } try { if (selAutoClip.RecAnchorAutoPoint.AutoValue != newValue) { //Move anchor point and add change if (MasterTimeMs - LastAutoRecMasterTime > 1000) { // set additional anchor if longer than threshold length aupoint = new AutoPoint() { sourcePointms = MasterTimeMs, AutoValue = selAutoClip.RecAnchorAutoPoint.AutoValue }; selAutoClip.autoPoints.Insert(selAutoClip.autoPoints.IndexOf(selAutoClip.RecAnchorAutoPoint), aupoint); selAutoClip.AutoRecordedPointCount += 1; } selAutoClip.RecAnchorAutoPoint.AutoValue = newValue; aupoint = new AutoPoint() { sourcePointms = MasterTimeMs, AutoValue = selAutoClip.RecAnchorAutoPoint.AutoValue }; selAutoClip.autoPoints.Insert(selAutoClip.autoPoints.IndexOf(selAutoClip.RecAnchorAutoPoint), aupoint); selAutoClip.AutoRecordedPointCount += 1; LastAutoRecMasterTime = MasterTimeMs; } } catch (Exception ex) { Debug.WriteLine("Error recording autopoint: " + ex.Message); } selAutoClip.NotifyAutoPointsChanged(); }, DispatcherPriority.Background); } trackAutomationFactor = 1; panAutomationFactor = 0; if (fromClip) { if (AutomationLanes[0].IsActive) { try { trackAutomationFactor = CalculateValBetweenAutoPoints(MasterTimeMs, AutomationLanes[0]); } catch (Exception ex) { trackAutomationFactor = 1; Debug.WriteLine("Couldn't get volume automation factor for track: " + this.TrackName + "\n" + ex.Message); } } if (AutomationLanes[1].IsActive) { try { panAutomationFactor = CalculateValBetweenAutoPoints(MasterTimeMs, AutomationLanes[1]); } catch (Exception ex) { panAutomationFactor = 0; Debug.WriteLine("Couldn't get pan automation factor for track: " + this.TrackName + "\n" + ex.Message); } } } float trackVolAutomationFactorL = LPan * Volume * trackAutomationFactor; float trackVolAutomationFactorR = RPan * Volume * trackAutomationFactor; for (int inIndex = 0; inIndex <= samplesRead - 2; inIndex += 2) { sourceBuffer[inIndex] *= trackVolAutomationFactorL; sourceBuffer[inIndex + 1] *= trackVolAutomationFactorR; } //MAKE MINIMUM RESOLUTION BETWEEN RECORDING AUTOPOINTS TO BE e.g. 50 MS (or maybe less) for all automation parameters counter += 1; if (counter >= 5) counter = 0; } public float GetCurrentSourceValue { get { float newValue = 0; switch (AutomationLanes[this.SelectedAutomationLaneIndex].AutomationType) { case "Volume": newValue = (float)Math.Round(this.Volume, 1); break; case "Pan": newValue = (float)Math.Round(this.Pan, 1); break; default: //Determine type of effect to record automate break; } return newValue; } } }