/* $Id: timer.c,v 1.4 1999/03/10 07:58:04 boyns Exp $ */

/*
 * Copyright (C) 1993-99 Mark R. Boyns <boyns@doit.org>
 *
 * This file is part of rplay.
 *
 * rplay 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.
 *
 * rplay 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 rplay; see the file COPYING.  If not, write to the
 * Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <stdio.h>
#include <unistd.h>
#include "rplayd.h"
#include "timer.h"

double timer_rate = 0.0;
double timer_count = 0.0;
int timer_enabled = 0;

void
timer_update()
{
#ifdef HAVE_OSS
    int bytes = rplay_audio_getospace_bytes();
    /* check bytes and sample size? */
    if (bytes > 0)
    {
	rplayd_write(bytes);
    }
    /* if audio device isn't open, just pretend to keep spool going */
    else if (!rplay_audio_isopen())
    {
	/* approximate the amount to be written */
	int sample_size = (rplay_audio_precision >> 3) * rplay_audio_channels;
	rplayd_write(rplay_audio_sample_rate / curr_rate * sample_size);
    }
#else
    rplayd_write(curr_bufsize);
#endif

    timer_count += timer_rate;
}

void
timer_init()
{
    struct sigaction sa;

    sa.sa_handler = timer_update;
    sa.sa_flags = 0;
#if defined(SA_RESTART) && ! defined(sgi)
    sa.sa_flags |= SA_RESTART;
#endif
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGALRM);
    if (sigaction(SIGALRM, &sa, (struct sigaction *) NULL) < 0)
    {
	report(REPORT_ERROR, "timer_init: sigaction: %s\n", sys_err_str(errno));
	done(1);
    }

    timer_enabled = 0;
}

void
timer_block()
{
    sigset_t sigset;

    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);

    if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0)
    {
	report(REPORT_ERROR, "timer_block: sigsetprocmask: %s\n", sys_err_str(errno));
	done(1);
    }
}

void
timer_unblock()
{
    sigset_t sigset;

    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);

    if (sigprocmask(SIG_UNBLOCK, &sigset, NULL) < 0)
    {
	report(REPORT_ERROR, "timer_block: sigsetprocmask: %s\n", sys_err_str(errno));
	done(1);
    }
}

#ifdef __STDC__
void
timer_restart(int new_rate)
#else
void
timer_restart(new_rate)
    int new_rate;
#endif
{
    struct itimerval it;

    timer_enabled = 1;

    if (new_rate <= 0)
    {
	report(REPORT_ERROR, "timer_restart: new_rate=%d, must be >= 1\n", new_rate);
	done(1);
    }
    else if (new_rate == 1)
    {
	it.it_interval.tv_sec = 1;
	it.it_interval.tv_usec = 0;
    }
    else
    {
	it.it_interval.tv_sec = 0;
	it.it_interval.tv_usec = 1000000 / new_rate;
    }
    it.it_value = it.it_interval;
    if (setitimer(ITIMER_REAL, &it, NULL) < 0)
    {
	report(REPORT_ERROR, "timer_restart: setitimer: %s\n", sys_err_str(errno));
	done(1);
    }

    if (it.it_interval.tv_sec)
    {
	timer_rate = it.it_interval.tv_sec;
    }
    else
    {
	timer_rate = (double) it.it_interval.tv_usec / 1000000;
    }
}

#ifdef __STDC__
void
timer_start(int new_rate)
#else
void
timer_start(new_rate)
    int new_rate;
#endif
{
    timer_restart(new_rate);
}

void
timer_stop()
{
    struct itimerval it;

    timer_enabled = 0;

    it.it_interval.tv_sec = 0;
    it.it_interval.tv_usec = 0;
    it.it_value = it.it_interval;
    if (setitimer(ITIMER_REAL, &it, NULL) < 0)
    {
	report(REPORT_ERROR, "timer_stop: setitimer: %s\n", sys_err_str(errno));
	done(1);
    }
}
