From f0e6df80480f9982b8a57ebeffe649a2a4c4dc13 Mon Sep 17 00:00:00 2001 From: Olivier Galibert Date: Tue, 17 Jun 2025 00:55:55 +0200 Subject: [PATCH] sound: move positions to osd interface add special-casing for LFE add reverb (currently too subtle, need to find out why) vgm_visualizer: stop going OOB on the bitmap --- scripts/src/mame/frontend.lua | 2 + scripts/src/osd/modules.lua | 1 + src/devices/sound/vgm_visualizer.cpp | 4 +- src/emu/audio_effects/aeffect.cpp | 13 +- src/emu/audio_effects/aeffect.h | 9 +- src/emu/audio_effects/compressor.cpp | 24 +- src/emu/audio_effects/compressor.h | 2 +- src/emu/audio_effects/eq.cpp | 10 +- src/emu/audio_effects/eq.h | 2 +- src/emu/audio_effects/filter.cpp | 10 +- src/emu/audio_effects/filter.h | 2 +- src/emu/audio_effects/reverb.cpp | 1756 +++++++++++++++++- src/emu/audio_effects/reverb.h | 418 ++++- src/emu/sound.cpp | 36 +- src/emu/sound.h | 23 +- src/emu/speaker.cpp | 38 +- src/emu/speaker.h | 47 +- src/frontend/mame/luaengine.cpp | 8 +- src/frontend/mame/ui/audio_effect_reverb.cpp | 655 +++++++ src/frontend/mame/ui/audio_effect_reverb.h | 88 + src/frontend/mame/ui/audioeffects.cpp | 5 + src/frontend/mame/ui/audiomix.cpp | 2 +- src/mame/atari/atarigt.cpp | 4 +- src/mame/atari/metalmx.cpp | 2 +- src/mame/midway/seattle.cpp | 4 +- src/mame/namco/namcos22.cpp | 4 +- src/mame/pinball/s11b.cpp | 2 +- src/mame/taito/ninjaw.cpp | 4 +- src/mame/taito/taito_z.cpp | 6 +- src/mame/taito/taitojc.cpp | 2 +- src/mame/taito/warriorb.cpp | 2 +- src/osd/interface/audio.cpp | 49 + src/osd/interface/audio.h | 53 +- src/osd/modules/sound/coreaudio_sound.cpp | 114 +- src/osd/modules/sound/mmdevice_helpers.cpp | 38 +- src/osd/modules/sound/pa_sound.cpp | 22 +- src/osd/modules/sound/pipewire_sound.cpp | 24 +- src/osd/modules/sound/pulse_sound.cpp | 22 +- src/osd/modules/sound/sdl_sound.cpp | 22 +- src/osd/modules/sound/sound_module.cpp | 16 +- src/osd/modules/sound/sound_module.h | 1 + 41 files changed, 3271 insertions(+), 275 deletions(-) create mode 100644 src/frontend/mame/ui/audio_effect_reverb.cpp create mode 100644 src/frontend/mame/ui/audio_effect_reverb.h create mode 100644 src/osd/interface/audio.cpp diff --git a/scripts/src/mame/frontend.lua b/scripts/src/mame/frontend.lua index 81636bd2bfd..73e6c438cfb 100644 --- a/scripts/src/mame/frontend.lua +++ b/scripts/src/mame/frontend.lua @@ -99,6 +99,8 @@ files { MAME_DIR .. "src/frontend/mame/ui/audio_effect_eq.h", MAME_DIR .. "src/frontend/mame/ui/audio_effect_filter.cpp", MAME_DIR .. "src/frontend/mame/ui/audio_effect_filter.h", + MAME_DIR .. "src/frontend/mame/ui/audio_effect_reverb.cpp", + MAME_DIR .. "src/frontend/mame/ui/audio_effect_reverb.h", MAME_DIR .. "src/frontend/mame/ui/auditmenu.cpp", MAME_DIR .. "src/frontend/mame/ui/auditmenu.h", MAME_DIR .. "src/frontend/mame/ui/barcode.cpp", diff --git a/scripts/src/osd/modules.lua b/scripts/src/osd/modules.lua index 773859116dc..7010d3c9b22 100644 --- a/scripts/src/osd/modules.lua +++ b/scripts/src/osd/modules.lua @@ -52,6 +52,7 @@ function osdmodulesbuild() files { MAME_DIR .. "src/osd/watchdog.cpp", MAME_DIR .. "src/osd/watchdog.h", + MAME_DIR .. "src/osd/interface/audio.cpp", MAME_DIR .. "src/osd/interface/audio.h", MAME_DIR .. "src/osd/interface/inputcode.h", MAME_DIR .. "src/osd/interface/inputdev.h", diff --git a/src/devices/sound/vgm_visualizer.cpp b/src/devices/sound/vgm_visualizer.cpp index 13d25bcb6ca..255e40dccf5 100644 --- a/src/devices/sound/vgm_visualizer.cpp +++ b/src/devices/sound/vgm_visualizer.cpp @@ -739,7 +739,7 @@ void vgmviz_device::draw_waveform(bitmap_rgb32 &bitmap) bitmap.pix(CHANNEL_HEIGHT + 1 + CHANNEL_CENTER, x) = MED_GRAY; const float raw_l = m_audio_buf[1 - m_audio_fill_index][0][((int)m_history_length + 1 + x) % FFT_LENGTH]; - const int sample_l = (int)((raw_l - 0.5f) * (CHANNEL_HEIGHT - 1)); + const int sample_l = std::clamp((int)((raw_l - 0.5f) * (CHANNEL_HEIGHT - 1)), -CHANNEL_CENTER, CHANNEL_CENTER); const int dy_l = (sample_l == 0) ? 0 : ((sample_l < 0) ? -1 : 1); const int endy_l = CHANNEL_CENTER; int y = endy_l - sample_l; @@ -750,7 +750,7 @@ void vgmviz_device::draw_waveform(bitmap_rgb32 &bitmap) } while(y != endy_l); const float raw_r = m_audio_buf[1 - m_audio_fill_index][1][((int)m_history_length + 1 + x) % FFT_LENGTH]; - const int sample_r = (int)((raw_r - 0.5f) * (CHANNEL_HEIGHT - 1)); + const int sample_r = std::clamp((int)((raw_r - 0.5f) * (CHANNEL_HEIGHT - 1)), -CHANNEL_CENTER, CHANNEL_CENTER); const int dy_r = (sample_r == 0) ? 0 : ((sample_r < 0) ? -1 : 1); const int endy_r = CHANNEL_HEIGHT + 1 + CHANNEL_CENTER; y = endy_r - sample_r; diff --git a/src/emu/audio_effects/aeffect.cpp b/src/emu/audio_effects/aeffect.cpp index dcff278099f..070db51dc25 100644 --- a/src/emu/audio_effects/aeffect.cpp +++ b/src/emu/audio_effects/aeffect.cpp @@ -15,13 +15,13 @@ const char *const audio_effect::effect_names[COUNT] = { "Equalizer" }; -audio_effect *audio_effect::create(int type, u32 sample_rate, audio_effect *def) +audio_effect *audio_effect::create(int type, speaker_device *speaker, u32 sample_rate, audio_effect *def) { switch(type) { - case FILTER: return new audio_effect_filter (sample_rate, def); - case COMPRESSOR: return new audio_effect_compressor(sample_rate, def); - case REVERB: return new audio_effect_reverb (sample_rate, def); - case EQ: return new audio_effect_eq (sample_rate, def); + case FILTER: return new audio_effect_filter (speaker, sample_rate, def); + case COMPRESSOR: return new audio_effect_compressor(speaker, sample_rate, def); + case REVERB: return new audio_effect_reverb (speaker, sample_rate, def); + case EQ: return new audio_effect_eq (speaker, sample_rate, def); } return nullptr; } @@ -31,8 +31,7 @@ void audio_effect::copy(const emu::detail::output_buffer_flat &src, em { u32 samples = src.available_samples(); dest.prepare_space(samples); - u32 channels = src.channels(); - for(u32 channel = 0; channel != channels; channel++) { + for(u32 channel = 0; channel != m_channels; channel++) { const sample_t *srcd = src.ptrs(channel, 0); sample_t *destd = dest.ptrw(channel, 0); std::copy(srcd, srcd + samples, destd); diff --git a/src/emu/audio_effects/aeffect.h b/src/emu/audio_effects/aeffect.h index 72e6279f1e6..987b2d2e1c1 100644 --- a/src/emu/audio_effects/aeffect.h +++ b/src/emu/audio_effects/aeffect.h @@ -6,6 +6,8 @@ #ifndef MAME_EMU_AUDIO_EFFECTS_AEFFECT_H #define MAME_EMU_AUDIO_EFFECTS_AEFFECT_H +#include "speaker.h" + class audio_effect { public: @@ -21,9 +23,9 @@ public: static const char *const effect_names[COUNT]; - static audio_effect *create(int type, u32 sample_rate, audio_effect *def = nullptr); + static audio_effect *create(int type, speaker_device *speaker, u32 sample_rate, audio_effect *def = nullptr); - audio_effect(u32 sample_rate, audio_effect *def) : m_default(def), m_sample_rate(sample_rate) {} + audio_effect(speaker_device *speaker, u32 sample_rate, audio_effect *def) : m_default(def), m_speaker(speaker), m_channels(speaker ? speaker->channels() : 0), m_sample_rate(sample_rate) {} virtual ~audio_effect() = default; void copy(const emu::detail::output_buffer_flat &src, emu::detail::output_buffer_flat &dest) const; @@ -37,7 +39,8 @@ public: protected: audio_effect *m_default; - u32 m_sample_rate; + speaker_device *m_speaker; + u32 m_channels, m_sample_rate; }; #endif diff --git a/src/emu/audio_effects/compressor.cpp b/src/emu/audio_effects/compressor.cpp index dda4fc4f21b..b6bdc855d67 100644 --- a/src/emu/audio_effects/compressor.cpp +++ b/src/emu/audio_effects/compressor.cpp @@ -7,8 +7,15 @@ #include "compressor.h" #include "xmlfile.h" -audio_effect_compressor::audio_effect_compressor(u32 sample_rate, audio_effect *def) : audio_effect(sample_rate, def) +audio_effect_compressor::audio_effect_compressor(speaker_device *speaker, u32 sample_rate, audio_effect *def) : + audio_effect(speaker, sample_rate, def) { + m_slewed_signal.resize(m_channels, -200); + m_gain_reduction.resize(m_channels, 0); + m_input_samples.resize(m_channels, 0); + m_output_samples.resize(m_channels, 0); + m_inertia_velocity.resize(m_channels, 0); + reset_mode(); reset_attack(); reset_release(); @@ -362,23 +369,14 @@ void audio_effect_compressor::apply(const emu::detail::output_buffer_flat max_gain) max_gain = m_gain_reduction[channel]; - for(u32 channel = 0; channel != channels; channel ++) { + for(u32 channel = 0; channel != m_channels; channel ++) { m_gain_reduction[channel] = max_gain * m_channel_link + m_gain_reduction[channel] * (1 - m_channel_link); double output_sample = db_to_value(m_input_samples[channel] - m_gain_reduction[channel]); if(*src.ptrs(channel, sample) < 0) diff --git a/src/emu/audio_effects/compressor.h b/src/emu/audio_effects/compressor.h index ac0d4730530..f4d56ad670a 100644 --- a/src/emu/audio_effects/compressor.h +++ b/src/emu/audio_effects/compressor.h @@ -11,7 +11,7 @@ class audio_effect_compressor : public audio_effect { public: - audio_effect_compressor(u32 sample_rate, audio_effect *def); + audio_effect_compressor(speaker_device *speaker, u32 sample_rate, audio_effect *def); virtual ~audio_effect_compressor() = default; virtual int type() const override { return COMPRESSOR; } diff --git a/src/emu/audio_effects/eq.cpp b/src/emu/audio_effects/eq.cpp index 3f0a68a1fb9..c52abcfa47c 100644 --- a/src/emu/audio_effects/eq.cpp +++ b/src/emu/audio_effects/eq.cpp @@ -12,8 +12,11 @@ // [Zölzer 2011] "DAFX: Digital Audio Effects", Udo Zölzer, Second Edition, Wiley publishing, 2011 (Tables 2.3 and 2.4) // [Zölzer 2008] "Digital Audio Signal Processing", Udo Zölzer, Second Edition, Wiley publishing, 2008 (Tables 5.3, 5.4 and 5.5) -audio_effect_eq::audio_effect_eq(u32 sample_rate, audio_effect *def) : audio_effect(sample_rate, def) +audio_effect_eq::audio_effect_eq(speaker_device *speaker, u32 sample_rate, audio_effect *def) : + audio_effect(speaker, sample_rate, def) { + m_history.resize(m_channels); + // Minimal init to avoid using uninitialized values when reset_* // recomputes filters @@ -322,11 +325,8 @@ void audio_effect_eq::apply(const emu::detail::output_buffer_flat &src u32 samples = src.available_samples(); dest.prepare_space(samples); - u32 channels = src.channels(); - if(m_history.empty()) - m_history.resize(channels); - for(u32 channel = 0; channel != channels; channel++) { + for(u32 channel = 0; channel != m_channels; channel++) { const sample_t *srcd = src.ptrs(channel, 0); sample_t *destd = dest.ptrw(channel, 0); for(u32 sample = 0; sample != samples; sample++) { diff --git a/src/emu/audio_effects/eq.h b/src/emu/audio_effects/eq.h index 2363f9a468d..ff09f278977 100644 --- a/src/emu/audio_effects/eq.h +++ b/src/emu/audio_effects/eq.h @@ -13,7 +13,7 @@ class audio_effect_eq : public audio_effect public: enum { BANDS = 5 }; - audio_effect_eq(u32 sample_rate, audio_effect *def); + audio_effect_eq(speaker_device *speaker, u32 sample_rate, audio_effect *def); virtual ~audio_effect_eq() = default; virtual int type() const override { return EQ; } diff --git a/src/emu/audio_effects/filter.cpp b/src/emu/audio_effects/filter.cpp index 947f4b7af58..1a3d0634384 100644 --- a/src/emu/audio_effects/filter.cpp +++ b/src/emu/audio_effects/filter.cpp @@ -13,8 +13,11 @@ // [Zölzer 2011] "DAFX: Digital Audio Effects", Udo Zölzer, Second Edition, Wiley publishing, 2011 (Table 2.2) -audio_effect_filter::audio_effect_filter(u32 sample_rate, audio_effect *def) : audio_effect(sample_rate, def) +audio_effect_filter::audio_effect_filter(speaker_device *speaker, u32 sample_rate, audio_effect *def) : + audio_effect(speaker, sample_rate, def) { + m_history.resize(m_channels); + // Minimal init to avoid using uninitialized values when reset_* // recomputes filters m_fl = m_fh = 1000; @@ -159,11 +162,8 @@ void audio_effect_filter::apply(const emu::detail::output_buffer_flat u32 samples = src.available_samples(); dest.prepare_space(samples); - u32 channels = src.channels(); - if(m_history.empty()) - m_history.resize(channels); - for(u32 channel = 0; channel != channels; channel++) { + for(u32 channel = 0; channel != m_channels; channel++) { const sample_t *srcd = src.ptrs(channel, 0); sample_t *destd = dest.ptrw(channel, 0); for(u32 sample = 0; sample != samples; sample++) { diff --git a/src/emu/audio_effects/filter.h b/src/emu/audio_effects/filter.h index 13463bd6b4b..c6830a37a79 100644 --- a/src/emu/audio_effects/filter.h +++ b/src/emu/audio_effects/filter.h @@ -11,7 +11,7 @@ class audio_effect_filter : public audio_effect { public: - audio_effect_filter(u32 sample_rate, audio_effect *def); + audio_effect_filter(speaker_device *speaker, u32 sample_rate, audio_effect *def); virtual ~audio_effect_filter() = default; virtual int type() const override { return FILTER; } diff --git a/src/emu/audio_effects/reverb.cpp b/src/emu/audio_effects/reverb.cpp index c7f231a9d92..22533d02b30 100644 --- a/src/emu/audio_effects/reverb.cpp +++ b/src/emu/audio_effects/reverb.cpp @@ -5,24 +5,1776 @@ #include "reverb.h" #include "xmlfile.h" -audio_effect_reverb::audio_effect_reverb(u32 sample_rate, audio_effect *def) : audio_effect(sample_rate, def) +#include + +// This is a reimplementation of the "RoomReverb" by ElephantDSP.com/Christian Voigt +// which is itself built from early reflection and Progenitor 2 from Freeverb3. +// + +const audio_effect_reverb::preset audio_effect_reverb::presets[] = { + { "Echo Chamber", 0, 12000, 100, 30, 30, 2, 80, 20, 9000, 10, 4, 90, 10, 30, 20 }, + { "Large Room", 0, 8000, 90, 45, 45, 0.5, 40, 64, 8000, 12, 1.2, 90, 10, 30, 20 }, + { "Large Room Bright", 0, 16000, 90, 45, 45, 0.5, 40, 59, 16000, 12, 1.2, 90, 10, 30, 20 }, + { "Large Room Dark", 0, 3600, 90, 45, 45, 1, 20, 70, 3600, 12, 1.4, 90, 10, 30, 20 }, + { "Large Room Drum", 0, 6500, 90, 35, 35, 1, 20, 30, 6500, 16, 1.2, 85, 10, 25, 25 }, + { "Large Room Tiled", 0, 8500, 90, 15, 70, 2, 0, 10, 8500, 12, 1.2, 90, 10, 30, 20 }, + { "Large Room Vocal", 0, 5500, 90, 45, 45, 2, 0, 80, 5500, 4, 1.5, 90, 10, 30, 20 }, + { "Large Room Wooden", 0, 9000, 90, 55, 35, 1, 20, 100, 9000, 12, 1.2, 90, 10, 30, 20 }, + { "Live", 0, 12000, 90, 20, 20, 2, 80, 25, 9000, 25, 1.5, 90, 30, 15, 60 }, + { "Long Reverb 12s", 0, 16000, 100, 25, 25, 1, 20, 80, 10000, 0, 12, 90, 10, 30, 20 }, + { "Long Reverb 30s", 0, 16000, 100, 25, 25, 1, 20, 80, 9000, 0, 30, 90, 10, 30, 20 }, + { "Medium Room", 0, 8000, 80, 30, 30, 0.5, 40, 57, 8000, 8, 0.6, 90, 10, 30, 20 }, + { "Medium Room Bright", 0, 16000, 80, 30, 30, 0.5, 40, 52, 16000, 8, 0.6, 90, 10, 30, 20 }, + { "Medium Room Dark", 0, 3600, 80, 30, 30, 1, 20, 65, 3600, 8, 0.8, 90, 10, 30, 20 }, + { "Medium Room Drum", 0, 6500, 80, 25, 25, 1, 20, 25, 6500, 12, 0.6, 85, 10, 25, 25 }, + { "Medium Room Tiled", 0, 8500, 80, 10, 65, 2, 0, 10, 8500, 8, 0.6, 90, 10, 30, 20 }, + { "Medium Room Vocal", 0, 5500, 80, 30, 30, 2, 0, 75, 5500, 2, 0.9, 90, 10, 30, 20 }, + { "Medium Room Wooden", 0, 9000, 80, 40, 20, 1, 20, 100, 9000, 8, 0.6, 90, 10, 30, 20 }, + { "Shimmer", 0, 8000, 100, 15, 15, 0.5, 40, 50, 8000, 0, 6, 100, 5, 5, 20 }, + { "Small Room", 0, 8000, 70, 15, 15, 0.5, 40, 50, 8000, 4, 0.3, 90, 10, 30, 20 }, + { "Small Room Bright", 0, 16000, 70, 15, 15, 0.5, 40, 45, 16000, 4, 0.3, 90, 10, 30, 20 }, + { "Small Room Dark", 0, 3600, 70, 15, 15, 1, 20, 60, 3600, 4, 0.5, 90, 10, 30, 20 }, + { "Small Room Drum", 0, 6500, 70, 15, 15, 1, 20, 20, 6500, 8, 0.3, 85, 10, 25, 25 }, + { "Small Room Tiled", 0, 8500, 70, 5, 60, 2, 0, 10, 8500, 4, 0.3, 90, 10, 30, 20 }, + { "Small Room Vocal", 0, 5500, 70, 15, 15, 2, 0, 70, 5500, 0, 0.6, 90, 10, 30, 20 }, + { "Small Room Wooden", 0, 9000, 70, 25, 5, 1, 20, 100, 9000, 4, 0.3, 90, 10, 30, 20 }, + { "Tunnel", 0, 8000, 50, 65, 65, 0.5, 10, 80, 6000, 0, 8, 90, 10, 30, 20 }, + { "Very Large Room", 0, 8000, 100, 60, 60, 0.5, 40, 70, 8000, 16, 2.0, 90, 10, 30, 20 }, + { "Very Large Room Bright", 0, 16000, 100, 60, 60, 0.5, 40, 65, 16000, 16, 2.0, 90, 10, 30, 20 }, + { "Very Large Room Dark", 0, 3600, 100, 60, 60, 1, 20, 75, 3600, 16, 2.2, 90, 10, 30, 20 }, + { "Very Large Room Drum", 0, 6500, 100, 45, 45, 1, 20, 35, 6500, 20, 2.0, 85, 10, 25, 25 }, + { "Very Large Room Tiled", 0, 8500, 100, 20, 75, 2, 0, 10, 8500, 16, 2.0, 90, 10, 30, 20 }, + { "Very Large Room Vocal", 0, 5500, 100, 60, 60, 2, 0, 85, 5500, 6, 2.3, 90, 10, 30, 20 }, + { "Very Large Room Wooden", 0, 9000, 100, 70, 50, 1, 20, 100, 9000, 16, 2.0, 90, 10, 30, 20 }, +}; + +const audio_effect_reverb::early_reverb_tap_map audio_effect_reverb::tap_maps[15] = { + { "0", { 18, 18 }, + {{ 0.0043, 0.0215, 0.0225, 0.0268, 0.0270, 0.0298, 0.0458, 0.0485, 0.0572, 0.0587, 0.0595, 0.0612, 0.0707, 0.0708, 0.0726, 0.0741, 0.0753, 0.0797 }, + { 0.0053, 0.0225, 0.0235, 0.0278, 0.0290, 0.0288, 0.0468, 0.0475, 0.0582, 0.0577, 0.0575, 0.0622, 0.0697, 0.0718, 0.0736, 0.0751, 0.0763, 0.0817 }}, + {{ 0.841, 0.504, 0.491, 0.379, 0.380, 0.346, 0.289, 0.272, 0.192, 0.193, 0.217, 0.181, 0.180, 0.181, 0.176, 0.142, 0.167, 0.134 }, + { 0.842, 0.506, 0.489, 0.382, 0.300, 0.346, 0.290, 0.271, 0.193, 0.192, 0.217, 0.195, 0.192, 0.166, 0.186, 0.131, 0.168, 0.133 }}}, + + { "1", { 6, 6 }, + {{ 0.0199, 0.0354, 0.0389, 0.0414, 0.0699, 0.0796 }, + { 0.0209, 0.0364, 0.0399, 0.0424, 0.0709, 0.0806 }}, + {{ 1.020, 0.818, 0.635, 0.719, 0.267, 0.242 }, + { 1.021, 0.820, 0.633, 0.722, 0.187, 0.243 }}}, + + { "2", { 4, 4 }, + {{ 0.0090, 0.0118, 0.0205, 0.0213 }, + { 0.0098, 0.0145, 0.0203, 0.0230 }}, + {{ 1.35, -1.15, 1.15, -1.14 }, + { 1.36, -1.16, -1.00, 1.14 }}}, + + { "11", { 11, 11 }, + {{ 0.003568, 0.011703, 0.019526, 0.024870, 0.037740, 0.048089, 0.053948, 0.061333, 0.061344, 0.070073, 0.077130 }, + { 0.002818, 0.009115, 0.017042, 0.023885, 0.033068, 0.042307, 0.051234, 0.059896, 0.067984, 0.067995, 0.076458 }}, + {{ 0.963333,-0.806667, 0.706667,-0.656667,-0.556667, 0.526667,-0.506667, 0.503333, 0.450000,-0.470000, 0.456667 }, + { 0.980000,-0.850000, 0.726667,-0.660000, 0.583333, 0.553333,-0.503333, 0.486667,-0.473333,-0.466667, 0.463333 }}}, + + { "12", { 12, 12 }, + {{ 0.006344, 0.012286, 0.023385, 0.028495, 0.036385, 0.045750, 0.053427, 0.059266, 0.063281, 0.063292, 0.066437, 0.066448 }, + { 0.004568, 0.011266, 0.015687, 0.030203, 0.030214, 0.038740, 0.048172, 0.054073, 0.058870, 0.062417, 0.064510, 0.064521 }}, + {{ 0.896667, 0.796667,-0.653333, 0.623333,-0.570000, 0.530000,-0.496667, 0.483333, 0.480000, 0.406667,-0.470000,-0.470000 }, + { 0.946667, 0.823333,-0.753333, 0.593333, 0.600000, 0.556667, 0.530000,-0.516667, 0.490000,-0.480000, 0.476667, 0.410000 }}}, + + { "13", { 10, 12 }, + {{ 0.003568, 0.011703, 0.019526, 0.024870, 0.037740, 0.048089, 0.053948, 0.061333, 0.070073, 0.077125 }, + { 0.002818, 0.009115, 0.017042, 0.023885, 0.033062, 0.033073, 0.033120, 0.042307, 0.051234, 0.059901, 0.067990, 0.076458 }}, + {{ 0.963333,-0.813333, 0.700000,-0.650000,-0.560000, 0.526667,-0.500000, 0.486667,-0.463333, 0.463333 }, + { 0.980000,-0.856667, 0.723333,-0.660000, 0.580000, 0.580000, 0.166667, 0.550000,-0.506667, 0.476667,-0.466667, 0.460000 }}}, + + { "14", { 11, 13 }, + {{ 0.002062, 0.008031, 0.023010, 0.042016, 0.055766, 0.059432, 0.064536, 0.071240, 0.076490, 0.079250, 0.079292 }, + { 0.001396, 0.009427, 0.023260, 0.039740, 0.039786, 0.052427, 0.052479, 0.053552, 0.062042, 0.067380, 0.067391, 0.073630, 0.076490 }}, + {{ 0.993333,-0.873333, 0.663333,-0.543333, 0.503333,-0.486667, 0.480000,-0.466667, 0.466667,-0.463333,-0.186667 }, + { 1.013333, 0.833333,-0.660000, 0.546667, 0.190000, 0.506667, 0.150000,-0.506667, 0.493333, 0.473333, 0.473333,-0.463333, 0.473333 }}}, + + { "15", { 11, 10 }, + {{ 0.009531, 0.014042, 0.028557, 0.037885, 0.043745, 0.051255, 0.057661, 0.066589, 0.075375, 0.084964, 0.084974 }, + { 0.006198, 0.016792, 0.022510, 0.033880, 0.048922, 0.056766, 0.067510, 0.074745, 0.080651, 0.083865 }}, + {{-0.636667, 1.013333, 0.770000, 0.866667, 0.983333, 0.700000, 0.786667, 0.763333, 0.796667, 0.680000, 0.690000 }, + { 1.006667, 0.736667, 0.973333, 0.760000, 0.713333, 0.963333, 0.980000, 0.920000,-0.670000, 0.870000 }}}, + + { "16", { 12, 11 }, + {{ 0.003021, 0.008531, 0.010703, 0.012203, 0.014682, 0.018547, 0.018604, 0.025391, 0.034026, 0.038948, 0.047807, 0.047818 }, + { 0.002526, 0.005193, 0.005255, 0.006682, 0.013500, 0.016937, 0.020109, 0.022115, 0.028370, 0.036130, 0.043328 }}, + {{ 0.823333, 0.773333,-0.810000,-0.873333, 0.770000, 0.723333, 0.166667,-0.766667,-0.753333, 0.656667, 0.533333, 0.533333 }, + { 0.963333, 0.883333, 0.170000, 0.736667,-0.870000,-0.790000,-0.766667,-0.763333, 0.750000, 0.653333, 0.520000 }}}, + + { "17", { 11, 11 }, + {{ 0.002964, 0.003031, 0.009786, 0.010870, 0.018380, 0.019984, 0.027745, 0.043411, 0.048651, 0.053406, 0.058391 }, + { 0.004193, 0.009203, 0.010198, 0.010208, 0.010266, 0.015708, 0.016917, 0.030208, 0.036193, 0.042406, 0.048135 }}, + {{ 0.913333, 0.143333, 0.626667, 0.610000, 0.800000, 0.643333, 0.773333,-0.773333, 0.630000,-0.806667,-0.683333 }, + { 0.976667, 0.726667, 0.716667, 0.716667, 0.143333,-0.683333,-0.730000,-0.893333, 0.770000,-0.856667,-0.850000 }}}, + + { "18", { 11, 13 }, + {{ 0.004693, 0.009786, 0.012036, 0.015625, 0.019521, 0.019531, 0.024615, 0.032896, 0.040786, 0.046208, 0.050464 }, + { 0.004026, 0.009510, 0.014000, 0.015312, 0.019276, 0.024344, 0.024406, 0.031292, 0.031354, 0.042391, 0.045625, 0.045693, 0.050609 }}, + {{ 0.810000, 0.853333,-0.733333, 1.023333, 0.690000, 0.683333, 0.906667,-0.903333, 0.666667, 0.726667, 0.660000 }, + { 0.883333, 0.803333,-0.876667, 0.823333, 0.836667, 0.853333, 0.166667,-0.903333,-0.173333, 0.783333, 0.880000, 0.140000, 0.790000 }}}, + + { "19", { 10, 11 }, + {{ 0.006031, 0.019214, 0.032687, 0.041682, 0.047917, 0.055474, 0.059266, 0.064068, 0.068786, 0.075062 }, + { 0.007031, 0.012599, 0.025599, 0.034943, 0.044250, 0.050255, 0.062771, 0.066646, 0.066656, 0.071807, 0.078354 }}, + {{ 0.933333, 0.833333, 0.793333, 0.783333, 0.840000,-0.793333, 0.860000, 0.716667,-0.810000, 0.733333 }, + { 0.706667, 0.873333, 0.846667, 0.846667,-0.836667, 0.826667,-0.860000, 0.780000, 0.776667,-0.890000,-0.836667 }}}, + + { "20", { 12, 11 }, + {{ 0.004292, 0.013469, 0.023667, 0.029073, 0.033005, 0.044031, 0.053677, 0.060505, 0.071667, 0.071677, 0.079833, 0.079844 }, + { 0.007917, 0.014245, 0.026464, 0.030865, 0.041781, 0.052109, 0.057672, 0.065104, 0.074745, 0.082375, 0.082385 }}, + {{ 0.950000,-0.786667, 0.680000, 0.620000,-0.556667, 0.520000, 0.500000,-0.476667, 0.466667, 0.470000,-0.450000,-0.456667 }, + { 0.863333, 0.736667,-0.643333,-0.593333, 0.536667, 0.523333,-0.483333, 0.486667,-0.463333, 0.450000, 0.453333 }}}, + + { "21", { 11, 11 }, + {{ 0.004474, 0.014313, 0.023130, 0.030646, 0.038906, 0.048729, 0.056156, 0.064510, 0.064521, 0.072859, 0.081219 }, + { 0.007729, 0.007797, 0.017010, 0.025641, 0.033901, 0.040318, 0.051432, 0.058495, 0.066859, 0.075224, 0.085417 }}, + {{ 0.950000, 0.756667, 0.680000, 0.616667, 0.563333, 0.523333, 0.493333, 0.493333, 0.446667, 0.460000, 0.456667 }, + { 0.893333, 0.146667, 0.733333, 0.656667, 0.576667, 0.543333, 0.510000, 0.503333, 0.466667, 0.470000, 0.480000 }}}, + + { "22", { 10, 10 }, + {{ 0.003276, 0.010714, 0.019526, 0.024656, 0.033474, 0.037687, 0.044661, 0.052552, 0.052599, 0.069260 }, + { 0.003276, 0.010714, 0.019526, 0.024656, 0.033474, 0.037682, 0.044661, 0.052552, 0.058604, 0.069266 }}, + {{ 0.963333, 0.810000, 0.720000, 0.640000, 0.596667, 0.540000, 0.520000, 0.493333, 0.173333, 0.473333 }, + { 0.970000, 0.803333, 0.720000, 0.646667, 0.596667, 0.543333, 0.523333, 0.503333, 0.483333, 0.470000 }}}, +}; + +u32 audio_effect_reverb::preset_count() { + return sizeof(presets)/sizeof(presets[0]); +} + +const char *audio_effect_reverb::preset_name(u32 id) +{ + return presets[id].name; +} + +u32 audio_effect_reverb::early_tap_setup_count() +{ + return sizeof(tap_maps)/sizeof(tap_maps[0]); +} + +const char *audio_effect_reverb::early_tap_setup_name(u32 id) +{ + return tap_maps[id].name; +} + +void audio_effect_reverb::load_preset(u32 id) +{ + const preset &p = presets[id]; + + set_early_tap_setup(p.early_tap_setup); + set_early_damping(p.early_damping); + set_stereo_width(p.stereo_width); + set_early_room_size(p.early_room_size); + set_late_room_size(p.late_room_size); + set_late_spin(p.late_spin); + set_late_wander(p.late_wander); + set_late_diffusion(p.late_diffusion); + set_late_damping(p.late_damping); + set_late_predelay(p.late_predelay); + set_late_global_decay(p.late_global_decay); + set_dry_level(p.dry_level); + set_early_level(p.early_level); + set_late_level(p.late_level); + set_early_to_late_level(p.early_to_late_level); +} + +const audio_effect_reverb::preset *audio_effect_reverb::find_preset(std::string name) +{ + for(u32 i=0; i != preset_count(); i++) + if(preset_name(i) == name) + return &presets[i]; + return nullptr; +} + +audio_effect_reverb::audio_effect_reverb(speaker_device *speaker, u32 sample_rate, audio_effect *def) : + audio_effect(speaker, sample_rate, def) +{ + m_default_preset = find_preset("Medium Room"); + + m_early_lpf_h.resize(m_channels); + m_early_hpf_h.resize(m_channels); + m_early_diffusion_allpass_h.resize(m_channels); + m_early_cross_allpass_h.resize(m_channels); + m_late_dccut_h.resize(m_channels); + + m_early_delays.resize(m_channels); + m_early_xdelays.resize(m_channels); + + m_late_input_diffusion.resize(m_channels); + m_late_cross_diffusion.resize(m_channels); + for(u32 channel = 0; channel != m_channels; channel ++) { + m_late_input_diffusion[channel].resize(INPUT_DIFFUSION_ALLPASS); + m_late_cross_diffusion[channel].resize(CROSS_DIFFUSION_ALLPASS); + } + m_late_input_damping_h.resize(m_channels); + m_late_damping_1_h.resize(m_channels); + m_late_damping_2_h.resize(m_channels); + m_late_output_lpf_h.resize(m_channels); + m_late_step_1.resize(m_channels); + m_late_step_2.resize(m_channels); + m_late_step_3.resize(m_channels); + m_late_step_4.resize(m_channels); + m_late_step_5.resize(m_channels); + m_late_step_6.resize(m_channels); + m_late_step_7.resize(m_channels); + m_late_step_8.resize(m_channels); + m_late_bass_h.resize(m_channels); + m_late_comb.resize(m_channels); + m_late_final_delay.resize(m_channels); + + m_early_in.resize(m_channels); + m_early_wet.resize(m_channels); + m_early_out.resize(m_channels); + m_late_in.resize(m_channels); + m_late_diff.resize(m_channels); + m_late_cross.resize(m_channels); + m_late_cross2.resize(m_channels); + m_late_pre_out.resize(m_channels); + m_late_out.resize(m_channels); + + // Speaker decomposition mapping, should it go up to be shared by all effects? + m_ch_type.resize(m_channels, T_MONO); + m_ch_pair.resize(m_channels); + for(u32 i=0; i != m_channels; i++) { + // Already picked up, skip + if(m_ch_type[i] != T_MONO) + continue; + + // Default to mono associated with itself + m_ch_pair[i] = i; + + const auto &pos = speaker->get_position(i); + // Keep mono if special or in the middle + if(pos.is_onreq() || pos.is_unknown() || pos.is_lfe() || pos.m_x == 0) + continue; + + // Search for another channel with same y, z, and opposite x + u32 j = i+1; + for(;j != m_channels;j++) { + const auto &jpos = speaker->get_position(j); + if(jpos.is_onreq() || jpos.is_unknown() || jpos.is_lfe()) + continue; + if(pos.m_x == -jpos.m_x && pos.m_y == jpos.m_y && pos.m_z == jpos.m_z) + break; + } + + // Found one, build the pair, otherwise leave as-is + if(j != m_channels) { + m_ch_pair[i] = j; + m_ch_pair[j] = i; + m_ch_type[i] = pos.m_x >= 0 ? T_LEFT : T_RIGHT; + m_ch_type[j] = pos.m_x < 0 ? T_LEFT : T_RIGHT; + } + } + + // Fixed + m_early_room_size_ratio = 1; + m_late_room_size_ratio = 1; + + set_early_hpf(4); + set_early_diffusion_ap(150, 4); + set_early_cross_ap(750, 4); + set_early_multichannel_delay(0.2); + set_late_dccut(5); + set_late_modulation_noise_1(0.09); + set_late_modulation_noise_2(0.06); + set_late_spin_limit_1(20); + set_late_spin_limit_2(12); + set_late_input_damping(20000); + set_late_diffusion_1(0.375); + set_late_diffusion_2(0.312); + set_late_diffusion_3(0.406); + set_late_diffusion_4(0.250); + m_late_crossfeed = 0.4; + set_late_bass_allpass(150, 4); + set_late_damping_2(500, 2); + m_late_bass_boost = 0.1; + + // Variables + reset_mode(); + reset_early_tap_setup(); + reset_early_damping(); + reset_stereo_width(); + reset_early_room_size(); + reset_late_room_size(); + reset_late_spin(); + reset_late_wander(); + reset_late_diffusion(); + reset_late_damping(); + reset_late_predelay(); + reset_late_global_decay(); + reset_dry_level(); + reset_early_level(); + reset_late_level(); + reset_early_to_late_level(); } void audio_effect_reverb::config_load(util::xml::data_node const *ef_node) { + if(ef_node->has_attribute("mode")) { + m_mode = ef_node->get_attribute_int("mode", 0); + m_isset_mode = true; + } else + reset_mode(); + + if(ef_node->has_attribute("early_tap_setup")) { + m_mode = ef_node->get_attribute_float("early_tap_setup", 0); + m_isset_early_tap_setup = true; + } else + reset_early_tap_setup(); + + if(ef_node->has_attribute("early_damping")) { + m_mode = ef_node->get_attribute_float("early_damping", 0); + m_isset_early_damping = true; + } else + reset_early_damping(); + + if(ef_node->has_attribute("stereo_width")) { + m_mode = ef_node->get_attribute_float("stereo_width", 0); + m_isset_stereo_width = true; + } else + reset_stereo_width(); + + if(ef_node->has_attribute("early_room_size")) { + m_mode = ef_node->get_attribute_float("early_room_size", 0); + m_isset_early_room_size = true; + } else + reset_early_room_size(); + + if(ef_node->has_attribute("late_room_size")) { + m_mode = ef_node->get_attribute_float("late_room_size", 0); + m_isset_late_room_size = true; + } else + reset_late_room_size(); + + if(ef_node->has_attribute("late_spin")) { + m_mode = ef_node->get_attribute_float("late_spin", 0); + m_isset_late_spin = true; + } else + reset_late_spin(); + + if(ef_node->has_attribute("late_wander")) { + m_mode = ef_node->get_attribute_float("late_wander", 0); + m_isset_late_wander = true; + } else + reset_late_wander(); + + if(ef_node->has_attribute("late_diffusion")) { + m_mode = ef_node->get_attribute_float("late_diffusion", 0); + m_isset_late_diffusion = true; + } else + reset_late_diffusion(); + + if(ef_node->has_attribute("late_damping")) { + m_mode = ef_node->get_attribute_float("late_damping", 0); + m_isset_late_damping = true; + } else + reset_late_damping(); + + if(ef_node->has_attribute("late_predelay")) { + m_mode = ef_node->get_attribute_float("late_predelay", 0); + m_isset_late_predelay = true; + } else + reset_late_predelay(); + + if(ef_node->has_attribute("late_global_decay")) { + m_mode = ef_node->get_attribute_float("late_global_decay", 0); + m_isset_late_global_decay = true; + } else + reset_late_global_decay(); + + if(ef_node->has_attribute("dry_level")) { + m_mode = ef_node->get_attribute_float("dry_level", 0); + m_isset_dry_level = true; + } else + reset_dry_level(); + + if(ef_node->has_attribute("early_level")) { + m_mode = ef_node->get_attribute_float("early_level", 0); + m_isset_early_level = true; + } else + reset_early_level(); + + if(ef_node->has_attribute("late_level")) { + m_mode = ef_node->get_attribute_float("late_level", 0); + m_isset_late_level = true; + } else + reset_late_level(); + + if(ef_node->has_attribute("early_to_late_level")) { + m_mode = ef_node->get_attribute_float("early_to_late_level", 0); + m_isset_early_to_late_level = true; + } else + reset_early_to_late_level(); + } void audio_effect_reverb::config_save(util::xml::data_node *ef_node) const { + if(m_isset_mode) + ef_node->set_attribute_int("mode", m_mode); + if(m_isset_early_tap_setup) + ef_node->set_attribute_float("early_tap_setup", m_early_tap_setup); + if(m_isset_early_damping) + ef_node->set_attribute_float("early_damping", m_early_damping); + if(m_isset_stereo_width) + ef_node->set_attribute_float("stereo_width", m_stereo_width); + if(m_isset_early_room_size) + ef_node->set_attribute_float("early_room_size", m_early_room_size); + if(m_isset_late_room_size) + ef_node->set_attribute_float("late_room_size", m_late_room_size); + if(m_isset_late_spin) + ef_node->set_attribute_float("late_spin", m_late_spin); + if(m_isset_late_wander) + ef_node->set_attribute_float("late_wander", m_late_wander); + if(m_isset_late_diffusion) + ef_node->set_attribute_float("late_diffusion", m_late_diffusion); + if(m_isset_late_damping) + ef_node->set_attribute_float("late_damping", m_late_damping); + if(m_isset_late_predelay) + ef_node->set_attribute_float("late_predelay", m_late_predelay); + if(m_isset_late_global_decay) + ef_node->set_attribute_float("late_global_decay", m_late_global_decay); + if(m_isset_dry_level) + ef_node->set_attribute_float("dry_level", m_dry_level); + if(m_isset_early_level) + ef_node->set_attribute_float("early_level", m_early_level); + if(m_isset_late_level) + ef_node->set_attribute_float("late_level", m_late_level); + if(m_isset_early_to_late_level) + ef_node->set_attribute_float("early_to_late_level", m_early_to_late_level); } void audio_effect_reverb::default_changed() { + if(!m_default) + return; + if(!m_isset_mode) + reset_mode(); } +void audio_effect_reverb::set_mode(u32 mode) +{ + m_isset_mode = true; + m_mode = mode; +} + +void audio_effect_reverb::set_early_tap_setup(u32 index) +{ + m_isset_early_tap_setup = true; + m_early_tap_setup = index; + commit_early_tap_setup(); +} + +void audio_effect_reverb::set_early_damping(double cutoff) +{ + m_isset_early_damping = true; + m_early_damping = cutoff; + commit_early_damping(); +} + +void audio_effect_reverb::set_stereo_width(double width) +{ + m_isset_stereo_width = true; + m_stereo_width = width; + commit_stereo_width(); +} + +void audio_effect_reverb::set_early_room_size(double size) +{ + m_isset_early_room_size = true; + m_early_room_size = size; + commit_early_room_size(); +} + +void audio_effect_reverb::set_late_room_size(double size) +{ + m_isset_late_room_size = true; + m_late_room_size = size; + commit_late_room_size(); +} + +void audio_effect_reverb::set_late_spin(double speed) +{ + m_isset_late_spin = true; + m_late_spin = speed; + commit_late_spin(); +} + +void audio_effect_reverb::set_late_wander(double wander) +{ + m_isset_late_wander = true; + m_late_wander = wander; + commit_late_wander(); +} + +void audio_effect_reverb::set_late_diffusion(double value) +{ + m_isset_late_diffusion = true; + m_late_diffusion = value; + commit_late_diffusion(); +} + +void audio_effect_reverb::set_late_damping(double cutoff) +{ + m_isset_late_damping = true; + m_late_damping = cutoff; + commit_late_damping(); +} + +void audio_effect_reverb::set_late_predelay(double delay) +{ + m_isset_late_predelay = true; + m_late_predelay = delay; + commit_late_predelay(); +} + +void audio_effect_reverb::set_late_global_decay(float value) +{ + m_isset_late_global_decay = true; + m_late_global_decay = value; + commit_late_decay(); +} + +void audio_effect_reverb::set_dry_level(double level) +{ + m_isset_dry_level = true; + m_dry_level = level; + commit_dry_level(); +} + +void audio_effect_reverb::set_early_level(double level) +{ + m_isset_early_level = true; + m_early_level = level; + commit_early_level(); +} + +void audio_effect_reverb::set_late_level(double level) +{ + m_isset_late_level = true; + m_late_level = level; + commit_late_level(); +} + +void audio_effect_reverb::set_early_to_late_level(double level) +{ + m_isset_early_to_late_level = true; + m_early_to_late_level = level; + commit_early_to_late_level(); +} + + + +void audio_effect_reverb::reset_mode() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_mode = false; + m_mode = d ? d->mode() : 1; +} + +void audio_effect_reverb::reset_early_tap_setup() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_early_tap_setup = false; + m_early_tap_setup = d ? d->early_tap_setup() : m_default_preset->early_tap_setup; + commit_early_tap_setup(); +} + +void audio_effect_reverb::reset_early_damping() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_early_damping = false; + m_early_damping = d ? d->early_damping() : m_default_preset->early_damping; + commit_early_damping(); +} + +void audio_effect_reverb::reset_stereo_width() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_stereo_width = false; + m_stereo_width = d ? d->stereo_width() : m_default_preset->stereo_width; + commit_stereo_width(); +} + +void audio_effect_reverb::reset_early_room_size() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_early_room_size = false; + m_early_room_size = d ? d->early_room_size() : m_default_preset->early_room_size; + commit_early_room_size(); +} + +void audio_effect_reverb::reset_late_room_size() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_late_room_size = false; + m_late_room_size = d ? d->late_room_size() : m_default_preset->late_room_size; + commit_late_room_size(); +} + +void audio_effect_reverb::reset_late_spin() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_late_spin = false; + m_late_spin = d ? d->late_spin() : m_default_preset->late_spin; + commit_late_spin(); +} + +void audio_effect_reverb::reset_late_wander() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_late_wander = false; + m_late_wander = d ? d->late_wander() : m_default_preset->late_wander; + commit_late_wander(); +} + +void audio_effect_reverb::reset_late_diffusion() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_late_diffusion = false; + m_late_diffusion = d ? d->late_diffusion() : m_default_preset->late_diffusion; + commit_late_diffusion(); +} + +void audio_effect_reverb::reset_late_damping() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_late_damping = false; + m_late_damping = d ? d->late_damping() : m_default_preset->late_damping; + commit_late_damping(); +} + +void audio_effect_reverb::reset_late_predelay() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_late_predelay = false; + m_late_predelay = d ? d->late_predelay() : m_default_preset->late_predelay; + commit_late_predelay(); +} + +void audio_effect_reverb::reset_late_global_decay() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_late_global_decay = false; + m_late_global_decay = d ? d->late_global_decay() : m_default_preset->late_global_decay; + commit_late_decay(); +} + +void audio_effect_reverb::reset_dry_level() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_dry_level = false; + m_dry_level = d ? d->dry_level() : m_default_preset->dry_level; + commit_dry_level(); +} + +void audio_effect_reverb::reset_early_level() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_early_level = false; + m_early_level = d ? d->early_level() : m_default_preset->early_level; + commit_early_level(); +} + +void audio_effect_reverb::reset_late_level() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_late_level = false; + m_late_level = d ? d->late_level() : m_default_preset->late_level; + commit_late_level(); +} + +void audio_effect_reverb::reset_early_to_late_level() +{ + audio_effect_reverb *d = static_cast(m_default); + m_isset_early_to_late_level = false; + m_early_to_late_level = d ? d->early_to_late_level() : m_default_preset->early_to_late_level; + commit_early_to_late_level(); +} + + + +void audio_effect_reverb::commit_early_tap_setup() +{ + const auto &tmap = tap_maps[m_early_tap_setup]; + for(u32 side = 0; side != 2; side++) + for(u32 tap = 0; tap != tmap.m_count[side]; tap++) + m_early_tap_dists[side][tap] = m_sample_rate * m_early_room_size_ratio * tmap.m_delay[side][tap]; +} + +void audio_effect_reverb::commit_early_damping() +{ + m_early_lpf.prepare_lpf(m_early_damping, m_sample_rate); +} + +void audio_effect_reverb::commit_stereo_width() +{ + m_wet1 = m_stereo_width / 100; + m_wet2 = 1 - m_wet1; +} + +void audio_effect_reverb::commit_early_room_size() +{ + m_early_room_size_ratio = m_early_room_size / 31.25 + 0.4; + commit_early_tap_setup(); +} + +void audio_effect_reverb::commit_late_room_size() +{ + m_late_room_size_ratio = m_late_room_size / 31.25 + 0.4; + + double mult = m_sample_rate * m_late_room_size_ratio; + + static double delays_diff[2][INPUT_DIFFUSION_ALLPASS] = { + { 18.08, 15.68, 12.72, 10.17, 6.39, 4.75, 4.22, 3.58, 3.19, 2.17 }, + { 17.67, 16.03, 12.19, 10.67, 6.92, 4.75, 4.10, 3.84, 3.25, 2.32 }, + }; + + static double delays_cross[2][CROSS_DIFFUSION_ALLPASS] = { + { 12.60, 9.99, 7.74, 5.10, }, + { 13.10, 9.49, 7.24, 5.60, }, + }; + + u32 modulation_factor_input = find_prime(m_sample_rate * 0.3126e-3); + u32 modulation_factor_step = find_prime(m_sample_rate * 0.9377e-3); + + for(u32 channel = 0; channel != m_channels; channel++) { + std::function dlookup; + std::function clookup; + switch(m_ch_type[channel]) { + case T_LEFT: dlookup = [](u32 index){ return delays_diff[0][index] * 1e-3; }; clookup = [](u32 index){ return delays_cross[0][index] * 1e-3; }; break; + case T_RIGHT: dlookup = [](u32 index){ return delays_diff[1][index] * 1e-3; }; clookup = [](u32 index){ return delays_cross[1][index] * 1e-3; }; break; + case T_MONO: + dlookup = [](u32 index){ return 0.5e-3*(delays_diff [0][index] + delays_diff [1][index]); }; + clookup = [](u32 index){ return 0.5*(delays_cross[0][index] + delays_cross[1][index]); }; + break; + } + + for(u32 index = 0; index != INPUT_DIFFUSION_ALLPASS; index++) + m_late_input_diffusion[channel][index].set_delay(find_prime(dlookup(index)*mult), modulation_factor_input); + for(u32 index = 0; index != CROSS_DIFFUSION_ALLPASS; index++) + m_late_cross_diffusion[channel][index].set_delay(find_prime(clookup(index)*mult)); + } + + for(u32 cchan = 0; cchan != m_channels; cchan++) { + int pchan = m_ch_pair[cchan]; + std::function sel; + switch(m_ch_type[cchan]) { + case T_LEFT: sel = [mult](double left, double right) { return find_prime(left * mult * 1e-3); }; break; + case T_RIGHT: sel = [mult](double left, double right) { return find_prime(right * mult * 1e-3); }; break; + case T_MONO: sel = [mult](double left, double right) { return find_prime(0.5 * (left + right) * mult * 1e-3); }; break; + } + + m_late_step_1[cchan].set_delay( sel( 7.004, 6.007), modulation_factor_step); + m_late_step_2[cchan].set_tap(0, sel( 0.059, 0.029)); + m_late_step_3[cchan].set_delay( sel(11.471, 9.641), modulation_factor_step); + m_late_step_4[cchan].set_tap(0, sel(30.916, 42.784)); + m_late_step_5[cchan].set_delays(sel(56.957, 59.546), sel(17.934, 10.784)); + m_late_step_6[cchan].set_tap(0, sel(10.081, 14.652)); + m_late_step_7[cchan].set_delays(sel(35.516, 42.549), sel( 3.546, 0.147), sel(23.912, 20.161), sel(37.040, 39.267)); + m_late_step_8[cchan].set_tap(0, sel(46.066, 0.469)); + + m_late_step_4[cchan].set_tap(1, sel( 0.029, 18.315)); + m_late_step_4[pchan].set_tap(2, sel( 3.223, 23.150)); + + m_late_step_5[cchan].set_tap_1(0, sel( 3.546, 0.293)); + m_late_step_5[cchan].set_tap_2(0, sel(14.066, 10.520)); + m_late_step_5[pchan].set_tap_2(1, sel( 3.018, 0.879)); + + m_late_step_6[cchan].set_tap(1, sel( 1.172, 13.714)); + m_late_step_6[pchan].set_tap(2, sel( 5.626, 9.143)); + m_late_step_6[cchan].set_tap(3, sel( 8.088, 0.703)); + m_late_step_6[pchan].set_tap(4, sel(13.714, 5.538)); + + m_late_step_7[cchan].set_tap_1(0, sel( 0.762, 0.293)); + m_late_step_7[cchan].set_tap_2(0, sel(22.857, 3.194)); + m_late_step_7[cchan].set_tap_3(0, sel(35.165, 38.388)); + m_late_step_7[pchan].set_tap_2(1, sel( 9.084, 23.443)); + + m_late_step_8[pchan].set_tap(1, sel( 1.055, 1.055)); + m_late_step_8[cchan].set_tap(2, sel(46.066, 0.234)); + m_late_step_8[cchan].set_tap(3, sel(22.857, 0.293)); + } + + commit_late_decay(); +} + +void audio_effect_reverb::commit_late_decay() +{ + float r1 = m_late_global_decay / m_late_room_size_ratio; + float r2 = r1 * m_late_decay_f; + + float d0 = std::pow(10.0f, std::log10(m_late_decay_0) / r1); + m_late_loop_decay = d0; + + float d1 = std::pow(10.0f, std::log10(m_late_decay_1) / r2); + for(auto &ap : m_late_step_5) + ap.set_decay_1(d1); + for(auto &ap : m_late_step_7) { + ap.set_decay_1(d1); + ap.set_decay_2(d1); + } + + float d2 = std::pow(10.0f, std::log10(m_late_decay_2) / r2); + for(auto &ap : m_late_step_1) + ap.set_decay(d2); + for(auto &ap : m_late_step_5) + ap.set_decay_2(d2); + for(auto &ap : m_late_step_7) + ap.set_decay_3(d2); + + float d3 = std::pow(10.0f, std::log10(m_late_decay_3) / r2); + for(auto &ap : m_late_step_3) + ap.set_decay(d3); +} + +void audio_effect_reverb::commit_late_spin() +{ + m_late_lfo1.prepare(m_late_spin, m_sample_rate); + m_late_lfo2.prepare(std::sqrt(100-(10-m_late_spin)*(10-m_late_spin))/2, m_sample_rate); +} + +void audio_effect_reverb::commit_late_wander() +{ + m_late_wander_actual = m_late_wander / 200 + 0.1; +} + +void audio_effect_reverb::commit_late_diffusion() +{ + double gain = - m_late_diffusion / 105; + for(auto &aps : m_late_input_diffusion) + for(auto &ap : aps) + ap.set_gain(gain); + for(auto &aps : m_late_cross_diffusion) + for(auto &ap : aps) + ap.set_gain(gain); +} + +void audio_effect_reverb::commit_late_damping() +{ + m_late_damping_1.prepare_lpf(m_late_damping, m_sample_rate); + m_late_output_lpf.prepare_lpf(m_late_damping, 2, m_sample_rate); +} + +void audio_effect_reverb::commit_late_predelay() +{ + u32 size = m_late_predelay * m_sample_rate / 1000; + for(auto &d : m_late_final_delay) + d.set_tap(0, size); +} + +void audio_effect_reverb::commit_dry_level() +{ + m_actual_dry_level = m_dry_level / 100; +} + +void audio_effect_reverb::commit_early_level() +{ + m_actual_early_level = m_early_level / 100; +} + +void audio_effect_reverb::commit_late_level() +{ + m_actual_late_level = m_late_level / 100; +} + +void audio_effect_reverb::commit_early_to_late_level() +{ + m_actual_early_to_late_level = m_early_to_late_level / 100; +} + + + +void audio_effect_reverb::set_early_hpf(double cutoff) +{ + m_early_hpf.prepare_hpf(cutoff, m_sample_rate); +} + +void audio_effect_reverb::set_early_diffusion_ap(double cutoff, double bw) +{ + m_early_diffusion_allpass.prepare_apf(cutoff, bw, m_sample_rate); +} + +void audio_effect_reverb::set_early_cross_ap(double cutoff, double bw) +{ + m_early_cross_allpass.prepare_apf(cutoff, bw, m_sample_rate); +} + +void audio_effect_reverb::set_early_multichannel_delay(double delay) +{ + m_early_xdelays_dist = m_sample_rate * delay / 1000; +} + +void audio_effect_reverb::set_late_dccut(double cutoff) +{ + m_late_dccut.prepare(cutoff, m_sample_rate); +} + +void audio_effect_reverb::set_late_spin_limit_1(double cutoff) +{ + m_late_lfo1_lpf.prepare_lpf(cutoff, m_sample_rate); +} + +void audio_effect_reverb::set_late_spin_limit_2(double cutoff) +{ + m_late_lfo2_lpf.prepare_lpf(cutoff, m_sample_rate); +} + +void audio_effect_reverb::set_late_modulation_noise_1(double mod) +{ + m_late_modulation_noise_1 = mod; +} + +void audio_effect_reverb::set_late_modulation_noise_2(double mod) +{ + m_late_modulation_noise_2 = mod; +} + +void audio_effect_reverb::set_late_diffusion_1(double value) +{ + for(auto &ap : m_late_step_1) + ap.set_gain(value); +} + +void audio_effect_reverb::set_late_diffusion_2(double value) +{ + for(auto &ap : m_late_step_3) + ap.set_gain(value); +} + +void audio_effect_reverb::set_late_diffusion_3(double value) +{ + for(auto &ap : m_late_step_5) + ap.set_gain_2(value); + for(auto &ap : m_late_step_7) + ap.set_gain_3(value); +} + +void audio_effect_reverb::set_late_diffusion_4(double value) +{ + for(auto &ap : m_late_step_5) + ap.set_gain_1(value); + for(auto &ap : m_late_step_7) { + ap.set_gain_1(value); + ap.set_gain_2(value); + } +} + +void audio_effect_reverb::set_late_input_damping(double cutoff) +{ + m_late_input_damping.prepare_lpf(cutoff, m_sample_rate); +} + +void audio_effect_reverb::set_late_decay_0(float value) +{ + m_late_decay_0 = value; +} + +void audio_effect_reverb::set_late_decay_1(float value) +{ + m_late_decay_1 = value; +} + +void audio_effect_reverb::set_late_decay_2(float value) +{ + m_late_decay_2 = value; +} + +void audio_effect_reverb::set_late_decay_3(float value) +{ + m_late_decay_3 = value; +} + +void audio_effect_reverb::set_late_decay_f(float value) +{ + m_late_decay_f = value; +} + +void audio_effect_reverb::set_late_bass_allpass(double cutoff, double bw) +{ + m_late_bass.prepare_apf(cutoff, bw, m_sample_rate); +} + +void audio_effect_reverb::set_late_damping_2(double cutoff, double bw) +{ + m_late_damping_2.prepare_lpf(cutoff, bw, m_sample_rate); +} + +void audio_effect_reverb::set_late_spin_to_wander(double value) +{ + u32 size = find_prime(value * m_sample_rate / 1000.0); + for(auto &c : m_late_comb) + c.set_size(size); +} + + +// An IIR1 filter, either lowpass or highpass + +void audio_effect_reverb::iir1::prepare_lpf(double cutoff, u32 sample_rate) +{ + double w2 = M_PI*cutoff/sample_rate; + double tw2 = std::tan(w2); + m_b1 = tw2/(1+tw2); + m_b2 = m_b1; + m_a2 = (1-tw2)/(1+tw2); +} + +void audio_effect_reverb::iir1::prepare_hpf(double cutoff, u32 sample_rate) +{ + double w2 = M_PI*cutoff/sample_rate; + double tw2 = std::tan(w2); + m_b1 = 1/(1+tw2); + m_b2 = -m_b1; + m_a2 = (1-tw2)/(1+tw2); +} + +void audio_effect_reverb::iir1h::clear() +{ + m_y1 = 0; +} + +audio_effect_reverb::sample_t audio_effect_reverb::iir1::process(iir1h &h, sample_t x0) +{ + sample_t y0 = x0 * m_b1 + h.m_y1; + h.m_y1 = y0 * m_a2 + x0 * m_b2; + return y0; +} + + +// An IIR2 filter, lowpass (changes the phases without touching the amplitudes) + +void audio_effect_reverb::iir2::prepare_lpf(double cutoff, double bw, u32 sample_rate) +{ + double w = 2*M_PI*cutoff/sample_rate; + double s = std::sin(w); + double c = std::cos(w); + double alpha = s * std::sinh(M_LN2 / 2 * bw * w / s); + double a0 = 1/(1+alpha); + m_b0 = a0*0.5*(1 - c); + m_b1 = a0*(1 - c); + m_b2 = a0*0.5*(1 - c); + m_a1 = a0*(-2 * c); + m_a2 = a0*(1-alpha); +} + +// An IIR2 filter, allpass (changes the phases without touching the amplitudes) + +void audio_effect_reverb::iir2::prepare_apf(double cutoff, double bw, u32 sample_rate) +{ + double w = 2*M_PI*cutoff/sample_rate; + double s = std::sin(w); + double c = std::cos(w); + double alpha = s * std::sinh(M_LN2 / 2 * bw * w / s); + double a0 = 1/(1+alpha); + m_b0 = a0*(1-alpha); + m_b1 = a0*(-2 * c); + m_b2 = a0*(1+alpha); + m_a1 = a0*(-2 * c); + m_a2 = a0*(1-alpha); +} + + +void audio_effect_reverb::iir2h::clear() +{ + m_x1 = m_x2 = m_y1 = m_y2 = 0; +} + +audio_effect_reverb::sample_t audio_effect_reverb::iir2::process(iir2h &h, sample_t x0) +{ + sample_t y0 = x0 * m_b0 + h.m_x1 * m_b1 + h.m_x2 * m_b2 - m_a1 * h.m_y1 - m_a2 * h.m_y2; + h.m_x2 = h.m_x1; + h.m_x1 = x0; + h.m_y2 = h.m_y1; + h.m_y1 = y0; + return y0; +} + +// A filter to cut off DC + +void audio_effect_reverb::dccut::prepare(double cutoff, u32 sample_rate) +{ + double w = 2*M_PI*cutoff/sample_rate; + double s = std::sin(w); + double c = std::cos(w); + constexpr double s3 = 1.73205080757; // std::sqrt(3.0); + + m_gain = (s3 - 2*s)/(s + s3*c); +} + +void audio_effect_reverb::dccuth::clear() +{ + m_y1 = 0; +} + +audio_effect_reverb::sample_t audio_effect_reverb::dccut::process(dccuth &h, sample_t x0) +{ + sample_t y0 = x0 - h.m_y1 + m_gain * h.m_y2; + h.m_y1 = x0; + h.m_y2 = y0; + return y0; +} + + +// A delay buffer, also known as a reverb buffer + +void audio_effect_reverb::delay_buffer::clear() +{ + memset(m_samples, 0, sizeof(m_samples)); + m_index = 0; +} + +void audio_effect_reverb::delay_buffer::push(sample_t value) +{ + m_index = (m_index + 1) & D_MASK; + m_samples[m_index] = value; +} + +audio_effect_reverb::sample_t audio_effect_reverb::delay_buffer::get(u32 dist) const +{ + return m_samples[(m_index - dist) & D_MASK]; +} + +audio_effect_reverb::sample_t audio_effect_reverb::delay_buffer::geti(float dist) const +{ + u32 di = u32(dist); + double t = dist - di; + sample_t s1 = m_samples[(m_index - di) & D_MASK]; + sample_t s2 = m_samples[(m_index - di - 1) & D_MASK]; + return s1 + (s2-s1)*t; +} + + +// A delay with the storage for taps, e.g. the distances in it to +// lookup samples + +void audio_effect_reverb::delay::clear() +{ + m_delay_buffer.clear(); + // Don't clear the taps positions +} + +void audio_effect_reverb::delay::push(sample_t value) +{ + m_delay_buffer.push(value); +} + +void audio_effect_reverb::delay::set_tap(u32 tap, u32 dist) +{ + if(m_taps.size() <= tap) + m_taps.resize(tap+1); + m_taps[tap] = dist; +} + +audio_effect_reverb::sample_t audio_effect_reverb::delay::get(u32 tap) const +{ + return m_delay_buffer.get(m_taps[tap]); +} + + + + +// Allpass. One category of allpass filters (there are others) is a +// delay line with a loop. The design developed by Manfred Schroeder +// has: +// - output = delay_output * (decay - gain**2) - input * gain +// - delay_input = delay_output * gain + input +// +// When disabled, decay is 1. +// +// Some variants have modulation, which change the delay and the gain +// dynamically. Some variants work with multiple delay lines, for a +// final result built similarly. + + +// Allpass variant without modulation or decay. + +void audio_effect_reverb::allpass::clear() +{ + m_delay_buffer.clear(); + m_gain = 0; + m_delay = 0; +} + +void audio_effect_reverb::allpass::set_gain(float base) +{ + m_gain = base; +} + +void audio_effect_reverb::allpass::set_delay(u32 base) +{ + m_delay = base; +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass::process(sample_t input) +{ + sample_t tap = m_delay_buffer.get(m_delay); + sample_t loop = input + tap * m_gain; + sample_t output = tap - loop * m_gain; + m_delay_buffer.push(loop); + return output; +} + + +// Allpass variant with modulation, no decay. + +void audio_effect_reverb::allpass_m::clear() +{ + m_delay_buffer.clear(); + m_base_gain = 0; + m_base_delay = 0; + m_mod_delay = 0; +} + +void audio_effect_reverb::allpass_m::set_gain(float base) +{ + m_base_gain = base; +} + +void audio_effect_reverb::allpass_m::set_delay(float base, float mod) +{ + m_base_delay = base; + m_mod_delay = mod; +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass_m::process(sample_t input, float delay_mod, float gain_mod) +{ + float gain = m_base_gain + gain_mod; + float delay = m_base_delay + (1 + delay_mod) * m_mod_delay; + + sample_t tap = m_delay_buffer.geti(delay); + sample_t loop = input + tap * gain; + sample_t output = tap - loop * gain; + m_delay_buffer.push(loop); + return output; +} + + +// Allpass variant with modulation and decay. + +void audio_effect_reverb::allpass_md::clear() +{ + m_delay_buffer.clear(); + m_base_gain = 0; + m_base_delay = 0; + m_mod_delay = 0; + m_decay = 0; +} + +void audio_effect_reverb::allpass_md::set_gain(float base) +{ + m_base_gain = base; +} + +void audio_effect_reverb::allpass_md::set_delay(float base, float mod) +{ + m_base_delay = base; + m_mod_delay = mod; +} + +void audio_effect_reverb::allpass_md::set_decay(float base) +{ + m_decay = base; +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass_md::process(sample_t input, float delay_mod, float gain_mod) +{ + float gain = m_base_gain + gain_mod; + float delay = m_base_delay + (1 + delay_mod) * m_mod_delay; + + sample_t tap = m_delay_buffer.geti(delay); + sample_t loop = input + tap * gain; + sample_t output = m_decay * tap - loop * gain; + m_delay_buffer.push(loop); + return output; +} + + +// Allpass variant with dual buffer, decay and taps + +void audio_effect_reverb::allpass2::clear() +{ + m_delay_buffer_1.clear(); + m_delay_buffer_2.clear(); + m_gain_1 = 0; + m_gain_2 = 0; + m_delay_1 = 0; + m_delay_2 = 0; + m_decay_1 = 0; + m_decay_2 = 0; + // Don't clear the taps positions +} + +void audio_effect_reverb::allpass2::set_gain_1(float base) +{ + m_gain_1 = base; +} + +void audio_effect_reverb::allpass2::set_gain_2(float base) +{ + m_gain_2 = base; +} + +void audio_effect_reverb::allpass2::set_delays(u32 base_1, u32 base_2) +{ + m_delay_1 = base_1; + m_delay_2 = base_2; +} + +void audio_effect_reverb::allpass2::set_decay_1(float base) +{ + m_decay_1 = base; +} + +void audio_effect_reverb::allpass2::set_decay_2(float base) +{ + m_decay_2 = base; +} + +void audio_effect_reverb::allpass2::set_tap_1(u32 tap, u32 dist) +{ + if(m_taps_1.size() <= tap) + m_taps_1.resize(tap+1); + m_taps_1[tap] = dist; +} + +void audio_effect_reverb::allpass2::set_tap_2(u32 tap, u32 dist) +{ + if(m_taps_2.size() <= tap) + m_taps_2.resize(tap+1); + m_taps_2[tap] = dist; +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass2::get_1(u32 tap) const +{ + return m_delay_buffer_1.get(m_taps_1[tap]); +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass2::get_2(u32 tap) const +{ + return m_delay_buffer_2.get(m_taps_2[tap]); +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass2::process(sample_t input) +{ + sample_t tap_2 = m_delay_buffer_2.get(m_delay_2); + sample_t loop_2 = input + tap_2 * m_gain_2; + sample_t output_2 = m_decay_2 * tap_2 - loop_2 * m_gain_2; + + sample_t tap_1 = m_delay_buffer_1.get(m_delay_1); + sample_t loop_1 = loop_2 + tap_1 * m_gain_1; + sample_t output_1 = m_decay_1 * tap_1 - loop_1 * m_gain_1; + + m_delay_buffer_2.push(output_1); + m_delay_buffer_1.push(loop_1); + + return output_2; +} + + +// Allpass variant with triple buffer, decay, taps and modulation for the first buffer delay + +void audio_effect_reverb::allpass3m::clear() +{ + m_delay_buffer_1.clear(); + m_delay_buffer_2.clear(); + m_delay_buffer_3.clear(); + m_gain_1 = 0; + m_gain_2 = 0; + m_gain_3 = 0; + m_base_delay_1 = 0; + m_mod_delay_1 = 0; + m_delay_2 = 0; + m_delay_3 = 0; + m_decay_1 = 0; + m_decay_2 = 0; + m_decay_3 = 0; + // Don't clear the taps positions +} + + +void audio_effect_reverb::allpass3m::set_gain_1(float base) +{ + m_gain_1 = base; +} + +void audio_effect_reverb::allpass3m::set_gain_2(float base) +{ + m_gain_2 = base; +} + +void audio_effect_reverb::allpass3m::set_gain_3(float base) +{ + m_gain_3 = base; +} + +void audio_effect_reverb::allpass3m::set_delays(u32 base_1, u32 mod_1, u32 base_2, u32 base_3) +{ + m_base_delay_1 = base_1; + m_mod_delay_1 = mod_1; + m_delay_2 = base_2; + m_delay_3 = base_3; +} + +void audio_effect_reverb::allpass3m::set_decay_1(float base) +{ + m_decay_1 = base; +} + +void audio_effect_reverb::allpass3m::set_decay_2(float base) +{ + m_decay_2 = base; +} + +void audio_effect_reverb::allpass3m::set_decay_3(float base) +{ + m_decay_3 = base; +} + +void audio_effect_reverb::allpass3m::set_tap_1(u32 tap, u32 dist) +{ + if(m_taps_1.size() <= tap) + m_taps_1.resize(tap+1); + m_taps_1[tap] = dist; +} + +void audio_effect_reverb::allpass3m::set_tap_2(u32 tap, u32 dist) +{ + if(m_taps_2.size() <= tap) + m_taps_2.resize(tap+1); + m_taps_2[tap] = dist; +} + +void audio_effect_reverb::allpass3m::set_tap_3(u32 tap, u32 dist) +{ + if(m_taps_3.size() <= tap) + m_taps_3.resize(tap+1); + m_taps_3[tap] = dist; +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass3m::get_1(u32 tap) const +{ + return m_delay_buffer_1.get(m_taps_1[tap]); +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass3m::get_2(u32 tap) const +{ + return m_delay_buffer_2.get(m_taps_2[tap]); +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass3m::get_3(u32 tap) const +{ + return m_delay_buffer_3.get(m_taps_3[tap]); +} + +audio_effect_reverb::sample_t audio_effect_reverb::allpass3m::process(sample_t input, float delay_mod) +{ + sample_t tap_3 = m_delay_buffer_3.get(m_delay_3); + sample_t loop_3 = input + tap_3 * m_gain_3; + sample_t output_3 = m_decay_3 * tap_3 - loop_3 * m_gain_3; + + sample_t tap_2 = m_delay_buffer_2.get(m_delay_2); + sample_t loop_2 = loop_3 + tap_2 * m_gain_2; + sample_t output_2 = m_decay_2 * tap_2 - loop_2 * m_gain_2; + + float delay_1 = m_base_delay_1 + (1+delay_mod) * m_mod_delay_1; + + sample_t tap_1 = m_delay_buffer_1.geti(delay_1); + sample_t loop_1 = loop_2 + tap_1 * m_gain_1; + sample_t output_1 = m_decay_1 * tap_1 - loop_1 * m_gain_1; + + m_delay_buffer_3.push(output_2); + m_delay_buffer_2.push(output_1); + m_delay_buffer_1.push(loop_1); + + return output_3; +} + +// A comb filter, another kind of delay which loops + +void audio_effect_reverb::comb::clear() +{ + m_delay_buffer.clear(); + m_filter_history = 0; + m_size = 0; +} + +void audio_effect_reverb::comb::set_size(u32 size) +{ + m_size = size; +} + +audio_effect_reverb::sample_t audio_effect_reverb::comb::process(sample_t input, float feedback) +{ + sample_t tap = m_delay_buffer.get(m_size) * feedback + input; + m_delay_buffer.push(tap); + return tap; +} + + + + +// Pink noise generator + +void audio_effect_reverb::pink::clear() +{ + m_index = SIZE; + memset(m_buffer, 0, sizeof(m_buffer)); +} + +audio_effect_reverb::sample_t audio_effect_reverb::pink::process() +{ + if(m_index == SIZE) { + m_index = 0; + // Generate a fractal pattern using the Midpoint Displacement + // Method with a fractal dimension of 0.5. + + m_buffer[0] = 0; + sample_t r = 2*(0.5*0.5) + 0.3; + for(u32 l = SIZE; l > 1; l >>= 1) { + for(u32 c = 0; c != SIZE; c += l) + m_buffer[c + l/2] = std::clamp((m_buffer[c] + m_buffer[(c+l) & (SIZE-1)]) / 2 + r * m_dis(m_rng), -1.f, 1.f); + + r /= M_SQRT2; // 2**0.5 + } + } + return m_buffer[m_index++]; +} + + +// LFO with sine-type output + +void audio_effect_reverb::lfo::clear() +{ + m_count = 0; + m_c = 1; + m_s = 0; +} + +void audio_effect_reverb::lfo::prepare(double speed, u32 sample_rate) +{ + float w = 2*M_PI*speed/sample_rate; + m_rc = std::cos(w); + m_rs = std::sin(w); +} + +float audio_effect_reverb::lfo::process() +{ + float r = m_s; + float nc = m_c * m_rc - m_s * m_rs; + float ns = m_c * m_rs + m_s * m_rc; + if(m_count++ == 10000) { + m_count = 0; + float l = std::sqrt(nc*nc + ns*ns); + nc /= l; + ns /= l; + } + m_c = nc; + m_s = ns; + return r; +} + + +// Find the lowest prime (or 1) more-or-equal to a given number +bool audio_effect_reverb::is_prime(u32 value) +{ + if(value == 1 || value == 2) + return value; + if(!(value & 1)) + return false; + for(u32 d = 3; d*d <= value; d += 2) + if(!(value % d)) + return false; + return true; +} + +u32 audio_effect_reverb::find_prime(u32 value) +{ + if(value <= 1) + return 1; + + while(!is_prime(value)) + value++; + + return value; +} + +// The complete effect implementation + void audio_effect_reverb::apply(const emu::detail::output_buffer_flat &src, emu::detail::output_buffer_flat &dest) { - copy(src, dest); + if(m_mode == 0) { + copy(src, dest); + return; + } + + u32 samples = src.available_samples(); + dest.prepare_space(samples); + + for(u32 i=0; i != samples; i++) { + for(u32 channel = 0; channel != m_channels; channel++) + m_early_in[channel] = *src.ptrs(channel, i); + + // We start by the early reflection + + for(u32 channel = 0; channel != m_channels; channel++) { + auto &d = m_early_delays[channel]; + + // The input audio is pushed to a reverb buffer and a + // number of taps are applied to it. + + // The taps each have a delay and a gain, they are set in + // a table with 15 different presets. How they were build + // does not seem documented. The delays are multiplied by + // the room size factor, between 0.4 and 3.6, build from + // the 0-100 room size. + + // Taps are different for left and right, we use left for + // mono. We can't just mean left and right because some + // presets have different numbers of taps. + + // RoomReverb only uses preset 0, we keep the others + // available though. + + d.push(m_early_in[channel]); + sample_t w1 = 0; + const auto &tmap = tap_maps[m_early_tap_setup]; + u32 side = m_ch_type[channel] == T_RIGHT ? 1 : 0; + for(u32 tap = 0; tap != tmap.m_count[side]; tap++) + w1 += d.get(m_early_tap_dists[side][tap]) * tmap.m_gain[side][tap]; + + // We end up with a current value for the channel and we + // push it in another delay for cross-channel reflections + // with the original signal added + + m_early_xdelays[channel].push(w1 + m_early_in[channel]); + m_early_wet[channel] = w1; + } + + for(u32 channel = 0; channel != m_channels; channel++) { + sample_t w2; + if(m_ch_type[channel] == T_MONO) + w2 = m_early_wet[channel]; + + else { + // For stereo pairs, once we have the per-channel + // early reverb signal we use a proportion between + // that raw signal and all the signals coming from the + // other channels after they go through an allpass to + // muddy the phases under 750Hz, as reflections are + // known to do, without changing the actual levels. + // That gives us a new composite per-channel signal. + // The proportion comes from the stereo width knob, + // where 0 is pure cross-channel reflection and 100 is + // pure raw signal. + int other = m_ch_pair[channel]; + w2 = m_wet1 * m_early_wet[channel] + m_wet2 * m_early_cross_allpass.process(m_early_cross_allpass_h[channel], m_early_xdelays[other].get(m_early_xdelays_dist)); + } + + // The per-channel result then goes through an allpass + // with cutoff at 150Hz (again, phases only), a 4Hz + // highpass that is essentially a DC cut, and a lowpass + // set by the knob "early damping". + + m_early_out[channel] = m_early_lpf.process(m_early_lpf_h[channel], m_early_hpf.process(m_early_hpf_h[channel], m_early_diffusion_allpass.process(m_early_diffusion_allpass_h[channel], w2))); + } + + // The late reverb input is the original signal plus a part of + // the early reverb signal, depending on the early-to-late + // level. + + for(u32 channel = 0; channel != m_channels; channel++) + m_late_in[channel] = m_early_in[channel] + m_actual_early_to_late_level * m_early_out[channel]; + + + // Then it's the Progenitor 2 reverb, and it's... complicated + sample_t noise = m_late_noise.process(); + float lfo1 = m_late_lfo1_lpf.process(m_late_lfo1_lpf_h, (m_late_lfo1.process() + m_late_modulation_noise_1*noise)*m_late_wander_actual); + float lfo2 = m_late_lfo2_lpf.process(m_late_lfo2_lpf_h, m_late_lfo2.process() * m_late_wander_actual); + noise *= m_late_modulation_noise_2; + + for(u32 channel = 0; channel != m_channels; channel++) { + sample_t value = m_late_dccut.process(m_late_dccut_h[channel], m_late_in[channel]); + if(m_ch_type[channel] == T_RIGHT) + for(auto &ap : m_late_input_diffusion[channel]) + value = ap.process(value, lfo1, (i & 1) ? noise : -noise); + else + for(auto &ap : m_late_input_diffusion[channel]) + value = ap.process(value, (i & 1) ? lfo1 : -lfo1, noise); + m_late_diff[channel] = value; + for(auto &ap : m_late_cross_diffusion[channel]) + value = ap.process(value); + m_late_cross[channel] = value; + } + + for(u32 channel = 0; channel != m_channels; channel++) + if(m_ch_type[channel] == T_MONO) + m_late_diff[channel] = m_late_input_damping.process(m_late_input_damping_h[channel], m_late_diff[channel] + m_late_crossfeed * m_late_cross[channel]); + else + m_late_diff[channel] = m_late_input_damping.process(m_late_input_damping_h[channel], m_late_diff[channel] + m_late_crossfeed * m_late_cross[m_ch_pair[channel]]); + + for(u32 cchan = 0; cchan != m_channels; cchan++) + m_late_cross2[cchan] = m_late_step_8[m_ch_pair[cchan]].get(0); + + for(u32 cchan = 0; cchan != m_channels; cchan++) { + bool right = m_ch_type[cchan] == T_RIGHT; + sample_t c1 = m_late_cross2[cchan]; + sample_t c2 = m_late_bass.process(m_late_bass_h[cchan], c1); + sample_t c3 = m_late_damping_2.process(m_late_damping_2_h[cchan], c2); + sample_t v0 = m_late_diff[cchan] + m_late_loop_decay * (c1 + m_late_bass_boost * c3); + sample_t v1 = m_late_damping_1.process(m_late_damping_1_h[cchan], v0); + sample_t v2 = m_late_step_1[cchan].process(v1, right ? -lfo1 : lfo1, right ? -noise : noise); + sample_t v3 = m_late_step_2[cchan].process(v2); + sample_t v4 = m_late_step_3[cchan].process(v3, right ? lfo1 : -lfo1, right ? noise : -noise); + sample_t v5 = m_late_step_4[cchan].process(v4); + sample_t v6 = m_late_step_5[cchan].process(v5); + sample_t v7 = m_late_step_6[cchan].process(v6); + sample_t v8 = m_late_step_7[cchan].process(v7, right ? lfo1 : -lfo1); + m_late_step_8[cchan].push(v8); + } + + for(u32 cchan = 0; cchan != m_channels; cchan++) { + u32 pchan = m_ch_pair[cchan]; + bool right = m_ch_type[cchan] == T_RIGHT; + sample_t v8 = 0.469 * m_late_step_4[cchan].get(1) + + 0.219 * (m_late_step_6[cchan].get(1) + - m_late_step_6[pchan].get(2) + + m_late_step_6[cchan].get(3) + - m_late_step_4[pchan].get(2) + - m_late_step_6[pchan].get(4)) + + 0.064 * (m_late_step_8[cchan].get(2) + + m_late_step_5[cchan].get_1(0) + + m_late_step_5[cchan].get_2(0) + - m_late_step_5[pchan].get_2(1) + + m_late_step_7[cchan].get_1(0) + + m_late_step_7[cchan].get_2(0) + + m_late_step_7[cchan].get_3(0) + - m_late_step_7[pchan].get_2(1)) + + 0.045 * m_late_step_8[cchan].get(3); + + if(right) + v8 -= 0.219 * m_late_step_8[pchan].get(1); + + sample_t v9 = m_late_comb[cchan].process(v8, right ? -lfo2 : lfo2); + sample_t v10 = m_late_output_lpf.process(m_late_output_lpf_h[cchan], v9); + sample_t v11 = m_late_final_delay[cchan].process(v10); + + m_late_pre_out[cchan] = v11; + } + + for(u32 cchan = 0; cchan != m_channels; cchan++) + if(m_ch_type[cchan] == T_MONO) + m_late_out[cchan] = m_late_pre_out[cchan]; + else + m_late_out[cchan] = m_wet1 * m_late_pre_out[cchan] + m_wet2 * m_late_pre_out[m_ch_pair[cchan]]; + + for(u32 channel = 0; channel != m_channels; channel++) + *dest.ptrw(channel, i) = m_early_in[channel] * m_actual_dry_level + m_early_out[channel] * m_actual_early_level + m_late_out[channel] * m_actual_late_level; + } + + dest.commit(samples); } diff --git a/src/emu/audio_effects/reverb.h b/src/emu/audio_effects/reverb.h index 36aaabb697b..bfb580380a2 100644 --- a/src/emu/audio_effects/reverb.h +++ b/src/emu/audio_effects/reverb.h @@ -8,10 +8,12 @@ #include "aeffect.h" +#include + class audio_effect_reverb : public audio_effect { public: - audio_effect_reverb(u32 sample_rate, audio_effect *def); + audio_effect_reverb(speaker_device *speaker, u32 sample_rate, audio_effect *def); virtual ~audio_effect_reverb() = default; virtual int type() const override { return REVERB; } @@ -19,6 +21,420 @@ public: virtual void config_load(util::xml::data_node const *ef_node) override; virtual void config_save(util::xml::data_node *ef_node) const override; virtual void default_changed() override; + + void set_mode(u32 mode); + void set_early_tap_setup(u32 index); + void set_early_damping(double cutoff); + void set_stereo_width(double width); + void set_early_room_size(double size); + void set_late_room_size(double size); + void set_late_spin(double speed); + void set_late_wander(double wander); + void set_late_diffusion(double value); + void set_late_damping(double cutoff); + void set_late_predelay(double delay); + void set_late_global_decay(float value); + void set_dry_level(double level); + void set_early_level(double level); + void set_late_level(double level); + void set_early_to_late_level(double level); + + u32 mode() const { return m_mode; } + u32 early_tap_setup() const { return m_early_tap_setup; } + double early_damping() const { return m_early_damping; } + double stereo_width() const { return m_stereo_width; } + double early_room_size() const { return m_early_room_size; } + double late_room_size() const { return m_late_room_size; } + double late_spin() const { return m_late_spin; } + double late_wander() const { return m_late_wander; } + double late_diffusion() const { return m_late_diffusion; } + double late_damping() const { return m_late_damping; } + double late_predelay() const { return m_late_predelay; } + float late_global_decay() const { return m_late_global_decay; } + double dry_level() const { return m_dry_level; } + double early_level() const { return m_early_level; } + double late_level() const { return m_late_level; } + double early_to_late_level() const { return m_early_to_late_level; } + + bool isset_mode() const { return m_isset_mode; } + bool isset_early_tap_setup() const { return m_isset_early_tap_setup; } + bool isset_early_damping() const { return m_isset_early_damping; } + bool isset_stereo_width() const { return m_isset_stereo_width; } + bool isset_early_room_size() const { return m_isset_early_room_size; } + bool isset_late_room_size() const { return m_isset_late_room_size; } + bool isset_late_spin() const { return m_isset_late_spin; } + bool isset_late_wander() const { return m_isset_late_wander; } + bool isset_late_diffusion() const { return m_isset_late_diffusion; } + bool isset_late_damping() const { return m_isset_late_damping; } + bool isset_late_predelay() const { return m_isset_late_predelay; } + bool isset_late_global_decay() const { return m_isset_late_global_decay; } + bool isset_dry_level() const { return m_isset_dry_level; } + bool isset_early_level() const { return m_isset_early_level; } + bool isset_late_level() const { return m_isset_late_level; } + bool isset_early_to_late_level() const { return m_isset_early_to_late_level; } + + void reset_mode(); + void reset_early_tap_setup(); + void reset_early_damping(); + void reset_stereo_width(); + void reset_early_room_size(); + void reset_late_room_size(); + void reset_late_spin(); + void reset_late_wander(); + void reset_late_diffusion(); + void reset_late_damping(); + void reset_late_predelay(); + void reset_late_global_decay(); + void reset_dry_level(); + void reset_early_level(); + void reset_late_level(); + void reset_early_to_late_level(); + + static u32 preset_count(); + static const char *preset_name(u32 id); + static u32 early_tap_setup_count(); + static const char *early_tap_setup_name(u32 id); + + void load_preset(u32 id); + +private: + struct preset { + const char *name; + u32 early_tap_setup; + double early_damping; + double stereo_width; + double early_room_size; + double late_room_size; + double late_spin; + double late_wander; + double late_diffusion; + double late_damping; + double late_predelay; + float late_global_decay; + double dry_level; + double early_level; + double late_level; + double early_to_late_level; + }; + + static const preset presets[]; + + enum { INPUT_DIFFUSION_ALLPASS = 10, CROSS_DIFFUSION_ALLPASS = 4 }; + + struct early_reverb_tap_map { + const char *name; + u32 m_count[2]; + float m_delay[2][18]; + float m_gain[2][18]; + }; + + static const early_reverb_tap_map tap_maps[15]; + + struct delay_buffer { + enum { D_MASK = 8191 }; + sample_t m_samples[D_MASK+1]; + u32 m_index; + + void clear(); + void push(sample_t value); + sample_t get(u32 dist) const; + sample_t geti(float dist) const; // Get with interpolation + delay_buffer() { clear(); } + }; + + struct delay { + delay_buffer m_delay_buffer; + std::vector m_taps; + void clear(); + void set_tap(u32 tap, u32 dist); + void push(sample_t value); + sample_t get(u32 tap) const; + sample_t process(sample_t value) { push(value); return get(0); } + delay() { clear(); } + }; + + struct iir1h { + sample_t m_y1; + void clear(); + iir1h() { clear(); } + }; + + struct iir1 { + float m_a2, m_b1, m_b2; + sample_t process(iir1h &h, sample_t x0); + void prepare_lpf(double cutoff, u32 sample_rate); + void prepare_hpf(double cutoff, u32 sample_rate); + }; + + struct iir2h { + sample_t m_x1, m_x2, m_y1, m_y2; + void clear(); + iir2h() { clear(); } + }; + + struct iir2 { + float m_a1, m_a2, m_b0, m_b1, m_b2; + sample_t process(iir2h &h, sample_t x0); + void prepare_lpf(double cutoff, double bw, u32 sample_rate); + void prepare_apf(double cutoff, double bw, u32 sample_rate); + }; + + struct dccuth { + float m_y1, m_y2; + void clear(); + dccuth() { clear(); } + }; + + struct dccut { + float m_gain; + sample_t process(dccuth &h, sample_t x0); + void prepare(double cutoff, u32 sample_rate); + }; + + struct pink { + enum { SIZE = 1 << 15 }; + std::mt19937 m_rng; + std::uniform_real_distribution m_dis; + sample_t m_buffer[SIZE]; + u32 m_index; + + void clear(); + pink() : m_rng(std::random_device()()), m_dis(-1, 1) { clear(); } + sample_t process(); + }; + + struct lfo { + float m_c, m_s, m_rc, m_rs; + u32 m_count; + + void clear(); + lfo() : m_rc(1), m_rs(0) { clear(); } + void prepare(double speed, u32 sample_rate); + float process(); + }; + + struct allpass { + delay_buffer m_delay_buffer; + float m_gain; + u32 m_delay; + + void clear(); + sample_t process(sample_t input); + void set_gain(float base); + void set_delay(u32 base); + allpass() { clear(); } + }; + + struct allpass_m { + delay_buffer m_delay_buffer; + float m_base_gain; + float m_base_delay, m_mod_delay; + + void clear(); + sample_t process(sample_t input, float delay_mod, float gain_mod); + void set_gain(float base); + void set_delay(float base, float mod); + allpass_m() { clear(); } + }; + + struct allpass_md { + delay_buffer m_delay_buffer; + float m_base_gain, m_decay; + float m_base_delay, m_mod_delay; + + void clear(); + sample_t process(sample_t input, float delay_mod, float gain_mod); + void set_gain(float base); + void set_delay(float base, float mod); + void set_decay(float base); + allpass_md() { clear(); } + }; + + struct allpass2 { + delay_buffer m_delay_buffer_1, m_delay_buffer_2; + float m_gain_1, m_gain_2, m_decay_1, m_decay_2; + u32 m_delay_1, m_delay_2; + std::vector m_taps_1, m_taps_2; + + void clear(); + sample_t process(sample_t input); + void set_gain_1(float base); + void set_gain_2(float base); + void set_delays(u32 base_1, u32 base_2); + void set_decay_1(float base); + void set_decay_2(float base); + void set_tap_1(u32 tap, u32 dist); + void set_tap_2(u32 tap, u32 dist); + sample_t get_1(u32 tap) const; + sample_t get_2(u32 tap) const; + allpass2() { clear(); } + }; + + struct allpass3m { + delay_buffer m_delay_buffer_1, m_delay_buffer_2, m_delay_buffer_3; + float m_gain_1, m_gain_2, m_gain_3, m_decay_1, m_decay_2, m_decay_3; + u32 m_base_delay_1, m_mod_delay_1, m_delay_2, m_delay_3; + std::vector m_taps_1, m_taps_2, m_taps_3; + + void clear(); + sample_t process(sample_t input, float delay_mod); + void set_gain_1(float base); + void set_gain_2(float base); + void set_gain_3(float base); + void set_delays(u32 base_1, u32 mod_1, u32 base_2, u32 base_3); + void set_decay_1(float base); + void set_decay_2(float base); + void set_decay_3(float base); + void set_tap_1(u32 tap, u32 dist); + void set_tap_2(u32 tap, u32 dist); + void set_tap_3(u32 tap, u32 dist); + sample_t get_1(u32 tap) const; + sample_t get_2(u32 tap) const; + sample_t get_3(u32 tap) const; + allpass3m() { clear(); } + }; + + struct comb { + delay_buffer m_delay_buffer; + sample_t m_filter_history; + u32 m_size; + + void clear(); + sample_t process(sample_t input, float feedback); + void set_size(u32 size); + comb() { clear(); } + }; + + enum ch_type_t { T_MONO, T_LEFT, T_RIGHT }; + + u32 m_mode; + u32 m_early_tap_setup; + double m_early_damping; + double m_stereo_width; + double m_early_room_size; + double m_late_room_size; + double m_late_spin; + double m_late_wander; + double m_late_diffusion; + double m_late_damping; + double m_late_predelay; + float m_late_global_decay; + double m_dry_level; + double m_early_level; + double m_late_level; + double m_early_to_late_level; + + bool m_isset_mode; + bool m_isset_early_tap_setup; + bool m_isset_early_damping; + bool m_isset_stereo_width; + bool m_isset_early_room_size; + bool m_isset_late_room_size; + bool m_isset_late_spin; + bool m_isset_late_wander; + bool m_isset_late_diffusion; + bool m_isset_late_damping; + bool m_isset_late_predelay; + bool m_isset_late_global_decay; + bool m_isset_dry_level; + bool m_isset_early_level; + bool m_isset_late_level; + bool m_isset_early_to_late_level; + + + + std::vector m_ch_type; + std::vector m_ch_pair; + + iir1 m_early_lpf, m_early_hpf; + iir2 m_early_diffusion_allpass, m_early_cross_allpass; + dccut m_late_dccut; + pink m_late_noise; + lfo m_late_lfo1, m_late_lfo2; + iir1 m_late_lfo1_lpf, m_late_lfo2_lpf, m_late_input_damping, m_late_damping_1; + iir2 m_late_bass, m_late_damping_2, m_late_output_lpf; + + std::vector m_early_lpf_h, m_early_hpf_h; + std::vector m_early_diffusion_allpass_h, m_early_cross_allpass_h; + std::vector m_late_dccut_h; + iir1h m_late_lfo1_lpf_h, m_late_lfo2_lpf_h; + std::vector m_early_delays; + std::vector m_early_xdelays; + std::vector> m_late_input_diffusion; + std::vector> m_late_cross_diffusion; + std::vector m_late_input_damping_h, m_late_damping_1_h; + std::vector m_late_bass_h, m_late_damping_2_h, m_late_output_lpf_h; + std::vector m_late_step_1, m_late_step_3; + std::vector m_late_step_5; + std::vector m_late_step_7; + std::vector m_late_step_2, m_late_step_4, m_late_step_6, m_late_step_8; + std::vector m_late_comb; + std::vector m_late_final_delay; + + std::vector m_early_in, m_early_wet, m_early_out; + std::vector m_late_in, m_late_diff, m_late_cross, m_late_cross2, m_late_pre_out, m_late_out; + + float m_wet1, m_wet2; + float m_early_room_size_ratio; + + float m_late_room_size_ratio; + float m_late_wander_actual; + float m_late_modulation_noise_1, m_late_modulation_noise_2; + float m_late_crossfeed; + float m_late_decay_0, m_late_decay_1, m_late_decay_2, m_late_decay_3, m_late_decay_f; + float m_late_loop_decay, m_late_bass_boost; + + float m_actual_dry_level, m_actual_early_level, m_actual_late_level, m_actual_early_to_late_level; + + u32 m_early_tap_dists[2][18]; + u32 m_early_xdelays_dist; + + const preset *m_default_preset; + + void commit_early_tap_setup(); + void commit_early_damping(); + void commit_stereo_width(); + void commit_early_room_size(); + void commit_late_room_size(); + void commit_late_decay(); + void commit_late_spin(); + void commit_late_wander(); + void commit_late_diffusion(); + void commit_late_damping(); + void commit_late_predelay(); + void commit_dry_level(); + void commit_early_level(); + void commit_late_level(); + void commit_early_to_late_level(); + + void set_early_hpf(double cutoff); + void set_early_diffusion_ap(double cutoff, double bw); + void set_early_cross_ap(double cutoff, double bw); + void set_early_multichannel_delay(double delay); + + void set_late_dccut(double cutoff); + void set_late_spin_limit_1(double cutoff); + void set_late_spin_limit_2(double cutoff); + void set_late_modulation_noise_1(double mod); + void set_late_modulation_noise_2(double mod); + void set_late_diffusion_1(double value); + void set_late_diffusion_2(double value); + void set_late_diffusion_3(double value); + void set_late_diffusion_4(double value); + void set_late_input_damping(double cutoff); + void set_late_damping_2(double cutoff, double bw); + void set_late_decay_0(float value); + void set_late_decay_1(float value); + void set_late_decay_2(float value); + void set_late_decay_3(float value); + void set_late_decay_f(float value); + void set_late_bass_allpass(double cutoff, double bw); + void set_late_spin_to_wander(double value); + + static const preset *find_preset(std::string name); + + static bool is_prime(u32 value); + static u32 find_prime(u32 v); }; #endif diff --git a/src/emu/sound.cpp b/src/emu/sound.cpp index 525b4c4f7b6..809fdbbfa6e 100644 --- a/src/emu/sound.cpp +++ b/src/emu/sound.cpp @@ -308,7 +308,8 @@ sound_stream::sound_stream(device_t &device, u32 inputs, u32 outputs, u32 sample m_sync_timer(nullptr), m_callback(std::move(callback)) { - sound_assert(outputs > 0 || inputs > 0); + if(inputs == 0 && outputs == 0) + fatalerror("Device %s requiring to create a stream without inputs or outputs\n", device.tag()); // create a name m_name = m_device.name(); @@ -854,7 +855,7 @@ void sound_manager::after_devices_init() // Create the default effect chain for(u32 effect = 0; effect != audio_effect::COUNT; effect++) - m_default_effects.emplace_back(audio_effect::create(effect, machine().sample_rate(), nullptr)); + m_default_effects.emplace_back(audio_effect::create(effect, nullptr, machine().sample_rate(), nullptr)); // Inventory speakers and microphones m_outputs_count = 0; @@ -862,7 +863,7 @@ void sound_manager::after_devices_init() dev.set_id(m_speakers.size()); m_speakers.emplace_back(speaker_info(dev, machine().sample_rate(), m_outputs_count)); for(u32 effect = 0; effect != audio_effect::COUNT; effect++) - m_speakers.back().m_effects[effect].m_effect.reset(audio_effect::create(effect, machine().sample_rate(), m_default_effects[effect].get())); + m_speakers.back().m_effects[effect].m_effect.reset(audio_effect::create(effect, &dev, machine().sample_rate(), m_default_effects[effect].get())); m_outputs_count += dev.inputs(); } @@ -1980,17 +1981,24 @@ void sound_manager::generate_mapping() // Find where to map a sound_io channel into a node's channels depending on their positions -std::vector sound_manager::find_channel_mapping(const std::array &position, const osd::audio_info::node_info *node) +std::vector sound_manager::find_channel_mapping(const osd::channel_position &pos, const osd::audio_info::node_info *node) { std::vector result; - if(position[0] == 0 && position[1] == 0 && position[2] == 0) + if(pos.is_lfe()) { + for(u32 port = 0; port != node->m_port_positions.size(); port++) + if(node->m_port_positions[port].is_lfe()) + result.push_back(port); return result; + } + if(pos.is_onreq()) + return result; + double best_dist = -1; for(u32 port = 0; port != node->m_port_positions.size(); port++) - if(sound_io_device::mapping_allowed(node->m_port_positions[port])) { - double dx = position[0] - node->m_port_positions[port][0]; - double dy = position[1] - node->m_port_positions[port][1]; - double dz = position[2] - node->m_port_positions[port][2]; + if(!node->m_port_positions[port].is_onreq() && !node->m_port_positions[port].is_lfe()) { + double dx = pos.m_x - node->m_port_positions[port].m_z; + double dy = pos.m_y - node->m_port_positions[port].m_y; + double dz = pos.m_z - node->m_port_positions[port].m_z; double dist = dx*dx + dy*dy + dz*dz; if(best_dist == -1 || dist < best_dist) { best_dist = dist; @@ -2496,12 +2504,10 @@ void sound_manager::mapping_update() if(port_count < node.m_sources) port_count = node.m_sources; for(uint32_t port = 0; port != port_count; port++) - LOG_OUTPUT_FUNC(" %s %s [%g %g %g]\n", - (port < node.m_sinks) ? ((port < node.m_sources) ? "<>" : ">") : "<", - node.m_port_names[port], - node.m_port_positions[port][0], - node.m_port_positions[port][1], - node.m_port_positions[port][2]); + LOG_OUTPUT_FUNC(" %s %s [%s]\n", + port < node.m_sinks ? port < node.m_sources ? "<>" : ">" : "<", + node.m_port_names[port].c_str(), + node.m_port_positions[port].name()); } LOG_OUTPUT_FUNC("- streams:\n"); for(const auto &stream : m_osd_info.m_streams) { diff --git a/src/emu/sound.h b/src/emu/sound.h index 8d8d4fa9eee..3d6e4d3568f 100644 --- a/src/emu/sound.h +++ b/src/emu/sound.h @@ -50,8 +50,8 @@ By default, the inputs will have been resampled to match the output sample rate, unless otherwise specified. - SOUND_DISABLE_THREADING if to be defined when your environment does - not support threads (e.g. emscripten). The effects suddendly become + SOUND_DISABLE_THREADING is to be defined when your environment does + not support threads (e.g. emscripten). The effects suddenly become costly then though. ***************************************************************************/ @@ -84,23 +84,6 @@ constexpr u32 SAMPLE_RATE_INPUT_ADAPTIVE = 0xffffffff; constexpr u32 SAMPLE_RATE_OUTPUT_ADAPTIVE = 0xfffffffe; constexpr u32 SAMPLE_RATE_ADAPTIVE = 0xfffffffd; -//************************************************************************** -// DEBUGGING -//************************************************************************** - -// turn this on to enable aggressive assertions and other checks -#ifdef MAME_DEBUG -#define SOUND_DEBUG (1) -#else -#define SOUND_DEBUG (1) -#endif - -// if SOUND_DEBUG is on, make assertions fire regardless of MAME_DEBUG -#if (SOUND_DEBUG) -#define sound_assert(x) do { if (!(x)) { osd_printf_error("sound_assert: " #x "\n"); osd_break_into_debugger("sound_assert: " #x "\n"); } } while (0) -#else -#define sound_assert assert -#endif using stream_update_delegate = delegate; class audio_effect; @@ -599,7 +582,7 @@ private: void update(s32); // handle mixing mapping update if needed - static std::vector find_channel_mapping(const std::array &position, const osd::audio_info::node_info *node); + static std::vector find_channel_mapping(const osd::channel_position &pos, const osd::audio_info::node_info *node); void startup_cleanups(); void streams_update(); template void apply_osd_changes(std::vector &streams); diff --git a/src/emu/speaker.cpp b/src/emu/speaker.cpp index d57f9119c24..58bb80dec64 100644 --- a/src/emu/speaker.cpp +++ b/src/emu/speaker.cpp @@ -18,37 +18,21 @@ DEFINE_DEVICE_TYPE(SPEAKER, speaker_device, "speaker", "Speaker") DEFINE_DEVICE_TYPE(MICROPHONE, microphone_device, "microphone", "Microphone") -const sound_io_device::position_name_mapping sound_io_device::position_name_mappings[] = { - { 0.0, 0.0, 1.0, "Front center" }, - { -0.2, 0.0, 1.0, "Front left" }, - { 0.0, -0.5, 1.0, "Front floor" }, - { 0.2, 0.0, 1.0, "Front right" }, - { 0.0, 0.0, -0.5, "Rear center" }, - { -0.2, 0.0, -0.5, "Rear left" }, - { 0.2, 0.0, -0.5, "Read right" }, - { 0.0, 0.0, -0.1, "Headrest center" }, - { -0.1, 0.0, -0.1, "Headrest left" }, - { 0.1, 0.0, -0.1, "Headrest right" }, - { 0.0, -0.5, 0.0, "Seat" }, - { 0.0, -0.2, 0.1, "Backrest" }, - { } -}; - -std::string sound_io_device::get_position_name(u32 channel) const -{ - for(unsigned int i = 0; position_name_mappings[i].m_name; i++) - if(m_positions[channel][0] == position_name_mappings[i].m_x && m_positions[channel][1] == position_name_mappings[i].m_y && m_positions[channel][2] == position_name_mappings[i].m_z) - return position_name_mappings[i].m_name; - return util::string_format("#%d", channel); -} - sound_io_device &sound_io_device::set_position(u32 channel, double x, double y, double z) { if(channel >= m_positions.size()) fatalerror("%s: Requested channel %d on %d channel device\n", tag(), channel, m_positions.size()); - m_positions[channel][0] = x; - m_positions[channel][1] = y; - m_positions[channel][2] = z; + m_positions[channel].m_x = x; + m_positions[channel].m_y = y; + m_positions[channel].m_z = z; + return *this; +} + +sound_io_device &sound_io_device::set_position(u32 channel, const osd::channel_position &pos) +{ + if(channel >= m_positions.size()) + fatalerror("%s: Requested channel %d on %d channel device\n", tag(), channel, m_positions.size()); + m_positions[channel] = pos; return *this; } diff --git a/src/emu/speaker.h b/src/emu/speaker.h index 1582ee97468..63fa6e0c86c 100644 --- a/src/emu/speaker.h +++ b/src/emu/speaker.h @@ -41,30 +41,32 @@ DECLARE_DEVICE_TYPE(MICROPHONE, microphone_device) class sound_io_device : public device_t, public device_sound_interface { public: + virtual ~sound_io_device(); // configuration helpers sound_io_device &set_position(u32 channel, double x, double y, double z); - sound_io_device &front_center(u32 channel = 0) { return set_position(channel, 0.0, 0.0, 1.0); } - sound_io_device &front_left(u32 channel = 0) { return set_position(channel, -0.2, 0.0, 1.0); } - sound_io_device &front_floor(u32 channel = 0) { return set_position(channel, 0.0, -0.5, 1.0); } - sound_io_device &front_right(u32 channel = 0) { return set_position(channel, 0.2, 0.0, 1.0); } - sound_io_device &rear_center(u32 channel = 0) { return set_position(channel, 0.0, 0.0, -0.5); } - sound_io_device &rear_left(u32 channel = 0) { return set_position(channel, -0.2, 0.0, -0.5); } - sound_io_device &rear_right(u32 channel = 0) { return set_position(channel, 0.2, 0.0, -0.5); } - sound_io_device &headrest_center(u32 channel = 0) { return set_position(channel, 0.0, 0.0, -0.1); } - sound_io_device &headrest_left(u32 channel = 0) { return set_position(channel, -0.1, 0.0, -0.1); } - sound_io_device &headrest_right(u32 channel = 0) { return set_position(channel, 0.1, 0.0, -0.1); } - sound_io_device &seat(u32 channel = 0) { return set_position(channel, 0.0, -0.5, 0.0); } - sound_io_device &backrest(u32 channel = 0) { return set_position(channel, 0.0, -0.2, 0.1); } - sound_io_device &unknown(u32 channel = 0) { return set_position(channel, 0.0, 0.0, 0.0); } - sound_io_device &map_on_request_only(u32 channel = 0) { return set_position(channel, 0.0, 0.0, 10.0); } + sound_io_device &set_position(u32 channel, const osd::channel_position &pos); + sound_io_device &front_center(u32 channel = 0) { return set_position(channel, osd::channel_position::FC); } + sound_io_device &front_left(u32 channel = 0) { return set_position(channel, osd::channel_position::FL); } + sound_io_device &front_right(u32 channel = 0) { return set_position(channel, osd::channel_position::FR); } + sound_io_device &rear_center(u32 channel = 0) { return set_position(channel, osd::channel_position::RC); } + sound_io_device &rear_left(u32 channel = 0) { return set_position(channel, osd::channel_position::RL); } + sound_io_device &rear_right(u32 channel = 0) { return set_position(channel, osd::channel_position::RR); } + sound_io_device &headrest_center(u32 channel = 0) { return set_position(channel, osd::channel_position::HC); } + sound_io_device &headrest_left(u32 channel = 0) { return set_position(channel, osd::channel_position::HL); } + sound_io_device &headrest_right(u32 channel = 0) { return set_position(channel, osd::channel_position::RC); } + sound_io_device &backrest(u32 channel = 0) { return set_position(channel, osd::channel_position::BACKREST); } + sound_io_device &unknown(u32 channel = 0) { return set_position(channel, osd::channel_position::UNKNOWN); } + sound_io_device &map_on_request_only(u32 channel = 0) { return set_position(channel, osd::channel_position::ONREQ); } + sound_io_device &lfe(u32 channel = 0) { return set_position(channel, osd::channel_position::LFE); } + sound_io_device &front() { return front_left(0).front_right(1); } sound_io_device &rear() { return rear_left(0).rear_right(1); } sound_io_device &corners() { return front_left(0).front_right(1).rear_left(2).rear_right(3); } + int channels() const { return m_positions.size(); } - std::array get_position(u32 channel) const { return m_positions[channel]; } - std::string get_position_name(u32 channel) const; + const osd::channel_position &get_position(u32 channel) const { return m_positions[channel]; } virtual bool is_output() const = 0; void set_id(int id) { m_id = id; } @@ -72,20 +74,9 @@ public: sound_stream *stream() const { return m_stream; } - static bool mapping_allowed(const std::array &position) { - return position[0] != 0 || position[1] != 0 || position[2] != 10.0; - } - protected: - struct position_name_mapping { - double m_x, m_y, m_z; - const char *m_name; - }; - - static const position_name_mapping position_name_mappings[]; - // configuration state - std::vector> m_positions; + std::vector m_positions; sound_stream *m_stream; int m_id; diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp index cf1e9d132a6..a6ae8016134 100644 --- a/src/frontend/mame/luaengine.cpp +++ b/src/frontend/mame/luaengine.cpp @@ -1696,9 +1696,9 @@ void lua_engine::initialize() { auto pos = iodev->get_position(channel); auto table = sol().create_table(); - table[1] = pos[0]; - table[2] = pos[1]; - table[3] = pos[2]; + table[1] = pos.m_x; + table[2] = pos.m_y; + table[3] = pos.m_z; pos_table[channel+1] = table; } return pos_table; @@ -1710,7 +1710,7 @@ void lua_engine::initialize() auto *iodev = dynamic_cast(&dev.device()); if (iodev) for(int channel=0; channel != iodev->channels(); channel++) - pos_table[channel+1] = iodev->get_position_name(channel); + pos_table[channel+1] = iodev->get_position(channel).name(); return pos_table; }); diff --git a/src/frontend/mame/ui/audio_effect_reverb.cpp b/src/frontend/mame/ui/audio_effect_reverb.cpp new file mode 100644 index 00000000000..50559673159 --- /dev/null +++ b/src/frontend/mame/ui/audio_effect_reverb.cpp @@ -0,0 +1,655 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/********************************************************************* + + ui/audio_effect_reverb.cpp + + Reverb configuration + +*********************************************************************/ + +#include "emu.h" +#include "ui/audio_effect_reverb.h" +#include "audio_effects/aeffect.h" +#include "audio_effects/reverb.h" + +#include "ui/ui.h" + +namespace ui { +menu_audio_effect_reverb::menu_audio_effect_reverb(mame_ui_manager &mui, render_container &container, u16 chain, u16 entry, audio_effect *effect) + : menu(mui, container) +{ + m_chain = chain; + m_entry = entry; + m_preset = 0; + m_effect = static_cast(effect); + set_heading(util::string_format("%s #%u", chain == 0xffff ? _("Default") : machine().sound().effect_chain_tag(chain), entry+1)); + set_process_flags(PROCESS_LR_REPEAT | PROCESS_LR_ALWAYS); +} + +menu_audio_effect_reverb::~menu_audio_effect_reverb() +{ +} + +bool menu_audio_effect_reverb::handle(event const *ev) +{ + if(!ev) + return false; + + bool alt_pressed = machine().input().code_pressed(KEYCODE_LALT) || machine().input().code_pressed(KEYCODE_RALT); + bool ctrl_pressed = machine().input().code_pressed(KEYCODE_LCONTROL) || machine().input().code_pressed(KEYCODE_RCONTROL); + bool shift_pressed = machine().input().code_pressed(KEYCODE_LSHIFT) || machine().input().code_pressed(KEYCODE_RSHIFT); + + switch(ev->iptkey) { + case IPT_UI_SELECT: + if(uintptr_t(ev->itemref) == PRESET) { + m_effect->load_preset(m_preset); + reset(reset_options::REMEMBER_POSITION); + return true; + } + break; + + case IPT_UI_LEFT: { + switch(uintptr_t(ev->itemref)) { + case MODE: + m_effect->set_mode(0); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case PRESET: + if(m_preset != 0) + m_preset --; + reset(reset_options::REMEMBER_POSITION); + return true; + + case DRYL: + m_effect->set_dry_level(change_percent(m_effect->dry_level(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case EL: + m_effect->set_early_level(change_percent(m_effect->early_level(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LL: + m_effect->set_late_level(change_percent(m_effect->late_level(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case E2LL: + m_effect->set_early_to_late_level(change_percent(m_effect->early_to_late_level(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case ERS: + m_effect->set_early_room_size(change_percent(m_effect->early_room_size(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LRS: + m_effect->set_late_room_size(change_percent(m_effect->late_room_size(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case SW: + m_effect->set_stereo_width(change_percent(m_effect->stereo_width(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LDIFF: + m_effect->set_late_diffusion(change_percent(m_effect->late_diffusion(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LWANDER: + m_effect->set_late_wander(change_percent(m_effect->late_wander(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case EDAMP: + m_effect->set_early_damping(change_freq(m_effect->early_damping(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LDAMP: + m_effect->set_late_damping(change_freq(m_effect->late_damping(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LPDELAY: + m_effect->set_late_predelay(change_ms(m_effect->late_predelay(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LDECAY: + m_effect->set_late_global_decay(change_decay(m_effect->late_global_decay(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LSPIN: + m_effect->set_late_spin(change_spin(m_effect->late_spin(), false, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case ETAP: + if(m_effect->early_tap_setup() != 0) { + m_effect->set_early_tap_setup(m_effect->early_tap_setup() - 1); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + } + return true; + } + break; + } + + case IPT_UI_RIGHT: { + switch(uintptr_t(ev->itemref)) { + case MODE: + m_effect->set_mode(1); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case PRESET: + if(m_preset != audio_effect_reverb::preset_count() - 1) + m_preset ++; + reset(reset_options::REMEMBER_POSITION); + return true; + + case DRYL: + m_effect->set_dry_level(change_percent(m_effect->dry_level(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case EL: + m_effect->set_early_level(change_percent(m_effect->early_level(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LL: + m_effect->set_late_level(change_percent(m_effect->late_level(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case E2LL: + m_effect->set_early_to_late_level(change_percent(m_effect->early_to_late_level(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case ERS: + m_effect->set_early_room_size(change_percent(m_effect->early_room_size(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LRS: + m_effect->set_late_room_size(change_percent(m_effect->late_room_size(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case SW: + m_effect->set_stereo_width(change_percent(m_effect->stereo_width(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LDIFF: + m_effect->set_late_diffusion(change_percent(m_effect->late_diffusion(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LWANDER: + m_effect->set_late_wander(change_percent(m_effect->late_wander(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case EDAMP: + m_effect->set_early_damping(change_freq(m_effect->early_damping(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LDAMP: + m_effect->set_late_damping(change_freq(m_effect->late_damping(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LPDELAY: + m_effect->set_late_predelay(change_ms(m_effect->late_predelay(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LDECAY: + m_effect->set_late_global_decay(change_decay(m_effect->late_global_decay(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LSPIN: + m_effect->set_late_spin(change_spin(m_effect->late_spin(), true, shift_pressed, ctrl_pressed, alt_pressed)); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case ETAP: + if(m_effect->early_tap_setup() != audio_effect_reverb::early_tap_setup_count() - 1) { + m_effect->set_early_tap_setup(m_effect->early_tap_setup() + 1); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + } + return true; + } + + break; + } + + case IPT_UI_CLEAR: { + switch(uintptr_t(ev->itemref)) { + case MODE: + m_effect->reset_mode(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case DRYL: + m_effect->reset_dry_level(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case EL: + m_effect->reset_early_level(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LL: + m_effect->reset_late_level(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case E2LL: + m_effect->reset_early_to_late_level(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case ERS: + m_effect->reset_early_room_size(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LRS: + m_effect->reset_late_room_size(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case SW: + m_effect->reset_stereo_width(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LDIFF: + m_effect->reset_late_diffusion(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LWANDER: + m_effect->reset_late_wander(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case EDAMP: + m_effect->reset_early_damping(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LDAMP: + m_effect->reset_late_damping(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LPDELAY: + m_effect->reset_late_predelay(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LDECAY: + m_effect->reset_late_global_decay(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case LSPIN: + m_effect->reset_late_spin(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + + case ETAP: + m_effect->reset_early_tap_setup(); + if(m_chain == 0xffff) + machine().sound().default_effect_changed(m_entry); + reset(reset_options::REMEMBER_POSITION); + return true; + } + break; + } + } + return false; +} + +std::string menu_audio_effect_reverb::format_percent(double val) +{ + return util::string_format("%d%", u32(val)); +} + +std::string menu_audio_effect_reverb::format_freq(double val) +{ + return util::string_format("%dHz", u32(val+0.5)); +} + +std::string menu_audio_effect_reverb::format_ms(double val) +{ + return util::string_format("%5.1fms", val); +} + +std::string menu_audio_effect_reverb::format_decay(double val) +{ + return util::string_format("%5.2fs", val); +} + +std::string menu_audio_effect_reverb::format_spin(double val) +{ + return util::string_format("%3.2fHz", val); +} + +u32 menu_audio_effect_reverb::flag_percent(double val, bool isset) +{ + u32 flag = 0; + if(!isset) + flag |= FLAG_INVERT; + if(val > 0) + flag |= FLAG_LEFT_ARROW; + if(val < 100) + flag |= FLAG_RIGHT_ARROW; + return flag; +} + +u32 menu_audio_effect_reverb::flag_freq(double val, bool isset) +{ + u32 flag = 0; + if(!isset) + flag |= FLAG_INVERT; + if(val > 100) + flag |= FLAG_LEFT_ARROW; + if(val < 16000) + flag |= FLAG_RIGHT_ARROW; + return flag; +} + +u32 menu_audio_effect_reverb::flag_ms(double val, bool isset) +{ + u32 flag = 0; + if(!isset) + flag |= FLAG_INVERT; + if(val > 0) + flag |= FLAG_LEFT_ARROW; + if(val < 200) + flag |= FLAG_RIGHT_ARROW; + return flag; +} + +u32 menu_audio_effect_reverb::flag_decay(double val, bool isset) +{ + u32 flag = 0; + if(!isset) + flag |= FLAG_INVERT; + if(val > 0.1) + flag |= FLAG_LEFT_ARROW; + if(val < 30) + flag |= FLAG_RIGHT_ARROW; + return flag; +} + +u32 menu_audio_effect_reverb::flag_spin(double val, bool isset) +{ + u32 flag = 0; + if(!isset) + flag |= FLAG_INVERT; + if(val > 0) + flag |= FLAG_LEFT_ARROW; + if(val < 5) + flag |= FLAG_RIGHT_ARROW; + return flag; +} + +double menu_audio_effect_reverb::change_percent(double val, bool inc, bool shift, bool ctrl, bool alt) +{ + double step = alt ? 100 : ctrl ? 20 : shift ? 1 : 5; + if(inc) + val = std::min(100.0, val + step); + else + val = std::max(0.0, val - step); + return val; +} + +double menu_audio_effect_reverb::change_freq(double val, bool inc, bool shift, bool ctrl, bool alt) +{ + double step = alt ? 16000 : ctrl ? 20 : shift ? 1 : 5; + if(val >= 10000) + step *= 100; + else if(val >= 500) + step *= 50; + else if(val >= 250) + step *= 20; + else if(val >= 1000) + step *= 10; + else if(val >= 500) + step *= 5; + else if(val >= 250) + step *= 2; + + if(inc) + val = std::min(16000.0, val + step); + else + val = std::max(100.0, val - step); + return val; +} + +double menu_audio_effect_reverb::change_ms(double val, bool inc, bool shift, bool ctrl, bool alt) +{ + double step = alt ? 200 : ctrl ? 10 : shift ? 0.1 : 1; + if(inc) + val = std::min(200.0, val + step); + else + val = std::max(0.0, val - step); + val = u32(val * 10 + 0.5) / 10.0; + return val; +} + +double menu_audio_effect_reverb::change_decay(double val, bool inc, bool shift, bool ctrl, bool alt) +{ + double step = alt ? 30 : ctrl ? 1 : shift ? 0.01 : 0.1; + if(inc) + val = std::min(30.0, val + step); + else + val = std::max(0.1, val - step); + val = u32(val * 100 + 0.5) / 100.0; + return val; +} + +double menu_audio_effect_reverb::change_spin(double val, bool inc, bool shift, bool ctrl, bool alt) +{ + double step = alt ? 5 : ctrl ? 1 : shift ? 0.01 : 0.1; + if(inc) + val = std::min(5.0, val + step); + else + val = std::max(0.0, val - step); + val = u32(val * 100 + 0.5) / 100.0; + return val; +} + + +void menu_audio_effect_reverb::populate() +{ + item_append(_(audio_effect::effect_names[audio_effect::REVERB]), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + item_append(_("Mode"), m_effect->mode() ? _("Active") : _("Bypass"), flag_mode(), (void *)MODE); + item_append(_("Load preset"), audio_effect_reverb::preset_name(m_preset), flag_preset(), (void *)PRESET); + item_append(_("Dry level"), format_percent(m_effect->dry_level()), flag_percent(m_effect->dry_level(), m_effect->isset_dry_level()), (void *)DRYL); + item_append(_("Stereo width"), format_percent(m_effect->stereo_width()), flag_percent(m_effect->stereo_width(), m_effect->isset_stereo_width()), (void *)SW); + + item_append(_("Early Reflections"), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + item_append(_("Room size"), format_percent(m_effect->early_room_size()), flag_percent(m_effect->early_room_size(), m_effect->isset_early_room_size()), (void *)ERS); + item_append(_("Tap setup"), audio_effect_reverb::early_tap_setup_name(m_effect->early_tap_setup()), flag_tap_setup(), (void *)ETAP); + item_append(_("Damping"), format_freq(m_effect->early_damping()), flag_percent(m_effect->early_damping(), m_effect->isset_early_damping()), (void *)EDAMP); + item_append(_("Level"), format_percent(m_effect->early_level()), flag_percent(m_effect->early_level(), m_effect->isset_early_level()), (void *)EL); + item_append(_("Send to Late"), format_percent(m_effect->early_to_late_level()), flag_percent(m_effect->early_to_late_level(), m_effect->isset_early_to_late_level()), (void *)E2LL); + + item_append(_("Late Reflections"), FLAG_UI_HEADING | FLAG_DISABLE, nullptr); + item_append(_("Room size"), format_percent(m_effect->late_room_size()), flag_percent(m_effect->late_room_size(), m_effect->isset_late_room_size()), (void *)LRS); + item_append(_("Damping"), format_freq(m_effect->late_damping()), flag_percent(m_effect->late_damping(), m_effect->isset_late_damping()), (void *)LDAMP); + item_append(_("Pre-delay"), format_ms(m_effect->late_predelay()), flag_percent(m_effect->late_predelay(), m_effect->isset_late_predelay()), (void *)LPDELAY); + item_append(_("Diffusion"), format_percent(m_effect->late_diffusion()), flag_percent(m_effect->late_diffusion(), m_effect->isset_late_diffusion()), (void *)LDIFF); + item_append(_("Wander"), format_percent(m_effect->late_wander()), flag_percent(m_effect->late_wander(), m_effect->isset_late_wander()), (void *)LWANDER); + item_append(_("Decay"), format_decay(m_effect->late_global_decay()), flag_decay(m_effect->late_global_decay(), m_effect->isset_late_global_decay()), (void *)LDECAY); + item_append(_("Spin"), format_spin(m_effect->late_spin()), flag_spin(m_effect->late_spin(), m_effect->isset_late_spin()), (void *)LSPIN); + item_append(_("Level"), format_percent(m_effect->late_level()), flag_percent(m_effect->late_level(), m_effect->isset_late_level()), (void *)LL); + item_append(menu_item_type::SEPARATOR); +} + +void menu_audio_effect_reverb::recompute_metrics(uint32_t width, uint32_t height, float aspect) +{ + menu::recompute_metrics(width, height, aspect); +} + +void menu_audio_effect_reverb::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2) +{ +} + +void menu_audio_effect_reverb::menu_activated() +{ + // scripts or the other form of the menu could have changed something in the mean time + reset(reset_options::REMEMBER_POSITION); +} + +void menu_audio_effect_reverb::menu_deactivated() +{ +} + + +u32 menu_audio_effect_reverb::flag_mode() const +{ + u32 flag = 0; + if(!m_effect->isset_mode()) + flag |= FLAG_INVERT; + if(m_effect->mode()) + flag |= FLAG_LEFT_ARROW; + else + flag |= FLAG_RIGHT_ARROW; + return flag; +} + +u32 menu_audio_effect_reverb::flag_tap_setup() const +{ + u32 flag = 0; + if(!m_effect->isset_early_tap_setup()) + flag |= FLAG_INVERT; + if(m_effect->early_tap_setup() != 0) + flag |= FLAG_LEFT_ARROW; + if(m_effect->early_tap_setup() != audio_effect_reverb::early_tap_setup_count() - 1) + flag |= FLAG_RIGHT_ARROW; + return flag; +} + +u32 menu_audio_effect_reverb::flag_preset() const +{ + u32 flag = 0; + if(m_preset != 0) + flag |= FLAG_LEFT_ARROW; + if(m_preset != audio_effect_reverb::preset_count() - 1) + flag |= FLAG_RIGHT_ARROW; + return flag; +} +} diff --git a/src/frontend/mame/ui/audio_effect_reverb.h b/src/frontend/mame/ui/audio_effect_reverb.h new file mode 100644 index 00000000000..d080fc4bc00 --- /dev/null +++ b/src/frontend/mame/ui/audio_effect_reverb.h @@ -0,0 +1,88 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/*************************************************************************** + + ui/audio_effect_reverb.h + + Reverb configuration + +***************************************************************************/ + +#ifndef MAME_FRONTEND_UI_AUDIO_EFFECT_REVERB_H +#define MAME_FRONTEND_UI_AUDIO_EFFECT_REVERB_H + +#pragma once + +#include "ui/menu.h" + +class audio_effect_reverb; + +namespace ui { + +class menu_audio_effect_reverb : public menu +{ +public: + menu_audio_effect_reverb(mame_ui_manager &mui, render_container &container, u16 chain, u16 entry, audio_effect *effect); + virtual ~menu_audio_effect_reverb() override; + +protected: + virtual void recompute_metrics(uint32_t width, uint32_t height, float aspect) override; + virtual void custom_render(uint32_t flags, void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; + virtual void menu_activated() override; + virtual void menu_deactivated() override; + +private: + enum { + MODE, + PRESET, + DRYL, + EL, + LL, + E2LL, + SW, + EDAMP, + ERS, + ETAP, + LDAMP, + LDIFF, + LPDELAY, + LRS, + LDECAY, + LSPIN, + LWANDER + }; + + u16 m_chain, m_entry; + u32 m_preset; + audio_effect_reverb *m_effect; + + virtual void populate() override; + virtual bool handle(event const *ev) override; + + static std::string format_percent(double val); + static std::string format_freq(double val); + static std::string format_ms(double val); + static std::string format_decay(double val); + static std::string format_spin(double val); + + u32 flag_mode() const; + u32 flag_tap_setup() const; + u32 flag_preset() const; + + static u32 flag_percent(double val, bool isset); + static u32 flag_freq(double val, bool isset); + static u32 flag_ms(double val, bool isset); + static u32 flag_decay(double val, bool isset); + static u32 flag_spin(double val, bool isset); + + static double change_percent(double val, bool inc, bool shift, bool ctrl, bool alt); + static double change_freq(double val, bool inc, bool shift, bool ctrl, bool alt); + static double change_ms(double val, bool inc, bool shift, bool ctrl, bool alt); + static double change_decay(double val, bool inc, bool shift, bool ctrl, bool alt); + static double change_spin(double val, bool inc, bool shift, bool ctrl, bool alt); + +}; + +} // namespace ui + +#endif // MAME_FRONTEND_UI_AUDIO_EFFECT_REVERB_H diff --git a/src/frontend/mame/ui/audioeffects.cpp b/src/frontend/mame/ui/audioeffects.cpp index 56122a8e054..eefa3a6352d 100644 --- a/src/frontend/mame/ui/audioeffects.cpp +++ b/src/frontend/mame/ui/audioeffects.cpp @@ -15,6 +15,7 @@ #include "audio_effect_compressor.h" #include "audio_effect_eq.h" #include "audio_effect_filter.h" +#include "audio_effect_reverb.h" #include "ui/ui.h" @@ -106,6 +107,10 @@ bool menu_audio_effects::handle(event const *ev) case audio_effect::FILTER: menu::stack_push(ui(), container(), chain, entry, eff); break; + + case audio_effect::REVERB: + menu::stack_push(ui(), container(), chain, entry, eff); + break; } return true; } diff --git a/src/frontend/mame/ui/audiomix.cpp b/src/frontend/mame/ui/audiomix.cpp index 18ac51e8491..d96b57ffc75 100644 --- a/src/frontend/mame/ui/audiomix.cpp +++ b/src/frontend/mame/ui/audiomix.cpp @@ -599,7 +599,7 @@ void menu_audio_mixer::populate() } for(const auto &cmap : omap.m_channel_mappings) { const auto &node = find_node(cmap.m_node); - std::string guest_channel = omap.m_dev->get_position_name(cmap.m_guest_channel); + std::string guest_channel = omap.m_dev->get_position(cmap.m_guest_channel).name(); if(curline == cursel_line && m_current_group == GRP_GUEST_CHANNEL) guest_channel = u8"\u25c4" + guest_channel + u8"\u25ba"; diff --git a/src/mame/atari/atarigt.cpp b/src/mame/atari/atarigt.cpp index 98275fc6fbb..e3ce3aaa3a5 100644 --- a/src/mame/atari/atarigt.cpp +++ b/src/mame/atari/atarigt.cpp @@ -903,7 +903,7 @@ void atarigt_state::atarigt_stereo(machine_config &config) // based on dedicated cabinet configuration; // 'universal' kit supports mono and stereo, with/without subwoofer. SPEAKER(config, "speaker", 2).front(); - SPEAKER(config, "subwoofer").front_floor(); // Next to the coin door at dedicated cabinet, just silence for now (not implemented) + SPEAKER(config, "subwoofer").lfe(); // Next to the coin door at dedicated cabinet, just silence for now (not implemented) // TODO: correct? sound board has only 1 DAC populated. m_cage->add_route(0, "speaker", 1.0, 1); @@ -925,7 +925,7 @@ void atarigt_state::tmek(machine_config &config) // 5 Channel output (4 Channel input connected to Quad Amp PCB) SPEAKER(config, "speaker", 4).front().headrest_left(2).headrest_right(3); - //SPEAKER(config, "subwoofer").seat(); Not implemented, Quad Amp PCB output; + //SPEAKER(config, "subwoofer").lfe(); Not implemented, Quad Amp PCB output; m_cage->set_speedup(0x4fad); m_cage->add_route(0, "speaker", 1.0, 1); // Foward Right diff --git a/src/mame/atari/metalmx.cpp b/src/mame/atari/metalmx.cpp index ee18f1fcc12..11a0fd4aeab 100644 --- a/src/mame/atari/metalmx.cpp +++ b/src/mame/atari/metalmx.cpp @@ -761,7 +761,7 @@ void metalmx_state::metalmx(machine_config &config) // TODO: copied from atarigt.cpp; Same configurations as T-Mek? // 5 Channel output (4 Channel input connected to Quad Amp PCB) SPEAKER(config, "speaker", 4).front().headrest_left(2).headrest_right(3); - //SPEAKER(config, "subwoofer").seat(); Not implemented, Quad Amp PCB output; + //SPEAKER(config, "subwoofer").lfe(); Not implemented, Quad Amp PCB output; ATARI_CAGE(config, m_cage, 0); m_cage->set_speedup(0); // TODO: speedup address diff --git a/src/mame/midway/seattle.cpp b/src/mame/midway/seattle.cpp index 09aa71fb2d1..314e190f844 100644 --- a/src/mame/midway/seattle.cpp +++ b/src/mame/midway/seattle.cpp @@ -2171,7 +2171,7 @@ void seattle_state::sfrush(machine_config &config) // 5 Channel output (4 Channel input connected to Quad Amp PCB) SPEAKER(config, "speaker", 4).corners(); - //SPEAKER(config, "subwoofer").seat(); Not implemented, Quad Amp PCB output; + //SPEAKER(config, "subwoofer").lfe(); Not implemented, Quad Amp PCB output; ATARI_CAGE_SEATTLE(config, m_cage, 0); m_cage->set_speedup(0x5236); @@ -2201,7 +2201,7 @@ void seattle_state::sfrushrk(machine_config &config) // 5 Channel output (4 Channel input connected to Quad Amp PCB) SPEAKER(config, "speaker", 4).corners(); - //SPEAKER(config, "subwoofer").seat(); Not implemented, Quad Amp PCB output; + //SPEAKER(config, "subwoofer").lfe(); Not implemented, Quad Amp PCB output; ATARI_CAGE_SEATTLE(config, m_cage, 0); m_cage->set_speedup(0x5329); diff --git a/src/mame/namco/namcos22.cpp b/src/mame/namco/namcos22.cpp index 777bde6e38a..bc4144e79f5 100644 --- a/src/mame/namco/namcos22.cpp +++ b/src/mame/namco/namcos22.cpp @@ -3846,7 +3846,7 @@ void namcos22s_state::airco22b(machine_config &config) { namcos22s(config); - SPEAKER(config, "bodysonic").backrest(); + SPEAKER(config, "bodysonic").lfe(); m_c352->add_route(2, "bodysonic", 0.50); // to subwoofer behind back } @@ -3892,7 +3892,7 @@ void namcos22s_state::tokyowar(machine_config &config) { namcos22s(config); - SPEAKER(config, "vibration").seat(); + SPEAKER(config, "vibration").lfe(); SPEAKER(config, "seat").headrest_center(); m_c352->add_route(2, "vibration", 0.5); // to "bass shaker" diff --git a/src/mame/pinball/s11b.cpp b/src/mame/pinball/s11b.cpp index ab225e6b074..5598a38ff82 100644 --- a/src/mame/pinball/s11b.cpp +++ b/src/mame/pinball/s11b.cpp @@ -394,7 +394,7 @@ void s11b_state::s11b_jokerz(machine_config &config) m_cvsd_filter2->add_route(ALL_OUTPUTS, m_ps88, (0.25*4.0), 1); m_pia34->ca2_handler().set(m_ps88, FUNC(pinsnd88_device::resetq_w)); m_ps88->syncq_cb().set(m_pia34, FUNC(pia6821_device::ca1_w)); // the sync connection comes from sound connector pin 16 to MCA1, not the usual pin 12 to MCB1 - SPEAKER(config, "cabinet").front_floor(); // the cabinet speaker is aimed down underneath the pinball table itself + SPEAKER(config, "cabinet").set_position(0, 0.0, -0.5, 1.0); // the cabinet speaker is aimed down underneath the pinball table itself SPEAKER(config, "backbox").front_center(); // the backbox speakers are roughly level with the user, but farther in front of them than the cabinet m_ps88->add_route(0, "cabinet", 1.0); m_ps88->add_route(1, "backbox", 1.0); diff --git a/src/mame/taito/ninjaw.cpp b/src/mame/taito/ninjaw.cpp index e8003d56fe8..45300ac02fc 100644 --- a/src/mame/taito/ninjaw.cpp +++ b/src/mame/taito/ninjaw.cpp @@ -974,7 +974,7 @@ void ninjaw_state::ninjaw(machine_config &config) /* sound hardware */ SPEAKER(config, "speaker", 2).front(); - SPEAKER(config, "subwoofer").seat(); + SPEAKER(config, "subwoofer").lfe(); ym2610_device &ymsnd(YM2610(config, "ymsnd", 16000000/2)); ymsnd.irq_handler().set_inputline("audiocpu", 0); @@ -1079,7 +1079,7 @@ void ninjaw_state::darius2(machine_config &config) /* sound hardware */ SPEAKER(config, "speaker", 2).front(); - SPEAKER(config, "subwoofer").seat(); + SPEAKER(config, "subwoofer").lfe(); ym2610_device &ymsnd(YM2610(config, "ymsnd", 16000000/2)); ymsnd.irq_handler().set_inputline("audiocpu", 0); diff --git a/src/mame/taito/taito_z.cpp b/src/mame/taito/taito_z.cpp index e20d13e2338..dc41318fb6f 100644 --- a/src/mame/taito/taito_z.cpp +++ b/src/mame/taito/taito_z.cpp @@ -3221,7 +3221,7 @@ void contcirc_state::contcirc(machine_config &config) //OSC: 26.686, 24.000, 16. /* sound hardware */ SPEAKER(config, "front").front_center(); SPEAKER(config, "rear").rear_center(); - SPEAKER(config, "subwoofer").set_position(0, 0.0, 0.0, 0.0); // FIXME: where is this speaker located? + SPEAKER(config, "subwoofer").lfe(); ym2610_device &ymsnd(YM2610(config, "ymsnd", XTAL(16'000'000)/2)); // 8 MHz ymsnd.irq_handler().set_inputline(m_audiocpu, 0); @@ -3280,7 +3280,7 @@ void chasehq_state::chasehq(machine_config &config) //OSC: 26.686, 24.000, 16.00 /* sound hardware */ SPEAKER(config, "front").front_center(); SPEAKER(config, "rear").rear_center(); - SPEAKER(config, "subwoofer").set_position(0, 0.0, 0.0, 0.0); // FIXME: where is this speaker located? + SPEAKER(config, "subwoofer").lfe(); ym2610_device &ymsnd(YM2610(config, "ymsnd", XTAL(16'000'000)/2)); // 8 MHz ymsnd.irq_handler().set_inputline(m_audiocpu, 0); @@ -3539,7 +3539,7 @@ void nightstr_state::nightstr(machine_config &config) //OSC: 26.686, 24.000, 16. /* sound hardware */ SPEAKER(config, "front").front_center(); SPEAKER(config, "rear").rear_center(); - SPEAKER(config, "subwoofer").set_position(0, 0.0, 0.0, 0.0); // FIXME: where is this located in the cabinet? + SPEAKER(config, "subwoofer").lfe(); ym2610_device &ymsnd(YM2610(config, "ymsnd", XTAL(16'000'000)/2)); // 8 MHz ymsnd.irq_handler().set_inputline(m_audiocpu, 0); diff --git a/src/mame/taito/taitojc.cpp b/src/mame/taito/taitojc.cpp index 17ae0c84975..b17a4450efa 100644 --- a/src/mame/taito/taitojc.cpp +++ b/src/mame/taito/taitojc.cpp @@ -1155,7 +1155,7 @@ void dendego_state::dendego(machine_config &config) m_screen->set_screen_update(FUNC(dendego_state::screen_update_dendego)); /* sound hardware */ - SPEAKER(config, "vibration").seat(); + SPEAKER(config, "vibration").lfe(); /* clock frequency & pin 7 not verified */ OKIM6295(config, "oki", 1056000, okim6295_device::PIN7_HIGH).add_route(ALL_OUTPUTS, "vibration", 0.20); diff --git a/src/mame/taito/warriorb.cpp b/src/mame/taito/warriorb.cpp index 261fdc9bd33..801283b20cd 100644 --- a/src/mame/taito/warriorb.cpp +++ b/src/mame/taito/warriorb.cpp @@ -630,7 +630,7 @@ void warriorb_state::darius2d(machine_config &config) // sound hardware SPEAKER(config, "speaker", 2).front(); - SPEAKER(config, "subwoofer").seat(); + SPEAKER(config, "subwoofer").lfe(); ym2610_device &ymsnd(YM2610(config, "ymsnd", 16_MHz_XTAL / 2)); ymsnd.irq_handler().set_inputline("audiocpu", 0); diff --git a/src/osd/interface/audio.cpp b/src/osd/interface/audio.cpp new file mode 100644 index 00000000000..a27e4135171 --- /dev/null +++ b/src/osd/interface/audio.cpp @@ -0,0 +1,49 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#include "emu.h" +#include "audio.h" + +const osd::channel_position osd::channel_position::UNKNOWN(0.0, 0.0, 0.0); +const osd::channel_position osd::channel_position::ONREQ (0.0, 0.0, 10.0); +const osd::channel_position osd::channel_position::LFE (0.0, 0.0, 11.0); + +bool osd::channel_position::is_lfe() const { return *this == LFE; } +bool osd::channel_position::is_onreq() const { return *this == ONREQ; } +bool osd::channel_position::is_unknown() const { return *this == UNKNOWN; } + +const osd::channel_position osd::channel_position::FC ( 0.0, 0.0, 1.0); +const osd::channel_position osd::channel_position::FL (-0.2, 0.0, 1.0); +const osd::channel_position osd::channel_position::FR ( 0.2, 0.0, 1.0); +const osd::channel_position osd::channel_position::RC ( 0.0, 0.0, -0.5); +const osd::channel_position osd::channel_position::RL (-0.2, 0.0, -0.5); +const osd::channel_position osd::channel_position::RR ( 0.2, 0.0, -0.5); +const osd::channel_position osd::channel_position::HC ( 0.0, 0.0, -0.1); +const osd::channel_position osd::channel_position::HL (-0.1, 0.0, -0.1); +const osd::channel_position osd::channel_position::HR ( 0.1, 0.0, -0.1); +const osd::channel_position osd::channel_position::BACKREST( 0.0, -0.2, 0.1); + +const osd::detail::position_name_mapping osd::detail::position_name_mappings[] = { + { channel_position::FC, "Front center" }, + { channel_position::FL, "Front left" }, + { channel_position::FR, "Front right" }, + { channel_position::RC, "Rear center" }, + { channel_position::RL, "Rear left" }, + { channel_position::RR, "Rear right" }, + { channel_position::HC, "Headrest center" }, + { channel_position::HL, "Headrest left" }, + { channel_position::HR, "Headrest right" }, + { channel_position::BACKREST, "Backrest" }, + { channel_position::LFE, "Subwoofer" }, + { channel_position::ONREQ, "Auxiliary" }, + { channel_position::UNKNOWN, "Unknown" } +}; + +std::string osd::channel_position::name() const +{ + for(unsigned int i=0; i != sizeof(detail::position_name_mappings)/sizeof(detail::position_name_mappings[0]); i++) + if(*this == detail::position_name_mappings[i].m_pos) + return detail::position_name_mappings[i].m_name; + return util::string_format("[%f %f %f]", m_x, m_y, m_z); +} + diff --git a/src/osd/interface/audio.h b/src/osd/interface/audio.h index 102b9e6dcb7..92fb1a8abac 100644 --- a/src/osd/interface/audio.h +++ b/src/osd/interface/audio.h @@ -16,6 +16,57 @@ namespace osd { +struct channel_position { + // Special positions + + // Position is unknown, placed in the middle + static const struct channel_position UNKNOWN; + + // This channel should only be mapped explicitely through a channel mapping (on request) + static const struct channel_position ONREQ; + + // This channel is a LFE + static const struct channel_position LFE; + + + // Standard positions + static const struct channel_position FC; + static const struct channel_position FL; + static const struct channel_position FR; + static const struct channel_position RC; + static const struct channel_position RL; + static const struct channel_position RR; + static const struct channel_position HC; + static const struct channel_position HL; + static const struct channel_position HR; + static const struct channel_position BACKREST; + + double m_x, m_y, m_z; + + bool is_lfe() const; + bool is_onreq() const; + bool is_unknown() const; + + channel_position() : channel_position(UNKNOWN) {} + channel_position(double x, double y, double z) : m_x(x), m_y(y), m_z(z) {} + channel_position(const channel_position &pos) : m_x(pos.m_x), m_y(pos.m_y), m_z(pos.m_z) {} + + bool operator == (const channel_position &pos) const { + return pos.m_x == m_x && pos.m_y == m_y && pos.m_z == m_z; + } + + std::string name() const; +}; + +namespace detail { +struct position_name_mapping { + struct channel_position m_pos; + const char *m_name; +}; + +extern const position_name_mapping position_name_mappings[]; +}; + struct audio_rate_range { uint32_t m_default_rate; uint32_t m_min_rate; @@ -41,7 +92,7 @@ struct audio_info { uint32_t m_id; audio_rate_range m_rate; std::vector m_port_names; - std::vector > m_port_positions; + std::vector m_port_positions; uint32_t m_sinks; uint32_t m_sources; diff --git a/src/osd/modules/sound/coreaudio_sound.cpp b/src/osd/modules/sound/coreaudio_sound.cpp index 536c5da1439..833d222e436 100644 --- a/src/osd/modules/sound/coreaudio_sound.cpp +++ b/src/osd/modules/sound/coreaudio_sound.cpp @@ -102,62 +102,62 @@ static const char *sMacChannelLabels[sMacChannelCount] = // "Surround" channels are at X = -0.4 (left) and 0.4 (right). static const std::array sChannelPositions[sMacChannelCount] = { - { 0.0, 0.0, 0.0 }, // unused - { -0.2, 0.0, 1.0 }, // Front Left - { 0.2, 0.0, 1.0 }, // Front Right - { 0.0, 0.0, 1.0 }, // Front Center - { 0.0, -0.5, 0.0 }, // Low Frequency Effects - { -0.2, 0.0, -0.5 }, // Rear Left - { 0.2, 0.0, -0.5 }, // Rear Right - { -0.1, 0.0, 1.0 }, // Front Left of Center - { 0.1, 0.0, 1.0 }, // Front Right of Center - { 0.0, 0.0, -1.0 }, // Rear Center - { -0.2, 0.0, 0.5 }, // Side Left - { 0.2, 0.0, 0.5 }, // Side Right - { 0.0, 0.5, -0.1 }, // Top Center - { -0.2, 0.5, 1.0 }, // Top Front Left - { 0.0, 0.5, 1.0 }, // Top Front Center - { 0.2, 0.5, 1.0 }, // Top Front Right - { -0.2, 0.5, -0.5 }, // Top Rear Left - { 0.2, 0.5, -0.5 }, // Top Rear Center - { 0.2, 0.5, -0.5 }, // Top Rear Right - { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, - { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, - { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, - { -0.4, 0.0, -0.5 }, // Rear Surround Left - { 0.4, 0.0, -0.5 }, // Rear Surround Right - { -0.4, 0.0, 1.0 }, // Left Wide - { 0.4, 0.0, 1.0 }, // Right Wide - { 0.0, -0.5, 0.0 }, // Low Frequency Effects 2 - { -0.1, 0.0, -0.1 }, // Left Total - { 0.1, 0.0, -0.1 }, // Right Total - { 0.0, 0.0, 1.0 }, // Hearing Impaired - { 0.0, 0.0, 1.0 }, // Narration - { 0.0, 0.0, 1.0 }, // Mono - { 0.0, 0.0, 1.0 }, // Dialog Centric Mix - { 0.0, 0.0, -0.1 }, // Center Surround Direct - { 0.0, 0.0, 0.0 }, // Haptic - { 0.0, 0.0, 0.0 }, // unused - { 0.0, 0.0, 0.0 }, // unused - { 0.0, 0.0, 0.0 }, // unused - { -0.2, 0.5, 0.0 }, // Left Top Middle - { 0.0, 0.0, 0.0 }, // unused - { 0.2, 0.5, 0.0 }, // Right Top Middle - { -0.2, 0.5, -0.5 }, // Left Top Rear - { 0.0, 0.5, -0.5 }, // Center Top Rear - { 0.2, 0.5, -0.5 }, // Right Top Rear - { -0.4, 0.0, -0.1 }, // Left Side Surround - { 0.4, 0.0, -0.1 }, // Right Side Surround - { -0.2, -0.5, 0.0 }, // Left Bottom - { 0.2, -0.5, 0.0 }, // Right Bottom - { 0.0, -0.5, 0.0 }, // Center Bottom - { -0.4, 0.5, -0.1 }, // Left Top Surround - { 0.4, 0.5, -0.1 }, // Right Top Surround - { 0.0, 0.0, 0.0 }, // Low Frequency Effects 3 - { -0.4, 0.0, -0.5 }, // Left Rear Surround - { 0.4, 0.0, -0.5 }, // Right Rear Surround - { -0.1, 0.0, 1.0 }, // Left Edge of Screen - { 0.1, 0.0, 1.0 } // Right Edge of Screen + osd::channel_position::UNKNOWN, // unused + osd::channel_position::FL, // Front Left + osd::channel_position::FR, // Front Right + osd::channel_position::FC, // Front Center + osd::channel_position::LFE, // Low Frequency Effects + osd::channel_position::RL, // Rear Left + osd::channel_position::RR, // Rear Right + osd::channel_position( -0.1, 0.0, 1.0 ), // Front Left of Center + osd::channel_position( 0.1, 0.0, 1.0 ), // Front Right of Center + osd::channel_position::RC, // Rear Center + osd::channel_position( -0.2, 0.0, 0.5 ), // Side Left + osd::channel_position( 0.2, 0.0, 0.5 ), // Side Right + osd::channel_position( 0.0, 0.5, -0.1 ), // Top Center + osd::channel_position( -0.2, 0.5, 1.0 ), // Top Front Left + osd::channel_position( 0.0, 0.5, 1.0 ), // Top Front Center + osd::channel_position( 0.2, 0.5, 1.0 ), // Top Front Right + osd::channel_position( -0.2, 0.5, -0.5 ), // Top Rear Left + osd::channel_position( 0.2, 0.5, -0.5 ), // Top Rear Center + osd::channel_position( 0.2, 0.5, -0.5 ), // Top Rear Right + osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, + osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, + osd::channel_position::UNKNOWN, osd::channel_position::UNKNOWN, + osd::channel_position( -0.4, 0.0, -0.5 ), // Rear Surround Left + osd::channel_position( 0.4, 0.0, -0.5 ), // Rear Surround Right + osd::channel_position( -0.4, 0.0, 1.0 ), // Left Wide + osd::channel_position( 0.4, 0.0, 1.0 ), // Right Wide + osd::channel_position::LFE, // Low Frequency Effects 2 + osd::channel_position::HL, // Left Total + osd::channel_position::HR, // Right Total + osd::channel_position::FC, // Hearing Impaired + osd::channel_position::FC, // Narration + osd::channel_position::FC, // Mono + osd::channel_position::FC, // Dialog Centric Mix + osd::channel_position::HC, // Center Surround Direct + osd::channel_position::UNKNOWN, // Haptic + osd::channel_position::UNKNOWN, // unused + osd::channel_position::UNKNOWN, // unused + osd::channel_position::UNKNOWN, // unused + osd::channel_position( -0.2, 0.5, 0.0 ), // Left Top Middle + osd::channel_position::UNKNOWN, // unused + osd::channel_position( 0.2, 0.5, 0.0 ), // Right Top Middle + osd::channel_position( -0.2, 0.5, -0.5 ), // Left Top Rear + osd::channel_position( 0.0, 0.5, -0.5 ), // Center Top Rear + osd::channel_position( 0.2, 0.5, -0.5 ), // Right Top Rear + osd::channel_position( -0.4, 0.0, -0.1 ), // Left Side Surround + osd::channel_position( 0.4, 0.0, -0.1 ), // Right Side Surround + osd::channel_position( -0.2, -0.5, 0.0 ), // Left Bottom + osd::channel_position( 0.2, -0.5, 0.0 ), // Right Bottom + osd::channel_position( 0.0, -0.5, 0.0 ), // Center Bottom + osd::channel_position( -0.4, 0.5, -0.1 ), // Left Top Surround + osd::channel_position( 0.4, 0.5, -0.1 ), // Right Top Surround + osd::channel_position::UNKNOWN, // Low Frequency Effects 3 + osd::channel_position( -0.4, 0.0, -0.5 ), // Left Rear Surround + osd::channel_position( 0.4, 0.0, -0.5 ), // Right Rear Surround + osd::channel_position( -0.1, 0.0, 1.0 ), // Left Edge of Screen + osd::channel_position( 0.1, 0.0, 1.0 ) // Right Edge of Screen }; struct coreaudio_device @@ -1064,7 +1064,7 @@ void sound_coreaudio::build_device_list() { std::string chLabel = "Channel " + std::to_string(desc + 1); node.m_port_names.push_back(chLabel); - node.m_port_positions.emplace_back(std::array({0.0, 0.0, 1.0})); + node.m_port_positions.emplace_back(osd::channel_position::FC); } else { diff --git a/src/osd/modules/sound/mmdevice_helpers.cpp b/src/osd/modules/sound/mmdevice_helpers.cpp index 9aef0b5dd07..931ecc2d470 100644 --- a/src/osd/modules/sound/mmdevice_helpers.cpp +++ b/src/osd/modules/sound/mmdevice_helpers.cpp @@ -48,25 +48,25 @@ char const *const f_speaker_names[] ={ "TBC", // SPEAKER_TOP_BACK_CENTER "TBR" }; // SPEAKER_TOP_BACK_RIGHT -std::array const f_speaker_positions[] = { - { -0.2, 0.0, 1.0 }, // SPEAKER_FRONT_LEFT - { 0.2, 0.0, 1.0 }, // SPEAKER_FRONT_RIGHT - { 0.0, 0.0, 1.0 }, // SPEAKER_FRONT_CENTER - { 0.0, -0.5, 1.0 }, // SPEAKER_LOW_FREQUENCY - { -0.2, 0.0, -0.5 }, // SPEAKER_BACK_LEFT - { 0.2, 0.0, -0.5 }, // SPEAKER_BACK_RIGHT - { -0.1, 0.0, 1.0 }, // SPEAKER_FRONT_LEFT_OF_CENTER - { 0.1, 0.0, 1.0 }, // SPEAKER_FRONT_RIGHT_OF_CENTER - { 0.0, 0.0, -0.5 }, // SPEAKER_BACK_CENTER - { -0.2, 0.0, 0.0 }, // SPEAKER_SIDE_LEFT - { 0.2, 0.0, 0.0 }, // SPEAKER_SIDE_RIGHT - { 0.0, 0.5, 0.0 }, // SPEAKER_TOP_CENTER - { -0.2, 0.5, 1.0 }, // SPEAKER_TOP_FRONT_LEFT - { 0.0, 0.5, 1.0 }, // SPEAKER_TOP_FRONT_CENTER - { 0.2, 0.5, 1.0 }, // SPEAKER_TOP_FRONT_RIGHT - { -0.2, 0.5, -0.5 }, // SPEAKER_TOP_BACK_LEFT - { 0.0, 0.5, -0.5 }, // SPEAKER_TOP_BACK_CENTER - { 0.2, 0.5, -0.5 } }; // SPEAKER_TOP_BACK_RIGHT +osd::channel_position const f_speaker_positions[] = { + osd::channel_position::FL, // SPEAKER_FRONT_LEFT + osd::channel_position::FR, // SPEAKER_FRONT_RIGHT + osd::channel_position::FC, // SPEAKER_FRONT_CENTER + osd::channel_position::LFE, // SPEAKER_LOW_FREQUENCY + osd::channel_position::RL, // SPEAKER_BACK_LEFT + osd::channel_position::RR, // SPEAKER_BACK_RIGHT + osd::channel_position( -0.1, 0.0, 1.0 ), // SPEAKER_FRONT_LEFT_OF_CENTER + osd::channel_position( 0.1, 0.0, 1.0 ), // SPEAKER_FRONT_RIGHT_OF_CENTER + osd::channel_position::RC, // SPEAKER_BACK_CENTER + osd::channel_position( -0.2, 0.0, 0.0 ), // SPEAKER_SIDE_LEFT + osd::channel_position( 0.2, 0.0, 0.0 ), // SPEAKER_SIDE_RIGHT + osd::channel_position( 0.0, 0.5, 0.0 ), // SPEAKER_TOP_CENTER + osd::channel_position( -0.2, 0.5, 1.0 ), // SPEAKER_TOP_FRONT_LEFT + osd::channel_position( 0.0, 0.5, 1.0 ), // SPEAKER_TOP_FRONT_CENTER + osd::channel_position( 0.2, 0.5, 1.0 ), // SPEAKER_TOP_FRONT_RIGHT + osd::channel_position( -0.2, 0.5, -0.5 ), // SPEAKER_TOP_BACK_LEFT + osd::channel_position( 0.0, 0.5, -0.5 ), // SPEAKER_TOP_BACK_CENTER + osd::channel_position( 0.2, 0.5, -0.5 ) }; // SPEAKER_TOP_BACK_RIGHT } // anonymous namespace diff --git a/src/osd/modules/sound/pa_sound.cpp b/src/osd/modules/sound/pa_sound.cpp index f314ce32cd8..a950a563656 100644 --- a/src/osd/modules/sound/pa_sound.cpp +++ b/src/osd/modules/sound/pa_sound.cpp @@ -101,17 +101,17 @@ int sound_pa::init(osd_interface &osd, osd_options const &options) enum { FL, FR, FC, LFE, BL, BR, BC, SL, SR, AUX }; static const char *const posname[10] = { "FL", "FR", "FC", "LFE", "BL", "BR", "BC", "SL", "SR", "AUX" }; - static const std::array pos3d[10] = { - { -0.2, 0.0, 1.0 }, - { 0.2, 0.0, 1.0 }, - { 0.0, 0.0, 1.0 }, - { 0.0, -0.5, 1.0 }, - { -0.2, 0.0, -0.5 }, - { 0.2, 0.0, -0.5 }, - { 0.0, 0.0, -0.5 }, - { -0.2, 0.0, 0.0 }, - { 0.2, 0.0, 0.0 }, - { 0.0, 0.0, 10.0 }, + static const osd::channel_position pos3d[10] = { + osd::channel_position::FL, + osd::channel_position::FR, + osd::channel_position::FC, + osd::channel_position::LFE, + osd::channel_position::RL, + osd::channel_position::RR, + osd::channel_position::RC, + osd::channel_position(-0.2, 0.0, 0.0), + osd::channel_position( 0.2, 0.0, 0.0), + osd::channel_position::ONREQ }; static const uint32_t positions[9][9] = { diff --git a/src/osd/modules/sound/pipewire_sound.cpp b/src/osd/modules/sound/pipewire_sound.cpp index 383398a28e6..f818ef4c1db 100644 --- a/src/osd/modules/sound/pipewire_sound.cpp +++ b/src/osd/modules/sound/pipewire_sound.cpp @@ -53,7 +53,7 @@ public: private: struct position_info { uint32_t m_position; - std::array m_coords; + osd::channel_position m_coords; }; static const position_info position_infos[]; @@ -73,7 +73,7 @@ private: uint32_t m_sinks, m_sources; std::vector m_position_codes; std::vector m_port_names; - std::vector> m_positions; + std::vector m_positions; osd::audio_rate_range m_rate; bool m_has_s16; @@ -165,16 +165,16 @@ private: // Try to more or less map to speaker.h positions const sound_pipewire::position_info sound_pipewire::position_infos[] = { - { SPA_AUDIO_CHANNEL_MONO, { 0.0, 0.0, 1.0 } }, - { SPA_AUDIO_CHANNEL_FL, { -0.2, 0.0, 1.0 } }, - { SPA_AUDIO_CHANNEL_FR, { 0.2, 0.0, 1.0 } }, - { SPA_AUDIO_CHANNEL_FC, { 0.0, 0.0, 1.0 } }, - { SPA_AUDIO_CHANNEL_LFE, { 0.0, -0.5, 1.0 } }, - { SPA_AUDIO_CHANNEL_RL, { -0.2, 0.0, -0.5 } }, - { SPA_AUDIO_CHANNEL_RR, { 0.2, 0.0, -0.5 } }, - { SPA_AUDIO_CHANNEL_RC, { 0.0, 0.0, -0.5 } }, - { SPA_AUDIO_CHANNEL_AUX0, { 0.0, 0.0, 10.0 } }, - { SPA_AUDIO_CHANNEL_UNKNOWN, { 0.0, 0.0, 0.0 } } + { SPA_AUDIO_CHANNEL_MONO, osd::channel_position::FC }, + { SPA_AUDIO_CHANNEL_FL, osd::channel_position::FL }, + { SPA_AUDIO_CHANNEL_FR, osd::channel_position::FR }, + { SPA_AUDIO_CHANNEL_FC, osd::channel_position::FC }, + { SPA_AUDIO_CHANNEL_LFE, osd::channel_position::LFE }, + { SPA_AUDIO_CHANNEL_RL, osd::channel_position::RL }, + { SPA_AUDIO_CHANNEL_RR, osd::channel_position::RR }, + { SPA_AUDIO_CHANNEL_RC, osd::channel_position::RC }, + { SPA_AUDIO_CHANNEL_AUX0, osd::channel_position::ONREQ }, + { SPA_AUDIO_CHANNEL_UNKNOWN, osd::channel_position::UNKNOWN } }; diff --git a/src/osd/modules/sound/pulse_sound.cpp b/src/osd/modules/sound/pulse_sound.cpp index 77331fa5ce2..f6e7c391d85 100644 --- a/src/osd/modules/sound/pulse_sound.cpp +++ b/src/osd/modules/sound/pulse_sound.cpp @@ -45,7 +45,7 @@ public: private: struct position_info { pa_channel_position_t m_position; - std::array m_coords; + osd::channel_position m_coords; }; static const position_info position_infos[]; @@ -61,7 +61,7 @@ private: // Audio node info std::vector m_position_codes; std::vector m_position_names; - std::vector> m_positions; + std::vector m_positions; uint32_t m_sink_port_count, m_source_port_count; osd::audio_rate_range m_rate; @@ -122,15 +122,15 @@ private: // Try to more or less map to speaker.h positions const sound_pulse::position_info sound_pulse::position_infos[] = { - { PA_CHANNEL_POSITION_MONO, { 0.0, 0.0, 1.0 } }, - { PA_CHANNEL_POSITION_FRONT_LEFT, { -0.2, 0.0, 1.0 } }, - { PA_CHANNEL_POSITION_FRONT_RIGHT, { 0.2, 0.0, 1.0 } }, - { PA_CHANNEL_POSITION_FRONT_CENTER, { 0.0, 0.0, 1.0 } }, - { PA_CHANNEL_POSITION_LFE, { 0.0, -0.5, 1.0 } }, - { PA_CHANNEL_POSITION_REAR_LEFT, { -0.2, 0.0, -0.5 } }, - { PA_CHANNEL_POSITION_REAR_RIGHT, { 0.2, 0.0, -0.5 } }, - { PA_CHANNEL_POSITION_REAR_CENTER, { 0.0, 0.0, -0.5 } }, - { PA_CHANNEL_POSITION_MAX, { 0.0, 0.0, 10.0 } } + { PA_CHANNEL_POSITION_MONO, osd::channel_position::FC }, + { PA_CHANNEL_POSITION_FRONT_LEFT, osd::channel_position::FL }, + { PA_CHANNEL_POSITION_FRONT_RIGHT, osd::channel_position::FR }, + { PA_CHANNEL_POSITION_FRONT_CENTER, osd::channel_position::FC }, + { PA_CHANNEL_POSITION_LFE, osd::channel_position::LFE }, + { PA_CHANNEL_POSITION_REAR_LEFT, osd::channel_position::RL }, + { PA_CHANNEL_POSITION_REAR_RIGHT, osd::channel_position::RR }, + { PA_CHANNEL_POSITION_REAR_CENTER, osd::channel_position::RC }, + { PA_CHANNEL_POSITION_MAX, osd::channel_position::ONREQ }, }; diff --git a/src/osd/modules/sound/sdl_sound.cpp b/src/osd/modules/sound/sdl_sound.cpp index c1ebc87f330..542d0bf4ac1 100644 --- a/src/osd/modules/sound/sdl_sound.cpp +++ b/src/osd/modules/sound/sdl_sound.cpp @@ -148,17 +148,17 @@ osd::audio_info sound_sdl::get_information() enum { FL, FR, FC, LFE, BL, BR, BC, SL, SR, AUX }; static const char *const posname[10] = { "FL", "FR", "FC", "LFE", "BL", "BR", "BC", "SL", "SR", "AUX" }; - static std::array pos3d[10] = { - { -0.2, 0.0, 1.0 }, - { 0.2, 0.0, 1.0 }, - { 0.0, 0.0, 1.0 }, - { 0.0, -0.5, 1.0 }, - { -0.2, 0.0, -0.5 }, - { 0.2, 0.0, -0.5 }, - { 0.0, 0.0, -0.5 }, - { -0.2, 0.0, 0.0 }, - { 0.2, 0.0, 0.0 }, - { 0.0, 0.0, 10.0 }, + static const osd::channel_position pos3d[10] = { + osd::channel_position::FL, + osd::channel_position::FR, + osd::channel_position::FC, + osd::channel_position::LFE, + osd::channel_position::RL, + osd::channel_position::RR, + osd::channel_position::RC, + osd::channel_position(-0.2, 0.0, 0.0), + osd::channel_position( 0.2, 0.0, 0.0), + osd::channel_position::ONREQ }; static const uint32_t positions[8][9] = { diff --git a/src/osd/modules/sound/sound_module.cpp b/src/osd/modules/sound/sound_module.cpp index 3a9d3654e0e..5b0432bc4d0 100644 --- a/src/osd/modules/sound/sound_module.cpp +++ b/src/osd/modules/sound/sound_module.cpp @@ -30,8 +30,8 @@ osd::audio_info sound_module::get_information() result.m_nodes[0].m_sources = 0; result.m_nodes[0].m_port_names.emplace_back("L"); result.m_nodes[0].m_port_names.emplace_back("R"); - result.m_nodes[0].m_port_positions.emplace_back(std::array({ -0.2, 0.0, 1.0 })); - result.m_nodes[0].m_port_positions.emplace_back(std::array({ 0.2, 0.0, 1.0 })); + result.m_nodes[0].m_port_positions.emplace_back(osd::channel_position::FL); + result.m_nodes[0].m_port_positions.emplace_back(osd::channel_position::FR); result.m_streams.resize(1); result.m_streams[0].m_id = 1; result.m_streams[0].m_node = 1; @@ -40,13 +40,18 @@ osd::audio_info sound_module::get_information() sound_module::abuffer::abuffer(uint32_t channels) noexcept : m_channels(channels), m_used_buffers(0), m_last_sample(channels, 0) { + m_delta = 0; + m_delta2 = 0; } void sound_module::abuffer::get(int16_t *data, uint32_t samples) noexcept { + m_delta -= samples; + m_delta2 -= samples; uint32_t pos = 0; while(pos != samples) { if(!m_used_buffers) { + m_delta2 += samples - pos; while(pos != samples) { std::copy_n(m_last_sample.data(), m_channels, data); data += m_channels; @@ -74,10 +79,13 @@ void sound_module::abuffer::get(int16_t *data, uint32_t samples) noexcept pos += avail; data += avail * m_channels; } + // printf("# %d %d\n", m_delta, m_delta2); } void sound_module::abuffer::push(const int16_t *data, uint32_t samples) { + m_delta += samples; + m_delta2 += samples; auto &buf = push_buffer(); buf.m_cpos = 0; buf.m_data.resize(samples * m_channels); @@ -85,6 +93,8 @@ void sound_module::abuffer::push(const int16_t *data, uint32_t samples) std::copy_n(data + ((samples - 1) * m_channels), m_channels, m_last_sample.data()); if(m_used_buffers > 10) { + for(uint32_t i=0; i != m_used_buffers-10; i++) + m_delta2 -= (m_buffers[i].m_data.size()/m_channels - m_buffers[i].m_cpos); // If there are way too many buffers, drop some so only 10 are left (roughly 0.2s) for(unsigned i = 0; 10 > i; ++i) { using std::swap; @@ -95,8 +105,10 @@ void sound_module::abuffer::push(const int16_t *data, uint32_t samples) // If there are too many buffers, remove five samples per buffer // to slowly resync to reduce latency (4 seconds to // compensate one buffer, roughly) + m_delta2 -= std::max(samples / 200, 1); buf.m_cpos = std::max(samples / 200, 1); } + // printf("# %d %d\n", m_delta, m_delta2); } uint32_t sound_module::abuffer::available() const noexcept diff --git a/src/osd/modules/sound/sound_module.h b/src/osd/modules/sound/sound_module.h index 1160ecb5749..3a2c2552bf9 100644 --- a/src/osd/modules/sound/sound_module.h +++ b/src/osd/modules/sound/sound_module.h @@ -63,6 +63,7 @@ protected: void pop_buffer() noexcept; buffer &push_buffer(); + int32_t m_delta, m_delta2; uint32_t m_channels; uint32_t m_used_buffers; std::vector m_last_sample;