gps/GPSResources/tcpmpVerOld/mikmod/libmikmod/drivers/drv_sun.c

516 lines
12 KiB
C
Executable File

/* MikMod sound library
(c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
AUTHORS for complete list.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
/*==============================================================================
$Id: drv_sun.c,v 1.3 2004/02/10 14:02:24 raph Exp $
Driver for output on the Sun audio device (/dev/audio).
Also works under NetBSD and OpenBSD
==============================================================================*/
/*
Written by Valtteri Vuorikoski <vuori@sci.fi>
NetBSD/OpenBSD code from Miodrag Vallat <miod@mikmod.org>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "mikmod_internals.h"
#ifdef DRV_SUN
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <sys/types.h>
#ifdef HAVE_SUN_AUDIOIO_H
#include <sun/audioio.h>
#endif
#ifdef HAVE_SYS_AUDIOIO_H
#include <sys/audioio.h>
#endif
#ifdef SUNOS
extern int ioctl(int, unsigned long, ...);
extern int fputs(const char *, FILE *);
#endif
#define DEFAULT_FRAGSIZE 12
#if !defined __NetBSD__ && !defined __OpenBSD__
#ifdef HAVE_SUN_AUDIOIO_H
#define SUNOS4
#else
#define SOLARIS
#endif
#endif
/* Sound device to open */
#ifdef SUNOS4
#define SOUNDDEVICE "/dev/sound"
#else /* Solaris, *BSD */
#define SOUNDDEVICE "/dev/audio"
#endif
/* Solaris doesn't have these */
#ifdef SOLARIS
#define AUDIO_ENCODING_SLINEAR AUDIO_ENCODING_LINEAR
#define AUDIO_ENCODING_ULINEAR AUDIO_ENCODING_LINEAR8
#endif
/* Compatibility defines, for old *BSD or SunOS systems */
#ifndef AUDIO_ENCODING_PCM16
#define AUDIO_ENCODING_PCM16 AUDIO_ENCODING_LINEAR
#endif
#ifndef AUDIO_ENCODING_PCM8
#define AUDIO_ENCODING_PCM8 AUDIO_ENCODING_LINEAR8
#endif
#ifndef AUDIO_ENCODING_SLINEAR_LE
#define AUDIO_ENCODING_SLINEAR_LE AUDIO_ENCODING_PCM16
#endif
#ifndef AUDIO_ENCODING_ULINEAR_LE
#define AUDIO_ENCODING_ULINEAR_LE AUDIO_ENCODING_PCM8
#endif
#ifndef AUDIO_ENCODING_SLINEAR
#if BYTE_ORDER == BIG_ENDIAN
#define AUDIO_ENCODING_SLINEAR AUDIO_ENCODING_SLINEAR_BE
#else
#define AUDIO_ENCODING_SLINEAR AUDIO_ENCODING_SLINEAR_LE
#endif
#endif
#ifndef AUDIO_ENCODING_ULINEAR
#if BYTE_ORDER == BIG_ENDIAN
#define AUDIO_ENCODING_ULINEAR AUDIO_ENCODING_ULINEAR_BE
#else
#define AUDIO_ENCODING_ULINEAR AUDIO_ENCODING_ULINEAR_LE
#endif
#endif
/* Compatibility defines, for old *BSD systems */
#ifndef AUDIO_SPEAKER
#define AUDIO_SPEAKER 0x01
#endif
#ifndef AUDIO_HEADPHONE
#define AUDIO_HEADPHONE 0x02
#endif
/* ``normalize'' AUDIO_ENCODING_xxx values for comparison */
static int normalize(int encoding)
{
switch (encoding) {
#ifdef AUDIO_ENCODING_LINEAR
case AUDIO_ENCODING_LINEAR:
return AUDIO_ENCODING_PCM16;
#endif
#ifdef AUDIO_ENCODING_LINEAR8
case AUDIO_ENCODING_LINEAR8:
return AUDIO_ENCODING_PCM8;
#endif
#if BYTE_ORDER == BIG_ENDIAN
#ifdef AUDIO_ENCODING_SLINEAR_BE
case AUDIO_ENCODING_SLINEAR:
return AUDIO_ENCODING_SLINEAR_BE;
#endif
#ifdef AUDIO_ENCODING_ULINEAR_BE
case AUDIO_ENCODING_ULINEAR:
return AUDIO_ENCODING_ULINEAR_BE;
#endif
#else
#ifdef AUDIO_ENCODING_SLINEAR_LE
case AUDIO_ENCODING_SLINEAR:
return AUDIO_ENCODING_SLINEAR_LE;
#endif
#ifdef AUDIO_ENCODING_ULINEAR_LE
case AUDIO_ENCODING_ULINEAR:
return AUDIO_ENCODING_ULINEAR_LE;
#endif
#endif
default:
return encoding;
}
}
static int sndfd = -1;
static unsigned int port = 0;
static int play_encoding;
static int play_precision;
static int fragsize = 1 << DEFAULT_FRAGSIZE;
static SBYTE *audiobuffer = NULL;
static void Sun_CommandLine(CHAR *cmdline)
{
CHAR *ptr;
if ((ptr = MD_GetAtom("buffer", cmdline, 0))) {
int buf = atoi(ptr);
if (buf >= 7 && buf <= 17)
fragsize = 1 << buf;
free(ptr);
}
if ((ptr = MD_GetAtom("headphone", cmdline, 1))) {
port = AUDIO_HEADPHONE;
free(ptr);
} else if ((ptr = MD_GetAtom("speaker", cmdline, 1))) {
port = AUDIO_SPEAKER;
free(ptr);
}
}
static BOOL Sun_IsThere(void)
{
if (getenv("AUDIODEV"))
return (access(getenv("AUDIODEV"), W_OK) == 0);
else {
if (access(SOUNDDEVICE, W_OK) == 0)
return 1;
#if defined __NetBSD__ || defined __OpenBSD__
/* old OpenBSD/sparc installation program creates /dev/audio0 but no
/dev/audio. Didn't check NetBSD behaviour */
if (access(SOUNDDEVICE "0", W_OK) == 0)
return 1;
#endif
}
return 0;
}
static BOOL Sun_Init(void)
{
int play_stereo, play_rate;
#ifdef SUNOS4
int audiotype;
#else
audio_device_t audiotype;
#endif
struct audio_info audioinfo;
if (getenv("AUDIODEV"))
sndfd = open(getenv("AUDIODEV"), O_WRONLY);
else {
sndfd = open(SOUNDDEVICE, O_WRONLY);
#if defined __NetBSD__ || defined __OpenBSD__
if (sndfd < 0)
sndfd = open(SOUNDDEVICE "0", O_WRONLY);
#endif
}
if (sndfd < 0) {
_mm_errno = MMERR_OPENING_AUDIO;
return 1;
}
if (!(audiobuffer = (SBYTE *)_mm_malloc(fragsize)))
return 1;
play_precision = (md_mode & DMODE_16BITS) ? 16 : 8;
play_stereo = (md_mode & DMODE_STEREO) ? 2 : 1;
play_rate = md_mixfreq;
/* attempt to guess the encoding */
play_encoding = -1;
if (ioctl(sndfd, AUDIO_GETDEV, &audiotype) < 0) {
#ifdef MIKMOD_DEBUG
fputs("\rSun driver warning: could not determine audio device type\n",
stderr);
#endif
} else {
#if defined SUNOS4 /* SunOS 4 */
switch (audiotype) {
case AUDIO_DEV_AMD:
/* AMD 79C30 */
/* 8bit mono ulaw 8kHz */
play_rate = md_mixfreq = 8000;
md_mode &= ~(DMODE_STEREO | DMODE_16BITS);
play_precision = 8;
play_stereo = 1;
play_encoding = AUDIO_ENCODING_ULAW;
break;
case AUDIO_DEV_SPEAKERBOX:
case AUDIO_DEV_CODEC:
/* CS 4231 or DBRI or speaker box */
/* 16bit mono/stereo linear 8kHz - 48kHz */
if (play_precision == 16)
play_encoding = AUDIO_ENCODING_LINEAR;
/* 8bit mono ulaw 8kHz - 48kHz */
else if (play_precision == 8) {
md_mode &= ~(DMODE_STEREO);
play_stereo = 1;
play_encoding = AUDIO_ENCODING_ULAW;
} else {
_mm_errno = MMERR_SUN_INIT;
return 1;
}
break;
}
#elif defined SOLARIS /* Solaris */
if (!strcmp(audiotype.name, "SUNW,am79c30")) {
/* AMD 79C30 */
/* 8bit mono ulaw 8kHz */
play_rate = md_mixfreq = 8000;
md_mode &= ~(DMODE_STEREO | DMODE_16BITS);
play_precision = 8;
play_stereo = 1;
play_encoding = AUDIO_ENCODING_ULAW;
} else
if ((!strcmp(audiotype.name, "SUNW,CS4231")) ||
(!strcmp(audiotype.name, "SUNW,dbri")) ||
(!strcmp(audiotype.name, "speakerbox"))) {
/* CS 4231 or DBRI or speaker box */
/* 16bit mono/stereo linear 8kHz - 48kHz */
if (play_precision == 16)
play_encoding = AUDIO_ENCODING_LINEAR;
/* 8bit mono ulaw 8kHz - 48kHz */
else if (play_precision == 8) {
md_mode &= ~(DMODE_STEREO);
play_stereo = 1;
play_encoding = AUDIO_ENCODING_ULAW;
} else {
_mm_errno = MMERR_SUN_INIT;
return 1;
}
}
#else /* NetBSD, OpenBSD */
if (!strcmp(audiotype.name, "amd7930")) {
/* AMD 79C30 */
/* 8bit mono ulaw 8kHz */
play_rate = md_mixfreq = 8000;
md_mode &= ~(DMODE_STEREO | DMODE_16BITS);
play_precision = 8;
play_stereo = 1;
play_encoding = AUDIO_ENCODING_ULAW;
}
if ((!strcmp(audiotype.name, "Am78C201")) ||
(!strcmp(audiotype.name, "UltraSound"))
) {
/* Gravis UltraSound, AMD Interwave and compatible cards */
/* 16bit stereo linear 44kHz */
play_rate = md_mixfreq = 44100;
md_mode |= (DMODE_STEREO | DMODE_16BITS);
play_precision = 16;
play_stereo = 2;
play_encoding = AUDIO_ENCODING_SLINEAR;
}
#endif
}
/* Sound devices which were not handled above don't have specific
limitations, so try and guess optimal settings */
if (play_encoding == -1) {
if ((play_precision == 8) && (play_stereo == 1) &&
(play_rate <= 8000)) play_encoding = AUDIO_ENCODING_ULAW;
else
#ifdef SUNOS4
play_encoding = AUDIO_ENCODING_LINEAR;
#else
play_encoding =
(play_precision ==
16) ? AUDIO_ENCODING_SLINEAR : AUDIO_ENCODING_ULINEAR;
#endif
}
/* get current audio settings if we want to keep the playback output
port */
if (!port) {
AUDIO_INITINFO(&audioinfo);
if (ioctl(sndfd, AUDIO_GETINFO, &audioinfo) < 0) {
_mm_errno = MMERR_SUN_INIT;
return 1;
}
port = audioinfo.play.port;
}
AUDIO_INITINFO(&audioinfo);
audioinfo.play.precision = play_precision;
audioinfo.play.channels = play_stereo;
audioinfo.play.sample_rate = play_rate;
audioinfo.play.encoding = play_encoding;
audioinfo.play.port = port;
#if defined __NetBSD__ || defined __OpenBSD__
#if defined AUMODE_PLAY_ALL
audioinfo.mode = AUMODE_PLAY | AUMODE_PLAY_ALL;
#else
audioinfo.mode = AUMODE_PLAY;
#endif
#endif
if (ioctl(sndfd, AUDIO_SETINFO, &audioinfo) < 0) {
_mm_errno = MMERR_SUN_INIT;
return 1;
}
/* check if our changes were accepted */
if (ioctl(sndfd, AUDIO_GETINFO, &audioinfo) < 0) {
_mm_errno = MMERR_SUN_INIT;
return 1;
}
if ((audioinfo.play.precision != play_precision) ||
(audioinfo.play.channels != play_stereo) ||
(normalize(audioinfo.play.encoding) != normalize(play_encoding))) {
_mm_errno = MMERR_SUN_INIT;
return 1;
}
if (audioinfo.play.sample_rate != play_rate) {
/* Accept a shift inferior to 5% of the expected rate */
int delta = audioinfo.play.sample_rate - play_rate;
if (delta < 0)
delta = -delta;
if (delta * 20 > play_rate) {
_mm_errno = MMERR_SUN_INIT;
return 1;
}
/* Align to what the card gave us */
md_mixfreq = audioinfo.play.sample_rate;
}
return VC_Init();
}
static void Sun_Exit(void)
{
VC_Exit();
_mm_free(audiobuffer);
if (sndfd >= 0) {
close(sndfd);
sndfd = -1;
}
}
static void Sun_Update(void)
{
int done;
done = VC_WriteBytes((char *)audiobuffer, fragsize);
if (play_encoding == AUDIO_ENCODING_ULAW)
unsignedtoulaw(audiobuffer, done);
write(sndfd, audiobuffer, done);
}
static void Sun_Pause(void)
{
int done;
done = VC_SilenceBytes((char *)audiobuffer, fragsize);
write(sndfd, audiobuffer, done);
}
static BOOL Sun_PlayStart(void)
{
struct audio_info audioinfo;
AUDIO_INITINFO(&audioinfo);
audioinfo.play.pause = 0;
if (ioctl(sndfd, AUDIO_SETINFO, &audioinfo) < 0)
return 1;
return VC_PlayStart();
}
static void Sun_PlayStop(void)
{
struct audio_info audioinfo;
VC_PlayStop();
if (ioctl(sndfd, AUDIO_DRAIN) < 0)
return;
AUDIO_INITINFO(&audioinfo);
audioinfo.play.pause = 1;
ioctl(sndfd, AUDIO_SETINFO, &audioinfo);
}
MIKMODAPI MDRIVER drv_sun = {
NULL,
#if defined __OpenBSD__
"OpenBSD Audio",
"OpenBSD audio driver v1.0",
#elif defined __NetBSD__
"NetBSD Audio",
"NetBSD audio driver v1.0",
#elif defined SUNOS4
"SunOS Audio",
"SunOS audio driver v1.4",
#elif defined SOLARIS
"Solaris Audio",
"Solaris audio driver v1.4",
#endif
0, 255,
"audio",
"buffer:r:7,17,12:Audio buffer log2 size\n"
#if defined(SUNOS) || defined(SOLARIS)
"headphone:b:0:Use headphone\n"
"speaker:b:0:Use speaker\n"
#endif
,
Sun_CommandLine,
Sun_IsThere,
VC_SampleLoad,
VC_SampleUnload,
VC_SampleSpace,
VC_SampleLength,
Sun_Init,
Sun_Exit,
NULL,
VC_SetNumVoices,
Sun_PlayStart,
Sun_PlayStop,
Sun_Update,
Sun_Pause,
VC_VoiceSetVolume,
VC_VoiceGetVolume,
VC_VoiceSetFrequency,
VC_VoiceGetFrequency,
VC_VoiceSetPanning,
VC_VoiceGetPanning,
VC_VoicePlay,
VC_VoiceStop,
VC_VoiceStopped,
VC_VoiceGetPosition,
VC_VoiceRealVolume
};
#else
MISSING(drv_sun);
#endif
/* ex:set ts=4: */