mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +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,
|
||||
public MidiInputCallback,
|
||||
public AudioIODeviceType::Listener
|
||||
/* This class is 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:
|
||||
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:
|
||||
void audioDeviceIOCallbackWithContext (const float* const* ins,
|
||||
|
|
@ -117,6 +206,7 @@ private:
|
|||
}
|
||||
|
||||
AudioDeviceManager& owner;
|
||||
CallbackMaxSizeEnforcer enforcer { *this };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
|
||||
};
|
||||
|
|
@ -271,7 +361,7 @@ void AudioDeviceManager::addAudioDeviceType (std::unique_ptr<AudioIODeviceType>
|
|||
availableDeviceTypes.add (newDeviceType.release());
|
||||
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)))
|
||||
{
|
||||
removed->removeListener (callbackHandler.get());
|
||||
removed->removeListener (callbackHandler->getAudioIODeviceTypeListener());
|
||||
lastDeviceTypeConfigs.remove (index, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -818,7 +908,7 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
|
|||
{
|
||||
currentDeviceType = currentAudioDevice->getTypeName();
|
||||
|
||||
currentAudioDevice->start (callbackHandler.get());
|
||||
currentAudioDevice->start (callbackHandler->getAudioIODeviceCallback());
|
||||
|
||||
error = currentAudioDevice->getLastError();
|
||||
}
|
||||
|
|
@ -1122,7 +1212,7 @@ void AudioDeviceManager::setMidiInputDeviceEnabled (const String& identifier, bo
|
|||
{
|
||||
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.back()->start();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue