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];
for (int i=0; i<3; i++) {
oscBuf[i]->begin(len);
}
if (sunsoft) {
for (size_t i=0; i<len; i++) {
runDAC();
runTFX();
checkWrites();
for (size_t i=0; i<len; i++) {
int advance=len-i;
// heuristic
for (int j=0; j<3; j++) {
// 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);
buf[0][i]=ayBuf[0];
buf[1][i]=buf[0][i];
// envelope
if (j<1) {
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[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));
}
} else {
for (size_t i=0; i<len; i++) {
runDAC();
runTFX();
checkWrites();
ay->sound_stream_update(ayBuf,1);
} else {
if (stereo) {
buf[0][i]=ayBuf[0]+ayBuf[1]+((ayBuf[2]*stereoSep)>>8);
buf[1][i]=((ayBuf[0]*stereoSep)>>8)+ayBuf[1]+ayBuf[2];
int out0=ayBuf[0]+ayBuf[1]+((ayBuf[2]*stereoSep)>>8);
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 {
buf[0][i]=ayBuf[0]+ayBuf[1]+ayBuf[2];
buf[1][i]=buf[0][i];
int out=ayBuf[0]+ayBuf[1]+ayBuf[2];
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);
@ -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) {
if (selCore && !intellivision) {
acquire_atomic(buf,len);
} else {
acquire_mame(buf,len);
}
}
@ -1011,6 +1048,8 @@ void DivPlatformAY8910::reset() {
ayEnvSlideLow=0;
delay=0;
lastOut[0]=0;
lastOut[1]=0;
ioPortA=false;
ioPortB=false;
@ -1026,6 +1065,10 @@ bool DivPlatformAY8910::keyOffAffectsArp(int ch) {
return true;
}
bool DivPlatformAY8910::hasAcquireDirect() {
return (!selCore || intellivision);
}
bool DivPlatformAY8910::getLegacyAlwaysSetVolume() {
return false;
}

View file

@ -128,6 +128,7 @@ class DivPlatformAY8910: public DivDispatch {
ssg_t ay_atomic;
int delay;
int lastOut[2];
bool extMode;
unsigned int extClock;
@ -149,7 +150,7 @@ class DivPlatformAY8910: public DivDispatch {
void checkWrites();
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);
friend void putDispatchChip(void*,int);
@ -160,6 +161,7 @@ class DivPlatformAY8910: public DivDispatch {
void runTFX(int runRate=0);
void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8);
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);
int dispatch(DivCommand c);
void* getChanState(int chan);
@ -177,6 +179,7 @@ class DivPlatformAY8910: public DivDispatch {
void setFlags(const DivConfig& flags);
int getOutputCount();
bool keyOffAffectsArp(int ch);
bool hasAcquireDirect();
DivMacroInt* getChanMacroInt(int ch);
DivSamplePos getSamplePos(int ch);
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
//-------------------------------------------------
void ay8910_device::sound_stream_update(short* outputs, int outLen)
void ay8910_device::sound_stream_update(short* outputs, int advance)
{
tone_t *tone;
envelope_t *envelope;
tone_t *tone;
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 */
if (!m_ready)
{
for (int chan = 0; chan < m_streams; chan++)
outputs[chan]=0;
}
/* The 8910 has three outputs, each output is the mix of one of the three */
/* tone generators and of the (single) noise generator. The two are mixed */
/* BEFORE going into the DAC. The formula to mix each channel is: */
/* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */
/* 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 */
/* tone generators and of the (single) noise generator. The two are mixed */
/* BEFORE going into the DAC. The formula to mix each channel is: */
/* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */
/* 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. */
/* loop? kill the loop and optimize! */
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone = &m_tone[chan];
const int period = std::max<int>(1, tone->period) * (m_step_mul << 1);
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 */
for (int sampindex = 0; sampindex < samples; sampindex++)
{
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone = &m_tone[chan];
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);
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;
}
}
const int period_noise = (int)(noise_period()) * m_step_mul;
m_count_noise+=advance;
while (m_count_noise >= period_noise)
{
/* toggle the prescaler output. Noise is no different to
* channels.
*/
m_count_noise -= period_noise;
m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1);
const int period_noise = (int)(noise_period()) * m_step_mul;
if ((++m_count_noise) >= period_noise)
{
/* toggle the prescaler output. Noise is no different to
* channels.
*/
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
{
// This is called "Noise value" on the docs, but is a counter whose period is determined by the LFSR.
// 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.
if (is_expanded_mode()) // AY8930 noise generator rate is twice? compares as compatibility mode
{
// This is called "Noise value" on the docs, but is a counter whose period is determined by the LFSR.
// 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.
// 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.
if ((++m_noise_value) >= (((unsigned char)(m_rng) & noise_and()) | noise_or())) // Clock the noise value.
{
m_noise_value = 0;
// 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.
if ((++m_noise_value) >= (((unsigned char)(m_rng) & noise_and()) | noise_or())) // Clock the noise value.
{
m_noise_value = 0;
// When everything is finally said and done, a 1bit latch is flipped.
// This is the final output of the noise, to be multiplied by the tone and envelope generators of the channel.
m_noise_out ^= 1;
// When everything is finally said and done, a 1bit latch is flipped.
// This is the final output of the noise, to be multiplied by the tone and envelope generators of the channel.
m_noise_out ^= 1;
noise_rng_tick();
}
}
else if (!m_prescale_noise)
noise_rng_tick();
}
noise_rng_tick();
}
}
else if (!m_prescale_noise)
noise_rng_tick();
}
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone = &m_tone[chan];
m_vol_enabled[chan] = (tone->output | (unsigned char)tone_enable(chan)) & (noise_output() | (unsigned char)noise_enable(chan));
}
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone = &m_tone[chan];
m_vol_enabled[chan] = (tone->output | (unsigned char)tone_enable(chan)) & (noise_output() | (unsigned char)noise_enable(chan));
}
/* update envelope */
// who cares about env 1/2 on 8910
for (int chan = 0; chan < (is_expanded_mode() ? NUM_CHANNELS : 1); 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 */
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
envelope = &m_envelope[chan];
if (envelope->holding == 0)
{
const int period = std::max<int>(1, envelope->period) * m_env_step_mul;
if ((++envelope->count) >= period)
{
envelope->count = 0;
envelope->step--;
/* check envelope current position */
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;
/* check envelope current position */
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->step &= m_env_step_mask;
}
}
}
}
envelope->volume = (envelope->step ^ envelope->attack);
}
}
}
envelope->volume = (envelope->step ^ envelope->attack);
}
if (m_streams == 3)
{
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone = &m_tone[chan];
if (tone_envelope(tone) != 0)
{
envelope = &m_envelope[get_envelope_chan(chan)];
unsigned int env_volume = envelope->volume;
if (m_feature & PSG_HAS_EXPANDED_MODE)
{
if (!is_expanded_mode())
{
env_volume >>= 1;
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];
else
outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? env_volume : 0];
}
else
{
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
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0];
}
}
else
{
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
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0];
}
}
else
{
if (is_expanded_mode())
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0];
else
outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0];
}
}
}
else
{
outputs[0]=mix_3D();
}
}
if (m_streams == 3)
{
for (int chan = 0; chan < NUM_CHANNELS; chan++)
{
tone = &m_tone[chan];
if (tone_envelope(tone) != 0)
{
envelope = &m_envelope[get_envelope_chan(chan)];
unsigned int env_volume = envelope->volume;
if (m_feature & PSG_HAS_EXPANDED_MODE)
{
if (!is_expanded_mode())
{
env_volume >>= 1;
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];
else
outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? env_volume : 0];
}
else
{
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
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0];
}
}
else
{
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
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? env_volume : 0];
}
}
else
{
if (is_expanded_mode())
outputs[chan]=m_env_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0];
else
outputs[chan]=m_vol_table[chan][m_vol_enabled[chan] ? tone_volume(tone) : 0];
}
}
}
else
{
outputs[0]=mix_3D();
}
}
void ay8910_device::build_mixer_table()

View file

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