Linux and precise time synchronization - the best way


#1

Hello,

I develop the multi-platform emulator of the 8-bit computer Sharp MZ-800 and
I uses SDL2 libs - here you can see the finished program

In emulation is a very important to keep the exact time
rendering of individual frames, which is 50 Hz.

Emulated machine is running on PC so, it jumping from one screen picture to
the next. First are performed all internal operations that fit to the time window of
one screen picture on the emulated machine. The emulator stops and wait until
has passed 20 ms since from the last screen picture.
In the fact for video is not verry important whether timing is really
accurate to 20 ms - the eye does not know this. Unfortunately for audio it is
important to be accurate because otherwise it’s heard noise of crunching.

This task is very simple in windows environment, but for example in Linux I
do not manage to keep the 20 ms spans betwen screen pictures.
I have opened the audio device with this parameters freq = 48000, samples =
48000/50 (it is buffer for 20 ms).

-------- code --------

volatile uint32_t g_last_audiocb_ticks = 0;
volatile uint32_t g_elapsed_screens = 0;
volatile uint32_t g_elapsed_audiocb = 0;


void MyAudioCallback ( void *userdata, Uint8 *stream, int len ) {

        uint32_t ticks = SDL_GetTicks ( );
        printf ( "screen: %d, ticks: %d\n", g_elapsed_screens, ticks - g_last_audiocb_ticks );
        g_last_audiocb_ticks = ticks;
        g_elapsed_audiocb++;

        memcpy ( stream, buffer, len );
}

void main_screen_loop ( void ) {

        uint32_t last_elapsed_audiocb = g_elapsed_audiocb;

        while ( 1 ) {
                g_elapsed_screens++;

                while ( ( SDL_GetTicks ( ) - g_last_audiocb_ticks ) < 20 ) {
                };
        };
}

----------- end of code --------

Output in Windows:

screen: 1, ticks: 10
screen: 2, ticks: 20
screen: 3, ticks: 20
screen: 4, ticks: 20
screen: 5, ticks: 20
screen: 6, ticks: 20
screen: 7, ticks: 20
screen: 8, ticks: 20
screen: 9, ticks: 20
screen: 10, ticks: 20


screen: 10000, ticks: 20

Output in Linux:

screen: 1, ticks: 7
screen: 2, ticks: 0
screen: 3, ticks: 0
screen: 4, ticks: 0
screen: 5, ticks: 3
screen: 6, ticks: 21
screen: 7, ticks: 4
screen: 8, ticks: 11
screen: 9, ticks: 0
screen: 10, ticks: 13
screen: 11, ticks: 12
screen: 12, ticks: 13
screen: 13, ticks: 12
screen: 14, ticks: 10
screen: 15, ticks: 13
screen: 16, ticks: 0
screen: 17, ticks: 13
screen: 18, ticks: 11
screen: 19, ticks: 13
screen: 20, ticks: 11

The audio device in Linux apparently uses some larger FIFO buffer which is
irregular loaded.
A simple solution seemed to add the following line to the start of
MyAudioCallback() function:

while ( ( SDL_GetTicks ( ) - g_last_audiocb_ticks ) < 19 ) {}

Timing is definitely not accurate the 20 ms, but the picture is synchronized
with the sound and in majority is not heard noise of crunching.

My Question: Is there in Linux a cleaner way (or by the system way) to
ensure precision timing like in Windows with 20 ms audio buffer?

Thank you in advance for your reply.

Michal


#2

The audio callback runs in another thread and volatile variables are not suitable for communication between threads. You need to use atomic variable or mutex.