Skip to content

Commit ec61977

Browse files
committed
0.19.2
1 parent 034304d commit ec61977

File tree

6 files changed

+248
-246
lines changed

6 files changed

+248
-246
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
Plugin Host2 can interface directly with audio and MIDI hardware, OBS audio sources, and output audio as new OBS sources, allowing for complex audio processing setups. E.g. use ASIO interface as audio device, take additional audio from OBS sources, route monitoring to ASIO outputs and/or different audio drivers/hardware, use plugins and create final mix, and output the processed audio as a new OBS source for recording and streaming. Or just create a simple soundboard with a sampler plugin and a MIDI keyboard.
2121

22+
Develop your own audio processing plugins and integrate them into `Plugin Host2` using the [JUCE framework](https://juce.com/) AudioProcessor class. See `InternalPlugins.cpp` how `GainPlugin` is loaded. See `GainPlugin.h` for implementation.
23+
2224
## Device I/O
2325

2426
- Send and receive audio directly into and from audio devices

buildspec.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@
4343
"uuids": {
4444
"windowsApp": "ad885c58-5ca9-44de-8f4f-1c12676626a9"
4545
},
46-
"version": "0.19.1",
46+
"version": "0.19.2",
4747
"website": "https://www.atkaudio.com"
4848
}
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
#pragma once
2+
#include <juce_audio_utils/juce_audio_utils.h>
3+
4+
//==============================================================================
5+
class GainPlugin final
6+
: public juce::AudioProcessor
7+
, public juce::Timer
8+
{
9+
public:
10+
//==============================================================================
11+
GainPlugin()
12+
: AudioProcessor(
13+
BusesProperties()
14+
.withInput("Input", juce::AudioChannelSet::stereo())
15+
.withOutput("Output", juce::AudioChannelSet::stereo())
16+
)
17+
{
18+
using namespace juce;
19+
apvts = std::make_unique<AudioProcessorValueTreeState>(*this, nullptr, "state", createParameterLayout());
20+
gainValue = apvts->getRawParameterValue("gain");
21+
midiEnabled = apvts->getRawParameterValue("midi");
22+
midiChannel = apvts->getRawParameterValue("ch");
23+
midiCc = apvts->getRawParameterValue("cc");
24+
midiLearn = apvts->getRawParameterValue("learn");
25+
26+
gainParam = apvts->getParameter("gain");
27+
channelParam = apvts->getParameter("ch");
28+
ccParam = apvts->getParameter("cc");
29+
30+
startTimerHz(30); // Start the timer to process MIDI messages
31+
}
32+
33+
void timerCallback() override
34+
{
35+
if (midiEnabled->load(std::memory_order_acquire) > 0.5f)
36+
{
37+
auto gain = toUiGain.load(std::memory_order_acquire);
38+
gain = gainParam->getNormalisableRange().convertTo0to1(gain);
39+
gainParam->setValueNotifyingHost(gain);
40+
}
41+
42+
if (midiLearn->load(std::memory_order_acquire) > 0.5f)
43+
{
44+
auto cc = toUiCc.load(std::memory_order_acquire);
45+
auto ch = toUiChannel.load(std::memory_order_acquire);
46+
47+
cc = ccParam->getNormalisableRange().convertTo0to1(cc);
48+
ch = channelParam->getNormalisableRange().convertTo0to1(ch);
49+
50+
ccParam->setValueNotifyingHost(cc);
51+
channelParam->setValueNotifyingHost(ch);
52+
}
53+
}
54+
55+
//==============================================================================
56+
void prepareToPlay(double sampleRate, int samplesPerBlock) override
57+
{
58+
juce::ignoreUnused(sampleRate, samplesPerBlock);
59+
gainValueSmoothed.reset(sampleRate, 0.05f); // Smooth the gain value with a time constant of 50ms
60+
}
61+
62+
void releaseResources() override
63+
{
64+
}
65+
66+
void processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiBuffer) override
67+
{
68+
auto gain = gainValue->load(std::memory_order_acquire);
69+
70+
if (midiEnabled->load(std::memory_order_acquire) > 0.5f)
71+
{
72+
for (const auto& event : midiBuffer)
73+
{
74+
const auto& message = event.getMessage();
75+
if (message.isController()
76+
&& message.getChannel() == static_cast<int>(midiChannel->load(std::memory_order_acquire)))
77+
{
78+
if (message.getControllerNumber() == static_cast<int>(midiCc->load(std::memory_order_acquire)))
79+
{
80+
auto faderPos = message.getControllerValue() / 127.0f;
81+
gain = gainParam->getNormalisableRange().convertFrom0to1(faderPos);
82+
toUiGain.store(gain, std::memory_order_release);
83+
}
84+
}
85+
}
86+
}
87+
88+
auto initialGain = gainValueSmoothed.getCurrentValue();
89+
for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
90+
{
91+
gainValueSmoothed.setTargetValue(gain);
92+
auto* readPtr = buffer.getReadPointer(channel);
93+
auto* writePtr = buffer.getWritePointer(channel);
94+
for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
95+
writePtr[sample] = readPtr[sample] * gainValueSmoothed.getNextValue();
96+
}
97+
98+
if (midiLearn->load(std::memory_order_acquire) > 0.5f)
99+
{
100+
for (const auto& event : midiBuffer)
101+
{
102+
const auto& message = event.getMessage();
103+
if (message.isController())
104+
{
105+
auto channel = message.getChannel();
106+
auto cc = message.getControllerNumber();
107+
108+
toUiChannel.store(channel, std::memory_order_release);
109+
toUiCc.store(cc, std::memory_order_release);
110+
}
111+
}
112+
}
113+
}
114+
115+
//==============================================================================
116+
juce::AudioProcessorEditor* createEditor() override
117+
{
118+
return new juce::GenericAudioProcessorEditor(*this);
119+
}
120+
121+
bool hasEditor() const override
122+
{
123+
return true;
124+
}
125+
126+
//==============================================================================
127+
const juce::String getName() const override
128+
{
129+
return "Gain Plugin";
130+
}
131+
132+
bool acceptsMidi() const override
133+
{
134+
return true;
135+
}
136+
137+
bool producesMidi() const override
138+
{
139+
return false;
140+
}
141+
142+
double getTailLengthSeconds() const override
143+
{
144+
return 0;
145+
}
146+
147+
//==============================================================================
148+
int getNumPrograms() override
149+
{
150+
return 1;
151+
}
152+
153+
int getCurrentProgram() override
154+
{
155+
return 0;
156+
}
157+
158+
void setCurrentProgram(int) override
159+
{
160+
}
161+
162+
const juce::String getProgramName(int) override
163+
{
164+
return "None";
165+
}
166+
167+
void changeProgramName(int, const juce::String&) override
168+
{
169+
}
170+
171+
//==============================================================================
172+
void getStateInformation(juce::MemoryBlock& destData) override
173+
{
174+
if (auto xml = apvts->copyState().createXml())
175+
copyXmlToBinary(*xml, destData);
176+
}
177+
178+
void setStateInformation(const void* data, int sizeInBytes) override
179+
{
180+
std::unique_ptr<juce::XmlElement> xml{getXmlFromBinary(data, sizeInBytes)};
181+
if (xml != nullptr)
182+
apvts->replaceState(juce::ValueTree::fromXml(*xml));
183+
184+
toUiGain.store(gainValue->load(std::memory_order_acquire), std::memory_order_release);
185+
toUiChannel.store(midiChannel->load(std::memory_order_acquire), std::memory_order_release);
186+
toUiCc.store(midiCc->load(std::memory_order_acquire), std::memory_order_release);
187+
}
188+
189+
//==============================================================================
190+
bool isBusesLayoutSupported(const BusesLayout& layouts) const override
191+
{
192+
const auto& mainInLayout = layouts.getChannelSet(true, 0);
193+
const auto& mainOutLayout = layouts.getChannelSet(false, 0);
194+
195+
return (mainInLayout == mainOutLayout && (!mainInLayout.isDisabled()));
196+
}
197+
198+
private:
199+
//==============================================================================
200+
static juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout()
201+
{
202+
using namespace juce;
203+
std::vector<std::unique_ptr<RangedAudioParameter>> params;
204+
205+
auto gainRange = NormalisableRange<float>(0.0f, 1.0f, 0.0f, 1.0f);
206+
gainRange.setSkewForCentre(0.25f);
207+
208+
auto channelRange = NormalisableRange<float>(0.0f, 16.0f, 1.0f, 1.0f);
209+
auto ccRange = NormalisableRange<float>(0.0f, 128.0f, 1.0f, 1.0f);
210+
211+
params.push_back(std::make_unique<AudioParameterFloat>(ParameterID{"gain", 1}, "Gain", gainRange, 1.0f));
212+
213+
params.push_back(std::make_unique<AudioParameterBool>(ParameterID{"midi", 1}, "MIDI", false));
214+
215+
params.push_back(std::make_unique<AudioParameterFloat>(ParameterID{"ch", 1}, "Channel", channelRange, 1.0f));
216+
217+
params.push_back(std::make_unique<AudioParameterFloat>(ParameterID{"cc", 1}, "CC", ccRange, 1.0f));
218+
219+
params.push_back(std::make_unique<AudioParameterBool>(ParameterID{"learn", 1}, "Learn", false));
220+
221+
return {params.begin(), params.end()};
222+
}
223+
224+
std::unique_ptr<juce::AudioProcessorValueTreeState> apvts;
225+
std::atomic<float>* gainValue = nullptr;
226+
std::atomic<float>* midiEnabled = nullptr;
227+
std::atomic<float>* midiChannel = nullptr;
228+
std::atomic<float>* midiCc = nullptr;
229+
std::atomic<float>* midiLearn = nullptr;
230+
231+
juce::LinearSmoothedValue<float> gainValueSmoothed;
232+
233+
juce::RangedAudioParameter* gainParam = nullptr;
234+
juce::RangedAudioParameter* channelParam = nullptr;
235+
juce::RangedAudioParameter* ccParam = nullptr;
236+
237+
std::atomic<float> toUiGain = 0.0f;
238+
std::atomic<float> toUiChannel = 0.0f;
239+
std::atomic<float> toUiCc = 0.0f;
240+
241+
//==============================================================================
242+
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GainPlugin)
243+
};

