Mema
Memory Matrix — multi-channel audio matrix monitor and router
Loading...
Searching...
No Matches
WaveformAudioComponent.cpp
Go to the documentation of this file.
1/* Copyright (c) 2025, Christian Ahrens
2 *
3 * This file is part of Mema <https://github.com/ChristianAhrens/Mema>
4 *
5 * This tool is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License version 3.0 as published
7 * by the Free Software Foundation.
8 *
9 * This tool is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 * details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this tool; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
20
21#include "../MemaMoAppConfiguration.h" // include to enable trigger cfg dump
22
23#include <CustomLookAndFeel.h>
24
25
26namespace Mema
27{
28
29//==============================================================================
30class CustomPaintingAudioVisualiserComponent : public juce::AudioVisualiserComponent
31{
32public:
33 //==============================================================================
35 : juce::AudioVisualiserComponent(initialNumChannels) {};
37
38 void setColours(juce::Colour tc, juce::Colour bc, juce::Colour wc)
39 {
40 m_trackColour = tc;
41 m_waveformColour = wc;
42
43 juce::AudioVisualiserComponent::setColours(bc, wc);
44 };
45
46 //==============================================================================
47 void paintChannel(juce::Graphics& g, juce::Rectangle<float> area,
48 const Range<float>* levels, int numLevels, int nextSample) override
49 {
50 auto margin = area.getHeight() * 0.05f;
51 margin = jlimit(1.0f, 3.0f, margin);
52
53 area.reduce(margin, margin);
54
55 g.setColour(m_trackColour);
56 g.fillRect(area);
57
58 juce::Path p;
59 getChannelAsPath(p, levels, numLevels, nextSample);
60
61 g.setColour(m_waveformColour);
62 g.fillPath(p, juce::AffineTransform::fromTargetPoints(0.0f, -1.0f, area.getX(), area.getY(),
63 0.0f, 1.0f, area.getX(), area.getBottom(),
64 (float)numLevels, -1.0f, area.getRight(), area.getY()));
65 }
66
67private:
68 juce::Colour m_trackColour = juce::Colours::white;
69 juce::Colour m_waveformColour = juce::Colours::grey;
70};
71
72
73//==============================================================================
76{
77 m_waveformsComponent = std::make_unique<CustomPaintingAudioVisualiserComponent>(m_numVisibleChannels);
78 m_waveformsComponent->setBufferSize(2048);
79 m_waveformsComponent->setRepaintRate(20);
80 addAndMakeVisible(m_waveformsComponent.get());
81
82 m_chNumSelButton = std::make_unique<juce::DrawableButton>("SelectChannelcount", juce::DrawableButton::ButtonStyle::ImageFitted);
83 m_chNumSelButton->setTooltip("Select number of visible channels.");
84 m_chNumSelButton->onClick = [this] {
85 juce::PopupMenu settingsMenu;
86 for (int i = 1; i <= m_numAvailableChannels; i++)
87 settingsMenu.addItem(i, juce::String(i));
88 settingsMenu.showMenuAsync(juce::PopupMenu::Options(), [=](int selectedId) {
89 setNumVisibleChannels(selectedId);
90 if (JUCEAppBasics::AppConfigurationBase::getInstance())
91 JUCEAppBasics::AppConfigurationBase::getInstance()->triggerConfigurationDump(false);
92 });
93 };
94 m_chNumSelButton->setAlwaysOnTop(true);
95 m_chNumSelButton->setColour(juce::DrawableButton::ColourIds::backgroundColourId, juce::Colours::transparentBlack);
96 m_chNumSelButton->setColour(juce::DrawableButton::ColourIds::backgroundOnColourId, juce::Colours::transparentBlack);
97 addAndMakeVisible(m_chNumSelButton.get());
98
100}
101
105
107{
109
110 auto visuArea = getLocalBounds();
111 auto legendArea = visuArea.removeFromRight(m_legendWidth);
112
113 // fill legend area background
114 g.setColour(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
115 g.fillRect(legendArea);
116
117 // draw legend, simply number the waveforms from 1..n
118 if (m_numVisibleChannels > 0)
119 {
120 g.setFont(14.0f);
121 g.setColour(getLookAndFeel().findColour(juce::TextButton::textColourOnId));
122 auto singleWaveformHeight = legendArea.getHeight() / m_numVisibleChannels;
123 for (int i = 1; i <= m_numVisibleChannels; ++i)
124 g.drawText(juce::String(i), legendArea.removeFromTop(singleWaveformHeight), juce::Justification::centred, true);
125 }
126}
127
129{
130 auto bounds = getLocalBounds();
131 auto visuArea = bounds;
132 auto legendArea = visuArea.removeFromRight(m_legendWidth);
133 ignoreUnused(legendArea);
134
135 if (m_waveformsComponent)
136 m_waveformsComponent->setBounds(visuArea);
137
138 if (m_chNumSelButton)
139 m_chNumSelButton->setBounds(bounds.removeFromBottom(22).removeFromLeft(22));
140
142}
143
145{
146 if (m_waveformsComponent)
147 m_waveformsComponent->setColours(
148 getLookAndFeel().findColour(juce::Slider::backgroundColourId),
149 getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId),
150 getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::MeteringRmsColourId));
151
152 auto chNumSelButtonDrawable = juce::Drawable::createFromSVG(*juce::XmlDocument::parse(BinaryData::waves24px_svg).get());
153 chNumSelButtonDrawable->replaceColour(juce::Colours::black, getLookAndFeel().findColour(juce::TextButton::ColourIds::textColourOnId));
154 m_chNumSelButton->setImages(chNumSelButtonDrawable.get());
155}
156
158{
159 if (!data)
160 if (!data)
161 return;
162
163 switch (data->GetDataType())
164 {
166 {
167 ProcessorAudioSignalData* sd = static_cast<ProcessorAudioSignalData*>(data);
168 if (sd->GetChannelCount() > 0)
169 {
170 if (m_numAvailableChannels != sd->getNumChannels())
171 {
172 auto init = (0 == m_numAvailableChannels) && (1 == m_numVisibleChannels);
173 m_numAvailableChannels = sd->getNumChannels();
174 if (init)
175 setNumVisibleChannels(m_numAvailableChannels);
176 }
177 if (m_waveformsComponent)
178 {
179 m_waveformsComponent->pushBuffer(*sd);
180 }
181 }
182 else
183 break;
185 break;
186 }
190 default:
191 break;
192 }
193}
194
196{
197 m_numVisibleChannels = numChannels;
198 if (m_waveformsComponent)
199 m_waveformsComponent->setNumChannels(numChannels);
200}
201
203{
204 return m_numVisibleChannels;
205}
206
207
208}
Base class for all audio-data visualisation components in the Mema processor editor.
void paint(Graphics &) override
Paints the visualiser background.
void notifyChanges()
Marks that new data is available and triggers a repaint on the next timer tick.
void resized() override
Lays out child components.
Base class for all data objects exchanged between the audio processor and its analyzers/visualisers.
@ Invalid
Uninitialised or unknown data.
@ AudioSignal
Raw audio buffer data.
@ Level
Peak/RMS/hold level metering data.
@ Spectrum
FFT frequency-spectrum data.
Type GetDataType()
Returns the concrete type of this data object.
void setColours(juce::Colour tc, juce::Colour bc, juce::Colour wc)
void paintChannel(juce::Graphics &g, juce::Rectangle< float > area, const Range< float > *levels, int numLevels, int nextSample) override
virtual ~CustomPaintingAudioVisualiserComponent()=default
unsigned long GetChannelCount() override
Returns the number of channels in the audio buffer.
void paint(juce::Graphics &) override
void setNumVisibleChannels(int numChannels)
void processingDataChanged(AbstractProcessorData *data) override
Definition Mema.cpp:27