/* > xaudio.c
 * (C) Andrew Brooks 1992
 * Permission to copy this software is granted provided the following
 * conditions are adhered to:
 * 1) The source code, Makefile and README are all provided
 * 2) No charge is made
 * 3) The copyright notice and these permissions remain unaltered
 * 4) Modified versions are marked as such
 * This software carries no warranty, and no responsibility can be assumed
 * for any loss or damage that may be caused.
 *
 * Compile with  cc -o xaudio xaudio.c -lX11 -lXt -lXaw -lXmu
 *
 * Control audio device on Sun SPARCstations:
 *   play volume
 *   record volume
 *   output port: speaker or headphones
 *
 * Version 1.00  20 Feb 1992
 *   Only has control of output volume
 * Version 1.10  03 Apr 1992
 *   Has control of both record and play volumes
 *   Detects changes in the audio device and updates settings
 * Version 1.20   16 Feb 1993
 *   Changed so that it works on SunOS 4.1.3
 *   Instead of reading audio params, changing one value, then writing back
 *   we have to use AUDIO_INITINFO, set the value, then write back, because
 *   for some reason GETINFO followed by SETINFO returns an error (even
 *   though it actually works!).
 *   
*/

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>                    /* for SIGPOLL */
#include <stropts.h>                   /* stream opts, for I_SETSIG */
#ifdef SVR4
#include <sys/audioio.h>
#else
#include <sun/audioio.h>               /* for AUDIO definitions */
#endif
#include <sys/ioccom.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Text.h>
#include <X11/Xaw/Scrollbar.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/List.h>
#include <X11/Xaw/Toggle.h>

#define AUDIO_IODEV     "/dev/audio"
#define AUDIO_CTLDEV    "/dev/audioctl"
#define PORT Audio_info.play.port
#define PLAYGAIN Audio_info.play.gain
#define RECGAIN  Audio_info.record.gain

int Audio_fd;
audio_info_t Audio_info;

int audio_changed();

Widget toplevel;

String FallBack_Resources[] = {
	"*Toggle.translations:  #override <Btn1Down>,<Btn1Up>: set() notify()",
	NULL,
};

static Arg xig_form_1_args[] = {                      /* main window */
	XtNhorizDistance,	(XtArgVal) 8,
	XtNvertDistance,	(XtArgVal) 9,
	XtNwidth,	(XtArgVal) 188,
	XtNheight,	(XtArgVal) 85,
};
Widget xig_form_1_widget;

static Arg xig_scrollbar_1_args[] = {                 /* play vol slider */
	XtNhorizDistance,	(XtArgVal) 55,
	XtNvertDistance,	(XtArgVal) 5,
	XtNwidth,	(XtArgVal) 128,
	XtNheight,	(XtArgVal) 19,
	XtNorientation,	(XtArgVal) XtorientHorizontal,
};
Widget xig_scrollbar_1_widget;

static Arg xig_scrollbar_2_args[] = {                 /* record vol slider */
	XtNhorizDistance,	(XtArgVal) 55,
	XtNvertDistance,	(XtArgVal) 27,
	XtNwidth,	(XtArgVal) 128,
	XtNheight,	(XtArgVal) 19,
	XtNorientation,	(XtArgVal) XtorientHorizontal,
};
Widget xig_scrollbar_2_widget;

static Arg xig_label_1_args[]= {                    /* play label */
	XtNhorizDistance,	(XtArgVal) 4,
	XtNvertDistance,	(XtArgVal) 5,
	XtNwidth,	(XtArgVal) 40,
	XtNheight,	(XtArgVal) 19,
	XtNjustify,	(XtArgVal) XtJustifyLeft,
	XtNlabel,	(XtArgVal) "Play",
	XtNborderWidth, 0,
};
Widget xig_label_1_widget;

static Arg xig_label_2_args[]= {                    /* record label */
	XtNhorizDistance,	(XtArgVal) 4,
	XtNvertDistance,	(XtArgVal) 27,
	XtNwidth,	(XtArgVal) 50,
	XtNheight,	(XtArgVal) 19,
	XtNjustify,	(XtArgVal) XtJustifyLeft,
	XtNlabel,	(XtArgVal) "Record",
	XtNborderWidth, 0,
};
Widget xig_label_2_widget;

