/* * pokeysnd.c - POKEY sound chip emulation, v2.4 * * Copyright (C) 1996-1998 Ron Fries * Copyright (C) 1998-2014 Atari800 development team (see DOC/CREDITS) * * This file is part of the Atari800 emulator project which emulates * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. * * Atari800 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. * * Atari800 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 Atari800; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef skip #include "config.h" #include #include #ifdef ASAP /* external project, see http://asap.sf.net */ #include "asap_internal.h" #else #include "atari.h" #ifndef __PLUS #include "sndsave.h" #else #include "sound_win.h" #endif #endif #include "mzpokeysnd.h" #include "pokeysnd.h" #if defined(PBI_XLD) || defined (VOICEBOX) #include "votraxsnd.h" #endif #include "antic.h" #include "gtia.h" #include "util.h" #endif #include "pokeysnd.h" #if defined(PBI_XLD) || defined (VOICEBOX) #include "votraxsnd.h" #endif #include "antic.h" #include "gtia.h" #ifdef WORDS_UNALIGNED_OK # define READ_U32(x) (*(ULONG *) (x)) # define WRITE_U32(x, d) (*(ULONG *) (x) = (d)) #else # ifdef WORDS_BIGENDIAN # define READ_U32(x) (((*(unsigned char *)(x)) << 24) | ((*((unsigned char *)(x) + 1)) << 16) | \ ((*((unsigned char *)(x) + 2)) << 8) | ((*((unsigned char *)(x) + 3)))) # define WRITE_U32(x, d) \ { \ ULONG i = d; \ (*(unsigned char *) (x)) = (((i) >> 24) & 255); \ (*((unsigned char *) (x) + 1)) = (((i) >> 16) & 255); \ (*((unsigned char *) (x) + 2)) = (((i) >> 8) & 255); \ (*((unsigned char *) (x) + 3)) = ((i) & 255); \ } # else # define READ_U32(x) ((*(unsigned char *) (x)) | ((*((unsigned char *) (x) + 1)) << 8) | \ ((*((unsigned char *) (x) + 2)) << 16) | ((*((unsigned char *) (x) + 3)) << 24)) # define WRITE_U32(x, d) \ { \ ULONG i = d; \ (*(unsigned char *)(x)) = ((i) & 255); \ (*((unsigned char *)(x) + 1)) = (((i) >> 8) & 255); \ (*((unsigned char *)(x) + 2)) = (((i) >> 16) & 255); \ (*((unsigned char *)(x) + 3)) = (((i) >> 24) & 255); \ } # endif #endif /* GLOBAL VARIABLE DEFINITIONS */ /* number of pokey chips currently emulated */ static UBYTE Num_pokeys; static UBYTE pokeysnd_AUDV[4 * POKEY_MAXPOKEYS]; /* Channel volume - derived */ static UBYTE Outbit[4 * POKEY_MAXPOKEYS]; /* current state of the output (high or low) */ static UBYTE Outvol[4 * POKEY_MAXPOKEYS]; /* last output volume for each channel */ /* Initialize the bit patterns for the polynomials. */ /* The 4bit and 5bit patterns are the identical ones used in the pokey chip. */ /* Though the patterns could be packed with 8 bits per byte, using only a */ /* single bit per byte keeps the math simple, which is important for */ /* efficient processing. */ static const UBYTE bit4[POKEY_POLY4_SIZE] = #ifndef POKEY23_POLY {1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}; /* new table invented by Perry */ #else {1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0}; /* original POKEY 2.3 table */ #endif static const UBYTE bit5[POKEY_POLY5_SIZE] = #ifndef POKEY23_POLY {1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0}; #else {0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1}; #endif static ULONG P4 = 0, /* Global position pointer for the 4-bit POLY array */ P5 = 0, /* Global position pointer for the 5-bit POLY array */ P9 = 0, /* Global position pointer for the 9-bit POLY array */ P17 = 0; /* Global position pointer for the 17-bit POLY array */ static ULONG Div_n_cnt[4 * POKEY_MAXPOKEYS], /* Divide by n counter. one for each channel */ Div_n_max[4 * POKEY_MAXPOKEYS]; /* Divide by n maximum, one for each channel */ static ULONG Samp_n_max, /* Sample max. For accuracy, it is *256 */ Samp_n_cnt[2]; /* Sample cnt. */ #ifdef INTERPOLATE_SOUND #ifdef CLIP_SOUND static SWORD last_val = 0; /* last output value */ #else static UWORD last_val = 0; #endif #ifdef STEREO_SOUND #ifdef CLIP_SOUND static SWORD last_val2 = 0; /* last output value */ #else static UWORD last_val2 = 0; #endif #endif #endif /* Volume only emulations declarations */ #ifdef VOL_ONLY_SOUND int POKEYSND_sampbuf_val[POKEYSND_SAMPBUF_MAX]; /* volume values */ int POKEYSND_sampbuf_cnt[POKEYSND_SAMPBUF_MAX]; /* relative start time */ int POKEYSND_sampbuf_ptr = 0; /* pointer to sampbuf */ int POKEYSND_sampbuf_rptr = 0; /* pointer to read from sampbuf */ int POKEYSND_sampbuf_last = 0; /* last absolute time */ int POKEYSND_sampbuf_AUDV[4 * POKEY_MAXPOKEYS]; /* prev. channel volume */ int POKEYSND_sampbuf_lastval = 0; /* last volume */ int POKEYSND_sampout; /* last out volume */ int POKEYSND_samp_freq; int POKEYSND_samp_consol_val = 0; /* actual value of console sound */ #ifdef STEREO_SOUND static int sampbuf_val2[POKEYSND_SAMPBUF_MAX]; /* volume values */ static int sampbuf_cnt2[POKEYSND_SAMPBUF_MAX]; /* relative start time */ static int sampbuf_ptr2 = 0; /* pointer to sampbuf */ static int sampbuf_rptr2 = 0; /* pointer to read from sampbuf */ static int sampbuf_last2 = 0; /* last absolute time */ static int sampbuf_lastval2 = 0; /* last volume */ static int sampout2; /* last out volume */ #endif #endif /* VOL_ONLY_SOUND */ static ULONG snd_freq17 = POKEYSND_FREQ_17_EXACT; int POKEYSND_playback_freq = 44100; UBYTE POKEYSND_num_pokeys = 1; int POKEYSND_snd_flags = 0; static int mz_quality = 0; /* default quality for mzpokeysnd */ #ifdef __PLUS int mz_clear_regs = 0; #endif int POKEYSND_enable_new_pokey = TRUE; int POKEYSND_bienias_fix = TRUE; /* when TRUE, high frequencies get emulated: better sound but slower */ #if defined(__PLUS) && !defined(_WX_) #define BIENIAS_FIX (g_Sound.nBieniasFix) #else #define BIENIAS_FIX POKEYSND_bienias_fix #endif #ifndef ASAP int POKEYSND_stereo_enabled = FALSE; #endif int POKEYSND_volume = 0x100; /* multiple sound engine interface */ static void pokeysnd_process_8(void *sndbuffer, int sndn); static void pokeysnd_process_16(void *sndbuffer, int sndn); static void null_pokey_process(void *sndbuffer, int sndn) {} void (*POKEYSND_Process_ptr)(void *sndbuffer, int sndn) = null_pokey_process; static void Update_pokey_sound_rf(UWORD, UBYTE, UBYTE, UBYTE); static void null_pokey_sound(UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) {} void (*POKEYSND_Update_ptr) (UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) = null_pokey_sound; #ifdef SERIO_SOUND static void Update_serio_sound_rf(int out, UBYTE data); static void null_serio_sound(int out, UBYTE data) {} void (*POKEYSND_UpdateSerio)(int out, UBYTE data) = null_serio_sound; int POKEYSND_serio_sound_enabled = 1; #endif #ifdef CONSOLE_SOUND static void Update_consol_sound_rf(int set); static void null_consol_sound(int set) {} void (*POKEYSND_UpdateConsol_ptr)(int set) = null_consol_sound; int POKEYSND_console_sound_enabled = 1; #endif #ifdef VOL_ONLY_SOUND static void Update_vol_only_sound_rf(void); static void null_vol_only_sound(void) {} void (*POKEYSND_UpdateVolOnly)(void) = null_vol_only_sound; #endif #ifdef SYNCHRONIZED_SOUND UBYTE *POKEYSND_process_buffer = NULL; unsigned int POKEYSND_process_buffer_length; unsigned int POKEYSND_process_buffer_fill; static unsigned int prev_update_tick; static void Generate_sync_rf(unsigned int num_ticks); static void null_generate_sync(unsigned int num_ticks) {} void (*POKEYSND_GenerateSync)(unsigned int num_ticks) = null_generate_sync; static double ticks_per_sample; static double samp_pos; static int speaker; static int const CONSOLE_VOL = 32; #endif /* SYNCHRONIZED_SOUND */ /*****************************************************************************/ /* In my routines, I treat the sample output as another divide by N counter */ /* For better accuracy, the Samp_n_cnt has a fixed binary decimal point */ /* which has 8 binary digits to the right of the decimal point. I use a two */ /* byte array to give me a minimum of 40 bits, and then use pointer math to */ /* reference either the 24.8 whole/fraction combination or the 32-bit whole */ /* only number. This is mainly used to keep the math simple for */ /* optimization. See below: */ /* */ /* Representation on little-endian machines: */ /* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx */ /* fraction whole whole whole whole unused unused unused */ /* */ /* Samp_n_cnt[0] gives me a 32-bit int 24 whole bits with 8 fractional bits, */ /* while (ULONG *)((UBYTE *)(&Samp_n_cnt[0])+1) gives me the 32-bit whole */ /* number only. */ /* */ /* Representation on big-endian machines: */ /* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx.xxxxxxxx */ /* unused unused unused whole whole whole whole fraction */ /* */ /* Samp_n_cnt[1] gives me a 32-bit int 24 whole bits with 8 fractional bits, */ /* while (ULONG *)((UBYTE *)(&Samp_n_cnt[0])+3) gives me the 32-bit whole */ /* number only. */ /*****************************************************************************/ /*****************************************************************************/ /* Module: pokeysnd_init_rf() */ /* Purpose: to handle the power-up initialization functions */ /* these functions should only be executed on a cold-restart */ /* */ /* Author: Ron Fries */ /* Date: January 1, 1997 */ /* */ /* Inputs: freq17 - the value for the '1.79MHz' Pokey audio clock */ /* playback_freq - the playback frequency in samples per second */ /* num_pokeys - specifies the number of pokey chips to be emulated */ /* */ /* Outputs: Adjusts local globals - no return value */ /* */ /*****************************************************************************/ static int pokeysnd_init_rf(ULONG freq17, int playback_freq, UBYTE num_pokeys, int flags); #ifdef VOL_ONLY_SOUND /* Initialise variables related to volume-only sound. */ static void init_vol_only(void) { POKEYSND_sampbuf_rptr = POKEYSND_sampbuf_ptr; POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; POKEYSND_sampbuf_lastval = 0; POKEYSND_samp_consol_val = 0; #ifdef STEREO_SOUND sampbuf_rptr2 = sampbuf_ptr2; sampbuf_last2 = ANTIC_CPU_CLOCK; sampbuf_lastval2 = 0; #endif /* STEREO_SOUND */ } #endif /* VOL_ONLY_SOUND */ int POKEYSND_DoInit(void) { #if SKIP SndSave_CloseSoundFile(); #endif #ifdef VOL_ONLY_SOUND init_vol_only(); #endif /* VOL_ONLY_SOUND */ #if SKIP if (POKEYSND_enable_new_pokey) return MZPOKEYSND_Init(snd_freq17, POKEYSND_playback_freq, POKEYSND_num_pokeys, POKEYSND_snd_flags, mz_quality #ifdef __PLUS , mz_clear_regs #endif ); else #endif return pokeysnd_init_rf(snd_freq17, POKEYSND_playback_freq, POKEYSND_num_pokeys, POKEYSND_snd_flags); } int POKEYSND_Init(ULONG freq17, int playback_freq, UBYTE num_pokeys, int flags #ifdef __PLUS , int clear_regs #endif ) { snd_freq17 = freq17; POKEYSND_playback_freq = playback_freq; POKEYSND_num_pokeys = num_pokeys; POKEYSND_snd_flags = flags; #ifdef __PLUS mz_clear_regs = clear_regs; #endif #ifdef SYNCHRONIZED_SOUND { /* A single call to Atari800_Frame may emulate a bit more CPU ticks than the exact number of ticks per frame (Atari800_tv_mode*114). So we add a few ticks to buffer size just to be safe. */ unsigned int const surplus_ticks = 10; double samples_per_frame = (double)POKEYSND_playback_freq/(Atari800_tv_mode == Atari800_TV_PAL ? Atari800_FPS_PAL : Atari800_FPS_NTSC); unsigned int ticks_per_frame = Atari800_tv_mode*114; unsigned int max_ticks_per_frame = ticks_per_frame + surplus_ticks; double ticks_per_sample = (double)ticks_per_frame / samples_per_frame; POKEYSND_process_buffer_length = POKEYSND_num_pokeys * (unsigned int)ceil((double)max_ticks_per_frame / ticks_per_sample) * ((POKEYSND_snd_flags & POKEYSND_BIT16) ? 2:1); free(POKEYSND_process_buffer); POKEYSND_process_buffer = (UBYTE *)Util_malloc(POKEYSND_process_buffer_length); POKEYSND_process_buffer_fill = 0; prev_update_tick = ANTIC_CPU_CLOCK; } #endif /* SYNCHRONIZED_SOUND */ #if defined(PBI_XLD) || defined (VOICEBOX) VOTRAXSND_Init(playback_freq, num_pokeys, (flags & POKEYSND_BIT16)); #endif return POKEYSND_DoInit(); } void POKEYSND_SetMzQuality(int quality) /* specially for win32, perhaps not needed? */ { mz_quality = quality; } void POKEYSND_Process(void *sndbuffer, int sndn) { POKEYSND_Process_ptr(sndbuffer, sndn); #if defined(PBI_XLD) || defined (VOICEBOX) VOTRAXSND_Process(sndbuffer,sndn); #endif #if !defined(__PLUS) && !defined(ASAP) #if SKIP SndSave_WriteToSoundFile((const unsigned char *)sndbuffer, sndn); #endif #endif } #ifdef SYNCHRONIZED_SOUND static void Update_synchronized_sound(void) { POKEYSND_GenerateSync(ANTIC_CPU_CLOCK - prev_update_tick); prev_update_tick = ANTIC_CPU_CLOCK; } int POKEYSND_UpdateProcessBuffer(void) { int sndn; Update_synchronized_sound(); sndn = POKEYSND_process_buffer_fill / ((POKEYSND_snd_flags & POKEYSND_BIT16) ? 2 : 1); POKEYSND_process_buffer_fill = 0; #if defined(PBI_XLD) || defined (VOICEBOX) VOTRAXSND_Process(POKEYSND_process_buffer, sndn); #endif #if !defined(__PLUS) && !defined(ASAP) SndSave_WriteToSoundFile((const unsigned char *)POKEYSND_process_buffer, sndn); #endif return sndn; } #endif /* SYNCHRONIZED_SOUND */ #ifdef SYNCHRONIZED_SOUND static void init_syncsound(void) { double samples_per_frame = (double)POKEYSND_playback_freq/(Atari800_tv_mode == Atari800_TV_PAL ? Atari800_FPS_PAL : Atari800_FPS_NTSC); unsigned int ticks_per_frame = Atari800_tv_mode*114; ticks_per_sample = (double)ticks_per_frame / samples_per_frame; samp_pos = 0.0; POKEYSND_GenerateSync = Generate_sync_rf; speaker = 0; } #endif /* SYNCHRONIZED_SOUND */ static int pokeysnd_init_rf(ULONG freq17, int playback_freq, UBYTE num_pokeys, int flags) { UBYTE chan; POKEYSND_Update_ptr = Update_pokey_sound_rf; #ifdef SERIO_SOUND POKEYSND_UpdateSerio = Update_serio_sound_rf; #endif #ifdef CONSOLE_SOUND POKEYSND_UpdateConsol_ptr = Update_consol_sound_rf; #endif #ifdef VOL_ONLY_SOUND POKEYSND_UpdateVolOnly = Update_vol_only_sound_rf; #endif POKEYSND_Process_ptr = (flags & POKEYSND_BIT16) ? pokeysnd_process_16 : pokeysnd_process_8; #ifdef VOL_ONLY_SOUND POKEYSND_samp_freq = playback_freq; #endif /* start all of the polynomial counters at zero */ P4 = 0; P5 = 0; P9 = 0; P17 = 0; /* calculate the sample 'divide by N' value based on the playback freq. */ Samp_n_max = ((ULONG) freq17 << 8) / playback_freq; Samp_n_cnt[0] = 0; /* initialize all bits of the sample */ Samp_n_cnt[1] = 0; /* 'divide by N' counter */ for (chan = 0; chan < (POKEY_MAXPOKEYS * 4); chan++) { Outvol[chan] = 0; Outbit[chan] = 0; Div_n_cnt[chan] = 0; Div_n_max[chan] = 0x7fffffffL; pokeysnd_AUDV[chan] = 0; #ifdef VOL_ONLY_SOUND POKEYSND_sampbuf_AUDV[chan] = 0; #endif } /* set the number of pokey chips currently emulated */ Num_pokeys = num_pokeys; #ifdef SYNCHRONIZED_SOUND init_syncsound(); #endif return 0; /* OK */ } /*****************************************************************************/ /* Module: Update_pokey_sound_rf() */ /* Purpose: To process the latest control values stored in the AUDF, AUDC, */ /* and AUDCTL registers. It pre-calculates as much information as */ /* possible for better performance. This routine has not been */ /* optimized. */ /* */ /* Author: Ron Fries */ /* Date: January 1, 1997 */ /* */ /* Inputs: addr - the address of the parameter to be changed */ /* val - the new value to be placed in the specified address */ /* gain - specified as an 8-bit fixed point number - use 1 for no */ /* amplification (output is multiplied by gain) */ /* */ /* Outputs: Adjusts local globals - no return value */ /* */ /*****************************************************************************/ void POKEYSND_Update(UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) { #ifdef SYNCHRONIZED_SOUND Update_synchronized_sound(); #endif /* SYNCHRONIZED_SOUND */ POKEYSND_Update_ptr(addr, val, chip, gain); } static void Update_pokey_sound_rf(UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) { ULONG new_val = 0; UBYTE chan; UBYTE chan_mask; UBYTE chip_offs; /* calculate the chip_offs for the channel arrays */ chip_offs = chip << 2; /* determine which address was changed */ switch (addr & 0x0f) { case POKEY_OFFSET_AUDF1: /* POKEY_AUDF[POKEY_CHAN1 + chip_offs] = val; */ chan_mask = 1 << POKEY_CHAN1; if (POKEY_AUDCTL[chip] & POKEY_CH1_CH2) /* if ch 1&2 tied together */ chan_mask |= 1 << POKEY_CHAN2; /* then also change on ch2 */ break; case POKEY_OFFSET_AUDC1: /* POKEY_AUDC[POKEY_CHAN1 + chip_offs] = val; */ pokeysnd_AUDV[POKEY_CHAN1 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; chan_mask = 1 << POKEY_CHAN1; break; case POKEY_OFFSET_AUDF2: /* POKEY_AUDF[POKEY_CHAN2 + chip_offs] = val; */ chan_mask = 1 << POKEY_CHAN2; break; case POKEY_OFFSET_AUDC2: /* POKEY_AUDC[POKEY_CHAN2 + chip_offs] = val; */ pokeysnd_AUDV[POKEY_CHAN2 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; chan_mask = 1 << POKEY_CHAN2; break; case POKEY_OFFSET_AUDF3: /* POKEY_AUDF[POKEY_CHAN3 + chip_offs] = val; */ chan_mask = 1 << POKEY_CHAN3; if (POKEY_AUDCTL[chip] & POKEY_CH3_CH4) /* if ch 3&4 tied together */ chan_mask |= 1 << POKEY_CHAN4; /* then also change on ch4 */ break; case POKEY_OFFSET_AUDC3: /* POKEY_AUDC[POKEY_CHAN3 + chip_offs] = val; */ pokeysnd_AUDV[POKEY_CHAN3 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; chan_mask = 1 << POKEY_CHAN3; break; case POKEY_OFFSET_AUDF4: /* POKEY_AUDF[POKEY_CHAN4 + chip_offs] = val; */ chan_mask = 1 << POKEY_CHAN4; break; case POKEY_OFFSET_AUDC4: /* POKEY_AUDC[POKEY_CHAN4 + chip_offs] = val; */ pokeysnd_AUDV[POKEY_CHAN4 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; chan_mask = 1 << POKEY_CHAN4; break; case POKEY_OFFSET_AUDCTL: /* POKEY_AUDCTL[chip] = val; */ chan_mask = 15; /* all channels */ break; default: chan_mask = 0; break; } /************************************************************/ /* As defined in the manual, the exact Div_n_cnt values are */ /* different depending on the frequency and resolution: */ /* 64 kHz or 15 kHz - AUDF + 1 */ /* 1 MHz, 8-bit - AUDF + 4 */ /* 1 MHz, 16-bit - POKEY_AUDF[POKEY_CHAN1]+256*POKEY_AUDF[POKEY_CHAN2] + 7 */ /************************************************************/ /* only reset the channels that have changed */ if (chan_mask & (1 << POKEY_CHAN1)) { /* process channel 1 frequency */ if (POKEY_AUDCTL[chip] & POKEY_CH1_179) new_val = POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 4; else new_val = (POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 1) * POKEY_Base_mult[chip]; if (new_val != Div_n_max[POKEY_CHAN1 + chip_offs]) { Div_n_max[POKEY_CHAN1 + chip_offs] = new_val; if (Div_n_cnt[POKEY_CHAN1 + chip_offs] > new_val) { Div_n_cnt[POKEY_CHAN1 + chip_offs] = new_val; } } } if (chan_mask & (1 << POKEY_CHAN2)) { /* process channel 2 frequency */ if (POKEY_AUDCTL[chip] & POKEY_CH1_CH2) { if (POKEY_AUDCTL[chip] & POKEY_CH1_179) new_val = POKEY_AUDF[POKEY_CHAN2 + chip_offs] * 256 + POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 7; else new_val = (POKEY_AUDF[POKEY_CHAN2 + chip_offs] * 256 + POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 1) * POKEY_Base_mult[chip]; } else new_val = (POKEY_AUDF[POKEY_CHAN2 + chip_offs] + 1) * POKEY_Base_mult[chip]; if (new_val != Div_n_max[POKEY_CHAN2 + chip_offs]) { Div_n_max[POKEY_CHAN2 + chip_offs] = new_val; if (Div_n_cnt[POKEY_CHAN2 + chip_offs] > new_val) { Div_n_cnt[POKEY_CHAN2 + chip_offs] = new_val; } } } if (chan_mask & (1 << POKEY_CHAN3)) { /* process channel 3 frequency */ if (POKEY_AUDCTL[chip] & POKEY_CH3_179) new_val = POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 4; else new_val = (POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 1) * POKEY_Base_mult[chip]; if (new_val != Div_n_max[POKEY_CHAN3 + chip_offs]) { Div_n_max[POKEY_CHAN3 + chip_offs] = new_val; if (Div_n_cnt[POKEY_CHAN3 + chip_offs] > new_val) { Div_n_cnt[POKEY_CHAN3 + chip_offs] = new_val; } } } if (chan_mask & (1 << POKEY_CHAN4)) { /* process channel 4 frequency */ if (POKEY_AUDCTL[chip] & POKEY_CH3_CH4) { if (POKEY_AUDCTL[chip] & POKEY_CH3_179) new_val = POKEY_AUDF[POKEY_CHAN4 + chip_offs] * 256 + POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 7; else new_val = (POKEY_AUDF[POKEY_CHAN4 + chip_offs] * 256 + POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 1) * POKEY_Base_mult[chip]; } else new_val = (POKEY_AUDF[POKEY_CHAN4 + chip_offs] + 1) * POKEY_Base_mult[chip]; if (new_val != Div_n_max[POKEY_CHAN4 + chip_offs]) { Div_n_max[POKEY_CHAN4 + chip_offs] = new_val; if (Div_n_cnt[POKEY_CHAN4 + chip_offs] > new_val) { Div_n_cnt[POKEY_CHAN4 + chip_offs] = new_val; } } } /* if channel is volume only, set current output */ for (chan = POKEY_CHAN1; chan <= POKEY_CHAN4; chan++) { if (chan_mask & (1 << chan)) { #ifdef VOL_ONLY_SOUND #ifdef __PLUS if (g_Sound.nDigitized) #endif if ((POKEY_AUDC[chan + chip_offs] & POKEY_VOL_ONLY)) { #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled && chip & 0x01) #else if (chip & 0x01) #endif { sampbuf_lastval2 += pokeysnd_AUDV[chan + chip_offs] - POKEYSND_sampbuf_AUDV[chan + chip_offs]; sampbuf_val2[sampbuf_ptr2] = sampbuf_lastval2; POKEYSND_sampbuf_AUDV[chan + chip_offs] = pokeysnd_AUDV[chan + chip_offs]; sampbuf_cnt2[sampbuf_ptr2] = (ANTIC_CPU_CLOCK - sampbuf_last2) * 128 * POKEYSND_samp_freq / 178979; sampbuf_last2 = ANTIC_CPU_CLOCK; sampbuf_ptr2++; if (sampbuf_ptr2 >= POKEYSND_SAMPBUF_MAX) sampbuf_ptr2 = 0; if (sampbuf_ptr2 == sampbuf_rptr2) { sampbuf_rptr2++; if (sampbuf_rptr2 >= POKEYSND_SAMPBUF_MAX) sampbuf_rptr2 = 0; } } else #endif /* STEREO_SOUND */ { POKEYSND_sampbuf_lastval += pokeysnd_AUDV[chan + chip_offs] -POKEYSND_sampbuf_AUDV[chan + chip_offs]; POKEYSND_sampbuf_val[POKEYSND_sampbuf_ptr] = POKEYSND_sampbuf_lastval; POKEYSND_sampbuf_AUDV[chan + chip_offs] = pokeysnd_AUDV[chan + chip_offs]; POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_ptr] = (ANTIC_CPU_CLOCK - POKEYSND_sampbuf_last) * 128 * POKEYSND_samp_freq / 178979; POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; POKEYSND_sampbuf_ptr++; if (POKEYSND_sampbuf_ptr >= POKEYSND_SAMPBUF_MAX) POKEYSND_sampbuf_ptr = 0; if (POKEYSND_sampbuf_ptr == POKEYSND_sampbuf_rptr) { POKEYSND_sampbuf_rptr++; if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) POKEYSND_sampbuf_rptr = 0; } } } #endif /* VOL_ONLY_SOUND */ /* I've disabled any frequencies that exceed the sampling frequency. There isn't much point in processing frequencies that the hardware can't reproduce. I've also disabled processing if the volume is zero. */ /* if the channel is volume only */ /* or the channel is off (volume == 0) */ /* or the channel freq is greater than the playback freq */ if ( (POKEY_AUDC[chan + chip_offs] & POKEY_VOL_ONLY) || ((POKEY_AUDC[chan + chip_offs] & POKEY_VOLUME_MASK) == 0) || (!BIENIAS_FIX && (Div_n_max[chan + chip_offs] < (Samp_n_max >> 8))) ) { /* indicate the channel is 'on' */ Outvol[chan + chip_offs] = 1; /* can only ignore channel if filtering off */ if ((chan == POKEY_CHAN3 && !(POKEY_AUDCTL[chip] & POKEY_CH1_FILTER)) || (chan == POKEY_CHAN4 && !(POKEY_AUDCTL[chip] & POKEY_CH2_FILTER)) || (chan == POKEY_CHAN1) || (chan == POKEY_CHAN2) || (!BIENIAS_FIX && (Div_n_max[chan + chip_offs] < (Samp_n_max >> 8))) ) { /* and set channel freq to max to reduce processing */ Div_n_max[chan + chip_offs] = 0x7fffffffL; Div_n_cnt[chan + chip_offs] = 0x7fffffffL; } } } } /* _enable(); */ /* RSF - removed for portability 31-MAR-97 */ } /*****************************************************************************/ /* Module: pokeysnd_process() */ /* Purpose: To fill the output buffer with the sound output based on the */ /* pokey chip parameters. */ /* */ /* Author: Ron Fries */ /* Date: January 1, 1997 */ /* */ /* Inputs: *buffer - pointer to the buffer where the audio output will */ /* be placed */ /* sndn - for mono, size of the playback buffer in samples */ /* for stereo, size of the playback buffer in left samples */ /* plus right samples. */ /* num_pokeys - number of currently active pokeys to process */ /* */ /* Outputs: the buffer will be filled with n bytes of audio - no return val */ /* Also the buffer will be written to disk if Sound recording is ON */ /* */ /*****************************************************************************/ static void pokeysnd_process_8(void *sndbuffer, int sndn) { register UBYTE *buffer = (UBYTE *) sndbuffer; register int n = sndn; register ULONG *div_n_ptr; register UBYTE *samp_cnt_w_ptr; register ULONG event_min; register UBYTE next_event; #ifdef CLIP_SOUND register SWORD cur_val; /* then we have to count as 16-bit signed */ #ifdef STEREO_SOUND register SWORD cur_val2; #endif #else /* CLIP_SOUND */ register UBYTE cur_val; /* otherwise we'll simplify as 8-bit unsigned */ #ifdef STEREO_SOUND register UBYTE cur_val2; #endif #endif /* CLIP_SOUND */ register UBYTE *out_ptr; register UBYTE audc; register UBYTE toggle; register UBYTE count; register UBYTE *vol_ptr; /* set a pointer to the whole portion of the samp_n_cnt */ #ifdef WORDS_BIGENDIAN samp_cnt_w_ptr = ((UBYTE *) (&Samp_n_cnt[0]) + 3); #else samp_cnt_w_ptr = ((UBYTE *) (&Samp_n_cnt[0]) + 1); #endif /* set a pointer for optimization */ out_ptr = Outvol; vol_ptr = pokeysnd_AUDV; /* The current output is pre-determined and then adjusted based on each */ /* output change for increased performance (less over-all math). */ /* add the output values of all 4 channels */ cur_val = POKEYSND_SAMP_MIN; #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled) #endif cur_val2 = POKEYSND_SAMP_MIN; #endif /* STEREO_SOUND */ count = Num_pokeys; do { if (*out_ptr++) cur_val += *vol_ptr; vol_ptr++; if (*out_ptr++) cur_val += *vol_ptr; vol_ptr++; if (*out_ptr++) cur_val += *vol_ptr; vol_ptr++; if (*out_ptr++) cur_val += *vol_ptr; vol_ptr++; #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled) #endif { count--; if (count) { if (*out_ptr++) cur_val2 += *vol_ptr; vol_ptr++; if (*out_ptr++) cur_val2 += *vol_ptr; vol_ptr++; if (*out_ptr++) cur_val2 += *vol_ptr; vol_ptr++; if (*out_ptr++) cur_val2 += *vol_ptr; vol_ptr++; } else break; } #endif /* STEREO_SOUND */ count--; } while (count); #ifdef SYNCHRONIZED_SOUND cur_val += speaker; #endif /* loop until the buffer is filled */ while (n) { /* Normally the routine would simply decrement the 'div by N' */ /* counters and react when they reach zero. Since we normally */ /* won't be processing except once every 80 or so counts, */ /* I've optimized by finding the smallest count and then */ /* 'accelerated' time by adjusting all pointers by that amount. */ /* find next smallest event (either sample or chan 1-4) */ next_event = POKEY_SAMPLE; event_min = READ_U32(samp_cnt_w_ptr); div_n_ptr = Div_n_cnt; count = 0; do { /* Though I could have used a loop here, this is faster */ if (*div_n_ptr <= event_min) { event_min = *div_n_ptr; next_event = POKEY_CHAN1 + (count << 2); } div_n_ptr++; if (*div_n_ptr <= event_min) { event_min = *div_n_ptr; next_event = POKEY_CHAN2 + (count << 2); } div_n_ptr++; if (*div_n_ptr <= event_min) { event_min = *div_n_ptr; next_event = POKEY_CHAN3 + (count << 2); } div_n_ptr++; if (*div_n_ptr <= event_min) { event_min = *div_n_ptr; next_event = POKEY_CHAN4 + (count << 2); } div_n_ptr++; count++; } while (count < Num_pokeys); /* if the next event is a channel change */ if (next_event != POKEY_SAMPLE) { /* shift the polynomial counters */ count = Num_pokeys; do { /* decrement all counters by the smallest count found */ /* again, no loop for efficiency */ div_n_ptr--; *div_n_ptr -= event_min; div_n_ptr--; *div_n_ptr -= event_min; div_n_ptr--; *div_n_ptr -= event_min; div_n_ptr--; *div_n_ptr -= event_min; count--; } while (count); WRITE_U32(samp_cnt_w_ptr, READ_U32(samp_cnt_w_ptr) - event_min); /* since the polynomials require a mod (%) function which is division, I don't adjust the polynomials on the SAMPLE events, only the CHAN events. I have to keep track of the change, though. */ P4 = (P4 + event_min) % POKEY_POLY4_SIZE; P5 = (P5 + event_min) % POKEY_POLY5_SIZE; P9 = (P9 + event_min) % POKEY_POLY9_SIZE; P17 = (P17 + event_min) % POKEY_POLY17_SIZE; /* adjust channel counter */ Div_n_cnt[next_event] += Div_n_max[next_event]; /* get the current AUDC into a register (for optimization) */ audc = POKEY_AUDC[next_event]; /* set a pointer to the current output (for opt...) */ out_ptr = &Outvol[next_event]; /* assume no changes to the output */ toggle = FALSE; /* From here, a good understanding of the hardware is required */ /* to understand what is happening. I won't be able to provide */ /* much description to explain it here. */ /* if VOLUME only then nothing to process */ if (!(audc & POKEY_VOL_ONLY)) { /* if the output is pure or the output is poly5 and the poly5 bit */ /* is set */ if ((audc & POKEY_NOTPOLY5) || bit5[P5]) { /* if the PURETONE bit is set */ if (audc & POKEY_PURETONE) { /* then simply toggle the output */ toggle = TRUE; } /* otherwise if POLY4 is selected */ else if (audc & POKEY_POLY4) { /* then compare to the poly4 bit */ toggle = (bit4[P4] == !(*out_ptr)); } else { /* if 9-bit poly is selected on this chip */ if (POKEY_AUDCTL[next_event >> 2] & POKEY_POLY9) { /* compare to the poly9 bit */ toggle = ((POKEY_poly9_lookup[P9] & 1) == !(*out_ptr)); } else { /* otherwise compare to the poly17 bit */ toggle = (((POKEY_poly17_lookup[P17 >> 3] >> (P17 & 7)) & 1) == !(*out_ptr)); } } } } /* check channel 1 filter (clocked by channel 3) */ if ( POKEY_AUDCTL[next_event >> 2] & POKEY_CH1_FILTER) { /* if we're processing channel 3 */ if ((next_event & 0x03) == POKEY_CHAN3) { /* check output of channel 1 on same chip */ if (Outvol[next_event & 0xfd]) { /* if on, turn it off */ Outvol[next_event & 0xfd] = 0; #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled && (next_event & 0x04)) #else if ((next_event & 0x04)) #endif cur_val2 -= pokeysnd_AUDV[next_event & 0xfd]; else #endif /* STEREO_SOUND */ cur_val -= pokeysnd_AUDV[next_event & 0xfd]; } } } /* check channel 2 filter (clocked by channel 4) */ if ( POKEY_AUDCTL[next_event >> 2] & POKEY_CH2_FILTER) { /* if we're processing channel 4 */ if ((next_event & 0x03) == POKEY_CHAN4) { /* check output of channel 2 on same chip */ if (Outvol[next_event & 0xfd]) { /* if on, turn it off */ Outvol[next_event & 0xfd] = 0; #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled && (next_event & 0x04)) #else if ((next_event & 0x04)) #endif cur_val2 -= pokeysnd_AUDV[next_event & 0xfd]; else #endif /* STEREO_SOUND */ cur_val -= pokeysnd_AUDV[next_event & 0xfd]; } } } /* if the current output bit has changed */ if (toggle) { if (*out_ptr) { /* remove this channel from the signal */ #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled && (next_event & 0x04)) #else if ((next_event & 0x04)) #endif cur_val2 -= pokeysnd_AUDV[next_event]; else #endif /* STEREO_SOUND */ cur_val -= pokeysnd_AUDV[next_event]; /* and turn the output off */ *out_ptr = 0; } else { /* turn the output on */ *out_ptr = 1; /* and add it to the output signal */ #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled && (next_event & 0x04)) #else if ((next_event & 0x04)) #endif cur_val2 += pokeysnd_AUDV[next_event]; else #endif /* STEREO_SOUND */ cur_val += pokeysnd_AUDV[next_event]; } } } else { /* otherwise we're processing a sample */ /* adjust the sample counter - note we're using the 24.8 integer which includes an 8 bit fraction for accuracy */ int iout; #ifdef STEREO_SOUND int iout2; #endif #ifdef INTERPOLATE_SOUND if (cur_val != last_val) { if (*Samp_n_cnt < Samp_n_max) { /* need interpolation */ #ifdef CLIP_SOUND iout = (cur_val * (SLONG)(*Samp_n_cnt) + last_val * (SLONG)(Samp_n_max - *Samp_n_cnt)) / (SLONG)Samp_n_max; #else iout = (cur_val * (*Samp_n_cnt) + last_val * (Samp_n_max - *Samp_n_cnt)) / Samp_n_max; #endif } else iout = cur_val; last_val = cur_val; } else iout = cur_val; #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled) #endif if (cur_val2 != last_val2) { if (*Samp_n_cnt < Samp_n_max) { /* need interpolation */ #ifdef CLIP_SOUND iout2 = (cur_val2 * (SLONG)(*Samp_n_cnt) + last_val2 * (SLONG)(Samp_n_max - *Samp_n_cnt)) / (SLONG)Samp_n_max; #else iout2 = (cur_val2 * (*Samp_n_cnt) + last_val2 * (Samp_n_max - *Samp_n_cnt)) / Samp_n_max; #endif } else iout2 = cur_val2; last_val2 = cur_val2; } else iout2 = cur_val2; #endif /* STEREO_SOUND */ #else /* INTERPOLATE_SOUND */ iout = cur_val; #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled) #endif iout2 = cur_val2; #endif /* STEREO_SOUND */ #endif /* INTERPOLATE_SOUND */ #ifdef VOL_ONLY_SOUND #ifdef __PLUS if (g_Sound.nDigitized) #endif { if (POKEYSND_sampbuf_rptr != POKEYSND_sampbuf_ptr) { int l; if (POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr] > 0) POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr] -= 1280; while ((l = POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr]) <= 0) { POKEYSND_sampout = POKEYSND_sampbuf_val[POKEYSND_sampbuf_rptr]; POKEYSND_sampbuf_rptr++; if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) POKEYSND_sampbuf_rptr = 0; if (POKEYSND_sampbuf_rptr != POKEYSND_sampbuf_ptr) POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr] += l; else break; } } iout += POKEYSND_sampout; #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled) #endif { if (sampbuf_rptr2 != sampbuf_ptr2) { int l; if (sampbuf_cnt2[sampbuf_rptr2] > 0) sampbuf_cnt2[sampbuf_rptr2] -= 1280; while ((l = sampbuf_cnt2[sampbuf_rptr2]) <= 0) { sampout2 = sampbuf_val2[sampbuf_rptr2]; sampbuf_rptr2++; if (sampbuf_rptr2 >= POKEYSND_SAMPBUF_MAX) sampbuf_rptr2 = 0; if (sampbuf_rptr2 != sampbuf_ptr2) sampbuf_cnt2[sampbuf_rptr2] += l; else break; } } iout2 += sampout2; } #endif /* STEREO_SOUND */ } #endif /* VOL_ONLY_SOUND */ #ifdef CLIP_SOUND if (iout > POKEYSND_SAMP_MAX) { /* then check high limit */ *buffer++ = (UBYTE) POKEYSND_SAMP_MAX; /* and limit if greater */ } else if (iout < POKEYSND_SAMP_MIN) { /* else check low limit */ *buffer++ = (UBYTE) POKEYSND_SAMP_MIN; /* and limit if less */ } else { /* otherwise use raw value */ *buffer++ = (UBYTE) iout; } #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled) { if (iout2 > POKEYSND_SAMP_MAX) *buffer++ = (UBYTE) POKEYSND_SAMP_MAX; else if (iout2 < POKEYSND_SAMP_MIN) *buffer++ = (UBYTE) POKEYSND_SAMP_MIN; else *buffer++ = (UBYTE) iout2; } #else /* __PLUS */ if (Num_pokeys > 1) { if ((POKEYSND_stereo_enabled ? iout2 : iout) > POKEYSND_SAMP_MAX) { /* then check high limit */ *buffer++ = (UBYTE) POKEYSND_SAMP_MAX; /* and limit if greater */ } else if ((POKEYSND_stereo_enabled ? iout2 : iout) < POKEYSND_SAMP_MIN) { /* else check low limit */ *buffer++ = (UBYTE) POKEYSND_SAMP_MIN; /* and limit if less */ } else { /* otherwise use raw value */ *buffer++ = (UBYTE) (POKEYSND_stereo_enabled ? iout2 : iout); } } #endif /* __PLUS */ #endif /* STEREO_SOUND */ #else /* CLIP_SOUND */ *buffer++ = (UBYTE) iout; /* clipping not selected, use value */ #ifdef STEREO_SOUND if (Num_pokeys > 1) #ifdef ASAP *buffer++ = (UBYTE) iout2; #else *buffer++ = (UBYTE) (POKEYSND_stereo_enabled ? iout2 : iout); #endif #endif /* STEREO_SOUND */ #endif /* CLIP_SOUND */ #ifdef WORDS_BIGENDIAN *(Samp_n_cnt + 1) += Samp_n_max; #else *Samp_n_cnt += Samp_n_max; #endif /* and indicate one less byte in the buffer */ n--; #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled) #endif if (Num_pokeys > 1) n--; #endif } } #ifdef VOL_ONLY_SOUND #ifdef __PLUS if (g_Sound.nDigitized) #endif { if (POKEYSND_sampbuf_rptr == POKEYSND_sampbuf_ptr) POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; #ifdef STEREO_SOUND #ifdef __PLUS if (POKEYSND_stereo_enabled) #endif if (sampbuf_rptr2 == sampbuf_ptr2) sampbuf_last2 = ANTIC_CPU_CLOCK; #endif /* STEREO_SOUND */ } #endif /* VOL_ONLY_SOUND */ } #ifdef SERIO_SOUND static void Update_serio_sound_rf(int out, UBYTE data) { #ifdef VOL_ONLY_SOUND #ifdef __PLUS if (g_Sound.nDigitized) { #endif int bits, pv, future; if (!POKEYSND_serio_sound_enabled) return; pv = 0; future = 0; bits = (data << 1) | 0x200; while (bits) { POKEYSND_sampbuf_lastval -= pv; pv = (bits & 0x01) * pokeysnd_AUDV[3]; /* FIXME!!! - set volume from AUDV */ POKEYSND_sampbuf_lastval += pv; POKEYSND_sampbuf_val[POKEYSND_sampbuf_ptr] = POKEYSND_sampbuf_lastval; POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_ptr] = (ANTIC_CPU_CLOCK + future-POKEYSND_sampbuf_last) * 128 * POKEYSND_samp_freq / 178979; POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK + future; POKEYSND_sampbuf_ptr++; if (POKEYSND_sampbuf_ptr >= POKEYSND_SAMPBUF_MAX ) POKEYSND_sampbuf_ptr = 0; if (POKEYSND_sampbuf_ptr == POKEYSND_sampbuf_rptr ) { POKEYSND_sampbuf_rptr++; if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) POKEYSND_sampbuf_rptr = 0; } /* 1789790/19200 = 93 */ future += 93; /* ~ 19200 bit/s - FIXME!!! set speed form AUDF [2] ??? */ bits >>= 1; } POKEYSND_sampbuf_lastval -= pv; #ifdef __PLUS } #endif #endif /* VOL_ONLY_SOUND */ } #endif /* SERIO_SOUND */ void POKEYSND_SetVolume(int vol) { if (vol > 100) vol = 100; if (vol < 0) vol = 0; POKEYSND_volume = vol * 0x100 / 100; } static void pokeysnd_process_16(void *sndbuffer, int sndn) { UWORD *buffer = (UWORD *) sndbuffer; int i; pokeysnd_process_8(buffer, sndn); for (i = sndn - 1; i >= 0; i--) { #ifndef POKEYSND_SIGNED_SAMPLES int smp = ((int) (((UBYTE *) buffer)[i]) - 0x80) * POKEYSND_volume; #else int smp = ((int) ((SBYTE *) buffer)[i]) * POKEYSND_volume; #endif if (smp > 32767) smp = 32767; else if (smp < -32768) smp = -32768; buffer[i] = smp; } } #ifdef SYNCHRONIZED_SOUND static void Generate_sync_rf(unsigned int num_ticks) { double new_samp_pos; unsigned int ticks; UBYTE *buffer = POKEYSND_process_buffer + POKEYSND_process_buffer_fill; UBYTE *buffer_end = POKEYSND_process_buffer + POKEYSND_process_buffer_length; for (;;) { double int_part; new_samp_pos = samp_pos + ticks_per_sample; new_samp_pos = modf(new_samp_pos, &int_part); ticks = (unsigned int)int_part; if (ticks > num_ticks) { samp_pos -= num_ticks; break; } if (buffer >= buffer_end) break; samp_pos = new_samp_pos; num_ticks -= ticks; if (POKEYSND_snd_flags & POKEYSND_BIT16) { pokeysnd_process_16(buffer, POKEYSND_num_pokeys); buffer += 2 * POKEYSND_num_pokeys; } else { pokeysnd_process_8(buffer, POKEYSND_num_pokeys); buffer += POKEYSND_num_pokeys; } } POKEYSND_process_buffer_fill = buffer - POKEYSND_process_buffer; } #endif /* SYNCHRONIZED_SOUND */ #ifdef CONSOLE_SOUND void POKEYSND_UpdateConsol(int set) { if (!POKEYSND_console_sound_enabled) return; #ifdef SYNCHRONIZED_SOUND if (set) Update_synchronized_sound(); #endif /* SYNCHRONIZED_SOUND */ POKEYSND_UpdateConsol_ptr(set); } static void Update_consol_sound_rf(int set) { #ifdef SYNCHRONIZED_SOUND if (set) speaker = CONSOLE_VOL * GTIA_speaker; #elif defined(VOL_ONLY_SOUND) static int prev_atari_speaker = 0; static unsigned int prev_cpu_clock = 0; int d; #ifdef __PLUS if (!g_Sound.nDigitized) return; #endif if (!set && POKEYSND_samp_consol_val == 0) return; POKEYSND_sampbuf_lastval -= POKEYSND_samp_consol_val; if (prev_atari_speaker != GTIA_speaker) { POKEYSND_samp_consol_val = GTIA_speaker * 8 * 4; /* gain */ prev_cpu_clock = ANTIC_CPU_CLOCK; } else if (!set) { d = ANTIC_CPU_CLOCK - prev_cpu_clock; if (d < 114) { POKEYSND_sampbuf_lastval += POKEYSND_samp_consol_val; return; } while (d >= 114 /* CPUL */) { POKEYSND_samp_consol_val = POKEYSND_samp_consol_val * 99 / 100; d -= 114; } prev_cpu_clock = ANTIC_CPU_CLOCK - d; } POKEYSND_sampbuf_lastval += POKEYSND_samp_consol_val; prev_atari_speaker = GTIA_speaker; POKEYSND_sampbuf_val[POKEYSND_sampbuf_ptr] = POKEYSND_sampbuf_lastval; POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_ptr] = (ANTIC_CPU_CLOCK - POKEYSND_sampbuf_last) * 128 * POKEYSND_samp_freq / 178979; POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; POKEYSND_sampbuf_ptr++; if (POKEYSND_sampbuf_ptr >= POKEYSND_SAMPBUF_MAX) POKEYSND_sampbuf_ptr = 0; if (POKEYSND_sampbuf_ptr == POKEYSND_sampbuf_rptr) { POKEYSND_sampbuf_rptr++; if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) POKEYSND_sampbuf_rptr = 0; } #endif /* !SYNCHRONIZED_SOUND && VOL_ONLY_SOUND */ } #endif /* CONSOLE_SOUND */ #ifdef VOL_ONLY_SOUND static void Update_vol_only_sound_rf(void) { #ifdef CONSOLE_SOUND POKEYSND_UpdateConsol(0); /* mmm */ #endif /* CONSOLE_SOUND */ } #endif /* VOL_ONLY_SOUND */