Skip to content

Commit cbe832c

Browse files
committed
0.31.11
1 parent e25f8ce commit cbe832c

6 files changed

Lines changed: 155 additions & 111 deletions

File tree

buildspec.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@
4747
"uuids": {
4848
"windowsApp": "ad885c58-5ca9-44de-8f4f-1c12676626a9"
4949
},
50-
"version": "0.31.10",
50+
"version": "0.31.11",
5151
"website": "https://www.atkaudio.com"
5252
}

lib/atkaudio/src/atkaudio/DeviceIo/DeviceIo.cpp

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,37 +30,37 @@ struct atk::DeviceIo::Impl
3030
void process(float** buffer, int numChannels, int numSamples, double sampleRate)
3131
{
3232
bool currentBypass = bypass.load(std::memory_order_acquire);
33-
bool wasJustBypassed = wasBypassed.exchange(currentBypass, std::memory_order_acq_rel);
33+
float targetGain = currentBypass ? 0.0f : 1.0f;
3434

35-
if (currentBypass)
36-
return;
37-
38-
// Clear stale data from buffers when transitioning from bypassed to active
39-
if (wasJustBypassed)
35+
// Update smoother if sample rate or target changed
36+
if (fadeGain.getTargetValue() != targetGain)
4037
{
41-
auto& toObsBuffer = deviceIoApp->getToObsBuffer();
42-
auto& fromObsBuffer = deviceIoApp->getFromObsBuffer();
43-
toObsBuffer.reset();
44-
fromObsBuffer.reset();
45-
// Start fade-in over one buffer
46-
fadeInSamplesRemaining = numSamples;
47-
fadeInTotalSamples = numSamples;
38+
fadeGain.reset(sampleRate, fadeDurationSeconds);
39+
40+
// Clear buffers when transitioning from bypass to active
41+
if (!currentBypass)
42+
{
43+
auto& toObsBuffer = deviceIoApp->getToObsBuffer();
44+
auto& fromObsBuffer = deviceIoApp->getFromObsBuffer();
45+
toObsBuffer.reset();
46+
fromObsBuffer.reset();
47+
}
48+
fadeGain.setTargetValue(targetGain);
4849
}
4950

50-
// Apply fade-in ramp if active
51-
if (fadeInSamplesRemaining > 0)
51+
// Skip processing if fully bypassed
52+
if (currentBypass && !fadeGain.isSmoothing())
53+
return;
54+
55+
// Apply fade gain
56+
if (fadeGain.isSmoothing())
5257
{
53-
for (int ch = 0; ch < numChannels; ++ch)
58+
for (int i = 0; i < numSamples; ++i)
5459
{
55-
auto* channelData = buffer[ch];
56-
int fadeStart = fadeInTotalSamples - fadeInSamplesRemaining;
57-
for (int i = 0; i < numSamples && fadeInSamplesRemaining > 0; ++i)
58-
{
59-
float gain = static_cast<float>(fadeStart + i + 1) / static_cast<float>(fadeInTotalSamples);
60-
channelData[i] *= gain;
61-
}
60+
float gain = fadeGain.getNextValue();
61+
for (int ch = 0; ch < numChannels; ++ch)
62+
buffer[ch][i] *= gain;
6263
}
63-
fadeInSamplesRemaining = std::max(0, fadeInSamplesRemaining - numSamples);
6464
}
6565

6666
if (tempBuffer.getNumChannels() < numChannels || tempBuffer.getNumSamples() < numSamples)
@@ -242,9 +242,8 @@ struct atk::DeviceIo::Impl
242242

243243
bool mixInput = false;
244244
std::atomic<bool> bypass{false};
245-
std::atomic<bool> wasBypassed{false};
246-
int fadeInSamplesRemaining{0};
247-
int fadeInTotalSamples{0};
245+
juce::SmoothedValue<float, juce::ValueSmoothingTypes::Linear> fadeGain{1.0f};
246+
static constexpr double fadeDurationSeconds = 0.5;
248247

249248
public:
250249
void setBypass(bool v)

lib/atkaudio/src/atkaudio/DeviceIo2/DeviceIo2.cpp

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ struct atk::DeviceIo2::Impl : public juce::AsyncUpdater
3232
std::vector<juce::SmoothedValue<float, juce::ValueSmoothingTypes::Linear>> outputDelaySmooth;
3333
bool delayPrepared = false;
3434
std::atomic<bool> bypass{false};
35-
std::atomic<bool> wasBypassed{false};
36-
int fadeInSamplesRemaining{0};
37-
int fadeInTotalSamples{0};
35+
juce::SmoothedValue<float, juce::ValueSmoothingTypes::Linear> fadeGain{1.0f};
36+
static constexpr double fadeDurationSeconds = 0.5;
3837

3938
enum class UpdateType
4039
{
@@ -199,35 +198,33 @@ struct atk::DeviceIo2::Impl : public juce::AsyncUpdater
199198
void process(float** buffer, int numChannels, int numSamples, double sampleRate)
200199
{
201200
bool currentBypass = bypass.load(std::memory_order_acquire);
202-
bool wasJustBypassed = wasBypassed.exchange(currentBypass, std::memory_order_acq_rel);
201+
float targetGain = currentBypass ? 0.0f : 1.0f;
203202

204-
// When bypassed, leave buffer as-is and skip IO routing
205-
if (currentBypass)
206-
return;
207-
208-
// Clear stale data from AudioClient buffers when transitioning from bypassed to active
209-
if (wasJustBypassed)
203+
// Update smoother if target changed
204+
if (fadeGain.getTargetValue() != targetGain)
210205
{
211-
audioClient.clearBuffers();
212-
// Start fade-in over one buffer
213-
fadeInSamplesRemaining = numSamples;
214-
fadeInTotalSamples = numSamples;
206+
fadeGain.reset(sampleRate, fadeDurationSeconds);
207+
208+
// Clear buffers when transitioning from bypass to active
209+
if (!currentBypass)
210+
audioClient.clearBuffers();
211+
212+
fadeGain.setTargetValue(targetGain);
215213
}
216214

217-
// Apply fade-in ramp if active
218-
if (fadeInSamplesRemaining > 0)
215+
// Skip processing if fully bypassed
216+
if (currentBypass && !fadeGain.isSmoothing())
217+
return;
218+
219+
// Apply fade gain
220+
if (fadeGain.isSmoothing())
219221
{
220-
for (int ch = 0; ch < numChannels; ++ch)
222+
for (int i = 0; i < numSamples; ++i)
221223
{
222-
auto* channelData = buffer[ch];
223-
int fadeStart = fadeInTotalSamples - fadeInSamplesRemaining;
224-
for (int i = 0; i < numSamples && fadeInSamplesRemaining > 0; ++i)
225-
{
226-
float gain = static_cast<float>(fadeStart + i + 1) / static_cast<float>(fadeInTotalSamples);
227-
channelData[i] *= gain;
228-
}
224+
float gain = fadeGain.getNextValue();
225+
for (int ch = 0; ch < numChannels; ++ch)
226+
buffer[ch][i] *= gain;
229227
}
230-
fadeInSamplesRemaining = std::max(0, fadeInSamplesRemaining - numSamples);
231228
}
232229
bool needsReconfiguration =
233230
preparedNumChannels != numChannels || preparedNumSamples < numSamples || preparedSampleRate != sampleRate;

lib/atkaudio/src/atkaudio/ModuleInfrastructure/MidiServer/MidiServer.cpp

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,8 @@ void MidiServer::initialize()
151151
return;
152152
}
153153

154-
auto midiInputs = juce::MidiInput::getAvailableDevices();
155-
for (const auto& device : midiInputs)
156-
{
157-
deviceManager.setMidiInputDeviceEnabled(device.identifier, true);
158-
deviceManager.addMidiInputDeviceCallback(device.identifier, this);
159-
DBG("[MidiServer] Enabled MIDI input: " + device.name);
160-
}
154+
// MIDI inputs are now opened on-demand based on client subscriptions
155+
// See updateMidiDeviceSubscriptions()
161156

162157
startTimer(10);
163158
initialized = true;
@@ -179,14 +174,16 @@ void MidiServer::shutdown()
179174
for (juce::HashMap<juce::String, juce::MidiOutput*>::Iterator it(outputDevices); it.next();)
180175
delete it.getValue();
181176
outputDevices.clear();
182-
clients.clear();
183-
}
184177

185-
auto midiInputs = juce::MidiInput::getAvailableDevices();
186-
for (const auto& device : midiInputs)
187-
{
188-
deviceManager.setMidiInputDeviceEnabled(device.identifier, false);
189-
deviceManager.removeMidiInputDeviceCallback(device.identifier, this);
178+
// Disable all enabled MIDI inputs
179+
for (const auto& [name, identifier] : enabledInputDevices)
180+
{
181+
deviceManager.setMidiInputDeviceEnabled(identifier, false);
182+
deviceManager.removeMidiInputDeviceCallback(identifier, this);
183+
}
184+
enabledInputDevices.clear();
185+
186+
clients.clear();
190187
}
191188

192189
deviceManager.closeAudioDevice();
@@ -391,23 +388,58 @@ void MidiServer::rebuildClientSnapshot()
391388

392389
void MidiServer::updateMidiDeviceSubscriptions()
393390
{
394-
juce::StringArray neededOutputs;
395-
for (auto& [clientPtr, info] : clients)
391+
// Collect all needed devices from client subscriptions
392+
juce::StringArray neededInputs, neededOutputs;
393+
for (const auto& [clientPtr, info] : clients)
394+
{
395+
for (const auto& device : info.state.subscribedInputDevices)
396+
neededInputs.addIfNotAlreadyThere(device);
396397
for (const auto& device : info.state.subscribedOutputDevices)
397398
neededOutputs.addIfNotAlreadyThere(device);
399+
}
400+
401+
// Build name->identifier map for available inputs
402+
std::unordered_map<juce::String, juce::String> availableInputMap;
403+
for (const auto& device : juce::MidiInput::getAvailableDevices())
404+
availableInputMap[device.name] = device.identifier;
405+
406+
// Enable newly needed input devices
407+
for (const auto& name : neededInputs)
408+
{
409+
if (enabledInputDevices.count(name) == 0)
410+
{
411+
auto it = availableInputMap.find(name);
412+
if (it != availableInputMap.end())
413+
{
414+
deviceManager.setMidiInputDeviceEnabled(it->second, true);
415+
deviceManager.addMidiInputDeviceCallback(it->second, this);
416+
enabledInputDevices[name] = it->second;
417+
DBG("[MidiServer] Enabled MIDI input: " + name);
418+
}
419+
}
420+
}
421+
422+
// Disable input devices no longer needed
423+
for (auto it = enabledInputDevices.begin(); it != enabledInputDevices.end();)
424+
if (!neededInputs.contains(it->first))
425+
{
426+
deviceManager.setMidiInputDeviceEnabled(it->second, false);
427+
deviceManager.removeMidiInputDeviceCallback(it->second, this);
428+
DBG("[MidiServer] Disabled MIDI input: " + it->first);
429+
it = enabledInputDevices.erase(it);
430+
}
431+
else
432+
++it;
398433

399-
juce::Array<juce::String> devicesToRemove;
434+
// Close output devices no longer needed (they're opened lazily in timerCallback)
400435
for (juce::HashMap<juce::String, juce::MidiOutput*>::Iterator it(outputDevices); it.next();)
401436
{
402437
if (!neededOutputs.contains(it.getKey()))
403438
{
404439
delete it.getValue();
405-
devicesToRemove.add(it.getKey());
440+
outputDevices.remove(it.getKey());
406441
}
407442
}
408-
409-
for (const auto& device : devicesToRemove)
410-
outputDevices.remove(device);
411443
}
412444

413445
} // namespace atk

lib/atkaudio/src/atkaudio/ModuleInfrastructure/MidiServer/MidiServer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ class MidiServer
211211
std::unordered_map<void*, ClientInfo> clients;
212212
AtomicSharedPtr<DeviceSnapshot> activeSnapshot{std::make_shared<DeviceSnapshot>()};
213213
juce::HashMap<juce::String, juce::MidiOutput*> outputDevices;
214+
std::unordered_map<juce::String, juce::String> enabledInputDevices; // name -> identifier
214215
bool initialized = false;
215216

216217
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiServer)

0 commit comments

Comments
 (0)