static Arg xig_command_1_args[] = {                /* speaker button */
	XtNhorizDistance,	(XtArgVal) 4,
	XtNvertDistance,	(XtArgVal) 56,
	XtNwidth,	(XtArgVal) 80,
	XtNheight,	(XtArgVal) 19,
	XtNjustify,	(XtArgVal) XtJustifyCenter,
	XtNlabel,	(XtArgVal) "Speaker",
};
Widget xig_command_1_widget;

static Arg xig_command_2_args[] = {                /* headphones button */
	XtNradioGroup, (XtArgVal)NULL,
	XtNhorizDistance,	(XtArgVal) 100,
	XtNvertDistance,	(XtArgVal) 56,
	XtNwidth,	(XtArgVal) 80,
	XtNheight,	(XtArgVal) 19,
	XtNjustify,	(XtArgVal) XtJustifyCenter,
	XtNlabel,	(XtArgVal) "Headphones",
};
Widget xig_command_2_widget;


/* set stream to send SIGPOLL when device is opened/closed/EOF/overflow/etc */
/* used to find out when gain has been altered (see signal()) */

setsig(audio_fd, on)
int audio_fd;
Boolean on;
{
	int msg;
	msg = on ? S_MSG : 0;
	if (ioctl(audio_fd, I_SETSIG, msg)==-1)
	{
		perror("ioctl: i_setsig(s_msg)");
		exit(1);
	}
}


/* --- Get/Set audio device parameters - gain, port */

/* Only get parameters if get!=0 */
/* otherwise just initialise */

get_audio_parameters(get)
int get;
{
	if (get==0) AUDIO_INITINFO(&Audio_info)
	else if (ioctl(Audio_fd, AUDIO_GETINFO, &Audio_info) < 0)
	{
		perror("Get output port");
		exit(1);
	}
}

set_audio_parameters()
{
	if (ioctl(Audio_fd, AUDIO_SETINFO, &Audio_info) < 0)
	{
		perror("Set output port");
		exit(1);
	}
}

set_audio_port(port)
int port;
{
    get_audio_parameters(0);
	PORT=port;
	set_audio_parameters();

}

set_audio_play_gain(gain)
int gain;
{
	get_audio_parameters(0);
	PLAYGAIN=gain;
	set_audio_parameters();
}

set_audio_record_gain(gain)
int gain;
{
	get_audio_parameters(0);
	RECGAIN=gain;
	set_audio_parameters();
}


/* --- Scrollbar changed.  closure indicates which scrollbar */

static void xig_scrollbar_1_callback_function(w, closure, calldata)
	Widget w;
	XtPointer closure;
	XtPointer calldata;
{
float pos;	                      /* 0->1 percentage of position */
int vol;

	XtVaGetValues(w, XtNtopOfThumb, &pos, NULL);
	vol=(pos * AUDIO_MAX_GAIN);
	(int)closure == 1 ? set_audio_play_gain(vol) : set_audio_record_gain(vol);
}


/* --- Button press.  closure indicates which button */

static void xig_command_1_callback_function(w, closure, calldata)
	Widget w;
	XtPointer closure;
	XtPointer calldata;
{
Boolean set;

	XtVaGetValues(w, XtNstate, &set, NULL);
	if (set)
		set_audio_port((int)closure == 1 ? AUDIO_SPEAKER : AUDIO_HEADPHONE);
}


audio_changed()
{
	float playvol, recvol;
	int port;
	Display *disp=XtDisplay(toplevel);

	get_audio_parameters();
	playvol=(float)PLAYGAIN / AUDIO_MAX_GAIN;
	recvol=(float)RECGAIN / AUDIO_MAX_GAIN;
	port=PORT;
	/* This is probably naughty, but it works for me */
	XawScrollbarSetThumb(xig_scrollbar_1_widget, playvol, 0.05);
	XawScrollbarSetThumb(xig_scrollbar_2_widget, recvol, 0.05);
	XtVaSetValues(PORT==AUDIO_HEADPHONE ? xig_command_2_widget : xig_command_1_widget, XtNstate, TRUE, NULL);
	XFlush(disp);
}


