📄 pokeysnd.c
字号:
/* * pokeysnd.c - POKEY sound chip emulation, v2.4 * * Copyright (C) 1996-1998 Ron Fries * Copyright (C) 1998-2006 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#include "config.h"#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"#ifdef WORDS_UNALIGNED_OK# define READ_U32(x) (*(uint32 *) (x))# define WRITE_U32(x, d) (*(uint32 *) (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) \ { \ uint32 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) \ { \ uint32 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 uint8 Num_pokeys;static uint8 AUDV[4 * MAXPOKEYS]; /* Channel volume - derived */static uint8 Outbit[4 * MAXPOKEYS]; /* current state of the output (high or low) */static uint8 Outvol[4 * MAXPOKEYS]; /* last output volume for each channel *//* Initialze 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 uint8 bit4[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 */#endifstatic uint8 bit5[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};#endifstatic uint32 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 uint32 Div_n_cnt[4 * MAXPOKEYS], /* Divide by n counter. one for each channel */ Div_n_max[4 * MAXPOKEYS]; /* Divide by n maximum, one for each channel */static uint32 Samp_n_max, /* Sample max. For accuracy, it is *256 */ Samp_n_cnt[2]; /* Sample cnt. */extern int atari_speaker;#ifdef INTERPOLATE_SOUNDstatic uint16 last_val = 0; /* last output value */#ifdef STEREO_SOUNDstatic uint16 last_val2 = 0; /* last output value */#endif#endif/* Volume only emulations declarations */#ifdef VOL_ONLY_SOUND#define SAMPBUF_MAX 2000int sampbuf_val[SAMPBUF_MAX]; /* volume values */int sampbuf_cnt[SAMPBUF_MAX]; /* relative start time */int sampbuf_ptr = 0; /* pointer to sampbuf */int sampbuf_rptr = 0; /* pointer to read from sampbuf */int sampbuf_last = 0; /* last absolute time */int sampbuf_AUDV[4 * MAXPOKEYS]; /* prev. channel volume */int sampbuf_lastval = 0; /* last volume */int sampout; /* last out volume */uint16 samp_freq;int samp_consol_val = 0; /* actual value of console sound */#ifdef STEREO_SOUNDint sampbuf_val2[SAMPBUF_MAX]; /* volume values */int sampbuf_cnt2[SAMPBUF_MAX]; /* relative start time */int sampbuf_ptr2 = 0; /* pointer to sampbuf */int sampbuf_rptr2 = 0; /* pointer to read from sampbuf */int sampbuf_last2 = 0; /* last absolute time */int sampbuf_lastval2 = 0; /* last volume */int sampout2; /* last out volume */#endif#endif /* VOL_ONLY_SOUND */static uint32 snd_freq17 = FREQ_17_EXACT;int32 snd_playback_freq = 44100;uint8 snd_num_pokeys = 1;static int snd_flags = 0;static int mz_quality = 0; /* default quality for mzpokeysnd */#ifdef __PLUSint mz_clear_regs = 0;#endifint enable_new_pokey = TRUE;#ifndef ASAPint stereo_enabled = FALSE;#endif/* multiple sound engine interface */static void Pokey_process_8(void *sndbuffer, unsigned sndn);static void Pokey_process_16(void *sndbuffer, unsigned sndn);static void null_pokey_process(void *sndbuffer, unsigned int sndn) {}void (*Pokey_process_ptr)(void *sndbuffer, unsigned int sndn) = null_pokey_process;static void Update_pokey_sound_rf(uint16, uint8, uint8, uint8);static void null_pokey_sound(uint16 addr, uint8 val, uint8 chip, uint8 gain) {}void (*Update_pokey_sound) (uint16 addr, uint8 val, uint8 chip, uint8 gain) = null_pokey_sound;#ifdef SERIO_SOUNDstatic void Update_serio_sound_rf(int out, UBYTE data);static void null_serio_sound(int out, UBYTE data) {}void (*Update_serio_sound)(int out, UBYTE data) = null_serio_sound;int serio_sound_enabled = 1;#endif#ifdef CONSOLE_SOUNDstatic void Update_consol_sound_rf(int set);static void null_consol_sound(int set) {}void (*Update_consol_sound)(int set) = null_consol_sound;int console_sound_enabled = 1;#endif#ifdef VOL_ONLY_SOUNDstatic void Update_vol_only_sound_rf(void);static void null_vol_only_sound(void) {}void (*Update_vol_only_sound)(void) = null_vol_only_sound;#endif/*****************************************************************************//* 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 (uint32 *)((uint8 *)(&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 (uint32 *)((uint8 *)(&Samp_n_cnt[0])+3) gives me the 32-bit whole *//* number only. *//*****************************************************************************//*****************************************************************************//* Module: Pokey_sound_init() *//* 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 Pokey_sound_init_rf(uint32 freq17, uint16 playback_freq, uint8 num_pokeys, unsigned int flags);int Pokey_DoInit(void){ if (enable_new_pokey) return Pokey_sound_init_mz(snd_freq17, (uint16) snd_playback_freq, snd_num_pokeys, snd_flags, mz_quality#ifdef __PLUS , mz_clear_regs#endif ); else return Pokey_sound_init_rf(snd_freq17, (uint16) snd_playback_freq, snd_num_pokeys, snd_flags);}int Pokey_sound_init(uint32 freq17, uint16 playback_freq, uint8 num_pokeys, unsigned int flags#ifdef __PLUS , int clear_regs#endif){ snd_freq17 = freq17; snd_playback_freq = playback_freq; snd_num_pokeys = num_pokeys; snd_flags = flags;#ifdef __PLUS mz_clear_regs = clear_regs;#endif return Pokey_DoInit();}void Pokey_set_mzquality(int quality) /* specially for win32, perhaps not needed? */{ mz_quality = quality;}void Pokey_process(void *sndbuffer, unsigned int sndn){ Pokey_process_ptr(sndbuffer, sndn);#if !defined(__PLUS) && !defined(ASAP) WriteToSoundFile(sndbuffer, sndn);#endif}static int Pokey_sound_init_rf(uint32 freq17, uint16 playback_freq, uint8 num_pokeys, unsigned int flags){ uint8 chan; Update_pokey_sound = Update_pokey_sound_rf;#ifdef SERIO_SOUND Update_serio_sound = Update_serio_sound_rf;#endif#ifdef CONSOLE_SOUND Update_consol_sound = Update_consol_sound_rf;#endif#ifdef VOL_ONLY_SOUND Update_vol_only_sound = Update_vol_only_sound_rf;#endif Pokey_process_ptr = (flags & SND_BIT16) ? Pokey_process_16 : Pokey_process_8;#ifdef VOL_ONLY_SOUND 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 = ((uint32) 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 < (MAXPOKEYS * 4); chan++) { Outvol[chan] = 0; Outbit[chan] = 0; Div_n_cnt[chan] = 0; Div_n_max[chan] = 0x7fffffffL; AUDV[chan] = 0;#ifdef VOL_ONLY_SOUND sampbuf_AUDV[chan] = 0;#endif } /* set the number of pokey chips currently emulated */ Num_pokeys = num_pokeys; return 0; /* OK */}/*****************************************************************************//* Module: Update_pokey_sound() *//* 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 *//* *//*****************************************************************************/static void Update_pokey_sound_rf(uint16 addr, uint8 val, uint8 chip, uint8 gain){ uint32 new_val = 0; uint8 chan; uint8 chan_mask; uint8 chip_offs; /* calculate the chip_offs for the channel arrays */ chip_offs = chip << 2; /* determine which address was changed */ switch (addr & 0x0f) { case _AUDF1: /* AUDF[CHAN1 + chip_offs] = val; */ chan_mask = 1 << CHAN1; if (AUDCTL[chip] & CH1_CH2) /* if ch 1&2 tied together */ chan_mask |= 1 << CHAN2; /* then also change on ch2 */ break; case _AUDC1: /* AUDC[CHAN1 + chip_offs] = val; */ AUDV[CHAN1 + chip_offs] = (val & VOLUME_MASK) * gain; chan_mask = 1 << CHAN1; break; case _AUDF2: /* AUDF[CHAN2 + chip_offs] = val; */ chan_mask = 1 << CHAN2; break; case _AUDC2: /* AUDC[CHAN2 + chip_offs] = val; */ AUDV[CHAN2 + chip_offs] = (val & VOLUME_MASK) * gain; chan_mask = 1 << CHAN2; break; case _AUDF3: /* AUDF[CHAN3 + chip_offs] = val; */ chan_mask = 1 << CHAN3; if (AUDCTL[chip] & CH3_CH4) /* if ch 3&4 tied together */ chan_mask |= 1 << CHAN4; /* then also change on ch4 */ break; case _AUDC3: /* AUDC[CHAN3 + chip_offs] = val; */ AUDV[CHAN3 + chip_offs] = (val & VOLUME_MASK) * gain; chan_mask = 1 << CHAN3; break; case _AUDF4: /* AUDF[CHAN4 + chip_offs] = val; */ chan_mask = 1 << CHAN4; break; case _AUDC4: /* AUDC[CHAN4 + chip_offs] = val; */ AUDV[CHAN4 + chip_offs] = (val & VOLUME_MASK) * gain; chan_mask = 1 << CHAN4; break; case _AUDCTL:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -