AnalogTapeModel

Physical modelling signal processing for analog tape recording
Log | Files | Refs | Submodules | README | LICENSE

commit ece26bddf60f1029f237569947906baa09fa30c5
parent 0ed360c9f959da74462fd5319a50588c633d4ea1
Author: jatinchowdhury18 <[email protected]>
Date:   Tue, 26 Feb 2019 17:45:15 -0800

FIX Biasing

Diffstat:
MPlugin/Source/GUI Components/BiasControls.cpp | 7++++---
MPlugin/Source/GUI Components/MainControls.cpp | 4++--
MPlugin/Source/PluginProcessor.cpp | 10+++++-----
MPlugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp | 57+++++++++++++++++++++++++++++++++++++++------------------
MPlugin/Source/Processors/Hysteresis/HysteresisProcessor.h | 11++++++++++-
MSimulations/Hysteresis/hystersis.py | 10+++++-----
6 files changed, 65 insertions(+), 34 deletions(-)

diff --git a/Plugin/Source/GUI Components/BiasControls.cpp b/Plugin/Source/GUI Components/BiasControls.cpp @@ -5,10 +5,11 @@ BiasControls::BiasControls (ChowtapeModelAudioProcessor& proc) : processor (proc) { ChowtapeModelAudioProcessorEditor::createSlider (biasFreqSlide, processor.biasFreq, myLNF, this, String ("kHz"), 1.0f); - biasFreqSlide.setSkewFactorFromMidPoint (55.0); + biasFreqSlide.setEnabled (false); + //biasFreqSlide.setSkewFactorFromMidPoint (55.0); - ChowtapeModelAudioProcessorEditor::createSlider (biasGainSlide, processor.biasGain, myLNF, this, String ("dB")); - biasGainSlide.setSkewFactorFromMidPoint (14.0); + ChowtapeModelAudioProcessorEditor::createSlider (biasGainSlide, processor.biasGain, myLNF, this); + //biasGainSlide.setSkewFactorFromMidPoint (14.0); ChowtapeModelAudioProcessorEditor::createLabel (biasFreqLabel, processor.biasFreq, this); ChowtapeModelAudioProcessorEditor::createLabel (biasGainLabel, processor.biasGain, this); diff --git a/Plugin/Source/GUI Components/MainControls.cpp b/Plugin/Source/GUI Components/MainControls.cpp @@ -8,13 +8,13 @@ MainControls::MainControls (ChowtapeModelAudioProcessor& proc) : ChowtapeModelAudioProcessorEditor::createSlider (gainOutKnob, processor.outGain, myLNF, this, String ("dB")); ChowtapeModelAudioProcessorEditor::createComboBox (oversampling, processor.overSampling, this); ChowtapeModelAudioProcessorEditor::createComboBox (tapeSpeed, processor.tapeSpeed, this); - ChowtapeModelAudioProcessorEditor::createComboBox (tapeType, processor.tapeType, this); + //ChowtapeModelAudioProcessorEditor::createComboBox (tapeType, processor.tapeType, this); ChowtapeModelAudioProcessorEditor::createLabel (inGainLabel, processor.inGain, this); ChowtapeModelAudioProcessorEditor::createLabel (outGainLabel, processor.outGain, this); ChowtapeModelAudioProcessorEditor::createLabel (oversampleLabel, processor.overSampling, this); ChowtapeModelAudioProcessorEditor::createLabel (speedLabel, processor.tapeSpeed, this); - ChowtapeModelAudioProcessorEditor::createLabel (typeLabel, processor.tapeType, this); + //ChowtapeModelAudioProcessorEditor::createLabel (typeLabel, processor.tapeType, this); } void MainControls::paint (Graphics&) {} diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp @@ -22,7 +22,7 @@ ChowtapeModelAudioProcessor::ChowtapeModelAudioProcessor() outGain->addListener (this); addParameter (overSampling = new AudioParameterChoice (String ("overSampling"), String ("Upsample"), - StringArray ({ "2x", "4x", "8x" }), 0)); + StringArray ({ "2x", "4x", "8x", "16x" }), 3)); overSampling->addListener (this); addParameter (tapeSpeed = new AudioParameterChoice (String ("tapeSpeed"), String ("Speed"), @@ -34,10 +34,10 @@ ChowtapeModelAudioProcessor::ChowtapeModelAudioProcessor() tapeType->addListener(this); //Bias Controls - addParameter (biasFreq = new AudioParameterFloat (String ("biasFreq"), String ("Bias Frequency"), 30.0f, 80.0f, 55.0)); + addParameter (biasFreq = new AudioParameterFloat (String ("biasFreq"), String ("Bias Frequency"), 45.0f, 55.0f, 50.0)); biasFreq->addListener (this); - addParameter (biasGain = new AudioParameterFloat (String ("biasGain"), String ("Bias Gain"), 0.0f, 20.0f, 14.0f)); + addParameter (biasGain = new AudioParameterFloat (String ("biasGain"), String ("Bias Gain"), 0.0f, 10.0f, 5.0f)); biasGain->addListener (this); //Loss Controls @@ -77,8 +77,8 @@ void ChowtapeModelAudioProcessor::parameterValueChanged (int paramIndex, float n lossEffects.setSpeed (*tapeSpeed); timingEffect.setTapeSpeed (*tapeSpeed); } - else if (paramIndex == tapeType->getParameterIndex()) - return; //@TODO + //else if (paramIndex == tapeType->getParameterIndex()) + // return; //@TODO else if (paramIndex == biasFreq->getParameterIndex()) hysteresis.setBiasFreq (biasFreq->convertFrom0to1 (newValue)); else if (paramIndex == biasGain->getParameterIndex()) diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp b/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.cpp @@ -2,9 +2,10 @@ HysteresisProcessor::HysteresisProcessor() : ProcessorBase ("HysteresisProcessor") { - overSample2.reset (new dsp::Oversampling<float> (2, 1, dsp::Oversampling<float>::filterHalfBandFIREquiripple)); - overSample4.reset (new dsp::Oversampling<float> (2, 2, dsp::Oversampling<float>::filterHalfBandFIREquiripple)); - overSample8.reset (new dsp::Oversampling<float> (2, 3, dsp::Oversampling<float>::filterHalfBandFIREquiripple)); + overSample2.reset (new dsp::Oversampling<float> (2, 1, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR)); + overSample4.reset (new dsp::Oversampling<float> (2, 2, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR)); + overSample8.reset (new dsp::Oversampling<float> (2, 3, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR)); + overSample16.reset (new dsp::Oversampling<float> (2, 4, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR)); } void HysteresisProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) @@ -14,15 +15,17 @@ void HysteresisProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) hProcs[0].setSampleRate ((float) (sampleRate * overSamplingFactor)); hProcs[1].setSampleRate ((float) (sampleRate * overSamplingFactor)); - if (overSamplingFactor == 8) + if (overSamplingFactor == 16) + overSample16->initProcessing (samplesPerBlock); + else if (overSamplingFactor == 8) overSample8->initProcessing (samplesPerBlock); else if (overSamplingFactor == 4) overSample4->initProcessing (samplesPerBlock); else overSample2->initProcessing (samplesPerBlock); - n[0] = 0; - n[1] = 0; + currentSampleRate = (float) sampleRate * overSamplingFactor; + updateAngleDelta(); fadeIn = true; } @@ -32,6 +35,7 @@ void HysteresisProcessor::releaseResources() overSample2->reset(); overSample4->reset(); overSample8->reset(); + overSample16->reset(); } void HysteresisProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& /*midi*/) @@ -44,8 +48,10 @@ void HysteresisProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& dsp::AudioBlock<float> block (buffer); dsp::AudioBlock<float> osBlock; - - if (overSamplingFactor == 8) + + if (overSamplingFactor == 16) + osBlock = overSample16->processSamplesUp (block); + else if (overSamplingFactor == 8) osBlock = overSample8->processSamplesUp (block); else if (overSamplingFactor == 4) osBlock = overSample4->processSamplesUp (block); @@ -55,22 +61,25 @@ void HysteresisProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& float* ptrArray[] = { osBlock.getChannelPointer(0), osBlock.getChannelPointer(1) }; AudioBuffer<float> osBuffer (ptrArray, 2, static_cast<int> (osBlock.getNumSamples())); - const auto sineTerm = MathConstants<float>::twoPi * biasFreq / (float) (getSampleRate() * overSamplingFactor); - for (int channel = 0; channel < osBuffer.getNumChannels(); ++channel) { auto* x = osBuffer.getWritePointer (channel); for (int samp = 0; samp < osBuffer.getNumSamples(); samp++) { - x[samp] = hProcs[channel].process ((float) 5e4 * (x[samp]));// + biasGain * sinf (sineTerm * (float) n[channel]))); + //x[samp] = hProcs[channel].process ((float) 5e4 * (x[samp])); - n[channel]++; - if ((float) (n[channel] / (getSampleRate() * overSamplingFactor)) >= 1.0f / biasFreq) - n[channel] = 0; + x[samp] = 10.0f * hProcs[channel].process ((float) 5e3 * (x[samp] + biasGain * sinf (currentAngle[channel]))); + //x[samp] = biasGain * sinf (currentAngle[channel]); + + currentAngle[channel] += angleDelta; + if (currentAngle[channel] > MathConstants<float>::twoPi) + currentAngle[channel] -= MathConstants<float>::twoPi; } } - if (overSamplingFactor == 8) + if (overSamplingFactor == 16) + overSample16->processSamplesDown (block); + else if (overSamplingFactor == 8) overSample8->processSamplesDown (block); else if (overSamplingFactor == 4) overSample4->processSamplesDown (block); @@ -78,8 +87,15 @@ void HysteresisProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& overSample2->processSamplesDown (block); } +void HysteresisProcessor::updateAngleDelta() +{ + auto cyclesPerSample = biasFreq / currentSampleRate; + angleDelta = cyclesPerSample * 2.0f * MathConstants<float>::pi; +} + void HysteresisProcessor::setOverSamplingFactor (String osFactor) { + //osFactor = "8x"; auto factor = overSamplingFactor; if (osFactor == "2x") factor = 2; // set overSample factor 2 = 2^1 @@ -87,6 +103,8 @@ void HysteresisProcessor::setOverSamplingFactor (String osFactor) factor = 4; // set overSample factor 4 = 2^2 else if (osFactor == "8x") factor = 8; // set overSample factor 8 = 2^3 + else if (osFactor == "16x") + factor = 16; //set overSample factor 16 = 2^4 if (factor != overSamplingFactor) { @@ -98,17 +116,20 @@ void HysteresisProcessor::setOverSamplingFactor (String osFactor) void HysteresisProcessor::setBiasFreq (float newFreqKHz) { - biasFreq = newFreqKHz * 100.0f; + biasFreq = newFreqKHz * 1000.0f; + updateAngleDelta(); } void HysteresisProcessor::setBiasGain (float newGainDB) { - biasGain = Decibels::decibelsToGain (newGainDB); + biasGain = newGainDB; //Decibels::decibelsToGain (newGainDB); } double HysteresisProcessor::getTailLengthSeconds() const { - if (overSamplingFactor == 8) + if (overSamplingFactor == 16) + return overSample16->getLatencyInSamples() * getSampleRate(); + else if (overSamplingFactor == 8) return overSample8->getLatencyInSamples() * getSampleRate(); else if (overSamplingFactor == 4) return overSample4->getLatencyInSamples() * getSampleRate(); diff --git a/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.h b/Plugin/Source/Processors/Hysteresis/HysteresisProcessor.h @@ -25,12 +25,21 @@ private: std::unique_ptr<dsp::Oversampling<float>> overSample2; std::unique_ptr<dsp::Oversampling<float>> overSample4; std::unique_ptr<dsp::Oversampling<float>> overSample8; + std::unique_ptr<dsp::Oversampling<float>> overSample16; int overSamplingFactor = 8; + + ////////////////////////////// float biasFreq = 0.0f; float biasGain = 5.0f; - int n[2] = { 0, 0 }; + float currentSampleRate = 0.0f; + float angleDelta = 0.0f; + float currentAngle[2] = { 0.0f, 0.0f }; + + void updateAngleDelta(); + + //int n[2] = { 0, 0 }; bool fadeIn = false; diff --git a/Simulations/Hysteresis/hystersis.py b/Simulations/Hysteresis/hystersis.py @@ -2,7 +2,7 @@ import numpy as np import matplotlib.pyplot as plt from matplotlib import animation -fs = 48000 * 8 +fs = 48000 * 16 T = 1/fs # sample interval M_s = 350000 # Jiles book a = 2.2e4 #adjustable parameter @@ -55,7 +55,7 @@ def M_n (M_n1, k1, k2, k3, k4): #input signal t = np.linspace (0, 1, fs) #t = 1 -H_in = (1e5) * np.sin (2 * np.pi * 2000 * t) +H_in = (1e5) * np.sin (2 * np.pi * 30000 * t) freq = 2000 #H_in = np.concatenate ((5e2 * np.sin (2 * np.pi * freq * t[0:fs*5]), 1e3 * np.sin (2 * np.pi * freq * t[fs*5:fs*10]), \ # 3e3 * np.sin (2 * np.pi * freq * t[fs*10:fs*15]), 5e3 * np.sin (2 * np.pi * freq * t[fs*15:fs*20]), \ @@ -106,8 +106,8 @@ plt.plot (H_in[0:20000] / 1000, M_out[0:20000] / M_s) plt.xlabel ("Magnetic Field (A/m)") plt.ylabel ("Tape Magnetisation (A/m)") plt.title ("Simulated Ditigal Tape Magnetization Hysteresis Loop") -MH.show() +#MH.show() -Mt = plt.figure (2) -plt.plot (t[0:20000], M_out[0:20000] / M_s) +#Mt = plt.figure (2) +#plt.plot (t[0:20000], M_out[0:20000] / M_s) plt.show()