main(argc, argv)
int argc;
char **argv;
{
	float initplayvolume, initrecvolume;
	int initport;
	int looping=1;
	XtAppContext appcontext;
	XEvent event;
	struct sigaction sigac;

	toplevel = XtVaAppInitialize(&appcontext, "Xaudio", NULL, 0, &argc, argv, FallBack_Resources, NULL);
	xig_form_1_widget =  XtCreateManagedWidget(0, formWidgetClass, toplevel, xig_form_1_args, XtNumber(xig_form_1_args) );
	xig_scrollbar_1_widget =  XtCreateManagedWidget(0, scrollbarWidgetClass, xig_form_1_widget, xig_scrollbar_1_args, XtNumber(xig_scrollbar_1_args) );
	xig_scrollbar_2_widget =  XtCreateManagedWidget(0, scrollbarWidgetClass, xig_form_1_widget, xig_scrollbar_2_args, XtNumber(xig_scrollbar_2_args) );
	XtAddCallback(xig_scrollbar_1_widget, XtNjumpProc, xig_scrollbar_1_callback_function, 1);
	XtAddCallback(xig_scrollbar_1_widget, XtNscrollProc, xig_scrollbar_1_callback_function, 1);
	XtAddCallback(xig_scrollbar_2_widget, XtNjumpProc, xig_scrollbar_1_callback_function, 2);
	XtAddCallback(xig_scrollbar_2_widget, XtNscrollProc, xig_scrollbar_1_callback_function, 2);
	xig_label_1_widget =  XtCreateManagedWidget(0, labelWidgetClass, xig_form_1_widget, xig_label_1_args, XtNumber(xig_label_1_args) );
	xig_label_2_widget =  XtCreateManagedWidget(0, labelWidgetClass, xig_form_1_widget, xig_label_2_args, XtNumber(xig_label_2_args) );
	xig_command_1_widget =  XtCreateManagedWidget(0, toggleWidgetClass, xig_form_1_widget, xig_command_1_args, XtNumber(xig_command_1_args) );
	XtAddCallback(xig_command_1_widget, XtNcallback, xig_command_1_callback_function, 1);
	xig_command_2_args[0].value = (XtArgVal)xig_command_1_widget;
	xig_command_2_widget =  XtCreateManagedWidget(0, toggleWidgetClass, xig_form_1_widget, xig_command_2_args, XtNumber(xig_command_2_args) );
	XtAddCallback(xig_command_2_widget, XtNcallback, xig_command_1_callback_function, 2);
	XtRealizeWidget(toplevel);

	if ((Audio_fd = open(AUDIO_CTLDEV, O_RDONLY)) < 0)
	{
		perror(AUDIO_CTLDEV);
		exit(1);
	}

	get_audio_parameters(1);
	initplayvolume=(float)PLAYGAIN / AUDIO_MAX_GAIN;
	initrecvolume=(float)RECGAIN / AUDIO_MAX_GAIN;
	initport=PORT;
	XawScrollbarSetThumb(xig_scrollbar_1_widget, initplayvolume, 0.05);
	XawScrollbarSetThumb(xig_scrollbar_2_widget, initrecvolume, 0.05);
	XtVaSetValues(PORT==AUDIO_HEADPHONE ? xig_command_2_widget : xig_command_1_widget, XtNstate, TRUE, NULL);

	/* notify me when audio parameters are changed */
	/* SIGPOLL is the SYSV name for SIGIO */

	sigac.sa_handler = audio_changed;
	sigac.sa_flags = SA_RESTART;
	sigemptyset(&sigac.sa_mask);
	sigaction(SIGPOLL, &sigac, NULL );

	setsig(Audio_fd, TRUE);

	XtAppMainLoop(appcontext);
}

