Oscilloscope 0.5.0
A simple oscilloscope VST
Loading...
Searching...
No Matches
OscilloscopeComponent.cpp
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 OscilloscopeComponent.cpp
5 Created: 24 Feb 2023 6:47:52pm
6 Author: wadda
7
8 ==============================================================================
9*/
10
12
13OscilloscopeComponent::OscilloscopeComponent(OscilloscopeAudioProcessor &aProcessor, int sampleRate, int framesPerSecond) : audioProcessor(aProcessor)
14{
15 // set frames per second
16 setFramesPerSecond(framesPerSecond);
17
18 // set attributes
19 this->sampleRate = sampleRate;
20 displayLength = (int)(*aProcessor.getTreeState()->getRawParameterValue("bufferLength") * aProcessor.getSampleRate());
21 ratio = (double)displayLength / (double)EDITOR_INITIAL_WIDTH();
22 displayLength /= ratio;
23
24 // resize buffers
25 double dataLength = audioProcessor.getAudioBufferQueue()->getBufferSize() / ratio;
26 sampleData.resize(EDITOR_INITIAL_WIDTH());
27 newData.resize(sampleData.size());
28 newlyPopped.resize(dataLength);
29 notInterpolatedData.resize(audioProcessor.getAudioBufferQueue()->getBufferSize());
30
31 // fill all buffers with 0
32 std::fill(sampleData.begin(), sampleData.end(), 0);
33 std::fill(newlyPopped.begin(), newlyPopped.end(), 0);
34 std::fill(notInterpolatedData.begin(), notInterpolatedData.end(), 0);
35 std::fill(newData.begin(), newData.end(), 0);
36
37 // add this as listener
38 aProcessor.getTreeState()->addParameterListener("bufferLength", this);
39
40 // reset interpolator
41 interpolator.reset();
42}
43
45{
46 // remove this as listener
47 audioProcessor.getTreeState()->removeParameterListener("bufferLength", this);
48}
49
51{
52 // start timer
53 jassert(framesPerSecond > 0 && framesPerSecond < 1000);
54 startTimerHz(framesPerSecond);
55}
56
57void OscilloscopeComponent::drawGrid(juce::Graphics &g, float w, float h)
58{
59 // set graphics parameters
60 g.setColour(juce::Colours::ghostwhite);
61 g.setOpacity(0.8);
62 g.drawLine(0, h / 2, w, h / 2);
63 g.drawLine(1, 0, 1, h);
64
65 // draw grid lines
66 for (size_t i = 0; i < 10; i++)
67 {
68 float xPos = i * w / 10;
69 float yPos = i * h / 10;
70 g.drawLine(xPos, h / 2 - 8, xPos, h / 2 + 8);
71 g.drawLine(0, yPos, 8, yPos);
72 }
73
74 // get duration of each x tick
75 float fontHeight = g.getCurrentFont().getAscent();
76 float duration = *audioProcessor.getTreeState()->getRawParameterValue("bufferLength") * static_cast<float>(10000);
77 duration /= 10;
78 auto xText = juce::String(duration, 2);
79 xText.append(" ms", 3);
80
81 // write x ticks values
82 g.drawLine(w - 95, h - 39, w - 95, h - 39 - fontHeight);
83 g.drawLine(w - 85, h - 39 - fontHeight / 2, w - 95, h - 39 - fontHeight / 2);
84 g.drawLine(w - 85, h - 39, w - 85, h - 39 - fontHeight);
85 g.drawSingleLineText(xText, w - 80, h - 39);
86
87 // write y ticks values
88 auto yText = "0.1";
89 g.drawLine(w - 95, h - 19, w - 85, h - 19);
90 g.drawLine(w - 90, h - 19 - fontHeight, w - 90, h - 19);
91 g.drawLine(w - 95, h - 19 - fontHeight, w - 85, h - 19 - fontHeight);
92 g.drawSingleLineText(yText, w - 80, h - 19);
93}
94
95void OscilloscopeComponent::paint(juce::Graphics &g)
96{
97 // set graphi parameters
98 g.fillAll(juce::Colours::black);
99 g.setColour(juce::Colours::white);
100
101 // get bounds
102 auto area = getLocalBounds();
103 auto h = (float)area.getHeight();
104 auto w = (float)area.getWidth();
105
106 // draw grid if necessary
107 if (audioProcessor.getTreeState()->getParameterAsValue("drawGrid").getValue())
108 {
109 drawGrid(g, w, h);
110 }
111
112 // draw waveform
113 auto scopeRect = juce::Rectangle<float>{float(0), float(0), w, h};
114 plot(g, scopeRect, float(1), h / 2);
115}
116
118{
119}
120
121void OscilloscopeComponent::parameterChanged(const juce::String &parameterID, float newValue)
122{
123 // resize buffers to new length
124 ratio = newValue * audioProcessor.getSampleRate() / EDITOR_INITIAL_WIDTH();
125 displayLength = newValue * audioProcessor.getSampleRate() / ratio;
126 sampleData.resize(displayLength);
127 newData.resize(sampleData.size());
128 int queueSize = audioProcessor.getAudioBufferQueue()->getBufferSize();
129 double dataLength = queueSize / ratio;
130 newlyPopped.resize(dataLength);
131}
132
133void OscilloscopeComponent::timerCallback()
134{
135 // pop raw data
136 audioProcessor.getAudioBufferQueue()->pop(notInterpolatedData.data());
137 int queueSize = newlyPopped.size();
138
139 // resample data
140 interpolator.process(ratio, notInterpolatedData.data(), newlyPopped.data(), queueSize);
141
142 // shift & add new data
143 std::copy(sampleData.data() + queueSize, sampleData.data() + sampleData.size(), newData.begin());
144 std::copy(newlyPopped.data(), newlyPopped.data() + queueSize, newData.begin() + sampleData.size() - queueSize);
145
146 // set data to plot equal to shifted data
147 sampleData = newData;
148
149 // perform subclass-specific operations
150 subclassSpecificCallback();
151
152 // repaint
153 repaint();
154}
Oscilloscope audio processor.
juce::AudioProcessorValueTreeState * getTreeState()
AudioBufferQueue< float > * getAudioBufferQueue()
void paint(juce::Graphics &g) override
void drawGrid(juce::Graphics &g, float w, float h)
OscilloscopeAudioProcessor & audioProcessor
void setFramesPerSecond(int framesPerSecond)
std::vector< float > sampleData
OscilloscopeComponent(OscilloscopeAudioProcessor &aProcessor, int sampleRate, int framesPerSecond)