mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
AudioDeviceManager: Ensure device settings are up to date before notifying callbacks
This commit is contained in:
parent
550d61e487
commit
df206371ff
1 changed files with 115 additions and 12 deletions
|
|
@ -715,11 +715,7 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
|
||||||
currentDeviceType = currentAudioDevice->getTypeName();
|
currentDeviceType = currentAudioDevice->getTypeName();
|
||||||
|
|
||||||
currentAudioDevice->start (callbackHandler.get());
|
currentAudioDevice->start (callbackHandler.get());
|
||||||
|
updateCurrentSetup();
|
||||||
currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
|
|
||||||
currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
|
|
||||||
currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
|
|
||||||
currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
|
|
||||||
|
|
||||||
for (int i = 0; i < availableDeviceTypes.size(); ++i)
|
for (int i = 0; i < availableDeviceTypes.size(); ++i)
|
||||||
if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType)
|
if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType)
|
||||||
|
|
@ -965,6 +961,8 @@ void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device
|
||||||
loadMeasurer.reset (device->getCurrentSampleRate(),
|
loadMeasurer.reset (device->getCurrentSampleRate(),
|
||||||
device->getCurrentBufferSizeSamples());
|
device->getCurrentBufferSizeSamples());
|
||||||
|
|
||||||
|
updateCurrentSetup();
|
||||||
|
|
||||||
{
|
{
|
||||||
const ScopedLock sl (audioCallbackLock);
|
const ScopedLock sl (audioCallbackLock);
|
||||||
|
|
||||||
|
|
@ -972,7 +970,6 @@ void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device
|
||||||
callbacks.getUnchecked(i)->audioDeviceAboutToStart (device);
|
callbacks.getUnchecked(i)->audioDeviceAboutToStart (device);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCurrentSetup();
|
|
||||||
sendChangeMessage();
|
sendChangeMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1444,6 +1441,48 @@ public:
|
||||||
enableInputChannels (manager);
|
enableInputChannels (manager);
|
||||||
closeDeviceByRequestingEmptyNames (manager);
|
closeDeviceByRequestingEmptyNames (manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beginTest ("AudioDeviceManager updates its current settings before notifying callbacks when device restarts itself");
|
||||||
|
{
|
||||||
|
AudioDeviceManager manager;
|
||||||
|
auto deviceType = std::make_unique<MockDeviceType> ("foo",
|
||||||
|
StringArray { "foo in a", "foo in b" },
|
||||||
|
StringArray { "foo out a", "foo out b" });
|
||||||
|
auto* ptr = deviceType.get();
|
||||||
|
manager.addAudioDeviceType (std::move (deviceType));
|
||||||
|
|
||||||
|
AudioDeviceManager::AudioDeviceSetup setup;
|
||||||
|
setup.sampleRate = 48000.0;
|
||||||
|
setup.bufferSize = 256;
|
||||||
|
setup.inputDeviceName = "foo in a";
|
||||||
|
setup.outputDeviceName = "foo out a";
|
||||||
|
setup.useDefaultInputChannels = true;
|
||||||
|
setup.useDefaultOutputChannels = true;
|
||||||
|
manager.setAudioDeviceSetup (setup, true);
|
||||||
|
|
||||||
|
const auto currentSetup = manager.getAudioDeviceSetup();
|
||||||
|
expectEquals (currentSetup.sampleRate, setup.sampleRate);
|
||||||
|
expectEquals (currentSetup.bufferSize, setup.bufferSize);
|
||||||
|
|
||||||
|
MockCallback callback;
|
||||||
|
manager.addAudioCallback (&callback);
|
||||||
|
|
||||||
|
constexpr auto newSr = 10000.0;
|
||||||
|
constexpr auto newBs = 1024;
|
||||||
|
auto numCalls = 0;
|
||||||
|
|
||||||
|
// Compilers disagree about whether newSr and newBs need to be captured
|
||||||
|
callback.aboutToStart = [&]
|
||||||
|
{
|
||||||
|
++numCalls;
|
||||||
|
const auto current = manager.getAudioDeviceSetup();
|
||||||
|
expectEquals (current.sampleRate, newSr);
|
||||||
|
expectEquals (current.bufferSize, newBs);
|
||||||
|
};
|
||||||
|
|
||||||
|
ptr->restartDevices (newSr, newBs);
|
||||||
|
expectEquals (numCalls, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -1608,11 +1647,26 @@ private:
|
||||||
const String mockBName = "mockB";
|
const String mockBName = "mockB";
|
||||||
const String emptyName = "empty";
|
const String emptyName = "empty";
|
||||||
|
|
||||||
class MockDevice : public AudioIODevice
|
struct Restartable
|
||||||
|
{
|
||||||
|
virtual ~Restartable() = default;
|
||||||
|
virtual void restart (double newSr, int newBs) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockDevice : public AudioIODevice,
|
||||||
|
private Restartable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MockDevice (String typeNameIn, String outNameIn, String inNameIn)
|
MockDevice (ListenerList<Restartable>& l, String typeNameIn, String outNameIn, String inNameIn)
|
||||||
: AudioIODevice ("mock", typeNameIn), outName (outNameIn), inName (inNameIn) {}
|
: AudioIODevice ("mock", typeNameIn), listeners (l), outName (outNameIn), inName (inNameIn)
|
||||||
|
{
|
||||||
|
listeners.add (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MockDevice() override
|
||||||
|
{
|
||||||
|
listeners.remove (this);
|
||||||
|
}
|
||||||
|
|
||||||
StringArray getOutputChannelNames() override { return { "o1", "o2", "o3" }; }
|
StringArray getOutputChannelNames() override { return { "o1", "o2", "o3" }; }
|
||||||
StringArray getInputChannelNames() override { return { "i1", "i2", "i3" }; }
|
StringArray getInputChannelNames() override { return { "i1", "i2", "i3" }; }
|
||||||
|
|
@ -1634,8 +1688,19 @@ private:
|
||||||
void close() override { on = false; }
|
void close() override { on = false; }
|
||||||
bool isOpen() override { return on; }
|
bool isOpen() override { return on; }
|
||||||
|
|
||||||
void start (AudioIODeviceCallback*) override { playing = true; }
|
void start (AudioIODeviceCallback* c) override
|
||||||
void stop() override { playing = false; }
|
{
|
||||||
|
callback = c;
|
||||||
|
callback->audioDeviceAboutToStart (this);
|
||||||
|
playing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() override
|
||||||
|
{
|
||||||
|
playing = false;
|
||||||
|
callback->audioDeviceStopped();
|
||||||
|
}
|
||||||
|
|
||||||
bool isPlaying() override { return playing; }
|
bool isPlaying() override { return playing; }
|
||||||
|
|
||||||
String getLastError() override { return {}; }
|
String getLastError() override { return {}; }
|
||||||
|
|
@ -1650,6 +1715,16 @@ private:
|
||||||
int getInputLatencyInSamples() override { return 0; }
|
int getInputLatencyInSamples() override { return 0; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void restart (double newSr, int newBs) override
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
close();
|
||||||
|
open (inChannels, outChannels, newSr, newBs);
|
||||||
|
start (callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
ListenerList<Restartable>& listeners;
|
||||||
|
AudioIODeviceCallback* callback = nullptr;
|
||||||
String outName, inName;
|
String outName, inName;
|
||||||
BigInteger outChannels, inChannels;
|
BigInteger outChannels, inChannels;
|
||||||
double sampleRate = 0.0;
|
double sampleRate = 0.0;
|
||||||
|
|
@ -1668,6 +1743,12 @@ private:
|
||||||
inNames (std::move (inputNames)),
|
inNames (std::move (inputNames)),
|
||||||
outNames (std::move (outputNames)) {}
|
outNames (std::move (outputNames)) {}
|
||||||
|
|
||||||
|
~MockDeviceType() override
|
||||||
|
{
|
||||||
|
// A Device outlived its DeviceType!
|
||||||
|
jassert (listeners.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
void scanForDevices() override {}
|
void scanForDevices() override {}
|
||||||
|
|
||||||
StringArray getDeviceNames (bool isInput) const override
|
StringArray getDeviceNames (bool isInput) const override
|
||||||
|
|
@ -1687,15 +1768,37 @@ private:
|
||||||
AudioIODevice* createDevice (const String& outputName, const String& inputName) override
|
AudioIODevice* createDevice (const String& outputName, const String& inputName) override
|
||||||
{
|
{
|
||||||
if (inNames.contains (inputName) || outNames.contains (outputName))
|
if (inNames.contains (inputName) || outNames.contains (outputName))
|
||||||
return new MockDevice (getTypeName(), outputName, inputName);
|
return new MockDevice (listeners, getTypeName(), outputName, inputName);
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call this to emulate the device restarting itself with new settings.
|
||||||
|
// This might happen e.g. when a user changes the ASIO settings.
|
||||||
|
void restartDevices (double newSr, int newBs)
|
||||||
|
{
|
||||||
|
listeners.call ([&] (auto& l) { return l.restart (newSr, newBs); });
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const StringArray& getNames (bool isInput) const { return isInput ? inNames : outNames; }
|
const StringArray& getNames (bool isInput) const { return isInput ? inNames : outNames; }
|
||||||
|
|
||||||
const StringArray inNames, outNames;
|
const StringArray inNames, outNames;
|
||||||
|
ListenerList<Restartable> listeners;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockCallback : public AudioIODeviceCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::function<void()> callback;
|
||||||
|
std::function<void()> aboutToStart;
|
||||||
|
std::function<void()> stopped;
|
||||||
|
std::function<void()> error;
|
||||||
|
|
||||||
|
void audioDeviceIOCallback (const float**, int, float**, int, int) override { NullCheckedInvocation::invoke (callback); }
|
||||||
|
void audioDeviceAboutToStart (AudioIODevice*) override { NullCheckedInvocation::invoke (aboutToStart); }
|
||||||
|
void audioDeviceStopped() override { NullCheckedInvocation::invoke (stopped); }
|
||||||
|
void audioDeviceError (const String&) override { NullCheckedInvocation::invoke (error); }
|
||||||
};
|
};
|
||||||
|
|
||||||
void initialiseManager (AudioDeviceManager& manager)
|
void initialiseManager (AudioDeviceManager& manager)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue