📄 sound.c
字号:
}
INLINE static void update_voice_pitch(int voice, struct voice_state *pvs,
unsigned char pitch_modulation_enable, unsigned char voice_bit)
{
#ifndef NO_PITCH_MODULATION
if (!(pitch_modulation_enable & voice_bit))
{
pvs->pitch_counter += pvs->step;
}
else
{
pvs->pitch_counter +=
pvs->step * (SNDvoices[voice - 1].outx + 32768) / 32768;
}
#else
pvs->pitch_counter += pvs->step;
#endif
}
#ifndef NO_PRELOAD_OUTPUT
#define STEREO_PRELOAD_OUTPUT asm volatile("movb (%%eax),%%al" : : "a" (buf + i * 2));
#define MONO_PRELOAD_OUTPUT asm volatile("movb (%%eax),%%al" : : "a" (buf + i));
#else
#define STEREO_PRELOAD_OUTPUT
#define MONO_PRELOAD_OUTPUT
#endif
#define STEREO_DECLARE_SAMPLES_NO_ECHO \
int mlsample, mrsample;
#define STEREO_INIT_SAMPLES_NO_ECHO \
(mlsample = mrsample = 0);
#define STEREO_DECLARE_SAMPLES_ECHO \
int mlsample, mrsample, elsample, ersample;
#define STEREO_INIT_SAMPLES_ECHO \
(mlsample = mrsample = elsample = ersample = 0);
#define MONO_DECLARE_SAMPLES_NO_ECHO \
int msample;
#define MONO_INIT_SAMPLES_NO_ECHO \
(msample = 0);
#define MONO_DECLARE_SAMPLES_ECHO \
int msample, esample;
#define MONO_INIT_SAMPLES_ECHO \
(msample = esample = 0);
#define SAMPLE_SET(S,D) ((S) = (D))
#define SAMPLE_ADD(S,D) ((S) += (D))
#define MONO_VOICE_VOLUME_NO_ECHO(OP) \
{ \
int jsample = (pvs->outx * (int) pvs->jvol); \
OP(msample, jsample); \
}
#define MONO_VOICE_VOLUME_ECHO(OP) \
{ \
int jsample = (pvs->outx * (int) pvs->jvol); \
OP(msample, jsample); \
if (SPC_DSP[DSP_EON] & voice_bit) \
{ \
OP(esample, jsample); \
} \
}
#define STEREO_VOICE_VOLUME_NO_ECHO(OP) \
{ \
int lsample = (pvs->outx * (int) pvs->lvol); \
int rsample = (pvs->outx * (int) pvs->rvol); \
OP(mlsample, lsample); \
OP(mrsample, rsample); \
}
#define STEREO_VOICE_VOLUME_ECHO(OP) \
{ \
int lsample = (pvs->outx * (int) pvs->lvol); \
int rsample = (pvs->outx * (int) pvs->rvol); \
OP(mlsample, lsample); \
OP(mrsample, rsample); \
if (SPC_DSP[DSP_EON] & voice_bit) \
{ \
OP(elsample, lsample); \
OP(ersample, rsample); \
} \
}
#define MONO_MAIN_VOLUME \
{ \
msample = (msample * main_jvol) >> 7; \
}
#define STEREO_MAIN_VOLUME \
{ \
mlsample = (mlsample * main_lvol) >> 7; \
mrsample = (mrsample * main_rvol) >> 7; \
}
#define MONO_COMPUTE_NO_ECHO
#define MONO_COMPUTE_ECHO \
{ \
int FIR_sample, FIR_temp_address; \
signed short *echo_ptr = (signed short *) \
&SPCRAM[(echo_base + echo_address) & 0xFFFF]; \
\
FIR_taps[FIR_address][0] = (echo_ptr[0] + echo_ptr[1]) >> 1; \
\
FIR_sample = FIR_taps[FIR_address][0] * FIR_coeff[0]; \
FIR_temp_address = (FIR_address + 1) & 7; \
FIR_sample += FIR_taps[FIR_temp_address][0] * FIR_coeff[1]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_sample += FIR_taps[FIR_temp_address][0] * FIR_coeff[2]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_sample += FIR_taps[FIR_temp_address][0] * FIR_coeff[3]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_sample += FIR_taps[FIR_temp_address][0] * FIR_coeff[4]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_sample += FIR_taps[FIR_temp_address][0] * FIR_coeff[5]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_sample += FIR_taps[FIR_temp_address][0] * FIR_coeff[6]; \
FIR_address = (FIR_temp_address + 1) & 7; \
FIR_sample += FIR_taps[FIR_address][0] * FIR_coeff[7]; \
msample += (FIR_sample * echo_jvol) >> 7; \
\
/* Store echo result with feedback if writes aren't disabled */ \
if (!(SPC_DSP[DSP_FLG] & DSP_FLG_NECEN)) \
{ \
FIR_sample *= echo_feedback; \
esample = (esample >> 7) + (FIR_sample >> 14); \
/* Saturate */ \
if (esample >= 32767) esample = 32767; \
else if (esample <= -32768) esample = -32768; \
echo_ptr[0] = echo_ptr[1] = esample; \
} \
\
echo_address += 4; \
if (echo_address >= echo_delay) echo_address = 0; \
}
#define STEREO_COMPUTE_NO_ECHO
#define STEREO_COMPUTE_ECHO \
{ \
int FIR_lsample, FIR_rsample, FIR_temp_address; \
short *echo_ptr = (signed short *) \
&SPCRAM[(echo_base + echo_address) & 0xFFFF]; \
\
FIR_taps[FIR_address][0] = echo_ptr[0]; \
FIR_taps[FIR_address][1] = echo_ptr[1]; \
\
FIR_lsample = FIR_taps[FIR_address][0] * FIR_coeff[0]; \
FIR_rsample = FIR_taps[FIR_address][1] * FIR_coeff[0]; \
FIR_temp_address = (FIR_address + 1) & 7; \
FIR_lsample += FIR_taps[FIR_temp_address][0] * FIR_coeff[1]; \
FIR_rsample += FIR_taps[FIR_temp_address][1] * FIR_coeff[1]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_lsample += FIR_taps[FIR_temp_address][0] * FIR_coeff[2]; \
FIR_rsample += FIR_taps[FIR_temp_address][1] * FIR_coeff[2]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_lsample += FIR_taps[FIR_temp_address][0] * FIR_coeff[3]; \
FIR_rsample += FIR_taps[FIR_temp_address][1] * FIR_coeff[3]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_lsample += FIR_taps[FIR_temp_address][0] * FIR_coeff[4]; \
FIR_rsample += FIR_taps[FIR_temp_address][1] * FIR_coeff[4]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_lsample += FIR_taps[FIR_temp_address][0] * FIR_coeff[5]; \
FIR_rsample += FIR_taps[FIR_temp_address][1] * FIR_coeff[5]; \
FIR_temp_address = (FIR_temp_address + 1) & 7; \
FIR_lsample += FIR_taps[FIR_temp_address][0] * FIR_coeff[6]; \
FIR_rsample += FIR_taps[FIR_temp_address][1] * FIR_coeff[6]; \
FIR_address = (FIR_temp_address + 1) & 7; \
FIR_lsample += FIR_taps[FIR_address][0] * FIR_coeff[7]; \
FIR_rsample += FIR_taps[FIR_address][1] * FIR_coeff[7]; \
mlsample += (FIR_lsample * echo_lvol) >> 7; \
mrsample += (FIR_rsample * echo_rvol) >> 7; \
\
/* Store echo result with feedback if writes aren't disabled */ \
if (!(SPC_DSP[DSP_FLG] & DSP_FLG_NECEN)) \
{ \
FIR_lsample *= echo_feedback; \
FIR_rsample *= echo_feedback; \
elsample = (elsample >> 7) + (FIR_lsample >> 14); \
ersample = (ersample >> 7) + (FIR_rsample >> 14); \
/* Saturate */ \
if (elsample >= 32767) elsample = 32767; \
else if (elsample <= -32768) elsample = -32768; \
if (ersample >= 32767) ersample = 32767; \
else if (ersample <= -32768) ersample = -32768; \
echo_ptr[0] = elsample; \
echo_ptr[1] = ersample; \
} \
\
echo_address += 4; \
if (echo_address >= echo_delay) echo_address = 0; \
}
#define SAMPLE_WRITE_ZERO(BITS,A) \
{ \
(A) = OUTPUT_ZERO_BASE_##BITS; \
}
#define MONO_SAMPLE_WRITE_ZERO(BITS) \
{ \
SAMPLE_WRITE_ZERO(BITS,buf[i]) \
}
#define STEREO_SAMPLE_WRITE_ZERO(BITS) \
{ \
SAMPLE_WRITE_ZERO(BITS,buf[i * 2]) \
SAMPLE_WRITE_ZERO(BITS,buf[i * 2 + 1]) \
}
#define SAMPLE_CLIP_AND_WRITE(BITS,A,S) \
{ \
if ((S) <= PREMIX_LOWER_LIMIT_##BITS) \
(S) = OUTPUT_LOWER_LIMIT_##BITS; \
else if ((S) >= PREMIX_UPPER_LIMIT_##BITS) \
(S) = OUTPUT_UPPER_LIMIT_##BITS; \
else (S) = OUTPUT_ZERO_BASE_##BITS + \
((S) >> PREMIX_SHIFT_##BITS); \
\
(A) = (S); \
}
#define MONO_SAMPLE_CLIP_AND_WRITE(BITS) \
SAMPLE_CLIP_AND_WRITE(BITS,buf[i],msample)
#define STEREO_SAMPLE_CLIP_AND_WRITE(BITS) \
SAMPLE_CLIP_AND_WRITE(BITS,buf[i * 2],mlsample) \
SAMPLE_CLIP_AND_WRITE(BITS,buf[i * 2 + 1],mrsample)
/* NOTE: Clip with sign-extension before last sample is added in */
#define GET_OUTX_GAUSS \
do \
{ \
pvs->outx = ( \
((( \
((((int) G4[-(pvs->pitch_counter >> 4)] * \
pvs->buf[(pvs->bufptr - 3) & 3]) >> 11) & ~1) + \
((((int) G3[-(pvs->pitch_counter >> 4)] * \
pvs->buf[(pvs->bufptr - 2) & 3]) >> 11) & ~1) + \
((((int) G2[(pvs->pitch_counter >> 4)] * \
pvs->buf[(pvs->bufptr - 1) & 3]) >> 11) & ~1)) \
& 0xFFFF) ^ 0x8000) - 0x8000) + \
((((int) G1[(pvs->pitch_counter >> 4)] * \
pvs->buf[(pvs->bufptr) & 3]) >> 11) & ~1); \
if (pvs->outx <= -32768) pvs->outx = -32768; \
else if (pvs->outx >= 32767) pvs->outx = 32767; \
} while (0)
#define GET_OUTX_NO_GAUSS pvs->outx = pvs->buf[(pvs->bufptr - 3) & 3]
#define MIX(BITS,CHANNELS,GAUSS,ECHO) \
{ \
/* int voice_samples_generated[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; */ \
/* int max_samples_generated; */ \
\
output_sample_##BITS *buf = \
(output_sample_##BITS *) sound_buffer_preload; \
\
unsigned i; \
int samples_left = samples; \
\
for (i = first; samples_left/*&& SNDkeys*/; samples_left--) \
{ \
CHANNELS##_DECLARE_SAMPLES_##ECHO \
\
CHANNELS##_PRELOAD_OUTPUT \
\
CHANNELS##_INIT_SAMPLES_##ECHO \
for (voice = 0, voice_bit = 1; voice < 8; voice++, voice_bit <<= 1) \
{ \
struct voice_state *pvs; \
if (!(SNDkeys & voice_bit)) continue; \
\
pvs = &SNDvoices[voice]; \
if (get_brr_blocks(voice,pvs)) continue; \
\
if (SPC_DSP[DSP_NON] & voice_bit) \
pvs->outx = noise_buffer [i]; \
else \
GET_OUTX_##GAUSS; \
\
pvs->outx &= ~1; \
\
update_voice_pitch(voice, pvs, pitch_modulation_enable, voice_bit); \
\
pvs->outx = (pvs->outx \
* (int) SoundGetEnvelopeHeight(voice)) >> ENVX_PRECISION_BITS; \
\
CHANNELS##_VOICE_VOLUME_##ECHO(SAMPLE_ADD) \
\
pvs->voice_sample_latch ++; \
\
} \
\
if (!(SPC_DSP[DSP_FLG] & DSP_FLG_MUTE)) \
{ \
CHANNELS##_MAIN_VOLUME \
CHANNELS##_COMPUTE_##ECHO \
CHANNELS##_SAMPLE_CLIP_AND_WRITE(BITS) \
} \
else \
{ \
CHANNELS##_COMPUTE_##ECHO \
CHANNELS##_SAMPLE_WRITE_ZERO(BITS) \
} \
\
if (++i >= buffer_size) \
i = 0; \
} \
\
for (; samples_left; samples_left--) \
{ \
CHANNELS##_DECLARE_SAMPLES_##ECHO \
\
CHANNELS##_PRELOAD_OUTPUT \
\
CHANNELS##_INIT_SAMPLES_##ECHO \
\
CHANNELS##_COMPUTE_##ECHO \
\
if (!(SPC_DSP[DSP_FLG] & DSP_FLG_MUTE)) \
{ \
CHANNELS##_SAMPLE_CLIP_AND_WRITE(BITS) \
} \
else \
{ \
CHANNELS##_SAMPLE_WRITE_ZERO(BITS) \
} \
\
if (++i >= buffer_size) \
i = 0; \
} \
\
sound_output_position = i; \
}
/*
Echo notes
Output sample generation is shared between main and echo outputs until
after channel multiplication. All channels are then mixed into the main
output, and those enabled (via DSP EON register) for echo are mixed for
the echo region.
The echo region receives the resulting sum of the aforementioned and
mixed channel output and the product of the result of the FIR filter
multiplied by the echo feedback (DSP EFB register) setting.
The echo output begins with the address in the echo region that the new
echo data will be written to. That address is read (two 16-bit stereo
samples) and placed in a queue for the FIR filter.
The eight values in the FIR filter queue are then multiplied
respectively by the eight FIR filter coefficients (DSP C0-C7 registers)
from oldest to newest.
The resulting value is multipled by echo feedback to be added to the
channel mix output as described above; in addition, it is multiplied by
the echo output volume and mixed with the main output, played by the
output device.
The echo region address is reset to 0 when it has reached or exceeded
the buffer length for the specified echo delay (DSP EDL register),
which is EDL * 2048 bytes (EDL * 512 samples).
*/
#define MIX_BITS(BITS) \
{ \
if (stereo) \
/* emulate audio in stereo */ \
{ \
MIX_CHANNELS(BITS,STEREO) \
} \
else \
/* emulate audio in mono */ \
{ \
MIX_CHANNELS(BITS,MONO) \
} \
}
#define MIX_CHANNELS(BITS,CHANNELS) \
{ \
if (sound_gauss_enabled) \
/* emulate 4-point pitch-regulated gaussian interpolation */ \
{ \
MIX_GAUSS(BITS,CHANNELS,GAUSS) \
} \
else \
/* no interpolation */ \
{ \
MIX_GAUSS(BITS,CHANNELS,NO_GAUSS) \
} \
}
#define MIX_GAUSS(BITS,CHANNELS,GAUSS) \
{ \
if (sound_echo_enabled) \
/* emulate echo with corresponding FIR filter and SPC RAM update */ \
{ \
MIX(BITS,CHANNELS,GAUSS,ECHO) \
} \
else \
/* no echo emulation */ \
{ \
MIX(BITS,CHANNELS,GAUSS,NO_ECHO) \
} \
}
void mix_voices(unsigned first, unsigned samples, unsigned buffer_size,
int sound_bits, int stereo)
{
int voice;
unsigned char voice_bit;
unsigned char pitch_modulation_enable;
// pitch modulation is not available for noise channels or channel 0
pitch_modulation_enable = SPC_DSP[DSP_PMON] & ~SPC_DSP[DSP_NON] & ~1;
#ifdef FAULT_ON_PITCH_MODULATION_USE
if (pitch_modulation_enable & SNDkeys) asm("ud2");
#endif
if (sound_bits == 8)
/* 8-bit */
{
MIX_BITS(8)
}
else
/* 16-bit */
{
MIX_BITS(16)
}
}
/* update_sound()
* This function is called to synchronize the sound DSP sample
* generation to the current SPC700 CPU timing.
* Active voices are processed and mixed into the sound buffer.
* This function MUST be called often enough that no more than one
* complete buffer of samples are processed per call.
*/
void update_sound(void)
{
unsigned first, samples, buffer_size;
int voice;
unsigned char voices_were_on;
unsigned char voice_bit;
if (!SPC_ENABLED) return;
samples = ((TotalCycles + SOUND_CYCLES_PER_SAMPLE) -
sound_cycle_latch) / SOUND_CYCLES_PER_SAMPLE;
if (!samples) return;
SPC_KeyOn(SPC_DSP[DSP_KON]);
SPC_KeyOff(SPC_DSP[DSP_KOF]);
sound_cycle_latch = (TotalCycles + SOUND_CYCLES_PER_SAMPLE) &
~(SOUND_CYCLES_PER_SAMPLE - 1);
sound_sample_latch += samples;
if (!sound_enabled) return;
samples_output += samples;
first = sound_output_position;
buffer_size = 2 * SOUND_FREQ / SOUND_LAG;
// Are we completing a block?
if (((first % (SOUND_FREQ / SOUND_LAG)) + samples) >=
(SOUND_FREQ / SOUND_LAG))
{
block_written = FALSE;
#ifdef DUMP_SOUND
block_dumped = FALSE;
#endif
}
if (SNDkeys & ~SPC_MASK)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -