While working on a track, in the more calm sections I had the impression that something is slightliy off in terms of timing/synchronization.
After some investigation, I found out that this is probably caused by a small delay when controlling the note played by the dsaw synth I use there (using control syn, note: ...).
Using Audacity, I could visualize it with the spectrogram view (screenshot below). At the top is the dsaw, with the cursor at the beat position. The change of the synth note is to the right of it, so somewhat delayed (0.03 - 0.04 sec). In the middle is saw, which shows a similar delay, but to a lesser extent. At the bottom is tb303, which does not show the delay, or at least it is too short to be visible.
Here is a minimal code example that reproduces the effect. It was also used for recording for the above image.
live_loop :tick, delay: 0.01 do
8.times do
use_sample_defaults finish: 0.05
sample :elec_twip
sleep 0.5
end
end
live_loop :bass, sync: :tick do
with_synth :dsaw do
use_synth_defaults detune: 0, sustain: 8, release: 0, amp: 0.4
syn = play :e2
4.times do
control syn, note: (scale :f2, :minor_pentatonic).choose
sleep 2
end
end
end
Does anybody know whether this is expected behaviour for these synths, or if it might be a bug? I am quite sure it is not a performance problem (scsynth is below 1% CPU load).
this is expected behaviour. Both the initial call to play and the subsequent call to control are converted to separate messages which are sent to the synthesis engine. At this stage, it’s possible for the messages to be received out of order. In order to guarantee that the order is preserved, in the case where both the initial trigger and and a following control are set to the same time stamp, (i.e. no call to sleep happens between them) then the control is delayed just slightly to ensure it arrives afterwards.
To avoid this, always ensure that there’s a sleep between the play and control.
In the future it may be possible to detect that this is happening prior to sending the messages out and then to automatically merge any similarly timed control into the play of a specific synth node as if you were just adding additional options to the play. However, this would require a considerable amount of “smarts” to be added to that layer of the system and isn’t particularly high priority at this stage.
Following on from @samaaron 's answer you could try this:
live_loop :tick, delay: 0.01 do
8.times do
use_sample_defaults finish: 0.05
sample :elec_twip
sleep 0.5
end
end
live_loop :bass, sync: :tick do
with_synth :dsaw do
use_synth_defaults detune: 0, sustain: 8, release: 0, amp: 0.4
syn = play (scale :f2, :minor_pentatonic).choose
sleep 2
3.times do
control syn, note: (scale :f2, :minor_pentatonic).choose
sleep 2
end
end
end
In my “real” project, it is the same. Currently, I shorten the first note by 0.06 beats / 0.03 sec to compensate, but it is not so cool to have to measure the delay for every synth to be used with note control.
I had the idea that the issue might be caused by the relatively low frequencies used in the tests, like maybe a new note only starts when an already running wave/period finishes. But using high frequencies like :f5 does not change anything either.
EDIT: Further, I tried to control different opts, still with sleeping between play and control. It appears that cutoff (top) changes with a delay, while amp (bottom) does not (or at least less delay than the length of the tick sound, here 0.03):
In that case, it’s quite likely to be related to the audio buffer size. Supercollider can’t issue a synth control change in the middle of computing an audio buffer. It can only do it at the buffer boundaries. Therefore the larger the buffer size, the larger the potential error for each control message. Note that the error is always there and bounded by the buffer size. Happy to explain this more if it doesn’t make sense.
You can play with the audio buffer size if you edit the new audio settings toml file in the config dir. this is located within the .sonic-pi dir within your home dir.
Let me know if changing the buffer size changes the results you see
Thanks for this further suggestion! So you refer to sound_card_buffer_size here?
When I go below 512, I get no sound at all. With 512, the delay seems unchanged, but I get artifacts in the sound. With 1024 and above, sound is ok, but the delay if still there. Not being able to go lower is probably a limitation of my sound card…
Also, I am wondering why the tb303 does not show any visible delay, as it should also be affected by the buffer boundary “error”.
In the project where the issue first appeared for me, I could circumvent its audible effect by shortenting the first note played by the respective synth (published to in_thread here). But still, I would be interested in ways to preventing the issue from ground.
Just a proposal. Eliminating the choose operation just before the control excludes a potential influence on the timing:
live_loop :tick, delay: 0.01 do
8.times do
use_sample_defaults finish: 0.05
sample :elec_twip
sleep 0.5
end
end
live_loop :bass, sync: :tick do
with_synth :dsaw do
use_synth_defaults detune: 0, sustain: 8, release: 0, amp: 0.4
syn = play (scale :f2, :minor_pentatonic).choose
notes = (scale :f2, :minor_pentatonic).pick(3)
sleep 2
notes.each do |n|
control syn, note: n
sleep 2
end
end
end
I haven’t done measurement, but when listening to it I somehow got the impression it is more on the point now. But it is hard to hear a difference at all.
However, the code shown here is only a minimal reproducing example. It uses choose only incidentially. The original project plays notes from arrays that are generated before any live loop starts.
For details on that project, see this forum thread (which you already commented on ). Code and documentation are linked there. The recorded track there is already corrected through a shortened first note.
EDIT: it is the bass live loop that is affected there. bass_delay is the correction.
I haven’t done measurement, but when listening to it I somehow got the impression it is more on the point now. But it is hard to hear a difference at all.
In the example presented here, I also don’t hear the delay, but visualized it (see screenshots). In the original project, however, it was audible in the uncorrected version.
Thanks for this test! For me, the delay in the note change is consistent in every call to control. As already reported here, the delay seems to affect only some of the opts, which does not include amp. However, I used the spectroscope visualization (in Audacity) as I found it better suited to see the relatively short delay.
Is the control command executed within a new thread?
Yes, playing the melody in the original is “released” to a separate thread using at do, in order to control other opts of the synth and an FX while playing.
Ok, if its consistent in every call on your machine and only in the note opt, then my observation apparently is a different issue and can be caused by the separate thread. The delay in the above picture is 1/32 = 0.03125 s.