kegs/src/sound_driver.c

430 lines
8.6 KiB
C

/************************************************************************/
/* KEGS: Apple //gs Emulator */
/* Copyright 2002 by Kent Dickey */
/* */
/* This code is covered by the GNU GPL */
/* */
/* The KEGS web page is kegs.sourceforge.net */
/* You may contact the author at: kadickey@alumni.princeton.edu */
/************************************************************************/
const char rcsid_sound_driver_c[] = "@(#)$KmKId: sound_driver.c,v 1.17 2004-09-21 10:37:15-04 kentd Exp $";
#include "defc.h"
#include "sound.h"
#ifdef HPUX
# include <sys/audio.h>
#endif
#if defined(__linux__) || defined(OSS)
# include <sys/soundcard.h>
#endif
#ifndef _WIN32
# include <sys/socket.h>
# include <netinet/in.h>
#endif
#include <errno.h>
extern int Verbose;
extern int g_audio_rate;
int g_preferred_rate = 48000;
int g_audio_socket = -1;
int g_bytes_written = 0;
#define ZERO_BUF_SIZE 2048
word32 g_snd_zero_buf[ZERO_BUF_SIZE];
#define ZERO_PAUSE_SAFETY_SAMPS (g_audio_rate >> 5)
#define ZERO_PAUSE_NUM_SAMPS (4*g_audio_rate)
int g_zeroes_buffered = 0;
int g_zeroes_seen = 0;
int g_sound_paused = 0;
int g_childsnd_vbl = 0;
int g_childsnd_pos = 0;
word32 *g_childsnd_shm_addr = 0;
void child_sound_init_linux();
void child_sound_init_hpdev();
void child_sound_init_win32();
void child_sound_init_mac();
void
reliable_buf_write(word32 *shm_addr, int pos, int size)
{
byte *ptr;
int ret;
if(size < 1 || pos < 0 || pos > SOUND_SHM_SAMP_SIZE ||
size > SOUND_SHM_SAMP_SIZE ||
(pos + size) > SOUND_SHM_SAMP_SIZE) {
printf("reliable_buf_write: pos: %04x, size: %04x\n",
pos, size);
exit(1);
}
ptr = (byte *)&(shm_addr[pos]);
size = size * 4;
while(size > 0) {
#ifdef _WIN32
ret = win32_send_audio(ptr, size);
#else
# ifdef MAC
ret = mac_send_audio(ptr, size);
# else
ret = write(g_audio_socket, ptr, size);
# endif
#endif
if(ret < 0) {
printf("audio write, errno: %d\n", errno);
exit(1);
}
size = size - ret;
ptr += ret;
g_bytes_written += ret;
}
}
void
reliable_zero_write(int amt)
{
int len;
while(amt > 0) {
len = MIN(amt, ZERO_BUF_SIZE);
reliable_buf_write(g_snd_zero_buf, 0, len);
amt -= len;
}
}
void
child_sound_loop(int read_fd, int write_fd, word32 *shm_addr)
{
#ifdef HPUX
long status_return;
#endif
word32 tmp;
int ret;
doc_printf("Child pipe fd: %d\n", read_fd);
g_audio_rate = g_preferred_rate;
g_zeroes_buffered = 0;
g_zeroes_seen = 0;
g_sound_paused = 0;
g_childsnd_pos = 0;
g_childsnd_vbl = 0;
g_childsnd_shm_addr = shm_addr;
#ifdef HPUX
child_sound_init_hpdev();
#endif
#if defined(__linux__) || defined(OSS)
child_sound_init_linux();
#endif
#ifdef _WIN32
child_sound_init_win32();
return;
#endif
#ifdef MAC
child_sound_init_mac();
return;
#endif
tmp = g_audio_rate;
ret = write(write_fd, &tmp, 4);
if(ret != 4) {
printf("Unable to send back audio rate to parent\n");
printf("ret: %d fd: %d, errno: %d\n", ret, write_fd, errno);
exit(1);
}
printf("Wrote to fd %d the audio rate\n", write_fd);
close(write_fd);
while(1) {
errno = 0;
ret = read(read_fd, &tmp, 4);
if(ret <= 0) {
printf("child dying from ret: %d, errno: %d\n",
ret, errno);
break;
}
child_sound_playit(tmp);
}
#ifdef HPUX
ioctl(g_audio_socket, AUDIO_DRAIN, 0);
#endif
close(g_audio_socket);
exit(0);
}
void
child_sound_playit(word32 tmp)
{
int size;
size = tmp & 0xffffff;
//printf("child_sound_playit: %08x\n", tmp);
if((tmp >> 24) == 0xa2) {
/* play sound here */
#if 0
g_childsnd_pos += g_zeroes_buffered;
while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) {
g_childsnd_pos -= SOUND_SHM_SAMP_SIZE;
}
#endif
if(g_zeroes_buffered) {
reliable_zero_write(g_zeroes_buffered);
}
g_zeroes_buffered = 0;
g_zeroes_seen = 0;
if((size + g_childsnd_pos) > SOUND_SHM_SAMP_SIZE) {
reliable_buf_write(g_childsnd_shm_addr, g_childsnd_pos,
SOUND_SHM_SAMP_SIZE - g_childsnd_pos);
size = (g_childsnd_pos + size) - SOUND_SHM_SAMP_SIZE;
g_childsnd_pos = 0;
}
reliable_buf_write(g_childsnd_shm_addr, g_childsnd_pos, size);
if(g_sound_paused) {
printf("Unpausing sound, zb: %d\n", g_zeroes_buffered);
g_sound_paused = 0;
}
} else if((tmp >> 24) == 0xa1) {
if(g_sound_paused) {
if(g_zeroes_buffered < ZERO_PAUSE_SAFETY_SAMPS) {
g_zeroes_buffered += size;
}
} else {
/* not paused, send it through */
g_zeroes_seen += size;
reliable_zero_write(size);
if(g_zeroes_seen >= ZERO_PAUSE_NUM_SAMPS) {
printf("Pausing sound\n");
g_sound_paused = 1;
}
}
} else {
printf("tmp received bad: %08x\n", tmp);
exit(3);
}
g_childsnd_pos += size;
while(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) {
g_childsnd_pos -= SOUND_SHM_SAMP_SIZE;
}
g_childsnd_vbl++;
if(g_childsnd_vbl >= 60) {
g_childsnd_vbl = 0;
#if 0
printf("sound bytes written: %06x\n", g_bytes_written);
printf("Sample samples[0]: %08x %08x %08x %08x\n",
g_childsnd_shm_addr[0], g_childsnd_shm_addr[1],
g_childsnd_shm_addr[2], g_childsnd_shm_addr[3]);
printf("Sample samples[100]: %08x %08x %08x %08x\n",
g_childsnd_shm_addr[100], g_childsnd_shm_addr[101],
g_childsnd_shm_addr[102], g_childsnd_shm_addr[103]);
#endif
g_bytes_written = 0;
}
}
#ifdef HPUX
void
child_sound_init_hpdev()
{
struct audio_describe audio_descr;
int output_channel;
char *str;
int speaker;
int ret;
int i;
g_audio_socket = open("/dev/audio", O_WRONLY, 0);
if(g_audio_socket < 0) {
printf("open /dev/audio failed, ret: %d, errno:%d\n",
g_audio_socket, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_DESCRIBE, &audio_descr);
if(ret < 0) {
printf("ioctl AUDIO_DESCRIBE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
for(i = 0; i < audio_descr.nrates; i++) {
printf("Audio rate[%d] = %d\n", i,
audio_descr.sample_rate[i]);
}
ret = ioctl(g_audio_socket, AUDIO_SET_DATA_FORMAT,
AUDIO_FORMAT_LINEAR16BIT);
if(ret < 0) {
printf("ioctl AUDIO_SET_DATA_FORMAT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_SET_CHANNELS, NUM_CHANNELS);
if(ret < 0) {
printf("ioctl AUDIO_SET_CHANNELS failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_SET_TXBUFSIZE, 16*1024);
if(ret < 0) {
printf("ioctl AUDIO_SET_TXBUFSIZE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_SET_SAMPLE_RATE, g_audio_rate);
if(ret < 0) {
printf("ioctl AUDIO_SET_SAMPLE_RATE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
ret = ioctl(g_audio_socket, AUDIO_GET_OUTPUT, &output_channel);
if(ret < 0) {
printf("ioctl AUDIO_GET_OUTPUT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
speaker = 1;
str = getenv("SPEAKER");
if(str) {
if(str[0] != 'i' && str[0] != 'I') {
speaker = 0;
}
}
if(speaker) {
printf("Sending sound to internal speaker\n");
output_channel |= AUDIO_OUT_SPEAKER;
} else {
printf("Sending sound to external jack\n");
output_channel &= (~AUDIO_OUT_SPEAKER);
output_channel |= AUDIO_OUT_HEADPHONE;
}
ret = ioctl(g_audio_socket, AUDIO_SET_OUTPUT, output_channel);
if(ret < 0) {
printf("ioctl AUDIO_SET_OUTPUT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
}
#endif /* HPUX */
#if defined(__linux__) || defined(OSS)
void
child_sound_init_linux()
{
int stereo;
int sample_size;
int rate;
int fragment;
int fmt;
int ret;
g_audio_socket = open("/dev/dsp", O_WRONLY, 0);
if(g_audio_socket < 0) {
printf("open /dev/dsp failed, ret: %d, errno:%d\n",
g_audio_socket, errno);
exit(1);
}
fragment = 0x00200009;
#if 0
ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFRAGMENT, &fragment);
if(ret < 0) {
printf("ioctl SETFRAGEMNT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
#endif
sample_size = 16;
ret = ioctl(g_audio_socket, SNDCTL_DSP_SAMPLESIZE, &sample_size);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_SAMPLESIZE failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
#ifdef KEGS_LITTLE_ENDIAN
fmt = AFMT_S16_LE;
#else
fmt = AFMT_S16_BE;
#endif
ret = ioctl(g_audio_socket, SNDCTL_DSP_SETFMT, &fmt);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_SETFMT failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
stereo = 1;
ret = ioctl(g_audio_socket, SNDCTL_DSP_STEREO, &stereo);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_STEREO failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
rate = g_audio_rate;
ret = ioctl(g_audio_socket, SNDCTL_DSP_SPEED, &rate);
if(ret < 0) {
printf("ioctl SNDCTL_DSP_SPEED failed, ret:%d, errno:%d\n",
ret, errno);
exit(1);
}
if(ret > 0) {
rate = ret; /* rate is returned value */
}
if(rate < 8000) {
printf("Audio rate of %d which is < 8000!\n", rate);
exit(1);
}
g_audio_rate = rate;
printf("Sound initialized\n");
}
#endif