From 72512f2fc17873a7f614b001caffcc688ed05e27 Mon Sep 17 00:00:00 2001 From: David Schmidt Date: Sun, 16 Jan 2011 22:52:06 +0000 Subject: [PATCH] Have Windows work with sound --- src/sound.c | 4154 ++++++++++++++++++++++---------------------- src/sound_driver.c | 880 +++++----- src/vars_win32 | 2 +- 3 files changed, 2518 insertions(+), 2518 deletions(-) diff --git a/src/sound.c b/src/sound.c index 6825b20..f7a42ae 100644 --- a/src/sound.c +++ b/src/sound.c @@ -1,2077 +1,2077 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 by GSport contributors - - Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU 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 General Public License - for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "defc.h" -#include "sound.h" - -extern int Verbose; -extern int g_use_shmem; -extern word32 g_vbl_count; -extern int g_preferred_rate; -extern int g_c03ef_doc_ptr; - -extern double g_last_vbl_dcycs; - -void U_STACK_TRACE(); - -byte doc_ram[0x10000 + 16]; - -word32 doc_sound_ctl = 0; -word32 doc_saved_val = 0; -int g_doc_num_osc_en = 1; -double g_dcycs_per_doc_update = 1.0; -double g_dupd_per_dcyc = 1.0; -double g_drecip_osc_en_plus_2 = 1.0 / (double)(1 + 2); - -int g_doc_saved_ctl = 0; -int g_queued_samps = 0; -int g_queued_nonsamps = 0; -int g_num_osc_interrupting = 0; - -#if defined(HPUX) || defined(__linux__) || defined(_WIN32) || defined(MAC) -int g_audio_enable = -1; -#else -# if defined(OSS) -/* default to off for now */ -int g_audio_enable = 0; -# else -/* Default to sound off */ -int g_audio_enable = 0; -# endif -#endif - -Doc_reg g_doc_regs[32]; - -word32 doc_reg_e0 = 0xff; - -/* local function prototypes */ -void doc_write_ctl_reg(int osc, int val, double dsamps); - - -int g_audio_rate = 0; -double g_daudio_rate = 0.0; -double g_drecip_audio_rate = 0.0; -double g_dsamps_per_dcyc = 0.0; -double g_dcycs_per_samp = 0.0; -float g_fsamps_per_dcyc = 0.0; - -int g_doc_vol = 2; - -#define MAX_C030_TIMES 18000 - -double g_last_sound_play_dsamp = 0.0; - -float c030_fsamps[MAX_C030_TIMES + 1]; -int g_num_c030_fsamps = 0; - -#define DOC_SCAN_RATE (DCYCS_28_MHZ/32.0) - -int g_pipe_fd[2] = { -1, -1 }; -int g_pipe2_fd[2] = { -1, -1 }; -word32 *g_sound_shm_addr = 0; -int g_sound_shm_pos = 0; - -#define LEN_DOC_LOG 128 - -STRUCT(Doc_log) { - char *msg; - int osc; - double dsamps; - double dtmp2; - int etc; - Doc_reg doc_reg; -}; - -Doc_log g_doc_log[LEN_DOC_LOG]; -int g_doc_log_pos = 0; - - -#ifdef DO_DOC_LOG -# define DOC_LOG(a,b,c,d) doc_log_rout(a,b,c,d) -#else -# define DOC_LOG(a,b,c,d) -#endif - -#define UPDATE_G_DCYCS_PER_DOC_UPDATE(osc_en) \ - g_dcycs_per_doc_update = (double)((osc_en + 2) * DCYCS_1_MHZ) / \ - DOC_SCAN_RATE; \ - g_dupd_per_dcyc = 1.0 / g_dcycs_per_doc_update; \ - g_drecip_osc_en_plus_2 = 1.0 / (double)(osc_en + 2); - -#define SND_PTR_SHIFT 14 -#define SND_PTR_SHIFT_DBL ((double)(1 << SND_PTR_SHIFT)) - -void -doc_log_rout(char *msg, int osc, double dsamps, int etc) -{ - int pos; - - pos = g_doc_log_pos; - g_doc_log[pos].msg = msg; - g_doc_log[pos].osc = osc; - g_doc_log[pos].dsamps = dsamps; - g_doc_log[pos].dtmp2 = g_last_sound_play_dsamp; - g_doc_log[pos].etc = etc; - if(osc >= 0 && osc < 32) { - g_doc_log[pos].doc_reg = g_doc_regs[osc]; - } - pos++; - if(pos >= LEN_DOC_LOG) { - pos = 0; - } - - doc_printf("log: %s, osc:%d dsamp:%f, etc:%d\n", msg, osc, dsamps, etc); - - g_doc_log_pos = pos; -} - -extern double g_cur_dcycs; - -void -show_doc_log(void) -{ - FILE *docfile; - Doc_reg *rptr; - double dsamp_start; - int osc, ctl, freq; - int pos; - int i; - - docfile = fopen("doc_log_out", "w"); - if(docfile == 0) { - printf("fopen failed, errno: %d\n", errno); - return; - } - pos = g_doc_log_pos; - fprintf(docfile, "DOC log pos: %d\n", pos); - dsamp_start = g_doc_log[pos].dsamps; - for(i = 0; i < LEN_DOC_LOG; i++) { - rptr = &(g_doc_log[pos].doc_reg); - osc = g_doc_log[pos].osc; - ctl = rptr->ctl; - freq = rptr->freq; - if(osc < 0) { - ctl = 0; - freq = 0; - } - fprintf(docfile, "%03x:%03x: %-11s ds:%11.1f dt2:%10.1f " - "etc:%08x o:%02x c:%02x fq:%04x\n", - i, pos, g_doc_log[pos].msg, - g_doc_log[pos].dsamps - dsamp_start, - g_doc_log[pos].dtmp2, - g_doc_log[pos].etc, osc & 0xff, ctl, freq); - if(osc >= 0) { - fprintf(docfile, " ire:%d,%d,%d ptr4:%08x " - "inc4:%08x comp_ds:%.1f left:%04x, vol:%02x " - "wptr:%02x, wsz:%02x, 4st:%08x, 4end:%08x\n", - rptr->has_irq_pending, rptr->running, - rptr->event, 4*rptr->cur_acc, 4*rptr->cur_inc, - rptr->complete_dsamp - dsamp_start, - rptr->samps_left, rptr->vol, rptr->waveptr, - rptr->wavesize, 4*rptr->cur_start, - 4*rptr->cur_end); - } - pos++; - if(pos >= LEN_DOC_LOG) { - pos = 0; - } - } - - fprintf(docfile, "cur_dcycs: %f\n", g_cur_dcycs); - fprintf(docfile, "dsamps_now: %f\n", - (g_cur_dcycs * g_dsamps_per_dcyc) - dsamp_start); - fprintf(docfile, "g_doc_num_osc_en: %d\n", g_doc_num_osc_en); - fclose(docfile); -} - -void -sound_init() -{ - Doc_reg *rptr; - int i; - - for(i = 0; i < 32; i++) { - rptr = &(g_doc_regs[i]); - rptr->dsamp_ev = 0.0; - rptr->dsamp_ev2 = 0.0; - rptr->complete_dsamp = 0.0; - rptr->samps_left = 0; - rptr->cur_acc = 0; - rptr->cur_inc = 0; - rptr->cur_start = 0; - rptr->cur_end = 0; - rptr->cur_mask = 0; - rptr->size_bytes = 0; - rptr->event = 0; - rptr->running = 0; - rptr->has_irq_pending = 0; - rptr->freq = 0; - rptr->vol = 0; - rptr->waveptr = 0; - rptr->ctl = 1; - rptr->wavesize = 0; - rptr->last_samp_val = 0; - } - - // OG sound globals initialization - g_num_c030_fsamps = 0; - g_sound_shm_pos = 0; - g_queued_samps = 0; - g_queued_nonsamps = 0; - - doc_sound_ctl = 0; - doc_saved_val = 0; - g_doc_num_osc_en = 1; - g_dcycs_per_doc_update = 1.0; - g_dupd_per_dcyc = 1.0; - g_drecip_osc_en_plus_2 = 1.0 / (double)(1 + 2); - - doc_reg_e0 = 0xff; - g_audio_rate = 0; - g_daudio_rate = 0.0; - g_drecip_audio_rate = 0.0; - g_dsamps_per_dcyc = 0.0; - g_dcycs_per_samp = 0.0; - g_fsamps_per_dcyc = 0.0; - - g_doc_vol = 2; - - g_last_sound_play_dsamp = 0.0; - - sound_init_general(); -} - - -void -sound_init_general() -{ -#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(MAC) && !defined(__OS2__) - int pid; - int shmid; - int tmp; - int i; -#endif - word32 *shmaddr; - int size; - int ret; - -#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(MAC) && !defined(__OS2__) - if(!g_use_shmem) { - if(g_audio_enable < 0) { - printf("Defaulting audio off for slow X display\n"); - g_audio_enable = 0; - } - } -#endif - ret = 0; - - if(g_audio_enable == 0) { - set_audio_rate(g_preferred_rate); - return; - } - - size = SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE; - -#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(MAC) && !defined(__OS2__) - shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777); - if(shmid < 0) { - printf("sound_init: shmget ret: %d, errno: %d\n", shmid, - errno); - exit(2); - } - - shmaddr = shmat(shmid, 0, 0); - tmp = (int)PTR2WORD(shmaddr); - if(tmp == -1) { - printf("sound_init: shmat ret: %p, errno: %d\n", shmaddr, - errno); - exit(3); - } - - ret = shmctl(shmid, IPC_RMID, 0); - if(ret < 0) { - printf("sound_init: shmctl ret: %d, errno: %d\n", ret, errno); - exit(4); - } -#else -/* windows and mac */ - shmaddr = (word32*)malloc(size); - memset(shmaddr, 0, size); -#endif - - g_sound_shm_addr = shmaddr; - - fflush(stdout); - -#if !defined(MAC) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__OS2__) - /* prepare pipe so parent can signal child each other */ - /* pipe[0] = read side, pipe[1] = write end */ - ret = pipe(&g_pipe_fd[0]); - if(ret < 0) { - printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno); - exit(5); - } - ret = pipe(&g_pipe2_fd[0]); - if(ret < 0) { - printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno); - exit(5); - } - - - printf("pipes: pipe_fd = %d, %d pipe2_fd: %d,%d\n", - g_pipe_fd[0], g_pipe_fd[1], g_pipe2_fd[0], g_pipe2_fd[1]); - fflush(stdout); - - pid = fork(); - switch(pid) { - case 0: - /* child */ - /* close stdin and write-side of pipe */ - close(0); - /* Close other fds to make sure X window fd is closed */ - for(i = 3; i < 100; i++) { - if((i != g_pipe_fd[0]) && (i != g_pipe2_fd[1])) { - close(i); - } - } - close(g_pipe_fd[1]); /*make sure write pipe closed*/ - close(g_pipe2_fd[0]); /*make sure read pipe closed*/ - child_sound_loop(g_pipe_fd[0], g_pipe2_fd[1], g_sound_shm_addr); - printf("Child sound loop returned\n"); - exit(0); - case -1: - /* error */ - printf("sound_init: fork ret: -1, errno: %d\n", errno); - exit(6); - default: - /* parent */ - /* close read-side of pipe1, and the write side of pipe2 */ - close(g_pipe_fd[0]); - close(g_pipe2_fd[1]); - doc_printf("Child is pid: %d\n", pid); - } - - parent_sound_get_sample_rate(g_pipe2_fd[0]); -#else -# ifdef MAC - macsnd_init(shmaddr); -# elif defined (_WIN32) -/* windows */ - win32snd_init(shmaddr); -# elif defined (__OS2__) -# endif -#endif /* _WIN32 */ - -} - -void -parent_sound_get_sample_rate(int read_fd) -{ -#ifndef __OS2__ - word32 tmp; - int ret; - - ret = read(read_fd, (char*)&tmp, 4); - if(ret != 4) { - printf("parent could not get audio sample rate from child, disabling sound.\n"); - printf("ret: %d, fd: %d errno:%d\n", ret, read_fd, errno); - g_audio_enable = 0; - } - close(read_fd); - - set_audio_rate(tmp); -#endif -} - -void -set_audio_rate(int rate) -{ - g_audio_rate = rate; - g_daudio_rate = (rate)*1.0; - g_drecip_audio_rate = 1.0/(rate); - g_dsamps_per_dcyc = ((rate*1.0) / DCYCS_1_MHZ); - g_dcycs_per_samp = (DCYCS_1_MHZ / (rate*1.0)); - g_fsamps_per_dcyc = (float)((rate*1.0) / DCYCS_1_MHZ); -} - -void -sound_reset(double dcycs) -{ - double dsamps; - int i; - - dsamps = dcycs * g_dsamps_per_dcyc; - for(i = 0; i < 32; i++) { - doc_write_ctl_reg(i, g_doc_regs[i].ctl | 1, dsamps); - doc_reg_e0 = 0xff; - if(g_doc_regs[i].has_irq_pending) { - halt_printf("reset: has_irq[%02x] = %d\n", i, - g_doc_regs[i].has_irq_pending); - } - g_doc_regs[i].has_irq_pending = 0; - } - if(g_num_osc_interrupting) { - halt_printf("reset: num_osc_int:%d\n", g_num_osc_interrupting); - } - g_num_osc_interrupting = 0; - -// OG No reason to reset the number of active oscillo on reset : this should only be done on startup. - /* - g_doc_num_osc_en = 1; - UPDATE_G_DCYCS_PER_DOC_UPDATE(1); - */ -} - -void -sound_shutdown() -{ - // OG stop sound and free memory on sound_shutdown - sound_reset(g_cur_dcycs); - -#ifdef _WIN32 - win32snd_shutdown(); -#elif defined(__OS2__) -#else - if((g_audio_enable != 0) && g_pipe_fd[1] != 0) { - close(g_pipe_fd[1]); - } -#endif - - // OG Free up allocated memory - if (g_sound_shm_addr) - { - free(g_sound_shm_addr); - g_sound_shm_addr = NULL; - } - -} - - - -void -sound_update(double dcycs) -{ - double dsamps; - /* Called every VBL time to update sound status */ - - /* "play" sounds for this vbl */ - - dsamps = dcycs * g_dsamps_per_dcyc; - DOC_LOG("do_snd_pl", -1, dsamps, 0); - sound_play(dsamps); -} - -#define MAX_SND_BUF 65536 - -int g_samp_buf[2*MAX_SND_BUF]; -word32 zero_buf[SOUND_SHM_SAMP_SIZE]; - -double g_doc_dsamps_extra = 0.0; - -float g_fvoices = 0.0; - -word32 g_cycs_in_sound1 = 0; -word32 g_cycs_in_sound2 = 0; -word32 g_cycs_in_sound3 = 0; -word32 g_cycs_in_sound4 = 0; -word32 g_cycs_in_start_sound = 0; -word32 g_cycs_in_est_sound = 0; - -int g_num_snd_plays = 0; -int g_num_doc_events = 0; -int g_num_start_sounds = 0; -int g_num_scan_osc = 0; -int g_num_recalc_snd_parms = 0; - -word32 g_last_c030_vbl_count = 0; -int g_c030_state = 0; - -#define VAL_C030_RANGE (32768) -#define VAL_C030_BASE (-16384) - -int g_sound_file_num = 0; -FILE *g_sound_file_fd = 0; -int g_send_sound_to_file = 0; -int g_send_file_bytes = 0; - -void -open_sound_file() -{ - char name[256]; - FILE *fd; - - sprintf(name, "snd.out.%d", g_sound_file_num); - - fd = fopen(name, "wb+"); - if(fd == 0) { - printf("open_sound_file open errno: %d\n", errno); - exit(1); - } - - g_sound_file_fd = fd; - g_sound_file_num++; - g_send_file_bytes = 0; -} - -void -close_sound_file() -{ - if(g_sound_file_fd != 0) { - fclose(g_sound_file_fd); - } - - g_sound_file_fd = 0; -} - -void -check_for_range(word32 *addr, int num_samps, int offset) -{ - short *shortptr; - int i; - int left; - int right; - int max; - - max = -32768; - - if(num_samps > SOUND_SHM_SAMP_SIZE) { - halt_printf("num_samps: %d > %d!\n", num_samps, - SOUND_SHM_SAMP_SIZE); - } - - for(i = 0; i < num_samps; i++) { - shortptr = (short *)&(addr[i]); - left = shortptr[0]; - right = shortptr[1]; - if((left > 0x3000) || (right > 0x3000)) { - halt_printf("Sample %d of %d at snd_buf: %p is: " - "%d/%d\n", i + offset, num_samps, - &addr[i], left, right); - return; - } - - max = MAX(max, left); - max = MAX(max, right); - } - - printf("check4 max: %d over %d\n", max, num_samps); -} - -void -send_sound_to_file(word32 *addr, int shm_pos, int num_samps) -{ - int size; - int ret; - - if(g_sound_file_fd == 0) { - open_sound_file(); - } - - size = 0; - if((num_samps + shm_pos) > SOUND_SHM_SAMP_SIZE) { - size = SOUND_SHM_SAMP_SIZE - shm_pos; - g_send_file_bytes += (size * 4); - - ret = fwrite(&(addr[shm_pos]), 1, 4*size, g_sound_file_fd); - if(ret != 4*size) { - halt_printf("wrote %d not %d\n", ret, 4*size); - } - - if(g_doc_vol < 3) { - check_for_range(&(addr[shm_pos]), size, 0); - } else { - printf("Not checking %d bytes since vol: %d\n", - 4*size, g_doc_vol); - } - shm_pos = 0; - num_samps -= size; - } - - g_send_file_bytes += (num_samps * 4); - - ret = fwrite(&(addr[shm_pos]), 1, 4*num_samps, g_sound_file_fd); - if(ret != 4*num_samps) { - halt_printf("wrote %d not %d\n", ret, 4*num_samps); - } - - if(g_doc_vol < 3) { - check_for_range(&(addr[shm_pos]), num_samps, size); - } else { - printf("Not checking2 %d bytes since vol: %d\n", - 4*num_samps, g_doc_vol); - } - -} - -void -send_sound(int real_samps, int size) -{ - word32 tmp; - int ret; - - if(g_audio_enable == 0) { - printf("Entered send_sound but audio off!\n"); - exit(2); - } - - if(real_samps) { - tmp = size + 0xa2000000; - } else { - tmp = size + 0xa1000000; - } - DOC_LOG("send_sound", -1, g_last_sound_play_dsamp, - (real_samps << 30) + size); - -#if defined(MAC) || defined(_WIN32) - ret = 0; - child_sound_playit(tmp); -#elif defined(__OS2__) - -#else - /* Although this looks like a big/little-endian issue, since the */ - /* child is also reading an int, it just works with no byte swap */ - ret = write(g_pipe_fd[1], &tmp, 4); - if(ret != 4) { - halt_printf("send_sound, wr ret: %d, errno: %d\n", ret, errno); - } -#endif -} - -void -show_c030_state() -{ - show_c030_samps(&(g_samp_buf[0]), 100); -} - -void -show_c030_samps(int *outptr, int num) -{ - int i; - - printf("c030_fsamps[]: %d\n", g_num_c030_fsamps); - - for(i = 0; i < g_num_c030_fsamps+2; i++) { - printf("%3d: %5.3f\n", i, c030_fsamps[i]); - } - - printf("Samples[] = %d\n", num); - - for(i = 0; i < num+2; i++) { - printf("%4d: %d %d\n", i, outptr[0], outptr[1]); - outptr += 2; - } -} - -int g_sound_play_depth = 0; - -void -sound_play(double dsamps) -{ - register word32 start_time1, start_time2, start_time3, start_time4; - register word32 end_time1, end_time2, end_time3; - Doc_reg *rptr; - int *outptr; - int *outptr_start; - word32 *sndptr; - double complete_dsamp; - double cur_dsamp; - double last_dsamp; - double dsamp_now; - double dnum_samps; - int val, val2; - int new_val; - float ftmp; - int imul; - int off; - int num; - float fsampnum; - float next_fsampnum; - int c030_lo_val, c030_hi_val; - float fc030_range; - float fc030_base; - int sampnum; - int next_sampnum; - float fpercent; - int c030_state; - int val0, val1; - word32 cur_acc; - word32 cur_pos; - word32 cur_mask; - word32 cur_inc; - word32 cur_end; - int ctl; - int num_osc_en; - int samps_left; - int samps_to_do; - int samps_played; - int samp_offset; - int snd_buf_init; - int pos; - int num_running; - int num_samps; - int osc; - int done; - int i, j; - - - GET_ITIMER(start_time1); - - g_num_snd_plays++; - if(g_sound_play_depth) { - halt_printf("Nested sound_play!\n"); - } - - g_sound_play_depth++; - - /* calc sample num */ - - last_dsamp = g_last_sound_play_dsamp; - num_samps = (int)(dsamps - g_last_sound_play_dsamp); - dnum_samps = (double)num_samps; - - dsamp_now = last_dsamp + dnum_samps; - - if(num_samps < 1) { - /* just say no */ - g_sound_play_depth--; - return; - } - - DOC_LOG("sound_play", -1, dsamp_now, num_samps); - - if(num_samps > MAX_SND_BUF) { - printf("num_samps: %d, too big!\n", num_samps); - g_sound_play_depth--; - return; - } - - - GET_ITIMER(start_time4); - - outptr_start = &(g_samp_buf[0]); - outptr = outptr_start; - - snd_buf_init = 0; - - samps_played = 0; - - num = g_num_c030_fsamps; - - if(num || ((g_vbl_count - g_last_c030_vbl_count) < 240)) { - - if(num) { - g_last_c030_vbl_count = g_vbl_count; - } - - pos = 0; - outptr = outptr_start; - c030_state = g_c030_state; - - c030_hi_val = ((VAL_C030_BASE + VAL_C030_RANGE)*g_doc_vol) >> 4; - c030_lo_val = (VAL_C030_BASE * g_doc_vol) >> 4; - - fc030_range = (float)(((VAL_C030_RANGE) * g_doc_vol) >> 4); - fc030_base = (float)(((VAL_C030_BASE) * g_doc_vol) >> 4); - - val = c030_lo_val; - if(c030_state) { - val = c030_hi_val; - } - - snd_buf_init++; - - c030_fsamps[num] = (float)(num_samps); - c030_fsamps[num+1] = (float)(num_samps+1); - - ftmp = (float)num_samps; - /* ensure that all samps are in range */ - for(i = num - 1; i >= 0; i--) { - if(c030_fsamps[i] > ftmp) { - c030_fsamps[i] = ftmp; - } - } - - num++; - fsampnum = c030_fsamps[0]; - sampnum = (int)fsampnum; - fpercent = (float)0.0; - i = 0; - - while(i < num) { - next_fsampnum = c030_fsamps[i+1]; - next_sampnum = (int)next_fsampnum; - if(sampnum < 0 || sampnum > num_samps) { - halt_printf("play c030: [%d]:%f is %d, > %d\n", - i, fsampnum, sampnum, num_samps); - break; - } - - /* write in samples to all samps < me */ - new_val = c030_lo_val; - if(c030_state) { - new_val = c030_hi_val; - } - for(j = pos; j < sampnum; j++) { - outptr[0] = new_val; - outptr[1] = new_val; - outptr += 2; - pos++; - } - - /* now, calculate me */ - fpercent = (float)0.0; - if(c030_state) { - fpercent = (fsampnum - (float)sampnum); - } - - c030_state = !c030_state; - - while(next_sampnum == sampnum) { - if(c030_state) { - fpercent += (next_fsampnum - fsampnum); - } - i++; - fsampnum = next_fsampnum; - - next_fsampnum = c030_fsamps[i+1]; - next_sampnum = (int)next_fsampnum; - c030_state = !c030_state; - } - - if(c030_state) { - /* add in fractional time */ - ftmp = (float)(int)(fsampnum + 1.0f); //OG added cast - fpercent += (ftmp - fsampnum); - } - - if((fpercent < (float)0.0) || (fpercent > (float)1.0)) { - halt_printf("fpercent: %d = %f\n", i, fpercent); - show_c030_samps(outptr_start, num_samps); - break; - } - - val = (int)((fpercent * fc030_range) + fc030_base); - outptr[0] = val; - outptr[1] = val; - outptr += 2; - pos++; - i++; - - sampnum = next_sampnum; - fsampnum = next_fsampnum; - } - - samps_played += num_samps; - - /* since we pretended to get one extra sample, we will */ - /* have toggled the speaker one time too many. Fix it */ - g_c030_state = !c030_state; - - if(g_send_sound_to_file) { - show_c030_samps(outptr_start, num_samps); - } - } - - g_num_c030_fsamps = 0; - - GET_ITIMER(start_time2); - - num_running = 0; - - num_osc_en = g_doc_num_osc_en; - - done = 0; - while(!done) { - done = 1; - for(j = 0; j < num_osc_en; j++) { - osc = j; - rptr = &(g_doc_regs[osc]); - complete_dsamp = rptr->complete_dsamp; - samps_left = rptr->samps_left; - cur_acc = rptr->cur_acc; - cur_mask = rptr->cur_mask; - cur_inc = rptr->cur_inc; - cur_end = rptr->cur_end; - if(!rptr->running || cur_inc == 0 || - (complete_dsamp >= dsamp_now)) { - continue; - } - - done = 0; - ctl = rptr->ctl; - - samp_offset = 0; - if(complete_dsamp > last_dsamp) { - samp_offset = (int)(complete_dsamp- last_dsamp); - if(samp_offset > num_samps) { - rptr->complete_dsamp = dsamp_now; - continue; - } - } - outptr = outptr_start + 2 * samp_offset; - if(ctl & 0x10) { - /* other channel */ - outptr += 1; - } - - imul = (rptr->vol * g_doc_vol); - off = imul * 128; - - samps_to_do = MIN(samps_left, num_samps - samp_offset); - if(imul == 0 || samps_to_do == 0) { - /* produce no sound */ - samps_left = samps_left - samps_to_do; - cur_acc += cur_inc * samps_to_do; - rptr->samps_left = samps_left; - rptr->cur_acc = cur_acc; - cur_dsamp = last_dsamp + - (double)(samps_to_do + samp_offset); - DOC_LOG("nosnd", osc, cur_dsamp, samps_to_do); - rptr->complete_dsamp = dsamp_now; - cur_pos = rptr->cur_start+(cur_acc & cur_mask); - if(samps_left <= 0) { - doc_sound_end(osc, 1, cur_dsamp, - dsamp_now); - val = 0; - j--; - } else { - val = doc_ram[cur_pos >> SND_PTR_SHIFT]; - } - rptr->last_samp_val = val; - continue; - } - - if(snd_buf_init == 0) { - memset(outptr_start, 0, - 2*sizeof(outptr_start[0])*num_samps); - snd_buf_init++; - } - - val = 0; - rptr->complete_dsamp = dsamp_now; - cur_pos = rptr->cur_start + (cur_acc & cur_mask); - for(i = 0; i < samps_to_do; i++) { - pos = cur_pos >> SND_PTR_SHIFT; - cur_pos += cur_inc; - cur_acc += cur_inc; - val = doc_ram[pos]; - - val2 = (val * imul - off) >> 4; - if((val == 0) || (cur_pos >= cur_end)) { - cur_dsamp = last_dsamp + - (double)(samp_offset + i + 1); - rptr->cur_acc = cur_acc; - rptr->samps_left = 0; - DOC_LOG("end or 0", osc, cur_dsamp, - (pos << 16) + ((i &0xff) << 8) + - val); - doc_sound_end(osc, val, cur_dsamp, - dsamp_now); - val = 0; - break; - } - - val2 = outptr[0] + val2; - - samps_left--; - *outptr = val2; - outptr += 2; - } - - rptr->last_samp_val = val; - - if(val != 0) { - rptr->cur_acc = cur_acc; - rptr->samps_left = samps_left; - rptr->complete_dsamp = dsamp_now; - } - - samps_played += samps_to_do; - DOC_LOG("splayed", osc, dsamp_now, - (samps_to_do << 16) + (pos & 0xffff)); - } - } - - GET_ITIMER(end_time2); - - g_cycs_in_sound2 += (end_time2 - start_time2); - - g_last_sound_play_dsamp = dsamp_now; - - GET_ITIMER(start_time3); - - outptr = outptr_start; - - pos = g_sound_shm_pos; - sndptr = g_sound_shm_addr; - -#if 0 - printf("samps_left: %d, num_samps: %d\n", samps_left, num_samps); -#endif - - if(g_audio_enable != 0) { - - if(snd_buf_init) { - /* convert sound buf */ - - for(i = 0; i < num_samps; i++) { - val0 = outptr[0]; - val1 = outptr[1]; - val = val0; - if(val0 > 32767) { - val = 32767; - } - if(val0 < -32768) { - val = -32768; - } - - val0 = val; - val = val1; - if(val1 > 32767) { - val = 32767; - } - if(val1 < -32768) { - val = -32768; - } - - - outptr += 2; - -#if defined(__linux__) || defined(OSS) - /* Linux seems to expect little-endian */ - /* samples always, even on PowerPC */ -# ifdef GSPORT_LITTLE_ENDIAN - sndptr[pos] = (val << 16) + (val0 & 0xffff); -# else - sndptr[pos] = ((val & 0xff) << 24) + - ((val & 0xff00) << 8) + - ((val0 & 0xff) << 8) + - ((val0 >> 8) & 0xff); -# endif -#else -# ifdef GSPORT_LITTLE_ENDIAN - sndptr[pos] = (val << 16) + (val0 & 0xffff); -# else - sndptr[pos] = (val0 << 16) + (val & 0xffff); -# endif -#endif - pos++; - if(pos >= SOUND_SHM_SAMP_SIZE) { - pos = 0; - } - } - - if(g_queued_nonsamps) { - /* force out old 0 samps */ - send_sound(0, g_queued_nonsamps); - g_queued_nonsamps = 0; - } - - if(g_send_sound_to_file) { - send_sound_to_file(g_sound_shm_addr, - g_sound_shm_pos, num_samps); - } - - g_queued_samps += num_samps; - } else { - /* move pos */ - pos += num_samps; - while(pos >= SOUND_SHM_SAMP_SIZE) { - pos -= SOUND_SHM_SAMP_SIZE; - } - - if(g_send_sound_to_file) { - send_sound_to_file(zero_buf, g_sound_shm_pos, - num_samps); - } - - if(g_queued_samps) { - /* force out old non-0 samps */ - send_sound(1, g_queued_samps); - g_queued_samps = 0; - } - - g_queued_nonsamps += num_samps; - } - - } - - g_sound_shm_pos = pos; - - - GET_ITIMER(end_time3); - - g_fvoices += ((float)(samps_played) * (float)(g_drecip_audio_rate)); - - if(g_audio_enable != 0) { - if(g_queued_samps >= (g_audio_rate/32)) { - send_sound(1, g_queued_samps); - g_queued_samps = 0; - } - - if(g_queued_nonsamps >= (g_audio_rate/32)) { - send_sound(0, g_queued_nonsamps); - g_queued_nonsamps = 0; - } - } - - GET_ITIMER(end_time1); - - g_cycs_in_sound1 += (end_time1 - start_time1); - g_cycs_in_sound3 += (end_time3 - start_time3); - g_cycs_in_sound4 += (start_time2 - start_time4); - - g_last_sound_play_dsamp = dsamp_now; - - g_sound_play_depth--; -} - - -void -doc_handle_event(int osc, double dcycs) -{ - double dsamps; - - /* handle osc stopping and maybe interrupting */ - - g_num_doc_events++; - - dsamps = dcycs * g_dsamps_per_dcyc; - - DOC_LOG("doc_ev", osc, dcycs, 0); - - g_doc_regs[osc].event = 0; - - sound_play(dsamps); - -} - -void -doc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps) -{ - Doc_reg *rptr, *orptr; - int mode, omode; - int other_osc; - int one_shot_stop; - int ctl; - - /* handle osc stopping and maybe interrupting */ - - if(osc < 0 || osc > 31) { - printf("doc_handle_event: osc: %d!\n", osc); - return; - } - - rptr = &(g_doc_regs[osc]); - ctl = rptr->ctl; - - if(rptr->event) { - remove_event_doc(osc); - } - rptr->event = 0; - rptr->cur_acc = 0; /* reset internal accumulator*/ - - /* check to make sure osc is running */ - if(ctl & 0x01) { - /* Oscillator already stopped. */ - halt_printf("Osc %d interrupt, but it was already stop!\n",osc); -#ifdef HPUX - U_STACK_TRACE(); -#endif - return; - } - - if(ctl & 0x08) { - if(rptr->has_irq_pending == 0) { - add_sound_irq(osc); - } - } - - if(!rptr->running) { - halt_printf("Doc event for osc %d, but ! running\n", osc); - } - - rptr->running = 0; - - mode = (ctl >> 1) & 3; - other_osc = osc ^ 1; - orptr = &(g_doc_regs[other_osc]); - omode = (orptr->ctl >> 1) & 3; - - /* If either this osc or it's partner is in swap mode, treat the */ - /* pair as being in swap mode. This Ensoniq feature pointed out */ - /* by Ian Schmidt */ - if(mode == 0 && can_repeat) { - /* free-running mode with no 0 byte! */ - /* start doing it again */ - - start_sound(osc, eff_dsamps, dsamps); - - return; - } else if((mode == 3) || (omode == 3)) { - /* swap mode (even if we're one_shot and partner is swap)! */ - /* unless we're one-shot and we hit a 0-byte--then */ - /* Olivier Goguel says just stop */ - rptr->ctl |= 1; - one_shot_stop = (mode == 1) && (!can_repeat); - if(!one_shot_stop && !orptr->running && - (orptr->ctl & 0x1)) { - orptr->ctl = orptr->ctl & (~1); - start_sound(other_osc, eff_dsamps, dsamps); - } - return; - } else { - /* stop the oscillator */ - rptr->ctl |= 1; - } - - return; -} - -void -add_sound_irq(int osc) -{ - int num_osc_interrupting; - - if(g_doc_regs[osc].has_irq_pending) { - halt_printf("Adding sound_irq for %02x, but irq_p: %d\n", osc, - g_doc_regs[osc].has_irq_pending); - } - - num_osc_interrupting = g_num_osc_interrupting + 1; - g_doc_regs[osc].has_irq_pending = num_osc_interrupting; - g_num_osc_interrupting = num_osc_interrupting; - - add_irq(IRQ_PENDING_DOC); - if(num_osc_interrupting == 1) { - doc_reg_e0 = 0x00 + (osc << 1); - } - - DOC_LOG("add_irq", osc, g_cur_dcycs * g_dsamps_per_dcyc, 0); -} - -void -remove_sound_irq(int osc, int must) -{ - Doc_reg *rptr; - int num_osc_interrupt; - int has_irq_pending; - int first; - int i; - - doc_printf("remove irq for osc: %d, has_irq: %d\n", - osc, g_doc_regs[osc].has_irq_pending); - - num_osc_interrupt = g_doc_regs[osc].has_irq_pending; - first = 0; - if(num_osc_interrupt) { - g_num_osc_interrupting--; - g_doc_regs[osc].has_irq_pending = 0; - DOC_LOG("rem_irq", osc, g_cur_dcycs * g_dsamps_per_dcyc, 0); - if(g_num_osc_interrupting == 0) { - remove_irq(IRQ_PENDING_DOC); - } - - first = 0x40 | (doc_reg_e0 >> 1); - /* if none found, then def = no ints */ - for(i = 0; i < g_doc_num_osc_en; i++) { - rptr = &(g_doc_regs[i]); - has_irq_pending = rptr->has_irq_pending; - if(has_irq_pending > num_osc_interrupt) { - has_irq_pending--; - rptr->has_irq_pending = has_irq_pending; - } - if(has_irq_pending == 1) { - first = i; - } - } - if(num_osc_interrupt == 1) { - doc_reg_e0 = (first << 1); - } else { -#if 0 - halt_printf("remove_sound_irq[%02x]=%d, first:%d\n", - osc, num_osc_interrupt, first); -#endif - } - } else { -#if 0 - /* make sure no int pending */ - if(doc_reg_e0 != 0xff) { - halt_printf("remove_sound_irq[%02x]=0, but e0: %02x\n", - osc, doc_reg_e0); - } -#endif - if(must) { - halt_printf("REMOVE_sound_irq[%02x]=0, but e0: %02x\n", - osc, doc_reg_e0); - } - } - - if(doc_reg_e0 & 0x80) { - for(i = 0; i < 0x20; i++) { - has_irq_pending = g_doc_regs[i].has_irq_pending; - if(has_irq_pending) { - halt_printf("remove_sound_irq[%02x], but " - "[%02x]=%d!\n", osc,i,has_irq_pending); - printf("num_osc_int: %d, first: %02x\n", - num_osc_interrupt, first); - } - } - } -} - -void -start_sound(int osc, double eff_dsamps, double dsamps) -{ - register word32 start_time1; - register word32 end_time1; - Doc_reg *rptr; - int ctl; - int mode; - word32 sz; - word32 size; - word32 wave_size; - - if(osc < 0 || osc > 31) { - halt_printf("start_sound: osc: %02x!\n", osc); - } - - g_num_start_sounds++; - - rptr = &(g_doc_regs[osc]); - - if(osc >= g_doc_num_osc_en) { - rptr->ctl |= 1; - return; - } - - GET_ITIMER(start_time1); - - ctl = rptr->ctl; - - mode = (ctl >> 1) & 3; - - wave_size = rptr->wavesize; - - sz = ((wave_size >> 3) & 7) + 8; - size = 1 << sz; - - if(size < 0x100) { - halt_printf("size: %08x is too small, sz: %08x!\n", size, sz); - } - - if(rptr->running) { - halt_printf("start_sound osc: %d, already running!\n", osc); - } - - rptr->running = 1; - - rptr->complete_dsamp = eff_dsamps; - - doc_printf("Starting osc %02x, dsamp: %f\n", osc, dsamps); - doc_printf("size: %04x\n", size); - - if((mode == 2) && ((osc & 1) == 0)) { - printf("Sync mode osc %d starting!\n", osc); - /* set_halt(1); */ - - /* see if we should start our odd partner */ - if((rptr[1].ctl & 7) == 5) { - /* odd partner stopped in sync mode--start him */ - rptr[1].ctl &= (~1); - start_sound(osc + 1, eff_dsamps, dsamps); - } else { - printf("Osc %d starting sync, but osc %d ctl: %02x\n", - osc, osc+1, rptr[1].ctl); - } - } - - wave_end_estimate(osc, eff_dsamps, dsamps); - - DOC_LOG("st playing", osc, eff_dsamps, size); -#if 0 - if(rptr->cur_acc != 0) { - halt_printf("Start osc %02x, acc: %08x\n", osc, rptr->cur_acc); - } -#endif - - GET_ITIMER(end_time1); - - g_cycs_in_start_sound += (end_time1 - start_time1); -} - -void -wave_end_estimate(int osc, double eff_dsamps, double dsamps) -{ - register word32 start_time1; - register word32 end_time1; - Doc_reg *rptr; - byte *ptr1; - double event_dsamp; - double event_dcycs; - double dcycs_per_samp; - double dsamps_per_byte; - double num_dsamps; - double dcur_inc; - word32 tmp1; - word32 cur_inc; - word32 save_val; - int save_size; - int pos; - int size; - int estimate; - - GET_ITIMER(start_time1); - - dcycs_per_samp = g_dcycs_per_samp; - - rptr = &(g_doc_regs[osc]); - - cur_inc = rptr->cur_inc; - dcur_inc = (double)cur_inc; - dsamps_per_byte = 0.0; - if(cur_inc) { - dsamps_per_byte = SND_PTR_SHIFT_DBL / (double)dcur_inc; - } - - /* see if there's a zero byte */ - tmp1 = rptr->cur_start + (rptr->cur_acc & rptr->cur_mask); - pos = tmp1 >> SND_PTR_SHIFT; - size = ((rptr->cur_end) >> SND_PTR_SHIFT) - pos; - - ptr1 = &doc_ram[pos]; - - estimate = 0; - if(rptr->ctl & 0x08 || g_doc_regs[osc ^ 1].ctl & 0x08) { - estimate = 1; - } - -#if 0 - estimate = 1; -#endif - if(estimate) { - save_size = size; - save_val = ptr1[size]; - ptr1[size] = 0; - size = strlen((char *)ptr1); - ptr1[save_size] = save_val; - } - - /* calc samples to play */ - num_dsamps = (dsamps_per_byte * (double)size) + 1.0; - - rptr->samps_left = (int)num_dsamps; - - if(rptr->event) { - remove_event_doc(osc); - } - rptr->event = 0; - - event_dsamp = eff_dsamps + num_dsamps; - if(estimate) { - rptr->event = 1; - rptr->dsamp_ev = event_dsamp; - rptr->dsamp_ev2 = dsamps; - event_dcycs = (event_dsamp * dcycs_per_samp) + 1.0; - add_event_doc(event_dcycs, osc); - } - - GET_ITIMER(end_time1); - - g_cycs_in_est_sound += (end_time1 - start_time1); -} - - -void -remove_sound_event(int osc) -{ - if(g_doc_regs[osc].event) { - g_doc_regs[osc].event = 0; - remove_event_doc(osc); - } -} - - -void -doc_write_ctl_reg(int osc, int val, double dsamps) -{ - Doc_reg *rptr; - double eff_dsamps; - word32 old_halt; - word32 new_halt; - int old_val; - int mode; - - if(osc < 0 || osc >= 0x20) { - halt_printf("doc_write_ctl_reg: osc: %02x, val: %02x\n", - osc, val); - return; - } - - eff_dsamps = dsamps; - rptr = &(g_doc_regs[osc]); - old_val = rptr->ctl; - g_doc_saved_ctl = old_val; - - if(old_val == val) { - return; - } - - DOC_LOG("ctl_reg", osc, dsamps, (old_val << 16) + val); - - mode = (val >> 1) & 3; - - old_halt = (old_val & 1); - new_halt = (val & 1); - - /* bits are: 28: old int bit */ - /* 29: old halt bit */ - /* 30: new int bit */ - /* 31: new halt bit */ - -#if 0 - if(osc == 0x10) { - printf("osc %d new_ctl: %02x, old: %02x\n", osc, val, old_val); - } -#endif - - /* no matter what, remove any pending IRQs on this osc */ - remove_sound_irq(osc, 0); - -#if 0 - if(old_halt) { - printf("doc_write_ctl to osc %d, val: %02x, old: %02x\n", - osc, val, old_val); - } -#endif - - if(new_halt != 0) { - /* make sure sound is stopped */ - remove_sound_event(osc); - if(old_halt == 0) { - /* it was playing, finish it up */ -#if 0 - halt_printf("Aborted osc %d at eff_dsamps: %f, ctl: " - "%02x, oldctl: %02x\n", osc, eff_dsamps, - val, old_val); -#endif - sound_play(eff_dsamps); - } - if(((old_val >> 1) & 3) > 0) { - /* don't clear acc if free-running */ - g_doc_regs[osc].cur_acc = 0; - } - - g_doc_regs[osc].ctl = val; - g_doc_regs[osc].running = 0; - } else { - /* new halt == 0 = make sure sound is running */ - if(old_halt != 0) { - /* start sound */ - DOC_LOG("ctl_sound_play", osc, eff_dsamps, val); - - // OG If the sound_play is executed, it may restart a oscillo we thought was stopped at time, - // hence crashing the start_sound function (cf. game Arrgh!) - //sound_play(eff_dsamps); - g_doc_regs[osc].ctl = val; - - start_sound(osc, eff_dsamps, dsamps); - } else { - /* was running, and something changed */ - doc_printf("osc %d old ctl:%02x new:%02x!\n", - osc, old_val, val); -#if 0 - sound_play(eff_dsamps); -/* HACK: fix this??? */ -#endif - g_doc_regs[osc].ctl = val; - if((old_val ^ val) & val & 0x8) { - /* now has ints on */ - wave_end_estimate(osc, dsamps, dsamps); - } - } - } -} - -void -doc_recalc_sound_parms(int osc, double eff_dcycs, double dsamps) -{ - Doc_reg *rptr; - double dfreq; - double dtmp1; - double dacc, dacc_recip; - word32 res; - word32 sz; - word32 size; - word32 wave_size; - word32 cur_start; - word32 shifted_size; - - g_num_recalc_snd_parms++; - - rptr = &(g_doc_regs[osc]); - - wave_size = rptr->wavesize; - - dfreq = (double)rptr->freq; - - sz = ((wave_size >> 3) & 7) + 8; - size = 1 << sz; - rptr->size_bytes = size; - res = wave_size & 7; - - shifted_size = size << SND_PTR_SHIFT; - - cur_start = (rptr->waveptr << (8 + SND_PTR_SHIFT)) & (-(int)shifted_size); // OG - - dtmp1 = dfreq * (DOC_SCAN_RATE * g_drecip_audio_rate); - dacc = (double)(1 << (20 - (17 - sz + res))); - dacc_recip = (SND_PTR_SHIFT_DBL) / ((double)(1 << 20)); - dtmp1 = dtmp1 * g_drecip_osc_en_plus_2 * dacc * dacc_recip; - - rptr->cur_inc = (int)(dtmp1); - rptr->cur_start = cur_start; - rptr->cur_end = cur_start + shifted_size; - rptr->cur_mask = (shifted_size - 1); - - DOC_LOG("recalc", osc, dsamps, (rptr->waveptr << 16) + wave_size); -} - -int -doc_read_c030(double dcycs) -{ - int num; - - num = g_num_c030_fsamps; - if(num >= MAX_C030_TIMES) { - halt_printf("Too many clicks per vbl: %d\n", num); - return 0; - } - - c030_fsamps[num] = (float)(dcycs * g_dsamps_per_dcyc - - g_last_sound_play_dsamp); - g_num_c030_fsamps = num + 1; - - doc_printf("read c030, num this vbl: %04x\n", num); - - return 0; -} - -int -doc_read_c03c(double dcycs) -{ - return doc_sound_ctl; -} - -int -doc_read_c03d(double dcycs) -{ - double dsamps; - Doc_reg *rptr; - int osc; - int type; - int ret; - - ret = doc_saved_val; - dsamps = dcycs * g_dsamps_per_dcyc; - - if(doc_sound_ctl & 0x40) { - /* Read RAM */ - doc_saved_val = doc_ram[g_c03ef_doc_ptr]; - } else { - /* Read DOC */ - doc_saved_val = 0; - - osc = g_c03ef_doc_ptr & 0x1f; - type = (g_c03ef_doc_ptr >> 5) & 0x7; - rptr = &(g_doc_regs[osc]); - - switch(type) { - case 0x0: /* freq lo */ - doc_saved_val = rptr->freq & 0xff; - break; - case 0x1: /* freq hi */ - doc_saved_val = rptr->freq >> 8; - break; - case 0x2: /* vol */ - doc_saved_val = rptr->vol; - break; - case 0x3: /* data register */ - /* HACK: make this call sound_play sometimes */ - doc_saved_val = rptr->last_samp_val; - break; - case 0x4: /* wave ptr register */ - doc_saved_val = rptr->waveptr; - break; - case 0x5: /* control register */ - doc_saved_val = rptr->ctl; - break; - case 0x6: /* control register */ - doc_saved_val = rptr->wavesize; - break; - case 0x7: /* 0xe0-0xff */ - switch(osc) { - case 0x00: /* 0xe0 */ - doc_saved_val = doc_reg_e0; - doc_printf("Reading doc 0xe0, ret: %02x\n", - doc_saved_val); - - /* Clear IRQ on read of e0, if any irq pend */ - if((doc_reg_e0 & 0x80) == 0) { - remove_sound_irq(doc_reg_e0 >> 1, 1); - } - break; - case 0x01: /* 0xe1 */ - doc_saved_val = (g_doc_num_osc_en - 1) << 1; - break; - case 0x02: /* 0xe2 */ - doc_saved_val = 0x80; -#if 0 - halt_printf("Reading doc 0xe2, ret: %02x\n", - doc_saved_val); -#endif - break; - default: - doc_saved_val = 0; - halt_printf("Reading bad doc_reg[%04x]: %02x\n", - g_c03ef_doc_ptr, doc_saved_val); - } - break; - default: - doc_saved_val = 0; - halt_printf("Reading bad doc_reg[%04x]: %02x\n", - g_c03ef_doc_ptr, doc_saved_val); - } - } - - doc_printf("read c03d, doc_ptr: %04x, ret: %02x, saved: %02x\n", - g_c03ef_doc_ptr, ret, doc_saved_val); - - DOC_LOG("read c03d", -1, dsamps, (g_c03ef_doc_ptr << 16) + - (doc_saved_val << 8) + ret); - - if(doc_sound_ctl & 0x20) { - g_c03ef_doc_ptr = (g_c03ef_doc_ptr + 1) & 0xffff; - } - - - return ret; -} - -void -doc_write_c03c(int val, double dcycs) -{ - int vol; - - vol = val & 0xf; - if(g_doc_vol != vol) { - /* don't bother playing sound..wait till next update */ - /* sound_play(dcycs); */ - - g_doc_vol = vol; - doc_printf("Setting doc vol to 0x%x at %f\n", - vol, dcycs); - } - DOC_LOG("c03c write", -1, dcycs * g_dsamps_per_dcyc, val); - - doc_sound_ctl = val; -} - -void -doc_write_c03d(int val, double dcycs) -{ - double dsamps; - double eff_dsamps; - Doc_reg *rptr; - int osc; - int type; - int ctl; - int tmp; - int i; - - val = val & 0xff; - - dsamps = dcycs * g_dsamps_per_dcyc; - eff_dsamps = dsamps; - doc_printf("write c03d, doc_ptr: %04x, val: %02x\n", - g_c03ef_doc_ptr, val); - - DOC_LOG("write c03d", -1, dsamps, (g_c03ef_doc_ptr << 16) + val); - - if(doc_sound_ctl & 0x40) { - /* RAM */ - doc_ram[g_c03ef_doc_ptr] = val; - } else { - /* DOC */ - osc = g_c03ef_doc_ptr & 0x1f; - type = (g_c03ef_doc_ptr >> 5) & 0x7; - - rptr = &(g_doc_regs[osc]); - ctl = rptr->ctl; -#if 0 - if((ctl & 1) == 0) { - if(type < 2 || type == 4 || type == 6) { - halt_printf("Osc %d is running, old ctl: %02x, " - "but write reg %02x=%02x\n", - osc, ctl, g_c03ef_doc_ptr & 0xff, val); - } - } -#endif - - switch(type) { - case 0x0: /* freq lo */ - if((rptr->freq & 0xff) == (word32)val) { - break; - } - if((ctl & 1) == 0) { - /* play through current status */ - DOC_LOG("flo_sound_play", osc, dsamps, val); - sound_play(dsamps); - } - rptr->freq = (rptr->freq & 0xff00) + val; - doc_recalc_sound_parms(osc, eff_dsamps, dsamps); - break; - case 0x1: /* freq hi */ - if((rptr->freq >> 8) == (word32)val) { - break; - } - if((ctl & 1) == 0) { - /* play through current status */ - DOC_LOG("fhi_sound_play", osc, dsamps, val); - sound_play(dsamps); - } - rptr->freq = (rptr->freq & 0xff) + (val << 8); - doc_recalc_sound_parms(osc, eff_dsamps, dsamps); - break; - case 0x2: /* vol */ - if(rptr->vol == (word32)val) { - break; - } - if((ctl & 1) == 0) { - /* play through current status */ - DOC_LOG("vol_sound_play", osc, dsamps, val); - sound_play(dsamps); -#if 0 - halt_printf("vol_sound_play at %.1f osc:%d " - "val:%d\n", dsamps, osc, val); -#endif - } - rptr->vol = val; - break; - case 0x3: /* data register */ -#if 0 - printf("Writing %02x into doc_data_reg[%02x]!\n", - val, osc); -#endif - break; - case 0x4: /* wave ptr register */ - if(rptr->waveptr == (word32)val) { - break; - } - if((ctl & 1) == 0) { - /* play through current status */ - DOC_LOG("wptr_sound_play", osc, dsamps, val); - sound_play(dsamps); - } - rptr->waveptr = val; - doc_recalc_sound_parms(osc, eff_dsamps, dsamps); - break; - case 0x5: /* control register */ -#if 0 - printf("doc_write ctl osc %d, val: %02x\n", osc, val); -#endif - if(rptr->ctl == (word32)val) { - break; - } - doc_write_ctl_reg(osc, val, dsamps); - break; - case 0x6: /* wavesize register */ - if(rptr->wavesize == (word32)val) { - break; - } - if((ctl & 1) == 0) { - /* play through current status */ - DOC_LOG("wsz_sound_play", osc, dsamps, val); - sound_play(dsamps); - } - rptr->wavesize = val; - doc_recalc_sound_parms(osc, eff_dsamps, dsamps); - break; - case 0x7: /* 0xe0-0xff */ - switch(osc) { - case 0x00: /* 0xe0 */ - doc_printf("writing doc 0xe0 with %02x, " - "was:%02x\n", val, doc_reg_e0); -#if 0 - if(val != doc_reg_e0) { - halt_printf("writing doc 0xe0 with " - "%02x, was:%02x\n", val, - doc_reg_e0); - } -#endif - break; - case 0x01: /* 0xe1 */ - doc_printf("Writing doc 0xe1 with %02x\n", val); - tmp = val & 0x3e; - tmp = (tmp >> 1) + 1; - if(tmp < 1) { - tmp = 1; - } - if(tmp > 32) { - halt_printf("doc 0xe1: %02x!\n", val); - tmp = 32; - } - g_doc_num_osc_en = tmp; - UPDATE_G_DCYCS_PER_DOC_UPDATE(tmp); - - // OG Update any oscs that were running to take care of the new numbers of oscillo - for(i = 0; ictl, rptr->waveptr, rptr->wavesize, rptr->freq, - rptr->vol, rptr->event, rptr->running, - rptr->has_irq_pending, rptr->size_bytes); - printf(" acc:%08x inc:%08x st:%08x end:%08x m:%08x\n", - rptr->cur_acc, rptr->cur_inc, rptr->cur_start, - rptr->cur_end, rptr->cur_mask); - printf(" compl_ds:%f samps_left:%d ev:%f ev2:%f\n", - rptr->complete_dsamp, rptr->samps_left, - rptr->dsamp_ev, rptr->dsamp_ev2); - } - -#if 0 - for(osc = 0; osc < 32; osc++) { - fmax = 0.0; - printf("osc %d has %d samps\n", osc, g_fsamp_num[osc]); - for(i = 0; i < g_fsamp_num[osc]; i++) { - printf("%4d: %f\n", i, g_fsamps[osc][i]); - fmax = MAX(fmax, g_fsamps[osc][i]); - } - printf("osc %d, fmax: %f\n", osc, fmax); - } -#endif -} +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 by GSport contributors + + Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU 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 General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "defc.h" +#include "sound.h" + +extern int Verbose; +extern int g_use_shmem; +extern word32 g_vbl_count; +extern int g_preferred_rate; +extern int g_c03ef_doc_ptr; + +extern double g_last_vbl_dcycs; + +void U_STACK_TRACE(); + +byte doc_ram[0x10000 + 16]; + +word32 doc_sound_ctl = 0; +word32 doc_saved_val = 0; +int g_doc_num_osc_en = 1; +double g_dcycs_per_doc_update = 1.0; +double g_dupd_per_dcyc = 1.0; +double g_drecip_osc_en_plus_2 = 1.0 / (double)(1 + 2); + +int g_doc_saved_ctl = 0; +int g_queued_samps = 0; +int g_queued_nonsamps = 0; +int g_num_osc_interrupting = 0; + +#if defined(HPUX) || defined(__linux__) || defined(WIN_SOUND) || defined(MAC) /* Workaround - gcc in cygwin wasn't defining _WIN32, substituted WIN_SOUND instead */ +int g_audio_enable = -1; +#else +# if defined(OSS) +/* default to off for now */ +int g_audio_enable = 0; +# else +/* Default to sound off */ +int g_audio_enable = 0; +# endif +#endif + +Doc_reg g_doc_regs[32]; + +word32 doc_reg_e0 = 0xff; + +/* local function prototypes */ +void doc_write_ctl_reg(int osc, int val, double dsamps); + + +int g_audio_rate = 0; +double g_daudio_rate = 0.0; +double g_drecip_audio_rate = 0.0; +double g_dsamps_per_dcyc = 0.0; +double g_dcycs_per_samp = 0.0; +float g_fsamps_per_dcyc = 0.0; + +int g_doc_vol = 2; + +#define MAX_C030_TIMES 18000 + +double g_last_sound_play_dsamp = 0.0; + +float c030_fsamps[MAX_C030_TIMES + 1]; +int g_num_c030_fsamps = 0; + +#define DOC_SCAN_RATE (DCYCS_28_MHZ/32.0) + +int g_pipe_fd[2] = { -1, -1 }; +int g_pipe2_fd[2] = { -1, -1 }; +word32 *g_sound_shm_addr = 0; +int g_sound_shm_pos = 0; + +#define LEN_DOC_LOG 128 + +STRUCT(Doc_log) { + char *msg; + int osc; + double dsamps; + double dtmp2; + int etc; + Doc_reg doc_reg; +}; + +Doc_log g_doc_log[LEN_DOC_LOG]; +int g_doc_log_pos = 0; + + +#ifdef DO_DOC_LOG +# define DOC_LOG(a,b,c,d) doc_log_rout(a,b,c,d) +#else +# define DOC_LOG(a,b,c,d) +#endif + +#define UPDATE_G_DCYCS_PER_DOC_UPDATE(osc_en) \ + g_dcycs_per_doc_update = (double)((osc_en + 2) * DCYCS_1_MHZ) / \ + DOC_SCAN_RATE; \ + g_dupd_per_dcyc = 1.0 / g_dcycs_per_doc_update; \ + g_drecip_osc_en_plus_2 = 1.0 / (double)(osc_en + 2); + +#define SND_PTR_SHIFT 14 +#define SND_PTR_SHIFT_DBL ((double)(1 << SND_PTR_SHIFT)) + +void +doc_log_rout(char *msg, int osc, double dsamps, int etc) +{ + int pos; + + pos = g_doc_log_pos; + g_doc_log[pos].msg = msg; + g_doc_log[pos].osc = osc; + g_doc_log[pos].dsamps = dsamps; + g_doc_log[pos].dtmp2 = g_last_sound_play_dsamp; + g_doc_log[pos].etc = etc; + if(osc >= 0 && osc < 32) { + g_doc_log[pos].doc_reg = g_doc_regs[osc]; + } + pos++; + if(pos >= LEN_DOC_LOG) { + pos = 0; + } + + doc_printf("log: %s, osc:%d dsamp:%f, etc:%d\n", msg, osc, dsamps, etc); + + g_doc_log_pos = pos; +} + +extern double g_cur_dcycs; + +void +show_doc_log(void) +{ + FILE *docfile; + Doc_reg *rptr; + double dsamp_start; + int osc, ctl, freq; + int pos; + int i; + + docfile = fopen("doc_log_out", "w"); + if(docfile == 0) { + printf("fopen failed, errno: %d\n", errno); + return; + } + pos = g_doc_log_pos; + fprintf(docfile, "DOC log pos: %d\n", pos); + dsamp_start = g_doc_log[pos].dsamps; + for(i = 0; i < LEN_DOC_LOG; i++) { + rptr = &(g_doc_log[pos].doc_reg); + osc = g_doc_log[pos].osc; + ctl = rptr->ctl; + freq = rptr->freq; + if(osc < 0) { + ctl = 0; + freq = 0; + } + fprintf(docfile, "%03x:%03x: %-11s ds:%11.1f dt2:%10.1f " + "etc:%08x o:%02x c:%02x fq:%04x\n", + i, pos, g_doc_log[pos].msg, + g_doc_log[pos].dsamps - dsamp_start, + g_doc_log[pos].dtmp2, + g_doc_log[pos].etc, osc & 0xff, ctl, freq); + if(osc >= 0) { + fprintf(docfile, " ire:%d,%d,%d ptr4:%08x " + "inc4:%08x comp_ds:%.1f left:%04x, vol:%02x " + "wptr:%02x, wsz:%02x, 4st:%08x, 4end:%08x\n", + rptr->has_irq_pending, rptr->running, + rptr->event, 4*rptr->cur_acc, 4*rptr->cur_inc, + rptr->complete_dsamp - dsamp_start, + rptr->samps_left, rptr->vol, rptr->waveptr, + rptr->wavesize, 4*rptr->cur_start, + 4*rptr->cur_end); + } + pos++; + if(pos >= LEN_DOC_LOG) { + pos = 0; + } + } + + fprintf(docfile, "cur_dcycs: %f\n", g_cur_dcycs); + fprintf(docfile, "dsamps_now: %f\n", + (g_cur_dcycs * g_dsamps_per_dcyc) - dsamp_start); + fprintf(docfile, "g_doc_num_osc_en: %d\n", g_doc_num_osc_en); + fclose(docfile); +} + +void +sound_init() +{ + Doc_reg *rptr; + int i; + + for(i = 0; i < 32; i++) { + rptr = &(g_doc_regs[i]); + rptr->dsamp_ev = 0.0; + rptr->dsamp_ev2 = 0.0; + rptr->complete_dsamp = 0.0; + rptr->samps_left = 0; + rptr->cur_acc = 0; + rptr->cur_inc = 0; + rptr->cur_start = 0; + rptr->cur_end = 0; + rptr->cur_mask = 0; + rptr->size_bytes = 0; + rptr->event = 0; + rptr->running = 0; + rptr->has_irq_pending = 0; + rptr->freq = 0; + rptr->vol = 0; + rptr->waveptr = 0; + rptr->ctl = 1; + rptr->wavesize = 0; + rptr->last_samp_val = 0; + } + + // OG sound globals initialization + g_num_c030_fsamps = 0; + g_sound_shm_pos = 0; + g_queued_samps = 0; + g_queued_nonsamps = 0; + + doc_sound_ctl = 0; + doc_saved_val = 0; + g_doc_num_osc_en = 1; + g_dcycs_per_doc_update = 1.0; + g_dupd_per_dcyc = 1.0; + g_drecip_osc_en_plus_2 = 1.0 / (double)(1 + 2); + + doc_reg_e0 = 0xff; + g_audio_rate = 0; + g_daudio_rate = 0.0; + g_drecip_audio_rate = 0.0; + g_dsamps_per_dcyc = 0.0; + g_dcycs_per_samp = 0.0; + g_fsamps_per_dcyc = 0.0; + + g_doc_vol = 2; + + g_last_sound_play_dsamp = 0.0; + + sound_init_general(); +} + + +void +sound_init_general() +{ +#if !defined(WIN_SOUND) && !defined(__CYGWIN__) && !defined(MAC) && !defined(__OS2__) /* Workaround - gcc in cygwin wasn't defining _WIN32 */ + int pid; + int shmid; + int tmp; + int i; +#endif + word32 *shmaddr; + int size; + int ret; + +#if !defined(WIN_SOUND) && !defined(__CYGWIN__) && !defined(MAC) && !defined(__OS2__) /* Workaround - gcc in cygwin wasn't defining _WIN32 */ + if(!g_use_shmem) { + if(g_audio_enable < 0) { + printf("Defaulting audio off for slow X display\n"); + g_audio_enable = 0; + } + } +#endif + ret = 0; + + if(g_audio_enable == 0) { + set_audio_rate(g_preferred_rate); + return; + } + + size = SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE; + +#if !defined(WIN_SOUND) && !defined(__CYGWIN__) && !defined(MAC) && !defined(__OS2__) /* Workaround - gcc in cygwin wasn't defining _WIN32 */ + shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777); + if(shmid < 0) { + printf("sound_init: shmget ret: %d, errno: %d\n", shmid, + errno); + exit(2); + } + + shmaddr = shmat(shmid, 0, 0); + tmp = (int)PTR2WORD(shmaddr); + if(tmp == -1) { + printf("sound_init: shmat ret: %p, errno: %d\n", shmaddr, + errno); + exit(3); + } + + ret = shmctl(shmid, IPC_RMID, 0); + if(ret < 0) { + printf("sound_init: shmctl ret: %d, errno: %d\n", ret, errno); + exit(4); + } +#else +/* windows and mac */ + shmaddr = (word32*)malloc(size); + memset(shmaddr, 0, size); +#endif + + g_sound_shm_addr = shmaddr; + + fflush(stdout); + +#if !defined(MAC) && !defined(WIN_SOUND) && !defined(__CYGWIN__) && !defined(__OS2__) /* Workaround - gcc in cygwin wasn't defining _WIN32 */ + /* prepare pipe so parent can signal child each other */ + /* pipe[0] = read side, pipe[1] = write end */ + ret = pipe(&g_pipe_fd[0]); + if(ret < 0) { + printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno); + exit(5); + } + ret = pipe(&g_pipe2_fd[0]); + if(ret < 0) { + printf("sound_init: pipe ret: %d, errno: %d\n", ret, errno); + exit(5); + } + + + printf("pipes: pipe_fd = %d, %d pipe2_fd: %d,%d\n", + g_pipe_fd[0], g_pipe_fd[1], g_pipe2_fd[0], g_pipe2_fd[1]); + fflush(stdout); + + pid = fork(); + switch(pid) { + case 0: + /* child */ + /* close stdin and write-side of pipe */ + close(0); + /* Close other fds to make sure X window fd is closed */ + for(i = 3; i < 100; i++) { + if((i != g_pipe_fd[0]) && (i != g_pipe2_fd[1])) { + close(i); + } + } + close(g_pipe_fd[1]); /*make sure write pipe closed*/ + close(g_pipe2_fd[0]); /*make sure read pipe closed*/ + child_sound_loop(g_pipe_fd[0], g_pipe2_fd[1], g_sound_shm_addr); + printf("Child sound loop returned\n"); + exit(0); + case -1: + /* error */ + printf("sound_init: fork ret: -1, errno: %d\n", errno); + exit(6); + default: + /* parent */ + /* close read-side of pipe1, and the write side of pipe2 */ + close(g_pipe_fd[0]); + close(g_pipe2_fd[1]); + doc_printf("Child is pid: %d\n", pid); + } + + parent_sound_get_sample_rate(g_pipe2_fd[0]); +#else +# ifdef MAC + macsnd_init(shmaddr); +# elif defined (WIN_SOUND) /* Workaround - gcc in cygwin wasn't defining _WIN32 */ +/* windows */ + win32snd_init(shmaddr); +# elif defined (__OS2__) +# endif +#endif /* WIN_SOUND */ + +} + +void +parent_sound_get_sample_rate(int read_fd) +{ +#ifndef __OS2__ + word32 tmp; + int ret; + + ret = read(read_fd, (char*)&tmp, 4); + if(ret != 4) { + printf("parent could not get audio sample rate from child, disabling sound.\n"); + printf("ret: %d, fd: %d errno:%d\n", ret, read_fd, errno); + g_audio_enable = 0; + } + close(read_fd); + + set_audio_rate(tmp); +#endif +} + +void +set_audio_rate(int rate) +{ + g_audio_rate = rate; + g_daudio_rate = (rate)*1.0; + g_drecip_audio_rate = 1.0/(rate); + g_dsamps_per_dcyc = ((rate*1.0) / DCYCS_1_MHZ); + g_dcycs_per_samp = (DCYCS_1_MHZ / (rate*1.0)); + g_fsamps_per_dcyc = (float)((rate*1.0) / DCYCS_1_MHZ); +} + +void +sound_reset(double dcycs) +{ + double dsamps; + int i; + + dsamps = dcycs * g_dsamps_per_dcyc; + for(i = 0; i < 32; i++) { + doc_write_ctl_reg(i, g_doc_regs[i].ctl | 1, dsamps); + doc_reg_e0 = 0xff; + if(g_doc_regs[i].has_irq_pending) { + halt_printf("reset: has_irq[%02x] = %d\n", i, + g_doc_regs[i].has_irq_pending); + } + g_doc_regs[i].has_irq_pending = 0; + } + if(g_num_osc_interrupting) { + halt_printf("reset: num_osc_int:%d\n", g_num_osc_interrupting); + } + g_num_osc_interrupting = 0; + +// OG No reason to reset the number of active oscillo on reset : this should only be done on startup. + /* + g_doc_num_osc_en = 1; + UPDATE_G_DCYCS_PER_DOC_UPDATE(1); + */ +} + +void +sound_shutdown() +{ + // OG stop sound and free memory on sound_shutdown + sound_reset(g_cur_dcycs); + +#ifdef WIN_SOUND /* Workaround - gcc in cygwin wasn't defining _WIN32 */ + win32snd_shutdown(); +#elif defined(__OS2__) +#else + if((g_audio_enable != 0) && g_pipe_fd[1] != 0) { + close(g_pipe_fd[1]); + } +#endif + + // OG Free up allocated memory + if (g_sound_shm_addr) + { + free(g_sound_shm_addr); + g_sound_shm_addr = NULL; + } + +} + + + +void +sound_update(double dcycs) +{ + double dsamps; + /* Called every VBL time to update sound status */ + + /* "play" sounds for this vbl */ + + dsamps = dcycs * g_dsamps_per_dcyc; + DOC_LOG("do_snd_pl", -1, dsamps, 0); + sound_play(dsamps); +} + +#define MAX_SND_BUF 65536 + +int g_samp_buf[2*MAX_SND_BUF]; +word32 zero_buf[SOUND_SHM_SAMP_SIZE]; + +double g_doc_dsamps_extra = 0.0; + +float g_fvoices = 0.0; + +word32 g_cycs_in_sound1 = 0; +word32 g_cycs_in_sound2 = 0; +word32 g_cycs_in_sound3 = 0; +word32 g_cycs_in_sound4 = 0; +word32 g_cycs_in_start_sound = 0; +word32 g_cycs_in_est_sound = 0; + +int g_num_snd_plays = 0; +int g_num_doc_events = 0; +int g_num_start_sounds = 0; +int g_num_scan_osc = 0; +int g_num_recalc_snd_parms = 0; + +word32 g_last_c030_vbl_count = 0; +int g_c030_state = 0; + +#define VAL_C030_RANGE (32768) +#define VAL_C030_BASE (-16384) + +int g_sound_file_num = 0; +FILE *g_sound_file_fd = 0; +int g_send_sound_to_file = 0; +int g_send_file_bytes = 0; + +void +open_sound_file() +{ + char name[256]; + FILE *fd; + + sprintf(name, "snd.out.%d", g_sound_file_num); + + fd = fopen(name, "wb+"); + if(fd == 0) { + printf("open_sound_file open errno: %d\n", errno); + exit(1); + } + + g_sound_file_fd = fd; + g_sound_file_num++; + g_send_file_bytes = 0; +} + +void +close_sound_file() +{ + if(g_sound_file_fd != 0) { + fclose(g_sound_file_fd); + } + + g_sound_file_fd = 0; +} + +void +check_for_range(word32 *addr, int num_samps, int offset) +{ + short *shortptr; + int i; + int left; + int right; + int max; + + max = -32768; + + if(num_samps > SOUND_SHM_SAMP_SIZE) { + halt_printf("num_samps: %d > %d!\n", num_samps, + SOUND_SHM_SAMP_SIZE); + } + + for(i = 0; i < num_samps; i++) { + shortptr = (short *)&(addr[i]); + left = shortptr[0]; + right = shortptr[1]; + if((left > 0x3000) || (right > 0x3000)) { + halt_printf("Sample %d of %d at snd_buf: %p is: " + "%d/%d\n", i + offset, num_samps, + &addr[i], left, right); + return; + } + + max = MAX(max, left); + max = MAX(max, right); + } + + printf("check4 max: %d over %d\n", max, num_samps); +} + +void +send_sound_to_file(word32 *addr, int shm_pos, int num_samps) +{ + int size; + int ret; + + if(g_sound_file_fd == 0) { + open_sound_file(); + } + + size = 0; + if((num_samps + shm_pos) > SOUND_SHM_SAMP_SIZE) { + size = SOUND_SHM_SAMP_SIZE - shm_pos; + g_send_file_bytes += (size * 4); + + ret = fwrite(&(addr[shm_pos]), 1, 4*size, g_sound_file_fd); + if(ret != 4*size) { + halt_printf("wrote %d not %d\n", ret, 4*size); + } + + if(g_doc_vol < 3) { + check_for_range(&(addr[shm_pos]), size, 0); + } else { + printf("Not checking %d bytes since vol: %d\n", + 4*size, g_doc_vol); + } + shm_pos = 0; + num_samps -= size; + } + + g_send_file_bytes += (num_samps * 4); + + ret = fwrite(&(addr[shm_pos]), 1, 4*num_samps, g_sound_file_fd); + if(ret != 4*num_samps) { + halt_printf("wrote %d not %d\n", ret, 4*num_samps); + } + + if(g_doc_vol < 3) { + check_for_range(&(addr[shm_pos]), num_samps, size); + } else { + printf("Not checking2 %d bytes since vol: %d\n", + 4*num_samps, g_doc_vol); + } + +} + +void +send_sound(int real_samps, int size) +{ + word32 tmp; + int ret; + + if(g_audio_enable == 0) { + printf("Entered send_sound but audio off!\n"); + exit(2); + } + + if(real_samps) { + tmp = size + 0xa2000000; + } else { + tmp = size + 0xa1000000; + } + DOC_LOG("send_sound", -1, g_last_sound_play_dsamp, + (real_samps << 30) + size); + +#if defined(MAC) || defined(WIN_SOUND) /* Workaround - gcc in cygwin wasn't defining _WIN32 */ + ret = 0; + child_sound_playit(tmp); +#elif defined(__OS2__) + +#else + /* Although this looks like a big/little-endian issue, since the */ + /* child is also reading an int, it just works with no byte swap */ + ret = write(g_pipe_fd[1], &tmp, 4); + if(ret != 4) { + halt_printf("send_sound, wr ret: %d, errno: %d\n", ret, errno); + } +#endif +} + +void +show_c030_state() +{ + show_c030_samps(&(g_samp_buf[0]), 100); +} + +void +show_c030_samps(int *outptr, int num) +{ + int i; + + printf("c030_fsamps[]: %d\n", g_num_c030_fsamps); + + for(i = 0; i < g_num_c030_fsamps+2; i++) { + printf("%3d: %5.3f\n", i, c030_fsamps[i]); + } + + printf("Samples[] = %d\n", num); + + for(i = 0; i < num+2; i++) { + printf("%4d: %d %d\n", i, outptr[0], outptr[1]); + outptr += 2; + } +} + +int g_sound_play_depth = 0; + +void +sound_play(double dsamps) +{ + register word32 start_time1, start_time2, start_time3, start_time4; + register word32 end_time1, end_time2, end_time3; + Doc_reg *rptr; + int *outptr; + int *outptr_start; + word32 *sndptr; + double complete_dsamp; + double cur_dsamp; + double last_dsamp; + double dsamp_now; + double dnum_samps; + int val, val2; + int new_val; + float ftmp; + int imul; + int off; + int num; + float fsampnum; + float next_fsampnum; + int c030_lo_val, c030_hi_val; + float fc030_range; + float fc030_base; + int sampnum; + int next_sampnum; + float fpercent; + int c030_state; + int val0, val1; + word32 cur_acc; + word32 cur_pos; + word32 cur_mask; + word32 cur_inc; + word32 cur_end; + int ctl; + int num_osc_en; + int samps_left; + int samps_to_do; + int samps_played; + int samp_offset; + int snd_buf_init; + int pos; + int num_running; + int num_samps; + int osc; + int done; + int i, j; + + + GET_ITIMER(start_time1); + + g_num_snd_plays++; + if(g_sound_play_depth) { + halt_printf("Nested sound_play!\n"); + } + + g_sound_play_depth++; + + /* calc sample num */ + + last_dsamp = g_last_sound_play_dsamp; + num_samps = (int)(dsamps - g_last_sound_play_dsamp); + dnum_samps = (double)num_samps; + + dsamp_now = last_dsamp + dnum_samps; + + if(num_samps < 1) { + /* just say no */ + g_sound_play_depth--; + return; + } + + DOC_LOG("sound_play", -1, dsamp_now, num_samps); + + if(num_samps > MAX_SND_BUF) { + printf("num_samps: %d, too big!\n", num_samps); + g_sound_play_depth--; + return; + } + + + GET_ITIMER(start_time4); + + outptr_start = &(g_samp_buf[0]); + outptr = outptr_start; + + snd_buf_init = 0; + + samps_played = 0; + + num = g_num_c030_fsamps; + + if(num || ((g_vbl_count - g_last_c030_vbl_count) < 240)) { + + if(num) { + g_last_c030_vbl_count = g_vbl_count; + } + + pos = 0; + outptr = outptr_start; + c030_state = g_c030_state; + + c030_hi_val = ((VAL_C030_BASE + VAL_C030_RANGE)*g_doc_vol) >> 4; + c030_lo_val = (VAL_C030_BASE * g_doc_vol) >> 4; + + fc030_range = (float)(((VAL_C030_RANGE) * g_doc_vol) >> 4); + fc030_base = (float)(((VAL_C030_BASE) * g_doc_vol) >> 4); + + val = c030_lo_val; + if(c030_state) { + val = c030_hi_val; + } + + snd_buf_init++; + + c030_fsamps[num] = (float)(num_samps); + c030_fsamps[num+1] = (float)(num_samps+1); + + ftmp = (float)num_samps; + /* ensure that all samps are in range */ + for(i = num - 1; i >= 0; i--) { + if(c030_fsamps[i] > ftmp) { + c030_fsamps[i] = ftmp; + } + } + + num++; + fsampnum = c030_fsamps[0]; + sampnum = (int)fsampnum; + fpercent = (float)0.0; + i = 0; + + while(i < num) { + next_fsampnum = c030_fsamps[i+1]; + next_sampnum = (int)next_fsampnum; + if(sampnum < 0 || sampnum > num_samps) { + halt_printf("play c030: [%d]:%f is %d, > %d\n", + i, fsampnum, sampnum, num_samps); + break; + } + + /* write in samples to all samps < me */ + new_val = c030_lo_val; + if(c030_state) { + new_val = c030_hi_val; + } + for(j = pos; j < sampnum; j++) { + outptr[0] = new_val; + outptr[1] = new_val; + outptr += 2; + pos++; + } + + /* now, calculate me */ + fpercent = (float)0.0; + if(c030_state) { + fpercent = (fsampnum - (float)sampnum); + } + + c030_state = !c030_state; + + while(next_sampnum == sampnum) { + if(c030_state) { + fpercent += (next_fsampnum - fsampnum); + } + i++; + fsampnum = next_fsampnum; + + next_fsampnum = c030_fsamps[i+1]; + next_sampnum = (int)next_fsampnum; + c030_state = !c030_state; + } + + if(c030_state) { + /* add in fractional time */ + ftmp = (float)(int)(fsampnum + 1.0f); //OG added cast + fpercent += (ftmp - fsampnum); + } + + if((fpercent < (float)0.0) || (fpercent > (float)1.0)) { + halt_printf("fpercent: %d = %f\n", i, fpercent); + show_c030_samps(outptr_start, num_samps); + break; + } + + val = (int)((fpercent * fc030_range) + fc030_base); + outptr[0] = val; + outptr[1] = val; + outptr += 2; + pos++; + i++; + + sampnum = next_sampnum; + fsampnum = next_fsampnum; + } + + samps_played += num_samps; + + /* since we pretended to get one extra sample, we will */ + /* have toggled the speaker one time too many. Fix it */ + g_c030_state = !c030_state; + + if(g_send_sound_to_file) { + show_c030_samps(outptr_start, num_samps); + } + } + + g_num_c030_fsamps = 0; + + GET_ITIMER(start_time2); + + num_running = 0; + + num_osc_en = g_doc_num_osc_en; + + done = 0; + while(!done) { + done = 1; + for(j = 0; j < num_osc_en; j++) { + osc = j; + rptr = &(g_doc_regs[osc]); + complete_dsamp = rptr->complete_dsamp; + samps_left = rptr->samps_left; + cur_acc = rptr->cur_acc; + cur_mask = rptr->cur_mask; + cur_inc = rptr->cur_inc; + cur_end = rptr->cur_end; + if(!rptr->running || cur_inc == 0 || + (complete_dsamp >= dsamp_now)) { + continue; + } + + done = 0; + ctl = rptr->ctl; + + samp_offset = 0; + if(complete_dsamp > last_dsamp) { + samp_offset = (int)(complete_dsamp- last_dsamp); + if(samp_offset > num_samps) { + rptr->complete_dsamp = dsamp_now; + continue; + } + } + outptr = outptr_start + 2 * samp_offset; + if(ctl & 0x10) { + /* other channel */ + outptr += 1; + } + + imul = (rptr->vol * g_doc_vol); + off = imul * 128; + + samps_to_do = MIN(samps_left, num_samps - samp_offset); + if(imul == 0 || samps_to_do == 0) { + /* produce no sound */ + samps_left = samps_left - samps_to_do; + cur_acc += cur_inc * samps_to_do; + rptr->samps_left = samps_left; + rptr->cur_acc = cur_acc; + cur_dsamp = last_dsamp + + (double)(samps_to_do + samp_offset); + DOC_LOG("nosnd", osc, cur_dsamp, samps_to_do); + rptr->complete_dsamp = dsamp_now; + cur_pos = rptr->cur_start+(cur_acc & cur_mask); + if(samps_left <= 0) { + doc_sound_end(osc, 1, cur_dsamp, + dsamp_now); + val = 0; + j--; + } else { + val = doc_ram[cur_pos >> SND_PTR_SHIFT]; + } + rptr->last_samp_val = val; + continue; + } + + if(snd_buf_init == 0) { + memset(outptr_start, 0, + 2*sizeof(outptr_start[0])*num_samps); + snd_buf_init++; + } + + val = 0; + rptr->complete_dsamp = dsamp_now; + cur_pos = rptr->cur_start + (cur_acc & cur_mask); + for(i = 0; i < samps_to_do; i++) { + pos = cur_pos >> SND_PTR_SHIFT; + cur_pos += cur_inc; + cur_acc += cur_inc; + val = doc_ram[pos]; + + val2 = (val * imul - off) >> 4; + if((val == 0) || (cur_pos >= cur_end)) { + cur_dsamp = last_dsamp + + (double)(samp_offset + i + 1); + rptr->cur_acc = cur_acc; + rptr->samps_left = 0; + DOC_LOG("end or 0", osc, cur_dsamp, + (pos << 16) + ((i &0xff) << 8) + + val); + doc_sound_end(osc, val, cur_dsamp, + dsamp_now); + val = 0; + break; + } + + val2 = outptr[0] + val2; + + samps_left--; + *outptr = val2; + outptr += 2; + } + + rptr->last_samp_val = val; + + if(val != 0) { + rptr->cur_acc = cur_acc; + rptr->samps_left = samps_left; + rptr->complete_dsamp = dsamp_now; + } + + samps_played += samps_to_do; + DOC_LOG("splayed", osc, dsamp_now, + (samps_to_do << 16) + (pos & 0xffff)); + } + } + + GET_ITIMER(end_time2); + + g_cycs_in_sound2 += (end_time2 - start_time2); + + g_last_sound_play_dsamp = dsamp_now; + + GET_ITIMER(start_time3); + + outptr = outptr_start; + + pos = g_sound_shm_pos; + sndptr = g_sound_shm_addr; + +#if 0 + printf("samps_left: %d, num_samps: %d\n", samps_left, num_samps); +#endif + + if(g_audio_enable != 0) { + + if(snd_buf_init) { + /* convert sound buf */ + + for(i = 0; i < num_samps; i++) { + val0 = outptr[0]; + val1 = outptr[1]; + val = val0; + if(val0 > 32767) { + val = 32767; + } + if(val0 < -32768) { + val = -32768; + } + + val0 = val; + val = val1; + if(val1 > 32767) { + val = 32767; + } + if(val1 < -32768) { + val = -32768; + } + + + outptr += 2; + +#if defined(__linux__) || defined(OSS) + /* Linux seems to expect little-endian */ + /* samples always, even on PowerPC */ +# ifdef GSPORT_LITTLE_ENDIAN + sndptr[pos] = (val << 16) + (val0 & 0xffff); +# else + sndptr[pos] = ((val & 0xff) << 24) + + ((val & 0xff00) << 8) + + ((val0 & 0xff) << 8) + + ((val0 >> 8) & 0xff); +# endif +#else +# ifdef GSPORT_LITTLE_ENDIAN + sndptr[pos] = (val << 16) + (val0 & 0xffff); +# else + sndptr[pos] = (val0 << 16) + (val & 0xffff); +# endif +#endif + pos++; + if(pos >= SOUND_SHM_SAMP_SIZE) { + pos = 0; + } + } + + if(g_queued_nonsamps) { + /* force out old 0 samps */ + send_sound(0, g_queued_nonsamps); + g_queued_nonsamps = 0; + } + + if(g_send_sound_to_file) { + send_sound_to_file(g_sound_shm_addr, + g_sound_shm_pos, num_samps); + } + + g_queued_samps += num_samps; + } else { + /* move pos */ + pos += num_samps; + while(pos >= SOUND_SHM_SAMP_SIZE) { + pos -= SOUND_SHM_SAMP_SIZE; + } + + if(g_send_sound_to_file) { + send_sound_to_file(zero_buf, g_sound_shm_pos, + num_samps); + } + + if(g_queued_samps) { + /* force out old non-0 samps */ + send_sound(1, g_queued_samps); + g_queued_samps = 0; + } + + g_queued_nonsamps += num_samps; + } + + } + + g_sound_shm_pos = pos; + + + GET_ITIMER(end_time3); + + g_fvoices += ((float)(samps_played) * (float)(g_drecip_audio_rate)); + + if(g_audio_enable != 0) { + if(g_queued_samps >= (g_audio_rate/32)) { + send_sound(1, g_queued_samps); + g_queued_samps = 0; + } + + if(g_queued_nonsamps >= (g_audio_rate/32)) { + send_sound(0, g_queued_nonsamps); + g_queued_nonsamps = 0; + } + } + + GET_ITIMER(end_time1); + + g_cycs_in_sound1 += (end_time1 - start_time1); + g_cycs_in_sound3 += (end_time3 - start_time3); + g_cycs_in_sound4 += (start_time2 - start_time4); + + g_last_sound_play_dsamp = dsamp_now; + + g_sound_play_depth--; +} + + +void +doc_handle_event(int osc, double dcycs) +{ + double dsamps; + + /* handle osc stopping and maybe interrupting */ + + g_num_doc_events++; + + dsamps = dcycs * g_dsamps_per_dcyc; + + DOC_LOG("doc_ev", osc, dcycs, 0); + + g_doc_regs[osc].event = 0; + + sound_play(dsamps); + +} + +void +doc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps) +{ + Doc_reg *rptr, *orptr; + int mode, omode; + int other_osc; + int one_shot_stop; + int ctl; + + /* handle osc stopping and maybe interrupting */ + + if(osc < 0 || osc > 31) { + printf("doc_handle_event: osc: %d!\n", osc); + return; + } + + rptr = &(g_doc_regs[osc]); + ctl = rptr->ctl; + + if(rptr->event) { + remove_event_doc(osc); + } + rptr->event = 0; + rptr->cur_acc = 0; /* reset internal accumulator*/ + + /* check to make sure osc is running */ + if(ctl & 0x01) { + /* Oscillator already stopped. */ + halt_printf("Osc %d interrupt, but it was already stop!\n",osc); +#ifdef HPUX + U_STACK_TRACE(); +#endif + return; + } + + if(ctl & 0x08) { + if(rptr->has_irq_pending == 0) { + add_sound_irq(osc); + } + } + + if(!rptr->running) { + halt_printf("Doc event for osc %d, but ! running\n", osc); + } + + rptr->running = 0; + + mode = (ctl >> 1) & 3; + other_osc = osc ^ 1; + orptr = &(g_doc_regs[other_osc]); + omode = (orptr->ctl >> 1) & 3; + + /* If either this osc or it's partner is in swap mode, treat the */ + /* pair as being in swap mode. This Ensoniq feature pointed out */ + /* by Ian Schmidt */ + if(mode == 0 && can_repeat) { + /* free-running mode with no 0 byte! */ + /* start doing it again */ + + start_sound(osc, eff_dsamps, dsamps); + + return; + } else if((mode == 3) || (omode == 3)) { + /* swap mode (even if we're one_shot and partner is swap)! */ + /* unless we're one-shot and we hit a 0-byte--then */ + /* Olivier Goguel says just stop */ + rptr->ctl |= 1; + one_shot_stop = (mode == 1) && (!can_repeat); + if(!one_shot_stop && !orptr->running && + (orptr->ctl & 0x1)) { + orptr->ctl = orptr->ctl & (~1); + start_sound(other_osc, eff_dsamps, dsamps); + } + return; + } else { + /* stop the oscillator */ + rptr->ctl |= 1; + } + + return; +} + +void +add_sound_irq(int osc) +{ + int num_osc_interrupting; + + if(g_doc_regs[osc].has_irq_pending) { + halt_printf("Adding sound_irq for %02x, but irq_p: %d\n", osc, + g_doc_regs[osc].has_irq_pending); + } + + num_osc_interrupting = g_num_osc_interrupting + 1; + g_doc_regs[osc].has_irq_pending = num_osc_interrupting; + g_num_osc_interrupting = num_osc_interrupting; + + add_irq(IRQ_PENDING_DOC); + if(num_osc_interrupting == 1) { + doc_reg_e0 = 0x00 + (osc << 1); + } + + DOC_LOG("add_irq", osc, g_cur_dcycs * g_dsamps_per_dcyc, 0); +} + +void +remove_sound_irq(int osc, int must) +{ + Doc_reg *rptr; + int num_osc_interrupt; + int has_irq_pending; + int first; + int i; + + doc_printf("remove irq for osc: %d, has_irq: %d\n", + osc, g_doc_regs[osc].has_irq_pending); + + num_osc_interrupt = g_doc_regs[osc].has_irq_pending; + first = 0; + if(num_osc_interrupt) { + g_num_osc_interrupting--; + g_doc_regs[osc].has_irq_pending = 0; + DOC_LOG("rem_irq", osc, g_cur_dcycs * g_dsamps_per_dcyc, 0); + if(g_num_osc_interrupting == 0) { + remove_irq(IRQ_PENDING_DOC); + } + + first = 0x40 | (doc_reg_e0 >> 1); + /* if none found, then def = no ints */ + for(i = 0; i < g_doc_num_osc_en; i++) { + rptr = &(g_doc_regs[i]); + has_irq_pending = rptr->has_irq_pending; + if(has_irq_pending > num_osc_interrupt) { + has_irq_pending--; + rptr->has_irq_pending = has_irq_pending; + } + if(has_irq_pending == 1) { + first = i; + } + } + if(num_osc_interrupt == 1) { + doc_reg_e0 = (first << 1); + } else { +#if 0 + halt_printf("remove_sound_irq[%02x]=%d, first:%d\n", + osc, num_osc_interrupt, first); +#endif + } + } else { +#if 0 + /* make sure no int pending */ + if(doc_reg_e0 != 0xff) { + halt_printf("remove_sound_irq[%02x]=0, but e0: %02x\n", + osc, doc_reg_e0); + } +#endif + if(must) { + halt_printf("REMOVE_sound_irq[%02x]=0, but e0: %02x\n", + osc, doc_reg_e0); + } + } + + if(doc_reg_e0 & 0x80) { + for(i = 0; i < 0x20; i++) { + has_irq_pending = g_doc_regs[i].has_irq_pending; + if(has_irq_pending) { + halt_printf("remove_sound_irq[%02x], but " + "[%02x]=%d!\n", osc,i,has_irq_pending); + printf("num_osc_int: %d, first: %02x\n", + num_osc_interrupt, first); + } + } + } +} + +void +start_sound(int osc, double eff_dsamps, double dsamps) +{ + register word32 start_time1; + register word32 end_time1; + Doc_reg *rptr; + int ctl; + int mode; + word32 sz; + word32 size; + word32 wave_size; + + if(osc < 0 || osc > 31) { + halt_printf("start_sound: osc: %02x!\n", osc); + } + + g_num_start_sounds++; + + rptr = &(g_doc_regs[osc]); + + if(osc >= g_doc_num_osc_en) { + rptr->ctl |= 1; + return; + } + + GET_ITIMER(start_time1); + + ctl = rptr->ctl; + + mode = (ctl >> 1) & 3; + + wave_size = rptr->wavesize; + + sz = ((wave_size >> 3) & 7) + 8; + size = 1 << sz; + + if(size < 0x100) { + halt_printf("size: %08x is too small, sz: %08x!\n", size, sz); + } + + if(rptr->running) { + halt_printf("start_sound osc: %d, already running!\n", osc); + } + + rptr->running = 1; + + rptr->complete_dsamp = eff_dsamps; + + doc_printf("Starting osc %02x, dsamp: %f\n", osc, dsamps); + doc_printf("size: %04x\n", size); + + if((mode == 2) && ((osc & 1) == 0)) { + printf("Sync mode osc %d starting!\n", osc); + /* set_halt(1); */ + + /* see if we should start our odd partner */ + if((rptr[1].ctl & 7) == 5) { + /* odd partner stopped in sync mode--start him */ + rptr[1].ctl &= (~1); + start_sound(osc + 1, eff_dsamps, dsamps); + } else { + printf("Osc %d starting sync, but osc %d ctl: %02x\n", + osc, osc+1, rptr[1].ctl); + } + } + + wave_end_estimate(osc, eff_dsamps, dsamps); + + DOC_LOG("st playing", osc, eff_dsamps, size); +#if 0 + if(rptr->cur_acc != 0) { + halt_printf("Start osc %02x, acc: %08x\n", osc, rptr->cur_acc); + } +#endif + + GET_ITIMER(end_time1); + + g_cycs_in_start_sound += (end_time1 - start_time1); +} + +void +wave_end_estimate(int osc, double eff_dsamps, double dsamps) +{ + register word32 start_time1; + register word32 end_time1; + Doc_reg *rptr; + byte *ptr1; + double event_dsamp; + double event_dcycs; + double dcycs_per_samp; + double dsamps_per_byte; + double num_dsamps; + double dcur_inc; + word32 tmp1; + word32 cur_inc; + word32 save_val; + int save_size; + int pos; + int size; + int estimate; + + GET_ITIMER(start_time1); + + dcycs_per_samp = g_dcycs_per_samp; + + rptr = &(g_doc_regs[osc]); + + cur_inc = rptr->cur_inc; + dcur_inc = (double)cur_inc; + dsamps_per_byte = 0.0; + if(cur_inc) { + dsamps_per_byte = SND_PTR_SHIFT_DBL / (double)dcur_inc; + } + + /* see if there's a zero byte */ + tmp1 = rptr->cur_start + (rptr->cur_acc & rptr->cur_mask); + pos = tmp1 >> SND_PTR_SHIFT; + size = ((rptr->cur_end) >> SND_PTR_SHIFT) - pos; + + ptr1 = &doc_ram[pos]; + + estimate = 0; + if(rptr->ctl & 0x08 || g_doc_regs[osc ^ 1].ctl & 0x08) { + estimate = 1; + } + +#if 0 + estimate = 1; +#endif + if(estimate) { + save_size = size; + save_val = ptr1[size]; + ptr1[size] = 0; + size = strlen((char *)ptr1); + ptr1[save_size] = save_val; + } + + /* calc samples to play */ + num_dsamps = (dsamps_per_byte * (double)size) + 1.0; + + rptr->samps_left = (int)num_dsamps; + + if(rptr->event) { + remove_event_doc(osc); + } + rptr->event = 0; + + event_dsamp = eff_dsamps + num_dsamps; + if(estimate) { + rptr->event = 1; + rptr->dsamp_ev = event_dsamp; + rptr->dsamp_ev2 = dsamps; + event_dcycs = (event_dsamp * dcycs_per_samp) + 1.0; + add_event_doc(event_dcycs, osc); + } + + GET_ITIMER(end_time1); + + g_cycs_in_est_sound += (end_time1 - start_time1); +} + + +void +remove_sound_event(int osc) +{ + if(g_doc_regs[osc].event) { + g_doc_regs[osc].event = 0; + remove_event_doc(osc); + } +} + + +void +doc_write_ctl_reg(int osc, int val, double dsamps) +{ + Doc_reg *rptr; + double eff_dsamps; + word32 old_halt; + word32 new_halt; + int old_val; + int mode; + + if(osc < 0 || osc >= 0x20) { + halt_printf("doc_write_ctl_reg: osc: %02x, val: %02x\n", + osc, val); + return; + } + + eff_dsamps = dsamps; + rptr = &(g_doc_regs[osc]); + old_val = rptr->ctl; + g_doc_saved_ctl = old_val; + + if(old_val == val) { + return; + } + + DOC_LOG("ctl_reg", osc, dsamps, (old_val << 16) + val); + + mode = (val >> 1) & 3; + + old_halt = (old_val & 1); + new_halt = (val & 1); + + /* bits are: 28: old int bit */ + /* 29: old halt bit */ + /* 30: new int bit */ + /* 31: new halt bit */ + +#if 0 + if(osc == 0x10) { + printf("osc %d new_ctl: %02x, old: %02x\n", osc, val, old_val); + } +#endif + + /* no matter what, remove any pending IRQs on this osc */ + remove_sound_irq(osc, 0); + +#if 0 + if(old_halt) { + printf("doc_write_ctl to osc %d, val: %02x, old: %02x\n", + osc, val, old_val); + } +#endif + + if(new_halt != 0) { + /* make sure sound is stopped */ + remove_sound_event(osc); + if(old_halt == 0) { + /* it was playing, finish it up */ +#if 0 + halt_printf("Aborted osc %d at eff_dsamps: %f, ctl: " + "%02x, oldctl: %02x\n", osc, eff_dsamps, + val, old_val); +#endif + sound_play(eff_dsamps); + } + if(((old_val >> 1) & 3) > 0) { + /* don't clear acc if free-running */ + g_doc_regs[osc].cur_acc = 0; + } + + g_doc_regs[osc].ctl = val; + g_doc_regs[osc].running = 0; + } else { + /* new halt == 0 = make sure sound is running */ + if(old_halt != 0) { + /* start sound */ + DOC_LOG("ctl_sound_play", osc, eff_dsamps, val); + + // OG If the sound_play is executed, it may restart a oscillo we thought was stopped at time, + // hence crashing the start_sound function (cf. game Arrgh!) + //sound_play(eff_dsamps); + g_doc_regs[osc].ctl = val; + + start_sound(osc, eff_dsamps, dsamps); + } else { + /* was running, and something changed */ + doc_printf("osc %d old ctl:%02x new:%02x!\n", + osc, old_val, val); +#if 0 + sound_play(eff_dsamps); +/* HACK: fix this??? */ +#endif + g_doc_regs[osc].ctl = val; + if((old_val ^ val) & val & 0x8) { + /* now has ints on */ + wave_end_estimate(osc, dsamps, dsamps); + } + } + } +} + +void +doc_recalc_sound_parms(int osc, double eff_dcycs, double dsamps) +{ + Doc_reg *rptr; + double dfreq; + double dtmp1; + double dacc, dacc_recip; + word32 res; + word32 sz; + word32 size; + word32 wave_size; + word32 cur_start; + word32 shifted_size; + + g_num_recalc_snd_parms++; + + rptr = &(g_doc_regs[osc]); + + wave_size = rptr->wavesize; + + dfreq = (double)rptr->freq; + + sz = ((wave_size >> 3) & 7) + 8; + size = 1 << sz; + rptr->size_bytes = size; + res = wave_size & 7; + + shifted_size = size << SND_PTR_SHIFT; + + cur_start = (rptr->waveptr << (8 + SND_PTR_SHIFT)) & (-(int)shifted_size); // OG + + dtmp1 = dfreq * (DOC_SCAN_RATE * g_drecip_audio_rate); + dacc = (double)(1 << (20 - (17 - sz + res))); + dacc_recip = (SND_PTR_SHIFT_DBL) / ((double)(1 << 20)); + dtmp1 = dtmp1 * g_drecip_osc_en_plus_2 * dacc * dacc_recip; + + rptr->cur_inc = (int)(dtmp1); + rptr->cur_start = cur_start; + rptr->cur_end = cur_start + shifted_size; + rptr->cur_mask = (shifted_size - 1); + + DOC_LOG("recalc", osc, dsamps, (rptr->waveptr << 16) + wave_size); +} + +int +doc_read_c030(double dcycs) +{ + int num; + + num = g_num_c030_fsamps; + if(num >= MAX_C030_TIMES) { + halt_printf("Too many clicks per vbl: %d\n", num); + return 0; + } + + c030_fsamps[num] = (float)(dcycs * g_dsamps_per_dcyc - + g_last_sound_play_dsamp); + g_num_c030_fsamps = num + 1; + + doc_printf("read c030, num this vbl: %04x\n", num); + + return 0; +} + +int +doc_read_c03c(double dcycs) +{ + return doc_sound_ctl; +} + +int +doc_read_c03d(double dcycs) +{ + double dsamps; + Doc_reg *rptr; + int osc; + int type; + int ret; + + ret = doc_saved_val; + dsamps = dcycs * g_dsamps_per_dcyc; + + if(doc_sound_ctl & 0x40) { + /* Read RAM */ + doc_saved_val = doc_ram[g_c03ef_doc_ptr]; + } else { + /* Read DOC */ + doc_saved_val = 0; + + osc = g_c03ef_doc_ptr & 0x1f; + type = (g_c03ef_doc_ptr >> 5) & 0x7; + rptr = &(g_doc_regs[osc]); + + switch(type) { + case 0x0: /* freq lo */ + doc_saved_val = rptr->freq & 0xff; + break; + case 0x1: /* freq hi */ + doc_saved_val = rptr->freq >> 8; + break; + case 0x2: /* vol */ + doc_saved_val = rptr->vol; + break; + case 0x3: /* data register */ + /* HACK: make this call sound_play sometimes */ + doc_saved_val = rptr->last_samp_val; + break; + case 0x4: /* wave ptr register */ + doc_saved_val = rptr->waveptr; + break; + case 0x5: /* control register */ + doc_saved_val = rptr->ctl; + break; + case 0x6: /* control register */ + doc_saved_val = rptr->wavesize; + break; + case 0x7: /* 0xe0-0xff */ + switch(osc) { + case 0x00: /* 0xe0 */ + doc_saved_val = doc_reg_e0; + doc_printf("Reading doc 0xe0, ret: %02x\n", + doc_saved_val); + + /* Clear IRQ on read of e0, if any irq pend */ + if((doc_reg_e0 & 0x80) == 0) { + remove_sound_irq(doc_reg_e0 >> 1, 1); + } + break; + case 0x01: /* 0xe1 */ + doc_saved_val = (g_doc_num_osc_en - 1) << 1; + break; + case 0x02: /* 0xe2 */ + doc_saved_val = 0x80; +#if 0 + halt_printf("Reading doc 0xe2, ret: %02x\n", + doc_saved_val); +#endif + break; + default: + doc_saved_val = 0; + halt_printf("Reading bad doc_reg[%04x]: %02x\n", + g_c03ef_doc_ptr, doc_saved_val); + } + break; + default: + doc_saved_val = 0; + halt_printf("Reading bad doc_reg[%04x]: %02x\n", + g_c03ef_doc_ptr, doc_saved_val); + } + } + + doc_printf("read c03d, doc_ptr: %04x, ret: %02x, saved: %02x\n", + g_c03ef_doc_ptr, ret, doc_saved_val); + + DOC_LOG("read c03d", -1, dsamps, (g_c03ef_doc_ptr << 16) + + (doc_saved_val << 8) + ret); + + if(doc_sound_ctl & 0x20) { + g_c03ef_doc_ptr = (g_c03ef_doc_ptr + 1) & 0xffff; + } + + + return ret; +} + +void +doc_write_c03c(int val, double dcycs) +{ + int vol; + + vol = val & 0xf; + if(g_doc_vol != vol) { + /* don't bother playing sound..wait till next update */ + /* sound_play(dcycs); */ + + g_doc_vol = vol; + doc_printf("Setting doc vol to 0x%x at %f\n", + vol, dcycs); + } + DOC_LOG("c03c write", -1, dcycs * g_dsamps_per_dcyc, val); + + doc_sound_ctl = val; +} + +void +doc_write_c03d(int val, double dcycs) +{ + double dsamps; + double eff_dsamps; + Doc_reg *rptr; + int osc; + int type; + int ctl; + int tmp; + int i; + + val = val & 0xff; + + dsamps = dcycs * g_dsamps_per_dcyc; + eff_dsamps = dsamps; + doc_printf("write c03d, doc_ptr: %04x, val: %02x\n", + g_c03ef_doc_ptr, val); + + DOC_LOG("write c03d", -1, dsamps, (g_c03ef_doc_ptr << 16) + val); + + if(doc_sound_ctl & 0x40) { + /* RAM */ + doc_ram[g_c03ef_doc_ptr] = val; + } else { + /* DOC */ + osc = g_c03ef_doc_ptr & 0x1f; + type = (g_c03ef_doc_ptr >> 5) & 0x7; + + rptr = &(g_doc_regs[osc]); + ctl = rptr->ctl; +#if 0 + if((ctl & 1) == 0) { + if(type < 2 || type == 4 || type == 6) { + halt_printf("Osc %d is running, old ctl: %02x, " + "but write reg %02x=%02x\n", + osc, ctl, g_c03ef_doc_ptr & 0xff, val); + } + } +#endif + + switch(type) { + case 0x0: /* freq lo */ + if((rptr->freq & 0xff) == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("flo_sound_play", osc, dsamps, val); + sound_play(dsamps); + } + rptr->freq = (rptr->freq & 0xff00) + val; + doc_recalc_sound_parms(osc, eff_dsamps, dsamps); + break; + case 0x1: /* freq hi */ + if((rptr->freq >> 8) == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("fhi_sound_play", osc, dsamps, val); + sound_play(dsamps); + } + rptr->freq = (rptr->freq & 0xff) + (val << 8); + doc_recalc_sound_parms(osc, eff_dsamps, dsamps); + break; + case 0x2: /* vol */ + if(rptr->vol == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("vol_sound_play", osc, dsamps, val); + sound_play(dsamps); +#if 0 + halt_printf("vol_sound_play at %.1f osc:%d " + "val:%d\n", dsamps, osc, val); +#endif + } + rptr->vol = val; + break; + case 0x3: /* data register */ +#if 0 + printf("Writing %02x into doc_data_reg[%02x]!\n", + val, osc); +#endif + break; + case 0x4: /* wave ptr register */ + if(rptr->waveptr == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("wptr_sound_play", osc, dsamps, val); + sound_play(dsamps); + } + rptr->waveptr = val; + doc_recalc_sound_parms(osc, eff_dsamps, dsamps); + break; + case 0x5: /* control register */ +#if 0 + printf("doc_write ctl osc %d, val: %02x\n", osc, val); +#endif + if(rptr->ctl == (word32)val) { + break; + } + doc_write_ctl_reg(osc, val, dsamps); + break; + case 0x6: /* wavesize register */ + if(rptr->wavesize == (word32)val) { + break; + } + if((ctl & 1) == 0) { + /* play through current status */ + DOC_LOG("wsz_sound_play", osc, dsamps, val); + sound_play(dsamps); + } + rptr->wavesize = val; + doc_recalc_sound_parms(osc, eff_dsamps, dsamps); + break; + case 0x7: /* 0xe0-0xff */ + switch(osc) { + case 0x00: /* 0xe0 */ + doc_printf("writing doc 0xe0 with %02x, " + "was:%02x\n", val, doc_reg_e0); +#if 0 + if(val != doc_reg_e0) { + halt_printf("writing doc 0xe0 with " + "%02x, was:%02x\n", val, + doc_reg_e0); + } +#endif + break; + case 0x01: /* 0xe1 */ + doc_printf("Writing doc 0xe1 with %02x\n", val); + tmp = val & 0x3e; + tmp = (tmp >> 1) + 1; + if(tmp < 1) { + tmp = 1; + } + if(tmp > 32) { + halt_printf("doc 0xe1: %02x!\n", val); + tmp = 32; + } + g_doc_num_osc_en = tmp; + UPDATE_G_DCYCS_PER_DOC_UPDATE(tmp); + + // OG Update any oscs that were running to take care of the new numbers of oscillo + for(i = 0; ictl, rptr->waveptr, rptr->wavesize, rptr->freq, + rptr->vol, rptr->event, rptr->running, + rptr->has_irq_pending, rptr->size_bytes); + printf(" acc:%08x inc:%08x st:%08x end:%08x m:%08x\n", + rptr->cur_acc, rptr->cur_inc, rptr->cur_start, + rptr->cur_end, rptr->cur_mask); + printf(" compl_ds:%f samps_left:%d ev:%f ev2:%f\n", + rptr->complete_dsamp, rptr->samps_left, + rptr->dsamp_ev, rptr->dsamp_ev2); + } + +#if 0 + for(osc = 0; osc < 32; osc++) { + fmax = 0.0; + printf("osc %d has %d samps\n", osc, g_fsamp_num[osc]); + for(i = 0; i < g_fsamp_num[osc]; i++) { + printf("%4d: %f\n", i, g_fsamps[osc][i]); + fmax = MAX(fmax, g_fsamps[osc][i]); + } + printf("osc %d, fmax: %f\n", osc, fmax); + } +#endif +} diff --git a/src/sound_driver.c b/src/sound_driver.c index c8a2f37..5c8e6e2 100644 --- a/src/sound_driver.c +++ b/src/sound_driver.c @@ -1,440 +1,440 @@ -/* - GSport - an Apple //gs Emulator - Copyright (C) 2010 by GSport contributors - - Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU 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 General Public License - for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "defc.h" -#include "sound.h" - -#ifdef HPUX -# include -#endif - -#if defined(__linux__) || defined(OSS) -# include -#endif - -#ifndef _WIN32 -# include -# include -#endif -#ifndef UNDER_CE -#include -#endif - - -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, (char*)&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 GSPORT_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 +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 by GSport contributors + + Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU 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 General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "defc.h" +#include "sound.h" + +#ifdef HPUX +# include +#endif + +#if defined(__linux__) || defined(OSS) +# include +#endif + +#ifndef WIN_SOUND /* Workaround - gcc in cygwin wasn't defining _WIN32 */ +# include +# include +#endif +#ifndef UNDER_CE +#include +#endif + + +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_initWIN_SOUND(); +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 WIN_SOUND + 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 WIN_SOUND + 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, (char*)&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 GSPORT_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 diff --git a/src/vars_win32 b/src/vars_win32 index 5ce9b4a..d3e2d08 100644 --- a/src/vars_win32 +++ b/src/vars_win32 @@ -1,7 +1,7 @@ TARGET = gsport.exe TFEOBJ = tfe/tfe.o arch/win32/tfearch.o tfe/tfesupp.o OBJECTS = $(OBJECTS1) $(TFEOBJ) scc_windriver.o win32snd_driver.o win_console.o win_generic.o -CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE +CCOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE -DWIN_SOUND CPPOPTS = -O2 -DGSPORT_LITTLE_ENDIAN -DHAVE_TFE SUFFIX = ".exe" NAME = gsport