Optimisation #1: Compute music data less often

OpenAL and fluidsynth internals are nowhere near my core strengths, therefore the following was found via some trial and error method.

When playing music, the program was doing the following:

On keyboard event (i.e. when pressing or releasing piano keys):
  For each key pressed:
    - tell fluidsynth that key just got pressed
    - ask fluidsynth to compute the new music data in a buffer
    - send that buffer to openAL

  For each key released:
    - tell fluidsynth that key just got un-pressed
    - ask fluidsynth to compute the new music data in a buffer
    - send that buffer to openAL

Wait for the next event.

One attempt here was to restructure this code to only ask fluidsynth to compute the music data once per music event, in other words, changing the code to the following:

On keyboard event (i.e. when pressing or releasing piano keys):
  For each key pressed:
    - tell fluidsynth that key just got pressed

  For each key released:
    - tell fluidsynth that key just got un-pressed

  ask fluidsynth to compute the new music data in a buffer
  send that buffer to openAL

Wait for the next event.

An important thing to do when trying these possible optimisations is to always ensure that the software still works. A lot of my attempts resulted in having no music getting played at all, or sometimes being played but feeling jerky. I was concerned that this optimisation could lead to only hearing the music from the last key instead of all of them. Before looking into performance improvements, correctness need to be maintained.

On this attempt, if no measurable timing difference would appear, we would know that computing music is linear to the number of key changes. If instead things would become about n times faster when there are n keys pressed and released, it would mean filling the buffer takes the same amount of time regardless of the number of key changes, and therefore should only be done as little times as strictly necessary.

Measuring this change showed that we were on the side of n times faster. This change alone was enough to make music playable at the right speed. With this change, a music event takes about 12ms on firefox, and 20 on chrome. Of course the exact computation times change based on several factors, e.g. number of key changes but also CPU thermal throtting or thread scheduling, of which I have little to no control over. Those numbers here are only a ballpark figure, which leads to the next issue: music correctness.

After this optimisation, the timing for computation was as follow on firefox and chrome.

Graph showing histogram to time computation

On this graph, each column represent a duration window of half a millisecond. We can see that firefox manages to compute the data most of time in less than 15 milliseconds. The most common duration being between 11.5 and 12 ms.

For chrome, the computation usually takes between 20 and 20.5 milliseconds.

Durations in ms min average stddev max
firefox 9.0 12.7 1.3 24.0
chrome 12.1 20.5 1.0 35.8

This graph was generated by playing Etude as dur -op25 - 01 on a 2011 laptop using firefox 102 and chromium 103 on linux with a kernel version 5.18.