lib/atkaudio/src/atkaudio/PluginHost2/Plugins/InternalPlugins.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "InternalPlugins.h"
22

3+
#include "GainPlugin.h"
34
#include "PluginGraph.h"
45

56
#include <config.h>
@@ -678,7 +679,7 @@ InternalPluginFormat::InternalPluginFormat()
678679
[] { return std::make_unique<InternalPlugin>(std::make_unique<Ph2DeviceIoProcessor>()); },
679680
[] { return std::make_unique<InternalPlugin>(std::make_unique<SineWaveSynth>()); },
680681
[] { return std::make_unique<InternalPlugin>(std::make_unique<ReverbPlugin>()); },
681-
[] { return std::make_unique<InternalPlugin>(std::make_unique<MidiGainPlugin>()); }
682+
[] { return std::make_unique<InternalPlugin>(std::make_unique<GainPlugin>()); }
682683
}
683684
{
684685
}

lib/atkaudio/src/atkaudio/PluginHost2/Plugins/InternalPlugins.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ using namespace juce;
66
#include "../ObsOutput.h"
77
#include "../ObsSource.h"
88
#include "../Ph2DeviceIo/Ph2DeviceIo.h"
9-
#include "MidiGainPlugin.h"
109
#include "PluginGraph.h"
1110

1211
//==============================================================================

0 commit comments

Comments
 (0)