mirror of
https://github.com/holub/mame
synced 2025-07-05 01:48:29 +03:00
spkrdev: fix regression with filtering
This commit is contained in:
parent
5a63d0e738
commit
17c64fa372
@ -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: */
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user