📄 es5506.c
字号:
/********************************************************************************************** * * Ensoniq ES5505/6 driver * by Aaron Giles * **********************************************************************************************/#include <stdio.h>#include <stdlib.h>#include <math.h>#include "sasound.h"#include "driver.h"#include "adpcm.h"//#define DUMP 1/********************************************************************************************** CONSTANTS***********************************************************************************************/#define BACKEND_INTERPOLATE 1#define LOG_COMMANDS 0#define MAX_SAMPLE_CHUNK 10000#define ULAW_MAXBITS 8#define FRAC_BITS 14#define FRAC_ONE (1 << FRAC_BITS)#define FRAC_MASK (FRAC_ONE - 1)#define CONTROL_BS1 0x8000#define CONTROL_BS0 0x4000#define CONTROL_CMPD 0x2000#define CONTROL_CA2 0x1000#define CONTROL_CA1 0x0800#define CONTROL_CA0 0x0400#define CONTROL_LP4 0x0200#define CONTROL_LP3 0x0100#define CONTROL_IRQ 0x0080#define CONTROL_DIR 0x0040#define CONTROL_IRQE 0x0020#define CONTROL_BLE 0x0010#define CONTROL_LPE 0x0008#define CONTROL_LEI 0x0004#define CONTROL_STOP1 0x0002#define CONTROL_STOP0 0x0001#define CONTROL_BSMASK (CONTROL_BS1 | CONTROL_BS0)#define CONTROL_CAMASK (CONTROL_CA2 | CONTROL_CA1 | CONTROL_CA0)#define CONTROL_LPMASK (CONTROL_LP4 | CONTROL_LP3)#define CONTROL_LOOPMASK (CONTROL_BLE | CONTROL_LPE)#define CONTROL_STOPMASK (CONTROL_STOP1 | CONTROL_STOP0)/********************************************************************************************** INTERNAL DATA STRUCTURES***********************************************************************************************//* struct describing a single playing voice */struct ES5506Voice{ /* external state */ UINT32 control; /* control register */ UINT32 freqcount; /* frequency count register */ UINT32 start; /* start register */ UINT32 lvol; /* left volume register */ UINT32 end; /* end register */ UINT32 lvramp; /* left volume ramp register */ UINT32 accum; /* accumulator register */ UINT32 rvol; /* right volume register */ UINT32 rvramp; /* right volume ramp register */ UINT32 ecount; /* envelope count register */ UINT32 k2; /* k2 register */ UINT32 k2ramp; /* k2 ramp register */ UINT32 k1; /* k1 register */ UINT32 k1ramp; /* k1 ramp register */ INT32 o4n1; /* filter storage O4(n-1) */ INT32 o3n1; /* filter storage O3(n-1) */ INT32 o3n2; /* filter storage O3(n-2) */ INT32 o2n1; /* filter storage O2(n-1) */ INT32 o2n2; /* filter storage O2(n-2) */ INT32 o1n1; /* filter storage O1(n-1) */ UINT32 exbank; /* external address bank */ /* internal state */ UINT8 index; /* index of this voice */ UINT8 filtcount; /* filter count */};struct ES5506Chip{ int stream; /* which stream are we using */ UINT16 * region_base[4]; /* pointer to the base of the region */ UINT32 write_latch; /* currently accumulated data for write */ UINT32 read_latch; /* currently accumulated data for read */ double master_clock; /* master clock frequency */ void (*irq_callback)(int); /* IRQ callback */ UINT16 (*port_read)(void); /* input port read */ UINT8 current_page; /* current register page */ UINT8 active_voices; /* number of active voices */ UINT8 mode; /* MODE register */ UINT8 wst; /* W_ST register */ UINT8 wend; /* W_END register */ UINT8 lrend; /* LR_END register */ UINT8 irqv; /* IRQV register */ INT32 output_step; /* step value for frequency conversion */ INT32 output_pos; /* current fractional position */ INT32 last_lsample; /* last sample output */ INT32 last_rsample; /* last sample output */ INT32 curr_lsample; /* current sample target */ INT32 curr_rsample; /* current sample target */ struct ES5506Voice voice[32]; /* the 32 voices */};/********************************************************************************************** GLOBALS***********************************************************************************************/static struct ES5506Chip es5506[MAX_ES5506];static INT32 *accumulator;static INT32 *scratch;static INT16 *ulaw_lookup;static UINT16 *volume_lookup;static FILE *eslog;/********************************************************************************************** update_irq_state -- update the IRQ state***********************************************************************************************/INLINE static void update_irq_state(struct ES5506Chip *chip){#if 0 int irq_bits = chip->status_register & chip->irq_mask; /* always off if the enable is off */ if (!chip->irq_enable) irq_bits = 0; /* update the state if changed */ if (irq_bits && !chip->irq_state) { chip->irq_state = 1; if (chip->irq_callback) (*chip->irq_callback)(1); } else if (!irq_bits && chip->irq_state) { chip->irq_state = 0; if (chip->irq_callback) (*chip->irq_callback)(0); }#endif}/********************************************************************************************** compute_tables -- compute static tables***********************************************************************************************/static int compute_tables(void){ int i; /* allocate ulaw lookup table */ if (!ulaw_lookup) ulaw_lookup = malloc(sizeof(ulaw_lookup[0]) << ULAW_MAXBITS); if (!ulaw_lookup) return 0; /* generate ulaw lookup table */ for (i = 0; i < (1 << ULAW_MAXBITS); i++) { UINT16 rawval = (i << (16 - ULAW_MAXBITS)) | (1 << (15 - ULAW_MAXBITS)); UINT8 exponent = rawval >> 13; UINT32 mantissa = (rawval << 3) & 0xffff; if (exponent == 0) ulaw_lookup[i] = (INT16)mantissa >> 7; else { mantissa = (mantissa >> 1) | (~mantissa & 0x8000); ulaw_lookup[i] = (INT16)mantissa >> (7 - exponent); } } /* allocate volume lookup table */ if (!volume_lookup) volume_lookup = malloc(sizeof(volume_lookup[0]) * 4096); if (!volume_lookup) return 0; /* generate ulaw lookup table */ for (i = 0; i < 4096; i++) { UINT8 exponent = i >> 8; UINT32 mantissa = (i & 0xff) | 0x100; volume_lookup[i] = (mantissa << 11) >> (20 - exponent); } return 1;}/********************************************************************************************** interpolate backend_interpolate -- interpolate between two samples***********************************************************************************************/#define interpolate(sample1, sample2, accum) \ (sample1 * (INT32)(0x800 - (voice->accum & 0x7ff)) + \ sample2 * (INT32)(voice->accum & 0x7ff)) >> 11;#if BACKEND_INTERPOLATE#define backend_interpolate(sample1, sample2, position) \ (sample1 * (INT32)(FRAC_ONE - position) + \ sample2 * (INT32)position) >> FRAC_BITS;#else#define backend_interpolate(sample1, sample2, position) sample1#endif/********************************************************************************************** apply_filters -- apply the 4-pole digital filter to the sample***********************************************************************************************/#define apply_filters(voice, sample) \do \{ \ /* pole 1 is always low-pass using K1 */ \ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o1n1) / 16384) + voice->o1n1; \ voice->o1n1 = sample; \ \ /* pole 2 is always low-pass using K1 */ \ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o2n1) / 16384) + voice->o2n1; \ voice->o2n2 = voice->o2n1; \ voice->o2n1 = sample; \ \ /* remaining poles depend on the current filter setting */ \ switch (voice->control & CONTROL_LPMASK) \ { \ case 0: \ /* pole 3 is high-pass using K2 */ \ sample = sample - voice->o2n2 + ((INT32)(voice->k2 >> 2) * voice->o3n1) / 32768 + voice->o3n1 / 2; \ voice->o3n2 = voice->o3n1; \ voice->o3n1 = sample; \ \ /* pole 4 is high-pass using K2 */ \ sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ voice->o4n1 = sample; \ break; \ \ case CONTROL_LP3: \ /* pole 3 is low-pass using K1 */ \ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ voice->o3n2 = voice->o3n1; \ voice->o3n1 = sample; \ \ /* pole 4 is high-pass using K2 */ \ sample = sample - voice->o3n2 + ((INT32)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ voice->o4n1 = sample; \ break; \ \ case CONTROL_LP4: \ /* pole 3 is low-pass using K2 */ \ sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ voice->o3n2 = voice->o3n1; \ voice->o3n1 = sample; \ \ /* pole 4 is low-pass using K2 */ \ sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ voice->o4n1 = sample; \ break; \ \ case CONTROL_LP4 | CONTROL_LP3: \ /* pole 3 is low-pass using K1 */ \ sample = ((INT32)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ voice->o3n2 = voice->o3n1; \ voice->o3n1 = sample; \ \ /* pole 4 is low-pass using K2 */ \ sample = ((INT32)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ voice->o4n1 = sample; \ break; \ } \} while (0)/********************************************************************************************** update_envelopes -- update the envelopes***********************************************************************************************/#define update_envelopes(voice, samples) \do \{ \ int count = (samples > 1 && samples > voice->ecount) ? voice->ecount : samples; \ \ /* decrement the envelope counter */ \ voice->ecount -= count; \ \ /* ramp left volume */ \ if (voice->lvramp) \ { \ voice->lvol += (INT8)voice->lvramp * count; \ if ((INT32)voice->lvol < 0) voice->lvol = 0; \ else if (voice->lvol > 0xffff) voice->lvol = 0xffff; \ } \ \ /* ramp right volume */ \ if (voice->rvramp) \ { \ voice->rvol += (INT8)voice->rvramp * count; \ if ((INT32)voice->rvol < 0) voice->rvol = 0; \ else if (voice->rvol > 0xffff) voice->rvol = 0xffff; \ } \ \ /* ramp k1 filter constant */ \ if (voice->k1ramp && ((INT32)voice->k1ramp >= 0 || !(voice->filtcount & 7))) \ { \ voice->k1 += (INT8)voice->k1ramp * count; \ if ((INT32)voice->k1 < 0) voice->k1 = 0; \ else if (voice->k1 > 0xffff) voice->k1 = 0xffff; \ } \ \ /* ramp k2 filter constant */ \ if (voice->k2ramp && ((INT32)voice->k2ramp >= 0 || !(voice->filtcount & 7))) \ { \ voice->k2 += (INT8)voice->k2ramp * count; \ if ((INT32)voice->k2 < 0) voice->k2 = 0; \ else if (voice->k2 > 0xffff) voice->k2 = 0xffff; \ } \ \ /* update the filter constant counter */ \ voice->filtcount += count; \ \} while (0)/********************************************************************************************** check_for_end_forward check_for_end_reverse -- check for loop end and loop appropriately***********************************************************************************************/#define check_for_end_forward(voice, accum) \do \{ \ /* are we past the end? */ \ if (accum > voice->end && !(voice->control & CONTROL_LEI)) \ { \ /* generate interrupt */ \ voice->control |= CONTROL_IRQ; \ \ /* handle the different types of looping */ \ switch (voice->control & CONTROL_LOOPMASK) \ { \ /* non-looping */ \ case 0: \ voice->control |= CONTROL_STOP0; \ goto alldone; \ \ /* uni-directional looping */ \ case CONTROL_LPE: \ accum = voice->start + (accum - voice->end); \ break; \ \ /* trans-wave looping */ \ case CONTROL_BLE: \ accum = voice->start + (accum - voice->end); \ voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ break; \ \ /* bi-directional looping */ \ case CONTROL_LPE | CONTROL_BLE: \ accum = voice->end - (accum - voice->end); \ voice->control ^= CONTROL_DIR; \ goto reverse; \ } \ } \} while (0)#define check_for_end_reverse(voice, accum) \do \{ \ /* are we past the end? */ \ if (accum < voice->start && !(voice->control & CONTROL_LEI)) \ { \ /* generate interrupt */ \ voice->control |= CONTROL_IRQ; \ \ /* handle the different types of looping */ \ switch (voice->control & CONTROL_LOOPMASK) \ { \ /* non-looping */ \ case 0: \ voice->control |= CONTROL_STOP0; \ goto alldone; \ \ /* uni-directional looping */ \ case CONTROL_LPE: \ accum = voice->end - (voice->start - accum); \ break; \ \ /* trans-wave looping */ \ case CONTROL_BLE: \ accum = voice->end - (voice->start - accum); \ voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ break; \ \ /* bi-directional looping */ \ case CONTROL_LPE | CONTROL_BLE: \ accum = voice->start + (voice->start - accum); \ voice->control ^= CONTROL_DIR; \ goto reverse; \ } \ } \} while (0)/********************************************************************************************** generate_dummy -- generate nothing, just apply envelopes***********************************************************************************************/static void generate_dummy(struct ES5506Voice *voice, UINT16 *base, INT32 *lbuffer, INT32 *rbuffer, int samples){ UINT32 freqcount = voice->freqcount; UINT32 accum = voice->accum; /* outer loop, in case we switch directions */ while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) {reverse: /* two cases: first case is forward direction */ if (!(voice->control & CONTROL_DIR)) { /* loop while we still have samples to generate */ while (samples--) { /* fetch two samples */ accum += freqcount; /* update filters/volumes */ if (voice->ecount != 0) update_envelopes(voice, 1); /* check for loop end */ check_for_end_forward(voice, accum); } } /* two cases: second case is backward direction */ else { /* loop while we still have samples to generate */ while (samples--) { /* fetch two samples */ accum -= freqcount; /* update filters/volumes */ if (voice->ecount != 0) update_envelopes(voice, 1); /* check for loop end */ check_for_end_reverse(voice, accum); } } } /* if we stopped, process any additional envelope */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -