diff options
Diffstat (limited to 'src/emu')
-rw-r--r-- | src/emu/diexec.cpp | 2 | ||||
-rw-r--r-- | src/emu/resampler.cpp | 142 | ||||
-rw-r--r-- | src/emu/resampler.h | 41 | ||||
-rw-r--r-- | src/emu/sound.cpp | 95 | ||||
-rw-r--r-- | src/emu/sound.h | 26 |
5 files changed, 276 insertions, 30 deletions
diff --git a/src/emu/diexec.cpp b/src/emu/diexec.cpp index 1813dbff611..1cd5266c518 100644 --- a/src/emu/diexec.cpp +++ b/src/emu/diexec.cpp @@ -438,7 +438,7 @@ void device_execute_interface::interface_post_reset() if (m_vblank_interrupt_screen != nullptr) { // get the screen that will trigger the VBLANK - screen_device * screen = device().siblingdevice<screen_device>(m_vblank_interrupt_screen); + screen_device *const screen = device().siblingdevice<screen_device>(m_vblank_interrupt_screen); assert(screen != nullptr); screen->register_vblank_callback(vblank_state_delegate(&device_execute_interface::on_vblank, this)); diff --git a/src/emu/resampler.cpp b/src/emu/resampler.cpp index b95fe80dfba..d319c4c0eb9 100644 --- a/src/emu/resampler.cpp +++ b/src/emu/resampler.cpp @@ -149,7 +149,7 @@ // Having the sum of coefficients being 1 ensures that. -audio_resampler::audio_resampler(u32 fs, u32 ft) +audio_resampler_hq::audio_resampler_hq(u32 fs, u32 ft, float latency, u32 max_order_per_lane, u32 max_lanes) { m_ft = ft; m_fs = fs; @@ -160,13 +160,13 @@ audio_resampler::audio_resampler(u32 fs, u32 ft) m_fsm = ft / gcd; // Compute the per-phase filter length to limit the latency to 5ms and capping it - m_order_per_lane = u32(fs * 0.005 * 2); - if(m_order_per_lane > 400) - m_order_per_lane = 400; + m_order_per_lane = u32(fs * latency * 2); + if(m_order_per_lane > max_order_per_lane) + m_order_per_lane = max_order_per_lane; - // Reduce the number of phases to be less than 200 + // Reduce the number of phases to be less than max_lanes m_phase_shift = 0; - while(((m_fsm - 1) >> m_phase_shift) >= 200) + while(((m_fsm - 1) >> m_phase_shift) >= max_lanes) m_phase_shift ++; m_phases = ((m_fsm - 1) >> m_phase_shift) + 1; @@ -217,7 +217,7 @@ audio_resampler::audio_resampler(u32 fs, u32 ft) m_skip = m_ftm / m_fsm; } -u32 audio_resampler::compute_gcd(u32 fs, u32 ft) +u32 audio_resampler_hq::compute_gcd(u32 fs, u32 ft) { u32 v1 = fs > ft ? fs : ft; u32 v2 = fs > ft ? ft : fs; @@ -229,7 +229,12 @@ u32 audio_resampler::compute_gcd(u32 fs, u32 ft) return v1; } -void audio_resampler::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const +u32 audio_resampler_hq::history_size() const +{ + return m_order_per_lane; +} + +void audio_resampler_hq::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const { u32 seconds = dest_sample / m_ft; u32 dsamp = dest_sample % m_ft; @@ -255,7 +260,7 @@ void audio_resampler::apply(const emu::detail::output_buffer_flat<sample_t> &src } } -void audio_resampler::apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const +void audio_resampler_hq::apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const { u32 seconds = dest_sample / m_ft; u32 dsamp = dest_sample % m_ft; @@ -287,7 +292,7 @@ void audio_resampler::apply(const emu::detail::output_buffer_interleaved<s16> &s } -void audio_resampler::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const +void audio_resampler_hq::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const { u32 seconds = dest_sample / m_ft; u32 dsamp = dest_sample % m_ft; @@ -315,3 +320,120 @@ void audio_resampler::apply(const emu::detail::output_buffer_flat<sample_t> &src } } } + + +// Now for the lo-fi version +// +// We mostly forget about filtering, and just try to do a decent +// interpolation. There's a nice 4-point formula used in yamaha +// devices from around 2000: +// f0(t) = (t - t**3)/6 +// f1(t) = t + (t**2 - t**3)/2 +// +// The polynoms are used with the decimal part 'p' (as in phase) of +// the sample position. The computation from the four samples s0..s3 +// is: +// s = - s0 * f0(1-p) + s1 * f1(1-p) + s2 * f1(p) - s3 * f0(p) +// +// The target sample must be between s1 and s2. +// +// When upsampling, that's enough. When downsampling, it feels like a +// good idea to filter a little with a moving average, dividing the +// source frequency by an integer just big enough to make the final +// source frequency lower. + +// Sample interpolation functions f0 and f1 + +const std::array<std::array<float, 0x1001>, 2> audio_resampler_lofi::interpolation_table = []() { + std::array<std::array<float, 0x1001>, 2> result; + + // The exact way of doing the computations replicate the values + // actually used by the chip (which are very probably a rom, of + // course). + + for(u32 i=1; i != 4096; i++) { + float p = i / 4096.0; + result[0][i] = (p - p*p*p) / 6; + } + for(u32 i=1; i != 2049; i++) { + float p = i / 4096.0; + result[1][i] = p + (p*p - p*p*p) / 2; + } + for(u32 i=2049; i != 4096; i++) + // When interpolating, f1 is added and f0 is subtracted, and the total must be 1 + result[1][i] = 1.0 + result[0][i] + result[0][4096-i] - result[1][4096-i]; + + result[0][ 0] = 0.0; + result[0][0x1000] = 0.0; + result[1][ 0] = 0.0; + result[1][0x1000] = 1.0; + return result; +}(); + +audio_resampler_lofi::audio_resampler_lofi(u32 fs, u32 ft) +{ + m_fs = fs; + m_ft = ft; + + m_source_divide = fs <= ft ? 1 : 1+fs/ft; + m_step = u64(fs) * 0x1000 / ft / m_source_divide; +} + + +u32 audio_resampler_lofi::history_size() const +{ + return 5 * m_source_divide; +} + +void audio_resampler_lofi::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const +{ + u32 seconds = dest_sample / m_ft; + u32 dsamp = dest_sample % m_ft; + u64 ssamp = (u64(dsamp) * m_fs * 0x1000) / m_ft; + u64 ssample = (ssamp >> 12) + u64(m_fs) * seconds; + u32 phase = ssamp & 0xfff; + if(m_source_divide > 1) { + u32 delta = ssample % m_source_divide; + phase = (phase | (delta << 12)) / m_source_divide; + ssample -= delta; + } + + // We're getting 2 samples latency, which is small enough + + ssample -= 4*m_source_divide; + + const sample_t *s = src.ptrs(srcc, ssample - src.sync_sample()); + + std::function<sample_t()> reader; + if(m_source_divide == 1) + reader = [s]() mutable -> sample_t { return *s++; }; + else + reader = [s, count = m_source_divide]() mutable -> sample_t { sample_t sm = 0; for(u32 i=0; i != count; i++) { sm += *s++; } return sm / count; }; + + sample_t s0 = reader(); + sample_t s1 = reader(); + sample_t s2 = reader(); + sample_t s3 = reader(); + + sample_t *d = dest.data(); + for(u32 sample = 0; sample != samples; sample++) { + *d++ = gain * (- s0 * interpolation_table[0][0x1000-phase] + s1 * interpolation_table[1][0x1000-phase] + s2 * interpolation_table[1][phase] - s3 * interpolation_table[0][phase]); + + phase += m_step; + if(phase & 0x1000) { + phase &= 0xfff; + s0 = s1; + s1 = s2; + s2 = s3; + s3 = reader(); + } + } +} + +void audio_resampler_lofi::apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const +{ +} + +void audio_resampler_lofi::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const +{ +} diff --git a/src/emu/resampler.h b/src/emu/resampler.h index c1b2f88f3e9..d0758b5ae7e 100644 --- a/src/emu/resampler.h +++ b/src/emu/resampler.h @@ -15,13 +15,26 @@ class audio_resampler public: using sample_t = sound_stream::sample_t; - audio_resampler(u32 fs, u32 ft); + virtual ~audio_resampler() = default; - u32 history_size() const { return m_order_per_lane; } + virtual u32 history_size() const = 0; - void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const; - void apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const; - void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const; + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const = 0; + virtual void apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const = 0; + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const = 0; +}; + +class audio_resampler_hq : public audio_resampler +{ +public: + audio_resampler_hq(u32 fs, u32 ft, float latency, u32 max_order_per_lane, u32 max_lanes); + virtual ~audio_resampler_hq() = default; + + virtual u32 history_size() const override; + + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + virtual void apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; private: u32 m_order_per_lane, m_ftm, m_fsm, m_ft, m_fs, m_delta, m_skip, m_phases, m_phase_shift; @@ -31,5 +44,23 @@ private: static u32 compute_gcd(u32 fs, u32 ft); }; +class audio_resampler_lofi : public audio_resampler +{ +public: + audio_resampler_lofi(u32 fs, u32 ft); + virtual ~audio_resampler_lofi() = default; + + virtual u32 history_size() const override; + + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + virtual void apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + +private: + static const std::array<std::array<float, 0x1001>, 2> interpolation_table; + u32 m_source_divide, m_fs, m_ft; + u32 m_step; +}; + #endif diff --git a/src/emu/sound.cpp b/src/emu/sound.cpp index 5ff9e86e1ed..e64941f558c 100644 --- a/src/emu/sound.cpp +++ b/src/emu/sound.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:O. Galibert, Aaron Giles /*************************************************************************** sound.cpp @@ -23,6 +23,8 @@ #include "osdepend.h" +#include "util/language.h" + #include <algorithm> //************************************************************************** @@ -36,7 +38,7 @@ #define LOG_OSD_STREAMS (1U << 3) #define LOG_ORDER (1U << 4) -#define VERBOSE 0 +#define VERBOSE -1 #include "logmacro.h" @@ -189,7 +191,7 @@ template<typename S> void emu::detail::output_buffer_flat<S>::resample(u32 previ return; auto si = [](attotime time, u32 rate) -> s64 { - return time.m_seconds * rate + ((time.m_attoseconds / 100'000'000) * rate) / 10'000'000'000LL; + return time.m_seconds * rate + ((time.m_attoseconds / 100000000) * rate) / 10000000000LL; }; auto cv = [](u32 source_rate, u32 dest_rate, s64 time) -> std::pair<s64, double> { @@ -635,7 +637,7 @@ void sound_stream::reprime_sync_timer() u64 next_sample = m_output_buffer.write_sample() + 1; attotime next_time = sample_to_time(next_sample); - next_time.m_attoseconds += 1'000'000'000; // Go to the next nanosecond + next_time.m_attoseconds += 1'000'000'000; // Go to the next nanosecond ' m_sync_timer->adjust(next_time - m_device.machine().time()); } @@ -657,7 +659,11 @@ sound_manager::sound_manager(running_machine &machine) : m_muted(0), m_nosound_mode(machine.osd().no_sound()), m_unique_id(0), - m_wavfile() + m_wavfile(), + m_resampler_type(RESAMPLER_LOFI), + m_resampler_hq_latency(0.005), + m_resampler_hq_length(400), + m_resampler_hq_phases(200) { // register callbacks machine.configuration().config_register( @@ -1116,11 +1122,22 @@ void sound_manager::config_load(config_type cfg_type, config_level cfg_level, ut // In the global config, get the default effect chain configuration util::xml::data_node const *efl_node = parentnode->get_child("default_audio_effects"); - for(util::xml::data_node const *ef_node = efl_node->get_child("effect"); ef_node != nullptr; ef_node = ef_node->get_next_sibling("effect")) { - unsigned int id = ef_node->get_attribute_int("step", 0); - std::string type = ef_node->get_attribute_string("type", ""); - if(id >= 1 && id <= m_default_effects.size() && audio_effect::effect_names[m_default_effects[id-1]->type()] == type) - m_default_effects[id-1]->config_load(ef_node); + if(efl_node) { + for(util::xml::data_node const *ef_node = efl_node->get_child("effect"); ef_node != nullptr; ef_node = ef_node->get_next_sibling("effect")) { + unsigned int id = ef_node->get_attribute_int("step", 0); + std::string type = ef_node->get_attribute_string("type", ""); + if(id >= 1 && id <= m_default_effects.size() && audio_effect::effect_names[m_default_effects[id-1]->type()] == type) + m_default_effects[id-1]->config_load(ef_node); + } + } + + // and the resampler configuration + util::xml::data_node const *rs_node = parentnode->get_child("resampler"); + if(rs_node) { + m_resampler_type = rs_node->get_attribute_int("type", RESAMPLER_LOFI); + m_resampler_hq_latency = rs_node->get_attribute_float("hq_latency", 0.0050); + m_resampler_hq_length = rs_node->get_attribute_int("hq_length", 400); + m_resampler_hq_phases = rs_node->get_attribute_int("hq_phases", 200); } break; } @@ -1211,6 +1228,12 @@ void sound_manager::config_save(config_type cfg_type, util::xml::data_node *pare ef_node->set_attribute("type", audio_effect::effect_names[e->type()]); e->config_save(ef_node); } + + util::xml::data_node *const rs_node = parentnode->add_child("resampler", nullptr); + rs_node->set_attribute_int("type", m_resampler_type); + rs_node->set_attribute_float("hq_latency", m_resampler_hq_latency); + rs_node->set_attribute_int("hq_length", m_resampler_hq_length); + rs_node->set_attribute_int("hq_phases", m_resampler_hq_phases); break; } @@ -2462,14 +2485,62 @@ void sound_manager::streams_update() } //**// Resampler management - const audio_resampler *sound_manager::get_resampler(u32 fs, u32 ft) { auto key = std::make_pair(fs, ft); auto i = m_resamplers.find(key); if(i != m_resamplers.end()) return i->second.get(); - auto *res = new audio_resampler(fs, ft); + audio_resampler *res; + if(m_resampler_type == RESAMPLER_HQ) + res = new audio_resampler_hq(fs, ft, m_resampler_hq_latency, m_resampler_hq_length, m_resampler_hq_phases); + else + res = new audio_resampler_lofi(fs, ft); m_resamplers[key].reset(res); return res; } + +void sound_manager::rebuild_all_resamplers() +{ + m_resamplers.clear(); + + for(auto &stream : m_stream_list) + stream->create_resamplers(); + + for(auto &stream : m_stream_list) + stream->lookup_history_sizes(); +} + +void sound_manager::set_resampler_type(u32 type) +{ + m_resampler_type = type; + rebuild_all_resamplers(); +} + +void sound_manager::set_resampler_hq_latency(double latency) +{ + m_resampler_hq_latency = latency; + rebuild_all_resamplers(); +} + +void sound_manager::set_resampler_hq_length(u32 length) +{ + m_resampler_hq_length = length; + rebuild_all_resamplers(); +} + +void sound_manager::set_resampler_hq_phases(u32 phases) +{ + m_resampler_hq_phases = phases; + rebuild_all_resamplers(); +} + +const char *sound_manager::resampler_type_names(u32 type) const +{ + using util::lang_translate; + + if(type == RESAMPLER_HQ) + return _("HQ"); + else + return _("LoFi"); +} diff --git a/src/emu/sound.h b/src/emu/sound.h index 8039948362b..6d699dbb8d9 100644 --- a/src/emu/sound.h +++ b/src/emu/sound.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:O. Galibert, Aaron Giles /*************************************************************************** sound.h @@ -379,6 +379,11 @@ class sound_manager public: using sample_t = sound_stream::sample_t; + enum { + RESAMPLER_LOFI, + RESAMPLER_HQ + }; + struct mapping { struct node_mapping { u32 m_node; @@ -465,6 +470,18 @@ public: void mapping_update(); + const char *resampler_type_names(u32 type) const; + + u32 resampler_type() const { return m_resampler_type; } + double resampler_hq_latency() const { return m_resampler_hq_latency; } + u32 resampler_hq_length() const { return m_resampler_hq_length; } + u32 resampler_hq_phases() const { return m_resampler_hq_phases; } + + void set_resampler_type(u32 type); + void set_resampler_hq_latency(double latency); + void set_resampler_hq_length(u32 length); + void set_resampler_hq_phases(u32 phases); + private: struct effect_step { std::unique_ptr<audio_effect> m_effect; @@ -583,7 +600,7 @@ private: void update_osd_streams(); void update_osd_input(); void speakers_update(attotime endtime); - + void rebuild_all_resamplers(); void run_effects(); u64 rate_and_time_to_index(attotime time, u32 sample_rate) const; @@ -642,6 +659,11 @@ private: std::vector<std::unique_ptr<sound_stream>> m_stream_list; // list of streams std::vector<sound_stream *> m_ordered_streams; // Streams in update order u32 m_outputs_count; + + // resampler data + u32 m_resampler_type; + double m_resampler_hq_latency; + u32 m_resampler_hq_length, m_resampler_hq_phases; }; |