Playhead Movement Fully Working Again
Playhead movement is now fully working with the new native C++ sequencer code, as can be seen in this video:
Implementing movement of the playhead is not as straightforward as it might seem. This is functionality is often taken for granted and it is hard to fathom on the surface how hard and complex this problem really is. I will attempt to explain:
Digital audio consists of a series of discrete, seperate values called samples, kind of like this:

When an audio application like Sputter runs, there is a seperate process (called a thread), which calls a specific function every time the audio circuitry on your device needs more audio to send out to the speakers. In our case this function looks like this:
static void callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount){
PdGenerator::generate((float*)pOutput, frameCount);
}
It is now up to us to fill the list of samples, in this case pOutput. We fill it with values using the Spitback Pure Data engine.
Similar to the audio thread, the UI thread is a seperate function which (ideally) is called by its own thread 60 times per second or more. On modern system these two threads run parallell and totally independant of each other, often on each their CPU core. So how to communicate between them, as is needed to make the playhead movement behave properly?
In the new sequencer engine, whenever the audio thread function has executed, it generates a “report” for the UI to act accordingly to. The “report” says how far into the song we have moved, the playhead position in the sequence and so on. The “report” is actually called a BufferEvent and looks something like this:

The buffer event (as the “report” is actually called) is put onto a queue which the UI threads looks for and picks up (ideally) every 1/60th of a second:

This looks easy in writing, but what happens when the user adds or removes a pattern or group? Or changes the length of a pattern? Or changes tempo? These changes has to be picked and acted upon which makes this problem a lot more complex than what it may seem on the surface.
Next step is to reimplement latency compensation, which will probably be done in a similar fashion as described above. After that there are no more substantial obstacles that I know of, so hopefully just smooth sailing from there.