📄 ymz280b.c
字号:
/********************************************************************************************** * * Yamaha YMZ280B driver * by Aaron Giles * **********************************************************************************************/#include <stdio.h>#include <stdlib.h>#include <math.h>#include "driver.h"#include "sasound.h"#include "savegame.h"#include "loadroms.h"#define MAX_SAMPLE_CHUNK 10000#define FRAC_BITS 14#define FRAC_ONE (1 << FRAC_BITS)#define FRAC_MASK (FRAC_ONE - 1)/* struct describing a single playing ADPCM voice */struct YMZ280BVoice{ UINT8 playing; /* 1 if we are actively playing */ UINT8 keyon; /* 1 if the key is on */ UINT8 looping; /* 1 if looping is enabled */ UINT8 mode; /* current playback mode */ UINT16 fnum; /* frequency */ UINT8 level; /* output level */ UINT8 pan; /* panning */ UINT32 start; /* start address, in nibbles */ UINT32 stop; /* stop address, in nibbles */ UINT32 loop_start; /* loop start address, in nibbles */ UINT32 loop_end; /* loop end address, in nibbles */ UINT32 position; /* current position, in nibbles */ INT32 signal; /* current ADPCM signal */ INT32 step; /* current ADPCM step */ INT32 loop_signal; /* signal at loop start */ INT32 loop_step; /* step at loop start */ UINT32 loop_count; /* number of loops so far */ INT32 output_left; /* output volume (left) */ INT32 output_right; /* output volume (right) */ INT32 output_step; /* step value for frequency conversion */ INT32 output_pos; /* current fractional position */ INT16 last_sample; /* last sample output */ INT16 curr_sample; /* current sample target */};struct YMZ280BChip{ int stream; /* which stream are we using */ UINT8 *region_base; /* pointer to the base of the region */ UINT8 current_register; /* currently accessible register */ UINT8 status_register; /* current status register */ UINT8 irq_state; /* current IRQ state */ UINT8 irq_mask; /* current IRQ mask */ UINT8 irq_enable; /* current IRQ enable */ UINT8 keyon_enable; /* key on enable */ double master_clock; /* master clock frequency */ void (*irq_callback)(int); /* IRQ callback */ struct YMZ280BVoice voice[8]; /* the 8 voices */};static struct YMZ280BChip ymz280b[MAX_YMZ280B];static INT32 *accumulator;static INT16 *scratch;/* step size index shift table */static int index_scale[8] = { 0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266 };/* lookup table for the precomputed difference */static int diff_lookup[16];INLINE void update_irq_state(struct YMZ280BChip *chip){ 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); }}INLINE void update_step(struct YMZ280BChip *chip, struct YMZ280BVoice *voice){ double frequency; /* handle the sound-off case */ if (audio_sample_rate == 0) { voice->output_step = 0; return; } /* compute the frequency */ if (voice->mode == 1) frequency = chip->master_clock * (double)((voice->fnum & 0x0ff) + 1) * (1.0 / 256.0); else frequency = chip->master_clock * (double)((voice->fnum & 0x1ff) + 1) * (1.0 / 256.0); voice->output_step = (UINT32)(frequency * (double)FRAC_ONE / (double)audio_sample_rate);}INLINE void update_volumes(struct YMZ280BVoice *voice){ if (voice->pan == 8) { voice->output_left = voice->level; voice->output_right = voice->level; } else if (voice->pan < 8) { voice->output_left = voice->level; voice->output_right = voice->level * voice->pan / 8; } else { voice->output_left = voice->level * (15 - voice->pan) / 8; voice->output_right = voice->level; }}/********************************************************************************************** compute_tables -- compute the difference tables***********************************************************************************************/static void compute_tables(void){ int nib; /* loop over all nibbles and compute the difference */ for (nib = 0; nib < 16; nib++) { int value = (nib & 0x07) * 2 + 1; diff_lookup[nib] = (nib & 0x08) ? -value : value; }}/********************************************************************************************** generate_adpcm -- general ADPCM decoding routine***********************************************************************************************/static int generate_adpcm(struct YMZ280BVoice *voice, UINT8 *base, INT16 *buffer, int samples){ int position = voice->position; int signal = voice->signal; int step = voice->step; int val; /* two cases: first cases is non-looping */ if (!voice->looping) { /* loop while we still have samples to generate */ while (samples) { /* compute the new amplitude and update the current step */ val = base[position / 2] >> ((~position & 1) << 2); signal += (step * diff_lookup[val & 15]) / 8; /* clamp to the maximum */ if (signal > 32767) signal = 32767; else if (signal < -32768) signal = -32768; /* adjust the step size and clamp */ step = (step * index_scale[val & 7]) >> 8; if (step > 0x6000) step = 0x6000; else if (step < 0x7f) step = 0x7f; /* output to the buffer, scaling by the volume */ *buffer++ = signal; samples--; /* next! */ position++; if ((UINT32)position >= voice->stop) break; } } /* second case: looping */ else { /* loop while we still have samples to generate */ while (samples) { /* compute the new amplitude and update the current step */ val = base[position / 2] >> ((~position & 1) << 2); signal += (step * diff_lookup[val & 15]) / 8; /* clamp to the maximum */ if (signal > 32767) signal = 32767; else if (signal < -32768) signal = -32768; /* adjust the step size and clamp */ step = (step * index_scale[val & 7]) >> 8; if (step > 0x6000) step = 0x6000; else if (step < 0x7f) step = 0x7f; /* output to the buffer, scaling by the volume */ *buffer++ = signal; samples--; /* next! */ position++; if ((UINT32)position == voice->loop_start && voice->loop_count == 0) { voice->loop_signal = signal; voice->loop_step = step; } if ((UINT32)position >= voice->loop_end) { if (voice->keyon) { position = voice->loop_start; signal = voice->loop_signal; step = voice->loop_step; voice->loop_count++; } } if ((UINT32)position >= voice->stop) break; } } /* update the parameters */ voice->position = position; voice->signal = signal; voice->step = step; return samples;}/********************************************************************************************** generate_pcm8 -- general 8-bit PCM decoding routine***********************************************************************************************/static int generate_pcm8(struct YMZ280BVoice *voice, UINT8 *base, INT16 *buffer, int samples){ int position = voice->position; int val; /* two cases: first cases is non-looping */ if (!voice->looping) { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ val = base[position / 2]; /* output to the buffer, scaling by the volume */ *buffer++ = (INT8)val * 256; samples--; /* next! */ position += 2; if ((UINT32)position >= voice->stop) break; } } /* second case: looping */ else { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ val = base[position / 2]; /* output to the buffer, scaling by the volume */ *buffer++ = (INT8)val * 256; samples--; /* next! */ position += 2; if ((UINT32)position >= voice->loop_end) { if (voice->keyon) position = voice->loop_start; } if ((UINT32)position >= voice->stop) break; } } /* update the parameters */ voice->position = position; return samples;}/********************************************************************************************** generate_pcm16 -- general 16-bit PCM decoding routine***********************************************************************************************/static int generate_pcm16(struct YMZ280BVoice *voice, UINT8 *base, INT16 *buffer, int samples){ int position = voice->position; int val; /* two cases: first cases is non-looping */ if (!voice->looping) { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ val = (INT16)((base[position / 2 + 1] << 8) + base[position / 2]); /* output to the buffer, scaling by the volume */ *buffer++ = val; samples--; /* next! */ position += 4; if ((UINT32)position >= voice->stop) break; } } /* second case: looping */ else { /* loop while we still have samples to generate */ while (samples) { /* fetch the current value */ val = (INT16)((base[position / 2 + 1] << 8) + base[position / 2]); /* output to the buffer, scaling by the volume */ *buffer++ = val; samples--; /* next! */ position += 4; if ((UINT32)position >= voice->loop_end) { if (voice->keyon) position = voice->loop_start; } if ((UINT32)position >= voice->stop) break; } } /* update the parameters */ voice->position = position; return samples;}/********************************************************************************************** ymz280b_update -- update the sound chip so that it is in sync with CPU execution***********************************************************************************************/static void ymz280b_update(int num, INT16 **buffer, int length){ struct YMZ280BChip *chip = &ymz280b[num]; INT32 *lacc = accumulator; INT32 *racc = accumulator + length; int v; /* clear out the accumulator */ memset(accumulator, 0, 2 * length * sizeof(accumulator[0])); /* loop over voices */ for (v = 0; v < 8; v++) { struct YMZ280BVoice *voice = &chip->voice[v]; INT16 prev = voice->last_sample; INT16 curr = voice->curr_sample; INT16 *curr_data = scratch; INT32 *ldest = lacc; INT32 *rdest = racc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -