mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +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();
|
||||
|
||||
currentAudioDevice->start (callbackHandler.get());
|
||||
|
||||
currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
|
||||
currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
|
||||
currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
|
||||
currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
|
||||
updateCurrentSetup();
|
||||
|
||||
for (int i = 0; i < availableDeviceTypes.size(); ++i)
|
||||
if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType)
|
||||
|
|
@ -965,6 +961,8 @@ void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device
|
|||
loadMeasurer.reset (device->getCurrentSampleRate(),
|
||||
device->getCurrentBufferSizeSamples());
|
||||
|
||||
updateCurrentSetup();
|
||||
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
|
||||
|
|
@ -972,7 +970,6 @@ void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device
|
|||
callbacks.getUnchecked(i)->audioDeviceAboutToStart (device);
|
||||
}
|
||||
|
||||
updateCurrentSetup();
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
|
|
@ -1444,6 +1441,48 @@ public:
|
|||
enableInputChannels (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:
|
||||
|
|
@ -1608,11 +1647,26 @@ private:
|
|||
const String mockBName = "mockB";
|
||||
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:
|
||||
MockDevice (String typeNameIn, String outNameIn, String inNameIn)
|
||||
: AudioIODevice ("mock", typeNameIn), outName (outNameIn), inName (inNameIn) {}
|
||||
MockDevice (ListenerList<Restartable>& l, String typeNameIn, String outNameIn, String 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 getInputChannelNames() override { return { "i1", "i2", "i3" }; }
|
||||
|
|
@ -1634,8 +1688,19 @@ private:
|
|||
void close() override { on = false; }
|
||||
bool isOpen() override { return on; }
|
||||
|
||||
void start (AudioIODeviceCallback*) override { playing = true; }
|
||||
void stop() override { playing = false; }
|
||||
void start (AudioIODeviceCallback* c) override
|
||||
{
|
||||
callback = c;
|
||||
callback->audioDeviceAboutToStart (this);
|
||||
playing = true;
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
playing = false;
|
||||
callback->audioDeviceStopped();
|
||||
}
|
||||
|
||||
bool isPlaying() override { return playing; }
|
||||
|
||||
String getLastError() override { return {}; }
|
||||
|
|
@ -1650,6 +1715,16 @@ private:
|
|||
int getInputLatencyInSamples() override { return 0; }
|
||||
|
||||
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;
|
||||
BigInteger outChannels, inChannels;
|
||||
double sampleRate = 0.0;
|
||||
|
|
@ -1668,6 +1743,12 @@ private:
|
|||
inNames (std::move (inputNames)),
|
||||
outNames (std::move (outputNames)) {}
|
||||
|
||||
~MockDeviceType() override
|
||||
{
|
||||
// A Device outlived its DeviceType!
|
||||
jassert (listeners.isEmpty());
|
||||
}
|
||||
|
||||
void scanForDevices() override {}
|
||||
|
||||
StringArray getDeviceNames (bool isInput) const override
|
||||
|
|
@ -1687,15 +1768,37 @@ private:
|
|||
AudioIODevice* createDevice (const String& outputName, const String& inputName) override
|
||||
{
|
||||
if (inNames.contains (inputName) || outNames.contains (outputName))
|
||||
return new MockDevice (getTypeName(), outputName, inputName);
|
||||
return new MockDevice (listeners, getTypeName(), outputName, inputName);
|
||||
|
||||
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:
|
||||
const StringArray& getNames (bool isInput) const { return isInput ? 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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue