AY: this sucks, part 1

This commit is contained in:
tildearrow 2025-03-07 20:19:24 -05:00
parent cb67527103
commit 9681f25e54
4 changed files with 215 additions and 171 deletions

View file

@ -248,40 +248,74 @@ void DivPlatformAY8910::checkWrites() {
} }
} }
void DivPlatformAY8910::acquire_mame(short** buf, size_t len) { void DivPlatformAY8910::acquire_mame(blip_buffer_t** bb, size_t len) {
thread_local short ayBuf[3]; thread_local short ayBuf[3];
for (int i=0; i<3; i++) { for (int i=0; i<3; i++) {
oscBuf[i]->begin(len); oscBuf[i]->begin(len);
} }
if (sunsoft) { for (size_t i=0; i<len; i++) {
for (size_t i=0; i<len; i++) { int advance=len-i;
runDAC(); // heuristic
runTFX(); for (int j=0; j<3; j++) {
checkWrites(); // tone counter
const int period=MAX(1,ay->m_tone[j].period)*(ay->m_step_mul<<1);
const int remain=period-ay->m_tone[j].count;
if (remain<advance) advance=remain;
ay->sound_stream_update(ayBuf,1); // envelope
buf[0][i]=ayBuf[0]; if (j<1) {
buf[1][i]=buf[0][i]; if (ay->m_envelope[j].holding==0) {
const int periodEnv=MAX(1,ay->m_envelope[j].period)*ay->m_env_step_mul;
const int remainEnv=periodEnv-ay->m_envelope[j].count;
if (remainEnv<advance) advance=remainEnv;
}
}
}
// noise
const int noisePeriod=((int)ay->noise_period())*ay->m_step_mul;
const int noiseRemain=noisePeriod-ay->m_count_noise;
if (noiseRemain<advance) advance=noiseRemain;
//runDAC();
//runTFX();
if (!writes.empty() || advance<1) advance=1;
checkWrites();
ay->sound_stream_update(ayBuf,advance);
i+=advance-1;
if (sunsoft) {
if (lastOut[0]!=ayBuf[0]) {
blip_add_delta(bb[0],i,ayBuf[0]-lastOut[0]);
blip_add_delta(bb[1],i,ayBuf[0]-lastOut[0]);
lastOut[0]=ayBuf[0];
}
oscBuf[0]->putSample(i,CLAMP(sunsoftVolTable[31-(ay->lastIndx&31)]<<3,-32768,32767)); oscBuf[0]->putSample(i,CLAMP(sunsoftVolTable[31-(ay->lastIndx&31)]<<3,-32768,32767));
oscBuf[1]->putSample(i,CLAMP(sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<3,-32768,32767)); oscBuf[1]->putSample(i,CLAMP(sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<3,-32768,32767));
oscBuf[2]->putSample(i,CLAMP(sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3,-32768,32767)); oscBuf[2]->putSample(i,CLAMP(sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3,-32768,32767));
} } else {
} else {
for (size_t i=0; i<len; i++) {
runDAC();
runTFX();
checkWrites();
ay->sound_stream_update(ayBuf,1);
if (stereo) { if (stereo) {
buf[0][i]=ayBuf[0]+ayBuf[1]+((ayBuf[2]*stereoSep)>>8); int out0=ayBuf[0]+ayBuf[1]+((ayBuf[2]*stereoSep)>>8);
buf[1][i]=((ayBuf[0]*stereoSep)>>8)+ayBuf[1]+ayBuf[2]; int out1=((ayBuf[0]*stereoSep)>>8)+ayBuf[1]+ayBuf[2];
if (lastOut[0]!=out0) {
blip_add_delta(bb[0],i,out0-lastOut[0]);
lastOut[0]=out0;
}
if (lastOut[1]!=out1) {
blip_add_delta(bb[1],i,out1-lastOut[1]);
lastOut[1]=out1;
}
} else { } else {
buf[0][i]=ayBuf[0]+ayBuf[1]+ayBuf[2]; int out=ayBuf[0]+ayBuf[1]+ayBuf[2];
buf[1][i]=buf[0][i]; if (lastOut[0]!=out) {
blip_add_delta(bb[0],i,out-lastOut[0]);
blip_add_delta(bb[1],i,out-lastOut[0]);
lastOut[0]=out;
}
} }
oscBuf[0]->putSample(i,ayBuf[0]<<2); oscBuf[0]->putSample(i,ayBuf[0]<<2);
@ -330,11 +364,14 @@ void DivPlatformAY8910::acquire_atomic(short** buf, size_t len) {
} }
} }
void DivPlatformAY8910::acquireDirect(blip_buffer_t** bb, size_t len) {
if (selCore && !intellivision) return;
acquire_mame(bb,len);
}
void DivPlatformAY8910::acquire(short** buf, size_t len) { void DivPlatformAY8910::acquire(short** buf, size_t len) {
if (selCore && !intellivision) { if (selCore && !intellivision) {
acquire_atomic(buf,len); acquire_atomic(buf,len);
} else {
acquire_mame(buf,len);
} }
} }
@ -1011,6 +1048,8 @@ void DivPlatformAY8910::reset() {
ayEnvSlideLow=0; ayEnvSlideLow=0;
delay=0; delay=0;
lastOut[0]=0;
lastOut[1]=0;
ioPortA=false; ioPortA=false;
ioPortB=false; ioPortB=false;
@ -1026,6 +1065,10 @@ bool DivPlatformAY8910::keyOffAffectsArp(int ch) {
return true; return true;
} }
bool DivPlatformAY8910::hasAcquireDirect() {
return (!selCore || intellivision);
}
bool DivPlatformAY8910::getLegacyAlwaysSetVolume() { bool DivPlatformAY8910::getLegacyAlwaysSetVolume() {
return false; return false;
} }

View file

@ -128,6 +128,7 @@ class DivPlatformAY8910: public DivDispatch {
ssg_t ay_atomic; ssg_t ay_atomic;
int delay; int delay;
int lastOut[2];
bool extMode; bool extMode;
unsigned int extClock; unsigned int extClock;
@ -149,7 +150,7 @@ class DivPlatformAY8910: public DivDispatch {
void checkWrites(); void checkWrites();
void updateOutSel(bool immediate=false); void updateOutSel(bool immediate=false);
void acquire_mame(short** buf, size_t len); void acquire_mame(blip_buffer_t** bb, size_t len);
void acquire_atomic(short** buf, size_t len); void acquire_atomic(short** buf, size_t len);
friend void putDispatchChip(void*,int); friend void putDispatchChip(void*,int);
@ -160,6 +161,7 @@ class DivPlatformAY8910: public DivDispatch {
void runTFX(int runRate=0); void runTFX(int runRate=0);
void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8); void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8);
void acquire(short** buf, size_t len); void acquire(short** buf, size_t len);
void acquireDirect(blip_buffer_t** bb, size_t len);
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len); void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
int dispatch(DivCommand c); int dispatch(DivCommand c);
void* getChanState(int chan); void* getChanState(int chan);
@ -177,6 +179,7 @@ class DivPlatformAY8910: public DivDispatch {
void setFlags(const DivConfig& flags); void setFlags(const DivConfig& flags);
int getOutputCount(); int getOutputCount();
bool keyOffAffectsArp(int ch); bool keyOffAffectsArp(int ch);
bool hasAcquireDirect();
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
DivSamplePos getSamplePos(int ch); DivSamplePos getSamplePos(int ch);
bool getLegacyAlwaysSetVolume(); bool getLegacyAlwaysSetVolume();

View file

@ -1035,167 +1035,165 @@ void ay8910_device::ay8910_write_reg(int r, int v)
// sound_stream_update - handle a stream update // sound_stream_update - handle a stream update
//------------------------------------------------- //-------------------------------------------------
void ay8910_device::sound_stream_update(short* outputs, int outLen) void ay8910_device::sound_stream_update(short* outputs, int advance)
{ {
tone_t *tone; tone_t *tone;
envelope_t *envelope; envelope_t *envelope;
int samples = outLen; /* hack to prevent us from hanging when starting filtered outputs */
if (!m_ready)
{
for (int chan = 0; chan < m_streams; chan++)
outputs[chan]=0;
}
/* hack to prevent us from hanging when starting filtered outputs */ /* The 8910 has three outputs, each output is the mix of one of the three */
if (!m_ready) /* tone generators and of the (single) noise generator. The two are mixed */
{ /* BEFORE going into the DAC. The formula to mix each channel is: */
for (int chan = 0; chan < m_streams; chan++) /* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */
outputs[chan]=0; /* Note that this means that if both tone and noise are disabled, the output */
} /* is 1, not 0, and can be modulated changing the volume. */
/* The 8910 has three outputs, each output is the mix of one of the three */ /* loop? kill the loop and optimize! */
/* tone generators and of the (single) noise generator. The two are mixed */ for (int chan = 0; chan < NUM_CHANNELS; chan++)
/* BEFORE going into the DAC. The formula to mix each channel is: */ {
/* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */ tone = &m_tone[chan];
/* Note that this means that if both tone and noise are disabled, the output */ const int period = std::max<int>(1, tone->period) * (m_step_mul << 1);
/* is 1, not 0, and can be modulated changing the volume. */ tone->count += advance << (is_expanded_mode() ? 5 : ((m_feature & PSG_HAS_EXPANDED_MODE) ? 0 : 1));
if (tone->count>=period) {
tone->duty_cycle = (tone->duty_cycle - (tone->count/period)) & 0x1f;
tone->output = is_expanded_mode() ? BIT(duty_cycle[tone_duty(tone)], tone->duty_cycle) : BIT(tone->duty_cycle, 0);
tone->count = tone->count % period;
}
}
/* buffering loop */ const int period_noise = (int)(noise_period()) * m_step_mul;
for (int sampindex = 0; sampindex < samples; sampindex++) m_count_noise+=advance;
{ while (m_count_noise >= period_noise)
for (int chan = 0; chan < NUM_CHANNELS; chan++) {
{ /* toggle the prescaler output. Noise is no different to
tone = &m_tone[chan]; * channels.
const int period = std::max<int>(1, tone->period) * (m_step_mul << 1); */
tone->count += is_expanded_mode() ? 32 : ((m_feature & PSG_HAS_EXPANDED_MODE) ? 1 : 2); m_count_noise -= period_noise;
if (tone->count>=period) { m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1);
tone->duty_cycle = (tone->duty_cycle - (tone->count/period)) & 0x1f;
tone->output = is_expanded_mode() ? BIT(duty_cycle[tone_duty(tone)], tone->duty_cycle) : BIT(tone->duty_cycle, 0);
tone->count = tone->count % period;
}
}
const int period_noise = (int)(noise_period()) * m_step_mul; if (is_expanded_mode()) // AY8930 noise generator rate is twice? compares as compatibility mode
if ((++m_count_noise) >= period_noise) {
{ // This is called "Noise value" on the docs, but is a counter whose period is determined by the LFSR.
/* toggle the prescaler output. Noise is no different to // Using AND/OR gates, specific periods can be "filtered" out.
* channels. // A square wave can be generated through this behavior, which can be used for crude AM pulse width modulation.
*/
m_count_noise = 0;
m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1);
if (is_expanded_mode()) // AY8930 noise generator rate is twice? compares as compatibility mode // The period of the noise is determined by this value.
{ // The least significant byte of the LFSR is bitwise ANDed with the AND mask, and then bitwise ORed with the OR mask.
// This is called "Noise value" on the docs, but is a counter whose period is determined by the LFSR. if ((++m_noise_value) >= (((unsigned char)(m_rng) & noise_and()) | noise_or())) // Clock the noise value.
// Using AND/OR gates, specific periods can be "filtered" out. {
// A square wave can be generated through this behavior, which can be used for crude AM pulse width modulation. m_noise_value = 0;
// The period of the noise is determined by this value. // When everything is finally said and done, a 1bit latch is flipped.
// The least significant byte of the LFSR is bitwise ANDed with the AND mask, and then bitwise ORed with the OR mask. // This is the final output of the noise, to be multiplied by the tone and envelope generators of the channel.
if ((++m_noise_value) >= (((unsigned char)(m_rng) & noise_and()) | noise_or())) // Clock the noise value. m_noise_out ^= 1;
{
m_noise_value = 0;
// When everything is finally said and done, a 1bit latch is flipped. noise_rng_tick();
// This is the final output of the noise, to be multiplied by the tone and envelope generators of the channel. }
m_noise_out ^= 1; }
else if (!m_prescale_noise)
noise_rng_tick();
}
noise_rng_tick(); for (int chan = 0; chan < NUM_CHANNELS; chan++)
} {
} tone = &m_tone[chan];
else if (!m_prescale_noise) m_vol_enabled[chan] = (tone->output | (unsigned char)tone_enable(chan)) & (noise_output() | (unsigned char)noise_enable(chan));
noise_rng_tick(); }
}
for (int chan = 0; chan < NUM_CHANNELS; chan++) /* update envelope */
{ // who cares about env 1/2 on 8910
tone = &m_tone[chan]; for (int chan = 0; chan < (is_expanded_mode() ? NUM_CHANNELS : 1); chan++)
m_vol_enabled[chan] = (tone->output | (unsigned char)tone_enable(chan)) & (noise_output() | (unsigned char)noise_enable(chan)); {
} envelope = &m_envelope[chan];
if (envelope->holding == 0)
{
const int period = std::max<int>(1, envelope->period) * m_env_step_mul;
envelope->count += advance;
if (envelope->count >= period)
{
envelope->count %= period;
envelope->step--;
/* update envelope */ /* check envelope current position */
for (int chan = 0; chan < NUM_CHANNELS; chan++) if (envelope->step < 0)
{ {
envelope = &m_envelope[chan]; if (envelope->hold)
if (envelope->holding == 0) {
{ if (envelope->alternate)
const int period = std::max<int>(1, envelope->period) * m_env_step_mul; envelope->attack ^= m_env_step_mask;
if ((++envelope->count) >= period) envelope->holding = 1;
{ envelope->step = 0;
envelope->count = 0; }
envelope->step--; else
{
/* if CountEnv has looped an odd number of times (usually 1), */
/* invert the output. */
if (envelope->alternate && (envelope->step & (m_env_step_mask + 1)))
envelope->attack ^= m_env_step_mask;
/* check envelope current position */ envelope->step &= m_env_step_mask;
if (envelope->step < 0) }
{ }
if (envelope->hold)
{
if (envelope->alternate)
envelope->attack ^= m_env_step_mask;
envelope->holding = 1;
envelope->step = 0;
}
else
{
/* if CountEnv has looped an odd number of times (usually 1), */
/* invert the output. */
if (envelope->alternate && (envelope->step & (m_env_step_mask + 1)))
envelope->attack ^= m_env_step_mask;
envelope->step &= m_env_step_mask; }
} }
} envelope->volume = (envelope->step ^ envelope->attack);
}
} if (m_streams == 3)
} {
envelope->volume = (envelope->step ^ envelope->attack); for (int chan = 0; chan < NUM_CHANNELS; chan++)
} {
tone = &m_tone[chan];
if (m_streams == 3) if (tone_envelope(tone) != 0)
{ {
for (int chan = 0; chan < NUM_CHANNELS; chan++) envelope = &m_envelope[get_envelope_chan(chan)];
{ unsigned int env_volume = envelope->volume;
tone = &m_tone[chan]; if (m_feature & PSG_HAS_EXPANDED_MODE)
if (tone_envelope(tone) != 0) {
{ if (!is_expanded_mode())
envelope = &m_envelope[get_envelope_chan(chan)]; {
unsigned int env_volume = envelope->volume; env_volume >>= 1;
if (m_feature & PSG_HAS_EXPANDED_MODE) if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field
{ outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0];
if (!is_expanded_mode()) else
{ outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? env_volume : 0];
env_volume >>= 1; }
if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field else
outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0]; {
else if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field
outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? env_volume : 0]; outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0];
} else
else outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0];
{ }
if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field }
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0]; else
else {
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0]; if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field
} outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0];
} else
else outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0];
{ }
if (m_feature & PSG_EXTENDED_ENVELOPE) // AY8914 Has a two bit tone_envelope field }
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume >> (3-tone_envelope(tone)) : 0]; else
else {
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0]; if (is_expanded_mode())
} outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0];
} else
else outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0];
{ }
if (is_expanded_mode()) }
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0]; }
else else
outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0]; {
} outputs[0]=mix_3D();
} }
}
else
{
outputs[0]=mix_3D();
}
}
} }
void ay8910_device::build_mixer_table() void ay8910_device::build_mixer_table()

View file

@ -164,7 +164,7 @@ public:
unsigned char ay8910_read_ym(); unsigned char ay8910_read_ym();
void ay8910_reset_ym(); void ay8910_reset_ym();
private: public:
static constexpr int NUM_CHANNELS = 3; static constexpr int NUM_CHANNELS = 3;
device_type chip_type; device_type chip_type;