spkrdev: fix regression with filtering

This commit is contained in:
hap 2021-04-04 21:38:26 +02:00
parent 5a63d0e738
commit 17c64fa372
2 changed files with 28 additions and 38 deletions

View File

@ -53,11 +53,11 @@
* Virtual stream 2: Intermediate representation. * Virtual stream 2: Intermediate representation.
* Sample rate = RATE_MULTIPLIER * stream 3 sample rate. * Sample rate = RATE_MULTIPLIER * stream 3 sample rate.
* If effective rate of stream 1 exceeds rate of stream 2, * If effective rate of stream 1 exceeds rate of stream 2,
* some aliasing distorsion is introduced in this step because the average filtering is a compromise. * some aliasing distortion is introduced in this step because the average filtering is a compromise.
* The distorsion is however mostly in the higher frequencies. * The distortion is however mostly in the higher frequencies.
* -> low-pass anti-alias filtering with kernel ampl[] -> * -> low-pass anti-alias filtering with kernel ampl[] ->
* -> down-sampling -> * -> down-sampling ->
* Actual stream 3: channel output generated by speaker_sound_update(). * Actual stream 3: channel output generated by sound_stream_update().
* Sample rate = device sample rate = configured "-samplerate". * Sample rate = device sample rate = configured "-samplerate".
* *
* In the speaker_state data structure, * In the speaker_state data structure,
@ -77,7 +77,7 @@
static constexpr double default_levels[2] = {0.0, 1.0}; static constexpr double default_levels[2] = {0.0, 1.0};
// Internal oversampling factor (interm. samples vs stream samples) // Internal oversampling factor (intermediate samples vs stream samples)
static constexpr int RATE_MULTIPLIER = 4; static constexpr int RATE_MULTIPLIER = 4;
@ -109,16 +109,17 @@ void speaker_sound_device::device_start()
m_composed_volume[i] = 0; m_composed_volume[i] = 0;
m_composed_sample_index = 0; m_composed_sample_index = 0;
m_interm_sample_index = 0;
m_prevx = m_prevy = 0.0;
m_last_update_time = machine().time(); m_last_update_time = machine().time();
m_channel_sample_period = HZ_TO_ATTOSECONDS(machine().sample_rate()); m_channel_sample_period = HZ_TO_ATTOSECONDS(machine().sample_rate());
m_channel_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_channel_sample_period); m_channel_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_channel_sample_period);
m_interm_sample_period = m_channel_sample_period / RATE_MULTIPLIER; m_interm_sample_period = m_channel_sample_period / RATE_MULTIPLIER;
m_interm_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_interm_sample_period); m_interm_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_interm_sample_period);
m_channel_last_sample_time = m_channel->sample_time(); m_channel_last_sample_time = m_last_update_time;
m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period); m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period);
m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period); m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period);
m_interm_sample_index = 0;
m_prevx = m_prevy = 0.0;
/* Note: To avoid time drift due to floating point inaccuracies, /* Note: To avoid time drift due to floating point inaccuracies,
* it is good if the speaker time synchronizes itself with the stream timing regularly. * it is good if the speaker time synchronizes itself with the stream timing regularly.
@ -172,23 +173,7 @@ void speaker_sound_device::device_start()
void speaker_sound_device::device_reset() void speaker_sound_device::device_reset()
{ {
int i;
m_level = 0; m_level = 0;
for (i = 0; i < FILTER_LENGTH; i++)
m_composed_volume[i] = 0;
m_composed_sample_index = 0;
m_last_update_time = machine().time();
m_channel_sample_period = HZ_TO_ATTOSECONDS(machine().sample_rate());
m_channel_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_channel_sample_period);
m_interm_sample_period = m_channel_sample_period / RATE_MULTIPLIER;
m_interm_sample_period_secfrac = ATTOSECONDS_TO_DOUBLE(m_interm_sample_period);
m_channel_last_sample_time = m_channel->sample_time();
m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period);
m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period);
m_interm_sample_index = 0;
m_prevx = m_prevy = 0.0;
} }
void speaker_sound_device::device_post_load() void speaker_sound_device::device_post_load()
@ -224,7 +209,7 @@ void speaker_sound_device::sound_stream_update(sound_stream &stream, std::vector
for (int sampindex = 0; sampindex < buffer.samples(); ) for (int sampindex = 0; sampindex < buffer.samples(); )
{ {
/* Note that first interm. sample may be composed... */ /* Note that first intermediate sample may be composed... */
filtered_volume = update_interm_samples_get_filtered_volume(volume); filtered_volume = update_interm_samples_get_filtered_volume(volume);
/* Composite volume is now quantized to the stream resolution */ /* Composite volume is now quantized to the stream resolution */
@ -249,7 +234,7 @@ void speaker_sound_device::sound_stream_update(sound_stream &stream, std::vector
void speaker_sound_device::level_w(int new_level) void speaker_sound_device::level_w(int new_level)
{ {
int volume; double volume;
attotime time; attotime time;
if (new_level == m_level) if (new_level == m_level)
@ -266,7 +251,7 @@ void speaker_sound_device::level_w(int new_level)
if (time < m_channel_next_sample_time) if (time < m_channel_next_sample_time)
{ {
/* Stream sample is yet unfinished, but we may have one or more interm. samples */ /* Stream sample is yet unfinished, but we may have one or more intermediate samples */
update_interm_samples(time, volume); update_interm_samples(time, volume);
/* Do not forget to update speaker state before returning! */ /* Do not forget to update speaker state before returning! */
@ -275,23 +260,28 @@ void speaker_sound_device::level_w(int new_level)
} }
/* Reaching here means such time has passed since last stream update /* Reaching here means such time has passed since last stream update
* that we can add at least one complete sample to the stream. * that we can add at least one complete sample to the stream.
* The details have to be handled by speaker_sound_update() * The details have to be handled by sound_stream_update()
*/ */
/* Force streams.c to update sound until this point in time now */ /* Force stream to update sound until this point in time now */
m_channel->update(); m_channel->update();
/* This is redundant because time update has to be done within speaker_sound_update() anyway, /* This is redundant because time update has to be done within sound_stream_update() anyway,
* however this ensures synchronization between the speaker and stream timing: * however this ensures synchronization between the speaker and stream timing:
*/ */
m_channel_last_sample_time = m_channel->sample_time(); m_channel_last_sample_time = m_channel->sample_time();
/* sample_time() may be ahead of us */
if (m_channel_last_sample_time > time)
m_channel_last_sample_time -= attotime(0, m_channel_sample_period);
m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period); m_channel_next_sample_time = m_channel_last_sample_time + attotime(0, m_channel_sample_period);
m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period); m_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period);
m_last_update_time = m_channel_last_sample_time; m_last_update_time = m_channel_last_sample_time;
/* Assertion: time - last_update_time < channel_sample_period, i.e. time < channel_next_sample_time */ /* Assertion: time - last_update_time < channel_sample_period, i.e. time < channel_next_sample_time */
/* The overshooting fraction of time will make zero, one or more interm. samples: */ /* The overshooting fraction of time will make zero, one or more intermediate samples: */
update_interm_samples(time, volume); update_interm_samples(time, volume);
/* Finally update speaker state before returning */ /* Finally update speaker state before returning */
@ -299,14 +289,14 @@ void speaker_sound_device::level_w(int new_level)
} }
void speaker_sound_device::update_interm_samples(const attotime &time, int volume) void speaker_sound_device::update_interm_samples(const attotime &time, double volume)
{ {
double fraction; double fraction;
/* We may have completed zero, one or more interm. samples: */ /* We may have completed zero, one or more intermediate samples: */
while (time >= m_next_interm_sample_time) while (time >= m_next_interm_sample_time)
{ {
/* First interm. sample may be composed, subsequent samples will be homogeneous. */ /* First intermediate sample may be composed, subsequent samples will be homogeneous. */
/* Treat all the same general way. */ /* Treat all the same general way. */
finalize_interm_sample(volume); finalize_interm_sample(volume);
init_next_interm_sample(); init_next_interm_sample();
@ -325,20 +315,20 @@ double speaker_sound_device::update_interm_samples_get_filtered_volume(double vo
{ {
double filtered_volume, tempx; double filtered_volume, tempx;
/* We may have one or more interm. samples to go */ /* We may have one or more intermediate samples to go */
if (m_interm_sample_index < RATE_MULTIPLIER) if (m_interm_sample_index < RATE_MULTIPLIER)
{ {
/* First interm. sample may be composed. */ /* First intermediate sample may be composed. */
finalize_interm_sample(volume); finalize_interm_sample(volume);
/* Subsequent interm. samples will be homogeneous. */ /* Subsequent intermediate samples will be homogeneous. */
while (m_interm_sample_index + 1 < RATE_MULTIPLIER) while (m_interm_sample_index + 1 < RATE_MULTIPLIER)
{ {
init_next_interm_sample(); init_next_interm_sample();
m_composed_volume[m_composed_sample_index] = volume; m_composed_volume[m_composed_sample_index] = volume;
} }
} }
/* Important: next interm. sample not initialised yet, so that no data is destroyed before filtering... */ /* Important: next intermediate sample not initialised yet, so that no data is destroyed before filtering... */
filtered_volume = get_filtered_volume(); filtered_volume = get_filtered_volume();
init_next_interm_sample(); init_next_interm_sample();
/* Reset counter to next stream sample: */ /* Reset counter to next stream sample: */

View File

@ -44,7 +44,7 @@ private:
// internal state // internal state
// Updates the composed volume array according to time // Updates the composed volume array according to time
void update_interm_samples(const attotime &time, int volume); void update_interm_samples(const attotime &time, double volume);
// Updates the composed volume array and returns final filtered volume of next stream sample // Updates the composed volume array and returns final filtered volume of next stream sample
double update_interm_samples_get_filtered_volume(double volume); double update_interm_samples_get_filtered_volume(double volume);