Skip to content

Commit 034304d

Browse files
committed
0.19.1
1 parent 63b42e7 commit 034304d

File tree

10 files changed

+276
-9
lines changed

10 files changed

+276
-9
lines changed

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.18.2",
46+
"version": "0.19.1",
4747
"website": "https://www.atkaudio.com"
4848
}

lib/atkaudio/src/atkaudio/FifoBuffer2.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,7 @@ class SyncBuffer : public juce::Timer
409409
&& juce::approximatelyEqual(finalRatio, (double)(writerSampleRate / readerSampleRate)))
410410
DBG("time: "
411411
<< juce::Time::getCurrentTime().toString(true, true)
412-
<< juce::String(" final ratio ")
413-
+ juce::String(finalRatio));
412+
<< juce::String(" final ratio ") + juce::String(finalRatio));
414413
#endif
415414
prevFinalRatio = finalRatio;
416415
fifoBuffer.advanceRead(totalSamplesConsumed);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,7 @@ InternalPluginFormat::InternalPluginFormat()
678678
[] { return std::make_unique<InternalPlugin>(std::make_unique<Ph2DeviceIoProcessor>()); },
679679
[] { return std::make_unique<InternalPlugin>(std::make_unique<SineWaveSynth>()); },
680680
[] { return std::make_unique<InternalPlugin>(std::make_unique<ReverbPlugin>()); },
681+
[] { return std::make_unique<InternalPlugin>(std::make_unique<MidiGainPlugin>()); }
681682
}
682683
{
683684
}

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

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

1112
//==============================================================================
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 MidiGainPlugin final
6+
: public juce::AudioProcessor
7+
, public juce::Timer
8+
{
9+
public:
10+
//==============================================================================
11+
MidiGainPlugin()
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(MidiGainPlugin)
243+
};

lib/atkaudio/src/atkaudio/atkaudio.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ void atk::pump()
2626
void atk::destroy()
2727
{
2828
juce::MessageManager::getInstance()->setCurrentThreadAsMessageThread();
29+
auto numComponents = juce::Desktop::getInstance().getNumComponents();
2930
juce::shutdownJuce_GUI();
31+
DBG("waiting components: " << numComponents);
32+
std::this_thread::sleep_for(std::chrono::milliseconds(100 * numComponents > 1000 ? 1000 : 100 * numComponents));
3033
}
3134

3235
void atk::update()

src/MessagePump.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@ MessagePump::~MessagePump()
1515
atk::destroy();
1616
}
1717

18+
void MessagePump::stopPump()
19+
{
20+
needsToStop.store(true, std::memory_order_release);
21+
}
22+
1823
void MessagePump::onTimeout()
1924
{
25+
if (needsToStop.load(std::memory_order_acquire))
26+
return;
27+
2028
atk::pump();
2129
}
2230
#endif

src/MessagePump.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ class MessagePump : public QObject
1818
MessagePump(QObject* parent = nullptr);
1919
~MessagePump();
2020

21+
void stopPump();
22+
2123
private slots:
2224

2325
void onTimeout();
2426

2527
private:
28+
std::atomic_bool needsToStop{false};
2629
QTimer* timer;
2730
};
28-
#endif
31+
#endif

src/device_io.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct adio_data
2424
obs_source_t* context = nullptr;
2525
obs_data_t* settings = nullptr;
2626

27-
int channels;
27+
int channels = 0;
2828
double sampleRate = 0.0;
2929

3030
std::atomic_bool mixInput = false;
@@ -33,6 +33,8 @@ struct adio_data
3333
std::atomic<float> outputGain = 1.0f;
3434

3535
atk::DeviceIo deviceIo;
36+
37+
bool hasInitUpdateLoad = false;
3638
};
3739

3840
static const char* devio_name(void* unused)
@@ -73,8 +75,11 @@ static void devio_update(void* data, obs_data_t* s)
7375
// auto outputGain = (float)obs_data_get_double(s, OG_ID);
7476
// outputGain = obs_db_to_mul(outputGain);
7577
// adio->outputGain.store(outputGain, std::memory_order_release);
76-
77-
// load(data, s);
78+
if (!adio->hasInitUpdateLoad)
79+
{
80+
adio->hasInitUpdateLoad = true;
81+
load(data, s);
82+
}
7883
}
7984

8085
static void* devio_create(obs_data_t* settings, obs_source_t* filter)

src/plugin-main.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ extern struct obs_source_info ph2helper_source_info;
4242
void obs_log(int log_level, const char* format, ...);
4343

4444
#include "MessagePump.h"
45+
4546
#ifndef NO_MESSAGE_PUMP
4647
#include <obs-frontend-api.h>
4748
MessagePump* messagePump = nullptr;
49+
4850
#endif
4951

5052
bool obs_module_load(void)
@@ -71,9 +73,11 @@ bool obs_module_load(void)
7173

7274
void obs_module_unload(void)
7375
{
74-
#ifdef NO_MESSAGE_PUMP
75-
atk::destroy();
76+
#ifndef NO_MESSAGE_PUMP
77+
if (messagePump)
78+
messagePump->stopPump();
7679
#endif
80+
atk::destroy();
7781
obs_log(LOG_INFO, "plugin unloaded");
7882
}
7983

0 commit comments

Comments
 (0)