higherDefender
higherDefender

Reputation: 1561

Periodic Callback in c

I am a newbie writing code for sound playback via "alsa api".

I have to repeatedly call a function send_output() 10 times in a sec as:

while(true)
{
     send_output();
     usleep(100000);
}

but this is a bad way of programming since within the usleep() function processor is still busy executing the code.

I want to know the way (for I know it exists) to put the function send_output() into a periodic callback function so that in between the calls, the processor can be kept free for other heavy tasks. Can you help me?

P.S. I am programming on 'Beagleboard' which has a very low clock rate and need to do other tasks too.

Thanks.

Upvotes: 1

Views: 3517

Answers (4)

Simon Richter
Simon Richter

Reputation: 29586

A typical mainloop in an application that needs to coordinate multiple things is centered around the select function, which waits for events on a set of file descriptors or a timeout.

Your ALSA loop then becomes

// This will keep the time when ALSA expects the next packet
struct timeval alsa_timeout;

// Initialize timeout with current time
gettimeofday(&alsa_timeout, 0);

for(;;) {
    // Get current time
    struct timeval now;
    gettimeofday(&now, 0);

    // Whether to re-check the current time before going to sleep
    bool restart = false;

    if(now.tv_sec > alsa_timeout.tv_sec ||
            (now.tv_sec == alsa_timeout.tv_sec && now.tv_usec >= alsa_timeout.tv_usec)) {
        send_output();
        alsa_timeout.tv_usec += 100000;
        if(alsa_timeout.tv_usec > 1000000) {
            alsa_timeout.tv_sec += alsa_timeout.tv_usec / 1000000;
            alsa_timeout.tv_usec %= 1000000;
        }
        restart = true;
    }

    // If we performed some action, this would have taken time, so we need
    // to re-fetch the current time
    if(restart)
        continue;

    // Determine when the next action is due
    struct timeval next_timeout;

    // We only have one action at the moment, which is ALSA. Otherwise, this is the place
    // where you look for the earliest time you need to wake up.
    next_timeout.tv_sec = alsa_timeout.tv_sec;
    next_timeout.tv_usec = alsa_timeout.tv_usec;

    struct timeval relative_time;
    if(next_timeout.tv_usec >= now.tv_usec) {
        relative_time.tv_usec = next_timeout.tv_usec - now.tv_usec;
        relative_time.tv_sec = next_timeout.tv_sec - now.tv_sec;
    } else {
        relative_time.tv_usec = 1000000 - now.tv_usec + next_timeout.tv_usec;
        relative_time.tv_sec = next_timeout.tv_sec + 1 - now.tv_usec;
    }

    // Other parameters become important when you do other work
    int rc = select(0, 0, 0, 0, &relative_time);

    if(rc == 0)
        continue;
    if(rc == -1)
        break;

    // Handle file descriptor based work here
}

Note that I use the absolute time in my calculation and increment that for every packet of samples I send -- this keeps the data rate constant regardless of how long the ALSA subsystem takes to transfer the data to the soundcard, and when I miss a timeout (because some other function took more than 100ms to complete) the output function will be called twice to re-fill the ALSA buffers.

You may need to add some extra code:

  • if a buffer underrun occured, restart by resetting the alsa_timeout value to the current time so we don't lose synchronisation
  • if the stream runs for a long time, you may need to adjust the timeout slightly as the sound chips DAC clock is not synchronized to the system clock behind gettimeofday()
  • if the system clock is adjusted, the loop needs to be resynchronized (you can ignore daylight saving time, gettimeofday() returns UTC, but if you run an NTP client or allow setting the date and time somewhere, that needs to be handled).

Upvotes: 0

AlexTheo
AlexTheo

Reputation: 4164

It is simple you should avoid using synchronization like that. Use event system implementation instead. Define an interface and register it to your class as listener. Each time your class perform some action you will be informed with event. You can take a look on Java listeners implementation also read about ( Bridge, Strategy, Observer ) design patterns. Interface implementation in c++ will looks like:

class IActionListener
{
public:
 virtual void actionPerformed( ... some arguments ... )=0;
};

implement this interaface like:

class ConcreteActionListener: public IActionListener
{
public:
 void actionPerformed( ... some arguments ... )
 {
   // do your staff here.
 }
};

Check this: http://en.wikipedia.org/wiki/Observer_pattern

Upvotes: 0

Timothy
Timothy

Reputation: 4487

You have to do that in the frame of multi-threads.

According to your tag, let's say you are programming on Linux, so:

void thread_funtion()
{
    while(true)
    {
         send_output();
         usleep(100000);
    }
}

invoke this function in main thread by:

pthread_t pid;
pthread_create(&pid, NULL, thread_function, NULL);

Please note that:

  1. #include <pthread.h>
  2. If you meet error with gcc command, then try gcc -pthread command.

Upvotes: 1

Sangeeth Saravanaraj
Sangeeth Saravanaraj

Reputation: 16597

man usleep

   usleep - suspend execution for microsecond intervals

Upvotes: 1

Related Questions