📄 sound.c
字号:
SPC_VoicesOff(~SPC_MASK, "SPC_MASK");
}
if (SNDkeys & SPC_DSP[DSP_NON])
{
unsigned i;
int samples_left = samples;
for (i = first; samples_left && noise_update_count; samples_left--)
{
noise_countdown -= noise_update_count;
if (noise_countdown <= 0)
{
noise_countdown = apu_counter_reset_value;
noise_vol = rand();
}
noise_buffer [i] = noise_vol;
if (++i >= buffer_size)
i = 0;
}
for (; samples_left; samples_left--)
{
noise_buffer [i] = noise_vol;
if (++i >= buffer_size)
i = 0;
}
}
voices_were_on = SNDkeys;
main_jvol = (main_lvol + main_rvol) >> 1;
echo_jvol = (echo_lvol + echo_rvol) >> 1;
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 (sound_enabled == 1)
{
pvs->jvol = (pvs->lvol + pvs->rvol) >> 1;
}
pvs->step = ((unsigned) *(unsigned short *)&SPC_DSP[(voice << 4) + DSP_VOICE_PITCH_L]);
}
/* MMX mixing notes
* PMADDWD takes two sets of four 16-bit words. abcd(r), efgh(r/m).
* Each 16-bit word in source is multiplied by its respective word in
* destination. Then the high 2 results are added and stored as the
* high 32-bit result, and the low 2 results are added and stored
* as the low 32-bit result.
*
* For audio channel mixing, propose that source contain channel volumes
* and destination contain channel sample height.
*
* In Stereo, high pair of 16-bit words are for two channels, left side,
* and low pair of 16-bit words are for two channels, right side.
*
* In Mono, instead of left and right side, two independent samples
* are processed at once.
*
* After all channels have been volume-adjusted, PADDD is used to combine
* them, and PACKSSDW converts them back to 16-bit samples for output.
*/
mix_voices(first, samples, buffer_size, sound_bits,
sound_enabled == 2 ? 1 : 0);
for (voice = 0, voice_bit = 1; voice < 8; voice++, voice_bit <<= 1)
{
if (voices_were_on & voice_bit)
{
/* If voice was on, but turned off for any reason */
if (!(SNDkeys & voice_bit))
{
SPC_VoiceOff(voice, "?");
}
else
{
/* If voice was on, and is still on */
SPC_DSP[(voice << 4) + DSP_VOICE_OUTX] =
SNDvoices[voice].outx;
}
}
}
}
void SPC_READ_DSP()
{
int addr_lo = SPC_DSP_ADDR & 0xF;
int addr_hi = SPC_DSP_ADDR >> 4;
#ifdef LOG_SOUND_DSP_READ
printf("\nread @ %08X,%04X: %02X", TotalCycles, (unsigned) _SPC_PC, (unsigned) SPC_DSP_ADDR);
#endif
#ifdef DSP_SPEED_HACK
/* if we're not reading endx */
if (SPC_DSP_ADDR != DSP_ENDX)
{
/* if we're reading envx or outx but voice is off */
if (addr_lo == DSP_VOICE_ENVX || addr_lo == DSP_VOICE_OUTX)
{
if (!(SNDkeys & (1 << addr_hi)))
{
#ifdef LOG_SOUND_DSP_READ
printf(" %02X", SPC_DSP[SPC_DSP_ADDR]);
#endif
return;
}
}
else
{
#ifdef LOG_SOUND_DSP_READ
printf(" %02X", SPC_DSP[SPC_DSP_ADDR]);
#endif
return;
}
}
#endif
// printf("\nSound update");
update_sound();
switch(addr_lo)
{
case DSP_VOICE_ENVX:
if (!sound_enabled) SNDvoices[addr_hi].voice_sample_latch =
sound_sample_latch;
#if defined(ZERO_ENVX_ON_VOICE_OFF) && !defined(DSP_SPEED_HACK)
if (ENVX_ENABLED && (SNDkeys & (1 << addr_hi)))
#else
if (ENVX_ENABLED)
#endif
UpdateEnvelopeHeight(addr_hi); // >> ENVX_DOWNSHIFT_BITS;
else
SPC_DSP[SPC_DSP_ADDR] = 0;
break;
}
#ifdef LOG_SOUND_DSP_READ
printf(" %02X", SPC_DSP[SPC_DSP_ADDR]);
#endif
}
void SPC_WRITE_DSP()
{
int i;
int addr_lo = SPC_DSP_ADDR & 0xF;
int addr_hi = SPC_DSP_ADDR >> 4;
if (addr_hi > 7)
{
SPC_DSP[SPC_DSP_ADDR] = SPC_DSP_DATA;
return;
}
#ifdef LOG_SOUND_DSP_WRITE
printf("\nwrite @ %08X,%04X: %02X %02X", TotalCycles, (unsigned) _SPC_PC,
(unsigned) SPC_DSP_ADDR, (unsigned) SPC_DSP_DATA);
#endif
#ifdef DSP_SPEED_HACK
/* if we're not writing to flg or endx */
if (addr_lo != 0x0C || addr_hi < 6)
/* and write would not change data, return */
if (SPC_DSP[SPC_DSP_ADDR] == SPC_DSP_DATA) return;
/* if it's not a voice register for a voice that's not on */
if (addr_lo > 9 ||
((SNDkeys | (SPC_DSP[DSP_KON] & ~SPC_DSP[DSP_KOF])) & (1 << addr_hi)))
{
#endif
update_sound();
//printf("\nSound update");
#ifdef DSP_SPEED_HACK
}
#endif
switch (addr_lo)
{
// break just means nothing needs to be done right now
// Commented cases are unsupported registers, or do-nothing cases
// Channel - volume left
case DSP_VOICE_LVOL:
SNDvoices[addr_hi].lvol = (signed char) SPC_DSP_DATA;
break;
// Channel - volume right
case DSP_VOICE_RVOL:
SNDvoices[addr_hi].rvol = (signed char) SPC_DSP_DATA;
break;
/*
// Channel - pitch low bits (0-7)
case DSP_VOICE_PITCH_L:
break;
*/
#ifdef MASK_PITCH_H
// Channel - pitch high bits (8-13)
case DSP_VOICE_PITCH_H:
SPC_DSP_DATA &= 0x3F;
break;
#endif
/*
// Channel - source number
case DSP_VOICE_SRCN:
break;
*/
// Channel - ADSR 1
case DSP_VOICE_ADSR1:
if (!sound_enabled) SNDvoices[addr_hi].voice_sample_latch =
sound_sample_latch;
if (SNDkeys & (1 << addr_hi)) UpdateEnvelopeHeight(addr_hi);
SNDvoices[addr_hi].ar = attack_time(SPC_DSP_DATA & 0xF);
SNDvoices[addr_hi].dr = decay_time((SPC_DSP_DATA >> 4) & 7);
/* If voice releasing or not playing, nothing else to update */
if (!(SNDkeys & (1 << addr_hi)) ||
SNDvoices[addr_hi].env_state == RELEASE) break;
if (SNDvoices[addr_hi].env_state == ATTACK)
SNDvoices[addr_hi].env_update_count = SNDvoices[addr_hi].ar;
else if (SNDvoices[addr_hi].env_state == DECAY)
SNDvoices[addr_hi].env_update_count = SNDvoices[addr_hi].dr;
if (SPC_DSP_DATA & 0x80)
{
// switch to ADSR (use old state if voice was switched since last key on)
if (!(SPC_DSP[SPC_DSP_ADDR] & 0x80))
{
SNDvoices[addr_hi].env_state = SNDvoices[addr_hi].adsr_state;
switch (SNDvoices[addr_hi].env_state)
{
case ATTACK:
SNDvoices[addr_hi].env_update_count = SNDvoices[addr_hi].ar;
break;
case DECAY:
SNDvoices[addr_hi].env_update_count = SNDvoices[addr_hi].dr;
break;
case SUSTAIN:
SNDvoices[addr_hi].env_update_count = SNDvoices[addr_hi].sr;
break;
}
}
}
else
{
// switch to a GAIN mode
i = SPC_DSP[(addr_hi << 4) + DSP_VOICE_GAIN];
SNDvoices[addr_hi].env_update_count =
SNDvoices[addr_hi].gain_update_count;
if (i & 0x80)
{
SNDvoices[addr_hi].env_state = i >> 5;
}
else
{
SNDvoices[addr_hi].env_state = DIRECT;
SPC_DSP[(addr_hi << 4) + DSP_VOICE_ENVX] = (i & 0x7F);
SNDvoices[addr_hi].envx = (i & 0x7F) << ENVX_DOWNSHIFT_BITS;
}
}
break;
// Channel - ADSR 2
case DSP_VOICE_ADSR2:
if (!sound_enabled) SNDvoices[addr_hi].voice_sample_latch =
sound_sample_latch;
if (SNDkeys & (1 << addr_hi)) UpdateEnvelopeHeight(addr_hi);
SNDvoices[addr_hi].sr = exp_time(SPC_DSP_DATA & 0x1F);
SNDvoices[addr_hi].sl = ((SPC_DSP_DATA >> 5) == 7) ? ENVX_MAX :
(ENVX_MAX_BASE / 8) * ((SPC_DSP_DATA >> 5) + 1);
if (SNDvoices[addr_hi].env_state == SUSTAIN)
SNDvoices[addr_hi].env_update_count = SNDvoices[addr_hi].sr;
break;
// Channel - GAIN
case DSP_VOICE_GAIN:
if (!sound_enabled) SNDvoices[addr_hi].voice_sample_latch =
sound_sample_latch;
if (SNDkeys & (1 << addr_hi)) UpdateEnvelopeHeight(addr_hi);
if (SPC_DSP_DATA & 0x80)
{
switch (SPC_DSP_DATA >> 5)
{
case INCREASE:
case DECREASE:
SNDvoices[addr_hi].gain_update_count =
linear_time(SPC_DSP_DATA & 0x1F);
break;
case BENT:
SNDvoices[addr_hi].gain_update_count =
bent_time(SPC_DSP_DATA & 0x1F);
break;
case EXP:
SNDvoices[addr_hi].gain_update_count =
exp_time(SPC_DSP_DATA & 0x1F);
}
}
else
{
SNDvoices[addr_hi].gain_update_count = 0;
}
/* If voice releasing or not playing, nothing else to update */
if (!(SNDkeys & (1 << addr_hi)) ||
SNDvoices[addr_hi].env_state == RELEASE) break;
/* is gain enabled? */
if (!(SPC_DSP[(addr_hi << 4) + DSP_VOICE_ADSR1] & 0x80))
{
SNDvoices[addr_hi].env_update_count =
SNDvoices[addr_hi].gain_update_count;
if (SPC_DSP_DATA & 0x80)
{
SNDvoices[addr_hi].env_state = SPC_DSP_DATA >> 5;
}
else
{
SNDvoices[addr_hi].env_state = DIRECT;
SPC_DSP[(addr_hi << 4) + DSP_VOICE_ENVX] = (SPC_DSP_DATA & 0x7F);
SNDvoices[addr_hi].envx = (SPC_DSP_DATA & 0x7F) << ENVX_DOWNSHIFT_BITS;
}
}
break;
case DSP_VOICE_ENVX:
case DSP_VOICE_OUTX:
#ifdef DISALLOW_ENVX_OUTX_WRITE
return;
#else
break;
#endif
// These are general registers
case 0xC:
switch (addr_hi)
{
// Main volume - left
case DSP_MAIN_LVOL >> 4:
main_lvol = (signed char) SPC_DSP_DATA;
break;
// Main volume - right
case DSP_MAIN_RVOL >> 4:
main_rvol = (signed char) SPC_DSP_DATA;
break;
// Echo volume - left
case DSP_ECHO_LVOL >> 4:
echo_lvol = (signed char) SPC_DSP_DATA;
break;
// Echo volume - right
case DSP_ECHO_RVOL >> 4:
echo_rvol = (signed char) SPC_DSP_DATA;
break;
// Key on
case DSP_KON >> 4:
break;
// Key off
case DSP_KOF >> 4:
break;
// Reset, mute, echo enable, noise clock select
case DSP_FLG >> 4:
noise_update_count = counter_update_table[SPC_DSP_DATA & 0x1F];
if (SPC_DSP_DATA & DSP_FLG_RESET)
{
int voice;
SPC_DSP[DSP_FLG] = SPC_DSP_DATA | DSP_FLG_NECEN | DSP_FLG_MUTE;
for (voice = 0; voice < 7; voice++)
{
SPC_VoicesOff(SNDkeys, "DSP reset");
}
SPC_DSP[DSP_ENDX] = 0;
SPC_DSP[DSP_KON] = 0;
SPC_DSP[DSP_KOF] = 0;
}
break;
// Sample end-block decoded
case DSP_ENDX >> 4:
SPC_DSP_DATA = 0;
break;
}
break;
case 0xD:
switch (addr_hi)
{
// Echo Feedback
case DSP_EFB >> 4:
echo_feedback = (signed char) SPC_DSP_DATA;
break;
// Pitch modulation
//case DSP_PMON >> 4:
// break;
// Noise enable
//case DSP_NON >> 4:
// break;
// Echo enable
//case DSP_EON >> 4:
// break;
// Source directory address
//case DSP_DIR >> 4:
// break;
// Echo start address
case DSP_ESA >> 4:
echo_base = (unsigned) SPC_DSP_DATA << 8;
break;
// Echo delay
case DSP_EDL >> 4:
echo_delay = (SPC_DSP_DATA & 0x0F) << 11;
break;
}
break;
// FIR echo filter
case 0xF:
FIR_coeff[7 - addr_hi] = (signed char) SPC_DSP_DATA;
break;
}
SPC_DSP[SPC_DSP_ADDR] = SPC_DSP_DATA;
}
void sound_pause(void)
{
if (sound_enabled) voice_stop(stream_buffer->voice);
}
void sound_resume(void)
{
if (sound_enabled) voice_start(stream_buffer->voice);
}
#ifdef ALLEGRO_DOS
BEGIN_DIGI_DRIVER_LIST
DIGI_DRIVER_SOUNDSCAPE
DIGI_DRIVER_AUDIODRIVE
DIGI_DRIVER_WINSOUNDSYS
DIGI_DRIVER_SB
END_DIGI_DRIVER_LIST
BEGIN_MIDI_DRIVER_LIST
END_MIDI_DRIVER_LIST
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -