/************************************************************************/ /* 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 #endif #if defined(__linux__) || defined(OSS) # include #endif #ifndef _WIN32 # include # include #endif #include 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