Resplendence wrote:
For instance the following code from the sineSynth sample accesses the voice List:
for j:=0 to sampleframes-1 do
for i:=0 to Voices.Count-1
do outputs[0,j]:=outputs[0,j]+Voices.Process;
What is not taken into consideration here is thread safety, voice are concurrently added and deleted as a response to midi events. This creates a race condition. It is possible that a voice gets deleted just while in the process path. This means your are calling voices.Process for a just deleted voice object.
In that case all voices should be pre-alocated. And just have a 'active' flag.
Code: Select all
for j:=0 to sampleframes-1 do
for i:=0 to Voices.Count-1 do
if (voices[i].active) then
outputs[0,j]:=outputs[0,j]+Voices[i].Process;
You can do this in a way which wont block the audio thread. By having two sets of data, the current data in use, and new data in waiting. Basicly the audio thread trys to get a lock, but if it cant it carries on with its current data. If it does get a lock then it checks for the updated data. That way the audio thread can never be blocked by another thread. The other thread waits for a lock, and once locked it sets the updated data and a flag saying it is waiting.The solution to this is to use a synchronization object such as a mutex and wrap it around every access (both read and write) to the voiceList.
I think is a good solution to the problem. This lock will be needed every time you read or write to the voice list. There may be other issues which need consideration for thread safety that I will reflect on later.
Code: Select all
audio thread:
if (csection.TryLock) then
begin
if (updateWaiting) then currentData = UpdatedData;
updateWaiting:= FALSE;
csection.Exit();
end;
gui thread:
csection.Enter();
UpdatedData = ....;
updateWaiting := TRUE;
csection.Exit();
