/* beepIndigo.c John R. Murray murray@scri.fsu.edu Jan. 4, 1994. Implementation
 * of beep.h for SGI Indigos. Basically a quick rewrite of beepSun.c, written
 * by seligman et al 6/92
 */

/*
 * If the audio library or audio include files are missing, you need
 * to get sound support off the SGI distribution tapes or CD.
 *
 * Defining FULLVOLUME has no effect. See notes below.
 */

/* NOTES: This will not compile on the older Personal Irises (4D/20 and 4D/25).
-- It should work for newer PIs (4D/30 and 4D/35) on SGI models newer than the
-- Indigo (Indy and Indigo^2), but has not been tested. Basically, anything
-- that uses the Audio Library (ALwhatever function names), and *not* the
-- /dev/audio interface, should work.
--
-- The sound library on my machine is only a partial implementation, so some
-- functionality in beep.h is missing. Notably:
--
-- 1) No software volume control. (relative volume with -v still works, tho)
--
-- 2) No software control of sampling rate.
--
-- These parameters are controllable through the Audio Control Panel,
-- accessible through the Tools submenu, or by running /usr/sbin/apanel.
-- NOTE: This code assumes you have the output sampling rate set to 48 KHz
-- (the default). If the tone is too low and the code timing is too slow,
-- your output sampling rate is incorrect and will need to be set to 48 KHz.
--
-- If you implement volume and sampling rate control, please be sure to send
-- the updated code to the writer of morse.c, joe@montebello.soest.hawaii.edu,
-- or to me and I'll forward it.
*/

#include "beep.h"
#include <audio.h>
#include <math.h>

#define ABUFSIZ (100000) /* 100000 is default buffer size */
#define RATE	(48000)	/*48 K samples = 1 second  (assuming it's set to the default)*/
#define RAMP	(RATE * 10 / 1000)	/* 10 millisecond ramp */
#define MAXTIME	(ABUFSIZ)	/* If STEREO, MAXTIME = ABUFSIZ / 2 */
#define ASCALE  (100) /* multiplier to size waveform to port width (assuming 8bit) */

ALport outPort;

void writeSound(port, buffer, len)
ALport port;
signed char buffer[];
unsigned long len;
{
    signed char *bufp;
    unsigned long len2, fill;

    bufp = buffer;
    len2 = len;
    while ((fill = ALgetfillable(port)) < len2)
    {
	if (fill)
	{
	    /* write as much as we can */
	    ALwritesamps(port, bufp, fill);
	    bufp += fill;
	    len2 -= fill;
	}
	else
	{
	    /* wait 1/CLK_TCK seconds (1/100 sec on my machine) */
	    sginap(1); 
	}
    }
    ALwritesamps(port, bufp, len2);
}

int
BeepInit ()
{

    ALconfig config;

    config = ALnewconfig();
    /* starting with default config (stereo, 16 bit wide samples, 100k buffer) */
    /* NOTE: we're assuming the sampling rate is set to the default (48K) */
    /* (software control of sampling rate is not fully implemented by SGI yet */

    if (config == 0)
    {
	/* Uh oh! */
	return 1;
    }
    else
    {
	/* this is just to avoid rewriting much of Beep, we could
	 * run 24 bit stereo if we wanted.
	 */
	ALsetwidth(config, AL_SAMPLE_8);
	ALsetchannels(config, AL_MONO); /* if STEREO, fix the #define of MAXTIME */
	ALsetqueuesize(config, ABUFSIZ);

#ifdef FULLVOLUME
	/* software volume control not fully implemented by SGI yet */
#endif

	if (0 == (outPort = ALopenport("morse", "w", config)))
	    /* another problem */
	    return 1;
	ALfreeconfig(config);
	return 0;
    }
}


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

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

/*
 * Initialize the sound of silence
 */
    if (first_time == 1)
    {
	for (i = 0; i < MAXTIME; i++)
	    silence[i] = 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)
    {
	writeSound(outPort, 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. * 3.14159 / cycle;	/* sine scale factor */

/* Ramp up; begin with silence */

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

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

/* Ramp down; end with silence */
	    for (i = 0; i < down_len; i++)
	    {
		ramp_down[i] = ASCALE * (1. - (float) (i + 1) / down_len)
				      * (volume / 100.) * sin (i * dt);
	    }
	}
	writeSound(outPort, soundbuf, first_len);
	writeSound(outPort, ramp_down, down_len);
    }

    return 0;
}


int
BeepWait ()
{
    while (ALgetfilled(outPort) > 0)
	sginap(1); /* wait 1/CLK_TCK seconds (1/100th second on my machine) */
    return 0;
}


int
BeepCleanup ()
{
   ALcloseport(outPort);
   return 0;
}


int
BeepResume ()
{
    return BeepInit();
}
