mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
AudioDeviceManager: Move CallbackMaxSizeEnforcer from StandaloneFilterWindow
This commit is contained in:
parent
28a2700a28
commit
ee37564083
2 changed files with 100 additions and 97 deletions
|
|
@ -73,12 +73,101 @@ bool AudioDeviceManager::AudioDeviceSetup::operator!= (const AudioDeviceManager:
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
class AudioDeviceManager::CallbackHandler final : public AudioIODeviceCallback,
|
/* This class is used to ensure that audio callbacks use buffers with a
|
||||||
public MidiInputCallback,
|
predictable maximum size.
|
||||||
public AudioIODeviceType::Listener
|
|
||||||
|
On some platforms (such as iOS 10), the expected buffer size reported in
|
||||||
|
audioDeviceAboutToStart may be smaller than the blocks passed to
|
||||||
|
audioDeviceIOCallbackWithContext. This can lead to out-of-bounds reads if the render
|
||||||
|
callback depends on additional buffers which were initialised using the
|
||||||
|
smaller size.
|
||||||
|
|
||||||
|
As a workaround, this class will ensure that the render callback will
|
||||||
|
only ever be called with a block with a length less than or equal to the
|
||||||
|
expected block size.
|
||||||
|
*/
|
||||||
|
class CallbackMaxSizeEnforcer : public AudioIODeviceCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {}
|
explicit CallbackMaxSizeEnforcer (AudioIODeviceCallback& callbackIn)
|
||||||
|
: inner (callbackIn) {}
|
||||||
|
|
||||||
|
void audioDeviceAboutToStart (AudioIODevice* device) override
|
||||||
|
{
|
||||||
|
maximumSize = device->getCurrentBufferSizeSamples();
|
||||||
|
storedInputChannels .resize ((size_t) device->getActiveInputChannels() .countNumberOfSetBits());
|
||||||
|
storedOutputChannels.resize ((size_t) device->getActiveOutputChannels().countNumberOfSetBits());
|
||||||
|
|
||||||
|
inner.audioDeviceAboutToStart (device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audioDeviceIOCallbackWithContext (const float* const* inputChannelData,
|
||||||
|
[[maybe_unused]] int numInputChannels,
|
||||||
|
float* const* outputChannelData,
|
||||||
|
[[maybe_unused]] int numOutputChannels,
|
||||||
|
int numSamples,
|
||||||
|
const AudioIODeviceCallbackContext& context) override
|
||||||
|
{
|
||||||
|
jassert ((int) storedInputChannels.size() == numInputChannels);
|
||||||
|
jassert ((int) storedOutputChannels.size() == numOutputChannels);
|
||||||
|
|
||||||
|
int position = 0;
|
||||||
|
|
||||||
|
while (position < numSamples)
|
||||||
|
{
|
||||||
|
const auto blockLength = jmin (maximumSize, numSamples - position);
|
||||||
|
|
||||||
|
initChannelPointers (inputChannelData, storedInputChannels, position);
|
||||||
|
initChannelPointers (outputChannelData, storedOutputChannels, position);
|
||||||
|
|
||||||
|
inner.audioDeviceIOCallbackWithContext (storedInputChannels.data(),
|
||||||
|
(int) storedInputChannels.size(),
|
||||||
|
storedOutputChannels.data(),
|
||||||
|
(int) storedOutputChannels.size(),
|
||||||
|
blockLength,
|
||||||
|
context);
|
||||||
|
|
||||||
|
position += blockLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void audioDeviceStopped() override
|
||||||
|
{
|
||||||
|
inner.audioDeviceStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct GetChannelWithOffset
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
template <typename Ptr>
|
||||||
|
auto operator() (Ptr ptr) const noexcept -> Ptr { return ptr + offset; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Ptr, typename Vector>
|
||||||
|
void initChannelPointers (Ptr&& source, Vector&& target, int offset)
|
||||||
|
{
|
||||||
|
std::transform (source, source + target.size(), target.begin(), GetChannelWithOffset { offset });
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioIODeviceCallback& inner;
|
||||||
|
int maximumSize = 0;
|
||||||
|
std::vector<const float*> storedInputChannels;
|
||||||
|
std::vector<float*> storedOutputChannels;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class AudioDeviceManager::CallbackHandler final : private MidiInputCallback,
|
||||||
|
private AudioIODeviceType::Listener,
|
||||||
|
private AudioIODeviceCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {}
|
||||||
|
|
||||||
|
MidiInputCallback* getMidiInputCallback() { return this; }
|
||||||
|
AudioIODeviceType::Listener* getAudioIODeviceTypeListener() { return this; }
|
||||||
|
AudioIODeviceCallback* getAudioIODeviceCallback() { return &enforcer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void audioDeviceIOCallbackWithContext (const float* const* ins,
|
void audioDeviceIOCallbackWithContext (const float* const* ins,
|
||||||
|
|
@ -117,6 +206,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioDeviceManager& owner;
|
AudioDeviceManager& owner;
|
||||||
|
CallbackMaxSizeEnforcer enforcer { *this };
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
|
||||||
};
|
};
|
||||||
|
|
@ -271,7 +361,7 @@ void AudioDeviceManager::addAudioDeviceType (std::unique_ptr<AudioIODeviceType>
|
||||||
availableDeviceTypes.add (newDeviceType.release());
|
availableDeviceTypes.add (newDeviceType.release());
|
||||||
lastDeviceTypeConfigs.add (new AudioDeviceSetup());
|
lastDeviceTypeConfigs.add (new AudioDeviceSetup());
|
||||||
|
|
||||||
availableDeviceTypes.getLast()->addListener (callbackHandler.get());
|
availableDeviceTypes.getLast()->addListener (callbackHandler->getAudioIODeviceTypeListener());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,7 +375,7 @@ void AudioDeviceManager::removeAudioDeviceType (AudioIODeviceType* deviceTypeToR
|
||||||
|
|
||||||
if (auto removed = std::unique_ptr<AudioIODeviceType> (availableDeviceTypes.removeAndReturn (index)))
|
if (auto removed = std::unique_ptr<AudioIODeviceType> (availableDeviceTypes.removeAndReturn (index)))
|
||||||
{
|
{
|
||||||
removed->removeListener (callbackHandler.get());
|
removed->removeListener (callbackHandler->getAudioIODeviceTypeListener());
|
||||||
lastDeviceTypeConfigs.remove (index, true);
|
lastDeviceTypeConfigs.remove (index, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -818,7 +908,7 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
|
||||||
{
|
{
|
||||||
currentDeviceType = currentAudioDevice->getTypeName();
|
currentDeviceType = currentAudioDevice->getTypeName();
|
||||||
|
|
||||||
currentAudioDevice->start (callbackHandler.get());
|
currentAudioDevice->start (callbackHandler->getAudioIODeviceCallback());
|
||||||
|
|
||||||
error = currentAudioDevice->getLastError();
|
error = currentAudioDevice->getLastError();
|
||||||
}
|
}
|
||||||
|
|
@ -1122,7 +1212,7 @@ void AudioDeviceManager::setMidiInputDeviceEnabled (const String& identifier, bo
|
||||||
{
|
{
|
||||||
if (enabled)
|
if (enabled)
|
||||||
{
|
{
|
||||||
if (auto midiIn = MidiInput::openDevice (identifier, callbackHandler.get()))
|
if (auto midiIn = MidiInput::openDevice (identifier, callbackHandler->getMidiInputCallback()))
|
||||||
{
|
{
|
||||||
enabledMidiInputs.push_back (std::move (midiIn));
|
enabledMidiInputs.push_back (std::move (midiIn));
|
||||||
enabledMidiInputs.back()->start();
|
enabledMidiInputs.back()->start();
|
||||||
|
|
|
||||||
|
|
@ -459,93 +459,6 @@ private:
|
||||||
processor = nullptr;
|
processor = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
/* This class can be used to ensure that audio callbacks use buffers with a
|
|
||||||
predictable maximum size.
|
|
||||||
|
|
||||||
On some platforms (such as iOS 10), the expected buffer size reported in
|
|
||||||
audioDeviceAboutToStart may be smaller than the blocks passed to
|
|
||||||
audioDeviceIOCallbackWithContext. This can lead to out-of-bounds reads if the render
|
|
||||||
callback depends on additional buffers which were initialised using the
|
|
||||||
smaller size.
|
|
||||||
|
|
||||||
As a workaround, this class will ensure that the render callback will
|
|
||||||
only ever be called with a block with a length less than or equal to the
|
|
||||||
expected block size.
|
|
||||||
*/
|
|
||||||
class CallbackMaxSizeEnforcer : public AudioIODeviceCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit CallbackMaxSizeEnforcer (AudioIODeviceCallback& callbackIn)
|
|
||||||
: inner (callbackIn) {}
|
|
||||||
|
|
||||||
void audioDeviceAboutToStart (AudioIODevice* device) override
|
|
||||||
{
|
|
||||||
maximumSize = device->getCurrentBufferSizeSamples();
|
|
||||||
storedInputChannels .resize ((size_t) device->getActiveInputChannels() .countNumberOfSetBits());
|
|
||||||
storedOutputChannels.resize ((size_t) device->getActiveOutputChannels().countNumberOfSetBits());
|
|
||||||
|
|
||||||
inner.audioDeviceAboutToStart (device);
|
|
||||||
}
|
|
||||||
|
|
||||||
void audioDeviceIOCallbackWithContext (const float* const* inputChannelData,
|
|
||||||
[[maybe_unused]] int numInputChannels,
|
|
||||||
float* const* outputChannelData,
|
|
||||||
[[maybe_unused]] int numOutputChannels,
|
|
||||||
int numSamples,
|
|
||||||
const AudioIODeviceCallbackContext& context) override
|
|
||||||
{
|
|
||||||
jassert ((int) storedInputChannels.size() == numInputChannels);
|
|
||||||
jassert ((int) storedOutputChannels.size() == numOutputChannels);
|
|
||||||
|
|
||||||
int position = 0;
|
|
||||||
|
|
||||||
while (position < numSamples)
|
|
||||||
{
|
|
||||||
const auto blockLength = jmin (maximumSize, numSamples - position);
|
|
||||||
|
|
||||||
initChannelPointers (inputChannelData, storedInputChannels, position);
|
|
||||||
initChannelPointers (outputChannelData, storedOutputChannels, position);
|
|
||||||
|
|
||||||
inner.audioDeviceIOCallbackWithContext (storedInputChannels.data(),
|
|
||||||
(int) storedInputChannels.size(),
|
|
||||||
storedOutputChannels.data(),
|
|
||||||
(int) storedOutputChannels.size(),
|
|
||||||
blockLength,
|
|
||||||
context);
|
|
||||||
|
|
||||||
position += blockLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void audioDeviceStopped() override
|
|
||||||
{
|
|
||||||
inner.audioDeviceStopped();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct GetChannelWithOffset
|
|
||||||
{
|
|
||||||
int offset;
|
|
||||||
|
|
||||||
template <typename Ptr>
|
|
||||||
auto operator() (Ptr ptr) const noexcept -> Ptr { return ptr + offset; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Ptr, typename Vector>
|
|
||||||
void initChannelPointers (Ptr&& source, Vector&& target, int offset)
|
|
||||||
{
|
|
||||||
std::transform (source, source + target.size(), target.begin(), GetChannelWithOffset { offset });
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioIODeviceCallback& inner;
|
|
||||||
int maximumSize = 0;
|
|
||||||
std::vector<const float*> storedInputChannels;
|
|
||||||
std::vector<float*> storedOutputChannels;
|
|
||||||
};
|
|
||||||
|
|
||||||
CallbackMaxSizeEnforcer maxSizeEnforcer { *this };
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
class SettingsComponent : public Component
|
class SettingsComponent : public Component
|
||||||
{
|
{
|
||||||
|
|
@ -682,7 +595,7 @@ private:
|
||||||
const String& preferredDefaultDeviceName,
|
const String& preferredDefaultDeviceName,
|
||||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
|
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
|
||||||
{
|
{
|
||||||
deviceManager.addAudioCallback (&maxSizeEnforcer);
|
deviceManager.addAudioCallback (this);
|
||||||
deviceManager.addMidiInputDeviceCallback ({}, &player);
|
deviceManager.addMidiInputDeviceCallback ({}, &player);
|
||||||
|
|
||||||
reloadAudioDeviceState (enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
|
reloadAudioDeviceState (enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
|
||||||
|
|
@ -693,7 +606,7 @@ private:
|
||||||
saveAudioDeviceState();
|
saveAudioDeviceState();
|
||||||
|
|
||||||
deviceManager.removeMidiInputDeviceCallback ({}, &player);
|
deviceManager.removeMidiInputDeviceCallback ({}, &player);
|
||||||
deviceManager.removeAudioCallback (&maxSizeEnforcer);
|
deviceManager.removeAudioCallback (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void timerCallback() override
|
void timerCallback() override
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue