Re: [linux-audio-dev] best method for timing

New Message Reply About this list Date view Thread view Subject view Author view Other groups

Subject: Re: [linux-audio-dev] best method for timing
From: Paul Davis (pbd_AT_Op.Net)
Date: Tue Apr 16 2002 - 16:34:21 EEST


>I need to do some timing code - for some basic kind of sequencing..
>Since ive not written this kind of stuff before, i'm unsure as to the best app
>roach for accurate timing.
>
>I've played around with using signals for this - with setitimer(..) and then c
>atching the signal, but it doesnt look too good - it can be 1000s of usecs out
>..
>
>What approaches have people used, and how successful were they?

poll(2) on /dev/rtc. You will need to be root, or have CAP_RESOURCE,
to set the frequency of the clock to a useful value, and you will need
to run SCHED_FIFO to not have the kernel scheduler mess things up.

ardour and softwerk (which doesn't compile right now) both have code
for the RTC. In fact, I'll include ardour's below - its very simple
really. Any parts that are not clear are almost certainly not
necessary :)

alternatively, use the ALSA sequencer and/or timer APIs.

--p

/*
    Copyright (C) 2001 Paul Davis

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Id: rtc.h,v 1.1 2001/11/24 00:15:23 pbd Exp $
*/

#ifndef __ardour_rtc_h__
#define __ardour_rtc_h__

#include <pbd/thread.h>
#include <sigc++/signal_system.h>

class RealTimeClock : public QMThread, public SigC::Object

{
  public:
        RealTimeClock ();
        virtual ~RealTimeClock();

        bool set_interval (unsigned int usecs);

        SigC::Signal0<void> tick;

  private:
        int rtc_fd;
        int current_hz;
        bool rtc_running;
        bool start_periodic_interrupts ();
        bool stop_periodic_interrupts ();

        static void *start_thread (void *);
        void *do_work ();
};

#endif /* __ardour_rtc_h__ */

/*
    Copyright (C) 2001 Paul Davis

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Id: rtc.cc,v 1.2 2002/04/10 17:42:57 pbd Exp $
*/

#include <stdio.h>
#include <linux/rtc.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
#include <math.h>
#include <sys/ioctl.h>
#include <asm/timex.h>

#include <ardour/ardour.h>
#include <ardour/rtc.h>

using namespace ARDOUR;

RealTimeClock::RealTimeClock ()
        : QMThread ("realtime clock", start_thread, this, true, 8)
{
        if ((rtc_fd = open ("/dev/rtc", O_RDONLY)) < 0) {
                error << "RealTimeClock: cannot open /dev/rtc ("
                      << strerror (errno)
                      << ')'
                      << endmsg;
                throw failed_constructor();
        }

        if (fcntl (rtc_fd, F_SETOWN, getpid()) < 0) {
                error << "RealTimeClock: cannot set ownership of /dev/rtc ("
                      << strerror (errno)
                      << ')'
                      << endmsg;
                throw failed_constructor();
        }

        rtc_running = false;
        current_hz = 0;
}

RealTimeClock::~RealTimeClock ()

{
        stop_periodic_interrupts ();
        close (rtc_fd);
}

void *
RealTimeClock::start_thread (void *arg)

{
        RealTimeClock *rtc = (RealTimeClock *) arg;
        return rtc->main ();
}

void *
RealTimeClock::do_work ()

{
        unsigned long rtc_data;

        start_periodic_interrupts ();

        while (!work_no_more()) {
                struct pollfd pfd;
                pfd.fd = rtc_fd;
                pfd.events = POLLIN | POLLERR;

                if (poll (&pfd, 1, 100000) < 0) {
                        if (errno == EINTR) {
                                // this happens mostly when run
                                // under gdb, or when exiting due to a signal
                                continue;
                        }

                        error << "RealTimeClock: poll call failed ("
                              << strerror (errno)
                              << ')'
                              << endmsg;

                        return (void *) -1;
                }
                read (rtc_fd, &rtc_data, sizeof (rtc_data));
                tick ();
        }

        return 0;
}

bool
RealTimeClock::set_interval (guint32 usecs)

{
        int req_hz = (int) floor ((1000000.0/usecs));
        int hz = 2;
        bool restart;

        hz = 2;
        while (hz < req_hz) {
                hz *= 2;
        }

        if (hz == current_hz) {
                return true;
        }
        
        restart = stop_periodic_interrupts ();

        if (ioctl(rtc_fd, RTC_IRQP_SET, hz) < 0) {
                error << "RealTimeClock: cannot set periodic interval ("
                      << strerror (errno)
                      << ')'
                      << endmsg;
                return -1;
        }

        current_hz = hz;

        if (restart) {
                start_periodic_interrupts ();
        }

        return true;
}

bool
RealTimeClock::start_periodic_interrupts ()

{
        if (!rtc_running) {
                if (ioctl (rtc_fd, RTC_PIE_ON, 0) < 0) {
                        error << "RealTimeClock: cannot start periodic interrupts ("
                              << strerror (errno)
                              << ')'
                              << endmsg;
                        return false;
                }
                rtc_running = true;
        }
        return rtc_running;
}
        
bool
RealTimeClock::stop_periodic_interrupts ()

{
        bool was_running = rtc_running;

        if (rtc_running) {
                if (ioctl (rtc_fd, RTC_PIE_OFF, 0) < 0) {
                        error << "RealTimeClock: cannot stop periodic interrupts ("
                              << strerror (errno)
                              << ')'
                              << endmsg;
                        return rtc_running;
                }
                rtc_running = false;
        }

        return was_running;
}
        


New Message Reply About this list Date view Thread view Subject view Author view Other groups

This archive was generated by hypermail 2b28 : Tue Apr 16 2002 - 16:19:59 EEST