Mercurial > foo_out_sdl
diff foosdk/sdk/foobar2000/SDK/dsp.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/sdk/foobar2000/SDK/dsp.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,761 @@ +#include "foobar2000-sdk-pch.h" +#include "dsp.h" +#include "resampler.h" + +#ifdef FOOBAR2000_HAVE_DSP + +#include <math.h> + +audio_chunk * dsp_chunk_list::add_item(t_size hint_size) { return insert_item(get_count(), hint_size); } + +void dsp_chunk_list::remove_all() { remove_mask(pfc::bit_array_true()); } + +double dsp_chunk_list::get_duration() { + double rv = 0; + t_size n, m = get_count(); + for (n = 0; n<m; n++) rv += get_item(n)->get_duration(); + return rv; +} + +void dsp_chunk_list::add_chunk(const audio_chunk * chunk) { + audio_chunk * dst = insert_item(get_count(), chunk->get_used_size()); + if (dst) dst->copy(*chunk); +} + +t_size dsp_chunk_list_impl::get_count() const {return m_data.size();} + +audio_chunk * dsp_chunk_list_impl::get_item(t_size n) const {return n<m_data.size() ? m_data[n].get() : 0; } + +void dsp_chunk_list_impl::remove_by_idx(t_size idx) +{ + PFC_ASSERT(idx < get_count()); + m_recycled.push_back(std::move(m_data[idx])); + m_data.erase(m_data.begin() + idx); +} + +void dsp_chunk_list_impl::remove_mask(const bit_array & mask) +{ + const auto total = m_data.size(); + mask.for_each(true, 0, total, [&](size_t idx) { + m_recycled.push_back(std::move(m_data[idx])); + }); + pfc::remove_mask_t(m_data, mask); +} + +audio_chunk * dsp_chunk_list_impl::insert_item(t_size idx,t_size hint_size) +{ + t_size max = get_count(); + if (idx>max) idx = max; + chunk_ptr_t ret; + if (!m_recycled.empty()) + { + t_size best; + if (hint_size > 0) + { + best = 0; + t_size best_found = m_recycled[0]->get_data_size(), n, total = m_recycled.size(); + for (n = 1; n < total; n++) + { + if (best_found == hint_size) break; + t_size size = m_recycled[n]->get_data_size(); + int delta_old = abs((int)best_found - (int)hint_size), delta_new = abs((int)size - (int)hint_size); + if (delta_new < delta_old) + { + best_found = size; + best = n; + } + } + } else best = m_recycled.size() - 1; + + ret = std::move(m_recycled[best]); + m_recycled.erase(m_recycled.begin() + best); + ret->set_sample_count(0); + ret->set_channels(0); + ret->set_srate(0); + } else ret = std::make_unique<audio_chunk_impl>(); + auto pRet = &*ret; + if (idx == max) m_data.push_back(std::move(ret)); + else m_data.insert(m_data.begin() + idx, std::move(ret)); + return pRet; +} + +void dsp_chunk_list::remove_bad_chunks() +{ + bool blah = false; + t_size idx; + for(idx=0;idx<get_count();) + { + audio_chunk * chunk = get_item(idx); + if (!chunk->is_valid()) + { +#if PFC_DEBUG + FB2K_console_formatter() << "Removing bad chunk: " << chunk->formatChunkSpec(); +#endif + chunk->reset(); + remove_by_idx(idx); + blah = true; + } + else idx++; + } + if (blah) console::info("one or more bad chunks removed from dsp chunk list"); +} + +bool dsp_entry_hidden::g_dsp_exists(const GUID & p_guid) { + dsp_entry_hidden::ptr p; + return g_get_interface(p, p_guid); +} + +bool dsp_entry_hidden::g_get_interface( dsp_entry_hidden::ptr & out, const GUID & guid ) { + for (auto p : enumerate()) { + if (p->get_guid() == guid) { + out = p; return true; + } + } + return false; +} + +bool dsp_entry_hidden::g_instantiate( dsp::ptr & out, const dsp_preset & preset ) { + dsp_entry_hidden::ptr i; + if (!g_get_interface(i, preset.get_owner())) return false; + return i->instantiate(out, preset); +} + +bool dsp_entry::g_instantiate(service_ptr_t<dsp> & p_out,const dsp_preset & p_preset, unsigned flags ) +{ + service_ptr_t<dsp_entry> ptr; + if (!g_get_interface(ptr,p_preset.get_owner())) return false; + if ( flags != 0 ) { + dsp_entry_v4::ptr v4; + if (v4 &= ptr) { + p_out = v4->instantiate_v4(p_preset, flags); + return true; + } + } + return ptr->instantiate(p_out,p_preset); +} + +bool dsp_entry::g_instantiate_default(service_ptr_t<dsp> & p_out,const GUID & p_guid) +{ + service_ptr_t<dsp_entry> ptr; + if (!g_get_interface(ptr,p_guid)) return false; + dsp_preset_impl preset; + if (!ptr->get_default_preset(preset)) return false; + return ptr->instantiate(p_out,preset); +} + +bool dsp_entry::g_name_from_guid(pfc::string_base & p_out,const GUID & p_guid) +{ + service_ptr_t<dsp_entry> ptr; + if (!g_get_interface(ptr,p_guid)) return false; + ptr->get_name(p_out); + return true; +} + +bool dsp_entry::g_dsp_exists(const GUID & p_guid) +{ + service_ptr_t<dsp_entry> blah; + return g_get_interface(blah,p_guid); +} + +bool dsp_entry::g_get_default_preset(dsp_preset & p_out,const GUID & p_guid) +{ + service_ptr_t<dsp_entry> ptr; + if (!g_get_interface(ptr,p_guid)) return false; + return ptr->get_default_preset(p_out); +} + +void dsp_chain_config::contents_to_stream(stream_writer * p_stream,abort_callback & p_abort) const { + uint32_t n, count = pfc::downcast_guarded<uint32_t>( get_count() ); + p_stream->write_lendian_t(count,p_abort); + for(n=0;n<count;n++) { + get_item(n).contents_to_stream(p_stream,p_abort); + } +} + +fb2k::memBlock::ptr dsp_chain_config::to_blob() const { + stream_writer_buffer_simple out; + this->contents_to_stream(&out, fb2k::noAbort); + return fb2k::memBlock::blockWithVector(out.m_buffer); +} + +void dsp_chain_config::from_blob(const void* p, size_t size) { + if (size == 0) { + remove_all(); return; + } + stream_reader_memblock_ref reader(p, size); + this->contents_from_stream(&reader, fb2k::noAbort); +} + +void dsp_chain_config::from_blob(fb2k::memBlock::ptr b) { + if (b.is_valid()) { + from_blob(b->data(), b->size()); + } else { + this->remove_all(); + } +} + +void dsp_chain_config::contents_from_stream(stream_reader * p_stream,abort_callback & p_abort) { + t_uint32 n,count; + + remove_all(); + + p_stream->read_lendian_t(count,p_abort); + + dsp_preset_impl temp; + + for(n=0;n<count;n++) { + temp.contents_from_stream(p_stream,p_abort); + add_item(temp); + } +} + +void dsp_chain_config::remove_item(t_size p_index) +{ + remove_mask(pfc::bit_array_one(p_index)); +} + +void dsp_chain_config::add_item(const dsp_preset & p_data) +{ + insert_item(p_data,get_count()); +} + +void dsp_chain_config::remove_all() +{ + remove_mask(pfc::bit_array_true()); +} + +size_t dsp_chain_config::find_first_of_type( const GUID & dspID ) const { + const size_t count = this->get_count(); + for(size_t w = 0; w < count; ++w) { + if (this->get_item(w).get_owner() == dspID) return w; + } + return SIZE_MAX; +} + +bool dsp_chain_config::contains_dsp( const GUID & dspID ) const { + return find_first_of_type( dspID ) != pfc_infinite; +} + +bool dsp_chain_config::enable_dsp( const GUID & dspID ) { + if (this->contains_dsp( dspID )) return false; + dsp_preset_impl preset; + dsp_entry::g_get_default_preset( preset, dspID ); + insert_item( preset, 0 ); + return true; +} + +bool dsp_chain_config::disable_dsp( const GUID & dspID ) { + const size_t count = this->get_count(); + if (count == 0) return false; + bool rv = false; + pfc::bit_array_bittable mask( count ); + for(size_t w = 0; w < count; ++ w) { + if (this->get_item(w).get_owner() == dspID ) { + rv = true; + mask.set(w, true); + } + } + if (rv) this->remove_mask( mask ); + return rv; +} + +bool dsp_chain_config::enable_dsp( const dsp_preset & preset ) { + dsp_chain_config & cfg = *this; + bool found = false; + bool changed = false; + t_size n,m = cfg.get_count(); + for(n=0;n<m;n++) { + if (cfg.get_item(n).get_owner() == preset.get_owner()) { + found = true; + if (cfg.get_item(n) != preset) { + cfg.replace_item(preset,n); + changed = true; + } + break; + } + } + if (!found) {cfg.insert_item(preset,0); changed = true;} + + return changed; +} + +void dsp_chain_config_impl::reorder(const size_t * order, size_t count) { + PFC_ASSERT( count == m_data.get_count() ); + m_data.reorder( order ); +} + +t_size dsp_chain_config_impl::get_count() const +{ + return m_data.get_count(); +} + +const dsp_preset & dsp_chain_config_impl::get_item(t_size p_index) const +{ + return m_data[p_index]->data; +} + +void dsp_chain_config_impl::replace_item(const dsp_preset & p_data,t_size p_index) +{ + auto& obj = *m_data[p_index]; + if (p_data.get_owner() != obj.data.get_owner()) { + obj.dspName = p_data.get_owner_name(); + } + obj.data = p_data; +} + +void dsp_chain_config_impl::insert_item(const dsp_preset & p_data,t_size p_index) +{ + this->insert_item_v2(p_data, nullptr, p_index); +} + +void dsp_chain_config_impl::remove_mask(const bit_array & p_mask) +{ + m_data.delete_mask(p_mask); +} + +dsp_chain_config_impl::~dsp_chain_config_impl() +{ + m_data.delete_all(); +} + +const char* dsp_chain_config_impl::get_dsp_name(size_t idx) const { + auto& n = m_data[idx]->dspName; + if (n.is_empty()) return nullptr; + return n.c_str(); +} + +void dsp_chain_config_impl::insert_item_v2(const dsp_preset& data, const char* dspName_, size_t index) { + pfc::string8 dspName; + if (dspName_) dspName = dspName_; + if (dspName.length() == 0) dspName = data.get_owner_name(); + m_data.insert_item(new entry_t{ data, std::move(dspName) }, index); +} + +const char* dsp_chain_config_impl::find_dsp_name(const GUID& guid) const { + for (size_t walk = 0; walk < m_data.get_size(); ++walk) { + auto& obj = *m_data[walk]; + if (obj.data.get_owner() == guid && obj.dspName.length() > 0) { + return obj.dspName.c_str(); + } + } + return nullptr; +} + +pfc::string8 dsp_preset::get_owner_name() const { + pfc::string8 ret; + dsp_entry::ptr obj; + if (dsp_entry::g_get_interface(obj, this->get_owner())) { + obj->get_name(ret); + } + return ret; +} + +pfc::string8 dsp_preset::get_owner_name_debug() const { + pfc::string8 ret; + dsp_entry::ptr obj; + if (dsp_entry::g_get_interface(obj, this->get_owner())) { + obj->get_name(ret); + } else { + ret = "[unknown]"; + } + return ret; +} + +pfc::string8 dsp_preset::debug(const char * knownName) const { + pfc::string8 name; + if (knownName) name = knownName; + else name = this->get_owner_name_debug(); + pfc::string8 ret; + ret << name << " :: " << pfc::print_guid(this->get_owner()) << " :: " << pfc::format_hexdump(this->get_data(), this->get_data_size()); + return ret; +} + +pfc::string8 dsp_chain_config::debug() const { + const size_t count = get_count(); + pfc::string8 ret; + ret << "dsp_chain_config: " << count << " items"; + for (size_t walk = 0; walk < count; ++walk) { + ret << "\n" << get_item(walk).debug(); + } + return ret; +} + +void dsp_chain_config_impl::add_item_v2(const dsp_preset& data, const char* dspName) { + insert_item_v2(data, dspName, get_count()); +} + +void dsp_chain_config_impl::copy_v2(dsp_chain_config_impl const& p_source) { + remove_all(); + t_size n, m = p_source.get_count(); + for (n = 0; n < m; n++) + add_item_v2(p_source.get_item(n), p_source.get_dsp_name(n)); +} + +pfc::string8 dsp_chain_config_impl::debug() const { + const size_t count = get_count(); + pfc::string8 ret; + ret << "dsp_chain_config_impl: " << count << " items"; + for (size_t walk = 0; walk < count; ++walk) { + ret << "\n" << get_item(walk).debug( this->get_dsp_name(walk) ); + } + return ret; +} + +void dsp_preset::contents_to_stream(stream_writer * p_stream,abort_callback & p_abort) const { + t_uint32 size = pfc::downcast_guarded<t_uint32>(get_data_size()); + p_stream->write_lendian_t(get_owner(),p_abort); + p_stream->write_lendian_t(size,p_abort); + if (size > 0) { + p_stream->write_object(get_data(),size,p_abort); + } +} + +void dsp_preset::contents_from_stream(stream_reader * p_stream,abort_callback & p_abort) { + t_uint32 size; + GUID guid; + p_stream->read_lendian_t(guid,p_abort); + set_owner(guid); + p_stream->read_lendian_t(size,p_abort); + if (size > 1024*1024*32) throw exception_io_data(); + set_data_from_stream(p_stream,size,p_abort); +} + +void dsp_preset::g_contents_from_stream_skip(stream_reader * p_stream,abort_callback & p_abort) { + t_uint32 size; + GUID guid; + p_stream->read_lendian_t(guid,p_abort); + p_stream->read_lendian_t(size,p_abort); + if (size > 1024*1024*32) throw exception_io_data(); + p_stream->skip_object(size,p_abort); +} + +void dsp_preset_impl::set_data_from_stream(stream_reader * p_stream,t_size p_bytes,abort_callback & p_abort) { + m_data.resize(p_bytes); + if (p_bytes > 0) p_stream->read_object(m_data.ptr(),p_bytes,p_abort); +} + +void dsp_chain_config::add_items(const dsp_chain_config & p_source) { + t_size n, m = p_source.get_count(); + for(n=0;n<m;n++) + add_item(p_source.get_item(n)); +} + +void dsp_chain_config::copy(const dsp_chain_config & p_source) { + remove_all(); + add_items( p_source ); +} + +bool dsp_entry::g_have_config_popup(const GUID & p_guid) +{ + service_ptr_t<dsp_entry> entry; + if (!g_get_interface(entry,p_guid)) return false; + return entry->have_config_popup(); +} + +bool dsp_entry::g_have_config_popup(const dsp_preset & p_preset) +{ + return g_have_config_popup(p_preset.get_owner()); +} + +#ifdef _WIN32 +bool dsp_entry::g_show_config_popup(dsp_preset & p_preset,fb2k::hwnd_t p_parent) +{ + service_ptr_t<dsp_entry> entry; + if (!g_get_interface(entry,p_preset.get_owner())) return false; + return entry->show_config_popup(p_preset,p_parent); +} + +bool dsp_entry::show_config_popup_v2_(const dsp_preset& p_preset, fb2k::hwnd_t p_parent, dsp_preset_edit_callback& p_callback) { + PFC_ASSERT(p_preset.get_owner() == this->get_guid()); + try { + service_ptr_t<dsp_entry_v2> entry_v2; + if (entry_v2 &= this) { + entry_v2->show_config_popup_v2(p_preset, p_parent, p_callback); + return true; + } + } catch (pfc::exception_not_implemented const&) {} + + dsp_preset_impl temp(p_preset); + bool rv = this->show_config_popup(temp, p_parent); + if (rv) p_callback.on_preset_changed(temp); + return rv; +} +namespace { + class dsp_preset_edit_callback_callV2 : public dsp_preset_edit_callback { + public: + dsp_preset_edit_callback_v2::ptr chain; + void on_preset_changed(const dsp_preset& arg) override { chain->set_preset(arg); } + }; +} +service_ptr dsp_entry::show_config_popup_v3_(fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback) { + dsp_entry_v3::ptr v3; + if (v3 &= this) { + try { + return v3->show_config_popup_v3(parent, callback); + } catch (pfc::exception_not_implemented const &) { + } + } + + dsp_preset_edit_callback_callV2 cb; + cb.chain = callback; + + dsp_preset_impl initPreset; callback->get_preset(initPreset); + bool status = this->show_config_popup_v2_(initPreset, parent, cb); + callback->dsp_dialog_done(status); + return nullptr; + +} +void dsp_entry::g_show_config_popup_v2(const dsp_preset & p_preset,fb2k::hwnd_t p_parent,dsp_preset_edit_callback & p_callback) { + auto api = g_get_interface(p_preset.get_owner()); + if (api.is_valid()) api->show_config_popup_v2_(p_preset, p_parent, p_callback); +} +#endif + +service_ptr_t<dsp_entry> dsp_entry::g_get_interface(const GUID& guid) { + for (auto ptr : enumerate()) { + if (ptr->get_guid() == guid) return ptr; + } + return nullptr; +} + +bool dsp_entry::g_get_interface(service_ptr_t<dsp_entry> & p_out,const GUID & p_guid) +{ + for (auto ptr : enumerate()) { + if (ptr->get_guid() == p_guid) { + p_out = ptr; + return true; + } + } + return false; +} + +bool resampler_entry::g_get_interface(service_ptr_t<resampler_entry> & p_out,unsigned p_srate_from,unsigned p_srate_to) +{ +#if defined(FOOBAR2000_DESKTOP) && FOOBAR2000_TARGET_VERSION >= 79 + auto r = resampler_manager::get()->get_resampler( p_srate_from, p_srate_to ); + bool v = r.is_valid(); + if ( v ) p_out = std::move(r); + return v; +#else + +#ifdef FOOBAR2000_DESKTOP + { + resampler_manager::ptr api; + if ( resampler_manager::tryGet(api) ) { + auto r = api->get_resampler( p_srate_from, p_srate_to ); + bool v = r.is_valid(); + if (v) p_out = std::move(r); + return v; + } + } +#endif + + resampler_entry::ptr ptr_resampler; + service_enum_t<dsp_entry> e; + float found_priority = 0; + resampler_entry::ptr found; + while(e.next(ptr_resampler)) + { + if (p_srate_from == 0 || ptr_resampler->is_conversion_supported(p_srate_from,p_srate_to)) + { + float priority = ptr_resampler->get_priority(); + if (found.is_empty() || priority > found_priority) + { + found = ptr_resampler; + found_priority = priority; + } + } + } + if (found.is_empty()) return false; + p_out = found; + return true; +#endif +} + +bool resampler_entry::g_create_preset(dsp_preset & p_out,unsigned p_srate_from,unsigned p_srate_to,float p_qualityscale) +{ + service_ptr_t<resampler_entry> entry; + if (!g_get_interface(entry,p_srate_from,p_srate_to)) return false; + return entry->create_preset(p_out,p_srate_to,p_qualityscale); +} + +bool resampler_entry::g_create(service_ptr_t<dsp> & p_out,unsigned p_srate_from,unsigned p_srate_to,float p_qualityscale) +{ + service_ptr_t<resampler_entry> entry; + if (!g_get_interface(entry,p_srate_from,p_srate_to)) return false; + dsp_preset_impl preset; + if (!entry->create_preset(preset,p_srate_to,p_qualityscale)) return false; + return entry->instantiate(p_out,preset); +} + + +bool dsp_chain_config::equals(dsp_chain_config const & v1, dsp_chain_config const & v2) { + const t_size count = v1.get_count(); + if (count != v2.get_count()) return false; + for(t_size walk = 0; walk < count; ++walk) { + if (v1.get_item(walk) != v2.get_item(walk)) return false; + } + return true; +} +bool dsp_chain_config::equals_debug(dsp_chain_config const& v1, dsp_chain_config const& v2) { + FB2K_DebugLog() << "Comparing DSP chains"; + const t_size count = v1.get_count(); + if (count != v2.get_count()) { + FB2K_DebugLog() << "Count mismatch, " << count << " vs " << v2.get_count(); + return false; + } + for (t_size walk = 0; walk < count; ++walk) { + if (v1.get_item(walk) != v2.get_item(walk)) { + FB2K_DebugLog() << "Item " << (walk+1) << " mismatch"; + FB2K_DebugLog() << "Item 1: " << v1.get_item(walk).debug(); + FB2K_DebugLog() << "Item 2: " << v2.get_item(walk).debug(); + return false; + } + } + FB2K_DebugLog() << "DSP chains are identical"; + return true; +} + +void dsp_chain_config::get_name_list(pfc::string_base & p_out) const { + p_out = get_name_list(); +} + +pfc::string8 dsp_chain_config::get_name_list() const { + const size_t count = get_count(); + pfc::string8 output; output.prealloc(1024); + for (size_t n = 0; n < count; n++) + { + const auto& preset = get_item(n); + service_ptr_t<dsp_entry> ptr; + if (dsp_entry::g_get_interface(ptr, preset.get_owner())) + { + pfc::string8 temp; + ptr->get_display_name_(preset, temp); + if (temp.length() > 0) { + if (output.length() > 0) output += ", "; + output += temp; + } + } + } + + return output; +} + +void dsp::run_abortable(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort) { + service_ptr_t<dsp_v2> this_v2; + if (this->service_query_t(this_v2)) this_v2->run_v2(p_chunk_list,p_cur_file,p_flags,p_abort); + else run(p_chunk_list,p_cur_file,p_flags); +} + +bool dsp::apply_preset_(const dsp_preset& arg) { + dsp_v3::ptr v3; + if (v3 &= this) return v3->apply_preset(arg); + return false; +} + +namespace { + class dsp_preset_edit_callback_impl : public dsp_preset_edit_callback { + public: + dsp_preset_edit_callback_impl(dsp_preset & p_data) : m_data(p_data) {} + void on_preset_changed(const dsp_preset & p_data) {m_data = p_data;} + private: + dsp_preset & m_data; + }; +}; + +#ifdef _WIN32 +bool dsp_entry_v2::show_config_popup(dsp_preset & p_data,fb2k::hwnd_t p_parent) { + PFC_ASSERT(p_data.get_owner() == get_guid()); + dsp_preset_impl temp(p_data); + + { + dsp_preset_edit_callback_impl cb(temp); + show_config_popup_v2(p_data,p_parent,cb); + } + PFC_ASSERT(temp.get_owner() == get_guid()); + if (temp == p_data) return false; + p_data = temp; + return true; +} +#endif + +#ifdef FOOBAR2000_MOBILE +void dsp_entry::g_show_config_popup( menu_context_ptr ctx, dsp_preset_edit_callback_v2::ptr callback) { + GUID dspID; + { + dsp_preset_impl temp; + callback->get_preset( temp ); + dspID = temp.get_owner(); + } + + dsp_entry::ptr entry; + if (!g_get_interface( entry, dspID)) return; + if (!entry->have_config_popup()) return; + entry->show_config_popup( ctx, callback ); +} +#endif // FOOBAR2000_MOBILE + +#ifdef FOOBAR2000_DESKTOP +void resampler_manager::make_chain_(dsp_chain_config& outChain, unsigned rateFrom, unsigned rateTo, float qualityScale) { + resampler_manager_v2::ptr v2; + if (v2 &= this) { + v2->make_chain(outChain, rateFrom, rateTo, qualityScale); + } else { + outChain.remove_all(); + auto obj = this->get_resampler(rateFrom, rateTo); + if (obj.is_valid()) { + dsp_preset_impl p; + if (obj->create_preset(p, rateTo, qualityScale)) { + outChain.add_item(p); + } + } + } +} +#endif + +void dsp_preset_edit_callback_v2::reset() { + dsp_preset_impl temp; get_preset( temp ); + GUID id = temp.get_owner(); temp.set_data(nullptr, 0); + if (dsp_entry::g_get_default_preset( temp, id )) { + this->set_preset( temp ); + } else { + PFC_ASSERT(!"Should not get here - no such DSP"); + } +} + +bool dsp_entry::get_display_name_supported() { + dsp_entry_v3::ptr v3; + return v3 &= this; +} + +void dsp_entry::get_display_name_(const dsp_preset& arg, pfc::string_base& out) { + PFC_ASSERT(arg.get_owner() == this->get_guid()); + dsp_entry_v3::ptr v3; + if (v3 &= this) { + v3->get_display_name(arg, out); return; + } + get_name(out); +} + +bool dsp_entry::enumerate_default_presets_(dsp_chain_config& ret) { + ret.remove_all(); + dsp_entry_v5::ptr v5; + if (v5 &= this) { + bool rv = v5->enumerate_default_presets(ret); +#if PFC_DEBUG + for (size_t walk = 0; walk < ret.get_count(); ++walk) { + PFC_ASSERT(ret.get_item(walk).get_owner() == get_guid()); + } +#endif + return rv; + } + return false; +} + +bool dsp_entry::match_preset_subclass_(dsp_preset const& x, dsp_preset const& y) { + dsp_entry_v5::ptr v5; + if (v5 &= this) return v5->match_preset_subclass(x, y); + return true; +} + +#endif // FOOBAR2000_HAVE_DSP
