Patch by Rick Yorgason
I modified it to use linear instead of n squared ring buffer size after verifying that this yields better performance on my mac and pc.
This is just a workaround for what appears to be a portaudio bug, where Pa_OpenStream() resets the device level that port mixer should control.
Looked at portaudio src (pa_win_wmme.c's OpenStream()) but I couldn't find the origin of the problem.
If the Meter Toolbar was monitoring input, PrefsDialog can be opened, but then PrefsDialog::OnOK() needs to StopStream() or AudioIO::HandleDeviceChange() will no-op. We could instead disable the Preferences command while monitoring, i.e., set AudioIONotBusyFlag/AudioIOBusyFlag according to monitoring, as well as gAudioIO->IsAudioTokenActive(). Instead allow it because unlike recording, for example, monitoring is not clearly something that should prohibit opening prefs.
This may have been some of the occasions of bug 29, where user changed device while monitoring, but the AudioIO::HandleDeviceChange() code did not actually change the device, so it look like the user had specificied the motherboard/sound card default device, but Audacity was still using the USB device.
// TO-DO: We *could* be smarter in this method and call HandleDeviceChange()
// only when the device choices actually changed. True of lots of prefs!
// As is, we always stop monitoring and handle the device change.
In AudacityProject::GetUpdateFlags(), don't need to check (GetAudioIOToken() == 0) alternative because gAudioIO->IsAudioTokenActive() checks that it's greater than zero.
In AudioIO.cpp, cleaned up some logic and encapsulation of boolean methods.
Was able to make the bug occur after lots of fast clicking around, by double-clicking Preview in Amplify dialog.
The key seems to be the call to wxYield. That function is obsolete, but looking at the documentation for its successor, wxApp::Yield():
"Caution should be exercised, however, since yielding may allow the user to perform actions which are not compatible with the current task. Disabling menu items or whole menus during processing can avoid unwanted reentrance of code: see ::wxSafeYield for a better function."
That sounds like exactly the situations where this has occurred. And the documentation for ::wxSafeYieldsays:
"This function is similar to wxYield, except that it disables the user input to all program windows before calling wxYield and re-enables it again afterwards."
Sounds like what we need. So I went through the code and for all the wxYield and wxGetApp()::Yield occurrences where Audacity is doing some drawing or already in the process of responding to a GUI event, I replaced them with ::wxSafeYield. Replaced all the remaining wxYield calls with calls to wxGetApp()::Yield().
Haven't been able to replicate the bug since these changes. Please test.