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.
* Sample rate = RATE_MULTIPLIER * stream 3 sample rate.
* 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.
* The distorsion is however mostly in the higher frequencies.
* some aliasing distortion is introduced in this step because the average filtering is a compromise.
* The distortion is however mostly in the higher frequencies.
* -> low-pass anti-alias filtering with kernel ampl[] ->
* -> 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".
*
* In the speaker_state data structure,
@ -77,7 +77,7 @@
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;
@ -109,16 +109,17 @@ void speaker_sound_device::device_start()
m_composed_volume[i] = 0;
m_composed_sample_index = 0;
m_interm_sample_index = 0;
m_prevx = m_prevy = 0.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_last_sample_time = m_last_update_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;
/* Note: To avoid time drift due to floating point inaccuracies,
* 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()
{
int i;
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()
@ -224,7 +209,7 @@ void speaker_sound_device::sound_stream_update(sound_stream &stream, std::vector
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);
/* 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)
{
int volume;
double volume;
attotime time;
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)
{
/* 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);
/* 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
* 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();
/* 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:
*/
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_next_interm_sample_time = m_channel_last_sample_time + attotime(0, m_interm_sample_period);
m_last_update_time = m_channel_last_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);
/* 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;
/* 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)
{
/* 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. */
finalize_interm_sample(volume);
init_next_interm_sample();
@ -325,20 +315,20 @@ double speaker_sound_device::update_interm_samples_get_filtered_volume(double vo
{
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)
{
/* First interm. sample may be composed. */
/* First intermediate sample may be composed. */
finalize_interm_sample(volume);
/* Subsequent interm. samples will be homogeneous. */
/* Subsequent intermediate samples will be homogeneous. */
while (m_interm_sample_index + 1 < RATE_MULTIPLIER)
{
init_next_interm_sample();
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();
init_next_interm_sample();
/* Reset counter to next stream sample: */

View File

@ -44,7 +44,7 @@ private:
// internal state
// 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
double update_interm_samples_get_filtered_volume(double volume);