/* Audio hardware handlers (SGI, OSS, ALSA, Sun, NeXT, Mac, W95, Be, HPUX, AIX) */
/*
* layout of this file:
* error handlers
* SGI new and old audio library (find "- SG")
* OSS ("- O")
* ALSA ("- A")
* NeXT ("- NE")
* Sun ("- SU") (has switches for OPENBSD, but they're untested)
* Mac ("- M") (for MacOS 8.1 -- apparently there are important differences in 8.5)
* Be ("- B")
* HPUX ("- H")
* W95 ("- WI")
* AIX, NEC EWS, SONY NEWS, OS2, AF, NetBSD etc -- untested and incomplete
* audio describers
*/
/* TODO: w95 input, read/write state
* sgi/w95/mac? also may have multiple systems/cards
* check Mac OS 8.5 changes
* Be audio system has apparently changed a lot
*/
/* when reading device_field, put default input device first, or mark somehow */
/*
* void describe_audio_state(void) describes the audio hardware state.
* char *report_audio_state(void) returns the same information as a string.
*
* int open_audio_output(int dev, int srate, int chans, int format, int size)
* int open_audio_input(int dev, int srate, int chans, int format, int size)
* int write_audio(int line, char *buf, int bytes)
* int close_audio(int line)
* int read_audio(int line, char *buf, int bytes)
*
* int read_audio_state(int dev, int field, int chan, float *val)
* int write_audio_state(int dev, int field, int chan, float *val)
* void save_audio_state(void)
* void restore_audio_state(void)
*
* int audio_error(void) returns the error code indicated by the immediately preceding audio call
* int initialize_audio(void) does whatever is needed to get set up
* char *audio_error_name(int err) gives string decription of error code
*
* int audio_systems(void) returns number of separate complete audio systems (soundcards essentially)
* AUDIO_SYSTEM(n) selects the nth card (counting from 0), AUDIO_SYSTEM(0) is always the default
* char *audio_system_name(int system) returns some user-recognizable (?) name for the given card
* char *audio_moniker(void) returns some brief description of the overall audio setup.
*/
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <math.h>
#include <stdio.h>
#if (!defined(HAVE_CONFIG_H)) || (defined(HAVE_FCNTL_H))
#include <fcntl.h>
#endif
#include <signal.h>
#if (!defined(HAVE_CONFIG_H)) || (defined(HAVE_LIMITS_H))
#include <limits.h>
#endif
#include <errno.h>
#include <stdlib.h>
#if (defined(NEXT) || (defined(HAVE_LIBC_H) && (!defined(HAVE_UNISTD_H))))
#include <libc.h>
#else
#if (!(defined(_MSC_VER))) && (!(defined(MPW_C)))
#include <unistd.h>
#endif
#endif
#define STRBUF_SIZE 4096
#if (defined(HAVE_CONFIG_H)) && (!defined(HAVE_STRERROR))
char *strerror(int errnum)
{
char *strerrbuf;
strerrbuf = (char *)CALLOC(16,sizeof(char));
sprintf(strerrbuf,"io err %d",errnum);
return(strerrbuf);
}
#else
#include <string.h>
#endif
#include "sndlib.h"
static char *version_name = NULL;
static int AUDIO_ERROR = SNDLIB_NO_ERROR;
int audio_error(void) {return(AUDIO_ERROR);}
void set_audio_error(int err) {AUDIO_ERROR = err;}
static int audio_initialized = 0;
static char *audio_error_name_1(int err)
{
switch (err)
{
case SNDLIB_NO_ERROR: return(""); break;
case SNDLIB_CHANNELS_NOT_AVAILABLE: return("channel(s) not available"); break;
case SNDLIB_SRATE_NOT_AVAILABLE: return("srate not available"); break;
case SNDLIB_FORMAT_NOT_AVAILABLE: return("format not available"); break;
case SNDLIB_NO_INPUT_AVAILABLE: return("no input available"); break;
case SNDLIB_NO_OUTPUT_AVAILABLE: return("no output available"); break;
case SNDLIB_INPUT_BUSY: return("input busy"); break;
case SNDLIB_OUTPUT_BUSY: return("output busy"); break;
case SNDLIB_CONFIGURATION_NOT_AVAILABLE: return("configuration not available"); break;
case SNDLIB_INPUT_CLOSED: return("input closed"); break;
case SNDLIB_OUTPUT_CLOSED: return("output closed"); break;
case SNDLIB_IO_INTERRUPTED: return("io interrupted"); break;
case SNDLIB_NO_LINES_AVAILABLE: return("no lines available"); break;
case SNDLIB_WRITE_ERROR: return("write error"); break;
case SNDLIB_SIZE_NOT_AVAILABLE: return("size not available"); break;
case SNDLIB_DEVICE_NOT_AVAILABLE: return("device not available"); break;
case SNDLIB_CANT_CLOSE: return("close failed"); break;
case SNDLIB_CANT_OPEN: return("open failed"); break;
case SNDLIB_READ_ERROR: return("read error"); break;
case SNDLIB_AMP_NOT_AVAILABLE: return("amp control not available"); break;
case SNDLIB_AUDIO_NO_OP: return("unimplemented operation"); break;
case SNDLIB_CANT_WRITE: return("write failed"); break;
case SNDLIB_CANT_READ: return("read failed"); break;
case SNDLIB_NO_READ_PERMISSION: return("need read permission on /dev/dsp"); break;
default: return("unknown error"); break;
}
}
static char *strbuf = NULL;
static void pprint(char *str);
int device_channels(int dev);
int device_gains(int dev);
int device_channels(int dev)
{
float val[1];
read_audio_state(dev,SNDLIB_CHANNEL_FIELD,0,val);
return((int)val[0]);
}
int device_gains(int ur_dev)
{
float val[1];
int err;
int dev;
dev = SNDLIB_DEVICE(ur_dev);
/* to get hardware gains, read device amp_field and error = none */
if ((dev == SNDLIB_DAC_FILTER_DEVICE) || (dev == SNDLIB_MIXER_DEVICE))
{
err = read_audio_state(ur_dev,SNDLIB_CHANNEL_FIELD,0,val);
return((int)val[0]);
}
err = read_audio_state(ur_dev,SNDLIB_AMP_FIELD,0,val);
if (err != SNDLIB_NO_ERROR) return(0);
return(device_channels(ur_dev));
}
/* ------------------------------- SGI ----------------------------------------- */
#ifdef SGI
#define AUDIO_OK
#include <audio.h>
static char *errstr = NULL;
static int al_err = 0;
int audio_systems(void) {return(1);} /* I think more than 1 is possible, but don't have a case to test with */
char *audio_system_name(int system) {return("SGI");}
char *audio_moniker(void)
{
#ifdef AL_RESOURCE
return("New SGI audio");
#else
return("Old SGI audio");
#endif
}
#ifdef AL_RESOURCE
char *audio_error_name(int err)
{
if (!al_err) return(audio_error_name_1(err));
sprintf(errstr,"%s: %s",audio_error_name_1(err),alGetErrorString(al_err));
return(errstr);
}
static int check_queue_size (int size, int chans) {return(size);}
#else
#define STEREO_QUEUE_MIN_SIZE 1024
#define STEREO_QUEUE_MIN_CHOICE 1024
/* docs say 510 or 512, but they die with "File size limit exceeded" %$@#!(& */
#define MONO_QUEUE_MIN_SIZE 1019
#define MONO_QUEUE_MIN_CHOICE 1024
#define STEREO_QUEUE_MAX_SIZE 131069
#define STEREO_QUEUE_MAX_CHOICE 65536
#define MONO_QUEUE_MAX_SIZE 262139
#define MONO_QUEUE_MAX_CHOICE 131072
/* if these limits are not followed, the damned thing dumps core and dies */
static int check_queue_size (int size, int chans)
{
if ((chans == 1) && (size > MONO_QUEUE_MAX_SIZE)) return(MONO_QUEUE_MAX_CHOICE);
if ((chans == 1) && (size < MONO_QUEUE_MIN_SIZE)) return(MONO_QUEUE_MIN_CHOICE);
if ((chans > 1) && (size > STEREO_QUEUE_MAX_SIZE)) return(STEREO_QUEUE_MAX_CHOICE);
if ((chans > 1) && (size < STEREO_QUEUE_MIN_SIZE)) return(STEREO_QUEUE_MIN_CHOICE);
return(size);
}
static void check_quad (int device, int channels)
{
long sr[2];
/* if quad, make sure we are set up for it, else make sure we aren't (perhaps the latter is unnecessary) */
/* in 4 channel mode, stereo mic and line-in are 4 inputs, headphones/speakers and stereo line-out are the 4 outputs */
sr[0] = AL_CHANNEL_MODE;
ALgetparams(device,sr,2);
if ((channels == 4) && (sr[1] != AL_4CHANNEL))
{
sr[1] = AL_4CHANNEL;
ALsetparams(device,sr,2);
}
else
{
if ((channels != 4) && (sr[1] != AL_STEREO))
{
sr[1] = AL_STEREO;
ALsetparams(device,sr,2);
}
}
}
static char *AL_GetErrorString(int err)
{
switch (err)
{
case AL_BAD_NOT_IMPLEMENTED: return("not implemented yet"); break;
case AL_BAD_PORT: return("tried to use an invalid port"); break;
case AL_BAD_CONFIG: return("tried to use an invalid configuration"); break;
case AL_BAD_DEVICE: return("tried to use an invalid device"); break;
case AL_BAD_DEVICE_ACCESS: return("unable to access the device"); break;
case AL_BAD_DIRECTION: return("illegal direction given for port"); break;
case AL_BAD_OUT_OF_MEM: return("operation has run out of memory"); break;
case AL_BAD_NO_PORTS: return("not able to allocate a port"); break;
case AL_BAD_WIDTH: return("invalid sample width given"); break;
case AL_BAD_ILLEGAL_STATE: return("an illegal state has occurred"); break;
case AL_BAD_QSIZE: return("attempt to set an invalid queue size"); break;
case AL_BAD_FILLPOINT: return("attempt to set an invalid fillpoint"); break;
case AL_BAD_BUFFER_NULL: return("null buffer pointer"); break;
case AL_BAD_COUNT_NEG: return("negative count"); break;
case AL_BAD_PVBUFFER: return("param/val buffer doesn't make sense"); break;
case AL_BAD_BUFFERLENGTH_NEG: return("negative buffer length"); break;
case AL_BAD_BUFFERLENGTH_ODD: return("odd length parameter/value buffer"); break;
case AL_BAD_CHANNELS: return("invalid channel specifier"); break;
case AL_BAD_PARAM: return("invalid parameter"); break;
case AL_BAD_SAMPFMT: return("attempt to set invalid sample format"); break;
case AL_BAD_RATE: return("invalid sample rate token"); break;
case AL_BAD_TRANSFER_SIZE: return("invalid size for sample read/write"); break;
case AL_BAD_FLOATMAX: return("invalid size for floatmax"); break;
case AL_BAD_PORTSTYLE: return("invalid port style"); break;
default: return("");
}
}
char *audio_error_name(int err)
{
if (!al_err) return(audio_error_name_1(err));
sprintf(errstr,"%s: %s",audio_error_name_1(err),AL_GetErrorString(al_err));
return(errstr);
}
#endif
#define IO_LINES 8
static ALconfig *config = NULL;
static ALport *port = NULL;
static int *line_in_use = NULL;
static int *channels = NULL;
static long *device = NULL;
static int *datum_size = NULL;
static int *line_out = NULL;
int initialize_audio(void)
{
if (!audio_initialized)
{
audio_initialized = 1;
AUDIO_ERROR = SNDLIB_NO_ERROR;
al_err = 0;
errstr = (char *)CALLOC(256,sizeof(char));
config = (ALconfig *)CALLOC(IO_LINES,sizeof(ALconfig));
port = (ALport *)CALLOC(IO_LINES,sizeof(ALport));
line_in_use = (int *)CALLOC(IO_LINES,sizeof(int));
channels = (int *)CALLOC(IO_LINES,sizeof(int));
device = (long *)CALLOC(IO_LINES,sizeof(long));
datum_size = (int *)CALLOC(IO_LINES,sizeof(int));
line_out = (int *)CALLOC(IO_LINES,sizeof(int));
}
return(0);
}
static int error_exit(int error, int line)
{
AUDIO_ERROR = error;
#ifdef AL_RESOURCE
if (line != -1) alFreeConfig(config[line]);
#else
if (line != -1) ALfreeconfig(config[line]);
#endif
return(-1);
}
#ifdef AL_RESOURCE
static int to_al_interface_or_device(int dev,int which)
{
switch (dev)
{
case SNDLIB_MICROPHONE_DEVICE: return(alGetResourceByName(AL_SYSTEM,"Microphone",which)); break;
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_READ_WRITE_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_SPEAKERS_DEVICE: return(alGetResourceByName(AL_SYSTEM,"Analog Out",which)); break;
case SNDLIB_ADAT_IN_DEVICE: return(alGetResourceByName(AL_SYSTEM,"ADAT In",which)); break;
case SNDLIB_AES_IN_DEVICE: return(alGetResourceByName(AL_SYSTEM,"AES In",which)); break;
case SNDLIB_ADAT_OUT_DEVICE: return(alGetResourceByName(AL_SYSTEM,"ADAT Out",which)); break;
case SNDLIB_DIGITAL_OUT_DEVICE:
case SNDLIB_AES_OUT_DEVICE: return(alGetResourceByName(AL_SYSTEM,"AES Out",which)); break;
case SNDLIB_LINE_IN_DEVICE: return(alGetResourceByName(AL_SYSTEM,"Line In",which)); break;
case SNDLIB_LINE_OUT_DEVICE: return(alGetResourceByName(AL_SYSTEM,"Line Out2",which)); break; /* ?? */
/* case SNDLIB_DIGITAL_IN_DEVICE: return(alGetResourceByName(AL_SYSTEM,"DAC2 In",which)); break; */ /* this is analog in ?? */
}
return(0);
}
static int to_al_device(int dev) {return(to_al_interface_or_device(dev,AL_DEVICE_TYPE));}
static int to_al_interface(int dev) {return(to_al_interface_or_device(dev,AL_INTERFACE_TYPE));}
#endif
#include <stdio.h>
/* just a placeholder for now */
int find_audio_output(int chans)
{
#ifdef AL_RESOURCE
ALvalue x[32];
ALpv y;
int n,i;
y.param = AL_INTERFACE;
y.value.i = AL_DIGITAL_IF_TYPE;
n = alQueryValues(AL_SYSTEM,AL_DEFAULT_OUTPUT,x,32,&y,1);
for (i=0;i<n;i++)
{
y.param = AL_CHANNELS;
alGetParams(x[i].i,&y,1);
if (chans <= y.value.i) return(x[i].i);
}
#endif
return(-1);
}
static int to_sgi_format(int frm)
{
switch (frm)
{
case SNDLIB_8_LINEAR:
case SNDLIB_16_LINEAR:
case SNDLIB_24_LINEAR: return(AL_SAMPFMT_TWOSCOMP); break;
case SNDLIB_32_FLOAT: return(AL_SAMPFMT_FLOAT); break;
case SNDLIB_64_DOUBLE: return(AL_SAMPFMT_DOUBLE); break;
}
return(-1);
}
int open_audio_output(int ur_dev, int srate, int chans, int format, int requested_size)
{
#ifdef AL_RESOURCE
ALpv z[2];
#endif
long sr[2];
int i,line,size,width,sgi_format,dev;
AUDIO_ERROR = SNDLIB_NO_ERROR; al_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
line = -1;
for (i=0;i<IO_LINES;i++) if (line_in_use[i] == 0) {line=i; break;}
if (line == -1) {AUDIO_ERROR = SNDLIB_NO_LINES_AVAILABLE; return(-1);}
channels[line] = chans;
line_out[line] = 1;
datum_size[line] = mus_format2bytes(format);
if (requested_size == 0)
size = 4096;
else size = check_queue_size(requested_size,chans);
if (datum_size[line] == 3)
width = AL_SAMPLE_24;
else
{
if (datum_size[line] == 1)
width = AL_SAMPLE_8;
else width = AL_SAMPLE_16;
}
sgi_format = to_sgi_format(format);
#ifdef AL_RESOURCE
if (dev == SNDLIB_DEFAULT_DEVICE)
device[line] = AL_DEFAULT_OUTPUT;
else device[line] = to_al_device(dev); if (!(device[line])) return(error_exit(SNDLIB_DEVICE_NOT_AVAILABLE,-1));
if (chans>2) size = 65536; /* for temp adat code */
if (device_channels(dev) < chans) /* look for some device that can play this file */
device[line] = find_audio_output(chans); if (device[line] == -1) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
if ((chans == 4) && (dev == SNDLIB_DAC_OUT_DEVICE))
{
/* kludge around a bug in the new audio library */
sr[0] = AL_CHANNEL_MODE;
sr[1] = AL_4CHANNEL;
ALsetparams(AL_DEFAULT_DEVICE,sr,2);
}
z[0].param = AL_RATE;
z[0].value.ll = alDoubleToFixed((double)srate);
z[1].param = AL_MASTER_CLOCK;
/* z[1].value.i = AL_CRYSTAL_MCLK_TYPE; */
z[1].value.i = AL_MCLK_TYPE; /* was AL_CRYSTAL_MCLK_TYPE -- digital I/O perhaps needs AL_VARIABLE_MCLK_TYPE */
alSetParams(device[line],z, 2);
config[line] = alNewConfig(); if (!(config[line])) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
if ((sgi_format == -1) ||
(alSetSampFmt(config[line],sgi_format) == -1) ||
(alSetWidth(config[line],width) == -1)) /* this is a no-op in the float and double cases */
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,line));
al_err = alSetChannels(config[line],chans); if (al_err == -1) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,line));
al_err = alSetQueueSize(config[line],size); if (al_err == -1) return(error_exit(SNDLIB_SIZE_NOT_AVAILABLE,line));
al_err = alSetDevice(config[line],device[line]); if (al_err == -1) return(error_exit(SNDLIB_DEVICE_NOT_AVAILABLE,line));
port[line] = alOpenPort("dac","w",config[line]); if (!(port[line])) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,line));
#else
device[line] = AL_DEFAULT_DEVICE;
check_quad(device[line],chans);
sr[0]=AL_OUTPUT_RATE;
sr[1]=srate;
al_err = ALsetparams(device[line],sr,2); if (al_err == -1) return(error_exit(SNDLIB_SRATE_NOT_AVAILABLE,-1));
config[line] = ALnewconfig(); if (!(config[line])) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
if ((sgi_format == -1) ||
(ALsetsampfmt(config[line],sgi_format) == -1) ||
(ALsetwidth(config[line],width) == -1)) /* this is a no-op in the float and double cases */
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,line));
al_err = ALsetchannels(config[line],chans); if (al_err == -1) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,line));
al_err = ALsetqueuesize(config[line],size); if (al_err == -1) return(error_exit(SNDLIB_SIZE_NOT_AVAILABLE,line));
port[line] = ALopenport("dac","w",config[line]); if (!(port[line])) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,line));
#endif
line_in_use[line] = 1;
return(line);
}
int write_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR; al_err = 0;
#ifdef AL_RESOURCE
al_err = alWriteFrames(port[line],(short *)buf,bytes/(channels[line]*datum_size[line]));
#else
al_err = ALwritesamps(port[line],(short *)buf,bytes/datum_size[line]);
#endif
if (al_err) {AUDIO_ERROR = SNDLIB_WRITE_ERROR; return(-1);}
return(0);
}
int close_audio(int line)
{
AUDIO_ERROR = SNDLIB_NO_ERROR; al_err = 0;
if (line_in_use[line])
{
if (line_out[line])
{
#ifdef AL_RESOURCE
while (alGetFilled(port[line]) > 0) sginap(1);
#else
while (ALgetfilled(port[line]) > 0) sginap(1);
#endif
}
#ifdef AL_RESOURCE
al_err = alClosePort(port[line]);
if (al_err == 0) al_err = alFreeConfig(config[line]);
#else
al_err = ALcloseport(port[line]);
if (al_err == 0) al_err = ALfreeconfig(config[line]);
#endif
line_in_use[line] = 0;
if (al_err) {AUDIO_ERROR = SNDLIB_CANT_CLOSE; return(-1);}
}
return(0);
}
int open_audio_input(int ur_dev, int srate, int chans, int format, int requested_size)
{
int dev;
#ifdef AL_RESOURCE
ALpv pv;
ALpv x[2];
int itf;
#else
long sr[2];
int resind;
#endif
int i,line,size;
AUDIO_ERROR = SNDLIB_NO_ERROR; al_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
line = -1;
for (i=0;i<IO_LINES;i++) if (line_in_use[i] == 0) {line=i; break;}
if (line == -1) {AUDIO_ERROR = SNDLIB_NO_LINES_AVAILABLE; return(-1);}
channels[line] = chans;
line_out[line] = 0;
datum_size[line] = mus_format2bytes(format);
if (requested_size == 0)
size = 4096;
else size = check_queue_size(requested_size,chans);
/* there are lots of ways this may be called in terms of the desired "device" */
/* in CLM, the caller specifies which device, in Snd we try to open everything available */
#ifdef AL_RESOURCE
if (dev == SNDLIB_DEFAULT_DEVICE)
device[line] = AL_DEFAULT_INPUT;
else
{
device[line] = to_al_device(dev);
itf = to_al_interface(dev);
if (itf)
{
pv.param = AL_INTERFACE;
pv.value.i = itf;
al_err = alSetParams(device[line],&pv,1); if (al_err == -1) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
}
}
if (!(device[line])) return(error_exit(SNDLIB_DEVICE_NOT_AVAILABLE,-1));
x[0].param = AL_RATE;
x[0].value.ll = alDoubleToFixed((double)srate);
x[1].param = AL_MASTER_CLOCK;
x[1].value.i = AL_MCLK_TYPE; /* AL_CRYSTAL_MCLK_TYPE; */
al_err = alSetParams(device[line],x,2); if (al_err == -1) return(error_exit(SNDLIB_SRATE_NOT_AVAILABLE,-1));
config[line] = alNewConfig(); if (!config[line]) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
if ((to_sgi_format(format) == -1) ||
(alSetSampFmt(config[line],to_sgi_format(format)) == -1) ||
(alSetWidth(config[line],(datum_size[line] == 2) ? AL_SAMPLE_16 : AL_SAMPLE_8) == -1))
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,line));
al_err = alSetChannels(config[line],chans); if (al_err == -1) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,line));
al_err = alSetQueueSize(config[line],size); if (al_err == -1) return(error_exit(SNDLIB_SIZE_NOT_AVAILABLE,line));
al_err = alSetDevice(config[line],device[line]); if (al_err == -1) return(error_exit(SNDLIB_DEVICE_NOT_AVAILABLE,line));
port[line] = alOpenPort("adc","r",config[line]); if (!(port[line])) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,line));
#else
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_READ_WRITE_DEVICE:
case SNDLIB_MICROPHONE_DEVICE: resind = AL_INPUT_MIC; break;
case SNDLIB_LINE_IN_DEVICE: resind = AL_INPUT_LINE; break;
case SNDLIB_DIGITAL_IN_DEVICE: resind = AL_INPUT_DIGITAL; break;
default: return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1)); break;
}
device[line] = AL_DEFAULT_DEVICE;
sr[0] = AL_INPUT_SOURCE;
sr[1] = resind;
al_err = ALsetparams(device[line],sr,2); if (al_err == -1) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
check_quad(device[line],chans);
sr[0] = AL_INPUT_RATE;
sr[1] = srate;
al_err = ALsetparams(device[line],sr,2); if (al_err == -1) return(error_exit(SNDLIB_SRATE_NOT_AVAILABLE,-1));
config[line] = ALnewconfig(); if (!config[line]) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
if ((to_sgi_format(format) == -1) ||
(ALsetsampfmt(config[line],to_sgi_format(format)) == -1) ||
(ALsetwidth(config[line],(datum_size[line] == 2) ? AL_SAMPLE_16 : AL_SAMPLE_8) == -1))
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,line));
al_err = ALsetchannels(config[line],chans); if (al_err == -1) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,line));
al_err = ALsetqueuesize(config[line],size); if (al_err == -1) return(error_exit(SNDLIB_SIZE_NOT_AVAILABLE,line));
port[line] = ALopenport("adc","r",config[line]); if (!(port[line])) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,line));
#endif
line_in_use[line] = 1;
return(line);
}
int read_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR; al_err = 0;
#ifdef AL_RESOURCE
al_err = alReadFrames(port[line],(short *)buf,bytes/(channels[line]*datum_size[line]));
#else
al_err = ALreadsamps(port[line],(short *)buf,bytes/datum_size[line]);
#endif
if (al_err) {AUDIO_ERROR = SNDLIB_WRITE_ERROR; return(-1);}
return(0);
}
#ifdef AL_RESOURCE
/* borrowed from /usr/share/src/dmedia/audio/printdevs.c with modifications */
#define MAX_CHANNELS 8
static float dB_to_linear(float val)
{
if (val == 0.0) return(1.0);
return(pow(10.0,val/20.0));
}
static float linear_to_dB(float val)
{
return(20.0 * log10(val));
}
static float dB_to_normalized(float val, float lo, float hi)
{
float linlo;
if (hi <= lo) return(1.0);
linlo = dB_to_linear(lo);
return((dB_to_linear(val) - linlo) / (dB_to_linear(hi) - linlo));
}
static float normalized_to_dB(float val_norm, float lo_dB, float hi_dB)
{
if (hi_dB <= lo_dB) return(0.0);
return(lo_dB + (hi_dB - lo_dB)*val_norm);
}
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
ALpv x[4];
ALparamInfo pinf;
ALfixed g[MAX_CHANNELS];
int rv,i,dev;
AUDIO_ERROR = SNDLIB_NO_ERROR; al_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
if (field != SNDLIB_DEVICE_FIELD)
{
rv = to_al_device(dev); if (!rv) return(error_exit(SNDLIB_DEVICE_NOT_AVAILABLE,-1));
}
switch (field)
{
case SNDLIB_DEVICE_FIELD:
/* in this case, chan == length of incoming val array. Number of devices is returned as val[0],
* and the rest of the available area (if needed) is filled with the device ids.
*/
/* TODO: get current AL_INPUT_SOURCE and output device and put these at the head of the list */
i = 0;
if (alGetResourceByName(AL_SYSTEM,"Microphone",AL_DEVICE_TYPE) != 0) {if ((i+1)<chan) val[i+1] = SNDLIB_MICROPHONE_DEVICE; i++;}
if (alGetResourceByName(AL_SYSTEM,"Analog Out",AL_DEVICE_TYPE) != 0) {if ((i+1)<chan) val[i+1] = SNDLIB_DAC_OUT_DEVICE; i++;}
if (alGetResourceByName(AL_SYSTEM,"ADAT In",AL_DEVICE_TYPE) != 0) {if ((i+1)<chan) val[i+1] = SNDLIB_ADAT_IN_DEVICE; i++;}
if (alGetResourceByName(AL_SYSTEM,"AES In",AL_DEVICE_TYPE) != 0) {if ((i+1)<chan) val[i+1] = SNDLIB_AES_IN_DEVICE; i++;}
if (alGetResourceByName(AL_SYSTEM,"ADAT Out",AL_DEVICE_TYPE) != 0) {if ((i+1)<chan) val[i+1] = SNDLIB_ADAT_OUT_DEVICE; i++;}
if (alGetResourceByName(AL_SYSTEM,"AES Out",AL_DEVICE_TYPE) != 0) {if ((i+1)<chan) val[i+1] = SNDLIB_AES_OUT_DEVICE; i++;}
if (alGetResourceByName(AL_SYSTEM,"Line In",AL_DEVICE_TYPE) != 0) {if ((i+1)<chan) val[i+1] = SNDLIB_LINE_IN_DEVICE; i++;}
/* if (alGetResourceByName(AL_SYSTEM,"DAC2 In",AL_DEVICE_TYPE) != 0) {if ((i+1)<chan) val[i+1] = SNDLIB_DIGITAL_IN_DEVICE; i++;} */
val[0] = i;
break;
case SNDLIB_FORMAT_FIELD: val[0] = 1; if (chan>1) val[1] = SNDLIB_16_LINEAR; break;
case SNDLIB_CHANNEL_FIELD:
x[0].param = AL_CHANNELS;
al_err = alGetParams(rv,x,1); if (al_err == -1) return(error_exit(SNDLIB_READ_ERROR,-1));
val[0] = (float)(x[0].value.i);
break;
case SNDLIB_SRATE_FIELD:
x[0].param = AL_RATE;
al_err = alGetParams(rv,x,1); if (al_err == -1) return(error_exit(SNDLIB_READ_ERROR,-1));
val[0] = (float)(x[0].value.i);
break;
case SNDLIB_AMP_FIELD:
al_err = alGetParamInfo(rv,AL_GAIN,&pinf); if (al_err == -1) return(error_exit(SNDLIB_READ_ERROR,-1));
if (pinf.min.ll == pinf.max.ll) return(error_exit(SNDLIB_AMP_NOT_AVAILABLE,-1));
/* this ridiculous thing is in dB with completely arbitrary min and max values */
x[0].param = AL_GAIN;
x[0].value.ptr = g;
x[0].sizeIn = MAX_CHANNELS;
al_err = alGetParams(rv,x,1); if (x[0].sizeOut <= chan) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,-1));
val[0] = dB_to_normalized(alFixedToDouble(g[chan]),alFixedToDouble(pinf.min.ll),alFixedToDouble(pinf.max.ll));
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
/* each field coming in assumes 0.0 to 1.0 as the range */
ALpv x[4];
ALparamInfo pinf;
ALfixed g[MAX_CHANNELS];
int rv,dev;
AUDIO_ERROR = SNDLIB_NO_ERROR; al_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
rv = to_al_device(dev); if (!rv) return(error_exit(SNDLIB_DEVICE_NOT_AVAILABLE,-1));
switch (field)
{
case SNDLIB_SRATE_FIELD:
x[0].param = AL_RATE;
x[0].value.i = (int)val[0];
x[1].param = AL_MASTER_CLOCK;
x[1].value.i = AL_CRYSTAL_MCLK_TYPE;
alSetParams(rv,x,2);
break;
case SNDLIB_AMP_FIELD:
/* need to change normalized linear value into dB between (dB) lo and hi */
al_err = alGetParamInfo(rv,AL_GAIN,&pinf); if (al_err == -1) return(error_exit(SNDLIB_READ_ERROR,-1));
/* I think we need to read all channels here, change the one we care about, then write all channels */
x[0].param = AL_GAIN;
x[0].value.ptr = g;
x[0].sizeIn = MAX_CHANNELS;
al_err = alGetParams(rv,x,1); if (x[0].sizeOut <= chan) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,-1));
g[chan] = alDoubleToFixed(normalized_to_dB(val[0],alFixedToDouble(pinf.min.ll),alFixedToDouble(pinf.max.ll)));
al_err = alSetParams(rv,x,1); if (al_err == -1) return(error_exit(SNDLIB_WRITE_ERROR,-1));
break;
case SNDLIB_DEVICE_FIELD:
/* TODO: */
/* how to set digital in??? */
AUDIO_ERROR = SNDLIB_CANT_WRITE;
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
#define STRING_SIZE 32
static void dump_resources(ALvalue *x,int rv)
{
ALpv y[4];
ALpv yy;
ALparamInfo pinf;
ALfixed g[MAX_CHANNELS];
char dn[STRING_SIZE];
char dl[STRING_SIZE];
int i,k;
ALvalue z[16];
int nres;
for (i=0;i<rv;i++)
{
y[0].param = AL_LABEL;
y[0].value.ptr = dl;
y[0].sizeIn = STRING_SIZE;
y[1].param = AL_NAME;
y[1].value.ptr = dn;
y[1].sizeIn = STRING_SIZE;
y[2].param = AL_CHANNELS;
y[3].param = AL_RATE;
alGetParams(x[i].i,y,5);
if (alIsSubtype(AL_DEVICE_TYPE, x[i].i))
{
al_err = alGetParamInfo(x[i].i,AL_GAIN,&pinf);
sprintf(strbuf,"\nDevice: %s (%s), srate: %d, chans: %d",
dn,dl,
y[3].value.i,y[2].value.i);
pprint(strbuf);
if (pinf.min.ll != pinf.max.ll)
{
yy.param = AL_GAIN;
yy.value.ptr = g;
yy.sizeIn = MAX_CHANNELS;
alGetParams(x[i].i,&yy,1);
pprint(" amps:[");
for (k=0;k<yy.sizeOut;k++)
{
sprintf(strbuf,"%.2f",dB_to_normalized(alFixedToDouble(g[k]),alFixedToDouble(pinf.min.ll),alFixedToDouble(pinf.max.ll)));
pprint(strbuf);
if (k<(yy.sizeOut-1)) pprint(" ");
}
pprint("]");
}
pprint("\n");
if ((nres= alQueryValues(x[i].i,AL_INTERFACE,z,16,0,0))>=0)
dump_resources(z,nres);
else sprintf(strbuf,"query failed: %s\n",alGetErrorString(oserror()));
pprint(strbuf);
}
else
{
sprintf(strbuf," %s (%s), chans: %d\n",dn,dl,y[2].value.i);
pprint(strbuf);
}
}
}
static void describe_audio_state_1(void)
{
int rv;
ALvalue x[16];
if (!strbuf) strbuf = (char *)CALLOC(STRBUF_SIZE,sizeof(char));
pprint("Devices and Interfaces on this system:\n");
rv= alQueryValues(AL_SYSTEM,AL_DEVICES,x,16,0,0);
if (rv>0) dump_resources(x,rv);
}
typedef struct {
int dev,has_gains;
ALfixed gains[MAX_CHANNELS];
int srate;
} saved_info;
static saved_info **si = NULL;
static int saved_devices = 0;
static int saved_devices_size = 0;
static void save_devices(ALvalue *x,int rv)
{
ALpv y;
saved_info *sv;
ALparamInfo pinf;
int i;
ALvalue z[16];
int nres;
for (i=0;i<rv;i++)
{
if (saved_devices >= saved_devices_size)
{
if (saved_devices_size == 0)
{
if (saved_devices >= 16) saved_devices_size = 2*saved_devices; else saved_devices_size = 16;
si = (saved_info **)CALLOC(saved_devices_size,sizeof(saved_info *));
}
else
{
si = (saved_info **)REALLOC(si,2*saved_devices*sizeof(saved_info *));
for (i=saved_devices_size;i<2*saved_devices;i++) si[i]=NULL;
saved_devices_size = 2*saved_devices;
}
}
si[saved_devices] = (saved_info *)CALLOC(1,sizeof(saved_info));
sv = si[saved_devices];
sv->dev = x[i].i;
y.param = AL_RATE;
alGetParams(x[i].i,&y,1);
sv->srate = y.value.i;
saved_devices++;
if (alIsSubtype(AL_DEVICE_TYPE, x[i].i))
{
alGetParamInfo(x[i].i,AL_GAIN,&pinf);
if (pinf.min.ll != pinf.max.ll)
{
y.param = AL_GAIN;
y.value.ptr = sv->gains;
y.sizeIn = MAX_CHANNELS;
alGetParams(x[i].i,&y,1);
sv->has_gains = 1;
}
else sv->has_gains = 0;
if ((nres = alQueryValues(x[i].i,AL_INTERFACE,z,16,0,0))>=0) save_devices(z,nres);
}
}
}
void save_audio_state (void)
{
int rv,i;
ALvalue x[16];
if (saved_devices>0)
{
for (i=0;i<saved_devices;i++)
{
if (si[i])
{
FREE(si[i]);
si[i] = NULL;
}
}
}
saved_devices = 0;
rv= alQueryValues(AL_SYSTEM,AL_DEVICES,x,16,0,0);
if (rv>0) save_devices(x,rv);
}
void restore_audio_state (void)
{
int i;
ALpv x;
if (saved_devices > 0)
{
for (i=0;i<saved_devices;i++)
{
if (si[i])
{
if (si[i]->has_gains)
{
x.param = AL_GAIN;
x.value.ptr = si[i]->gains;
x.sizeIn = MAX_CHANNELS;
alSetParams(si[i]->dev,&x,1);
}
x.param = AL_RATE;
x.value.i = si[i]->srate;
alSetParams(si[i]->dev,&x,1);
}
}
}
}
#else
/* old audio library */
#define MAX_VOLUME 255
static int decode_field(int dev, int field, int chan)
{
int fld = -1;
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_READ_WRITE_DEVICE:
case SNDLIB_SPEAKERS_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
fld = ((chan == 0) ? AL_LEFT_SPEAKER_GAIN : AL_RIGHT_SPEAKER_GAIN);
break;
case SNDLIB_SRATE_FIELD:
fld = AL_OUTPUT_RATE;
break;
}
break;
case SNDLIB_ADAT_IN_DEVICE: case SNDLIB_ADAT_OUT_DEVICE:
case SNDLIB_AES_IN_DEVICE: case SNDLIB_AES_OUT_DEVICE:
AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
break;
case SNDLIB_LINE_OUT_DEVICE:
switch (field)
{
case SNDLIB_SRATE_FIELD:
fld = AL_OUTPUT_RATE; /* ? */
break;
}
break;
case SNDLIB_DIGITAL_OUT_DEVICE:
if (field == SNDLIB_SRATE_FIELD)
fld = AL_OUTPUT_RATE;
else AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
break;
case SNDLIB_DIGITAL_IN_DEVICE:
if (field == SNDLIB_SRATE_FIELD)
fld = AL_INPUT_RATE;
else AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
break;
case SNDLIB_LINE_IN_DEVICE:
if (field == SNDLIB_AMP_FIELD)
fld = ((chan == 0) ? AL_LEFT_INPUT_ATTEN : AL_RIGHT_INPUT_ATTEN);
else
{
if (field == SNDLIB_SRATE_FIELD)
fld = AL_INPUT_RATE;
else AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
}
break;
case SNDLIB_MICROPHONE_DEVICE:
if (field == SNDLIB_AMP_FIELD)
fld = ((chan == 0) ? AL_LEFT2_INPUT_ATTEN : AL_RIGHT2_INPUT_ATTEN);
else
{
if (field == SNDLIB_SRATE_FIELD)
fld = AL_INPUT_RATE;
else AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
}
break;
}
return(fld);
}
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
long pb[4];
long fld;
int dev;
AUDIO_ERROR = SNDLIB_NO_ERROR; al_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
switch (field)
{
case SNDLIB_CHANNEL_FIELD:
val[0] = 2;
break;
case SNDLIB_FORMAT_FIELD: val[0] = 1; if (chan>1) val[1] = SNDLIB_16_LINEAR; break;
case SNDLIB_DEVICE_FIELD:
/* how to tell which machine we're on? */
/* TODO: put default in/out devices first */
val[0] = 4;
if (chan>1) val[1]=SNDLIB_LINE_IN_DEVICE;
if (chan>2) val[2]=SNDLIB_MICROPHONE_DEVICE;
if (chan>3) val[3]=SNDLIB_DIGITAL_IN_DEVICE;
if (chan>4) val[4]=SNDLIB_DAC_OUT_DEVICE;
/* does this order work for digital input as well? (i.e. does it replace the microphone)? */
break;
case SNDLIB_AMP_FIELD:
fld = decode_field(dev,field,chan);
if (fld != -1)
{
pb[0] = fld;
al_err = ALgetparams(AL_DEFAULT_DEVICE,pb,2);
if ((fld == AL_LEFT_SPEAKER_GAIN) || (fld == AL_RIGHT_SPEAKER_GAIN))
val[0] = ((float)pb[1])/((float)MAX_VOLUME);
else val[0] = 1.0 - ((float)pb[1])/((float)MAX_VOLUME);
if (al_err) AUDIO_ERROR = SNDLIB_READ_ERROR;
}
break;
case SNDLIB_SRATE_FIELD:
fld = decode_field(dev,field,chan);
if (fld != -1)
{
pb[0] = fld;
al_err = ALgetparams(AL_DEFAULT_DEVICE,pb,2);
val[0] = pb[1];
if (al_err) AUDIO_ERROR = SNDLIB_READ_ERROR;
}
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
long pb[4];
long fld;
int dev;
AUDIO_ERROR = SNDLIB_NO_ERROR; al_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
switch (field)
{
case SNDLIB_DEVICE_FIELD:
if (dev == SNDLIB_DEFAULT_DEVICE)
{
pb[0] = AL_CHANNEL_MODE;
pb[1] = ((chan == SNDLIB_DIGITAL_IN_DEVICE) ? AL_STEREO : AL_4CHANNEL);
pb[2] = AL_INPUT_SOURCE;
pb[3] = ((chan == SNDLIB_DIGITAL_IN_DEVICE) ? AL_INPUT_DIGITAL : AL_INPUT_MIC);
al_err = ALsetparams(AL_DEFAULT_DEVICE,pb,4);
if (al_err) AUDIO_ERROR = SNDLIB_WRITE_ERROR;
}
else AUDIO_ERROR = SNDLIB_CANT_WRITE;
break;
case SNDLIB_CHANNEL_FIELD:
if (dev == SNDLIB_MICROPHONE_DEVICE)
{
pb[0] = AL_MIC_MODE;
pb[1] = ((chan == 2) ? AL_STEREO : AL_MONO);
al_err = ALsetparams(AL_DEFAULT_DEVICE,pb,2);
if (al_err) AUDIO_ERROR = SNDLIB_WRITE_ERROR;
}
else
{
if (dev == SNDLIB_DEFAULT_DEVICE)
{
pb[0] = AL_CHANNEL_MODE;
pb[1] = ((chan == 4) ? AL_4CHANNEL : AL_STEREO);
al_err = ALsetparams(AL_DEFAULT_DEVICE,pb,2);
if (al_err) AUDIO_ERROR = SNDLIB_WRITE_ERROR;
}
else AUDIO_ERROR = SNDLIB_CANT_WRITE;
}
break;
case SNDLIB_AMP_FIELD:
fld = decode_field(dev,field,chan);
if (fld != -1)
{
pb[0] = fld;
if ((fld == AL_LEFT_SPEAKER_GAIN) || (fld == AL_RIGHT_SPEAKER_GAIN))
pb[1] = val[0] * MAX_VOLUME;
else pb[1] = (1.0 - val[0])*MAX_VOLUME;
al_err = ALsetparams(AL_DEFAULT_DEVICE,pb,2);
if (al_err) AUDIO_ERROR = SNDLIB_WRITE_ERROR;
}
else AUDIO_ERROR = SNDLIB_CANT_WRITE;
break;
case SNDLIB_SRATE_FIELD:
fld = decode_field(dev,field,chan);
if (fld != -1)
{
pb[0] = fld;
pb[1] = val[0];
al_err = ALsetparams(AL_DEFAULT_DEVICE,pb,2);
if (al_err)
AUDIO_ERROR = SNDLIB_WRITE_ERROR;
else
{
if (fld == AL_INPUT_RATE)
{
pb[0] = AL_OUTPUT_RATE;
pb[1] = val[0];
al_err = ALsetparams(AL_DEFAULT_DEVICE,pb,2);
if (al_err) AUDIO_ERROR = SNDLIB_WRITE_ERROR;
}
}
}
else AUDIO_ERROR = SNDLIB_CANT_WRITE;
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
static void describe_audio_state_1(void)
{
float amps[1];
int err;
if (!strbuf) strbuf = (char *)CALLOC(STRBUF_SIZE,sizeof(char));
err = read_audio_state(SNDLIB_SPEAKERS_DEVICE,SNDLIB_SRATE_FIELD,0,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf,"srate: %.2f\n",amps[0]); pprint(strbuf);} else {fprintf(stdout,"err: %d!\n",err); fflush(stdout);}
err = read_audio_state(SNDLIB_SPEAKERS_DEVICE,SNDLIB_AMP_FIELD,0,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf,"speakers: %.2f",amps[0]); pprint(strbuf);}
err = read_audio_state(SNDLIB_SPEAKERS_DEVICE,SNDLIB_AMP_FIELD,1,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf," %.2f\n",amps[0]); pprint(strbuf);}
err = read_audio_state(SNDLIB_LINE_IN_DEVICE,SNDLIB_AMP_FIELD,0,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf,"line in: %.2f",amps[0]); pprint(strbuf);}
err = read_audio_state(SNDLIB_LINE_IN_DEVICE,SNDLIB_AMP_FIELD,1,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf," %.2f\n",amps[0]); pprint(strbuf);}
err = read_audio_state(SNDLIB_MICROPHONE_DEVICE,SNDLIB_AMP_FIELD,0,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf,"microphone: %.2f",amps[0]); pprint(strbuf);}
err = read_audio_state(SNDLIB_MICROPHONE_DEVICE,SNDLIB_AMP_FIELD,1,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf," %.2f\n",amps[0]); pprint(strbuf);}
err = read_audio_state(SNDLIB_LINE_OUT_DEVICE,SNDLIB_AMP_FIELD,0,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf,"line out: %.2f",amps[0]); pprint(strbuf);}
err = read_audio_state(SNDLIB_LINE_OUT_DEVICE,SNDLIB_AMP_FIELD,1,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf," %.2f\n",amps[0]); pprint(strbuf);}
err = read_audio_state(SNDLIB_DIGITAL_OUT_DEVICE,SNDLIB_AMP_FIELD,0,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf,"digital out: %.2f",amps[0]); pprint(strbuf);}
err = read_audio_state(SNDLIB_DIGITAL_OUT_DEVICE,SNDLIB_AMP_FIELD,1,amps);
if (err == SNDLIB_NO_ERROR) {sprintf(strbuf," %.2f\n",amps[0]); pprint(strbuf);}
}
static long *init_state = NULL;
void save_audio_state (void)
{
/* save channel mode, input source, left/right atten (2), speaker gain, input/output rate, mic_mode */
AUDIO_ERROR = SNDLIB_NO_ERROR;
al_err = 0;
init_state = (long *)CALLOC(22,sizeof(long));
init_state[0] = AL_CHANNEL_MODE;
init_state[2] = AL_INPUT_SOURCE;
init_state[4] = AL_LEFT_INPUT_ATTEN;
init_state[6] = AL_RIGHT_INPUT_ATTEN;
init_state[8] = AL_LEFT2_INPUT_ATTEN;
init_state[10] = AL_RIGHT2_INPUT_ATTEN;
init_state[12] = AL_MIC_MODE;
init_state[14] = AL_LEFT_SPEAKER_GAIN;
init_state[16] = AL_RIGHT_SPEAKER_GAIN;
init_state[18] = AL_INPUT_RATE;
init_state[20] = AL_OUTPUT_RATE;
al_err = ALgetparams(AL_DEFAULT_DEVICE,init_state,22);
if (al_err) AUDIO_ERROR = SNDLIB_READ_ERROR;
}
void restore_audio_state (void)
{
if (init_state)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
al_err = 0;
al_err = ALsetparams(AL_DEFAULT_DEVICE,init_state,22);
if (al_err) AUDIO_ERROR = SNDLIB_WRITE_ERROR;
}
}
#endif
/* new or old AL */
#endif
/* SGI */
/* ------------------------------- OSS ----------------------------------------- */
#if HAVE_OSS
#define AUDIO_OK
#include <sys/ioctl.h>
/* the system version of the soundcard header file may have no relation to the current OSS actually loaded */
/* sys/soundcard.h is usually just a pointer to linux/soundcard.h */
#if (USR_LIB_OSS)
#include "/usr/lib/oss/include/sys/soundcard.h"
#else
#if (USR_LOCAL_LIB_OSS)
#include "/usr/local/lib/oss/include/sys/soundcard.h"
#else
#if defined(HAVE_SYS_SOUNDCARD_H) || defined(LINUX) || defined(UW2)
#include <sys/soundcard.h>
#else
#if defined(HAVE_MACHINE_SOUNDCARD_H)
#include <machine/soundcard.h>
#else
#include <soundcard.h>
#endif
#endif
#endif
#endif
#if ((SOUND_VERSION > 360) && (defined(OSS_SYSINFO)))
#define NEW_OSS
#endif
#define MAX_VOLUME 100
#define DAC_NAME "/dev/dsp"
#define MIXER_NAME "/dev/mixer"
#define SYNTH_NAME "/dev/music"
/* some programs use /dev/audio */
/* there can be more than one sound card installed, and a card can be handled through
* more than one /dev/dsp device, so we can't use a global dac device here.
* The caller has to keep track of the various cards (via AUDIO_SYSTEM) --
* I toyed with embedding all that in open_audio_output and write_audio, but
* decided it's better to keep them explicit -- the caller may want entirely
* different (non-synchronous) streams going to the various cards. This same
* code (AUDIO_SYSTEM(n)->devn) should work in Windoze (see below), and
* might work on the Mac and SGI -- something for a rainy day...
*/
static int FRAGMENTS = 4;
static int FRAGMENT_SIZE = 12;
static int fragments_locked = 0;
/* defaults here are FRAGMENTS 16 and FRAGMENT_SIZE 12; these values however
* cause about a .5 second delay, which is not acceptable in "real-time" situations.
*/
void set_oss_buffers(int num,int size) {FRAGMENTS = num; FRAGMENT_SIZE = size; fragments_locked = 1;}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
#define MAX_SOUNDCARDS 8
#define MAX_DSPS 8
#define MAX_MIXERS 8
/* there can be (apparently) any number of mixers and dsps per soundcard, but 8 is enough! */
static int *audio_fd = NULL;
static int *audio_open_ctr = NULL;
static int *audio_dsp = NULL;
static int *audio_mixer = NULL;
static int *audio_type = NULL;
static int *audio_mode = NULL;
enum {NORMAL_CARD,SONORUS_STUDIO};
/* the Sonorus Studi/o card is a special case in all regards */
static int sound_cards = 0;
static int new_oss_running = 0;
static char *dev_name = NULL;
int audio_systems(void)
{
return(sound_cards);
}
static char *mixer_name(int sys)
{
if (sys < sound_cards)
{
if (audio_mixer[sys] == -2)
return(MIXER_NAME); /* if we have /dev/dsp (not /dev/dsp0), I assume the corresponding mixer is /dev/mixer (not /dev/mixer0) */
else
{
sprintf(dev_name,"%s%d",MIXER_NAME,audio_mixer[sys]);
return(dev_name);
}
}
return(DAC_NAME);
}
char *audio_system_name(int system)
{
#ifdef NEW_OSS
static mixer_info mixinfo;
int status,ignored,fd;
fd = open(mixer_name(system),O_RDONLY,0);
if (fd != -1)
{
status = ioctl(fd,OSS_GETVERSION,&ignored);
if (status == 0)
{
status = ioctl(fd,SOUND_MIXER_INFO,&mixinfo);
if (status == 0)
{
close(fd);
return(mixinfo.name);
}
}
close(fd);
}
#endif
return("OSS");
}
char *audio_moniker(void)
{
char version[4];
if (version_name == NULL) version_name = (char *)CALLOC(16,sizeof(char));
if (SOUND_VERSION < 361)
{
sprintf(version,"%d",SOUND_VERSION);
sprintf(version_name,"OSS %c.%c.%c",version[0],version[1],version[2]);
}
else
sprintf(version_name,"OSS %x.%x.%x",(SOUND_VERSION>>16)&0xff,(SOUND_VERSION>>8)&0xff,SOUND_VERSION&0xff);
return(version_name);
}
static char *dac_name(int sys, int offset)
{
if ((sys < sound_cards) && (audio_mixer[sys] != -2))
{
sprintf(dev_name,"%s%d",DAC_NAME,audio_dsp[sys]+offset);
return(dev_name);
}
return(DAC_NAME);
}
void set_dsp_devices(int cards, int *dsps, int *mixers)
{
/* see note below -- there are still bugs in OSS multi-cards support, so sometimes the map has to be made by hand */
int i;
if (audio_dsp == NULL) initialize_audio();
if (cards > MAX_SOUNDCARDS) cards = MAX_SOUNDCARDS;
sound_cards = cards;
for (i=0;i<cards;i++)
{
audio_dsp[i] = dsps[i];
audio_mixer[i] = mixers[i];
}
}
void dsp_devices(int cards, int *dsps, int *mixers)
{
int i;
if (audio_dsp == NULL) initialize_audio();
if (cards > MAX_SOUNDCARDS) cards = MAX_SOUNDCARDS;
sound_cards = cards;
for (i=0;i<cards;i++)
{
dsps[i] = audio_dsp[i];
mixers[i] = audio_mixer[i];
}
}
#define MIXER_SIZE SOUND_MIXER_NRDEVICES
static int **mixer_state = NULL;
static int *init_srate = NULL,*init_chans = NULL,*init_format = NULL;
void set_dsp_reset(int val);
static int dsp_reset = 0; /* trying to find out if DSP_RESET is ever needed */
void set_dsp_reset(int val) {dsp_reset = val;}
int initialize_audio(void)
{
/* here we need to set up the map of /dev/dsp and /dev/mixer to a given system */
/* since this info is not passed to us by OSS, we have to work at it... */
/* for the time being, I'll ignore auxiliary dsp and mixer ports (each is a special case) */
int i,fd = -1,md,err = 0;
char dname[16];
int amp,old_mixer_amp,old_dsp_amp,new_mixer_amp,responsive_field;
int devmask;
#ifdef NEW_OSS
int status,ignored;
oss_sysinfo sysinfo;
static mixer_info mixinfo;
int sysinfo_ok = 0;
#endif
int num_mixers,num_dsps,nmix,ndsp;
if (!audio_initialized)
{
audio_initialized = 1;
AUDIO_ERROR = SNDLIB_NO_ERROR;
audio_fd = (int *)CALLOC(MAX_SOUNDCARDS,sizeof(int));
audio_open_ctr = (int *)CALLOC(MAX_SOUNDCARDS,sizeof(int));
audio_dsp = (int *)CALLOC(MAX_SOUNDCARDS,sizeof(int));
audio_mixer = (int *)CALLOC(MAX_SOUNDCARDS,sizeof(int));
audio_type = (int *)CALLOC(MAX_SOUNDCARDS,sizeof(int));
audio_mode = (int *)CALLOC(MAX_SOUNDCARDS,sizeof(int));
dev_name = (char *)CALLOC(64,sizeof(char));
init_srate = (int *)CALLOC(MAX_SOUNDCARDS,sizeof(int));
init_chans = (int *)CALLOC(MAX_SOUNDCARDS,sizeof(int));
init_format = (int *)CALLOC(MAX_SOUNDCARDS,sizeof(int));
mixer_state = (int **)CALLOC(MAX_SOUNDCARDS,sizeof(int *));
for (i=0;i<MAX_SOUNDCARDS;i++) mixer_state[i] = (int *)CALLOC(MIXER_SIZE,sizeof(int));
for (i=0;i<MAX_SOUNDCARDS;i++)
{
audio_fd[i] = -1;
audio_open_ctr[i] = 0;
audio_dsp[i] = -1;
audio_mixer[i] = -1;
audio_type[i] = NORMAL_CARD;
}
num_mixers = MAX_MIXERS;
num_dsps = MAX_DSPS;
#ifdef NEW_OSS
fd = open(DAC_NAME,O_WRONLY|O_NONBLOCK,0);
if (fd == -1) fd = open(SYNTH_NAME,O_RDONLY|O_NONBLOCK,0);
if (fd == -1) fd = open(MIXER_NAME,O_RDONLY|O_NONBLOCK,0);
if (fd != -1)
{
status = ioctl(fd,OSS_GETVERSION,&ignored);
new_oss_running = (status == 0);
if (new_oss_running)
{
status = ioctl(fd,OSS_SYSINFO,&sysinfo);
sysinfo_ok = (status == 0);
}
if ((new_oss_running) && (sysinfo_ok))
{
num_mixers = sysinfo.nummixers;
num_dsps = sysinfo.numaudios;
}
close(fd);
}
#endif
/* need to get which /dev/dsp lines match which /dev/mixer lines,
* find out how many separate systems (soundcards) are available,
* fill the audio_dsp and audio_mixer arrays with the system-related numbers,
* since we have no way to tell from OSS info which mixers/dsps are the
* main ones, we'll do some messing aound to try to deduce this info.
* for example, SB uses two dsp ports and two mixers per card, whereas
* Ensoniq uses 2 dsps and 1 mixer.
*
* the data we are gathering here:
* int audio_dsp[MAX_SOUNDCARDS] -> main_dsp_port[SNDLIB_AUDIO_SYSTEM(n)] (-1 => no such system dsp)
* int audio_mixer[MAX_SOUNDCARDS] -> main_mixer_port[SNDLIB_AUDIO_SYSTEM(n)]
* int sound_cards = 0 -> usable systems
* all auxiliary ports are currently ignored (SB equalizer, etc)
*/
sound_cards = 0;
ndsp = 0;
nmix = 0;
while ((nmix<num_mixers) && (ndsp < num_dsps))
{
/* for each mixer, find associated main dsp (assumed to be first in /dev/dsp ordering) */
/* if mixer's dsp overlaps or we run out of dsps first, ignore it (aux mixer) */
/* our by-guess-or-by-gosh method here is to try to open the mixer.
* if that fails, quit (if very first, try at least to get the dsp setup)
* find volume field, if none, go on, else read current volume
* open next unchecked dsp, try to set volume, read current, if different we found a match -- set and go on.
* if no change, move to next dsp and try again, if no more dsps, quit (checking for null case as before)
*/
sprintf(dname,"%s%d",MIXER_NAME,nmix);
md = open(dname,O_RDWR|O_NONBLOCK,0);
if (md == -1)
{
if (errno == EBUSY)
{
fprintf(stderr,"%s is busy: can't access it",dname);
nmix++;
continue;
}
else break;
}
sprintf(dname,"%s%d",DAC_NAME,ndsp);
fd = open(dname,O_RDWR|O_NONBLOCK,0);
if (fd == -1) fd = open(dname,O_RDONLY|O_NONBLOCK,0);
if (fd == -1) fd = open(dname,O_WRONLY|O_NONBLOCK,0); /* some output devices need this */
if (fd == -1)
{
close(md);
if (errno == EBUSY) /* in linux /usr/include/asm/errno.h */
{
fprintf(stderr,"%s is busy: can't access it\n",dname);
ndsp++;
continue;
}
else
{
if (errno != ENXIO && errno != ENODEV)
fprintf(stderr,"%s: %s! ",dname,strerror(errno));
break;
}
}
#ifdef NEW_OSS
/* can't change volume yet of Sonorus, so the method above won't work --
* try to catch this case via the mixer's name
*/
status = ioctl(md,SOUND_MIXER_INFO,&mixinfo);
if ((status == 0) && (mixinfo.name) && (*(mixinfo.name)) &&
(strlen(mixinfo.name) > 6) && (strncmp("STUDI/O",mixinfo.name,7) == 0))
{
/* a special case in every regard */
audio_type[sound_cards] = SONORUS_STUDIO;
audio_mixer[sound_cards] = nmix;
nmix++;
audio_dsp[sound_cards] = ndsp;
if (num_dsps >= 21)
{
ndsp+=21;
audio_mode[sound_cards] = 1;
}
else
{
ndsp+=9;
audio_mode[sound_cards] = 0;
}
sound_cards++;
close(fd);
close(md);
continue;
}
#endif
err = ioctl(md,SOUND_MIXER_READ_DEVMASK,&devmask);
responsive_field = SOUND_MIXER_VOLUME;
for (i=0;i<SOUND_MIXER_NRDEVICES;i++)
if ((1<<i) & devmask)
{
responsive_field = i;
break;
}
if (!err)
{
err = ioctl(md,MIXER_READ(responsive_field),&old_mixer_amp);
if (!err)
{
err = ioctl(fd,MIXER_READ(responsive_field),&old_dsp_amp);
if ((!err) && (old_dsp_amp == old_mixer_amp))
{
if (old_mixer_amp == 0) amp = 50; else amp = 0; /* 0..100 */
err = ioctl(fd,MIXER_WRITE(responsive_field),&);
if (!err)
{
err = ioctl(md,MIXER_READ(responsive_field),&new_mixer_amp);
if (!err)
{
if (new_mixer_amp == amp)
{
/* found one! */
audio_dsp[sound_cards] = ndsp; ndsp++;
audio_mixer[sound_cards] = nmix; nmix++;
audio_type[sound_cards] = NORMAL_CARD;
sound_cards++;
}
else ndsp++;
err = ioctl(fd,MIXER_WRITE(responsive_field),&old_dsp_amp);
}
else nmix++;
}
else ndsp++;
}
else ndsp++;
}
else nmix++;
}
else nmix++;
close(fd);
close(md);
}
if (sound_cards == 0)
{
fd = open(DAC_NAME,O_WRONLY|O_NONBLOCK,0);
if (fd != -1)
{
sound_cards = 1;
audio_dsp[0] = 0;
audio_type[0] = NORMAL_CARD;
audio_mixer[0] = -2; /* hmmm -- need a way to see /dev/dsp as lonely outpost */
close(fd);
}
}
}
return(0);
}
/* TODO: fix this to handle aux devices correctly */
static int linux_audio_open(const char *pathname, int flags, mode_t mode, int system)
{
if (audio_fd[system] == -1)
{
audio_fd[system] = open(pathname,flags,mode);
audio_open_ctr[system] = 0;
}
else audio_open_ctr[system]++;
return(audio_fd[system]);
}
static int find_system(int line)
{
int i;
for (i=0;i<MAX_SOUNDCARDS;i++)
{
if (line == audio_fd[i])
return(i);
}
return(-1);
}
static int linux_audio_close(int fd)
{
int err = 0,sys;
if (fd != -1)
{
sys = find_system(fd);
if (sys != -1)
{
if (audio_open_ctr[sys] > 0)
audio_open_ctr[sys]--;
else
{
err = close(fd);
audio_open_ctr[sys] = 0;
audio_fd[sys] = -1;
}
}
else err = close(fd);
}
return(err);
}
static int error_exit(int error, int line)
{
AUDIO_ERROR = error;
if (line != -1) linux_audio_close(line);
return(-1);
}
static int to_oss_format(int snd_format)
{
switch (snd_format)
{
case SNDLIB_8_LINEAR: return(AFMT_S8); break;
case SNDLIB_16_LINEAR: return(AFMT_S16_BE); break;
case SNDLIB_8_UNSIGNED: return(AFMT_U8); break;
case SNDLIB_8_MULAW: return(AFMT_MU_LAW); break;
case SNDLIB_8_ALAW: return(AFMT_A_LAW); break;
case SNDLIB_16_LINEAR_LITTLE_ENDIAN: return(AFMT_S16_LE); break;
case SNDLIB_16_UNSIGNED: return(AFMT_U16_BE); break;
case SNDLIB_16_UNSIGNED_LITTLE_ENDIAN: return(AFMT_U16_LE); break;
#ifdef NEW_OSS
case SNDLIB_32_LINEAR_LITTLE_ENDIAN: return(AFMT_S32_LE); break;
case SNDLIB_32_LINEAR: return(AFMT_S32_BE); break;
#endif
}
return(-1);
}
static char sonorus_buf[16];
static char *sonorus_name(int sys, int offset)
{
sprintf(sonorus_buf,"/dev/dsp%d",offset + audio_dsp[sys]);
return(sonorus_buf);
}
int open_audio_output(int ur_dev, int srate, int chans, int format, int size)
{
/* ur_dev is in general SNDLIB_AUDIO_SYSTEM(n) | SNDLIB_DEVICE */
int oss_format,buffer_info,audio_out = -1,sys,dev;
#ifndef NEW_OSS
int stereo;
#endif
AUDIO_ERROR = SNDLIB_NO_ERROR;
sys = SNDLIB_SYSTEM(ur_dev);
dev = SNDLIB_DEVICE(ur_dev);
oss_format = to_oss_format(format);
if (oss_format == -1) return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,-1));
if (audio_type[sys] == SONORUS_STUDIO)
{
/* in this case the output devices are parcelled out to the /dev/dsp locs */
/* dev/dsp0 is always stereo */
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
if (chans > 2)
audio_out = open(sonorus_name(sys,1),O_WRONLY,0);
else audio_out = open(sonorus_name(sys,0),O_WRONLY,0);
/* probably should write to both outputs */
if (audio_out == -1) audio_out = open("/dev/dsp",O_WRONLY,0);
break;
case SNDLIB_SPEAKERS_DEVICE:
audio_out = open(sonorus_name(sys,0),O_WRONLY,0);
if (audio_out == -1) audio_out = open("/dev/dsp",O_WRONLY,0);
break;
case SNDLIB_ADAT_OUT_DEVICE: case SNDLIB_SPDIF_OUT_DEVICE:
audio_out = open(sonorus_name(sys,1),O_WRONLY,0);
break;
case SNDLIB_AES_OUT_DEVICE:
audio_out = open(sonorus_name(sys,9),O_WRONLY,0);
break;
default:
return(error_exit(SNDLIB_DEVICE_NOT_AVAILABLE,audio_out));
break;
}
if (audio_out == -1) return(error_exit(SNDLIB_CANT_OPEN,audio_out));
if (ioctl(audio_out,SNDCTL_DSP_CHANNELS,&chans) == -1) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,audio_out));
return(audio_out);
}
if (dev == SNDLIB_DEFAULT_DEVICE)
audio_out = linux_audio_open(dac_name(sys,0),O_WRONLY,0,sys);
else audio_out = linux_audio_open(dac_name(sys,(dev == SNDLIB_AUX_OUTPUT_DEVICE) ? 1 : 0),O_RDWR,0,sys);
if (audio_out == -1) return(error_exit(SNDLIB_CANT_OPEN,audio_out));
/* ioctl(audio_out,SNDCTL_DSP_RESET,0); */ /* causes clicks */
if ((dev == SNDLIB_READ_WRITE_DEVICE) || (size != 0))
{
if (!fragments_locked) {if (srate > 30000) FRAGMENTS = 4; else FRAGMENTS = 2;}
buffer_info = (FRAGMENTS<<16) | (FRAGMENT_SIZE);
if (ioctl(audio_out,SNDCTL_DSP_SETFRAGMENT,&buffer_info) == -1)
{
/* older Linuces (or OSS's?) refuse to handle the fragment reset if O_RDWR used --
* someone at OSS forgot to update the version number when this was fixed, so
* I have no way to get around this except to try and retry...
*/
linux_audio_close(audio_out);
audio_out = linux_audio_open(dac_name(sys,(dev == SNDLIB_AUX_OUTPUT_DEVICE) ? 1 : 0),O_WRONLY,0,sys);
if (audio_out == -1) return(error_exit(SNDLIB_CANT_OPEN,audio_out));
buffer_info = (FRAGMENTS<<16) | (FRAGMENT_SIZE);
if (ioctl(audio_out,SNDCTL_DSP_SETFRAGMENT,&buffer_info) == -1) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,audio_out));
}
}
if ((ioctl(audio_out,SNDCTL_DSP_SETFMT,&oss_format) == -1) || (oss_format != to_oss_format(format)))
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,audio_out));
#ifdef NEW_OSS
if (ioctl(audio_out,SNDCTL_DSP_CHANNELS,&chans) == -1) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,audio_out));
#else
if (chans == 2) stereo = 1; else stereo = 0;
if ((ioctl(audio_out,SNDCTL_DSP_STEREO,&stereo) == -1) || ((chans == 2) && (stereo == 0))) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,audio_out));
#endif
if (ioctl(audio_out,SNDCTL_DSP_SPEED,&srate) == -1) return(error_exit(SNDLIB_SRATE_NOT_AVAILABLE,audio_out));
/* http://www.4front-tech.com/pguide/audio.html says this order has to be followed */
return(audio_out);
}
int write_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
write(line,buf,bytes);
return(0);
}
int close_audio(int line)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
return(linux_audio_close(line));
}
int read_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
read(line,buf,bytes);
return(0);
}
int open_audio_input(int ur_dev, int srate, int chans, int format, int requested_size)
{
/* dev can be SNDLIB_DEFAULT_DEVICE or SNDLIB_READ_WRITE_DEVICE as well as the obvious others */
int audio_fd = -1,oss_format,buffer_info,sys,dev,srcbit,cursrc,adat_mode = 0,err;
#ifndef NEW_OSS
int stereo;
#endif
AUDIO_ERROR = SNDLIB_NO_ERROR;
sys = SNDLIB_SYSTEM(ur_dev);
dev = SNDLIB_DEVICE(ur_dev);
oss_format = to_oss_format(format);
if (oss_format == -1) return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,-1));
if (audio_type[sys] == SONORUS_STUDIO)
{
adat_mode = (audio_mode[sys] == 1);
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
if (adat_mode)
audio_fd = open(sonorus_name(sys,11),O_RDONLY,0);
else audio_fd = open(sonorus_name(sys,5),O_RDONLY,0);
break;
case SNDLIB_ADAT_IN_DEVICE:
audio_fd = open(sonorus_name(sys,11),O_RDONLY,0);
break;
case SNDLIB_AES_IN_DEVICE:
audio_fd = open(sonorus_name(sys,20),O_RDONLY,0);
break;
case SNDLIB_SPDIF_IN_DEVICE:
audio_fd = open(sonorus_name(sys,5),O_RDONLY,0);
break;
default:
return(error_exit(SNDLIB_DEVICE_NOT_AVAILABLE,audio_fd));
break;
}
if (audio_fd == -1) return(error_exit(SNDLIB_NO_INPUT_AVAILABLE,-1));
if (ioctl(audio_fd,SNDCTL_DSP_CHANNELS,&chans) == -1) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,audio_fd));
return(audio_fd);
}
if (((dev == SNDLIB_DEFAULT_DEVICE) || (dev == SNDLIB_READ_WRITE_DEVICE)) && (sys == 0))
audio_fd = linux_audio_open(dac_name(sys,(dev == SNDLIB_AUX_INPUT_DEVICE) ? 1 : 0),O_RDWR,0,sys);
else audio_fd = linux_audio_open(dac_name(sys,(dev == SNDLIB_AUX_INPUT_DEVICE) ? 1 : 0),O_RDONLY,0,sys);
if (audio_fd == -1)
{
if (dev == SNDLIB_READ_WRITE_DEVICE) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
if ((audio_fd = linux_audio_open(dac_name(sys,(dev == SNDLIB_AUX_INPUT_DEVICE) ? 1 : 0),O_RDONLY,0,sys)) == -1)
{
if ((errno == EACCES) || (errno == ENOENT))
{
fprintf(stdout,"(to get input in Linux, we need read permission on /dev/dsp)");
fflush(stdout);
return(error_exit(SNDLIB_NO_READ_PERMISSION,-1));
}
return(error_exit(SNDLIB_NO_INPUT_AVAILABLE,-1));
}
}
else
{
err = 0;
err = ioctl(audio_fd,SNDCTL_DSP_SETDUPLEX,&err); /* not always a no-op! */
/* if (err == -1) AUDIO_ERROR = SNDLIB_NO_OUTPUT_AVAILABLE; */
/* this damned thing returns -1 even when full duplex is available */
}
/* need to make sure the desired recording source is active -- does this actually have any effect? */
switch (dev)
{
case SNDLIB_MICROPHONE_DEVICE: srcbit = SOUND_MASK_MIC; break;
case SNDLIB_LINE_IN_DEVICE: srcbit = SOUND_MASK_LINE; break;
case SNDLIB_LINE1_DEVICE: srcbit = SOUND_MASK_LINE1; break;
case SNDLIB_LINE2_DEVICE: srcbit = SOUND_MASK_LINE2; break;
case SNDLIB_LINE3_DEVICE: srcbit = SOUND_MASK_LINE3; break; /* also digital1..3 */
case SNDLIB_READ_WRITE_DEVICE:
case SNDLIB_DEFAULT_DEVICE: srcbit = SOUND_MASK_LINE | SOUND_MASK_MIC; break;
case SNDLIB_CD_IN_DEVICE: srcbit = SOUND_MASK_CD; break;
default: srcbit = 0; break;
/* other possibilities: synth, radio, phonein but these apparently bypass the mixer (no gains?) */
}
ioctl(audio_fd,MIXER_READ(SOUND_MIXER_RECSRC),&cursrc);
srcbit = (srcbit | cursrc);
ioctl(audio_fd,MIXER_WRITE(SOUND_MIXER_RECSRC),&srcbit);
if (dsp_reset) ioctl(audio_fd,SNDCTL_DSP_RESET,0);
if (requested_size != 0)
{
buffer_info = (FRAGMENTS<<16) | (FRAGMENT_SIZE);
ioctl(audio_fd,SNDCTL_DSP_SETFRAGMENT,&buffer_info);
}
if ((ioctl(audio_fd,SNDCTL_DSP_SETFMT,&oss_format) == -1) || (oss_format != to_oss_format(format)))
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,audio_fd));
#ifdef NEW_OSS
if (ioctl(audio_fd,SNDCTL_DSP_CHANNELS,&chans) == -1) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,audio_fd));
#else
if (chans == 2) stereo = 1; else stereo = 0;
if ((ioctl(audio_fd,SNDCTL_DSP_STEREO,&stereo) == -1) || ((chans == 2) && (stereo == 0))) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,audio_fd));
#endif
if (ioctl(audio_fd,SNDCTL_DSP_SPEED,&srate) == -1) return(error_exit(SNDLIB_SRATE_NOT_AVAILABLE,audio_fd));
return(audio_fd);
}
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
int fd,amp,channels,err,devmask,stereodevs,ind,formats,sys,dev,srate,adat_mode;
AUDIO_ERROR = SNDLIB_NO_ERROR;
sys = SNDLIB_SYSTEM(ur_dev);
dev = SNDLIB_DEVICE(ur_dev);
if (audio_type[sys] == SONORUS_STUDIO)
{
adat_mode = (audio_mode[sys] == 1);
if (dev == SNDLIB_MIXER_DEVICE) val[0]=0; /* no mixer */
else
{
if (field == SNDLIB_DEVICE_FIELD)
{
if (adat_mode)
{
val[0] = 5;
val[1] = SNDLIB_ADAT_IN_DEVICE;
val[2] = SNDLIB_ADAT_OUT_DEVICE;
val[3] = SNDLIB_SPEAKERS_DEVICE;
val[4] = SNDLIB_AES_IN_DEVICE;
val[5] = SNDLIB_AES_OUT_DEVICE;
}
else
{
val[0] = 3;
val[1] = SNDLIB_SPDIF_IN_DEVICE;
val[2] = SNDLIB_SPDIF_OUT_DEVICE;
val[3] = SNDLIB_SPEAKERS_DEVICE;
}
}
else
{
if (field == SNDLIB_FORMAT_FIELD)
{
val[0] = 1;
val[1] = SNDLIB_16_LINEAR_LITTLE_ENDIAN;
}
else
{
if (field == SNDLIB_CHANNEL_FIELD)
{
switch (dev)
{
case SNDLIB_SPEAKERS_DEVICE: channels = 2; break;
case SNDLIB_ADAT_IN_DEVICE: case SNDLIB_ADAT_OUT_DEVICE: channels = 8; break;
case SNDLIB_AES_IN_DEVICE: case SNDLIB_AES_OUT_DEVICE: channels = 2; break;
case SNDLIB_SPDIF_IN_DEVICE: case SNDLIB_SPDIF_OUT_DEVICE: channels = 4; break;
case SNDLIB_DEFAULT_DEVICE: if (adat_mode) channels = 8; else channels = 4; break;
default: channels = 0; break;
}
val[0] = channels;
}
else
{
if (field == SNDLIB_SRATE_FIELD)
{
val[0] = 44100;
}
}
}
}
}
return(SNDLIB_NO_ERROR);
}
fd = linux_audio_open(mixer_name(sys),O_RDONLY | O_NONBLOCK,0,sys);
if (fd == -1)
{
fd = linux_audio_open(DAC_NAME,O_RDONLY,0,sys);
if (fd == -1)
{
fd = linux_audio_open(DAC_NAME,O_WRONLY,0,sys);
if (fd == -1) return(error_exit(SNDLIB_CANT_OPEN,-1));
}
}
err = ioctl(fd,SOUND_MIXER_READ_DEVMASK,&devmask);
if (err) return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,fd));
if ((dev == SNDLIB_MIXER_DEVICE) || (dev == SNDLIB_DAC_FILTER_DEVICE)) /* these give access to all the on-board analog input gain controls */
{
amp = 0;
err = ioctl(fd,SOUND_MIXER_READ_DEVMASK,&devmask);
switch (field)
{
/* also DIGITAL1..3 PHONEIN PHONEOUT VIDEO RADIO MONITOR */
/* the digital lines should get their own panes in the recorder */
/* not clear whether the phone et al lines are routed to the ADC */
/* also, I've never seen a card with any of these devices */
case SNDLIB_IMIX_FIELD: if (SOUND_MASK_IMIX & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_IMIX),&); break;
case SNDLIB_IGAIN_FIELD: if (SOUND_MASK_IGAIN & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_IGAIN),&); break;
case SNDLIB_RECLEV_FIELD: if (SOUND_MASK_RECLEV & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_RECLEV),&); break;
case SNDLIB_PCM_FIELD: if (SOUND_MASK_PCM & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_PCM),&); break;
case SNDLIB_PCM2_FIELD: if (SOUND_MASK_ALTPCM & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_ALTPCM),&); break;
case SNDLIB_OGAIN_FIELD: if (SOUND_MASK_OGAIN & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_OGAIN),&); break;
case SNDLIB_LINE_FIELD: if (SOUND_MASK_LINE & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_LINE),&); break;
case SNDLIB_MIC_FIELD: if (SOUND_MASK_MIC & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_MIC),&); break;
case SNDLIB_LINE1_FIELD: if (SOUND_MASK_LINE1 & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_LINE1),&); break;
case SNDLIB_LINE2_FIELD: if (SOUND_MASK_LINE2 & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_LINE2),&); break;
case SNDLIB_LINE3_FIELD: if (SOUND_MASK_LINE3 & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_LINE3),&); break;
case SNDLIB_SYNTH_FIELD: if (SOUND_MASK_SYNTH & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_SYNTH),&); break;
case SNDLIB_BASS_FIELD: if (SOUND_MASK_BASS & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_BASS),&); break;
case SNDLIB_TREBLE_FIELD: if (SOUND_MASK_TREBLE & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_TREBLE),&); break;
case SNDLIB_CD_FIELD: if (SOUND_MASK_CD & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_CD),&); break;
case SNDLIB_CHANNEL_FIELD:
if (dev == SNDLIB_MIXER_DEVICE)
{
channels = 0;
err = ioctl(fd,SOUND_MIXER_READ_STEREODEVS,&stereodevs);
if (SOUND_MASK_IMIX & devmask) {if (SOUND_MASK_IMIX & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_IGAIN & devmask) {if (SOUND_MASK_IGAIN & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_RECLEV & devmask) {if (SOUND_MASK_RECLEV & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_PCM & devmask) {if (SOUND_MASK_PCM & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_ALTPCM & devmask) {if (SOUND_MASK_ALTPCM & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_OGAIN & devmask) {if (SOUND_MASK_OGAIN & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_LINE & devmask) {if (SOUND_MASK_LINE & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_MIC & devmask) {if (SOUND_MASK_MIC & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_LINE1 & devmask) {if (SOUND_MASK_LINE1 & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_LINE2 & devmask) {if (SOUND_MASK_LINE2 & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_LINE3 & devmask) {if (SOUND_MASK_LINE3 & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_SYNTH & devmask) {if (SOUND_MASK_SYNTH & stereodevs) channels += 2; else channels += 1;}
if (SOUND_MASK_CD & devmask) {if (SOUND_MASK_CD & stereodevs) channels += 2; else channels += 1;}
}
else
if (SOUND_MASK_TREBLE & devmask) channels = 2; else channels = 0;
val[0] = channels;
linux_audio_close(fd);
return(0);
break;
case SNDLIB_FORMAT_FIELD: /* this is asking for configuration info -- we return an array with per-"device" channels */
err = ioctl(fd,SOUND_MIXER_READ_STEREODEVS,&stereodevs);
for (ind=0;ind<=SNDLIB_SYNTH_FIELD;ind++) {if (chan>ind) val[ind]=0;}
if (SOUND_MASK_IMIX & devmask) {if (chan>SNDLIB_IMIX_FIELD) val[SNDLIB_IMIX_FIELD] = ((SOUND_MASK_IMIX & stereodevs) ? 2 : 1);}
if (SOUND_MASK_IGAIN & devmask) {if (chan>SNDLIB_IGAIN_FIELD) val[SNDLIB_IGAIN_FIELD] = ((SOUND_MASK_IGAIN & stereodevs) ? 2 : 1);}
if (SOUND_MASK_RECLEV & devmask) {if (chan>SNDLIB_RECLEV_FIELD) val[SNDLIB_RECLEV_FIELD] = ((SOUND_MASK_RECLEV & stereodevs) ? 2 : 1);}
if (SOUND_MASK_PCM & devmask) {if (chan>SNDLIB_PCM_FIELD) val[SNDLIB_PCM_FIELD] = ((SOUND_MASK_PCM & stereodevs) ? 2 : 1);}
if (SOUND_MASK_ALTPCM & devmask) {if (chan>SNDLIB_PCM2_FIELD) val[SNDLIB_PCM2_FIELD] = ((SOUND_MASK_ALTPCM & stereodevs) ? 2 : 1);}
if (SOUND_MASK_OGAIN & devmask) {if (chan>SNDLIB_OGAIN_FIELD) val[SNDLIB_OGAIN_FIELD] = ((SOUND_MASK_OGAIN & stereodevs) ? 2 : 1);}
if (SOUND_MASK_LINE & devmask) {if (chan>SNDLIB_LINE_FIELD) val[SNDLIB_LINE_FIELD] = ((SOUND_MASK_LINE & stereodevs) ? 2 : 1);}
if (SOUND_MASK_MIC & devmask) {if (chan>SNDLIB_MIC_FIELD) val[SNDLIB_MIC_FIELD] = ((SOUND_MASK_MIC & stereodevs) ? 2 : 1);}
if (SOUND_MASK_LINE1 & devmask) {if (chan>SNDLIB_LINE1_FIELD) val[SNDLIB_LINE1_FIELD] = ((SOUND_MASK_LINE1 & stereodevs) ? 2 : 1);}
if (SOUND_MASK_LINE2 & devmask) {if (chan>SNDLIB_LINE2_FIELD) val[SNDLIB_LINE2_FIELD] = ((SOUND_MASK_LINE2 & stereodevs) ? 2 : 1);}
if (SOUND_MASK_LINE3 & devmask) {if (chan>SNDLIB_LINE3_FIELD) val[SNDLIB_LINE3_FIELD] = ((SOUND_MASK_LINE3 & stereodevs) ? 2 : 1);}
if (SOUND_MASK_SYNTH & devmask) {if (chan>SNDLIB_SYNTH_FIELD) val[SNDLIB_SYNTH_FIELD] = ((SOUND_MASK_SYNTH & stereodevs) ? 2 : 1);}
if (SOUND_MASK_CD & devmask) {if (chan>SNDLIB_CD_FIELD) val[SNDLIB_CD_FIELD] = ((SOUND_MASK_CD & stereodevs) ? 2 : 1);}
linux_audio_close(fd);
return(0);
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
if (chan == 0)
val[0] = ((float)(amp & 0xff))*0.01;
else val[0] = (((float)((amp & 0xff00) >> 8))*0.01);
}
else
{
switch (field)
{
case SNDLIB_DEVICE_FIELD:
ind = 1;
val[1] = SNDLIB_MIXER_DEVICE;
if ((SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_CD) & devmask) {ind++; if (chan>ind) val[ind] = SNDLIB_LINE_IN_DEVICE;}
/* problem here is that microphone and line_in are mixed before the ADC */
if (SOUND_MASK_SPEAKER & devmask) {ind++; if (chan>ind) val[ind] = SNDLIB_SPEAKERS_DEVICE;}
if (SOUND_MASK_VOLUME & devmask) {ind++; if (chan>ind) val[ind] = SNDLIB_DAC_OUT_DEVICE;}
if (SOUND_MASK_TREBLE & devmask) {ind++; if (chan>ind) val[ind] = SNDLIB_DAC_FILTER_DEVICE;}
/* DIGITAL1..3 as RECSRC(?) => SNDLIB_DIGITAL_IN_DEVICE */
val[0] = ind;
break;
case SNDLIB_FORMAT_FIELD:
err = ioctl(fd,SOUND_PCM_GETFMTS,&formats);
ind = 0;
if (formats & (to_oss_format(SNDLIB_8_LINEAR))) {ind++; if (chan>ind) val[ind] = SNDLIB_8_LINEAR;}
if (formats & (to_oss_format(SNDLIB_16_LINEAR))) {ind++; if (chan>ind) val[ind] = SNDLIB_16_LINEAR;}
if (formats & (to_oss_format(SNDLIB_8_UNSIGNED))) {ind++; if (chan>ind) val[ind] = SNDLIB_8_UNSIGNED;}
if (formats & (to_oss_format(SNDLIB_8_MULAW))) {ind++; if (chan>ind) val[ind] = SNDLIB_8_MULAW;}
if (formats & (to_oss_format(SNDLIB_8_ALAW))) {ind++; if (chan>ind) val[ind] = SNDLIB_8_ALAW;}
if (formats & (to_oss_format(SNDLIB_16_LINEAR_LITTLE_ENDIAN))) {ind++; if (chan>ind) val[ind] = SNDLIB_16_LINEAR_LITTLE_ENDIAN;}
if (formats & (to_oss_format(SNDLIB_16_UNSIGNED))) {ind++; if (chan>ind) val[ind] = SNDLIB_16_UNSIGNED;}
if (formats & (to_oss_format(SNDLIB_16_UNSIGNED_LITTLE_ENDIAN))) {ind++; if (chan>ind) val[ind] = SNDLIB_16_UNSIGNED_LITTLE_ENDIAN;}
val[0] = ind;
break;
case SNDLIB_CHANNEL_FIELD:
err = ioctl(fd,SOUND_MIXER_READ_STEREODEVS,&stereodevs);
channels = 0;
switch (dev)
{
case SNDLIB_MICROPHONE_DEVICE: if (SOUND_MASK_MIC & devmask) {if (SOUND_MASK_MIC & stereodevs) channels = 2; else channels = 1;} break;
case SNDLIB_SPEAKERS_DEVICE: if (SOUND_MASK_SPEAKER & devmask) {if (SOUND_MASK_SPEAKER & stereodevs) channels = 2; else channels = 1;} break;
case SNDLIB_LINE_IN_DEVICE: if (SOUND_MASK_LINE & devmask) {if (SOUND_MASK_LINE & stereodevs) channels = 2; else channels = 1;} break;
case SNDLIB_LINE1_DEVICE: if (SOUND_MASK_LINE1 & devmask) {if (SOUND_MASK_LINE1 & stereodevs) channels = 2; else channels = 1;} break;
case SNDLIB_LINE2_DEVICE: if (SOUND_MASK_LINE2 & devmask) {if (SOUND_MASK_LINE2 & stereodevs) channels = 2; else channels = 1;} break;
case SNDLIB_LINE3_DEVICE: if (SOUND_MASK_LINE3 & devmask) {if (SOUND_MASK_LINE3 & stereodevs) channels = 2; else channels = 1;} break;
case SNDLIB_DAC_OUT_DEVICE: if (SOUND_MASK_VOLUME & devmask) {if (SOUND_MASK_VOLUME & stereodevs) channels = 2; else channels = 1;} break;
case SNDLIB_DEFAULT_DEVICE: if (SOUND_MASK_VOLUME & devmask) {if (SOUND_MASK_VOLUME & stereodevs) channels = 2; else channels = 1;} break;
case SNDLIB_CD_IN_DEVICE: if (SOUND_MASK_CD & devmask) {if (SOUND_MASK_CD & stereodevs) channels = 2; else channels = 1;} break;
case SNDLIB_READ_WRITE_DEVICE:
err = ioctl(fd,SNDCTL_DSP_GETCAPS,&ind);
if (err != -1)
channels = (ind & DSP_CAP_DUPLEX);
else channels = 0;
break;
default: AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE; break;
}
val[0] = channels;
break;
case SNDLIB_AMP_FIELD:
amp = 0;
switch (dev)
{
case SNDLIB_MICROPHONE_DEVICE: if (SOUND_MASK_MIC & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_MIC),&); break;
case SNDLIB_SPEAKERS_DEVICE: if (SOUND_MASK_SPEAKER & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_SPEAKER),&); break;
case SNDLIB_LINE_IN_DEVICE: if (SOUND_MASK_LINE & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_LINE),&); break;
case SNDLIB_LINE1_DEVICE: if (SOUND_MASK_LINE1 & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_LINE1),&); break;
case SNDLIB_LINE2_DEVICE: if (SOUND_MASK_LINE2 & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_LINE2),&); break;
case SNDLIB_LINE3_DEVICE: if (SOUND_MASK_LINE3 & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_LINE3),&); break;
case SNDLIB_DAC_OUT_DEVICE: if (SOUND_MASK_VOLUME & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_VOLUME),&); break;
case SNDLIB_DEFAULT_DEVICE: if (SOUND_MASK_VOLUME & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_VOLUME),&); break;
case SNDLIB_CD_IN_DEVICE: if (SOUND_MASK_CD & devmask) err = ioctl(fd,MIXER_READ(SOUND_MIXER_CD),&); break;
default: AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE; break;
}
if (chan == 0)
val[0] = ((float)(amp & 0xff))*0.01;
else val[0] = (((float)((amp & 0xff00) >> 8))*0.01);
break;
case SNDLIB_SRATE_FIELD:
srate = (int)(val[0]);
if (ioctl(fd,SNDCTL_DSP_SPEED,&srate) == -1) AUDIO_ERROR = SNDLIB_SRATE_NOT_AVAILABLE;
val[0] = (float)srate;
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
}
linux_audio_close(fd);
if (err) {AUDIO_ERROR = SNDLIB_READ_ERROR; return(-1);}
return(0);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
int fd,err = 0,devmask,vol,sys,dev;
float amp[1];
AUDIO_ERROR = SNDLIB_NO_ERROR;
sys = SNDLIB_SYSTEM(ur_dev);
dev = SNDLIB_DEVICE(ur_dev);
if (audio_type[sys] == SONORUS_STUDIO) return(SNDLIB_NO_ERROR); /* there are apparently volume controls, but they're not acecssible yet */
fd = linux_audio_open(mixer_name(sys),O_RDWR | O_NONBLOCK,0,sys);
if (fd == -1)
{
fd = linux_audio_open(DAC_NAME,O_WRONLY,0,sys);
if (fd == -1) return(error_exit(SNDLIB_CANT_OPEN,-1));
}
if ((dev == SNDLIB_MIXER_DEVICE) || (dev == SNDLIB_DAC_FILTER_DEVICE)) /* these give access to all the on-board analog input gain controls */
{
read_audio_state(ur_dev,field,(chan == 0) ? 1 : 0,amp);
if (val[0] >= 0.99) val[0] = 0.99; if (val[0] < 0.0) val[0] = 0.0;
if (amp[0] >= 0.99) amp[0] = 0.99;
if (chan == 0)
vol = (((int)(amp[0]*100)) << 8) + ((int)(val[0]*100));
else vol = (((int)(val[0]*100)) << 8) + ((int)(amp[0]*100));
err = ioctl(fd,SOUND_MIXER_READ_DEVMASK,&devmask);
switch (field)
{
case SNDLIB_IMIX_FIELD: if (SOUND_MASK_IMIX & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_IMIX),&vol); break;
case SNDLIB_IGAIN_FIELD: if (SOUND_MASK_IGAIN & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_IGAIN),&vol); break;
case SNDLIB_RECLEV_FIELD: if (SOUND_MASK_RECLEV & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_RECLEV),&vol); break;
case SNDLIB_PCM_FIELD: if (SOUND_MASK_PCM & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_PCM),&vol); break;
case SNDLIB_PCM2_FIELD: if (SOUND_MASK_ALTPCM & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_ALTPCM),&vol); break;
case SNDLIB_OGAIN_FIELD: if (SOUND_MASK_OGAIN & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_OGAIN),&vol); break;
case SNDLIB_LINE_FIELD: if (SOUND_MASK_LINE & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_LINE),&vol); break;
case SNDLIB_MIC_FIELD: if (SOUND_MASK_MIC & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_MIC),&vol); break;
case SNDLIB_LINE1_FIELD: if (SOUND_MASK_LINE1 & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_LINE1),&vol); break;
case SNDLIB_LINE2_FIELD: if (SOUND_MASK_LINE2 & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_LINE2),&vol); break;
case SNDLIB_LINE3_FIELD: if (SOUND_MASK_LINE3 & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_LINE3),&vol); break;
case SNDLIB_SYNTH_FIELD: if (SOUND_MASK_SYNTH & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_SYNTH),&vol); break;
case SNDLIB_BASS_FIELD: if (SOUND_MASK_BASS & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_BASS),&vol); break;
case SNDLIB_TREBLE_FIELD: if (SOUND_MASK_TREBLE & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_TREBLE),&vol); break;
case SNDLIB_CD_FIELD: if (SOUND_MASK_CD & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_CD),&vol); break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
}
else
{
switch (field)
{
case SNDLIB_AMP_FIELD:
/* need to read both channel amps, then change the one we're concerned with */
read_audio_state(ur_dev,field,(chan == 0) ? 1 : 0,amp);
if (val[0] >= 0.99) val[0] = 0.99; if (val[0] < 0.0) val[0] = 0.0;
if (amp[0] >= 0.99) amp[0] = 0.99;
if (chan == 0)
vol = (((int)(amp[0]*100)) << 8) + ((int)(val[0]*100));
else vol = (((int)(val[0]*100)) << 8) + ((int)(amp[0]*100));
err = ioctl(fd,SOUND_MIXER_READ_DEVMASK,&devmask);
switch (dev)
{
case SNDLIB_MICROPHONE_DEVICE: if (SOUND_MASK_MIC & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_MIC),&vol); break;
case SNDLIB_SPEAKERS_DEVICE: if (SOUND_MASK_SPEAKER & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_SPEAKER),&vol); break;
case SNDLIB_LINE_IN_DEVICE: if (SOUND_MASK_LINE & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_LINE),&vol); break;
case SNDLIB_LINE1_DEVICE: if (SOUND_MASK_LINE1 & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_LINE1),&vol); break;
case SNDLIB_LINE2_DEVICE: if (SOUND_MASK_LINE2 & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_LINE2),&vol); break;
case SNDLIB_LINE3_DEVICE: if (SOUND_MASK_LINE3 & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_LINE3),&vol); break;
case SNDLIB_DAC_OUT_DEVICE: if (SOUND_MASK_VOLUME & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_VOLUME),&vol); break;
case SNDLIB_DEFAULT_DEVICE: if (SOUND_MASK_VOLUME & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_VOLUME),&vol); break;
case SNDLIB_CD_IN_DEVICE: if (SOUND_MASK_CD & devmask) err = ioctl(fd,MIXER_WRITE(SOUND_MIXER_CD),&vol); break;
default: AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE; break;
}
break;
case SNDLIB_SRATE_FIELD:
vol = (int)val[0];
if (dsp_reset) ioctl(fd,SNDCTL_DSP_RESET,0); /* is this needed? */
err = ioctl(fd,SNDCTL_DSP_SPEED,&vol);
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
/* case SNDLIB_FORMAT_FIELD: to force 16-bit input or give up */
/* case SNDLIB_CHANNEL_FIELD: to open as stereo if possible?? */
/* case SNDLIB_DEVICE_FIELD: to open digital out? */
}
}
linux_audio_close(fd);
if (err) {AUDIO_ERROR = SNDLIB_WRITE_ERROR; return(-1);}
return(0);
}
static char *synth_names[] =
{"",
"Adlib","SoundBlaster","ProAudio Spectrum","Gravis UltraSound","MPU 401",
"SoundBlaster 16","SoundBlaster 16 MIDI","6850 UART","Gravis UltraSound 16","Microsoft",
"Personal sound system","Ensoniq Soundscape","Personal sound system + MPU","Personal/Microsoft",
"Mediatrix Pro","MAD16","MAD16 + MPU","CS4232","CS4232 + MPU","Maui",
"Pseudo-MSS","Gravis Ultrasound PnP","UART 401"};
static char *synth_name(int i)
{
if ((i>0) && (i<=SNDCARD_UART401))
return(synth_names[i]);
return("unknown");
}
static char *device_types[] = {"FM","Sampling","MIDI"};
static char *device_type(int i)
{
if ((i>=0) && (i<=2))
return(device_types[i]);
return("unknown");
}
static void yes_no (int condition)
{
if (condition)
pprint(" yes ");
else pprint(" no ");
}
static int set_dsp(int fd, int channels, int bits, int *rate)
{
int val;
val = channels;
ioctl(fd,SOUND_PCM_WRITE_CHANNELS,&val);
if (val != channels) return(-1);
val = bits;
ioctl(fd,SOUND_PCM_WRITE_BITS,&val);
if (val != bits) return(-1);
ioctl(fd,SOUND_PCM_WRITE_RATE,rate);
return(0);
}
static void describe_audio_state_1(void)
{
/* this code taken largely from "Linux Multimedia Guide" by Jeff Tranter, O'Reilly & Associates, Inc 1996 */
/* it is explicitly released under the GPL, so I think I can use it here without elaborate disguises */
int fd;
int status = 0,level,i,recsrc,devmask,recmask,stereodevs,caps,numdevs,rate,channels,bits,blocksize,formats,deffmt,min_rate,max_rate;
struct synth_info sinfo;
struct midi_info minfo;
const char *sound_device_names[] = SOUND_DEVICE_LABELS;
char dsp_name[16];
char version[4];
int dsp_num = 0;
#ifdef NEW_OSS
mixer_info mixinfo;
oss_sysinfo sysinfo;
#endif
if (sound_cards <= 0) initialize_audio();
#ifdef NEW_OSS
fd = open(DAC_NAME,O_WRONLY,0);
if (fd == -1) fd = open(SYNTH_NAME,O_RDONLY,0);
if (fd == -1) fd = open(MIXER_NAME,O_RDONLY,0);
if (fd != -1)
{
status = ioctl(fd,OSS_GETVERSION,&level);
new_oss_running = (status == 0);
status = ioctl(fd,OSS_SYSINFO,&sysinfo);
close(fd);
}
#endif
if (!strbuf) strbuf = (char *)CALLOC(STRBUF_SIZE,sizeof(char));
if (new_oss_running)
{
#ifdef NEW_OSS
if (status == 0)
{
sprintf(strbuf,"OSS version: %s\n",sysinfo.version);
pprint(strbuf);
}
else
{
sprintf(strbuf,"OSS version: %x.%x.%x\n",(level>>16)&0xff,(level>>8)&0xff,level&0xff);
pprint(strbuf);
}
#else
sprintf(strbuf,"OSS version: %x.%x.%x\n",(level>>16)&0xff,(level>>8)&0xff,level&0xff);
pprint(strbuf);
#endif
}
else
{
/* refers to the version upon compilation */
sprintf(version,"%d",SOUND_VERSION);
sprintf(strbuf,"OSS version: %c.%c.%c\n",version[0],version[1],version[2]);
pprint(strbuf);
}
sprintf(strbuf,"%d card%s found",sound_cards,(sound_cards != 1) ? "s" : ""); pprint(strbuf);
if (sound_cards > 1)
{
pprint(": ");
for (i=0;i<sound_cards;i++)
{
sprintf(strbuf,"/dev/dsp%d with /dev/mixer%d%s",audio_dsp[i],audio_mixer[i],(i<(sound_cards-1)) ? ", " : "");
pprint(strbuf);
}
}
pprint("\n\n");
fd = open(SYNTH_NAME,O_RDWR,0);
if (fd == -1) fd = open(SYNTH_NAME,O_RDONLY,0);
if (fd == -1)
{
sprintf(strbuf,"%s: %s\n",SYNTH_NAME,strerror(errno)); pprint(strbuf);
pprint("no synth found\n");
}
else
{
/* ioctl(fd,SNDCTL_DSP_SETDUPLEX,0); */ /* try to enable "full duplex" mode -- appears to be a no-op */
status = ioctl(fd,SNDCTL_SEQ_NRSYNTHS,&numdevs);
if (status == -1)
{
close(fd);
pprint("no sequencer?");
}
else
{
sprintf(strbuf,"/dev/sequencer: %d device%s installed\n",numdevs,(numdevs == 1) ? "" : "s");
pprint(strbuf);
for (i=0;i<numdevs;i++)
{
sinfo.device = i;
status = ioctl(fd,SNDCTL_SYNTH_INFO,&sinfo);
if (status != -1)
{
sprintf(strbuf," device: %d: %s, %s, %d voices\n",i,sinfo.name,device_type(sinfo.synth_type),sinfo.nr_voices);
pprint(strbuf);
}
}
status = ioctl(fd,SNDCTL_SEQ_NRMIDIS,&numdevs);
if (status == -1)
{
close(fd);
pprint("no midi");
}
else
{
sprintf(strbuf," %d midi device%s installed\n",numdevs,(numdevs == 1) ? "" : "s");
pprint(strbuf);
for (i=0;i<numdevs;i++)
{
minfo.device = i;
status = ioctl(fd,SNDCTL_MIDI_INFO,&minfo);
if (status != -1)
{
sprintf(strbuf," device %d: %s, %s\n",i,minfo.name,synth_name(minfo.dev_type));
pprint(strbuf);
}}}}}
close(fd);
pprint("--------------------------------\n");
MIXER_INFO:
sprintf(dsp_name,"%s%d",MIXER_NAME,dsp_num);
fd = linux_audio_open(dsp_name,O_RDWR,0,0);
if (fd == -1)
{
/* maybe output only */
fd = linux_audio_open(dsp_name,O_WRONLY,0,0);
if (fd == -1)
{
if (dsp_num == 0)
{
sprintf(dsp_name,"%s",DAC_NAME);
fd = linux_audio_open(DAC_NAME,O_RDWR,0,0);
if (fd == -1)
{
/* maybe output only */
fd = linux_audio_open(DAC_NAME,O_WRONLY,0,0);
if (fd == -1)
{
pprint("no audio device found\n");
return;
}
}
}
else goto AUDIO_INFO; /* no /dev/mixern */
}
else pprint("no audio input enabled\n");
}
status = ioctl(fd,SOUND_MIXER_READ_RECSRC,&recsrc);
if (status == -1)
{
linux_audio_close(fd);
pprint("no recsrc\n");
}
else
{
status = ioctl(fd,SOUND_MIXER_READ_DEVMASK,&devmask);
if (status == -1)
{
linux_audio_close(fd);
pprint("no devmask\n");
}
else
{
status = ioctl(fd,SOUND_MIXER_READ_RECMASK,&recmask);
if (status == -1)
{
linux_audio_close(fd);
pprint("no recmask\n");
}
else
{
status = ioctl(fd,SOUND_MIXER_READ_STEREODEVS,&stereodevs);
if (status == -1)
{
linux_audio_close(fd);
pprint("no stereodevs\n");
}
else
{
status = ioctl(fd,SOUND_MIXER_READ_CAPS,&caps);
if (status == -1)
{
linux_audio_close(fd);
pprint("no caps\n");
}
else
{
#ifdef NEW_OSS
if (new_oss_running) status = ioctl(fd,SOUND_MIXER_INFO,&mixinfo);
#endif
sprintf(strbuf,"%s",dsp_name);
pprint(strbuf);
#ifdef NEW_OSS
if ((new_oss_running) && (status == 0))
{
sprintf(strbuf," (%s",mixinfo.name);
pprint(strbuf);
for (i=0;i<sound_cards;i++)
{
if ((audio_mixer[i] == dsp_num) && (audio_type[i] == SONORUS_STUDIO))
{
sprintf(strbuf," in mode %d",audio_mode[i]);
pprint(strbuf);
break;
}
}
pprint(")");
}
#endif
pprint(":\n\n"
" mixer recording active stereo current\n"
" channel source source device level\n"
" -------- -------- -------- -------- -------- \n");
for (i=0;i<SOUND_MIXER_NRDEVICES;i++)
{
if ((1<<i) & devmask)
{
sprintf(strbuf," %-10s",sound_device_names[i]);
pprint(strbuf);
yes_no((1<<i) & recmask);
yes_no((1<<i) & recsrc);
yes_no((1<<i) & stereodevs);
status = ioctl(fd,MIXER_READ(i),&level);
if (status != -1)
{
if ((1<<i) & stereodevs)
sprintf(strbuf," %.2f %.2f",(float)(level&0xff) * 0.01,(float)((level&0xff00)>>8) * 0.01);
else sprintf(strbuf," %.2f",(float)(level&0xff) * 0.01);
/* can't use %% here because subsequent fprintf in pprint evaluates the %! #$@$! */
pprint(strbuf);
}
pprint("\n");
}
}
pprint("--------------------------------\n");
}}}}}
AUDIO_INFO:
linux_audio_close(fd);
sprintf(dsp_name,"%s%d",DAC_NAME,dsp_num);
fd = linux_audio_open(dsp_name,O_RDWR,0,0);
if ((fd == -1) && (dsp_num == 0)) fd = linux_audio_open(DAC_NAME,O_WRONLY,0,0);
if (fd == -1) return;
sprintf(strbuf,"%s:\n\n",dsp_name); pprint(strbuf);
if ((ioctl(fd,SOUND_PCM_READ_RATE,&rate) != -1) &&
(ioctl(fd,SOUND_PCM_READ_CHANNELS,&channels) != -1) &&
(ioctl(fd,SOUND_PCM_READ_BITS,&bits) != -1) &&
(ioctl(fd,SNDCTL_DSP_GETBLKSIZE,&blocksize) != -1))
{
sprintf(strbuf," defaults:\n sampling rate: %d, chans: %d, sample size: %d bits, block size: %d bytes\n",rate,channels,bits,blocksize);
pprint(strbuf);
deffmt = AFMT_QUERY;
if ((ioctl(fd,SOUND_PCM_SETFMT,&deffmt) != -1) &&
(ioctl(fd,SOUND_PCM_GETFMTS,&formats) != -1))
{
pprint(" supported formats:\n");
if (formats & AFMT_MU_LAW) {pprint(" mulaw"); if (deffmt == AFMT_MU_LAW) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_A_LAW) {pprint(" alaw"); if (deffmt == AFMT_A_LAW) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_IMA_ADPCM) {pprint(" adpcm"); if (deffmt == AFMT_IMA_ADPCM) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_U8) {pprint(" unsigned byte"); if (deffmt == AFMT_U8) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_S16_LE) {pprint(" signed little-endian short"); if (deffmt == AFMT_S16_LE) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_S16_BE) {pprint(" signed big-endian short"); if (deffmt == AFMT_S16_BE) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_S8) {pprint(" signed byte"); if (deffmt == AFMT_S8) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_U16_LE) {pprint(" unsigned little-endian short"); if (deffmt == AFMT_U16_LE) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_U16_BE) {pprint(" unsigned big-endian short"); if (deffmt == AFMT_U16_BE) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_MPEG) {pprint(" mpeg 2"); if (deffmt == AFMT_MPEG) pprint(" (default)"); pprint("\n");}
#ifdef NEW_OSS
if (formats & AFMT_S32_LE) {pprint(" signed little-endian int"); if (deffmt == AFMT_S32_LE) pprint(" (default)"); pprint("\n");}
if (formats & AFMT_S32_BE) {pprint(" signed big-endian int"); if (deffmt == AFMT_S32_BE) pprint(" (default)"); pprint("\n");}
#endif
status = ioctl(fd,SNDCTL_DSP_GETCAPS,&caps);
if (status != -1)
{
if (caps & DSP_CAP_DUPLEX) pprint(" full duplex\n");
pprint(" sample srate\n channels size min max\n");
for (channels=1;channels<=2;channels++)
{
for (bits=8;bits<=16;bits+=8)
{
min_rate = 1;
if (set_dsp(fd,channels,bits,&min_rate) == -1) continue;
max_rate = 100000;
if (set_dsp(fd,channels,bits,&max_rate) == -1) continue;
sprintf(strbuf," %4d %8d %8d %8d\n",channels,bits,min_rate,max_rate);
pprint(strbuf);
}}}}}
pprint("--------------------------------\n");
linux_audio_close(fd);
dsp_num++;
if (dsp_num < 16)
{
sprintf(dsp_name,"%s%d",MIXER_NAME,dsp_num);
goto MIXER_INFO;
}
}
void save_audio_state (void)
{
int afd,i,devmask,err,level,system,systems;
systems = audio_systems();
for (system=0;system<systems;system++)
{
afd = linux_audio_open(mixer_name(system),O_RDONLY,0,0);
if (afd == -1) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return;}
ioctl(afd,SOUND_MIXER_READ_DEVMASK,&devmask);
for (i=0;i<SOUND_MIXER_NRDEVICES;i++)
{
mixer_state[system][i] = 0;
if ((1<<i) & devmask)
{
err = ioctl(afd,MIXER_READ(i),&level);
if (err != -1) mixer_state[system][i] = level;
}
}
ioctl(afd,SOUND_PCM_READ_RATE,&(init_srate[system]));
ioctl(afd,SOUND_PCM_READ_CHANNELS,&(init_chans[system]));
init_format[system] = AFMT_QUERY;
ioctl(afd,SOUND_PCM_SETFMT,&(init_format[system]));
linux_audio_close(afd);
}
}
void restore_audio_state (void)
{
int afd,i,level,devmask,system,systems;
systems = audio_systems();
for (system=0;system<systems;system++)
{
afd = linux_audio_open(mixer_name(system),O_RDWR,0,0);
if (afd == -1) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return;}
ioctl(afd,SOUND_PCM_WRITE_CHANNELS,&(init_chans[system]));
ioctl(afd,SOUND_PCM_WRITE_RATE,&(init_srate[system]));
ioctl(afd,SOUND_PCM_SETFMT,&(init_format[system]));
ioctl(afd,SOUND_MIXER_READ_DEVMASK,&devmask);
for (i=0;i<SOUND_MIXER_NRDEVICES;i++)
{
if ((1<<i) & devmask)
{
level = mixer_state[system][i];
ioctl(afd,MIXER_WRITE(i),&level);
}
}
linux_audio_close(afd);
}
}
void write_mixer_state(char *file)
{
int fd,systems,i;
save_audio_state();
fd = creat(file,0666);
if (fd != -1)
{
systems = audio_systems();
for (i=0;i<systems;i++)
write(fd,(unsigned char *)mixer_state[i],MIXER_SIZE * sizeof(int));
close(fd);
}
}
void read_mixer_state(char *file)
{
int fd,afd,i,level,devmask,system,systems;
int vals[MAX_SOUNDCARDS][MIXER_SIZE];
fd = open(file,O_RDONLY,0);
if (fd != -1)
{
systems = audio_systems();
for (i=0;i<systems;i++)
read(fd,(unsigned char *)vals[i],MIXER_SIZE * sizeof(int));
close(fd);
for (system=0;system<systems;system++)
{
afd = linux_audio_open(mixer_name(system),O_RDONLY,0,0);
if (afd != -1)
{
ioctl(afd,SOUND_MIXER_READ_DEVMASK,&devmask);
for (i=0;i<SOUND_MIXER_NRDEVICES;i++)
{
if ((1<<i) & devmask)
{
level = vals[system][i];
ioctl(afd,MIXER_WRITE(i),&level);
}
}
linux_audio_close(afd);
}
}
}
}
#endif
/* ------------------------------- ALSA ----------------------------------------- */
/*
* this code thanks to Paul Barton-Davis
*/
#if HAVE_ALSA
#define AUDIO_OK
#include <sys/ioctl.h>
#include <sys/asoundlib.h>
static int fragments = 4;
static int fragment_size = 4096; /* same as default OSS equivalent */
static char dev_name[64];
void set_oss_buffers (int num, int size) {
fragments = num;
fragment_size = size;
}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
/* an audio subsystem is identified by a two-part, 4 byte designator:
the card number, stored in the leftmost two bytes, and the device
number, stored in the lower two bytes.
*/
#define card(sysid) ((sysid >> 16) & 0xFFFF)
#define device(sysid) (sysid & 0xFFFF)
#define make_sysid(c,d) ((c << 16) | (d & 0xFFFF))
#define SUBSYSTEM_OPEN 0x1
#define SUBSYSTEM_PCM 0x1
#define SUBSYSTEM_MIXER 0x2
typedef struct _audio_subsystem {
struct _audio_subsystem *next;
snd_pcm_t *pcm_handle;
int sysid;
int snd_type;
int state;
int type;
} audio_subsystem;
typedef struct _soundcard {
audio_subsystem *subsystems;
char name[64];
} soundcard;
#define MAX_SOUNDCARDS 8
static int sound_cards = 0;
static soundcard *soundcards;
static int error_exit(int error, int line)
{
AUDIO_ERROR = error;
/* maybe close "line" at some point */
return(-1);
}
int audio_systems(void)
{
return (sound_cards);
}
char *audio_system_name(int system)
{
if (system < sound_cards) {
return soundcards[system].name;
} else {
return "illegal card number";
}
}
char *audio_moniker(void)
{
sprintf(version_name,"ALSA %s", SND_LIB_VERSION_STR);
return(version_name);
}
int initialize_audio(void)
{
snd_ctl_t *handle;
struct snd_ctl_hw_info info;
int cardnum;
if (audio_initialized) {
return 0;
}
sound_cards = snd_cards ();
soundcards = CALLOC (sound_cards, sizeof (soundcard));
for (cardnum = 0; cardnum < sound_cards; cardnum++) {
if (snd_ctl_open (&handle, cardnum) < 0) {
strcpy (soundcards[cardnum].name,
"unknown");
continue;
}
if (snd_ctl_hw_info (handle, &info) < 0) {
snd_ctl_close (handle);
strcpy (soundcards[cardnum].name, "indeterminate");
}
strcpy (soundcards[cardnum].name, info.name);
snd_ctl_close (handle);
}
audio_initialized = 1;
return 0;
}
static int to_alsa_format(int snd_format)
{
switch (snd_format)
{
case SNDLIB_8_LINEAR: return(SND_PCM_SFMT_S8); break;
case SNDLIB_16_LINEAR: return(SND_PCM_SFMT_S16_BE); break;
case SNDLIB_8_UNSIGNED: return(SND_PCM_SFMT_U8); break;
case SNDLIB_8_MULAW: return(SND_PCM_SFMT_MU_LAW); break;
case SNDLIB_8_ALAW: return(SND_PCM_SFMT_A_LAW); break;
case SNDLIB_16_LINEAR_LITTLE_ENDIAN: return(SND_PCM_SFMT_S16_LE); break;
case SNDLIB_16_UNSIGNED: return(SND_PCM_SFMT_U16_BE); break;
case SNDLIB_16_UNSIGNED_LITTLE_ENDIAN: return(SND_PCM_SFMT_U16_LE); break;
}
return(-1);
}
static audio_subsystem *get_audio_subsystem (int card_id, int snd_type)
{
audio_subsystem *sys;
audio_subsystem *l;
/* Card ID tells us the card, device id tells us which
card device we want to access
First check the existing list of open systems
for the specified card.
*/
if (card_id >= sound_cards) {
return NULL;
}
for (sys = soundcards[card_id].subsystems, l = NULL;
sys != NULL; l = sys, sys = sys->next) {
if (sys->snd_type == snd_type) {
return sys;
}
}
/* cons up a new system */
sys = (audio_subsystem *) CALLOC (1, sizeof (audio_subsystem));
sys->snd_type = snd_type;
if (l == NULL) {
soundcards[card_id].subsystems = sys;
} else {
l->next = sys;
}
return sys;
}
static audio_subsystem *alsa_audio_open (int card_id, int snd_type, int mode)
{
audio_subsystem *sys;
int device;
int err;
if ((sys = get_audio_subsystem (card_id, snd_type)) == NULL) {
return NULL;
}
if (sys->state == SUBSYSTEM_OPEN) {
return sys;
}
if (snd_type == SNDLIB_DEFAULT_DEVICE) {
if (mode == O_WRONLY) {
snd_type = SNDLIB_DAC_OUT_DEVICE;
} else if (mode == O_RDONLY) {
snd_type = SNDLIB_DIGITAL_IN_DEVICE;
} else {
snd_type = SNDLIB_READ_WRITE_DEVICE;
}
}
switch (snd_type) {
case SNDLIB_READ_WRITE_DEVICE:
device = 0;
err = snd_pcm_open (&sys->pcm_handle,
card_id,
device,
SND_PCM_OPEN_DUPLEX);
break;
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_ADAT_OUT_DEVICE:
case SNDLIB_AES_OUT_DEVICE:
case SNDLIB_SPDIF_OUT_DEVICE:
case SNDLIB_DIGITAL_OUT_DEVICE:
err = snd_pcm_open (&sys->pcm_handle,
card_id,
device,
SND_PCM_OPEN_PLAYBACK);
break;
case SNDLIB_AUX_OUTPUT_DEVICE:
device = 1;
err = snd_pcm_open (&sys->pcm_handle,
card_id,
device,
SND_PCM_OPEN_PLAYBACK);
break;
case SNDLIB_ADAT_IN_DEVICE:
case SNDLIB_AES_IN_DEVICE:
case SNDLIB_DIGITAL_IN_DEVICE:
case SNDLIB_SPDIF_IN_DEVICE:
device = 0;
err = snd_pcm_open (&sys->pcm_handle,
card_id,
device,
SND_PCM_OPEN_CAPTURE);
break;
default:
err = -1;
}
if (err) {
sys = NULL;
} else {
sys->state = SUBSYSTEM_OPEN;
sys->type = SUBSYSTEM_PCM;
sys->sysid = make_sysid (card_id, device);
}
return sys;
}
int open_audio_output(int ur_dev, int srate, int chans, int format, int size)
{
/* ur_dev is in general SNDLIB_AUDIO_SYSTEM(n) | SNDLIB_DEVICE */
int alsa_format,card_id,dev;
snd_pcm_format_t fmt;
snd_pcm_playback_params_t params;
audio_subsystem *sys;
int err;
AUDIO_ERROR = SNDLIB_NO_ERROR;
card_id = SNDLIB_SYSTEM(ur_dev);
dev = SNDLIB_DEVICE(ur_dev);
if ((alsa_format = to_alsa_format(format)) == -1)
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,-1));
if ((sys = alsa_audio_open (card_id, dev, O_WRONLY)) == NULL)
return(error_exit(SNDLIB_CANT_OPEN,-1));
params.fragment_size = fragment_size;
params.fragments_max = (srate > 3000) ? fragments : 2;
params.fragments_room = 1;
if ((err = snd_pcm_playback_params (sys->pcm_handle, &
params)) != 0)
return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
fmt.format = alsa_format;
fmt.rate = srate;
fmt.channels = chans;
if ((err = snd_pcm_playback_format (sys->pcm_handle, &fmt)) != 0)
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,-1));
return (snd_pcm_file_descriptor (sys->pcm_handle));
}
int open_audio_input(int ur_dev, int srate, int chans, int format, int requested_size)
{
/* ur_dev is in general SNDLIB_AUDIO_SYSTEM(n) | SNDLIB_DEVICE */
int alsa_format,card_id,dev;
int audio_out = -1;
snd_pcm_format_t fmt;
snd_pcm_capture_params_t params;
audio_subsystem *sys;
int err;
AUDIO_ERROR = SNDLIB_NO_ERROR;
card_id = SNDLIB_SYSTEM(ur_dev);
dev = SNDLIB_DEVICE(ur_dev);
if ((alsa_format = to_alsa_format(format)) == -1)
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,-1));
if ((sys = alsa_audio_open (card_id, dev, O_RDONLY)) == NULL)
return(error_exit(SNDLIB_CANT_OPEN,-1));
params.fragment_size = fragment_size;
params.fragments_min = 1;
if ((err = snd_pcm_capture_params (sys->pcm_handle, & params)) != 0)
return(error_exit(SNDLIB_CONFIGURATION_NOT_AVAILABLE,-1));
fmt.format = alsa_format;
fmt.rate = srate;
fmt.channels = chans;
if ((err = snd_pcm_capture_format (sys->pcm_handle, &fmt)) != 0)
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,-1));
/* XXX ??? need to make sure the desired recording source is active */
return (snd_pcm_file_descriptor (sys->pcm_handle));
}
static audio_subsystem *pcm_subsystem_by_file_descriptor (int fd)
{
int i;
audio_subsystem *sys;
for (i = 0; i < sound_cards; i++) {
for (sys = soundcards[i].subsystems;
sys != NULL;
sys = sys->next) {
if (sys->type == SUBSYSTEM_PCM) {
if (snd_pcm_file_descriptor
(sys->pcm_handle) == fd) {
return sys;
}
}
}
}
return NULL;
}
int write_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
write(line,buf,bytes);
return(0);
}
int close_audio(int line)
{
audio_subsystem *sys;
AUDIO_ERROR = SNDLIB_NO_ERROR;
sys = pcm_subsystem_by_file_descriptor (line);
if (sys == NULL) {
AUDIO_ERROR = SNDLIB_CANT_CLOSE;
return -1;
}
snd_pcm_close (sys->pcm_handle);
sys->state &= ~SUBSYSTEM_OPEN;
return 0;
}
int read_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
read(line,buf,bytes);
return(0);
}
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
int card_id;
int snd_type;
snd_pcm_info_t pcminfo;
snd_pcm_playback_info_t playinfo;
snd_pcm_capture_info_t recinfo;
snd_ctl_t *handle;
struct snd_ctl_hw_info info;
int i;
int channels;
AUDIO_ERROR = SNDLIB_NO_ERROR;
card_id = SNDLIB_SYSTEM(ur_dev);
snd_type = SNDLIB_DEVICE(ur_dev);
snd_ctl_open (&handle, card_id);
snd_ctl_hw_info (handle, &info);
if (field == SNDLIB_DEVICE_FIELD) {
val[0] = info.pcmdevs > chan ? chan : info.pcmdevs;
for (i = 0; i < val[0]; i++) {
val[i+1] = SNDLIB_LINE_IN_DEVICE;
}
}
switch (snd_type) {
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_SPEAKERS_DEVICE:
case SNDLIB_LINE_OUT_DEVICE:
switch (field) {
case SNDLIB_AMP_FIELD:
val[0] = 10;
break;
case SNDLIB_CHANNEL_FIELD:
val[0] = 2;
break;
case SNDLIB_SRATE_FIELD:
val[0] = 44100;
break;
case SNDLIB_FORMAT_FIELD:
val[0] = 1;
if (chan > 1)
val[1] = SNDLIB_16_LINEAR_LITTLE_ENDIAN;
break;
default:
AUDIO_ERROR = SNDLIB_CANT_READ;
break;
}
break;
case SNDLIB_LINE_IN_DEVICE:
switch (field) {
case SNDLIB_AMP_FIELD:
val[0] = 10;
break;
case SNDLIB_CHANNEL_FIELD:
val[0] = 2;
break;
case SNDLIB_SRATE_FIELD:
val[0] = 44100;
break;
case SNDLIB_FORMAT_FIELD:
val[0] = 1;
if (chan > 1)
val[1] = SNDLIB_16_LINEAR_LITTLE_ENDIAN;
break;
default:
AUDIO_ERROR = SNDLIB_CANT_READ;
break;
}
break;
case SNDLIB_MICROPHONE_DEVICE:
switch (field) {
case SNDLIB_AMP_FIELD:
val[0] = 10;
break;
case SNDLIB_CHANNEL_FIELD:
val[0] = 1;
break;
case SNDLIB_SRATE_FIELD:
val[0] = 44100;
break;
case SNDLIB_FORMAT_FIELD:
val[0] = 1;
if (chan > 1)
val[1] = SNDLIB_16_LINEAR_LITTLE_ENDIAN;
break;
default:
AUDIO_ERROR = SNDLIB_CANT_READ;
break;
}
break;
default:
AUDIO_ERROR = SNDLIB_CANT_READ;
break;
}
snd_ctl_close (handle);
return(0);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
return(0);
}
void save_audio_state (void)
{
}
void restore_audio_state (void)
{
}
void write_mixer_state(char *file)
{
}
void read_mixer_state(char *file)
{
}
static void describe_audio_state_1 (void)
{
pprint ("Sorry, no description of ALSA audio state available");
}
#endif
/* -------------------------------- NEXT -------------------------------- */
#ifdef NEXT
#define AUDIO_OK
#include <sound/sound.h>
#include <sound/sounddriver.h>
#include <sound/snddriver_client.h>
#include <mach/mach.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
static int low_water = 48*1024;
static int high_water = 64*1024;
static port_t dev_port = 0;
static port_t owner_port = 0;
static port_t write_port = 0;
static port_t read_port = 0;
static port_t reply_port = 0;
static short *rtbuf = NULL;
static int rtbuf_size = 0;
static char *readbuf;
#define WRITE_TAG 1
#define READ_TAG 1
#define BYTES_PER_SAMPLE 2
#define WRITE_COMPLETED_MSG 1
#define OUTPUT_LINE 1
#define INPUT_LINE 2
static int nxt_err = 0;
static char errstr[128];
char *audio_error_name(int err)
{
if (!nxt_err) return(audio_error_name_1(err));
sprintf(errstr,"%s: %s",audio_error_name_1(err),SNDSoundError(nxt_err));
return(errstr);
}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("NeXT");}
char *audio_moniker(void) {return("NeXT audio");}
static int waiting = 0;
static void write_completed(void *arg, int tag)
{
if (tag == WRITE_TAG) waiting--;
}
int initialize_audio(void) {AUDIO_ERROR = SNDLIB_NO_ERROR; return(0);}
static int output_chans = 0;
static snddriver_handlers_t replyHandlers;
static msg_header_t *reply_msg;
int open_audio_output(int ur_dev, int srate, int chans, int format, int size)
{
int sr,i,dev;
int protocol = 0;
AUDIO_ERROR = SNDLIB_NO_ERROR; nxt_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
if (format != SNDLIB_COMPATIBLE_FORMAT) {AUDIO_ERROR = SNDLIB_FORMAT_NOT_AVAILABLE; return(-1);}
if (size == 0) size = 1024;
/* large buffers (4096) can cause bus errors! */
if (rtbuf == NULL)
{
rtbuf = (short *)CALLOC(size,sizeof(short));
rtbuf_size = size;
}
else
{
if (size > rtbuf_size)
{
FREE(rtbuf);
rtbuf = (short *)CALLOC(size,sizeof(short));
rtbuf_size = size;
}
for (i=0;i<size;i++) rtbuf[i] = 0;
}
nxt_err = SNDAcquire(SND_ACCESS_OUT,0,0,0,NULL_NEGOTIATION_FUN,0,&dev_port,&owner_port);
if (nxt_err == 0)
{
if (srate > 30000) sr = SNDDRIVER_STREAM_TO_SNDOUT_44; else sr = SNDDRIVER_STREAM_TO_SNDOUT_22;
nxt_err = snddriver_stream_setup(dev_port,owner_port,sr,size,BYTES_PER_SAMPLE,low_water,high_water,&protocol,&write_port);
output_chans = chans;
waiting = 0;
if (nxt_err == 0)
{
reply_msg = (msg_header_t *)malloc(MSG_SIZE_MAX);
nxt_err = port_allocate(task_self(),&reply_port);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
reply_msg->msg_size = MSG_SIZE_MAX;
reply_msg->msg_local_port = reply_port;
replyHandlers.completed = write_completed;
for (sr=0;sr<4;sr++)
{
nxt_err = snddriver_stream_start_writing(write_port,rtbuf,size,WRITE_TAG,0,0,0,WRITE_COMPLETED_MSG,0,0,0,0,reply_port);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
waiting++;
}
}
}
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
return(OUTPUT_LINE);
}
int write_audio(int line, char *buf, int bytes)
{
int i,j,samps,err;
#ifdef SNDLIB_LITTLE_ENDIAN
char tmp;
#endif
short *sbuf;
AUDIO_ERROR = SNDLIB_NO_ERROR; nxt_err = 0;
if (line != OUTPUT_LINE) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
sbuf = (short *)buf;
samps = (bytes>>1);
#ifdef SNDLIB_LITTLE_ENDIAN
/* "sound" format in NextStep is always big-endian */
for (i=0;i<bytes;i+=2)
{
tmp = buf[i+1];
buf[i+1] = buf[i];
buf[i] = tmp;
}
#endif
reply_msg->msg_size = MSG_SIZE_MAX;
reply_msg->msg_local_port = reply_port;
if (output_chans == 2)
{
nxt_err = snddriver_stream_start_writing(write_port,sbuf,samps,WRITE_TAG,0,0,0,WRITE_COMPLETED_MSG,0,0,0,0,reply_port);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
waiting++;
nxt_err = msg_receive(reply_msg,RCV_TIMEOUT,0);
if (nxt_err == RCV_SUCCESS) nxt_err = snddriver_reply_handler(reply_msg,&replyHandlers);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
}
else
{
/* since output is always stereo on the Next, we have to fake up the second channel in the 1-channel case */
j = 0;
for (i=0;i<samps;i++)
{
rtbuf[j++] = sbuf[i];
rtbuf[j++] = sbuf[i];
if (j == rtbuf_size)
{
j = 0;
nxt_err = snddriver_stream_start_writing(write_port,rtbuf,rtbuf_size,WRITE_TAG,0,0,0,WRITE_COMPLETED_MSG,0,0,0,0,reply_port);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
waiting++;
nxt_err = msg_receive(reply_msg,RCV_TIMEOUT,0);
if (nxt_err == RCV_SUCCESS) nxt_err = snddriver_reply_handler(reply_msg,&replyHandlers);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
}
}
if (j > 0)
{
nxt_err = snddriver_stream_start_writing(write_port,rtbuf,j,WRITE_TAG,0,0,0,WRITE_COMPLETED_MSG,0,0,0,0,reply_port);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
waiting++;
nxt_err = msg_receive(reply_msg,RCV_TIMEOUT,0);
if (nxt_err == RCV_SUCCESS) nxt_err = snddriver_reply_handler(reply_msg,&replyHandlers);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
}
}
return(0);
}
int close_audio(int line)
{
int ctr,oldwait;
ctr = 0;
AUDIO_ERROR = SNDLIB_NO_ERROR; nxt_err = 0;
if (dev_port)
{
if (line == OUTPUT_LINE)
{
while (waiting)
{
oldwait = waiting;
nxt_err = msg_receive(reply_msg,RCV_TIMEOUT,1);
if (nxt_err == RCV_SUCCESS)
nxt_err = snddriver_reply_handler(reply_msg,&replyHandlers);
else if (nxt_err == RCV_TIMEOUT) break;
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_CLOSE; return(-1);}
if (oldwait == waiting) ctr++; else ctr=0;
/* messages seem to be dropped at random on the Next, so we need a fail-safe way to break out of this loop */
if (ctr > 1000) break;
}
if (reply_msg) FREE(reply_msg);
nxt_err = SNDRelease(SND_ACCESS_OUT,dev_port,owner_port);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_CLOSE; return(-1);}
port_deallocate(task_self(),reply_port);
}
else
{
if (line == INPUT_LINE)
{
if (reply_msg) FREE(reply_msg);
nxt_err = SNDRelease(SND_ACCESS_IN,dev_port,owner_port);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_CLOSE; return(-1);}
port_deallocate(task_self(),reply_port);
}
else {AUDIO_ERROR = SNDLIB_CANT_CLOSE; return(-1);}
}
}
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_CLOSE; return(-1);}
return(0);
}
/* here we'll block if data not ready for read */
/* also since it's the next built-in microphone, we get mulaw data at 8KHz */
static void read_completed(void *arg, int tag, void *inp, int bytes)
{
int i;
if (tag == READ_TAG)
{
for (i=0;i<bytes;i++) readbuf[i] = ((char *)inp)[i];
}
}
/* as far as I can tell, you only get one read from the NeXT, so it better be a monster...*/
int open_audio_input(int ur_dev, int srate, int chans, int format, int size)
{
int protocol,dev;
AUDIO_ERROR = SNDLIB_NO_ERROR; nxt_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
if (format != SNDLIB_8_MULAW) {AUDIO_ERROR = SNDLIB_FORMAT_NOT_AVAILABLE; return(-1);}
if ((srate != 8000) && (srate != 8012)) {AUDIO_ERROR = SNDLIB_SRATE_NOT_AVAILABLE; return(-1);}
if (size == 0) size = 1024;
nxt_err = SNDAcquire(SND_ACCESS_IN,0,0,0,NULL_NEGOTIATION_FUN,0,&dev_port,&owner_port);
if (nxt_err == 0)
{
nxt_err = snddriver_stream_setup(dev_port,owner_port,SNDDRIVER_STREAM_FROM_SNDIN,size,1,low_water,high_water,&protocol,&read_port);
if (nxt_err == 0)
{
reply_msg = (msg_header_t *)malloc(MSG_SIZE_MAX);
reply_msg->msg_size = MSG_SIZE_MAX;
nxt_err = port_allocate(task_self(),&reply_port);
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
reply_msg->msg_local_port = reply_port;
replyHandlers.recorded_data = read_completed;
nxt_err = snddriver_stream_start_reading(read_port,0,size,READ_TAG,0,0,0,0,0,0,reply_port);
}
}
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
return(INPUT_LINE);
}
int read_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR; nxt_err = 0;
if (line != INPUT_LINE) {AUDIO_ERROR = SNDLIB_CANT_READ; return(-1);}
reply_msg->msg_size = MSG_SIZE_MAX;
reply_msg->msg_local_port = reply_port;
nxt_err = msg_receive(reply_msg,MSG_OPTION_NONE,0);
if (nxt_err == RCV_SUCCESS)
{
readbuf = buf;
nxt_err = snddriver_reply_handler(reply_msg,&replyHandlers);
}
if (nxt_err) {AUDIO_ERROR = SNDLIB_CANT_READ; return(-1);}
return(0);
}
#define MAX_VOLUME 0x2b
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
int left,right,dev;
int amps[2];
AUDIO_ERROR = SNDLIB_NO_ERROR; nxt_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
if ((dev == SNDLIB_DAC_FILTER_DEVICE) && ((field == SNDLIB_TREBLE_FIELD) || (field == SNDLIB_BASS_FIELD)))
{
SNDGetFilter(&left);
val[0] = left;
}
else
{
if ((dev == SNDLIB_SPEAKERS_DEVICE) && (field == SNDLIB_AMP_FIELD))
{
SNDGetMute(&left);
val[0] = left;
}
else
{
switch (field)
{
case SNDLIB_DEVICE_FIELD: val[0] = 2; if (chan>1) val[1] = SNDLIB_MICROPHONE_DEVICE; if (chan>2) val[2] = SNDLIB_DAC_OUT_DEVICE; break;
case SNDLIB_FORMAT_FIELD: val[0] = 1; if (chan>1) val[1] = SNDLIB_16_LINEAR; break;
case SNDLIB_SRATE_FIELD: val[0] = 44100; break;
case SNDLIB_CHANNEL_FIELD: val[0] = 2; break;
case SNDLIB_AMP_FIELD:
if ((dev == SNDLIB_DAC_OUT_DEVICE) || (dev == SNDLIB_DEFAULT_DEVICE))
{
SNDGetVolume(&left,&right);
/* lower level snddriver_get_device_parms doesn't work in NeXTStep/Intel */
if (chan == 0)
val[0] = (float)left/(float)MAX_VOLUME;
else val[0] = (float)right/(float)MAX_VOLUME;
}
else AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE;
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
}
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
int left,right,dev;
AUDIO_ERROR = SNDLIB_NO_ERROR; nxt_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
if ((dev == SNDLIB_DAC_FILTER_DEVICE) && ((field == SNDLIB_TREBLE_FIELD) || (field == SNDLIB_BASS_FIELD)))
{
SNDSetFilter(val[0]);
}
else
{
if ((dev == SNDLIB_SPEAKERS_DEVICE) && (field == SNDLIB_AMP_FIELD))
{
SNDSetMute(val[0]);
}
else
{
if (field != SNDLIB_AMP_FIELD)
AUDIO_ERROR = SNDLIB_WRITE_ERROR;
else
{
if ((dev == SNDLIB_DAC_OUT_DEVICE) || (dev == SNDLIB_DEFAULT_DEVICE))
{
SNDGetVolume(&left,&right);
if (chan == 0)
left = (int)(val[0] * MAX_VOLUME);
else right = (int)(val[0] * MAX_VOLUME);
SNDSetVolume(left,right);
}
else AUDIO_ERROR = SNDLIB_CANT_WRITE;
}
}
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
static void describe_audio_state_1(void)
{
/* get volume -- not much else we can do here */
float amps[1];
float val;
if (!strbuf) strbuf = (char *)CALLOC(1024,sizeof(char));
read_audio_state(SNDLIB_DEFAULT_DEVICE,SNDLIB_AMP_FIELD,0,amps);
val = amps[0];
read_audio_state(SNDLIB_DEFAULT_DEVICE,SNDLIB_AMP_FIELD,1,amps);
sprintf(strbuf,"speaker vols: %.2f %.2f\n",val,amps[0]);
pprint(strbuf);
read_audio_state(SNDLIB_DAC_FILTER_DEVICE,SNDLIB_TREBLE_FIELD,0,amps);
pprint("dac lowpass: ");
pprint((amps[0] == 0.0) ? "off\n" : "on\n");
read_audio_state(SNDLIB_SPEAKERS_DEVICE,SNDLIB_AMP_FIELD,0,amps);
pprint("speakers: ");
pprint((amps[0] == 0) ? "off\n" : "on\n");
}
static int saved_left,saved_right,saved_mute,saved_filter;
void save_audio_state (void)
{
SNDGetVolume(&saved_left,&saved_right);
SNDGetMute(&saved_mute);
SNDGetFilter(&saved_filter);
}
void restore_audio_state (void)
{
SNDSetVolume(saved_left,saved_right);
SNDSetMute(saved_mute);
SNDSetFilter(saved_filter);
}
#endif
/* -------------------------------- SUN -------------------------------- */
/*
* Thanks to Seppo Ingalsuo for several bugfixes.
* record case improved after perusal of Snack 1.6/src/jkAudio_sun.c (but it's still not right)
*/
/* apparently input other than 8000 is 16-bit, 8000 is (?) mulaw */
#if (defined(SUN) || defined(OPENBSD)) && (!(defined(AUDIO_OK)))
#define AUDIO_OK
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stropts.h>
#include <sys/filio.h>
#ifdef SUNOS
#include <sun/audioio.h>
#else
#include <sys/audioio.h>
#endif
int initialize_audio(void) {AUDIO_ERROR = SNDLIB_NO_ERROR; return(0);}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("Sun");}
static int sun_default_outputs = (AUDIO_HEADPHONE | AUDIO_LINE_OUT | AUDIO_SPEAKER);
void sun_outputs(int speakers, int headphones, int line_out)
{
sun_default_outputs = 0;
if (speakers) sun_default_outputs |= AUDIO_SPEAKER;
if (headphones) sun_default_outputs |= AUDIO_HEADPHONE;
if (line_out) sun_default_outputs |= AUDIO_LINE_OUT;
}
#ifdef OPENBSD
#define DAC_NAME "/dev/sound"
#else
#define DAC_NAME "/dev/audio"
#endif
#define AUDIODEV_ENV "AUDIODEV"
static int error_exit(int error, int line)
{
AUDIO_ERROR = error;
if (line != -1) close(line);
return(-1);
}
char *audio_moniker(void)
{
#ifndef AUDIO_DEV_AMD
struct audio_device ad;
#else
int ad;
#endif
int audio_fd,err;
char *dev_name;
if (getenv(AUDIODEV_ENV) != NULL) dev_name = getenv(AUDIODEV_ENV); else dev_name = DAC_NAME;
audio_fd = open(dev_name,O_RDONLY,0);
if (audio_fd == -1) audio_fd = open("dev/audioctl",O_RDONLY | O_NONBLOCK,0);
err = ioctl(audio_fd,AUDIO_GETDEV,&ad);
if (err == -1) {fprintf(stderr,"%s[%d] %s: getdev failed",__FILE__,__LINE__,__FUNCTION__); error_exit(SNDLIB_CANT_READ,audio_fd); return("sun");}
close_audio(audio_fd);
if (version_name == NULL) version_name = (char *)CALLOC(32,sizeof(char));
#ifndef AUDIO_DEV_AMD
sprintf(version_name,"audio: %s (%s)",ad.name,ad.version);
#else
switch (ad)
{
case AUDIO_DEV_AMD: sprintf(version_name,"audio: amd"); break;
#ifdef AUDIO_DEV_CS4231
case AUDIO_DEV_CS4231: sprintf(version_name,"audio: cs4231"); break;
#endif
case AUDIO_DEV_SPEAKERBOX: sprintf(version_name,"audio: speakerbox"); break;
case AUDIO_DEV_CODEC: sprintf(version_name,"audio: codec"); break;
default: sprintf(version_name,"audio: unknown"); break;
}
#endif
return(version_name);
}
static int to_sun_format(int format)
{
switch (format)
{
case SNDLIB_16_LINEAR:
#ifdef OPENBSD
return(AUDIO_ENCODING_PCM16);
#else
return(AUDIO_ENCODING_LINEAR);
#endif
break;
case SNDLIB_8_LINEAR:
#if defined(AUDIO_ENCODING_LINEAR8)
return(AUDIO_ENCODING_LINEAR8); break;
#else
#ifdef OPENBSD
return(AUDIO_ENCODING_PCM8);
#else
return(AUDIO_ENCODING_LINEAR);
#endif
break;
#endif
case SNDLIB_8_MULAW: return(AUDIO_ENCODING_ULAW); break;
case SNDLIB_8_ALAW: return(AUDIO_ENCODING_ALAW); break;
/* there's also AUDIO_ENCODING_DVI */
}
return(-1);
}
int open_audio_output(int ur_dev, int srate, int chans, int format, int size)
{
struct audio_info info;
char *dev_name;
int encode,bits,dev;
int audio_fd,err;
dev = SNDLIB_DEVICE(ur_dev);
AUDIO_ERROR = SNDLIB_NO_ERROR;
errno = 0;
encode = to_sun_format(format);
if (encode == -1) return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,-1));
if (getenv(AUDIODEV_ENV) != NULL) dev_name = getenv(AUDIODEV_ENV); else dev_name = DAC_NAME;
if (dev != SNDLIB_READ_WRITE_DEVICE)
audio_fd = open(dev_name,O_WRONLY,0);
else audio_fd = open(dev_name,O_RDWR,0);
if (audio_fd == -1)
{
if (errno == EBUSY) return(error_exit(SNDLIB_OUTPUT_BUSY,-1));
return(error_exit(SNDLIB_CANT_OPEN,-1));
}
AUDIO_INITINFO(&info);
if (dev == SNDLIB_LINE_OUT_DEVICE)
info.play.port = AUDIO_LINE_OUT;
else
{
if (dev == SNDLIB_SPEAKERS_DEVICE)
/* OR may not be available */
info.play.port = AUDIO_SPEAKER | (sun_default_outputs & AUDIO_HEADPHONE);
else
info.play.port = sun_default_outputs;
}
info.play.sample_rate = srate;
info.play.channels = chans;
bits = 8 * mus_format2bytes(format);
info.play.precision = bits;
info.play.encoding = encode;
err = ioctl(audio_fd,AUDIO_SETINFO,&info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: setinfo failed",__FILE__,__LINE__,__FUNCTION__); return(error_exit(SNDLIB_CANT_WRITE,audio_fd));}
if ((int)info.play.channels != chans) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,audio_fd));
if (((int)info.play.precision != bits) || ((int)info.play.encoding != encode)) return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,audio_fd));
/* man audio sez the play.buffer_size field is not currently supported */
/* but since the default buffer size is 8180! we need ioctl(audio_fd,I_SETSIG,...) */
ioctl(audio_fd,I_FLUSH,FLUSHR);
return(audio_fd);
}
int write_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
write(line,buf,bytes);
return(0);
}
int close_audio(int line)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
write(line,(char *)NULL,0);
close(line);
return(0);
}
int read_audio(int line, char *buf, int bytes)
{
int i,bytes_read,bytes_available,total=0;
char *curbuf;
AUDIO_ERROR = SNDLIB_NO_ERROR;
/* ioctl(line,AUDIO_DRAIN,NULL) */
/* this seems to return 8-12 bytes fewer than requested -- perverse! */
/* should I buffer data internally? */
/* apparently we need to loop here ... */
curbuf = buf;
while (total < bytes)
{
ioctl(line,FIONREAD,&bytes_available);
if (bytes_available > 0)
{
if ((total+bytes_available) > bytes) bytes_available = bytes-total;
bytes_read = read(line,curbuf,bytes_available);
total += bytes_read;
curbuf = (char *)(buf+total);
}
}
return(0);
}
int open_audio_input(int ur_dev, int srate, int chans, int format, int size)
{
struct audio_info info;
int indev,encode,bits,dev,audio_fd,err;
char *dev_name;
dev = SNDLIB_DEVICE(ur_dev);
AUDIO_ERROR = SNDLIB_NO_ERROR;
encode = to_sun_format(format);
if (encode == -1) return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,-1));
if (getenv(AUDIODEV_ENV) != NULL) dev_name = getenv(AUDIODEV_ENV); else dev_name = DAC_NAME;
if (dev != SNDLIB_READ_WRITE_DEVICE)
audio_fd = open(dev_name,O_RDONLY,0);
else audio_fd = open(dev_name,O_RDWR,0);
if (audio_fd == -1)
{
if (errno == EBUSY) return(error_exit(SNDLIB_INPUT_BUSY,-1));
return(error_exit(SNDLIB_CANT_OPEN,-1));
}
AUDIO_INITINFO(&info);
/* ioctl(audio_fd,AUDIO_GETINFO,&info); */
info.record.sample_rate = srate;
info.record.channels = chans;
err = ioctl(audio_fd, AUDIO_SETINFO, &info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: setinfo failed",__FILE__,__LINE__,__FUNCTION__); }
ioctl(audio_fd,AUDIO_GETINFO,&info);
if (info.record.sample_rate != (unsigned int)srate) fprintf(stderr,"%s[%d]: sampling rate: %d != %d\n",__FILE__,__LINE__,info.record.sample_rate,srate);
if (info.record.channels != (unsigned int)chans) fprintf(stderr,"%s[%d]: channels: %d != %d\n",__FILE__,__LINE__,info.record.channels,chans);
bits = 8 * mus_format2bytes(format);
info.play.precision = bits;
info.play.encoding = encode;
err = ioctl(audio_fd, AUDIO_SETINFO, &info);
if (err == -1) fprintf(stderr,"%s[%d] %s: setinfo failed",__FILE__,__LINE__,__FUNCTION__);
ioctl(audio_fd,AUDIO_GETINFO,&info);
/* these cannot be OR'd */
if (dev == SNDLIB_LINE_IN_DEVICE)
indev = AUDIO_LINE_IN;
else
{
if (dev == SNDLIB_CD_IN_DEVICE)
indev = AUDIO_INTERNAL_CD_IN;
else indev = AUDIO_MICROPHONE;
}
info.record.port = indev;
err = ioctl(audio_fd, AUDIO_SETINFO, &info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: setinfo failed",__FILE__,__LINE__,__FUNCTION__); return(error_exit(SNDLIB_CANT_WRITE,audio_fd));}
err = ioctl(audio_fd,AUDIO_GETINFO,&info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: getinfo failed",__FILE__,__LINE__,__FUNCTION__); return(error_exit(SNDLIB_CANT_READ,audio_fd));}
else
{
if ((int)info.record.port != indev) return(error_exit(SNDLIB_DEVICE_NOT_AVAILABLE,audio_fd));
if ((int)info.record.channels != chans) return(error_exit(SNDLIB_CHANNELS_NOT_AVAILABLE,audio_fd));
if (((int)info.record.precision != bits) || ((int)info.record.encoding != encode))
{
fprintf(stderr,"bits %d != %d, or encoding %d != %d\n",bits,info.record.precision,encode,info.record.encoding);
return(error_exit(SNDLIB_FORMAT_NOT_AVAILABLE,audio_fd));
}
}
/* this may be a bad idea */
info.record.buffer_size = size;
err = ioctl(audio_fd, AUDIO_SETINFO, &info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: buffer size setinfo failed",__FILE__,__LINE__,__FUNCTION__); return(error_exit(SNDLIB_CANT_WRITE,audio_fd));}
return(audio_fd);
}
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
#ifndef AUDIO_DEV_AMD
struct audio_device ad;
#else
int ad;
#endif
int audio_fd,err;
struct audio_info info;
int dev,port;
char *dev_name;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
AUDIO_INITINFO(&info);
if (getenv(AUDIODEV_ENV) != NULL) dev_name = getenv(AUDIODEV_ENV); else dev_name = DAC_NAME;
audio_fd = open(dev_name,O_RDONLY | O_NONBLOCK,0);
if (audio_fd == -1)
audio_fd = open("/dev/audioctl",O_RDONLY | O_NONBLOCK);
if (audio_fd == -1)
{
AUDIO_ERROR = SNDLIB_CANT_READ;
return(-1);
}
err = ioctl(audio_fd, AUDIO_GETINFO, &info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: getinfo failed",__FILE__,__LINE__,__FUNCTION__); return(error_exit(SNDLIB_CANT_READ,audio_fd));}
if (field == SNDLIB_DEVICE_FIELD)
{
/* info.play|record have a field avail_ports */
port = 1;
if ((chan > port) && (info.record.avail_ports & AUDIO_MICROPHONE)) {val[port] = SNDLIB_MICROPHONE_DEVICE; port++;}
if ((chan > port) && (info.record.avail_ports & AUDIO_LINE_IN)) {val[port] = SNDLIB_LINE_IN_DEVICE; port++;}
#ifndef AUDIO_DEV_AMD
if ((chan > port) && (info.record.avail_ports & AUDIO_INTERNAL_CD_IN))
{
/* this field lies -- there is no such port available on the Ultra */
err = ioctl(audio_fd,AUDIO_GETDEV,&ad);
if (err == -1) {fprintf(stderr,"%s[%d] %s: getdev failed",__FILE__,__LINE__,__FUNCTION__); return(error_exit(SNDLIB_CANT_READ,audio_fd));}
if (strcmp(ad.version,"a") == 0) /* is it a SparcStation? */
{
val[port] = SNDLIB_CD_IN_DEVICE;
port++;
}
}
#endif
if ((chan > port) && (info.play.avail_ports & AUDIO_SPEAKER)) {val[port] = SNDLIB_SPEAKERS_DEVICE; port++;}
if ((chan > port) && (info.play.avail_ports & AUDIO_LINE_OUT)) {val[port] = SNDLIB_LINE_OUT_DEVICE; port++;}
if ((chan > port) && (info.play.avail_ports & AUDIO_HEADPHONE)) {val[port] = SNDLIB_DAC_OUT_DEVICE; port++;}
val[0] = port-1;
}
else
{
if (field == SNDLIB_FORMAT_FIELD) /* this actually depends on the audio device */
{
err = ioctl(audio_fd,AUDIO_GETDEV,&ad); /* SUNW,dbri|am79c30|CS4231|sbpro|sb16 */
if (err == -1) {fprintf(stderr,"%s[%d] %s: getdev failed",__FILE__,__LINE__,__FUNCTION__); return(error_exit(SNDLIB_CANT_READ,audio_fd));}
port = 1;
#ifndef AUDIO_DEV_AMD
if (strcmp(ad.name,"SUNW,am79c30") != 0)
#else
if (ad == AUDIO_DEV_AMD)
#endif
{
if (chan>port) val[port] = SNDLIB_16_LINEAR; port++;
}
#ifndef AUDIO_DEV_AMD
if ((strcmp(ad.name,"SUNW,sbpro") != 0) && (strcmp(ad.name,"SUNW,sb16") != 0))
{
if (chan>port) val[port] = SNDLIB_8_ALAW; port++;
}
#endif
if (chan>port) val[port] = SNDLIB_8_MULAW;
val[0] = port;
}
else
{
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_SPEAKERS_DEVICE:
case SNDLIB_LINE_OUT_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
/* who knows how this really works? documentation is incomplete, actual behavior seems to be: */
if (chan == 0)
{
if (info.play.balance <= (AUDIO_RIGHT_BALANCE/2))
val[0] = info.play.gain / (float)(AUDIO_MAX_GAIN);
else val[0] = info.play.gain * (AUDIO_RIGHT_BALANCE - info.play.balance) / (float)(AUDIO_MAX_GAIN * (AUDIO_RIGHT_BALANCE/2));
}
else
{
if (info.play.balance >= (AUDIO_RIGHT_BALANCE/2))
val[0] = info.play.gain / (float)(AUDIO_MAX_GAIN);
else val[0] = info.play.gain * info.play.balance / (float)(AUDIO_MAX_GAIN * (AUDIO_RIGHT_BALANCE/2));
}
break;
case SNDLIB_CHANNEL_FIELD: val[0] = 2; break;
/* appears to depend on data format (mulaw is mono) */
case SNDLIB_SRATE_FIELD: val[0] = (float)info.play.sample_rate; break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
break;
case SNDLIB_MICROPHONE_DEVICE:
case SNDLIB_LINE_IN_DEVICE:
case SNDLIB_READ_WRITE_DEVICE:
case SNDLIB_CD_IN_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
if (chan == 0)
{
if (info.record.balance <= (AUDIO_RIGHT_BALANCE/2))
val[0] = info.record.gain / (float)(AUDIO_MAX_GAIN);
else val[0] = info.record.gain * (AUDIO_RIGHT_BALANCE - info.record.balance) / (float)(AUDIO_MAX_GAIN * (AUDIO_RIGHT_BALANCE/2));
}
else
{
if (info.record.balance >= (AUDIO_RIGHT_BALANCE/2))
val[0] = info.record.gain / (float)(AUDIO_MAX_GAIN);
else val[0] = info.record.gain * info.record.balance / (float)(AUDIO_MAX_GAIN * (AUDIO_RIGHT_BALANCE/2));
}
break;
case SNDLIB_CHANNEL_FIELD: val[0] = 1; break;
case SNDLIB_SRATE_FIELD: val[0] = (float)(info.record.sample_rate); break;
case SNDLIB_IGAIN_FIELD: val[0] = (float)(info.monitor_gain)/(float)AUDIO_MAX_GAIN; break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
}
}
close_audio(audio_fd);
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
struct audio_info info;
int dev,balance,gain,maxv;
float ratio,lc,rc;
int audio_fd,err;
char *dev_name;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
AUDIO_INITINFO(&info);
if (getenv(AUDIODEV_ENV) != NULL) dev_name = getenv(AUDIODEV_ENV); else dev_name = DAC_NAME;
audio_fd = open(dev_name,O_RDWR | O_NONBLOCK,0);
if (audio_fd == -1) audio_fd = open("/dev/audioctl",O_RDWR | O_NONBLOCK);
if (audio_fd == -1)
{
AUDIO_ERROR = SNDLIB_CANT_WRITE;
return(-1);
}
err = ioctl(audio_fd, AUDIO_GETINFO, &info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: getinfo failed",__FILE__,__LINE__,__FUNCTION__); return(error_exit(SNDLIB_CANT_READ,audio_fd));}
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_SPEAKERS_DEVICE:
case SNDLIB_LINE_OUT_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
balance = info.play.balance;
gain = info.play.gain;
if (balance <= (AUDIO_RIGHT_BALANCE/2))
{
lc = gain;
rc = gain * balance / (float)(AUDIO_RIGHT_BALANCE/2);
}
else
{
lc = gain * (AUDIO_RIGHT_BALANCE - balance) / (float)(AUDIO_RIGHT_BALANCE/2);
rc = gain;
}
if (chan == 0)
lc = AUDIO_MAX_GAIN * val[0];
else rc = AUDIO_MAX_GAIN * val[0];
if ((rc+lc) == 0)
info.play.gain = 0;
else
{
ratio = (float)rc/(float)(rc+lc);
info.play.balance = AUDIO_RIGHT_BALANCE * ratio;
if (rc>lc)
info.play.gain = rc;
else info.play.gain = lc;
}
break;
case SNDLIB_CHANNEL_FIELD: info.play.channels = (int)val[0]; break;
/* amd device only mono */
case SNDLIB_SRATE_FIELD: info.play.sample_rate = (int)val[0]; break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
break;
case SNDLIB_MICROPHONE_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
info.record.gain = AUDIO_MAX_GAIN * val[0];
info.record.balance = 0;
break;
case SNDLIB_CHANNEL_FIELD: info.record.channels = 1; break;
case SNDLIB_SRATE_FIELD: info.record.sample_rate = 8000; break;
case SNDLIB_IGAIN_FIELD: info.monitor_gain = AUDIO_MAX_GAIN * val[0]; break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
break;
case SNDLIB_LINE_IN_DEVICE:
case SNDLIB_READ_WRITE_DEVICE:
case SNDLIB_CD_IN_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
balance = info.record.balance;
gain = info.record.gain;
lc = gain * (float)(AUDIO_RIGHT_BALANCE - balance) / (float)AUDIO_RIGHT_BALANCE;
rc = gain - lc;
if (chan == 0)
lc = AUDIO_MAX_GAIN * val[0];
else rc = AUDIO_MAX_GAIN * val[0];
gain = (rc + lc);
if (gain == 0)
info.record.gain = 0;
else
{
info.record.balance = AUDIO_RIGHT_BALANCE * ((float)rc / (float)(rc+lc));
if (rc > lc)
info.record.gain = rc;
else info.record.gain = lc;
}
break;
case SNDLIB_CHANNEL_FIELD: info.record.channels = 1; break;
case SNDLIB_SRATE_FIELD: info.record.sample_rate = (int)val[0]; break;
case SNDLIB_IGAIN_FIELD: info.monitor_gain = AUDIO_MAX_GAIN * val[0]; break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
err = ioctl(audio_fd,AUDIO_SETINFO,&info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: setinfo failed",__FILE__,__LINE__,__FUNCTION__); return(error_exit(SNDLIB_CANT_WRITE,audio_fd));}
close_audio(audio_fd);
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
/* pause can be implemented with play.pause and record.pause */
static char *sun_format_name(int format)
{
switch (format)
{
#ifdef AUDIO_ENCODING_ALAW
case AUDIO_ENCODING_ALAW: return("alaw"); break;
#endif
#ifdef AUDIO_ENCODING_ULAW
case AUDIO_ENCODING_ULAW: return("ulaw"); break;
#endif
#ifdef AUDIO_ENCODING_DVI
case AUDIO_ENCODING_DVI: return("dvi adpcm"); break;
#endif
#ifdef AUDIO_ENCODING_LINEAR8
case AUDIO_ENCODING_LINEAR8: return("linear"); break;
#else
#ifdef AUDIO_ENCODING_PCM8
case AUDIO_ENCODING_PCM8: return("linear"); break;
#endif
#endif
#ifdef AUDIO_ENCODING_LINEAR
case AUDIO_ENCODING_LINEAR: return("linear"); break;
#else
#ifdef AUDIO_ENCODING_PCM16
case AUDIO_ENCODING_PCM16: return("linear"); break;
#endif
#endif
#ifdef AUDIO_ENCODING_NONE
case AUDIO_ENCODING_NONE: return("not audio"); break; /* dbri interface configured for something else */
#endif
}
return("unknown");
}
static char *sun_in_device_name(int dev)
{
if (dev == AUDIO_MICROPHONE) return("microphone");
if (dev == AUDIO_LINE_IN) return("line in");
if (dev == AUDIO_INTERNAL_CD_IN) return("cd");
if (dev == (AUDIO_MICROPHONE | AUDIO_LINE_IN)) return("microphone + line in");
if (dev == (AUDIO_MICROPHONE | AUDIO_LINE_IN | AUDIO_INTERNAL_CD_IN)) return("microphone + line in + cd");
if (dev == (AUDIO_MICROPHONE | AUDIO_INTERNAL_CD_IN)) return("microphone + cd");
if (dev == (AUDIO_LINE_IN | AUDIO_INTERNAL_CD_IN)) return("line in + cd");
return("unknown");
}
static char *sun_out_device_name(int dev)
{
if (dev == AUDIO_SPEAKER) return("speakers");
if (dev == AUDIO_LINE_OUT) return("line out");
if (dev == AUDIO_HEADPHONE) return("headphones");
if (dev == (AUDIO_SPEAKER | AUDIO_LINE_OUT)) return("speakers + line out");
if (dev == (AUDIO_SPEAKER | AUDIO_LINE_OUT | AUDIO_HEADPHONE)) return("speakers + line out + headphones");
if (dev == (AUDIO_SPEAKER | AUDIO_HEADPHONE)) return("speakers + headphones");
if (dev == (AUDIO_LINE_OUT | AUDIO_HEADPHONE)) return("line out + headphones");
return("unknown");
}
static char *sun_vol_name = NULL;
static char *sun_volume_name(float vol, int balance, int chans)
{
if (sun_vol_name == NULL) sun_vol_name = (char *)CALLOC(64,sizeof(char));
if (chans != 2)
sprintf(sun_vol_name,"%.3f",vol);
else
{
sprintf(sun_vol_name,"%.3f %.3f",
vol*(float)(AUDIO_RIGHT_BALANCE - balance)/(float)AUDIO_RIGHT_BALANCE,
vol*(float)balance/(float)AUDIO_RIGHT_BALANCE);
}
return(sun_vol_name);
}
static void describe_audio_state_1(void)
{
struct audio_info info;
#ifndef AUDIO_DEV_AMD
struct audio_device ad;
#else
int ad;
#endif
int audio_fd,err;
char *dev_name;
AUDIO_INITINFO(&info);
AUDIO_ERROR = SNDLIB_NO_ERROR;
if (!strbuf) strbuf = (char *)CALLOC(STRBUF_SIZE,sizeof(char));
if (getenv(AUDIODEV_ENV) != NULL) dev_name = getenv(AUDIODEV_ENV); else dev_name = DAC_NAME;
audio_fd = open(dev_name,O_RDONLY | O_NONBLOCK,0);
if (audio_fd == -1)
audio_fd = open("/dev/audioctl",O_RDONLY | O_NONBLOCK,0);
if (audio_fd == -1)
{
AUDIO_ERROR = SNDLIB_CANT_READ;
return;
}
err = ioctl(audio_fd,AUDIO_GETINFO,&info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: getinfo failed",__FILE__,__LINE__,__FUNCTION__); error_exit(SNDLIB_CANT_READ,audio_fd); return;}
err = ioctl(audio_fd,AUDIO_GETDEV,&ad);
if (err == -1) {fprintf(stderr,"%s[%d] %s: getdev failed",__FILE__,__LINE__,__FUNCTION__); error_exit(SNDLIB_CANT_READ,audio_fd); return;}
close_audio(audio_fd);
#ifndef AUDIO_DEV_AMD
sprintf(strbuf,"%s (version %s, configuration: %s)\n",ad.name,ad.version,ad.config);
#else
sprintf(strbuf,"type: %d\n",ad);
#endif
pprint(strbuf);
sprintf(strbuf,"output: %s\n srate: %d, vol: %s, chans: %d, format %d-bit %s\n",
sun_out_device_name(info.play.port),
info.play.sample_rate,
sun_volume_name((float)info.play.gain / (float)AUDIO_MAX_GAIN,info.play.balance,2),
info.play.channels,
info.play.precision,
sun_format_name(info.play.encoding));
pprint(strbuf);
sprintf(strbuf,"input: %s\n srate: %d, vol: %s, chans: %d, format %d-bit %s\n",
sun_in_device_name(info.record.port),
info.record.sample_rate,
sun_volume_name((float)info.record.gain / (float)AUDIO_MAX_GAIN,info.record.balance,2),
info.record.channels,
info.record.precision,
sun_format_name(info.record.encoding));
pprint(strbuf);
sprintf(strbuf,"input->output vol: %.3f\n",(float)info.monitor_gain / (float)AUDIO_MAX_GAIN);
pprint(strbuf);
if (info.play.pause) {sprintf(strbuf,"Playback is paused\n"); pprint(strbuf);}
if (info.record.pause) {sprintf(strbuf,"Recording is paused\n"); pprint(strbuf);}
if (info.output_muted) {sprintf(strbuf,"Output is muted\n"); pprint(strbuf);}
}
static struct audio_info saved_info;
void save_audio_state (void)
{
int audio_fd,err;
audio_fd = open("/dev/audioctl",O_RDONLY | O_NONBLOCK,0);
if (audio_fd != -1)
{
err = ioctl(audio_fd,AUDIO_GETINFO,&saved_info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: getinfo failed",__FILE__,__LINE__,__FUNCTION__); error_exit(SNDLIB_CANT_READ,audio_fd); return;}
close(audio_fd);
}
}
void restore_audio_state (void)
{
int audio_fd,err;
audio_fd = open("/dev/audioctl",O_WRONLY | O_NONBLOCK,0);
if (audio_fd != -1)
{
err = ioctl(audio_fd,AUDIO_SETINFO,&saved_info);
if (err == -1) {fprintf(stderr,"%s[%d] %s: setinfo failed",__FILE__,__LINE__,__FUNCTION__); error_exit(SNDLIB_CANT_READ,audio_fd); return;}
close(audio_fd);
}
}
#endif
/* ------------------------------- MACOS ----------------------------------------- */
#ifdef MACOS
#define AUDIO_OK
#include <Resources.h>
#include <Sound.h>
#include <SoundInput.h>
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);} /* if Audiomedia, multiple? */
char *audio_system_name(int system) {return("Mac");}
static int available_input_devices(void)
{
unsigned char *devname;
OSErr err;
int i;
Handle h;
devname = (unsigned char *)CALLOC(256,sizeof(char));
for (i=1;i<16;i++)
{
err = SPBGetIndexedDevice(i,devname,&h);
if (err != noErr) break;
}
FREE(devname);
return(i-1);
}
static int input_device_is_connected(long refnum)
{
OSErr err;
short connected;
err = SPBGetDeviceInfo(refnum,siDeviceConnected,&connected);
return(connected);
}
static int input_device_get_source(long refnum)
{
OSErr err;
short source;
err = SPBGetDeviceInfo(refnum,siInputSource,&source);
return(source);
}
static int input_device_set_source(long refnum, short source)
{
OSErr err;
err = SPBSetDeviceInfo(refnum,siInputSource,&source);
return((err == noErr) ? 0 : -1);
}
static int input_device_get_sources(long refnum, char **names)
{
OSErr err;
short sources;
Handle h;
err = SPBSetDeviceInfo(refnum,siInputSourceNames,&h);
if (err == siUnknownInfoType) return(0);
sources = (short)(*h);
/* printf("%d sources: %s ",sources,strdup(p2cstr((unsigned char *)(*(h+2))))); */
/* need an example to test this silly thing */
return((err == noErr) ? sources : -1);
}
static int input_device_channels (long refnum)
{
OSErr err;
short chans;
err = SPBGetDeviceInfo(refnum,siChannelAvailable,&chans);
if (err == noErr) return(chans);
return(-1);
}
static int input_device_get_async(long refnum)
{
OSErr err;
short async;
err = SPBGetDeviceInfo(refnum,siAsync,&async);
if (err == noErr) return(async);
return(-1);
}
static char *input_device_name(long refnum)
{
char *name;
OSErr err;
name = (char *)CALLOC(256,sizeof(char));
err = SPBGetDeviceInfo(refnum,siDeviceName,name);
if (err == noErr) return(name);
FREE(name);
return(NULL);
}
static float input_device_get_gain(long refnum)
{
OSErr err;
unsigned long val;
err = SPBGetDeviceInfo(refnum,siInputGain,&val);
/* val is a "4 byte fixed value between .5 and 1.5"!! */
if (err == noErr)
return((float)val/65536.0);
return(-1);
}
static int input_device_set_gain(long refnum, float gain)
{
OSErr err;
int val;
val = ((int)(gain*65536));
err = SPBSetDeviceInfo(refnum,siInputGain,&val);
return((err == noErr) ? 0 : -1);
}
static int input_device_get_channels(long refnum)
{
OSErr err;
short chans;
err = SPBGetDeviceInfo(refnum,siNumberChannels,&chans);
return((err == noErr) ? chans : -1);
}
static int input_device_set_channels(long refnum, short chans)
{
OSErr err;
err = SPBSetDeviceInfo(refnum,siNumberChannels,&chans);
return((err == noErr) ? 0 : -1);
}
static int input_device_get_quality(long refnum)
{
OSErr err;
OSType val;
err = SPBGetDeviceInfo(refnum,siRecordingQuality,&val);
if (err == noErr)
{
if (val == siCDQuality) return(3);
if (val == siBestQuality) return(2);
if (val == siBetterQuality) return(1);
if (val == siGoodQuality) return(0);
}
return(-1);
}
static int input_device_set_quality(long refnum, int quality)
{
OSErr err;
OSType val;
if (quality == 3) val = siCDQuality;
else if (quality == 2) val = siBestQuality;
else if (quality == 1) val = siBetterQuality;
else val = siGoodQuality;
err = SPBSetDeviceInfo(refnum,siRecordingQuality,&val);
return((err == noErr) ? 0 : -1);
}
static int input_device_get_srate(long refnum)
{
OSErr err;
unsigned long fixed_srate;
err = SPBGetDeviceInfo(refnum,siSampleRate,&fixed_srate);
if (err == noErr) return(fixed_srate>>16);
return(-1);
}
static int input_device_set_srate(long refnum, int srate)
{
OSErr err;
unsigned long fixed_srate;
fixed_srate = (unsigned long)(srate * 65536);
err = SPBSetDeviceInfo(refnum,siSampleRate,&fixed_srate);
return((err == noErr) ? 0 : -1);
}
static int input_device_get_sample_size(long refnum)
{
OSErr err;
short size;
err = SPBGetDeviceInfo(refnum,siSampleSize,&size);
if (err == noErr) return(size);
return(-1);
}
static int input_device_set_sample_size(long refnum, short size)
{
OSErr err;
err = SPBSetDeviceInfo(refnum,siSampleSize,&size);
return((err == noErr) ? 0 : -1);
}
static int input_device_get_signed(long refnum)
{ /* 0 = unsigned */
OSErr err;
short sign;
err = SPBGetDeviceInfo(refnum,siSampleRate,&sign);
if (err == noErr) return(sign);
return(-1);
}
static int input_device_set_signed(long refnum, short sign)
{
OSErr err;
err = SPBSetDeviceInfo(refnum,siSampleRate,&sign);
return((err == noErr) ? 0 : -1);
}
static int input_device_sample_rates(long refnum, int *range, int *rates)
{
unsigned short num;
int i,j;
unsigned long ptr;
OSErr err;
unsigned char pp[6]; /* can't depend on C compiler to pack a struct correctly here */
num = 0;
err = SPBGetDeviceInfo(refnum,siSampleRateAvailable,pp);
if (err == noErr)
{
num = pp[1] + (pp[0]<<8); /* unsigned short is first element */
if (num == 0) {(*range) = 1; num = 2;} else (*range) = 0;
ptr = pp[5] + (pp[4]<<8) + (pp[3]<<16) + (pp[2]<<24); /* pointer to "fixed" table is second element */
for (i=0,j=0;i<num;i++,j+=4)
rates[i] = (*(unsigned short *)(j+(*(int *)ptr))); /* ignore fraction -- this is dubious code */
}
return(num);
}
static int input_device_sample_sizes(long refnum, int *sizes)
{
unsigned short num;
int i,j;
unsigned long ptr;
OSErr err;
unsigned char pp[6];
num = 0;
err = SPBGetDeviceInfo(refnum,siSampleSizeAvailable,pp);
if (err == noErr)
{
num = pp[1] + (pp[0]<<8);
ptr = pp[5] + (pp[4]<<8) + (pp[3]<<16) + (pp[2]<<24);
for (i=0,j=0;i<num;i++,j+=2) sizes[i] = (*(unsigned short *)(j+(*(int *)ptr)));
}
return(num);
}
static int input_device_get_gains(long refnum, float *gains)
{
OSErr err;
long ptr[2];
err = SPBGetDeviceInfo(refnum,siStereoInputGain,ptr);
if (err == noErr)
{
gains[0] = (float)ptr[0]/65536.0;
gains[1] = (float)ptr[1]/65536.0;
}
else return(-1);
return(0);
}
static int input_device_set_gains(long refnum, float *gains)
{
OSErr err;
long val[2];
val[0] = gains[0]*65536;
val[1] = gains[1]*65536;
err = SPBSetDeviceInfo(refnum,siStereoInputGain,val);
return((err == noErr) ? 0 : -1);
}
char *audio_moniker(void)
{
NumVersion nv;
if (version_name == NULL) version_name = (char *)CALLOC(32,sizeof(char));
nv = SndSoundManagerVersion();
sprintf(version_name,"Mac audio: %d.%d.%d.%d\n",nv.majorRev,nv.minorAndBugRev,nv.stage,nv.nonRelRev);
return(version_name);
}
static char *quality_names[5] = {"indescribable","bad","not bad","ok","good"};
static void describe_audio_state_1(void)
{
long response;
NumVersion nv;
OSErr err;
int vals[64];
float gains[2];
int have_IO_mgr = 0, have_input_device = 0;
unsigned char *devname = NULL;
int i,j,devs,rates,range,sizes,connected;
long refnum;
Handle h;
if (!strbuf) strbuf = (char *)CALLOC(STRBUF_SIZE,sizeof(char));
nv = SndSoundManagerVersion();
sprintf(strbuf,"Sound Manager: %d.%d.%d.%d\n",nv.majorRev,nv.minorAndBugRev,nv.stage,nv.nonRelRev); pprint(strbuf);
err = Gestalt(gestaltSoundAttr,&response);
have_IO_mgr = (response & gestaltSoundIOMgrPresent);
have_input_device = (response & gestaltHasSoundInputDevice);
if (have_IO_mgr)
{
nv = SPBVersion();
sprintf(strbuf,"Sound Input Manager: %d.%d.%d.%d\n",nv.majorRev,nv.minorAndBugRev,nv.stage,nv.nonRelRev); pprint(strbuf);
}
if (!have_IO_mgr) pprint("Sound IO Manager absent!\n");
if (!(response & gestaltBuiltInSoundInput)) pprint("no built-in input device!\n");
if (!have_input_device) pprint("no input devices available!\n");
if (!(response & gestaltSndPlayDoubleBuffer)) pprint("double buffering not supported!\n");
if (response & gestalt16BitAudioSupport) pprint("has 16-bit audio ");
if (response & gestalt16BitSoundIO) pprint("has 16-bit sound ");
if (response & gestaltStereoInput) pprint("has stereo input\n");
if (response & gestaltPlayAndRecord) pprint("can play and record simultaneously\n");
if (response & gestaltMultiChannels) pprint("has multichannel support\n");
GetSysBeepVolume(&response);
sprintf(strbuf,"beep vol: %.3f %.3f\n",((float)(response>>16))/255.0,((float)(response&0xffff))/255.0); pprint(strbuf);
GetDefaultOutputVolume(&response);
sprintf(strbuf,"output vol: %.3f %.3f\n",((float)(response>>16))/255.0,((float)(response&0xffff))/255.0); pprint(strbuf);
if ((have_IO_mgr) && (have_input_device))
{
devs = available_input_devices();
if (devs>0)
{
devname = (unsigned char *)CALLOC(256,sizeof(char));
sprintf(strbuf,"input device%s:\n",(devs>1) ? "s" : ""); pprint(strbuf);
for (i=1;i<=devs;i++)
{
for (i=1;i<=devs;i++)
{
err = SPBGetIndexedDevice(i,devname,&h);
if (err == noErr)
{
err = SPBOpenDevice(devname,siWritePermission,&refnum);
if (err == noErr)
{
range = input_device_get_source(refnum);
connected = input_device_is_connected(refnum);
sprintf(strbuf," %s: %s%s",
(*devname) ? devname : (unsigned char *)"un-named",
((input_device_get_async(refnum) == 1) ? "(async) " : ""),
((connected == siDeviceIsConnected) ? "" :
((connected == siDeviceNotConnected) ?
"(not connected )" : "(might not be connected)")));
pprint(strbuf);
if (range == 0) pprint("\n");
else
{
sprintf(strbuf," (source: %d)\n",range);
pprint(strbuf);
}
sprintf(strbuf," %d chans available, %d active\n",
input_device_channels(refnum),
input_device_get_channels(refnum));
pprint(strbuf);
/* input_device_get_sources(refnum,NULL); */
range = 0;
rates = input_device_sample_rates(refnum,&range,vals);
if (rates>1)
{
sprintf(strbuf," srates available:");
pprint(strbuf);
if (range)
{sprintf(strbuf,"%d to %d",vals[0],vals[1]); pprint(strbuf);}
else
{for (j=0;j<rates;j++) {sprintf(strbuf," %d",vals[j]); pprint(strbuf);}}
sprintf(strbuf,", current srate: %d\n",
input_device_get_srate(refnum));
pprint(strbuf);
}
else
{
sprintf(strbuf," srate: %d\n",input_device_get_srate(refnum));
pprint(strbuf);
}
err = input_device_get_quality(refnum);
if (err != -1)
{
sprintf(strbuf," quality: %s\n",quality_names[1+input_device_get_quality(refnum)]);
pprint(strbuf);
}
input_device_get_gains(refnum,gains);
sprintf(strbuf," gain: %.3f (%.3f %.3f)\n sample: %s %d bits",
input_device_get_gain(refnum),gains[0],gains[1],
((input_device_get_signed(refnum)) ? "signed" : "unsigned"),
input_device_get_sample_size(refnum));
pprint(strbuf);
sizes = input_device_sample_sizes(refnum,vals);
if (sizes > 0)
{
sprintf(strbuf," (%d",vals[0]); pprint(strbuf);
for (j=1;j<sizes;j++)
{sprintf(strbuf,", %d",vals[j]); pprint(strbuf);}
pprint(" bit samples available)");
}
pprint("\n");
SPBCloseDevice(refnum);
}
}
}
}
FREE(devname);
}
}
}
#define BUFFER_FILLED 1
#define BUFFER_EMPTY 2
#define SOUND_UNREADY 0
#define SOUND_INITIALIZED 1
#define SOUND_RUNNING 2
#define INPUT_LINE 1
#define OUTPUT_LINE 2
static int buffer_size = 1024;
static SndDoubleBufferPtr *db = NULL;
static SndDoubleBufferHeader dh;
static SndChannelPtr chan;
static int *db_state = NULL;
static int sound_state = 0;
static int current_chans = 1;
static int current_datum_size = 2;
static int current_srate = 22050;
static int current_buf = 0;
static long in_ref = -1;
static SPB spb;
#define DATA_EMPTY 0
#define DATA_READY 1
#define DATA_WRITTEN 2
static int data_status = DATA_EMPTY;
static int data_bytes = 0;
static char *data = NULL;
#ifdef CLM
#ifdef MCL_PPC
static void reset_db(void)
{
db=NULL;
db_state=NULL;
data = NULL;
data_bytes = 0;
}
#endif
#endif
static pascal void nextbuffer(SndChannelPtr cp, SndDoubleBufferPtr db)
{
db_state[current_buf] = BUFFER_EMPTY;
}
int open_audio_output(int ur_dev, int srate, int chans, int format, int size)
{
OSErr err;
int dev;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
if (!db) db = (SndDoubleBufferPtr *)CALLOC(2,sizeof(SndDoubleBufferPtr));
if (!db_state) db_state = (int *)CALLOC(2,sizeof(int));
chan = nil;
err = SndNewChannel(&chan,sampledSynth,0,nil);
if (err != noErr) {AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE; return(-1);}
dh.dbhNumChannels = chans;
current_chans = chans;
if (format == SNDLIB_8_UNSIGNED)
{
dh.dbhSampleSize = 8;
current_datum_size = 1;
}
else
{
dh.dbhSampleSize = 16;
current_datum_size = 2;
}
dh.dbhCompressionID = 0;
dh.dbhPacketSize = 0;
dh.dbhSampleRate = (srate << 16);
dh.dbhDoubleBack = NewSndDoubleBackProc(nextbuffer);
if (size <= 0) buffer_size = 1024; else buffer_size = size;
db[0] = (SndDoubleBufferPtr)CALLOC(sizeof(SndDoubleBuffer) + buffer_size,sizeof(char));
if ((db[0] == nil) || (MemError() != 0)) {AUDIO_ERROR = SNDLIB_SIZE_NOT_AVAILABLE; SndDisposeChannel(chan,0); return(-1);}
dh.dbhBufferPtr[0] = db[0];
db[0]->dbNumFrames = 0;
db[0]->dbFlags = 0;
db_state[0] = BUFFER_EMPTY;
db[1] = (SndDoubleBufferPtr)CALLOC(sizeof(SndDoubleBuffer) + buffer_size,sizeof(char));
if ((db[1] == nil) || (MemError() != 0)) {AUDIO_ERROR = SNDLIB_SIZE_NOT_AVAILABLE; FREE(db[0]); SndDisposeChannel(chan,0); return(-1);}
dh.dbhBufferPtr[1] = db[1];
db[1]->dbNumFrames = 0;
db[1]->dbFlags = 0;
db_state[1] = BUFFER_EMPTY;
sound_state = SOUND_INITIALIZED;
current_buf = 0;
return(OUTPUT_LINE);
}
static OSErr fill_buffer(int dbi, char *inbuf, int instart, int bytes)
{
int i,j;
OSErr err;
err = noErr;
for (i=instart,j=0;j<bytes;j++,i++) db[dbi]->dbSoundData[j] = inbuf[i];
db_state[dbi] = BUFFER_FILLED;
db[dbi]->dbFlags = (db[dbi]->dbFlags | dbBufferReady);
db[dbi]->dbNumFrames = (bytes / (current_chans * current_datum_size));
if ((sound_state == SOUND_INITIALIZED) && (dbi == 1))
{
sound_state = SOUND_RUNNING;
err = SndPlayDoubleBuffer(chan,&dh);
}
return(err);
}
static OSErr wait_for_empty_buffer(int buf)
{
SCStatus Stats;
OSErr err;
err = noErr;
while (db_state[buf] != BUFFER_EMPTY)
{
err = SndChannelStatus(chan,sizeof(Stats),&Stats);
if ((err != noErr) || (!(Stats.scChannelBusy))) break;
}
return(err);
}
int write_audio(int line, char *buf, int bytes)
{
OSErr err;
int lim,leftover,start;
if (line != OUTPUT_LINE) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
AUDIO_ERROR = SNDLIB_NO_ERROR;
leftover = bytes;
start = 0;
while (leftover > 0)
{
lim = leftover;
if (lim > buffer_size) lim = buffer_size;
leftover -= lim;
err = wait_for_empty_buffer(current_buf);
if (err != noErr) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
err = fill_buffer(current_buf,buf,start,lim);
if (err != noErr) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
start += lim;
current_buf++;
if (current_buf>1) current_buf=0;
}
return(0);
}
int close_audio(int line)
{
OSErr err;
int i;
AUDIO_ERROR = SNDLIB_NO_ERROR;
if (line == OUTPUT_LINE)
{
/* fill with a few zeros, wait for empty flag */
if (sound_state != SOUND_UNREADY)
{
wait_for_empty_buffer(current_buf);
for (i=0;i<128;i++) db[current_buf]->dbSoundData[i] = 0;
db[current_buf]->dbFlags = (db[current_buf]->dbFlags | dbBufferReady | dbLastBuffer);
db[current_buf]->dbNumFrames = (128 / (current_chans * current_datum_size));
wait_for_empty_buffer(current_buf);
FREE(db[0]); db[0]=NULL;
FREE(db[1]); db[1]=NULL;
db_state[0] = BUFFER_EMPTY;
db_state[1] = BUFFER_EMPTY;
sound_state = SOUND_UNREADY;
err = SndDisposeChannel(chan,0);
/* this is the line that forced me to use FREE/CALLOC throughout! */
if (err != noErr) {AUDIO_ERROR = SNDLIB_CANT_CLOSE; return(-1);}
}
}
else
{
if (line == INPUT_LINE)
{
if (in_ref != -1)
{
data_status = DATA_EMPTY;
SPBStopRecording(in_ref);
if (spb.bufferPtr) FREE(spb.bufferPtr);
SPBCloseDevice(in_ref);
in_ref = -1;
}
}
else {AUDIO_ERROR = SNDLIB_CANT_CLOSE; return(-1);}
}
return(0);
}
static void read_callback(SPB *spb)
{
int i,lim;
if (data_status != DATA_EMPTY)
{
if (data_bytes > spb->bufferLength) lim=spb->bufferLength; else lim=data_bytes;
for (i=0;i<lim;i++) data[i] = spb->bufferPtr[i];
spb->bufferLength = data_bytes;
SPBRecord(spb,TRUE);
data_status = DATA_WRITTEN;
}
}
int open_audio_input(int ur_dev, int srate, int chans, int format, int size)
{
OSErr err;
short source;
int dev;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
data_status = DATA_EMPTY;
if (size<=0) size = 1024;
if (in_ref != -1) {AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE; return(-1);}
err = SPBOpenDevice((unsigned char *)"",siWritePermission,&in_ref);
if (err != noErr) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
spb.inRefNum = in_ref;
spb.count = size;
source = 3; /* the microphone ?? (2: CD, 4: modem, 0: none) -- nowhere is this documented! */
input_device_set_source(in_ref,source);
input_device_set_srate(in_ref,srate);
input_device_set_channels(in_ref,(short)chans);
input_device_set_sample_size(in_ref,(format == SNDLIB_16_LINEAR) ? 2 : 1);
input_device_set_signed(in_ref,(format == SNDLIB_16_LINEAR) ? 1 : 0);
spb.milliseconds = (int)((float)(size * 1000) / (float)(((format == SNDLIB_16_LINEAR) ? 2 : 1) * srate));
spb.bufferLength = size;
spb.bufferPtr = (char *)CALLOC(size,sizeof(char));
spb.completionRoutine = NewSICompletionProc(read_callback);
err = SPBRecord(&spb,TRUE);
return(INPUT_LINE);
}
int read_audio(int line, char *buf, int bytes)
{
OSErr err;
unsigned long total_samps,num_samps,total_msecs,num_msecs;
short level,status;
if (line != INPUT_LINE) {AUDIO_ERROR = SNDLIB_CANT_READ; return(-1);}
AUDIO_ERROR = SNDLIB_NO_ERROR;
data_status = DATA_READY;
data_bytes = bytes;
data = buf;
while (data_status == DATA_READY)
{
err = SPBGetRecordingStatus(in_ref,&status,&level,&total_samps,&num_samps,&total_msecs,&num_msecs);
if ((err != noErr) || (status <= 0)) break; /* not necessarily an error */
}
return(0);
}
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
OSErr err;
long response;
int dev;
float in_val[2];
int our_open = 0;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
switch (field)
{
case SNDLIB_CHANNEL_FIELD:
val[0] = 2;
break;
case SNDLIB_DEVICE_FIELD:
val[0] = 2;
if (chan>1) val[1] = SNDLIB_MICROPHONE_DEVICE;
if (chan>2) val[2] = SNDLIB_DAC_OUT_DEVICE;
break;
case SNDLIB_FORMAT_FIELD:
val[0] = 2;
if (chan>1) val[1] = SNDLIB_16_LINEAR;
if (chan>2) val[2] = SNDLIB_8_UNSIGNED;
break;
default:
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_SPEAKERS_DEVICE:
case SNDLIB_LINE_OUT_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
GetDefaultOutputVolume(&response);
if (chan == 0)
val[0] = ((float)(response>>16))/255.0;
else val[0] = ((float)(response&0xffff))/255.0;
break;
case SNDLIB_CHANNEL_FIELD: val[0] = 2; break;
case SNDLIB_SRATE_FIELD: val[0] = current_srate; break;
}
break;
case SNDLIB_MICROPHONE_DEVICE:
case SNDLIB_LINE_IN_DEVICE:
if (in_ref == -1)
{
err = SPBOpenDevice((const unsigned char *)"",siWritePermission,&in_ref);
if (err != noErr) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
our_open = 1;
}
switch (field)
{
case SNDLIB_AMP_FIELD:
err = input_device_get_gains(in_ref,in_val);
if (chan == 0) val[0] = in_val[0]; else val[0] = in_val[1];
break;
case SNDLIB_CHANNEL_FIELD:
err = input_device_get_channels(in_ref);
break;
case SNDLIB_SRATE_FIELD:
err = input_device_get_srate(in_ref);
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
if (err == -1)
AUDIO_ERROR = SNDLIB_CANT_READ;
else val[0] = err;
if (our_open)
{
SPBCloseDevice(in_ref);
in_ref = -1;
}
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
OSErr err;
float out_val[2];
long curval,newval;
int amp,our_open,dev;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_SPEAKERS_DEVICE:
case SNDLIB_LINE_OUT_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
amp = (int)(255 * val[0]);
GetDefaultOutputVolume(&curval);
if (chan == 0)
newval = ((curval & 0xffff0000) | (amp & 0xffff));
else newval = (((amp<<16) & 0xffff0000) | (curval & 0xffff));
SetDefaultOutputVolume(newval);
break;
case SNDLIB_CHANNEL_FIELD:
case SNDLIB_SRATE_FIELD: break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
break;
case SNDLIB_MICROPHONE_DEVICE:
case SNDLIB_LINE_IN_DEVICE:
if (in_ref == -1)
{
err = SPBOpenDevice((const unsigned char *)"",siWritePermission,&in_ref);
if (err != noErr) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
our_open = 1;
}
switch (field)
{
case SNDLIB_AMP_FIELD:
input_device_get_gains(in_ref,out_val);
if (chan == 0) out_val[0] = val[0]; else out_val[1] = val[0];
err = input_device_set_gains(in_ref,out_val);
break;
case SNDLIB_CHANNEL_FIELD:
err = input_device_set_channels(in_ref,(int)val[0]);
break;
case SNDLIB_SRATE_FIELD:
err = input_device_set_srate(in_ref,(int)val[0]);
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
if (err == -1)
AUDIO_ERROR = SNDLIB_CANT_READ;
if (our_open)
{
SPBCloseDevice(in_ref);
in_ref = -1;
}
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
static long beep_vol = 0,out_vol = 0;
void save_audio_state(void)
{
GetDefaultOutputVolume(&out_vol);
GetSysBeepVolume(&beep_vol);
}
void restore_audio_state(void)
{
SetDefaultOutputVolume(out_vol);
SetSysBeepVolume(beep_vol);
}
int initialize_audio(void)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
return(0);
}
#endif
/* ------------------------------- BEOS ----------------------------------------- */
#ifdef BEOS
#define AUDIO_OK
#include <Be.h>
#define OUTPUT_LINE 1
#define INPUT_LINE 2
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("Be");}
char *audio_moniker(void) {return("Be audio");}
static void describe_audio_state_1(void)
{
long chans;
float g[1];
float gleft,gright;
bool enabled;
BDACStream B_Stream;
BADCStream A_Stream;
if (!strbuf) strbuf = (char *)CALLOC(STRBUF_SIZE,sizeof(char));
read_audio_state(SNDLIB_DAC_OUT_DEVICE,SNDLIB_AMP_FIELD,0,g); gleft=g[0];
read_audio_state(SNDLIB_DAC_OUT_DEVICE,SNDLIB_AMP_FIELD,1,g); gright=g[0];
sprintf(strbuf,"dac vol: %.3f %.3f",gleft,gright); pprint(strbuf);
if ((gleft == 0.0) && (gright == 0.0) && (!(B_Stream.IsDeviceEnabled(B_DAC_OUT)))) pprint(" (muted)\n"); else pprint("\n");
read_audio_state(SNDLIB_LINE_OUT_DEVICE,SNDLIB_AMP_FIELD,0,g); gleft=g[0];
read_audio_state(SNDLIB_LINE_OUT_DEVICE,SNDLIB_AMP_FIELD,1,g); gright=g[0];
sprintf(strbuf,"line out vol: %.3f %.3f",gleft,gright); pprint(strbuf);
if ((gleft == 0.0) && (gright == 0.0) && (!(B_Stream.IsDeviceEnabled(B_MASTER_OUT)))) pprint(" (muted)\n"); else pprint("\n");
read_audio_state(SNDLIB_SPEAKERS_DEVICE,SNDLIB_AMP_FIELD,0,g); gleft=g[0];
read_audio_state(SNDLIB_SPEAKERS_DEVICE,SNDLIB_AMP_FIELD,1,g); gright=g[0];
sprintf(strbuf,"speaker vol: %.3f %.3f",gleft,gright); pprint(strbuf);
if ((gleft == 0.0) && (gright == 0.0) && (!(B_Stream.IsDeviceEnabled(B_SPEAKER_OUT)))) pprint(" (muted)\n"); else pprint("\n");
if (A_Stream.IsMicBoosted()) pprint("mic boosted\n");
read_audio_state(SNDLIB_DAC_OUT_DEVICE,SNDLIB_SRATE_FIELD,0,g);
sprintf(strbuf,"srate: %d\n",(int)g[0]); pprint(strbuf);
B_Stream.GetVolume(B_CD_THROUGH,&gleft,&gright,&enabled);
if (!enabled)
pprint("CD muted\n");
else
{
sprintf(strbuf,"CD through vol: %.3f %.3f\n",gleft,gright);
pprint(strbuf);
}
B_Stream.GetVolume(B_LOOPBACK,&gleft,&gright,&enabled);
if (!enabled)
pprint("loopback muted\n");
else
{
sprintf(strbuf,"loopback vol: %.3f %.3f\n",gleft,gright);
pprint(strbuf);
}
B_Stream.GetVolume(B_LINE_IN_THROUGH,&gleft,&gright,&enabled);
if (!enabled)
pprint("line in through muted\n");
else
{
sprintf(strbuf,"line in through vol: %.3f %.3f\n",gleft,gright);
pprint(strbuf);
}
A_Stream.ADCInput(&chans);
if (chans == B_CD_IN) pprint("CD in\n");
else if (chans == B_LINE_IN) pprint("line in\n");
else pprint("mic in\n");
/* SetADCInput apparently to B_CD|LINE|MIC_IN */
}
static BSubscriber B_dac_sub; /* BAudioSubscriber? */
static BDACStream B_dac_stream;
static BSubscriber B_adc_sub;
static BADCStream B_adc_stream;
#define DATA_EMPTY 0
#define DATA_READY 1
#define DATA_WRITTEN 2
static int data_status;
static int data_bytes;
static char *data = NULL;
#define SOUND_UNREADY 0
#define SOUND_INITIALIZED 1
#define SOUND_RUNNING 2
static int sound_state = SOUND_UNREADY;
static bool read_callback(void *userData, char *buf, unsigned long count, void *hdr)
{
int i,lim;
short *sys,*loc;
if (data_status != DATA_EMPTY)
{
if (data_bytes > count) lim=count; else lim=data_bytes;
for (i=0;i<lim;i++) data[i] = buf[i];
data_status = DATA_WRITTEN;
}
return(TRUE);
/* return TRUE to continue writing, FALSE to exit stream */
}
static bool write_callback(void *userData, char *buf, unsigned long count, void *hdr)
{
int i,lim;
short *sys,*loc;
if (data_status != DATA_EMPTY)
{
if (data_bytes > count) lim=count; else lim=data_bytes;
lim = lim/2;
sys = (short *)buf;
loc = (short *)data;
for (i=0;i<lim;i++) sys[i]+=loc[i];
data_status = DATA_WRITTEN;
}
return(TRUE);
/* return TRUE to continue writing, FALSE to exit stream */
}
int open_audio_output(int ur_dev, int srate, int chans, int format, int size)
{
status_t err;
int dev;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
if (chans != 2) {AUDIO_ERROR = SNDLIB_CHANNELS_NOT_AVAILABLE; return(-1);}
if (format != SNDLIB_16_LINEAR) {AUDIO_ERROR = SNDLIB_FORMAT_NOT_AVAILABLE; return(-1);}
if ((srate != 44100) && (srate != 22050)) {AUDIO_ERROR = SNDLIB_SRATE_NOT_AVAILABLE; return(-1);}
if ((B_dac_sub.Subscribe(&B_dac_stream)) == B_SNDLIB_NO_ERROR)
{
err = B_dac_stream.SetSamplingRate(srate);
if (err != B_SNDLIB_NO_ERROR) fprintf(stdout,"can't reset srate ");
err = B_dac_stream.SetStreamBuffers(size,8); /* size, count */
if (err != B_SNDLIB_NO_ERROR) fprintf(stdout,"can't reset size ");
data_status = DATA_EMPTY;
sound_state = SOUND_INITIALIZED;
}
else {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
return(OUTPUT_LINE);
}
int open_audio_input(int ur_dev, int srate, int chans, int format, int size)
{
status_t err;
int dev;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
if ((chans != 2) || (format != SNDLIB_16_LINEAR)) {AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE; return(-1);}
if ((srate != 44100) && (srate != 22050)) {AUDIO_ERROR = SNDLIB_SRATE_NOT_AVAILABLE; return(-1);}
if ((B_adc_sub.Subscribe(&B_adc_stream)) == B_SNDLIB_NO_ERROR)
{
err = B_adc_stream.SetSamplingRate(srate);
if (err != B_SNDLIB_NO_ERROR) fprintf(stdout,"can't reset srate ");
err = B_adc_stream.SetStreamBuffers(size,8); /* size, count */
if (err != B_SNDLIB_NO_ERROR) fprintf(stdout,"can't reset size ");
err = B_adc_sub.EnterStream(NULL,TRUE,NULL,read_callback,NULL,TRUE);
if (err != B_SNDLIB_NO_ERROR) fprintf(stdout,"can't enter stream ");
data_status = DATA_EMPTY;
/* no "neighbor", place at front, no user data, call read_callback, no exit function, execute in separate thread */
}
else {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
return(INPUT_LINE);
}
int write_audio(int line, char *buf, int bytes)
{
size_t size;
status_t err;
long count,subs;
bool runs;
if (line != OUTPUT_LINE) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
AUDIO_ERROR = SNDLIB_NO_ERROR;
if (sound_state == SOUND_UNREADY) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
if (sound_state == SOUND_INITIALIZED)
{
err = B_dac_sub.EnterStream(NULL,TRUE,NULL,write_callback,NULL,TRUE);
if (err != B_SNDLIB_NO_ERROR) fprintf(stdout,"can't enter stream ");
/* no "neighbor", place at front, no user data, call write_callback, no exit function, execute in separate thread */
sound_state = SOUND_RUNNING;
}
data_status = DATA_READY;
data_bytes = bytes;
data = buf;
while (data_status == DATA_READY)
{
B_dac_stream.GetStreamParameters(&size,&count,&runs,&subs);
if (!runs) {AUDIO_ERROR = SNDLIB_CANT_WRITE; B_dac_sub.ExitStream(); sound_state = SOUND_UNREADY; return(-1);}
}
return(0);
}
int close_audio(int line)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
if (line == OUTPUT_LINE)
{
B_dac_sub.Unsubscribe();
sound_state = SOUND_UNREADY;
}
else
{
if (line == INPUT_LINE)
B_adc_sub.Unsubscribe();
else {AUDIO_ERROR = CANT_CLOSE; return(-1);}
}
return(0);
}
int read_audio(int line, char *buf, int bytes)
{
size_t size;
long count,subs;
bool runs;
if (line != INPUT_LINE) {AUDIO_ERROR = SNDLIB_CANT_READ; return(-1);}
AUDIO_ERROR = SNDLIB_NO_ERROR;
data_status = DATA_READY;
data_bytes = bytes;
data = buf;
while (data_status == DATA_READY)
{
B_adc_stream.GetStreamParameters(&size,&count,&runs,&subs);
if (!runs) {AUDIO_ERROR = SNDLIB_CANT_READ; B_adc_sub.ExitStream(); return(-1);}
}
return(0);
}
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
/* also B_CD_THROUGH */
float gleft,gright,g0,g1;
bool enabled;
int dev;
status_t err;
BDACStream B_stream;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
switch (field)
{
case SNDLIB_AMP_FIELD:
{
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
err = B_stream.GetVolume(B_DAC_OUT,&gleft,&gright,&enabled);
if (!enabled)
val[0] = 0.0;
else
{
if (chan == 0)
val[0] = gleft;
else val[0] = gright;
}
break;
case SNDLIB_SPEAKERS_DEVICE:
err = B_stream.GetVolume(B_DAC_OUT,&g0,&g1,&enabled);
if (!enabled)
val[0] = 0.0;
else
{
err = B_stream.GetVolume(B_MASTER_OUT,&gleft,&gright,&enabled);
if (!enabled)
val[0] = 0.0;
else
{
g0 *= gleft; g1 *= gright;
err = B_stream.GetVolume(B_SPEAKER_OUT,&gleft,&gright,&enabled);
if (!enabled)
val[0] = 0.0;
else
{
if (chan == 0)
val[0] = g0 * gleft;
else val[0] = g1 * gright;
}
}
}
break;
case SNDLIB_LINE_OUT_DEVICE:
err = B_stream.GetVolume(B_DAC_OUT,&g0,&g1,&enabled);
if (!enabled)
val[0] = 0.0;
else
{
err = B_stream.GetVolume(B_MASTER_OUT,&gleft,&gright,&enabled);
if (!enabled)
val[0] = 0.0;
else
{
if (chan == 0)
val[0] = g0 * gleft;
else val[0] = g1 * gright;
}
}
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
break;
}
case SNDLIB_SRATE_FIELD:
err = B_stream.SamplingRate(&g0);
val[0] = g0;
break;
case SNDLIB_CHANNEL_FIELD:
val[0] = 2;
break;
case SNDLIB_DEVICE_FIELD:
val[0] = 3;
if (chan>1) val[1] = SNDLIB_MICROPHONE_DEVICE;
if (chan>2) val[2] = SNDLIB_DAC_OUT_DEVICE;
if (chan>3) val[3] = LINE_IN_DEVICE;
break;
case SNDLIB_FORMAT_FIELD:
val[0] = 1;
if (chan>1) val[1] = SNDLIB_16_LINEAR;
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
return(0);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
long srate;
int dev;
status_t err;
BDACStream B_stream;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
switch (field)
{
case SNDLIB_AMP_FIELD:
{
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
if ((val[0] > 0.0) && (!(B_stream.IsDeviceEnabled(B_DAC_OUT))))
B_stream.EnableDevice(B_DAC_OUT,TRUE);
if (chan == 0)
err = B_stream.SetVolume(B_DAC_OUT,val[0],B_NO_CHANGE);
else err = B_stream.SetVolume(B_DAC_OUT,B_NO_CHANGE,val[0]);
break;
case SNDLIB_SPEAKERS_DEVICE:
if ((val[0] > 0.0) && (!(B_stream.IsDeviceEnabled(B_SPEAKER_OUT))))
B_stream.EnableDevice(B_SPEAKER_OUT,TRUE);
if (chan == 0)
err = B_stream.SetVolume(B_SPEAKER_OUT,val[0],B_NO_CHANGE);
else err = B_stream.SetVolume(B_SPEAKER_OUT,B_NO_CHANGE,val[0]);
break;
case SNDLIB_LINE_OUT_DEVICE:
if ((val[0] > 0.0) && (!(B_stream.IsDeviceEnabled(B_MASTER_OUT))))
B_stream.EnableDevice(B_MASTER_OUT,TRUE);
if (chan == 0)
err = B_stream.SetVolume(B_MASTER_OUT,val[0],B_NO_CHANGE);
else err = B_stream.SetVolume(B_MASTER_OUT,B_NO_CHANGE,val[0]);
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
break;
}
case SNDLIB_SRATE_FIELD:
srate = val[0];
err = B_stream.SetSamplingRate(srate);
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
return(0);
}
#define BE_DEVICES 6
/* B_ADC_IN exists but seems to return garbage */
static float be_gains[BE_DEVICES * 2];
static int be_mutes[BE_DEVICES];
static int be_srate;
static int be_devices[BE_DEVICES] = {B_CD_THROUGH,B_LINE_IN_THROUGH,B_LOOPBACK,B_DAC_OUT,B_MASTER_OUT,B_SPEAKER_OUT};
void save_audio_state(void)
{
int i,j;
bool enabled;
float gleft,gright;
BDACStream B_Stream;
BADCStream A_Stream;
B_Stream.SamplingRate(&gleft);
be_srate = gleft;
for (i=0,j=0;i<BE_DEVICES;i++,j+=2)
{
B_Stream.GetVolume(be_devices[i],&gleft,&gright,&enabled);
be_gains[j] = gleft;
be_gains[j+1] = gright;
be_mutes[i] = enabled;
}
}
void restore_audio_state(void)
{
int i,j;
BDACStream B_Stream;
BADCStream A_Stream;
B_Stream.SetSamplingRate(be_srate);
for (i=0,j=0;i<BE_DEVICES;i++,j+=2)
{
B_Stream.EnableDevice(be_devices[i],be_mutes[i]);
B_Stream.SetVolume(be_devices[i],be_gains[j],be_gains[j+1]);
}
}
int initialize_audio(void)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
return(0);
}
#endif
/* ------------------------------- HPUX ----------------------------------------- */
/* if this is basically the same as the Sun case with different macro names,
* then it could perhaps be updated to match the new Sun version above --
* Sun version changed 28-Jan-99
*/
#if defined(HPUX) && (!(defined(AUDIO_OK)))
#define AUDIO_OK
#include <sys/audio.h>
char *audio_moniker(void) {return("HPUX audio");}
int open_audio_output(int ur_dev, int srate, int chans, int format, int size)
{
int fd,i,dev;
struct audio_describe desc;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
fd = open("/dev/audio",O_RDWR);
if (fd == -1) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
ioctl(fd,AUDIO_SET_CHANNELS,chans);
if (dev == SNDLIB_SPEAKERS_DEVICE)
ioctl(fd,AUDIO_SET_OUTPUT,AUDIO_OUT_SPEAKER);
else
if (dev == SNDLIB_LINE_OUT_DEVICE)
ioctl(fd,AUDIO_SET_OUTPUT,AUDIO_OUT_LINE);
else ioctl(fd,AUDIO_SET_OUTPUT,AUDIO_OUT_HEADPHONE);
if (format == SNDLIB_16_LINEAR)
ioctl(fd,AUDIO_SET_DATA_FORMAT,AUDIO_FORMAT_LINEAR16BIT);
else
if (format == SNDLIB_8_MULAW)
ioctl(fd,AUDIO_SET_DATA_FORMAT,AUDIO_FORMAT_ULAW);
else
if (format == SNDLIB_8_ALAW)
ioctl(fd,AUDIO_SET_DATA_FORMAT,AUDIO_FORMAT_ALAW);
else {AUDIO_ERROR = SNDLIB_FORMAT_NOT_AVAILABLE; close(fd); return(-1);}
ioctl(fd,AUDIO_DESCRIBE,&desc);
for(i=0;i<desc.nrates;i++) if(srate == desc.sample_rate[i]) break;
if (i == desc.nrates) {AUDIO_ERROR = SRATE_NOT_AVAILABLE; close(fd); return(-1);}
ioctl(fd,AUDIO_SET_SAMPLE_RATE,srate);
return(fd);
}
int write_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
write(line,buf,bytes);
return(0);
}
int close_audio(int line)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
close(line);
return(0);
}
static void describe_audio_state_1(void)
{
struct audio_describe desc;
struct audio_gain gain;
int mina,maxa,fd,tmp;
int g[2];
fd = open("/dev/audio",O_RDWR);
if (fd == -1) return;
ioctl(fd,AUDIO_GET_OUTPUT,&tmp);
switch (tmp)
{
case AUDIO_OUT_SPEAKER: pprint("output: speakers\n"); break;
case AUDIO_OUT_HEADPHONE: pprint("output: headphone\n"); break;
case AUDIO_OUT_LINE: pprint("output: line out\n"); break;
}
ioctl(fd,AUDIO_GET_INPUT,&tmp);
switch (tmp)
{
case AUDIO_IN_MIKE: pprint("input: mic\n"); break;
case AUDIO_IN_LINE: pprint("input: line in\n"); break;
}
ioctl(fd,AUDIO_GET_DATA_FORMAT,&tmp);
switch (tmp)
{
case AUDIO_FORMAT_LINEAR16BIT: pprint("format: 16-bit linear\n"); break;
case AUDIO_FORMAT_ULAW: pprint("format: mulaw\n"); break;
case AUDIO_FORMAT_ALAW: pprint("format: alaw\n"); break;
}
ioctl(fd,AUDIO_DESCRIBE,&desc);
gain.channel_mask = (AUDIO_CHANNEL_LEFT | AUDIO_CHANNEL_RIGHT);
ioctl(fd,AUDIO_GET_GAINS,&gain);
close(fd);
if (!strbuf) strbuf = (char *)CALLOC(STRBUF_SIZE,sizeof(char));
g[0] = gain.cgain[0].transmit_gain; g[1] = gain.cgain[1].transmit_gain;
mina = desc.min_transmit_gain; maxa = desc.max_transmit_gain;
sprintf(strbuf,"out vols: %.3f %.3f\n",(float)(g[0]-mina)/(float)(maxa-mina),(float)(g[1]-mina)/(float)(maxa-mina)); pprint(strbuf);
g[0] = gain.cgain[0].receive_gain; g[1] = gain.cgain[1].receive_gain;
mina = desc.min_receive_gain; maxa = desc.max_receive_gain;
sprintf(strbuf,"in vols: %.3f %.3f\n",(float)(g[0]-mina)/(float)(maxa-mina),(float)(g[1]-mina)/(float)(maxa-mina)); pprint(strbuf);
g[0] = gain.cgain[0].monitor_gain; g[1] = gain.cgain[1].monitor_gain;
mina = desc.min_monitor_gain; maxa = desc.max_monitor_gain;
sprintf(strbuf,"monitor vols: %.3f %.3f\n",(float)(g[0]-mina)/(float)(maxa-mina),(float)(g[1]-mina)/(float)(maxa-mina)); pprint(strbuf);
}
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
struct audio_describe desc;
struct audio_gain gain;
int audio_fd,srate,g,maxa,mina,dev;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
if (field == SNDLIB_DEVICE_FIELD)
{
val[0] = 4;
if (chan>1) val[1] = SNDLIB_MICROPHONE_DEVICE;
if (chan>2) val[2] = SNDLIB_DAC_OUT_DEVICE;
if (chan>3) val[3] = SNDLIB_LINE_OUT_DEVICE;
if (chan>4) val[4] = SNDLIB_LINE_IN_DEVICE;
}
else
{
if (field == FORMAT_FIELD)
{
val[0] = 3;
if (chan>1) val[1] = SNDLIB_16_LINEAR;
if (chan>2) val[2] = SNDLIB_8_MULAW;
if (chan>3) val[3] = SNDLIB_8_ALAW;
}
else
{
audio_fd = open("/dev/audio",O_RDWR);
ioctl(audio_fd,AUDIO_DESCRIBE,&desc);
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_SPEAKERS_DEVICE:
case SNDLIB_LINE_OUT_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
ioctl(audio_fd,AUDIO_GET_GAINS,&gain);
if (chan == 0) g = gain.cgain[0].transmit_gain; else g = gain.cgain[1].transmit_gain;
mina = desc.min_transmit_gain; maxa = desc.max_transmit_gain;
val[0] = (float)(g-mina)/(float)(maxa-mina);
break;
case SNDLIB_CHANNEL_FIELD: val[0] = 2; break;
case SNDLIB_SRATE_FIELD:
ioctl(audio_fd,AUDIO_GET_SAMPLE_RATE,&srate);
val[0] = srate;
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
break;
case SNDLIB_MICROPHONE_DEVICE:
case SNDLIB_LINE_IN_DEVICE:
case SNDLIB_READ_WRITE_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
ioctl(audio_fd,AUDIO_GET_GAINS,&gain);
if (chan == 0) g = gain.cgain[0].receive_gain; else g = gain.cgain[1].receive_gain;
mina = desc.min_receive_gain; maxa = desc.max_receive_gain;
val[0] = (float)(g-mina)/(float)(maxa-mina);
break;
case SNDLIB_CHANNEL_FIELD: val[0] = 2; break;
case SNDLIB_SRATE_FIELD:
ioctl(audio_fd,AUDIO_GET_SAMPLE_RATE,&srate);
val[0] = srate;
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
break;
default: AUDIO_ERROR = SNDLIB_CANT_READ; break;
}
close(audio_fd);
}
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
struct audio_describe desc;
struct audio_gain gain;
int audio_fd,srate,g,maxa,mina,dev;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
audio_fd = open("/dev/audio",O_RDWR);
ioctl(audio_fd,AUDIO_DESCRIBE,&desc);
switch (dev)
{
case SNDLIB_DEFAULT_DEVICE:
case SNDLIB_DAC_OUT_DEVICE:
case SNDLIB_SPEAKERS_DEVICE:
case SNDLIB_LINE_OUT_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
mina = desc.min_transmit_gain; maxa = desc.max_transmit_gain;
ioctl(audio_fd,AUDIO_GET_GAINS,&gain);
g = mina + val[0] * (maxa-mina);
if (chan == 0) gain.cgain[0].transmit_gain = g; else gain.cgain[1].transmit_gain = g;
ioctl(audio_fd,AUDIO_SET_GAINS,&gain);
break;
case SNDLIB_SRATE_FIELD:
srate = val[0];
ioctl(audio_fd,AUDIO_SET_SAMPLE_RATE,srate);
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
break;
case SNDLIB_MICROPHONE_DEVICE:
case SNDLIB_LINE_IN_DEVICE:
case SNDLIB_READ_WRITE_DEVICE:
switch (field)
{
case SNDLIB_AMP_FIELD:
mina = desc.min_receive_gain; maxa = desc.max_receive_gain;
ioctl(audio_fd,AUDIO_GET_GAINS,&gain);
g = mina + val[0] * (maxa-mina);
if (chan == 0) gain.cgain[0].receive_gain = g; else gain.cgain[1].receive_gain = g;
ioctl(audio_fd,AUDIO_SET_GAINS,&gain);
break;
case SNDLIB_SRATE_FIELD:
srate = val[0];
ioctl(audio_fd,AUDIO_SET_SAMPLE_RATE,srate);
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
break;
default: AUDIO_ERROR = SNDLIB_CANT_WRITE; break;
}
close(audio_fd);
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
static int saved_gains[6];
void save_audio_state(void)
{
int fd;
struct audio_gain gain;
gain.channel_mask = (AUDIO_CHANNEL_LEFT | AUDIO_CHANNEL_RIGHT);
fd = open("/dev/audio",O_RDWR);
ioctl(fd,AUDIO_GET_GAINS,&gain);
close(fd);
saved_gains[0] = gain.cgain[0].transmit_gain;
saved_gains[1] = gain.cgain[0].receive_gain;
saved_gains[2] = gain.cgain[0].monitor_gain;
saved_gains[3] = gain.cgain[1].transmit_gain;
saved_gains[4] = gain.cgain[1].receive_gain;
saved_gains[5] = gain.cgain[1].monitor_gain;
}
void restore_audio_state(void)
{
int fd;
struct audio_gain gain;
gain.channel_mask = (AUDIO_CHANNEL_LEFT | AUDIO_CHANNEL_RIGHT);
fd = open("/dev/audio",O_RDWR);
ioctl(fd,AUDIO_GET_GAINS,&gain);
gain.cgain[0].transmit_gain = saved_gains[0];
gain.cgain[0].receive_gain = saved_gains[1];
gain.cgain[0].monitor_gain = saved_gains[2];
gain.cgain[1].transmit_gain = saved_gains[3];
gain.cgain[1].receive_gain = saved_gains[4];
gain.cgain[1].monitor_gain = saved_gains[5];
ioctl(fd,AUDIO_SET_GAINS,&gain);
}
int initialize_audio(void) {AUDIO_ERROR = SNDLIB_NO_ERROR; return(0);}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("HPUX");}
/* struct audio_status status_b;
* ioctl(devAudio, AUDIO_GET_STATUS, &status_b)
* not_busy = (status_b.transmit_status == AUDIO_DONE);
*/
int open_audio_input(int ur_dev, int srate, int chans, int format, int size)
{
int fd,i,dev;
struct audio_describe desc;
AUDIO_ERROR = SNDLIB_NO_ERROR;
dev = SNDLIB_DEVICE(ur_dev);
fd = open("/dev/audio",O_RDWR);
if (fd == -1) {AUDIO_ERROR = SNDLIB_CANT_OPEN; return(-1);}
ioctl(fd,AUDIO_SET_CHANNELS,chans);
if (dev == SNDLIB_MICROPHONE_DEVICE)
ioctl(fd,AUDIO_SET_INPUT,AUDIO_IN_MIKE);
else ioctl(fd,AUDIO_SET_INPUT,AUDIO_IN_LINE);
if (format == SNDLIB_16_LINEAR)
ioctl(fd,AUDIO_SET_DATA_FORMAT,AUDIO_FORMAT_LINEAR16BIT);
else
if (format == SNDLIB_8_MULAW)
ioctl(fd,AUDIO_SET_DATA_FORMAT,AUDIO_FORMAT_ULAW);
else
if (format == SNDLIB_8_ALAW)
ioctl(fd,AUDIO_SET_DATA_FORMAT,AUDIO_FORMAT_ALAW);
else {AUDIO_ERROR = SNDLIB_FORMAT_NOT_AVAILABLE; close(fd); return(-1);}
ioctl(fd,AUDIO_DESCRIBE,&desc);
for(i=0;i<desc.nrates;i++) if(srate == desc.sample_rate[i]) break;
if (i == desc.nrates) {AUDIO_ERROR = SNDLIB_SRATE_NOT_AVAILABLE; close(fd); return(-1);}
ioctl(fd,AUDIO_SET_SAMPLE_RATE,srate);
return(fd);
}
int read_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
read(line,buf,bytes);
return(0);
}
#endif
/* ------------------------------- WINDOZE ----------------------------------------- */
#if defined(WINDOZE) && (!(defined(__CYGWIN__)))
#define AUDIO_OK
#include <windows.h>
#include <mmsystem.h>
#include <mmreg.h>
#define BUFFER_FILLED 1
#define BUFFER_EMPTY 2
#define OUTPUT_LINE 1
#define INPUT_LINE 2
#define SOUND_UNREADY 0
#define SOUND_INITIALIZED 1
#define SOUND_RUNNING 2
static int buffer_size = 1024;
static int db_state[2];
static int sound_state = 0;
static int current_chans = 1;
static int current_datum_size = 2;
static int current_buf = 0;
WAVEHDR wh[2];
HWAVEOUT fd;
HWAVEIN record_fd;
WAVEHDR rec_wh;
static int rec_state = SOUND_UNREADY;
static MMRESULT win_in_err = 0,win_out_err=0;
static char errstr[128],getstr[128];
char *audio_error_name(int err)
{
if ((win_in_err == 0) && (win_out_err == 0)) return(audio_error_name_1(err));
if (win_in_err)
waveInGetErrorText(win_in_err,getstr,128);
else waveOutGetErrorText(win_out_err,getstr,128);
sprintf(errstr,"%s: %s",audio_error_name_1(err),getstr);
return(errstr);
}
int audio_systems(void)
{
/* this number is available -- see below (user mixer number as in linux)->mixerGetNumDevs */
return(1);
}
char *audio_system_name(int system) {return("Windoze");}
DWORD CALLBACK next_buffer(HWAVEOUT w, UINT msg, DWORD user_data, DWORD p1, DWORD p2)
{
if (msg == WOM_DONE)
{
db_state[current_buf] = BUFFER_EMPTY;
}
return(0);
}
int open_audio_output(int ur_dev, int srate, int chans, int format, int size)
{
WAVEFORMATEX wf;
int dev;
AUDIO_ERROR = SNDLIB_NO_ERROR; win_out_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
wf.nChannels = chans;
current_chans = chans;
wf.wFormatTag = WAVE_FORMAT_PCM;
wf.cbSize = 0;
if (format == SNDLIB_8_UNSIGNED)
{
wf.wBitsPerSample = 8;
current_datum_size = 1;
}
else
{
wf.wBitsPerSample = 16;
current_datum_size = 2;
}
wf.nSamplesPerSec = srate;
wf.nBlockAlign = chans * current_datum_size;
wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
win_out_err = waveOutOpen(&fd,WAVE_MAPPER,&wf,(DWORD)next_buffer,0,CALLBACK_FUNCTION); /* 0 here = user_data above, other case = WAVE_FORMAT_QUERY */
if (win_out_err) {AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE; return(-1);}
waveOutPause(fd);
if (size <= 0) buffer_size = 1024; else buffer_size = size;
wh[0].dwBufferLength = buffer_size * current_datum_size;
wh[0].dwFlags = 0;
wh[0].dwLoops = 0;
wh[0].lpData = (char *)CALLOC(wh[0].dwBufferLength,sizeof(char));
if ((wh[0].lpData) == 0) {AUDIO_ERROR = SNDLIB_SIZE_NOT_AVAILABLE; waveOutClose(fd); return(-1);}
win_out_err = waveOutPrepareHeader(fd,&(wh[0]),sizeof(WAVEHDR));
if (win_out_err)
{
AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
FREE(wh[0].lpData);
waveOutClose(fd);
return(-1);
}
db_state[0] = BUFFER_EMPTY;
wh[1].dwBufferLength = buffer_size * current_datum_size;
wh[1].dwFlags = 0;
wh[1].dwLoops = 0;
wh[1].lpData = (char *)CALLOC(wh[0].dwBufferLength,sizeof(char));
if ((wh[1].lpData) == 0) {AUDIO_ERROR = SNDLIB_SIZE_NOT_AVAILABLE; FREE(wh[0].lpData); waveOutClose(fd); return(-1);}
win_out_err = waveOutPrepareHeader(fd,&(wh[1]),sizeof(WAVEHDR));
if (win_out_err)
{
AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
waveOutUnprepareHeader(fd,&(wh[0]),sizeof(WAVEHDR));
FREE(wh[0].lpData);
FREE(wh[1].lpData);
waveOutClose(fd);
return(-1);
}
db_state[1] = BUFFER_EMPTY;
sound_state = SOUND_INITIALIZED;
current_buf = 0;
return(OUTPUT_LINE);
}
static MMRESULT fill_buffer(int dbi, char *inbuf, int instart, int bytes)
{
int i,j;
win_out_err = 0;
if (sound_state == SOUND_UNREADY) return(0);
for (i=instart,j=0;j<bytes;j++,i++) wh[dbi].lpData[j] = inbuf[i];
wh[dbi].dwBufferLength = bytes;
db_state[dbi] = BUFFER_FILLED;
if ((sound_state == SOUND_INITIALIZED) && (dbi == 1))
{
sound_state = SOUND_RUNNING;
win_out_err = waveOutRestart(fd);
}
return(win_out_err);
}
static void wait_for_empty_buffer(int buf)
{
while (db_state[buf] != BUFFER_EMPTY)
{
Sleep(1); /* in millisecs, so even this may be too much if buf=256 bytes */
}
}
int write_audio(int line, char *buf, int bytes)
{
int lim,leftover,start;
if (line != OUTPUT_LINE) {AUDIO_ERROR = SNDLIB_CANT_WRITE; return(-1);}
AUDIO_ERROR = SNDLIB_NO_ERROR; win_out_err = 0;
leftover = bytes;
start = 0;
if (sound_state == SOUND_UNREADY) return(0);
while (leftover > 0)
{
lim = leftover;
if (lim > buffer_size) lim = buffer_size;
leftover -= lim;
wait_for_empty_buffer(current_buf);
win_out_err = fill_buffer(current_buf,buf,start,lim);
if (win_out_err)
{
AUDIO_ERROR = SNDLIB_CANT_WRITE;
fprintf(stderr,"error %s upon fill",win_out_err);
}
win_out_err = waveOutWrite(fd,&wh[current_buf],sizeof(WAVEHDR));
if (win_out_err)
{
AUDIO_ERROR = SNDLIB_CANT_WRITE;
fprintf(stderr,"error %s upon write",win_out_err);
}
start += lim;
current_buf++;
if (current_buf>1) current_buf=0;
}
if (AUDIO_ERROR != SNDLIB_NO_ERROR) return(-1);
return(0);
}
static int out_saved = 0, aux_saved = 0;
static DWORD *out_vols = NULL, *aux_vols = NULL;
static int *out_set = NULL, *aux_set = NULL;
void save_audio_state(void)
{
UINT dev;
MMRESULT err;
DWORD val;
HWAVEOUT hd;
WAVEOUTCAPS wocaps;
AUXCAPS wacaps;
WAVEFORMATEX pwfx;
out_saved = waveOutGetNumDevs();
if (out_vols) {FREE(out_vols); out_vols = NULL;}
if (out_set) {FREE(out_set); out_set = NULL;}
if (out_saved>0)
{
out_vols = (DWORD *)CALLOC(out_saved,sizeof(DWORD));
out_set = (int *)CALLOC(out_saved,sizeof(int));
for (dev=0;dev<out_saved;dev++)
{
err = waveOutGetDevCaps(dev,&wocaps,sizeof(wocaps));
if ((!err) && (wocaps.dwSupport & WAVECAPS_VOLUME))
{
err = waveOutOpen(&hd,dev,&pwfx,0,0,WAVE_MAPPER);
if (!err)
{
err = waveOutGetVolume(hd,&val);
if (!err)
{
out_vols[dev] = val;
out_set[dev] = 1;
}
waveOutClose(hd);
}
}
}
}
aux_saved = auxGetNumDevs();
if (aux_vols) {FREE(aux_vols); aux_vols = NULL;}
if (aux_set) {FREE(aux_set); aux_set = NULL;}
if (aux_saved>0)
{
aux_vols = (DWORD *)CALLOC(aux_saved,sizeof(unsigned long));
aux_set = (int *)CALLOC(aux_saved,sizeof(int));
for (dev=0;dev<aux_saved;dev++)
{
err = auxGetDevCaps(dev,&wacaps,sizeof(wacaps));
if ((!err) && (wacaps.dwSupport & AUXCAPS_VOLUME))
{
err = auxGetVolume(dev,&val);
if (!err)
{
aux_vols[dev] = val;
aux_set[dev] = 1;
}
}
}
}
/* mixer state needs to be saved too, I suppose */
}
void restore_audio_state(void)
{
int i;
HWAVEOUT hd;
WAVEFORMATEX pwfx;
MMRESULT err;
for (i=0;i<out_saved;i++)
if (out_set[i])
{
err = waveOutOpen(&hd,i,&pwfx,0,0,WAVE_MAPPER);
if (!err)
{
waveOutSetVolume(hd,out_vols[i]);
waveOutClose(hd);
}
}
for (i=0;i<aux_saved;i++) if (aux_set[i]) auxSetVolume(i,aux_vols[i]);
}
static float unlog(unsigned short val)
{
/* 1.0 linear is 0xffff, rest is said to be "logarithmic", whatever that really means here */
if (val == 0) return(0.0);
return((float)val / 65536.0);
/* return(pow(2.0,amp) - 1.0); */ /* doc seems to be bogus */
}
#define SRATE_11025_BITS (WAVE_FORMAT_1S16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1M08)
#define SRATE_22050_BITS (WAVE_FORMAT_2S16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2M08)
#define SRATE_44100_BITS (WAVE_FORMAT_4S16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4M08)
#define SHORT_SAMPLE_BITS (WAVE_FORMAT_1S16 | WAVE_FORMAT_1M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_4M16)
#define BYTE_SAMPLE_BITS (WAVE_FORMAT_1S08 | WAVE_FORMAT_1M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M08)
static char *mfg(int mf)
{
switch (mf)
{
case MM_MICROSOFT: return("Microsoft"); break; case MM_CREATIVE: return("Creative Labs"); break;
case MM_MEDIAVISION: return("Media Vision"); break; case MM_FUJITSU: return("Fujitsu Corp"); break;
case MM_ARTISOFT: return("Artisoft"); break; case MM_TURTLE_BEACH: return("Turtle Beach"); break;
case MM_IBM: return("IBM"); break; case MM_VOCALTEC: return("Vocaltec"); break;
case MM_ROLAND: return("Roland"); break; case MM_DSP_SOLUTIONS: return("DSP Solutions"); break;
case MM_NEC: return("NEC"); break; case MM_ATI: return("ATI"); break;
case MM_WANGLABS: return("Wang Laboratories"); break; case MM_TANDY: return("Tandy"); break;
case MM_VOYETRA: return("Voyetra"); break; case MM_ANTEX: return("Antex Electronics"); break;
case MM_ICL_PS: return("ICL Personal Systems"); break; case MM_INTEL: return("Intel"); break;
case MM_GRAVIS: return("Advanced Gravis"); break; case MM_VAL: return("Video Associates Labs"); break;
case MM_INTERACTIVE: return("InterActive"); break; case MM_YAMAHA: return("Yamaha"); break;
case MM_EVEREX: return("Everex Systems"); break; case MM_ECHO: return("Echo Speech"); break;
case MM_SIERRA: return("Sierra Semiconductor"); break; case MM_CAT: return("Computer Aided Technologies"); break;
case MM_APPS: return("APPS Software"); break; case MM_DSP_GROUP: return("DSP Group"); break;
case MM_MELABS: return("microEngineering Labs"); break; case MM_COMPUTER_FRIENDS: return("Computer Friends"); break;
case MM_ESS: return("ESS Technology"); break; case MM_AUDIOFILE: return("Audio"); break;
case MM_MOTOROLA: return("Motorola"); break; case MM_CANOPUS: return("Canopus"); break;
case MM_EPSON: return("Seiko Epson"); break; case MM_TRUEVISION: return("Truevision"); break;
case MM_AZTECH: return("Aztech Labs"); break; case MM_VIDEOLOGIC: return("Videologic"); break;
case MM_SCALACS: return("SCALACS"); break; case MM_KORG: return("Korg"); break;
case MM_APT: return("Audio Processing Technology"); break; case MM_ICS: return("Integrated Circuit Systems"); break;
case MM_ITERATEDSYS: return("Iterated Systems"); break; case MM_METHEUS: return("Metheus"); break;
case MM_LOGITECH: return("Logitech"); break; case MM_WINNOV: return("Winnov"); break;
case MM_NCR: return("NCR"); break; case MM_EXAN: return("EXAN"); break;
case MM_AST: return("AST Research"); break; case MM_WILLOWPOND: return("Willow Pond"); break;
case MM_SONICFOUNDRY: return("Sonic Foundry"); break; case MM_VITEC: return("Vitec Multimedia"); break;
case MM_MOSCOM: return("MOSCOM"); break; case MM_SILICONSOFT: return("Silicon Soft"); break;
case MM_SUPERMAC: return("Supermac"); break; case MM_AUDIOPT: return("Audio Processing Technology"); break;
case MM_SPEECHCOMP: return("Speech Compression"); break; case MM_DOLBY: return("Dolby Laboratories"); break;
case MM_OKI: return("OKI"); break; case MM_AURAVISION: return("AuraVision"); break;
case MM_OLIVETTI: return("Olivetti"); break; case MM_IOMAGIC: return("I/O Magic"); break;
case MM_MATSUSHITA: return("Matsushita Electric"); break; case MM_CONTROLRES: return("Control Resources"); break;
case MM_XEBEC: return("Xebec Multimedia Solutions"); break; case MM_NEWMEDIA: return("New Media"); break;
case MM_NMS: return("Natural MicroSystems"); break; case MM_LYRRUS: return("Lyrrus"); break;
case MM_COMPUSIC: return("Compusic"); break; case MM_OPTI: return("OPTi Computers"); break;
case MM_DIALOGIC: return("Dialogic"); break;
}
return("");
}
static char *mixer_status_name(int status)
{
switch (status)
{
case MIXERLINE_LINEF_ACTIVE: return(", (active)"); break;
case MIXERLINE_LINEF_DISCONNECTED: return(", (disconnected)"); break;
case MIXERLINE_LINEF_SOURCE: return(", (source)"); break;
default: return(""); break;
}
}
static char *mixer_target_name(int type)
{
switch (type)
{
case MIXERLINE_TARGETTYPE_UNDEFINED: return("undefined"); break;
case MIXERLINE_TARGETTYPE_WAVEOUT: return("output"); break;
case MIXERLINE_TARGETTYPE_WAVEIN: return("input"); break;
case MIXERLINE_TARGETTYPE_MIDIOUT: return("midi output"); break;
case MIXERLINE_TARGETTYPE_MIDIIN: return("midi input"); break;
case MIXERLINE_TARGETTYPE_AUX: return("aux"); break;
default: return(""); break;
}
}
static char *mixer_component_name(int type)
{
switch (type)
{
case MIXERLINE_COMPONENTTYPE_DST_UNDEFINED: return("undefined"); break;
case MIXERLINE_COMPONENTTYPE_DST_DIGITAL: return("digital"); break;
case MIXERLINE_COMPONENTTYPE_DST_LINE: return("line"); break;
case MIXERLINE_COMPONENTTYPE_DST_MONITOR: return("monitor"); break;
case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS: return("speakers"); break;
case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES: return("headphones"); break;
case MIXERLINE_COMPONENTTYPE_DST_TELEPHONE: return("telephone"); break;
case MIXERLINE_COMPONENTTYPE_DST_WAVEIN: return("wave in"); break;
case MIXERLINE_COMPONENTTYPE_DST_VOICEIN: return("voice in"); break;
case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED: return("undefined"); break;
case MIXERLINE_COMPONENTTYPE_SRC_DIGITAL: return("digital"); break;
case MIXERLINE_COMPONENTTYPE_SRC_LINE: return("line"); break;
case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE: return("mic"); break;
case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER: return("synth"); break;
case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC: return("CD"); break;
case MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE: return("telephone"); break;
case MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER: return("speaker"); break;
case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT: return("wave out"); break;
case MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY: return("aux"); break;
case MIXERLINE_COMPONENTTYPE_SRC_ANALOG: return("analog"); break;
default: return(""); break;
}
}
#define MAX_DESCRIBE_CHANS 8
#define MAX_DESCRIBE_CONTROLS 16
/* these actually need to be big enough to handle whatever comes along, since we can't read partial states */
/* or they need to be expanded as necessary */
char *audio_moniker(void) {return("MS audio");} /* version number of some sort? */
static void describe_audio_state_1(void)
{
int devs,dev,srate,chans,format,need_comma,maker;
MMRESULT err;
unsigned long val,rate,pitch,version;
WAVEOUTCAPS wocaps;
WAVEINCAPS wicaps;
AUXCAPS wacaps;
HWAVEOUT hd;
WAVEFORMATEX pwfx;
#ifdef MIXERR_BASE
MIXERCAPS wmcaps;
MIXERLINE mixline;
MIXERLINECONTROLS linecontrols;
MIXERCONTROL mc[MAX_DESCRIBE_CONTROLS];
MIXERCONTROLDETAILS controldetails;
MIXERCONTROLDETAILS_LISTTEXT clist[MAX_DESCRIBE_CHANS];
MIXERCONTROLDETAILS_BOOLEAN cbool[MAX_DESCRIBE_CHANS];
MIXERCONTROLDETAILS_UNSIGNED cline[MAX_DESCRIBE_CHANS];
MIXERCONTROLDETAILS_SIGNED csign[MAX_DESCRIBE_CHANS];
HMIXER mfd;
int control,controls,dest,dests,source,happy,dest_time,chan,mina,maxa,ctype;
#endif
need_comma = 1;
chans=1;
if (!strbuf) strbuf = (char *)CALLOC(STRBUF_SIZE,sizeof(char));
devs = waveOutGetNumDevs();
if (devs>0)
{
pprint("Output:\n");
for (dev=0;dev<devs;dev++)
{
err = waveOutGetDevCaps(dev,&wocaps,sizeof(wocaps));
if (!err)
{
version = wocaps.vDriverVersion;
maker = wocaps.wMid;
sprintf(strbuf," %s %s: version %d.%d\n",
mfg(maker),wocaps.szPname,
version>>8,version&0xff);
pprint(strbuf);
if (wocaps.wChannels == 2) {chans=2; pprint(" stereo");} else {chans=1; pprint(" mono");}
if (wocaps.dwFormats & SRATE_11025_BITS) {srate = 11025; if (need_comma) pprint(","); pprint(" 11025"); need_comma=1;}
if (wocaps.dwFormats & SRATE_22050_BITS) {srate = 22050; if (need_comma) pprint(","); pprint(" 22050"); need_comma=1;}
if (wocaps.dwFormats & SRATE_44100_BITS) {srate = 44100; if (need_comma) pprint(","); pprint(" 44100"); need_comma=1;}
if (wocaps.dwFormats & BYTE_SAMPLE_BITS) {format = 8; if (need_comma) pprint(","); pprint(" unsigned byte"); need_comma=1;}
if (wocaps.dwFormats & SHORT_SAMPLE_BITS) {format = 16; if (need_comma) pprint(","); pprint(" little-endian short"); need_comma=1;}
if (need_comma) pprint("\n");
need_comma = 0;
pwfx.wFormatTag = WAVE_FORMAT_PCM;
pwfx.nChannels = chans;
pwfx.nSamplesPerSec = srate;
pwfx.nAvgBytesPerSec = srate;
pwfx.nBlockAlign = 1;
pwfx.wBitsPerSample = format;
err = waveOutOpen(&hd,dev,&pwfx,0,0,WAVE_FORMAT_QUERY);
if (wocaps.dwSupport & WAVECAPS_VOLUME)
{
err = waveOutGetVolume(hd,&val);
if (!err)
{
if (wocaps.dwSupport & WAVECAPS_LRVOLUME)
sprintf(strbuf," vol: %.3f %.3f",unlog((unsigned short)(val>>16)),unlog((unsigned short)(val&0xffff)));
else sprintf(strbuf," vol: %.3f",unlog((unsigned short)(val&0xffff)));
pprint(strbuf);
need_comma = 1;
}
}
if (!err)
{
/* this is just to get the hd data for subsequent info */
if (wocaps.dwSupport & WAVECAPS_PLAYBACKRATE)
{
err = waveOutGetPlaybackRate(hd,&rate);
if (!err)
{
sprintf(strbuf,"%s playback rate: %.3f",(need_comma ? "," : ""),(float)rate/65536.0);
pprint(strbuf);
need_comma = 1;
}
}
if (wocaps.dwSupport & WAVECAPS_PITCH)
{
err = waveOutGetPitch(hd,&pitch);
if (!err)
{
sprintf(strbuf,"%s pitch: %.3f",(need_comma ? "," : ""),(float)pitch/65536.0);
pprint(strbuf);
need_comma = 1;
}
}
waveOutClose(hd);
}
if (need_comma) {need_comma = 0; pprint("\n");}
}
}
}
devs = waveInGetNumDevs();
if (devs>0)
{
pprint("Input:\n");
for (dev=0;dev<devs;dev++)
{
err = waveInGetDevCaps(dev,&wicaps,sizeof(wicaps));
if (!err)
{
sprintf(strbuf," %s%s",(wicaps.wMid != maker) ? mfg(wicaps.wMid) : "",wicaps.szPname);
pprint(strbuf);
if ((wicaps.wMid != maker) || (version != wicaps.vDriverVersion))
{
sprintf(strbuf,": version %d.%d\n",(wicaps.vDriverVersion>>8),wicaps.vDriverVersion&0xff);
pprint(strbuf);
}
else pprint("\n");
if (wicaps.wChannels == 2) pprint(" stereo"); else pprint(" mono");
if (wicaps.dwFormats & SRATE_11025_BITS) {pprint(", 11025"); need_comma=1;}
if (wicaps.dwFormats & SRATE_22050_BITS) {if (need_comma) pprint(","); pprint(" 22050"); need_comma=1;}
if (wicaps.dwFormats & SRATE_44100_BITS) {if (need_comma) pprint(","); pprint(" 44100"); need_comma=1;}
if (wicaps.dwFormats & BYTE_SAMPLE_BITS) {if (need_comma) pprint(","); pprint(" unsigned byte"); need_comma=1;}
if (wicaps.dwFormats & SHORT_SAMPLE_BITS) {if (need_comma) pprint(","); pprint(" little-endian short");}
pprint("\n");
}
}
}
devs = auxGetNumDevs();
if (devs>0)
{
pprint("Auxiliary:\n");
for (dev=0;dev<devs;dev++)
{
err = auxGetDevCaps(dev,&wacaps,sizeof(wacaps));
if (!err)
{
sprintf(strbuf," %s%s",(wacaps.wMid != maker) ? mfg(wacaps.wMid) : "",wacaps.szPname);
pprint(strbuf);
if ((wacaps.wMid != maker) || (version != wacaps.vDriverVersion))
sprintf(strbuf,": version %d.%d%s",
(wacaps.vDriverVersion>>8),wacaps.vDriverVersion&0xff,
(wacaps.wTechnology & AUXCAPS_CDAUDIO) ? " (CD)" : "");
else sprintf(strbuf,"%s\n",(wacaps.wTechnology & AUXCAPS_CDAUDIO) ? " (CD)" : "");
pprint(strbuf);
if (wacaps.dwSupport & AUXCAPS_VOLUME)
{
err = auxGetVolume(dev,&val);
if (!err)
{
if (wacaps.dwSupport & AUXCAPS_LRVOLUME)
sprintf(strbuf," vol: %.3f %.3f\n",unlog((unsigned short)(val>>16)),unlog((unsigned short)(val&0xffff)));
else sprintf(strbuf," vol: %.3f\n",unlog((unsigned short)(val&0xffff)));
pprint(strbuf);
}
}
}
}
}
#ifdef MIXERR_BASE
devs = mixerGetNumDevs();
if (devs > 0)
{
pprint("Mixer:\n");
for (dev=0;dev<devs;dev++)
{
err = mixerGetDevCaps(dev,&wmcaps,sizeof(wmcaps));
if (!err)
{
sprintf(strbuf," %s%s",(wmcaps.wMid != maker) ? mfg(wmcaps.wMid) : "",wmcaps.szPname);
pprint(strbuf);
if ((wmcaps.wMid != maker) || (version != wmcaps.vDriverVersion))
{
sprintf(strbuf,": version %d.%d\n",(wmcaps.vDriverVersion>>8),wmcaps.vDriverVersion&0xff);
pprint(strbuf);
}
else pprint("\n");
dests = wmcaps.cDestinations;
err = mixerOpen(&mfd,dev,0,0,CALLBACK_NULL);
if (!err)
{
dest=0;
source=0;
dest_time = 1;
happy = 1;
while (happy)
{
if (dest_time)
{
mixline.dwDestination = dest;
mixline.cbStruct = sizeof(MIXERLINE);
err = mixerGetLineInfo(mfd,&mixline,MIXER_GETLINEINFOF_DESTINATION);
}
else
{
mixline.dwSource = source;
mixline.cbStruct = sizeof(MIXERLINE);
err = mixerGetLineInfo(mfd,&mixline,MIXER_GETLINEINFOF_SOURCE);
}
if (!err)
{
if ((source == 0) && (!dest_time)) pprint(" Sources:\n");
if ((dest == 0) && (dest_time)) pprint(" Destinations:\n");
sprintf(strbuf," %s: %s (%s), %d chan%s",
mixline.szName,
mixer_component_name(mixline.dwComponentType),
mixer_target_name(mixline.Target.dwType),
mixline.cChannels,((mixline.cChannels != 1) ? "s" : ""));
pprint(strbuf);
if (mixline.cConnections > 0)
{
sprintf(strbuf,", %d connection%s",
mixline.cConnections,((mixline.cConnections != 1) ? "s" : ""));
pprint(strbuf);
}
if (dest_time)
{
sprintf(strbuf,"%s\n",mixer_status_name(mixline.fdwLine));
pprint(strbuf);
}
else pprint("\n");
if (mixline.cControls > 0)
{
linecontrols.cbStruct = sizeof(MIXERLINECONTROLS);
linecontrols.dwLineID = mixline.dwLineID;
linecontrols.dwControlID = MIXER_GETLINECONTROLSF_ONEBYID;
if (linecontrols.cControls > MAX_DESCRIBE_CONTROLS)
linecontrols.cControls = MAX_DESCRIBE_CONTROLS;
else linecontrols.cControls = mixline.cControls;
linecontrols.pamxctrl = mc;
linecontrols.cbmxctrl = sizeof(MIXERCONTROL);
err = mixerGetLineControls(mfd,&linecontrols,MIXER_GETLINECONTROLSF_ALL);
if (!err)
{
sprintf(strbuf," %d control%s:\n",linecontrols.cControls,(linecontrols.cControls != 1) ? "s" : "");
pprint(strbuf);
controls = linecontrols.cControls;
if (controls > MAX_DESCRIBE_CONTROLS) controls = MAX_DESCRIBE_CONTROLS;
for (control=0;control<controls;control++)
{
sprintf(strbuf," %s",mc[control].szName);
pprint(strbuf);
controldetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
controldetails.dwControlID = mc[control].dwControlID;
ctype = (mc[control].dwControlType);
if ((ctype == MIXERCONTROL_CONTROLTYPE_EQUALIZER) ||
(ctype == MIXERCONTROL_CONTROLTYPE_MUX) ||
(ctype == MIXERCONTROL_CONTROLTYPE_MIXER) ||
(ctype == MIXERCONTROL_CONTROLTYPE_SINGLESELECT) ||
(ctype == MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT))
{
controldetails.cChannels = 1;
controldetails.cMultipleItems = mc[control].cMultipleItems;
controldetails.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
controldetails.paDetails = clist;
err = mixerGetControlDetails(mfd,&controldetails,MIXER_GETCONTROLDETAILSF_LISTTEXT);
if (!err)
{
for (chan=0;chan<mixline.cChannels;chan++)
{
sprintf(strbuf," [%s]",clist[chan].szName);
pprint(strbuf);
}
}
}
if (mixline.cChannels > MAX_DESCRIBE_CHANS)
controldetails.cChannels = MAX_DESCRIBE_CHANS;
else controldetails.cChannels = mixline.cChannels;
controldetails.cMultipleItems = 0;
err = 0;
switch (mc[control].dwControlType & MIXERCONTROL_CT_UNITS_MASK)
{
case MIXERCONTROL_CT_UNITS_BOOLEAN:
controldetails.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
controldetails.paDetails = cbool;
break;
case MIXERCONTROL_CT_UNITS_SIGNED: case MIXERCONTROL_CT_UNITS_DECIBELS:
controldetails.cbDetails = sizeof(MIXERCONTROLDETAILS_SIGNED);
controldetails.paDetails = csign;
break;
case MIXERCONTROL_CT_UNITS_UNSIGNED: case MIXERCONTROL_CT_UNITS_PERCENT:
controldetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
controldetails.paDetails = cline;
break;
default: err=1; break;
}
if (err)
pprint("\n");
else
{
err = mixerGetControlDetails(mfd,&controldetails,MIXER_GETCONTROLDETAILSF_VALUE);
if (!err)
{
chans = controldetails.cChannels;
if (chans > MAX_DESCRIBE_CHANS) chans = MAX_DESCRIBE_CHANS;
switch (mc[control].dwControlType & MIXERCONTROL_CT_UNITS_MASK)
{
case MIXERCONTROL_CT_UNITS_BOOLEAN:
for (chan=0;chan<chans;chan++)
{
sprintf(strbuf," %s",(cbool[chan].fValue) ? " on" : " off");
pprint(strbuf);
}
break;
case MIXERCONTROL_CT_UNITS_SIGNED: case MIXERCONTROL_CT_UNITS_DECIBELS:
mina = mc[control].Bounds.lMinimum;
maxa = mc[control].Bounds.lMaximum;
if (maxa > mina)
{
for (chan=0;chan<chans;chan++)
{
sprintf(strbuf," %.3f",(float)(csign[chan].lValue - mina)/(float)(maxa - mina));
pprint(strbuf);
}
}
break;
case MIXERCONTROL_CT_UNITS_UNSIGNED: case MIXERCONTROL_CT_UNITS_PERCENT:
mina = mc[control].Bounds.dwMinimum;
maxa = mc[control].Bounds.dwMaximum;
if (maxa > mina)
{
for (chan=0;chan<chans;chan++)
{
sprintf(strbuf," %.3f",(float)(cline[chan].dwValue - mina)/(float)(maxa - mina));
pprint(strbuf);
}
}
break;
default: break;
}
pprint("\n");
}
else pprint("\n");
}
}
}
}
}
else if (!dest_time) happy = 0;
if (dest_time) dest++; else source++;
if (dest == dests) dest_time = 0;
}
}
mixerClose(mfd);
}
}
}
#endif
}
int initialize_audio(void)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
return(0);
}
int close_audio(int line)
{
int i;
AUDIO_ERROR = SNDLIB_NO_ERROR; win_out_err = 0; win_in_err = 0;
if (line == OUTPUT_LINE)
{
/* fill with a few zeros, wait for empty flag */
if (sound_state != SOUND_UNREADY)
{
wait_for_empty_buffer(current_buf);
for (i=0;i<128;i++) wh[current_buf].lpData[i] = 0;
wait_for_empty_buffer(current_buf);
win_out_err = waveOutClose(fd);
i = 0;
while (win_out_err == WAVERR_STILLPLAYING)
{
Sleep(1);
win_out_err = waveOutClose(fd);
i++;
if (i > 1024) break;
}
if (win_out_err) AUDIO_ERROR = SNDLIB_CANT_CLOSE;
db_state[0] = BUFFER_EMPTY;
db_state[1] = BUFFER_EMPTY;
sound_state = SOUND_UNREADY;
waveOutUnprepareHeader(fd,&(wh[0]),sizeof(WAVEHDR));
waveOutUnprepareHeader(fd,&(wh[1]),sizeof(WAVEHDR));
FREE(wh[0].lpData);
FREE(wh[1].lpData);
}
}
else
{
if (line == INPUT_LINE)
{
if (rec_state != SOUND_UNREADY)
{
waveInReset(record_fd);
waveInClose(record_fd);
waveInUnprepareHeader(record_fd,&rec_wh,sizeof(WAVEHDR));
if (rec_wh.lpData) {FREE(rec_wh.lpData); rec_wh.lpData = NULL;}
rec_state = SOUND_UNREADY;
}
}
else AUDIO_ERROR = SNDLIB_CANT_CLOSE;
}
return((AUDIO_ERROR == SNDLIB_NO_ERROR) ? 0 : -1);
}
/*
* waveInAddBuffer sends buffer to get data
* MM_WIM_DATA lParam->WAVEHDR dwBytesRecorded =>how much data actually in buffer
*/
static int current_record_chans = 0, current_record_datum_size = 0;
DWORD CALLBACK next_input_buffer(HWAVEIN w, UINT msg, DWORD user_data, DWORD p1, DWORD p2)
{
if (msg == WIM_DATA)
{
/* grab data */
/* p1->dwBytesRecorded */
}
return(0);
}
int open_audio_input(int ur_dev, int srate, int chans, int format, int size)
{
WAVEFORMATEX wf;
int dev;
AUDIO_ERROR = SNDLIB_NO_ERROR; win_in_err = 0;
dev = SNDLIB_DEVICE(ur_dev);
wf.nChannels = chans;
current_record_chans = chans;
wf.wFormatTag = WAVE_FORMAT_PCM;
wf.cbSize = 0;
if (format == SNDLIB_8_UNSIGNED)
{
wf.wBitsPerSample = 8;
current_record_datum_size = 1;
}
else
{
wf.wBitsPerSample = 16;
current_record_datum_size = 2;
}
wf.nSamplesPerSec = srate;
wf.nBlockAlign = chans * current_datum_size;
wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
rec_wh.dwBufferLength = size * current_record_datum_size;
rec_wh.dwFlags = 0;
rec_wh.dwLoops = 0;
rec_wh.lpData = (char *)CALLOC(rec_wh.dwBufferLength,sizeof(char));
if ((rec_wh.lpData) == 0) {AUDIO_ERROR = SNDLIB_SIZE_NOT_AVAILABLE; return(-1);}
win_in_err = waveInOpen(&record_fd,WAVE_MAPPER,&wf,(DWORD)next_input_buffer,0,CALLBACK_FUNCTION);
if (win_in_err)
{
AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE;
FREE(rec_wh.lpData);
return(-1);
}
win_in_err = waveInPrepareHeader(record_fd,&(rec_wh),sizeof(WAVEHDR));
if (win_in_err)
{
AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
FREE(rec_wh.lpData);
waveInClose(record_fd);
return(-1);
}
return(0);
}
int read_audio(int line, char *buf, int bytes)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
win_in_err = 0;
return(-1);
}
int read_audio_state(int ur_dev, int field, int chan, float *val)
{
int dev,sys;
unsigned long lval;
MMRESULT err;
AUDIO_ERROR = SNDLIB_NO_ERROR;
sys = SNDLIB_SYSTEM(ur_dev);
dev = SNDLIB_DEVICE(ur_dev);
if (field == SNDLIB_AMP_FIELD)
{
err = auxGetVolume(sys,&lval);
if (!err)
{
if (chan == 0)
val[0] = unlog((unsigned short)(lval>>16));
else val[0] = unlog((unsigned short)(lval&0xffff));
}
else AUDIO_ERROR = SNDLIB_CANT_READ;
}
else AUDIO_ERROR = SNDLIB_CANT_READ;
return((AUDIO_ERROR == SNDLIB_NO_ERROR) ? 0 : -1);
}
int write_audio_state(int ur_dev, int field, int chan, float *val)
{
int dev,sys;
unsigned long lval;
MMRESULT err;
AUDIO_ERROR = SNDLIB_NO_ERROR;
sys = SNDLIB_SYSTEM(ur_dev);
dev = SNDLIB_DEVICE(ur_dev);
if (field == SNDLIB_AMP_FIELD)
{
err = auxGetVolume(sys,&lval);
if (!err)
{
if (chan == 0)
lval = (unsigned long)((lval & 0xffff) | (((unsigned short)(val[0]*65535))<<16));
else lval = (unsigned long)((lval & 0xffff0000) | ((unsigned short)(val[0]*65535)));
err = auxSetVolume(sys,lval);
if (!err) AUDIO_ERROR = SNDLIB_CANT_WRITE;
}
else AUDIO_ERROR = SNDLIB_CANT_WRITE;
}
else AUDIO_ERROR = SNDLIB_CANT_WRITE;
return((AUDIO_ERROR == SNDLIB_NO_ERROR) ? 0 : -1);
}
#endif
/* ------------------------------- AIX ----------------------------------------- */
#ifdef AIX
#define AUDIO_OK
/* this code taken from Xanim, esound, and MikMod */
#include <errno.h>
#include <fcntl.h>
#include <sys/audio.h>
#include <stropts.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/param.h>
/* Some AIX machines have "/dev/acpa0/1" instead. */
/* Some AIX machines have "/dev/baud0/1" instead. UltiMedia Sound system */
#define DAC_NAME "/dev/paud0/1"
int audio_started = 0;
int open_audio_output(int dev, int srate, int chans, int format, int size)
{
audio_init init;
audio_control control;
audio_change change;
int line,err;
AUDIO_ERROR = SNDLIB_NO_ERROR;
audio_started = 0;
line = open(DAC_NAME,O_WRONLY | O_NDELAY);
if (line == -1)
AUDIO_ERROR = SNDLIB_CANT_OPEN;
else
{
memset(&init,'\0',sizeof(init));
init.srate = srate;
init.channels = chans;
init.mode = PCM;
init.operation = PLAY;
init.flags = FIXED | BIG_ENDIAN | TWOS_COMPLEMENT;
init.bits_per_sample = 16;
init.bsize = AUDIO_IGNORE;
err = ioctl(line,AUDIO_INIT,&init);
if (err == -1)
AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
else
{
memset (&control,'\0',sizeof(control));
memset (&change,'\0',sizeof(change));
change.output = EXTERNAL_SPEAKER | INTERNAL_SPEAKER | OUTPUT_1;
change.balance = 0x3fff0000;
change.balance_delay = 0;
change.volume = (long)(0x7fff << 16);
change.volume_delay = 0;
change.input = AUDIO_IGNORE;
change.monitor = AUDIO_IGNORE;
change.dev_info = (char *)NULL;
control.ioctl_request = AUDIO_CHANGE; /* AUDIO_STOP AUDIO_WAIT AUDIO_START */
control.position = 0;
control.request_info = (char *)&change;
err = ioctl(line,AUDIO_CONTROL,&control);
if (err == -1) AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
}
}
return(line);
}
int write_audio(int line, char *buf, int bytes)
{
audio_control control;
int err;
if (audio_started == 0)
{
audio_started = 1;
memset(&control,'\0',sizeof(control));
control.ioctl_request = AUDIO_START;
control.request_info = NULL;
control.position = 0;
err = ioctl(line,AUDIO_CONTROL,&control);
if (err == -1) AUDIO_ERROR = SNDLIB_CANT_WRITE;
}
err = write(line,buf,bytes);
return(0);
}
int close_audio(int line)
{
close(line);
audio_started = 0;
return(0);
}
static void describe_audio_state_1(void) {pprint("audio stubbed out");}
int open_audio_input(int dev, int srate, int chans, int format, int size) {return(-1);} /* no input */
int read_audio(int line, char *buf, int bytes) {return(-1);}
int read_audio_state(int dev, int field, int chan, float *val) {return(-1);}
int write_audio_state(int dev, int field, int chan, float *val) {return(-1);}
void save_audio_state(void) {}
void restore_audio_state(void) {}
int initialize_audio(void) {AUDIO_ERROR = SNDLIB_NO_ERROR; return(-1);}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("aix");}
void set_oss_buffers(int num,int size) {}
char *audio_moniker(void) {return("aix audio");}
#endif
/* ------------------------------- NEC EWS ----------------------------------------- */
#ifdef NEC_EWS
#define AUDIO_OK
/* this code taken from Xanim */
#include <sys/audio.h>
#include <errno.h>
#define DAC_NAME "/dev/audio/audio"
static struct AU_Volume audio_vol;
static struct AU_Type audio_type;
static struct AU_Line audio_line;
static struct AU_Status audio_status;
static int valid[] = {AURATE5_5,AURATE6_6,AURATE8_0,
AURATE9_6,AURATE11_0,AURATE16_0,AURATE18_9,AURATE22_1,
AURATE27_4,AURATE32_0,AURATE33_1,AURATE37_8,AURATE44_1,
AURATE48_0,0};
int open_audio_output(int dev, int srate, int chans, int format, int size)
{
int line,err,i,best;
AUDIO_ERROR = SNDLIB_NO_ERROR;
line = open(DAC_NAME,O_WRONLY | O_NDELAY);
if (line == -1)
AUDIO_ERROR = SNDLIB_CANT_OPEN;
else
{
i = 0;
best = 8000;
while (valid[i])
{
if (srate <= valid[i])
{
best = i;
break;
}
i++;
}
audio_type.rate=best;
audio_type.bit_type=AU_PCM16;
audio_type.channel=((chans == 1) ? AU_MONO : AU_STEREO);
err = ioctl(line, AUIOC_SETTYPE,&audio_type);
audio_line.outline = AUOUT_SP | AUOUT_PHONE;
err = ioctl(line,AUIOC_SETLINE,&audio_line);
if (err == -1) AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
}
return(line);
}
int write_audio(int line, char *buf, int bytes)
{
write(line,buf,bytes);
return(0);
}
int close_audio(int line)
{
close(line);
return(0);
}
/* also AUIOC_GETVOLUME via ioctl(line,AUIOC_GETVOLUME,audio_vol);
then volume * AUOUT_ATTEMAX) or something
audio_vol.left = audio_vol.right = vol;
ioctl(line,AUIOC_SETVOLUME,&autio_vol);
*/
static void describe_audio_state_1(void) {pprint("audio stubbed out");}
int open_audio_input(int dev, int srate, int chans, int format, int size) {return(-1);} /* no input */
int read_audio(int line, char *buf, int bytes) {return(-1);}
int read_audio_state(int dev, int field, int chan, float *val) {return(-1);}
int write_audio_state(int dev, int field, int chan, float *val) {return(-1);}
void save_audio_state(void) {}
void restore_audio_state(void) {}
int initialize_audio(void) {AUDIO_ERROR = SNDLIB_NO_ERROR; return(-1);}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("nec ews");}
void set_oss_buffers(int num,int size) {}
char *audio_moniker(void) {return("nec ews audio");}
#endif
/* ------------------------------- SONY NEWS ----------------------------------------- */
#ifdef SONY_NEWS
#define AUDIO_OK
/* this code taken from Xanim */
/* this SVR4 check doesn't work in Solaris, so I doubt it works here */
#ifdef SVR4
#include <sys/sound.h>
#else
#include <newsiodev/sound.h>
#endif
#include <errno.h>
static int sony_audio_rate1[] = /* NWS-5000,4000 */
{ RATE8000, RATE9450, RATE11025, RATE12000, RATE16000,
RATE18900, RATE22050, RATE24000, RATE32000, RATE37800,
RATE44056, RATE44100, RATE48000, 0 } ;
static int sony_audio_rate2[] = /* NWS-3200, 3400, 3700, 3800 */
{ RATE8000, RATE9450, RATE18900, RATE37800, 0 } ;
static int sony_audio_rate3[] = /* NWS-3100 */
{ RATE8000, 0 } ;
int sony_audio_buf_len ;
static struct sbparam audio_info ;
static int *sony_audio_rate;
static int sony_max_vol, sony_min_vol;
int open_audio_output(int dev, int srate, int chans, int format, int size)
{
int line,err;
AUDIO_ERROR = SNDLIB_NO_ERROR;
line = open("/dev/sb0", O_WRONLY | O_NDELAY);
if (line == -1)
{
if (errno == EBUSY)
AUDIO_ERROR = OUTPUT_BUSY;
else AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE;
return(-1);
}
if (ioctl(line,SBIOCGETTYPE,(char *)&sbtype)<0)
{
#ifdef SVR4
AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
close(line);
return(-1);
#endif
sbtype = SBTYPE_AIF2;
}
switch(sbtype)
{
case SBTYPE_AIF2: case SBTYPE_AIF2_L: case SBTYPE_AIF2_E:
sony_max_vol = 0;
sony_min_vol = -32;
sony_audio_rate = &sony_audio_rate2[0];
break;
case SBTYPE_AIF3:
sony_max_vol = 8;
sony_min_vol = -8;
sony_audio_rate = &sony_audio_rate3[0];
break;
case SBTYPE_AIF5: case SBTYPE_AD1848: default:
sony_max_vol = 16;
sony_min_vol = -16;
sony_audio_rate = &sony_audio_rate1[0];
break;
}
audio_info.sb_mode = LOGPCM ;
audio_info.sb_format = 0 ;
audio_info.sb_compress= MULAW ;
audio_info.sb_rate = RATE8000 ;
audio_info.sb_channel = MONO ;
audio_info.sb_bitwidth= RES8B ;
audio_info.sb_emphasis= 0 ;
err = ioctl(line,SBIOCSETPARAM,&audio_info);
if (err == -1)
{
AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
close(line);
return(-1);
}
return(line);
}
int write_audio(int line, char *buf, int bytes)
{
write(line,buf,bytes);
ioctl(line,SBIOCFLUSH,0);
return(0);
}
int close_audio(int line)
{
ioctl(line,SBIOCFLUSH,0);
ioctl(line,SBIOCWAIT,0);
close(line);
return(0);
}
/* set volume
struct sblevel gain;
int mflg;
int o_level;
mflg = MUTE_OFF;
ioctl(devAudio, SBIOCMUTE, &mflg);
o_level = sony_min_vol +
((volume * (sony_max_vol - sony_min_vol)) / XA_AUDIO_MAXVOL);
if (o_level > sony_max_vol) o_level = sony_max_vol;
if (o_level < sony_min_vol) o_level = sony_min_vol;
if (o_level == sony_min_vol)
{
mflg = MUTE_ON;
ioctl(devAudio, SBIOCMUTE, &mflg);
}
gain.sb_right = gain.sb_left = (o_level << 16);
ioctl(devAudio, SBIOCSETOUTLVL, &gain);
*/
int open_audio_input(int dev, int srate, int chans, int format, int size) {return(-1);}
static void describe_audio_state_1(void) {pprint("audio stubbed out");}
int read_audio(int line, char *buf, int bytes) {return(-1);}
int read_audio_state(int dev, int field, int chan, float *val) {return(-1);}
int write_audio_state(int dev, int field, int chan, float *val) {return(-1);}
void save_audio_state(void) {}
void restore_audio_state(void) {}
int initialize_audio(void) {return(-1);}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("Sony news");}
void set_oss_buffers(int num,int size) {}
char *audio_moniker(void) {return("Sony audio");}
#endif
/* ------------------------------- NETBSD ----------------------------------------- */
#if defined(NETBSD) && (!(defined(AUDIO_OK)))
#define AUDIO_OK
/* taken from Xanim */
#include <errno.h>
#include <fcntl.h>
#include <sys/audioio.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/ioccom.h>
int open_audio_output(int dev, int srate, int chans, int format, int size)
{
audio_info_t a_info;
line = open("/dev/audio",O_WRONLY | O_NDELAY);
if (line == -1)
{
if (errno == EBUSY)
AUDIO_ERROR = OUTPUT_BUSY;
else AUDIO_ERROR = DEVICE_NOT_AVAILABLE;
return(-1);
}
AUDIO_INITINFO(&a_info);
a_info.blocksize = 1024;
ioctl(line, AUDIO_SETINFO, &a_info);
AUDIO_INITINFO(&a_info);
#ifndef AUDIO_ENCODING_SLINEAR
a_info.play.encoding = AUDIO_ENCODING_PCM16;
#else
/* NetBSD-1.3 */
a_info.play.encoding = AUDIO_ENCODING_SLINEAR; /* Signed, nativeorder */
#endif
ioctl(line, AUDIO_SETINFO, &a_info);
AUDIO_INITINFO(&a_info);
a_info.mode = AUMODE_PLAY | AUMODE_PLAY_ALL;
ioctl(line, AUDIO_SETINFO, &a_info);
AUDIO_INITINFO(&a_info);
a_info.play.precision = 16;
ioctl(line, AUDIO_SETINFO, &a_info);
AUDIO_INITINFO(&a_info);
a_info.play.sample_rate = srate;
a.info.play.port = AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
ioctl(line, AUDIO_SETINFO, &a_info);
return(line);
/* a.info.blocksize after getinfo */
}
int write_audio(int line, char *buf, int bytes)
{
write(line,buf,bytes);
return(0);
}
int close_audio(int line)
{
close(line);
return(0);
}
/* set volume
a_info.play.gain = AUDIO_MIN_GAIN + (volume * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN));
ioctl(devAudio, AUDIO_SETINFO, &a_info);
*/
int open_audio_input(int dev, int srate, int chans, int format, int size) {return(-1);}
static void describe_audio_state_1(void) {pprint("audio stubbed out");}
int read_audio(int line, char *buf, int bytes) {return(-1);}
int read_audio_state(int dev, int field, int chan, float *val) {return(-1);}
int write_audio_state(int dev, int field, int chan, float *val) {return(-1);}
void save_audio_state(void) {}
void restore_audio_state(void) {}
int initialize_audio(void) {return(-1);}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("NetBSD");}
void set_oss_buffers(int num,int size) {}
char *audio_moniker(void) {return("NetBSD audio");}
#endif
/* ------------------------------- AudioFile ----------------------------------------- */
#ifdef AUDIOFILE
#define AUDIO_OK
/* taken from Xanim */
#include <AF/audio.h>
#include <AF/AFlib.h>
#include <AF/AFUtils.h>
static AFAudioConn *AFaud;
static AC ac;
static AFSetACAttributes AFattributes;
static ATime AFtime0=0;
static ATime AFbaseT=0;
#define AF_C_MIN -30
#define AF_C_MAX 30
#define AF_MAP_TO_C_GAIN(vol) (( ((vol)*AF_C_MAX)/XA_AUDIO_MAXVOL) + (AF_C_MIN))
#define SPU(type) AF_sample_sizes[type].samps_per_unit
#define BPU(type) AF_sample_sizes[type].bytes_per_unit
#define DPHONE(od) (int)(((AAudioDeviceDescriptor(AFaud, (od)))->outputsToPhone))
#define DFREQ(od) (int)(((AAudioDeviceDescriptor(AFaud, (od)))->playSampleFreq))
#define DCHAN(od) (int)((AAudioDeviceDescriptor(AFaud, (od)))->playNchannels)
#define DTYPE(od) ((AAudioDeviceDescriptor(AFaud, (od)))->playBufType)
#define TOFFSET 250
static int FindDefaultDevice(AFAudioConn *aud)
{
int i;
char *ep;
if ((ep = getenv("AF_DEVICE")) != NULL)
{
int udevice;
udevice = atoi(ep);
if ((udevice >= 0) && (udevice < ANumberOfAudioDevices(aud)))
return udevice;
}
for(i=0; i<ANumberOfAudioDevices(aud); i++)
{
if ( DPHONE(i) == 0 && DCHAN(i) == 1 ) return i;
}
return -1;
}
int open_audio_output(int dev, int srate, int chans, int format, int size)
{
if ( (AFaud = AFOpenAudioConn("")) == NULL)
{
AUDIO_ERROR = SNDLIB_DEVICE_NOT_AVAILABLE;
return(-);
}
AFdevice = FindDefaultDevice(AFaud);
AFattributes.type = LIN16;
AFattributes.endian = ALittleEndian;
AFattributes.play_gain = AF_MAP_TO_C_GAIN(XAAUD->volume);
ac = AFCreateAC(AFaud, AFdevice, (ACPlayGain | ACEncodingType | ACEndian), &AFattributes);
AFSync(AFaud, 0); /* Make sure we confirm encoding type support. */
if (ac == NULL)
{
AUDIO_ERROR = SNDLIB_CONFIGURATION_NOT_AVAILABLE;
return(-1);
}
AFtime0 = AFbaseT = AFGetTime(ac) + TOFFSET;
return(0);
}
int write_audio(int line, char *buf, int bytes)
{
ATime act, atd = AFtime0;
act = AFPlaySamples(ac,AFtime0,len,buf);
if (AFtime0 < act)
AFtime0 = act+TOFFSET;
elseAFtime0 += bytes >> 1; /* Number of samples */
}
int close_audio(int line)
{
AF_Audio_Off(0);
AFCloseAudioConn(AFaud);
}
/* set volume
Client gain settings range from -30 to +30 dB.
AFattributes.play_gain = AF_MAP_TO_C_GAIN(volume);
AFChangeACAttributes(ac, ACPlayGain, &AFattributes);
AFSync(AFaud,0);
*/
int open_audio_input(int dev, int srate, int chans, int format, int size) {return(-1);}
static void describe_audio_state_1(void) {pprint("audio stubbed out");}
int read_audio(int line, char *buf, int bytes) {return(-1);}
int read_audio_state(int dev, int field, int chan, float *val) {return(-1);}
int write_audio_state(int dev, int field, int chan, float *val) {return(-1);}
void save_audio_state(void) {}
void restore_audio_state(void) {}
int initialize_audio(void) {return(-1);}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("AudioFile");}
void set_oss_buffers(int num,int size) {}
char *audio_moniker(void) {return("AudioFile audio");}
#endif
/* ------------------------------- OS2 ----------------------------------------- */
#ifdef OS2
#define AUDIO_OK
/* from mpg123 */
#include <stdlib.h>
#include <os2.h>
#define INCL_OS2MM
#include <os2me.h>
#define BUFNUM 20
#define BUFSIZE 16384
typedef struct {
ULONG operation;
ULONG operand1;
ULONG operand2;
ULONG operand3;
} ple;
MCI_WAVE_SET_PARMS msp;
MCI_PLAY_PARMS mpp;
int id, pos=0;
ple pl[BUFNUM+2];
static struct audio_info_struct *ai;
int open_audio_output(int dev, int srate, int chans, int format, int size)
{
char *buf[BUFNUM];
int i;
ULONG rc;
MCI_OPEN_PARMS mop;
pos = 0;
pl[0].operation = (ULONG)NOP_OPERATION;
pl[0].operand1 = 0;
pl[0].operand2 = 0;
pl[0].operand3 = 0;
for(i = 0; i < BUFNUM; i++)
{
buf[i] = (char*)malloc(BUFSIZE);
memset(buf[i], 0, BUFSIZE);
pl[i+1].operation = (ULONG)DATA_OPERATION;
pl[i+1].operand1 = (ULONG)buf[i];
pl[i+1].operand2 = BUFSIZE/2;
pl[i+1].operand3 = 0;
}
pl[BUFNUM+1].operation = (ULONG)BRANCH_OPERATION;
pl[BUFNUM+1].operand1 = 0;
pl[BUFNUM+1].operand2 = 1;
pl[BUFNUM+1].operand3 = 0;
mop.pszDeviceType = (PSZ)MCI_DEVTYPE_WAVEFORM_AUDIO_NAME;
mop.pszElementName = (PSZ)&pl;
rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_PLAYLIST, (PVOID)&mop, 0);
if (rc)
{
AUDIO_ERROR = DEVICE_NOT_AVAILABLE;
return(-1);
}
id = mop.usDeviceID;
msp.usBitsPerSample = 16;
msp.ulSamplesPerSec = srate;
msp.usChannels = chans;
rc = mciSendCommand(id, MCI_SET, MCI_WAIT | MCI_WAVE_SET_BITSPERSAMPLE | MCI_WAVE_SET_SAMPLESPERSEC | MCI_WAVE_SET_CHANNELS, (PVOID)&msp, 0);
mciSendCommand(id, MCI_PLAY, 0, (PVOID)&mpp, 0);
return(0);
}
int write_audio(int line, char *buf, int bytes)
{
pos = pos + 1;
if (pos > BUFNUM) pos = 1;
while (pl[pos].operand3 < pl[pos].operand2) _sleep2(125);
memcpy((char*)pl[pos].operand1, buf, len);
pl[pos].operand2 = len;
pl[pos].operand3 = 0;
return(0);
}
int close_audio(int line)
{
ULONG rc;
pl[pos].operation = (ULONG)EXIT_OPERATION;
pl[pos].operand2 = 0;
pl[pos].operand3 = 0;
pos = pos - 1;
if(pos == 0) pos = BUFNUM;
while (pl[pos].operand3 < pl[pos].operand2) _sleep2(250);
_sleep2(2000);
rc = mciSendCommand(id, MCI_CLOSE, MCI_WAIT, (PVOID)NULL, 0);
return(0);
}
int open_audio_input(int dev, int srate, int chans, int format, int size) {return(-1);}
static void describe_audio_state_1(void) {pprint("audio stubbed out");}
int read_audio(int line, char *buf, int bytes) {return(-1);}
int read_audio_state(int dev, int field, int chan, float *val) {return(-1);}
int write_audio_state(int dev, int field, int chan, float *val) {return(-1);}
void save_audio_state(void) {}
void restore_audio_state(void) {}
int initialize_audio(void) {return(-1);}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(1);}
char *audio_system_name(int system) {return("OS2");}
void set_oss_buffers(int num,int size) {}
char *audio_moniker(void) {return("OS2 audio");}
#endif
/* and DEC using MMSYSTEM?? #include <mme/mmsystem.h> -- see xanim */
/* ------------------------------- STUBS ----------------------------------------- */
#ifndef AUDIO_OK
static void describe_audio_state_1(void) {pprint("audio stubbed out");}
int open_audio_output(int dev, int srate, int chans, int format, int size) {return(-1);}
int open_audio_input(int dev, int srate, int chans, int format, int size) {return(-1);}
int write_audio(int line, char *buf, int bytes) {return(-1);}
int close_audio(int line) {return(-1);}
int read_audio(int line, char *buf, int bytes) {return(-1);}
int read_audio_state(int dev, int field, int chan, float *val) {return(-1);}
int write_audio_state(int dev, int field, int chan, float *val) {return(-1);}
void save_audio_state(void) {}
void restore_audio_state(void) {}
int initialize_audio(void) {return(-1);}
char *audio_error_name(int err) {return(audio_error_name_1(err));}
int audio_systems(void) {return(0);}
char *audio_system_name(int system) {return("unknown");}
void set_oss_buffers(int num,int size) {}
char *audio_moniker(void) {return("unknown audio");}
#endif
static char *save_it = NULL;
static int print_it = 1;
static int save_it_len = 0;
static int save_it_loc = 0;
static void check_save_it(int loc)
{
#ifdef MACOS
char *ptr;
int k;
#endif
if (loc >= save_it_len)
{
save_it_len += 1024;
#ifdef MACOS
ptr = (char *)CALLOC(save_it_len,sizeof(char));
if (save_it)
{
for (k=0;k<save_it_loc;k++) ptr[k] = save_it[k];
FREE(save_it);
}
save_it = ptr;
#else
save_it = (char *)REALLOC(save_it,save_it_len * sizeof(char));
#endif
}
}
static void pprint(char *str)
{
int i,len;
if ((str) && (*str))
{
if ((print_it) || (!(save_it)))
{
mus_error(MUS_NO_ERROR,str);
}
else
{
len = strlen(str);
for (i=0;i<len;i++,save_it_loc++)
{
check_save_it(save_it_loc);
save_it[save_it_loc] = str[i];
}
check_save_it(save_it_loc);
save_it[save_it_loc] = 0;
}
}
}
char *report_audio_state(void)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
if (!(save_it))
{
save_it_len = 1024;
save_it = (char *)CALLOC(save_it_len,sizeof(char));
}
save_it_loc = 0;
print_it = 0;
describe_audio_state_1();
return(save_it);
}
void describe_audio_state(void)
{
AUDIO_ERROR = SNDLIB_NO_ERROR;
print_it = 1;
describe_audio_state_1();
}
#ifdef CLM
void reset_audio_c (void)
{
audio_initialized = 0;
AUDIO_ERROR = SNDLIB_NO_ERROR;
save_it = NULL;
version_name = NULL;
#ifdef SUN
sun_vol_name = NULL;
#endif
save_it_len = 0;
strbuf = NULL;
#ifdef MCL_PPC
reset_db();
#endif
}
#endif