Unfortunately updating didn’t help.
Here’s the Asan trace in case it’s helpful but doesn’t really provide more info that I’ve already added:
==83809==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61a00006c590 at pc 0x0001082063ec bp 0x00017767e170 sp 0x00017767e168
WRITE of size 4 at 0x61a00006c590 thread T17
#0 0x1082063e8 in juce::CoreAudioClasses::CoreAudioInternal::audioCallback(AudioTimeStamp const*, AudioTimeStamp const*, AudioBufferList const*, AudioBufferList*)+0x1024 (Waveform 13:arm64+0x1037123e8)
#1 0x108204c8c in juce::CoreAudioClasses::CoreAudioInternal::audioIOProc(unsigned int, AudioTimeStamp const*, AudioBufferList const*, AudioTimeStamp const*, AudioBufferList*, AudioTimeStamp const*, void*)+0x84 (Waveform 13:arm64+0x103710c8c)
#2 0x1866ce1b4 in HALC_ProxyIOContext::IOWorkLoop()+0x2b38 (CoreAudio:arm64e+0x1ed1b4)
#3 0x1866caefc in invocation function for block in HALC_ProxyIOContext::HALC_ProxyIOContext(unsigned int, unsigned int)+0xac (CoreAudio:arm64e+0x1e9efc)
#4 0x186877104 in HALC_IOThread::Entry(void*)+0x54 (CoreAudio:arm64e+0x396104)
#5 0x164115858 in asan_thread_start(void*)+0x40 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x51858)
#6 0x183bbf2e0 in _pthread_start+0x84 (libsystem_pthread.dylib:arm64e+0x72e0)
#7 0x183bba0f8 in thread_start+0x4 (libsystem_pthread.dylib:arm64e+0x20f8)
0x61a00006c590 is located 0 bytes after 1296-byte region [0x61a00006c080,0x61a00006c590)
allocated by thread T0 here:
#0 0x164118fd0 in calloc+0x9c (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x54fd0)
#1 0x1081f99a8 in juce::HeapBlock<float, false>::callocWrapper(unsigned long, unsigned long)::'lambda'()::operator()() const+0xc8 (Waveform 13:arm64+0x1037059a8)
#2 0x1081f98b4 in float* juce::HeapBlock<float, false>::wrapper<juce::HeapBlock<float, false>::callocWrapper(unsigned long, unsigned long)::'lambda'()>(unsigned long, juce::HeapBlock<float, false>::callocWrapper(unsigned long, unsigned long)::'lambda'()&&)+0x7c (Waveform 13:arm64+0x1037058b4)
#3 0x1081f978c in juce::HeapBlock<float, false>::callocWrapper(unsigned long, unsigned long)+0x1b8 (Waveform 13:arm64+0x10370578c)
#4 0x1081f8d68 in void juce::HeapBlock<float, false>::calloc<int>(int, unsigned long)+0xb4 (Waveform 13:arm64+0x103704d68)
#5 0x1081dc39c in juce::CoreAudioClasses::CoreAudioInternal::allocateTempBuffers()+0x338 (Waveform 13:arm64+0x1036e839c)
#6 0x1081d7a74 in juce::CoreAudioClasses::CoreAudioInternal::updateDetailsFromDevice(juce::BigInteger const&, juce::BigInteger const&)+0xb60 (Waveform 13:arm64+0x1036e3a74)
#7 0x1082023fc in juce::CoreAudioClasses::CoreAudioInternal::reopen(juce::BigInteger const&, juce::BigInteger const&, double, int)+0x880 (Waveform 13:arm64+0x10370e3fc)
#8 0x1081b7ccc in juce::CoreAudioClasses::CoreAudioIODevice::open(juce::BigInteger const&, juce::BigInteger const&, double, int)+0x688 (Waveform 13:arm64+0x1036c3ccc)
#9 0x108251d64 in juce::CoreAudioClasses::AudioIODeviceCombiner::DeviceWrapper::open(juce::BigInteger const&, juce::BigInteger const&, double, int) const+0x1b0 (Waveform 13:arm64+0x10375dd64)
#10 0x10820dde0 in juce::CoreAudioClasses::AudioIODeviceCombiner::open(juce::BigInteger const&, juce::BigInteger const&, double, int)+0xb38 (Waveform 13:arm64+0x103719de0)
#11 0x1080f57ac in juce::AudioDeviceManager::setAudioDeviceSetup(juce::AudioDeviceManager::AudioDeviceSetup const&, bool)+0x1f28 (Waveform 13:arm64+0x1036017ac)
#12 0x1080ed118 in juce::AudioDeviceManager::initialiseDefault(juce::String const&, juce::AudioDeviceManager::AudioDeviceSetup const*)+0x6d8 (Waveform 13:arm64+0x1035f9118)
#13 0x1080f10e8 in juce::AudioDeviceManager::initialise(int, int, juce::XmlElement const*, bool, juce::String const&, juce::AudioDeviceManager::AudioDeviceSetup const*)+0x50c (Waveform 13:arm64+0x1035fd0e8)
#14 0x1080ec2c8 in juce::AudioDeviceManager::initialiseFromXML(juce::XmlElement const&, bool, juce::String const&, juce::AudioDeviceManager::AudioDeviceSetup const*)+0x1928 (Waveform 13:arm64+0x1035f82c8)
#15 0x1080f1084 in juce::AudioDeviceManager::initialise(int, int, juce::XmlElement const*, bool, juce::String const&, juce::AudioDeviceManager::AudioDeviceSetup const*)+0x4a8 (Waveform 13:arm64+0x1035fd084)
#16 0x111aff158 in tracktion::engine::DeviceManager::loadSettings()+0xa48 (Waveform 13:arm64+0x10d00b158)
#17 0x111afe234 in tracktion::engine::DeviceManager::initialise(int, int)+0x27c (Waveform 13:arm64+0x10d00a234)
#18 0x1145b7100 in tracktion::engine::Engine::initialise()+0x1164 (Waveform 13:arm64+0x10fac3100)
#19 0x1145b4628 in tracktion::engine::Engine::Engine(std::__1::unique_ptr<tracktion::engine::PropertyStorage, std::__1::default_delete<tracktion::engine::PropertyStorage>>, std::__1::unique_ptr<tracktion::engine::UIBehaviour, std::__1::default_delete<tracktion::engine::UIBehaviour>>, std::__1::unique_ptr<tracktion::engine::EngineBehaviour, std::__1::default_delete<tracktion::engine::EngineBehaviour>>)+0x878 (Waveform 13:arm64+0x10fac0628)
#20 0x1145b7fb8 in tracktion::engine::Engine::Engine(std::__1::unique_ptr<tracktion::engine::PropertyStorage, std::__1::default_delete<tracktion::engine::PropertyStorage>>, std::__1::unique_ptr<tracktion::engine::UIBehaviour, std::__1::default_delete<tracktion::engine::UIBehaviour>>, std::__1::unique_ptr<tracktion::engine::EngineBehaviour, std::__1::default_delete<tracktion::engine::EngineBehaviour>>)+0x88 (Waveform 13:arm64+0x10fac3fb8)
#21 0x115d03e34 in std::__1::__unique_if<tracktion::engine::Engine>::__unique_single std::__1::make_unique[abi:de180100]<tracktion::engine::Engine, std::__1::unique_ptr<tracktion::engine::PropertyStorage, std::__1::default_delete<tracktion::engine::PropertyStorage>>, std::__1::unique_ptr<WaveformUIBehaviour, std::__1::default_delete<WaveformUIBehaviour>>, std::__1::unique_ptr<WaveformEngineBehaviour, std::__1::default_delete<WaveformEngineBehaviour>>>(std::__1::unique_ptr<tracktion::engine::PropertyStorage, std::__1::default_delete<tracktion::engine::PropertyStorage>>&&, std::__1::unique_ptr<WaveformUIBehaviour, std::__1::default_delete<WaveformUIBehaviour>>&&, std::__1::unique_ptr<WaveformEngineBehaviour, std::__1::default_delete<WaveformEngineBehaviour>>&&)+0x2ec (Waveform 13:arm64+0x11120fe34)
#22 0x115d03988 in createEngine(std::__1::unique_ptr<tracktion::engine::PropertyStorage, std::__1::default_delete<tracktion::engine::PropertyStorage>>)+0x1d8 (Waveform 13:arm64+0x11120f988)
#23 0x11601478c in TracktionApp::initialise(juce::String const&)+0x1ab8 (Waveform 13:arm64+0x11152078c)
#24 0x10a3b9a30 in juce::JUCEApplicationBase::initialiseApp()+0x374 (Waveform 13:arm64+0x1058c5a30)
#25 0x10bd4ff0c in juce::JUCEApplication::initialiseApp()+0x88 (Waveform 13:arm64+0x10725bf0c)
#26 0x10a3b8890 in juce::JUCEApplicationBase::main()+0x338 (Waveform 13:arm64+0x1058c4890)
#27 0x10a3b84fc in juce::JUCEApplicationBase::main(int, char const**)+0xfc (Waveform 13:arm64+0x1058c44fc)
#28 0x115db16a8 in main+0x70 (Waveform 13:arm64+0x1112bd6a8)
#29 0x18383c270 (<unknown module>)
I think the problem is in reopen:
if (! audioObjectSetProperty (deviceID, { kAudioDevicePropertyBufferFrameSize,
kAudioObjectPropertyScopeGlobal,
juceAudioObjectPropertyElementMain },
static_cast<UInt32> (bufferSizeSamples), err2log()))
{
updateDetailsFromDevice (ins, outs);
return "Couldn't change buffer size";
}
// Annoyingly, after changing the rate and buffer size, some devices fail to
// correctly report their new settings until some random time in the future, so
// after calling updateDetailsFromDevice, we need to manually bodge these values
// to make sure we're using the correct numbers..
updateDetailsFromDevice (ins, outs);
sampleRate = newSampleRate;
bufferSize = bufferSizeSamples;
The call to audioObjectSetProperty (to set 512) seems to succeed (no error logged) but during the latter updateDetailsFromDevice, the device is still reporting 320, so the buffer gets allocated only space for 320.
The line after that bufferSize = bufferSizeSamples; assumes the buffer was correctly changed and stores 512 in the bufferSize member even though space was only allocated for 320.
It feels like there needs to be this check after reopen → updateDetailsFromDevice is called but there’s a comment to state devices don’t report their sizes correctly.
If I add this:
if (sampleRate = newSampleRate)
return "Couldn't change sample rate";
if (bufferSize != bufferSizeSamples)
return "Couldn't change buffer size";
The device fails to open as I would expect if the buffer size wasn’t changed.
But perhaps a change more in line with the comment on mis-behaving devices would be to call allocateBuffers if the buffer sizes don’t match? At least they’d be allocated large enough then?
if (bufferSize != bufferSizeSamples)
{
bufferSize = bufferSizeSamples;
allocateTempBuffers();
}
When I do that, I don’t get the crash but I don’t get any audio output either.
Interestingly, if I try and change the buffer size to 512 from the juce::AudioDeviceSelectorComponent, it tries to but then eventually goes back to 320 where I do get audio again. So it does seem like the device just can’t run at 512 but reports that as a valid block size.
I think I’d prefer silence and not-crashing as the desired behaviour but maybe at least rather than using a raw juce::HeapBlock, some kind of sized view could be created and the audio callback could just bail out if it’s about to access OOB memory?