Skip to content

calculate_sustain now respects default attack, decay, and release#3514

Open
mschubmehl wants to merge 2 commits into
sonic-pi-net:devfrom
mschubmehl:dev
Open

calculate_sustain now respects default attack, decay, and release#3514
mschubmehl wants to merge 2 commits into
sonic-pi-net:devfrom
mschubmehl:dev

Conversation

@mschubmehl
Copy link
Copy Markdown

@mschubmehl mschubmehl commented Feb 5, 2026

The following four notes should all be played with attack: 0, decay: 0, sustain: 0, release: 1.

play :c
sleep 1

play :c, attack: 0, decay: 0, sustain: 0, release: 1
sleep 1

play :c, attack: 0, decay: 0, duration: 1, release: 1
sleep 1

play :c, duration: 1
sleep 1

The first three correctly release over one beat, as expected, but the last one plays over two beats with attack: 0, decay: 0, sustain: 1, release: 1. This is because the sustain value calculated in calculate_sustain! only takes into account explicit arguments and thread-local defaults, ignoring the defaults for the underlying synth. After calculate_sustain! converts duration: 1 into sustain: 1 (incorrectly assuming attack: 0, decay: 0, release: 0), the default release: 1 is applied, resulting in a note that lasts two beats.

This is easily fixed by passing the defaults variable into calculate_sustain!, where it can be queried for attack, decay, and release values that are not specified in the explicit arguments args. With this change, all four notes above sound the same, as expected. Note that this will result in a user-visible change to the way a piece plays, but only if they do not specify release, either using an explicit argument or something like the thread-local use_synth_defaults.

I noticed this as I was trying to understand why play_pattern_timed without a release argument produces so much overlap, so it's worth noting that this change carries through to something like

play_pattern_timed [:c, :c, :g, :g, :a, :a, :g], [1,1,1,1, 1,1,2]

With this change, there is no longer an extra beat of overlap between the notes, so it now sounds identical to the following, as expected:

play_pattern_timed [:c, :c, :g, :g, :a, :a, :g], [1,1,1,1, 1,1,2], release: 1

Finally, the examples in the documentation for play_pattern_timed should also say that it's equivalent to setting duration for each note played, not sustain directly. This was true before and after my change.

Thanks to all of you for your amazing work on this project!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant