/*
 * If the audio library or audio include files are missing, you need
 * to get /usr/demo/SOUND off the Sun OS tapes or CD.
 *
 * If FULLVOLUME is defined this routine will turn your audio device up to
 * full volume; it will not attempt to reset the current speaker / jack choice.
 */

/* beepSun.c -- seligman 6/92 */

/*
-- Implementation of beep.h for Sun.
-- Rick to Joe to Scott to Joe.
*/

/* Date: Fri, 2 Aug 91 08:26:08 PDT */
/* From: rick@st.unocal.com (Richard Ottolini) */

/* Revised: Joe Dellinger */
/* Sat Nov 28 02:30:32 HST 1992 */

/* Revised: Brian Suggs (fixed the "annoying click" bug) */
/* Fri Sep 16 12:29:13 PDT 1994 */

#include "beep.h"
#include <sun/audioio.h>
#include </usr/demo/SOUND/multimedia/libaudio.h>
#include </usr/demo/SOUND/multimedia/ulaw2linear.h>
#include <stropts.h>
#include <math.h>
#include <fcntl.h>


#define RATE	(8000)	/* 8 K samples = 1 second */
#define RAMP	(RATE * 5 / 1000)	/* 5 millisecond ramp */
#define MAXTIME	(2 * RATE)	/* 2 seconds max tone time */

static int      audio;
static audio_info_t audio_stat;

int
BeepInit ()
{
    audio = open ("/dev/audio", O_WRONLY);

    if (audio < 0)
	/* Uh oh! */
	return 1;
    else
    {
#ifdef FULLVOLUME
	/*
	 * Turn the speaker (or headphone, whichever the user has it set to)
	 * up to full volume.
	 */
	AUDIO_INITINFO(&audio_stat);
	audio_stat.play.gain = AUDIO_MAX_GAIN;
	ioctl (audio, AUDIO_SETINFO, &audio_stat);
#endif

	return 0;
    }
}


int
Beep (time, volume, pitch)
    int             time, volume, pitch;
{
static unsigned char silence[MAXTIME];
static unsigned char soundbuf[MAXTIME - RAMP];
static unsigned char ramp_down[RAMP];
static int      last_pitch = -1, last_n = -1, last_volume = -1;
static int      first_time = 1;

int             i, n, first_len, down_len, cycle;
double          dt;

/*
 * Initialize the sound of silence
 */
    if (first_time == 1)
    {
	for (i = 0; i < MAXTIME; i++)
	    silence[i] = audio_l2u (0);
	first_time = 0;
    }


/*
 * Finagle the number of samples
 */
    n = (time / 1000.) * RATE;	/* Number samples in tone time */
    n = n < MAXTIME ? n : MAXTIME;	/* clip to buffer size */
    n = n < 2 * RAMP ? 2 * RAMP : n;	/* leave room for ramps */

/*
 * Catch stupidity
 */
    if (pitch <= 0)
	volume = 0;


    if (volume <= 0)
    {
	write (audio, silence, n);
    }
    else
    {
/*
 * clip to Nyquist
 * (Of course this means that if you ask for too high a frequency you
 * just get silence, since you sample all the zero-crossings of the
 * sine wave.)
 */
	pitch = pitch < RATE / 2 ? pitch : RATE / 2;
	cycle = ((RATE + 1.e-6) / pitch);	/* samples per cycle */

	if (cycle > MAXTIME / 2)
	    cycle = MAXTIME / 2;

	/* round down length of (rampup + mesa top) to integral cycle */
	first_len = ((n - RAMP) / cycle) * cycle;
	if (first_len < cycle)
	    first_len = cycle;
	if (first_len > MAXTIME - RAMP)
	    first_len = MAXTIME - RAMP;

	/* round down length of (rampdown) to integral cycle */
	down_len = ((RAMP) / cycle) * cycle;
	if (down_len < cycle)
	    down_len = cycle;
	if (down_len > RAMP)
	    down_len = RAMP;

/*
 * Can we just reuse what we had before?
 */
	if (pitch != last_pitch || n > last_n || volume != last_volume)
	{
	    last_pitch = pitch;
	    last_n = n;
	    last_volume = volume;

	    dt = 2. * M_PI / cycle;	/* sine scale factor */

/* Ramp up; begin with silence */

	    for (i = 0; i < RAMP; i++)
	    {
		soundbuf[i] = audio_l2u (audio_d2l (
			 ((float) i / RAMP) * (volume / 100.) * sin (i * dt)
						    ));
	    }

/* Mesa top */
	    for (i = RAMP; i < first_len; i++)
	    {
		soundbuf[i] = audio_l2u (audio_d2l (
					 1. * (volume / 100.) * sin (i * dt)
						    ));
	    }

/* Ramp down; end with silence */
	    for (i = 0; i < down_len; i++)
	    {
		ramp_down[i] = audio_l2u (audio_d2l (
						     (1. - (float) (i + 1) / down_len) * (volume / 100.) * sin (i * dt)
						     ));
	    }
	}

	write (audio, soundbuf, first_len);
	write (audio, ramp_down, down_len);
    }

    return 0;
}


int
BeepWait ()
{
    ioctl (audio, AUDIO_DRAIN);
    return 0;
}


int
BeepCleanup ()
{
    return 0;
}


int
BeepResume ()
{
    return 0;
}
