If we don't specify framesPerBuffer parameter in Pa_OpenStream, PortAudio will choose optimal value for particular callback call. It can vary from run to run even on the same hardware depending on available system resources.
While generating signal, interpolating and applying effects, we assume that framesBuffer contains more than minimal number of frames to generate envelope. BTW, it is not true. If framesBuffer is smaller, algos cannot generate correct sound and just keep silence.
I've implemented cache which keeps generated values from dsp algorithms and applies it step-by-step to buffer values from pa_callback. Cache is filled each time algos generate dsp values. If buffer frames are not enough to generate envelope, algos generate values for further calls and keep it in cache.
Required number of frames has been selected as a number of frames for one phase multiplying by number of phases. Actually, smaller numbers of this value generates good results, but it is better to keep it as max as possible to provide perfect sound.
Code changes:
- Replaced C-like variables with std containers for comfortable debugging and better usage
- Extracted similar code calls to separate methods
- Implemented cache as std constainers, so also implemented convertion from std::vector to C-like float* to fill the pa buffer
- Changed the logic of applying effects and interpolation, it is now possible to use them separately. This is required to fill effects several times after calculating the interpolation is finished.
Removed std::vector<float> to keep cache - process buff values on the fly. Performance is better, but still glitches on https://musescore.com/user/166080/scores/175421. BuffSize = 64 with my wired headphones.
The old Preferences struct holding all preferences are removed in favor of a
new Preferences class which acts as a proxy for QSettings. The settings stored
in QSettings are accessed directly through access methods like getBool(key),
getInt(key), etc. and changed with setPreference(key, value).
Since we are using QSettings directly the preferences are stored automatically
without the need for a custom write() and read() method like before.
The preferences.cpp/.h and prefdialog.cpp/h are refactored to have fewer
responsibilities than before. The Preferences class are all about storing and
retrieving preferences - it should not contain any code to handle any other
aspect of MuseScore.
Testing:
The Preferences class can be used in tests. All preferences are initialized with
default values in mtest. If a test requires that a preference has a specific
value it can be changed using setPreference() for that single test. In the tests
the preferences are stored in memory only.
The Preference class is supposed to be used as a singleton. In preferences.h an
'extern Preferences preferences' is set and it is defined in preferences.cpp. All
files which includes preferences.h have access to the 'preferences' singleton
and should use this to get and set preferences.