Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/SDK/dsp.h @ 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 |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #pragma once | |
| 2 #include "audio_chunk.h" | |
| 3 #include "filesystem.h" | |
| 4 | |
| 5 #ifdef FOOBAR2000_HAVE_DSP | |
| 6 | |
| 7 #ifdef FOOBAR2000_MOBILE | |
| 8 #include "dsp-context.h" | |
| 9 #endif | |
| 10 | |
| 11 #include <memory> | |
| 12 #include <vector> | |
| 13 | |
| 14 class dsp_preset; class dsp_chain_config; // forward declaration | |
| 15 | |
| 16 #ifdef FOOBAR2000_HAVE_METADB | |
| 17 typedef metadb_handle_ptr dsp_track_t; | |
| 18 #else | |
| 19 typedef trackRef dsp_track_t; | |
| 20 #endif | |
| 21 | |
| 22 //! Interface to a DSP chunk list. A DSP chunk list object is passed to the DSP chain each time, since DSPs are allowed to remove processed chunks or insert new ones. | |
| 23 class NOVTABLE dsp_chunk_list { | |
| 24 public: | |
| 25 virtual t_size get_count() const = 0; | |
| 26 virtual audio_chunk * get_item(t_size n) const = 0; | |
| 27 virtual void remove_by_idx(t_size idx) = 0; | |
| 28 virtual void remove_mask(const bit_array & mask) = 0; | |
| 29 virtual audio_chunk * insert_item(t_size idx,t_size hint_size=0) = 0; | |
| 30 | |
| 31 audio_chunk * add_item(t_size hint_size=0); | |
| 32 | |
| 33 void remove_all(); | |
| 34 | |
| 35 double get_duration(); | |
| 36 | |
| 37 void add_chunk(const audio_chunk * chunk); | |
| 38 | |
| 39 void remove_bad_chunks(); | |
| 40 protected: | |
| 41 dsp_chunk_list() {} | |
| 42 ~dsp_chunk_list() {} | |
| 43 }; | |
| 44 | |
| 45 class dsp_chunk_list_impl : public dsp_chunk_list//implementation | |
| 46 { | |
| 47 typedef std::unique_ptr<audio_chunk_impl> chunk_ptr_t; | |
| 48 std::vector<chunk_ptr_t> m_data, m_recycled; | |
| 49 public: | |
| 50 dsp_chunk_list_impl() {} | |
| 51 dsp_chunk_list_impl(const dsp_chunk_list_impl&) = delete; | |
| 52 void operator=(const dsp_chunk_list_impl&) = delete; | |
| 53 t_size get_count() const; | |
| 54 audio_chunk * get_item(t_size n) const; | |
| 55 void remove_by_idx(t_size idx); | |
| 56 void remove_mask(const bit_array & mask); | |
| 57 audio_chunk * insert_item(t_size idx,t_size hint_size=0); | |
| 58 | |
| 59 audio_chunk_impl* get_item_(size_t n) const { return m_data[n].get(); } | |
| 60 }; | |
| 61 | |
| 62 //! Instance of a DSP.\n | |
| 63 //! Implementation: Derive from dsp_impl_base instead of deriving from dsp directly.\n | |
| 64 //! Instantiation: Use dsp_entry static helper methods to instantiate DSPs, or dsp_chain_config / dsp_manager to deal with entire DSP chains. | |
| 65 class NOVTABLE dsp : public service_base { | |
| 66 public: | |
| 67 enum { | |
| 68 //! Flush whatever you need to when tracks change. | |
| 69 END_OF_TRACK = 1, | |
| 70 //! Flush everything. | |
| 71 FLUSH = 2 | |
| 72 }; | |
| 73 | |
| 74 //! @param p_chunk_list List of chunks to process. The implementation may alter the list in any way, inserting chunks of different sample rate / channel configuration etc. | |
| 75 //! @param p_cur_file Optional, location of currently decoded file. May be null. | |
| 76 //! @param p_flags Flags. Can be null, or a combination of END_OF_TRACK and FLUSH constants. | |
| 77 virtual void run(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags)=0; | |
| 78 | |
| 79 //! Flushes the DSP (reinitializes / drops any buffered data). Called after seeking, etc. | |
| 80 virtual void flush() = 0; | |
| 81 | |
| 82 //! Retrieves amount of data buffered by the DSP, for syncing visualisation. | |
| 83 //! @returns Amount of buffered audio data, in seconds. | |
| 84 virtual double get_latency() = 0; | |
| 85 //! Returns true if DSP needs to know exact track change point (eg. for crossfading, removing silence).\n | |
| 86 //! Signaling this will force-flush any DSPs placed before this DSP so when it gets END_OF_TRACK, relevant chunks contain last samples of the track.\n | |
| 87 //! Signaling this will often break regular gapless playback so don't use it unless you have reasons to. | |
| 88 virtual bool need_track_change_mark() = 0; | |
| 89 | |
| 90 void run_abortable(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort); | |
| 91 | |
| 92 //! Attempts to apply preset without recreating the DSP, if supported. | |
| 93 //! @returns True on success, false if not supported (DSP needs re-creating). | |
| 94 bool apply_preset_(const dsp_preset&); | |
| 95 | |
| 96 FB2K_MAKE_SERVICE_INTERFACE(dsp,service_base); | |
| 97 }; | |
| 98 | |
| 99 //! Backwards-compatible extension to dsp interface, allows abortable operation. Introduced in 0.9.2. | |
| 100 class NOVTABLE dsp_v2 : public dsp { | |
| 101 public: | |
| 102 //! Abortable version of dsp::run(). See dsp::run() for descriptions of parameters. | |
| 103 virtual void run_v2(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort) = 0; | |
| 104 private: | |
| 105 void run(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags) { | |
| 106 run_v2(p_chunk_list,p_cur_file,p_flags,fb2k::noAbort); | |
| 107 } | |
| 108 | |
| 109 FB2K_MAKE_SERVICE_INTERFACE(dsp_v2,dsp); | |
| 110 }; | |
| 111 | |
| 112 //! Extended version allowing live changes in configuration without reinitialization. | |
| 113 class NOVTABLE dsp_v3 : public dsp_v2 { | |
| 114 FB2K_MAKE_SERVICE_INTERFACE(dsp_v3, dsp_v2); | |
| 115 public: | |
| 116 //! Live change of DSP settings. Return true if accepted, false if not (DSP will be destroyed and recreated). | |
| 117 virtual bool apply_preset(const dsp_preset&) = 0; | |
| 118 }; | |
| 119 | |
| 120 //! Helper class for implementing dsps. You should derive from dsp_impl_base instead of from dsp directly.\n | |
| 121 //! The dsp_impl_base_t template allows you to use a custom interface class as a base class for your implementation, in case you provide extended functionality.\n | |
| 122 //! Use dsp_factory_t<> template to register your dsp implementation. | |
| 123 //! The implementation - as required by dsp_factory_t<> template - must also provide following methods:\n | |
| 124 //! A constructor taking const dsp_preset&, initializing the DSP with specified preset data.\n | |
| 125 //! static void g_get_name(pfc::string_base &); - retrieving human-readable name of the DSP to display.\n | |
| 126 //! static bool g_get_default_preset(dsp_preset &); - retrieving default preset for this DSP. Return value is reserved for future use and should always be true.\n | |
| 127 //! static GUID g_get_guid(); - retrieving GUID of your DSP implementation, to be used to identify it when storing DSP chain configuration.\n | |
| 128 //! static bool g_have_config_popup(); - retrieving whether your DSP implementation supplies a popup dialog for configuring it.\n | |
| 129 //! static void g_show_config_popup(const dsp_preset & p_data,HWND p_parent, dsp_preset_edit_callback & p_callback); - displaying your DSP's settings dialog; called only when g_have_config_popup() returns true; call p_callback.on_preset_changed() whenever user has made adjustments to the preset data.\n | |
| 130 template<class t_baseclass> | |
| 131 class dsp_impl_base_t : public t_baseclass { | |
| 132 private: | |
| 133 typedef dsp_impl_base_t<t_baseclass> t_self; | |
| 134 dsp_chunk_list * m_list = nullptr; | |
| 135 t_size m_chunk_ptr = 0; | |
| 136 dsp_track_t m_cur_file = nullptr; | |
| 137 void run_v2(dsp_chunk_list * p_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort) override; | |
| 138 protected: | |
| 139 //! Call only from on_chunk / on_endoftrack (on_endoftrack will give info on track being finished).\n | |
| 140 //! May return false when there's no known track and the metadb_handle ptr will be empty/null. | |
| 141 bool get_cur_file(dsp_track_t & p_out) const {p_out = m_cur_file; return p_out.is_valid();} | |
| 142 dsp_track_t get_cur_file() const { return m_cur_file; } | |
| 143 | |
| 144 dsp_impl_base_t() {} | |
| 145 | |
| 146 //! Inserts a new chunk of audio data. \n | |
| 147 //! You can call this only from on_chunk(), on_endofplayback() and on_endoftrack(). You're NOT allowed to call this from flush() which should just drop any queued data. | |
| 148 //! @param p_hint_size Optional, amount of buffer space that you require (in audio_samples). This is just a hint for memory allocation logic and will not cause the framework to allocate the chunk for you. | |
| 149 //! @returns A pointer to the newly allocated chunk. Pass the audio data you want to insert to this chunk object. The chunk is owned by the framework, you can't delete it etc. | |
| 150 audio_chunk * insert_chunk(t_size p_hint_size = 0) { | |
| 151 PFC_ASSERT(m_list != NULL); | |
| 152 return m_list->insert_item(m_chunk_ptr++,p_hint_size); | |
| 153 } | |
| 154 audio_chunk * insert_chunk( const audio_chunk & sourceCopy ) { | |
| 155 audio_chunk * c = insert_chunk( sourceCopy.get_used_size() ); | |
| 156 c->copy( sourceCopy ); | |
| 157 return c; | |
| 158 } | |
| 159 | |
| 160 | |
| 161 //! To be overridden by a DSP implementation.\n | |
| 162 //! Called on track change. You can use insert_chunk() to dump any data you have to flush. \n | |
| 163 //! Note that you must implement need_track_change_mark() to return true if you need this method called. | |
| 164 virtual void on_endoftrack(abort_callback & p_abort) = 0; | |
| 165 //! To be overridden by a DSP implementation.\n | |
| 166 //! Called at the end of played stream, typically at the end of last played track, to allow the DSP to return all data it has buffered-ahead.\n | |
| 167 //! Use insert_chunk() to return any data you have buffered.\n | |
| 168 //! Note that this call does not imply that the DSP will be destroyed next. \n | |
| 169 //! This is also called on track changes if some DSP placed after your DSP requests track change marks. | |
| 170 virtual void on_endofplayback(abort_callback & p_abort) = 0; | |
| 171 //! To be overridden by a DSP implementation.\n | |
| 172 //! Processes a chunk of audio data.\n | |
| 173 //! You can call insert_chunk() from inside on_chunk() to insert any audio data before currently processed chunk.\n | |
| 174 //! @param p_chunk Current chunk being processed. You can alter it in any way you like. | |
| 175 //! @returns True to keep p_chunk (with alterations made inside on_chunk()) in the stream, false to remove it. | |
| 176 virtual bool on_chunk(audio_chunk * p_chunk,abort_callback & p_abort) = 0; | |
| 177 | |
| 178 public: | |
| 179 //! To be overridden by a DSP implementation.\n | |
| 180 //! Flushes the DSP (drops any buffered data). The implementation should reset the DSP to the same state it was in before receiving any audio data. \n | |
| 181 //! Called after seeking, etc. | |
| 182 virtual void flush() override = 0; | |
| 183 //! To be overridden by a DSP implementation.\n | |
| 184 //! Retrieves amount of data buffered by the DSP, for syncing visualisation. | |
| 185 //! @returns Amount of buffered audio data, in seconds. | |
| 186 virtual double get_latency() override = 0; | |
| 187 //! To be overridden by a DSP implementation.\n | |
| 188 //! Returns true if DSP needs to know exact track change point (eg. for crossfading, removing silence).\n | |
| 189 //! Signaling this will force-flush any DSPs placed before this DSP so when it gets on_endoftrack(), relevant chunks contain last samples of the track.\n | |
| 190 //! Signaling this may interfere with gapless playback in certain scenarios (forces flush of DSPs placed before you) so don't use it unless you have reasons to. | |
| 191 virtual bool need_track_change_mark() override = 0; | |
| 192 private: | |
| 193 dsp_impl_base_t(const t_self&) = delete; | |
| 194 const t_self & operator=(const t_self &) = delete; | |
| 195 }; | |
| 196 | |
| 197 template<class t_baseclass> | |
| 198 void dsp_impl_base_t<t_baseclass>::run_v2(dsp_chunk_list * p_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort) { | |
| 199 pfc::vartoggle_t<dsp_chunk_list*> l_list_toggle(m_list,p_list); | |
| 200 auto track_toggle = pfc::autoToggle(m_cur_file, p_cur_file); | |
| 201 | |
| 202 for(m_chunk_ptr = 0;m_chunk_ptr<m_list->get_count();m_chunk_ptr++) { | |
| 203 audio_chunk * c = m_list->get_item(m_chunk_ptr); | |
| 204 if (c->is_empty() || !on_chunk(c,p_abort)) | |
| 205 m_list->remove_by_idx(m_chunk_ptr--); | |
| 206 } | |
| 207 | |
| 208 if (p_flags & dsp::FLUSH) { | |
| 209 on_endofplayback(p_abort); | |
| 210 } else if (p_flags & dsp::END_OF_TRACK) { | |
| 211 if (need_track_change_mark()) on_endoftrack(p_abort); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 | |
| 216 typedef dsp_impl_base_t<dsp_v2> dsp_impl_base; | |
| 217 | |
| 218 class NOVTABLE dsp_preset { | |
| 219 public: | |
| 220 virtual GUID get_owner() const = 0; | |
| 221 virtual void set_owner(const GUID & p_owner) = 0; | |
| 222 virtual const void * get_data() const = 0; | |
| 223 virtual t_size get_data_size() const = 0; | |
| 224 virtual void set_data(const void * p_data,t_size p_data_size) = 0; | |
| 225 virtual void set_data_from_stream(stream_reader * p_stream,t_size p_bytes,abort_callback & p_abort) = 0; | |
| 226 | |
| 227 const dsp_preset & operator=(const dsp_preset & p_source) {copy(p_source); return *this;} | |
| 228 | |
| 229 void copy(const dsp_preset & p_source) {set_owner(p_source.get_owner());set_data(p_source.get_data(),p_source.get_data_size());} | |
| 230 | |
| 231 void contents_to_stream(stream_writer * p_stream,abort_callback & p_abort) const; | |
| 232 void contents_from_stream(stream_reader * p_stream,abort_callback & p_abort); | |
| 233 static void g_contents_from_stream_skip(stream_reader * p_stream,abort_callback & p_abort); | |
| 234 | |
| 235 bool operator==(const dsp_preset & p_other) const { | |
| 236 if (get_owner() != p_other.get_owner()) return false; | |
| 237 if (get_data_size() != p_other.get_data_size()) return false; | |
| 238 if (memcmp(get_data(),p_other.get_data(),get_data_size()) != 0) return false; | |
| 239 return true; | |
| 240 } | |
| 241 bool operator!=(const dsp_preset & p_other) const { | |
| 242 return !(*this == p_other); | |
| 243 } | |
| 244 | |
| 245 pfc::string8 get_owner_name() const; | |
| 246 pfc::string8 get_owner_name_debug() const; | |
| 247 pfc::string8 debug(const char * knownName = nullptr) const; | |
| 248 protected: | |
| 249 dsp_preset() {} | |
| 250 ~dsp_preset() {} | |
| 251 }; | |
| 252 | |
| 253 class dsp_preset_writer : public stream_writer { | |
| 254 public: | |
| 255 void write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort) { | |
| 256 p_abort.check(); | |
| 257 m_data.append_fromptr((const t_uint8 *) p_buffer,p_bytes); | |
| 258 } | |
| 259 void flush(dsp_preset & p_preset) { | |
| 260 p_preset.set_data(m_data.get_ptr(),m_data.get_size()); | |
| 261 m_data.set_size(0); | |
| 262 } | |
| 263 private: | |
| 264 pfc::array_t<t_uint8,pfc::alloc_fast_aggressive> m_data; | |
| 265 }; | |
| 266 | |
| 267 class dsp_preset_reader : public stream_reader { | |
| 268 public: | |
| 269 dsp_preset_reader() : m_walk(0) {} | |
| 270 dsp_preset_reader(const dsp_preset_reader & p_source) : m_walk(0) {*this = p_source;} | |
| 271 void init(const dsp_preset & p_preset) { | |
| 272 m_data.set_data_fromptr( (const t_uint8*) p_preset.get_data(), p_preset.get_data_size() ); | |
| 273 m_walk = 0; | |
| 274 } | |
| 275 t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) { | |
| 276 p_abort.check(); | |
| 277 t_size todo = pfc::min_t<t_size>(p_bytes,m_data.get_size()-m_walk); | |
| 278 memcpy(p_buffer,m_data.get_ptr()+m_walk,todo); | |
| 279 m_walk += todo; | |
| 280 return todo; | |
| 281 } | |
| 282 bool is_finished() {return m_walk == m_data.get_size();} | |
| 283 private: | |
| 284 t_size m_walk; | |
| 285 pfc::array_t<t_uint8> m_data; | |
| 286 }; | |
| 287 | |
| 288 class dsp_preset_impl : public dsp_preset | |
| 289 { | |
| 290 public: | |
| 291 dsp_preset_impl() {} | |
| 292 dsp_preset_impl(const dsp_preset_impl & p_source) {copy(p_source);} | |
| 293 dsp_preset_impl(const dsp_preset & p_source) {copy(p_source);} | |
| 294 dsp_preset_impl(dsp_preset_impl && p_source) noexcept {move(p_source);} | |
| 295 void clear() {m_owner = pfc::guid_null; m_data.clear();} | |
| 296 bool is_valid() const { return m_owner != pfc::guid_null; } | |
| 297 | |
| 298 const dsp_preset_impl& operator=(const dsp_preset_impl & p_source) {copy(p_source); return *this;} | |
| 299 const dsp_preset_impl& operator=(const dsp_preset & p_source) {copy(p_source); return *this;} | |
| 300 const dsp_preset_impl& operator=(dsp_preset_impl&& p_source) noexcept { move(p_source); return *this; } | |
| 301 | |
| 302 GUID get_owner() const {return m_owner;} | |
| 303 void set_owner(const GUID & p_owner) {m_owner = p_owner;} | |
| 304 const void * get_data() const {return m_data.ptr();} | |
| 305 t_size get_data_size() const {return m_data.size();} | |
| 306 void set_data(const void * p_data,t_size p_data_size) {m_data.set_data_fromptr((const t_uint8*)p_data,p_data_size);} | |
| 307 void set_data_from_stream(stream_reader * p_stream,t_size p_bytes,abort_callback & p_abort); | |
| 308 | |
| 309 void move(dsp_preset_impl& source) noexcept { | |
| 310 m_owner = source.m_owner; | |
| 311 m_data = std::move(source.m_data); | |
| 312 } | |
| 313 | |
| 314 void set_data(pfc::mem_block&& data) { m_data = std::move(data); } | |
| 315 private: | |
| 316 GUID m_owner = {}; | |
| 317 pfc::mem_block m_data; | |
| 318 }; | |
| 319 | |
| 320 class NOVTABLE dsp_preset_edit_callback { | |
| 321 public: | |
| 322 virtual void on_preset_changed(const dsp_preset &) = 0; | |
| 323 private: | |
| 324 dsp_preset_edit_callback(const dsp_preset_edit_callback&) = delete; | |
| 325 const dsp_preset_edit_callback & operator=(const dsp_preset_edit_callback &) = delete; | |
| 326 protected: | |
| 327 dsp_preset_edit_callback() {} | |
| 328 ~dsp_preset_edit_callback() {} | |
| 329 }; | |
| 330 | |
| 331 class NOVTABLE dsp_preset_edit_callback_v2 : public service_base { | |
| 332 FB2K_MAKE_SERVICE_INTERFACE(dsp_preset_edit_callback_v2, service_base); | |
| 333 public: | |
| 334 virtual void get_preset( dsp_preset & outPreset ) = 0; | |
| 335 virtual void set_preset( const dsp_preset & inPreset ) = 0; | |
| 336 virtual void dsp_dialog_done( bool bOK ) = 0; | |
| 337 void reset(); | |
| 338 }; | |
| 339 | |
| 340 | |
| 341 class NOVTABLE dsp_entry : public service_base { | |
| 342 public: | |
| 343 virtual void get_name(pfc::string_base & p_out) = 0; | |
| 344 virtual bool get_default_preset(dsp_preset & p_out) = 0; | |
| 345 virtual bool instantiate(service_ptr_t<dsp> & p_out,const dsp_preset & p_preset) = 0; | |
| 346 virtual GUID get_guid() = 0; | |
| 347 virtual bool have_config_popup() = 0; | |
| 348 | |
| 349 #ifdef FOOBAR2000_MOBILE | |
| 350 virtual void show_config_popup( fb2k::dspConfigContext_t parent, dsp_preset_edit_callback_v2::ptr callback ) {} | |
| 351 #endif | |
| 352 | |
| 353 #ifdef FOOBAR2000_DESKTOP | |
| 354 #ifdef _WIN32 | |
| 355 //! Shows configuration popup. Call from main thread only! \n | |
| 356 //! Blocks until done. Returns true if preset has been altered, false otherwise. | |
| 357 //! Legacy method, replaced in dsp_entry_v2 and newer. | |
| 358 virtual bool show_config_popup(dsp_preset& p_data, fb2k::hwnd_t p_parent) { (void)p_data; (void)p_parent; return false; } | |
| 359 #else // non-Windows desktop | |
| 360 //! Shows configuration popup. Main thread only! \n | |
| 361 //! Mac: returns NSObjectWrapper holding NSViewController | |
| 362 virtual service_ptr show_config_popup(fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback) { (void)parent; (void)callback; throw pfc::exception_not_implemented(); } | |
| 363 #endif | |
| 364 #endif // FOOBAR2000_DESKTOP | |
| 365 | |
| 366 //! Obsolete method, hidden DSPs now use a different entry class. | |
| 367 bool is_user_accessible() { return true; } | |
| 368 | |
| 369 static constexpr unsigned flag_playback = 1 << 0, | |
| 370 flag_conversion = 1 << 1; | |
| 371 | |
| 372 static bool g_get_interface(service_ptr_t<dsp_entry> & p_out,const GUID & p_guid); | |
| 373 static service_ptr_t<dsp_entry> g_get_interface(const GUID&); | |
| 374 static bool g_instantiate(service_ptr_t<dsp> & p_out,const dsp_preset & p_preset, unsigned flags = 0); | |
| 375 static bool g_instantiate_default(service_ptr_t<dsp> & p_out,const GUID & p_guid); | |
| 376 static bool g_name_from_guid(pfc::string_base & p_out,const GUID & p_guid); | |
| 377 static bool g_dsp_exists(const GUID & p_guid); | |
| 378 static bool g_get_default_preset(dsp_preset & p_out,const GUID & p_guid); | |
| 379 static bool g_have_config_popup(const GUID & p_guid); | |
| 380 static bool g_have_config_popup(const dsp_preset & p_preset); | |
| 381 #ifdef _WIN32 | |
| 382 //! Shows configuration popup. Main thread only! \n | |
| 383 //! Blocks until done. Returns true if preset has been altered, false otherwise. | |
| 384 static bool g_show_config_popup(dsp_preset & p_preset,fb2k::hwnd_t p_parent); | |
| 385 //! Shows configuration popup. Main thread only! | |
| 386 //! Blocks until done. Uses callback to notify host about preset change. | |
| 387 static void g_show_config_popup_v2(const dsp_preset & p_preset,fb2k::hwnd_t p_parent,dsp_preset_edit_callback & p_callback); | |
| 388 | |
| 389 //! Shows configuration popup. Main thread only! \n | |
| 390 //! Blocks until done. Uses callback to notify host about preset change. \n | |
| 391 //! Implements a fallback using legacy methods if show_config_popup_v2() is not available. \n | |
| 392 //! @returns OK/cancel status (true/false), if the dialog supports it; otherwise always true. | |
| 393 bool show_config_popup_v2_(const dsp_preset& p_preset, fb2k::hwnd_t p_parent, dsp_preset_edit_callback& p_callback); | |
| 394 | |
| 395 //! Shows configuration popup. Main thread only! \n | |
| 396 //! May either block until done and return null, or run asynchronously and return an object to release to cancel the dialog. \n | |
| 397 //! Implements a fallback using legacy methods if show_config_popup_v3() is not available. | |
| 398 service_ptr show_config_popup_v3_(fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback); | |
| 399 #endif | |
| 400 | |
| 401 #ifdef FOOBAR2000_MOBILE | |
| 402 static void g_show_config_popup( fb2k::dspConfigContext_t parent, dsp_preset_edit_callback_v2::ptr callback); | |
| 403 #endif | |
| 404 | |
| 405 bool get_display_name_supported(); | |
| 406 void get_display_name_(const dsp_preset& arg, pfc::string_base& out); | |
| 407 bool enumerate_default_presets_(dsp_chain_config& ret); | |
| 408 bool match_preset_subclass_(dsp_preset const& x, dsp_preset const& y); | |
| 409 | |
| 410 FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(dsp_entry); | |
| 411 }; | |
| 412 | |
| 413 class NOVTABLE dsp_entry_v2 : public dsp_entry { | |
| 414 public: | |
| 415 #ifdef _WIN32 | |
| 416 //! Shows configuration popup. Main thread only! | |
| 417 virtual void show_config_popup_v2(const dsp_preset & p_data,fb2k::hwnd_t p_parent,dsp_preset_edit_callback & p_callback) = 0; | |
| 418 // Obsolete method, redirected to show_config_popup_v2() by default, no need to implement. | |
| 419 bool show_config_popup(dsp_preset& p_data, fb2k::hwnd_t p_parent) override; | |
| 420 #endif | |
| 421 private: | |
| 422 | |
| 423 FB2K_MAKE_SERVICE_INTERFACE(dsp_entry_v2,dsp_entry); | |
| 424 }; | |
| 425 | |
| 426 //! \since Late 1.6.x | |
| 427 class NOVTABLE dsp_entry_v3 : public dsp_entry_v2 { | |
| 428 FB2K_MAKE_SERVICE_INTERFACE(dsp_entry_v3, dsp_entry_v2); | |
| 429 public: | |
| 430 //! Returns the text to show in DSP list, for this specific preset. | |
| 431 virtual void get_display_name(const dsp_preset& arg, pfc::string_base& out) = 0; | |
| 432 | |
| 433 #ifdef _WIN32 | |
| 434 //! Shows configuration popup, asynchronous version - creates dialog then returns immediately. \n | |
| 435 //! Since not every DSP implements this, caller must be prepared to call legacy blocking show_config_popup methods instead. \n | |
| 436 //! show_config_popup_v3() may throw pfc::exception_not_implemented() to signal host that this DSP doesn't support this method yet. \n | |
| 437 //! Main thread only! \n | |
| 438 //! @returns Object to retain by host, to be released to request the dialog to be closed. | |
| 439 virtual service_ptr show_config_popup_v3(fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback) = 0; | |
| 440 #endif | |
| 441 }; | |
| 442 | |
| 443 //! \since 2.1 | |
| 444 class NOVTABLE dsp_entry_v4 : public dsp_entry_v3 { | |
| 445 FB2K_MAKE_SERVICE_INTERFACE(dsp_entry_v4, dsp_entry_v3); | |
| 446 public: | |
| 447 virtual dsp::ptr instantiate_v4( const dsp_preset & arg, unsigned flags ) = 0; | |
| 448 }; | |
| 449 | |
| 450 //! \since 2.2 | |
| 451 class NOVTABLE dsp_entry_v5 : public dsp_entry_v4 { | |
| 452 FB2K_MAKE_SERVICE_INTERFACE(dsp_entry_v5, dsp_entry_v4); | |
| 453 public: | |
| 454 //! If your DSP implementation is meant to preset as multiple item in available DSP list, implement this method. \n | |
| 455 //! @returns True if preset list has been returned (your DSP will be hidden if blank), false if your DSP is meant to be shown as just one item. | |
| 456 virtual bool enumerate_default_presets(dsp_chain_config& ret) { (void)ret; return false; } | |
| 457 //! Can possibly reach state Y by editing state X, and vice versa? \n | |
| 458 //! If this DSP has no configuration UI, this should just test if the presets are identical. | |
| 459 //! Frontend will use this to pin running presets to one of available DSP list items. | |
| 460 virtual bool match_preset_subclass(dsp_preset const& x, dsp_preset const& y) { (void)x; (void)y; return true; } | |
| 461 }; | |
| 462 | |
| 463 class NOVTABLE dsp_entry_hidden : public service_base { | |
| 464 FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(dsp_entry_hidden); | |
| 465 public: | |
| 466 //! Obsolete method, hidden DSPs now use a different entry class from ordinary ones. | |
| 467 bool is_user_accessible() {return false; } | |
| 468 | |
| 469 static bool g_get_interface( dsp_entry_hidden::ptr & out, const GUID & guid ); | |
| 470 static bool g_instantiate( dsp::ptr & out, const dsp_preset & preset ); | |
| 471 static bool g_dsp_exists(const GUID & p_guid); | |
| 472 | |
| 473 virtual bool instantiate(service_ptr_t<dsp> & p_out,const dsp_preset & p_preset) = 0; | |
| 474 virtual GUID get_guid() = 0; | |
| 475 }; | |
| 476 | |
| 477 template<class T,class t_entry = dsp_entry> | |
| 478 class dsp_entry_impl_nopreset_t : public t_entry { | |
| 479 public: | |
| 480 void get_name(pfc::string_base & p_out) override {T::g_get_name(p_out);} | |
| 481 bool get_default_preset(dsp_preset & p_out) override | |
| 482 { | |
| 483 p_out.set_owner(T::g_get_guid()); | |
| 484 p_out.set_data(0,0); | |
| 485 return true; | |
| 486 } | |
| 487 bool instantiate(service_ptr_t<dsp> & p_out,const dsp_preset & p_preset) override | |
| 488 { | |
| 489 if (p_preset.get_owner() == T::g_get_guid() && p_preset.get_data_size() == 0) | |
| 490 { | |
| 491 p_out = new service_impl_t<T>(); | |
| 492 return p_out.is_valid(); | |
| 493 } | |
| 494 else return false; | |
| 495 } | |
| 496 GUID get_guid() override {return T::g_get_guid();} | |
| 497 | |
| 498 bool have_config_popup() override {return false;} | |
| 499 }; | |
| 500 | |
| 501 template<typename T, typename interface_t> | |
| 502 class dsp_entry_common_t : public interface_t { | |
| 503 public: | |
| 504 void get_name(pfc::string_base& p_out) override { T::g_get_name(p_out); } | |
| 505 bool get_default_preset(dsp_preset& p_out) override { return T::g_get_default_preset(p_out); } | |
| 506 bool instantiate(service_ptr_t<dsp>& p_out, const dsp_preset& p_preset) override { | |
| 507 if (p_preset.get_owner() == T::g_get_guid()) { | |
| 508 p_out = new service_impl_t<T>(p_preset); | |
| 509 return true; | |
| 510 } else return false; | |
| 511 } | |
| 512 GUID get_guid() override { return T::g_get_guid(); } | |
| 513 | |
| 514 bool have_config_popup() override { return T::g_have_config_popup(); } | |
| 515 #ifdef FOOBAR2000_MOBILE | |
| 516 void show_config_popup( fb2k::dspConfigContext_t parent, dsp_preset_edit_callback_v2::ptr callback ) override { T::g_show_config_popup(parent, callback); } | |
| 517 #endif | |
| 518 #if defined(FOOBAR2000_DESKTOP) && !defined(_WIN32) | |
| 519 service_ptr show_config_popup( fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback ) override { | |
| 520 return T::g_show_config_popup(parent, callback); | |
| 521 } | |
| 522 #endif | |
| 523 }; | |
| 524 | |
| 525 template<class T, class t_entry = dsp_entry> | |
| 526 class dsp_entry_impl_t : public dsp_entry_common_t<T, t_entry> { | |
| 527 public: | |
| 528 #ifdef _WIN32 | |
| 529 bool show_config_popup(dsp_preset & p_data,fb2k::hwnd_t p_parent) override {return T::g_show_config_popup(p_data,p_parent);} | |
| 530 #endif | |
| 531 }; | |
| 532 | |
| 533 template<class T, class t_entry = dsp_entry_v2> | |
| 534 class dsp_entry_v2_impl_t : public dsp_entry_common_t<T, t_entry> { | |
| 535 public: | |
| 536 #ifdef _WIN32 | |
| 537 void show_config_popup_v2(const dsp_preset & p_data,fb2k::hwnd_t p_parent,dsp_preset_edit_callback & p_callback) override {T::g_show_config_popup(p_data,p_parent,p_callback);} | |
| 538 #endif | |
| 539 }; | |
| 540 | |
| 541 template<class T, class t_entry = dsp_entry_v3> | |
| 542 class dsp_entry_v3_impl_t : public dsp_entry_v2_impl_t<T, t_entry> { | |
| 543 public: | |
| 544 void get_display_name(const dsp_preset& arg, pfc::string_base& out) override { | |
| 545 T::g_get_display_name(arg, out); | |
| 546 } | |
| 547 | |
| 548 #ifdef _WIN32 | |
| 549 service_ptr show_config_popup_v3(fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback) override { | |
| 550 return T::g_show_config_popup_v3(parent, callback); | |
| 551 } | |
| 552 #endif | |
| 553 }; | |
| 554 | |
| 555 template<class dsp_t, class entry_t = dsp_entry_v4> | |
| 556 class dsp_entry_v4_impl_t : public dsp_entry_v3_impl_t< dsp_t, entry_t > { | |
| 557 public: | |
| 558 dsp::ptr instantiate_v4( const dsp_preset & arg, unsigned flags ) override { | |
| 559 PFC_ASSERT( arg.get_owner() == dsp_t::g_get_guid() ); | |
| 560 return new service_impl_t< dsp_t > ( arg, flags ); | |
| 561 } | |
| 562 }; | |
| 563 | |
| 564 template<class dsp_t, class entry_t = dsp_entry_v5> | |
| 565 class dsp_entry_v5_impl_t : public dsp_entry_v4_impl_t< dsp_t, entry_t > { | |
| 566 public: | |
| 567 bool enumerate_default_presets(dsp_chain_config& ret) { | |
| 568 return dsp_t::g_enumerate_default_presets(ret); | |
| 569 } | |
| 570 bool match_preset_subclass(dsp_preset const& x, dsp_preset const& y) { | |
| 571 return dsp_t::g_match_preset_subclass(x, y); | |
| 572 } | |
| 573 }; | |
| 574 | |
| 575 template<typename dsp_t> | |
| 576 class dsp_entry_hidden_t : public dsp_entry_hidden { | |
| 577 public: | |
| 578 bool instantiate(service_ptr_t<dsp> & p_out,const dsp_preset & p_preset) override { | |
| 579 if (p_preset.get_owner() == dsp_t::g_get_guid()) { | |
| 580 p_out = new service_impl_t<dsp_t>(p_preset); | |
| 581 return true; | |
| 582 } else return false; | |
| 583 } | |
| 584 GUID get_guid() override {return dsp_t::g_get_guid();} | |
| 585 }; | |
| 586 | |
| 587 template<typename dsp_t, typename interface_t> | |
| 588 class implement_dsp_entry; | |
| 589 | |
| 590 template<typename dsp_t> class implement_dsp_entry<dsp_t, dsp_entry> : public dsp_entry_impl_t<dsp_t> {}; | |
| 591 template<typename dsp_t> class implement_dsp_entry<dsp_t, dsp_entry_v2> : public dsp_entry_v2_impl_t<dsp_t> {}; | |
| 592 template<typename dsp_t> class implement_dsp_entry<dsp_t, dsp_entry_v3> : public dsp_entry_v3_impl_t<dsp_t> {}; | |
| 593 template<typename dsp_t> class implement_dsp_entry<dsp_t, dsp_entry_v4> : public dsp_entry_v4_impl_t<dsp_t> {}; | |
| 594 template<typename dsp_t> class implement_dsp_entry<dsp_t, dsp_entry_v5> : public dsp_entry_v5_impl_t<dsp_t> {}; | |
| 595 template<typename dsp_t> class implement_dsp_entry<dsp_t, dsp_entry_hidden> : public dsp_entry_hidden_t<dsp_t> {}; | |
| 596 | |
| 597 template<class T> | |
| 598 class dsp_factory_nopreset_t : public service_factory_single_t<dsp_entry_impl_nopreset_t<T> > {}; | |
| 599 | |
| 600 template<typename dsp_t, typename interface_t = dsp_entry_v2> | |
| 601 class dsp_factory_t : public service_factory_single_t<implement_dsp_entry<dsp_t, interface_t> > {}; | |
| 602 | |
| 603 template<class T> | |
| 604 class dsp_factory_hidden_t : public service_factory_single_t< dsp_entry_hidden_t<T> > {}; | |
| 605 | |
| 606 class NOVTABLE dsp_chain_config | |
| 607 { | |
| 608 public: | |
| 609 virtual t_size get_count() const = 0; | |
| 610 virtual const dsp_preset & get_item(t_size p_index) const = 0; | |
| 611 virtual void replace_item(const dsp_preset & p_data,t_size p_index) = 0; | |
| 612 virtual void insert_item(const dsp_preset & p_data,t_size p_index) = 0; | |
| 613 virtual void remove_mask(const bit_array & p_mask) = 0; | |
| 614 | |
| 615 void remove_item(t_size p_index); | |
| 616 void remove_all(); | |
| 617 void add_item(const dsp_preset & p_data); | |
| 618 void copy(const dsp_chain_config & p_source); | |
| 619 void add_items(const dsp_chain_config & p_source); | |
| 620 | |
| 621 const dsp_chain_config & operator=(const dsp_chain_config & p_source) {copy(p_source); return *this;} | |
| 622 | |
| 623 void contents_to_stream(stream_writer * p_stream,abort_callback & p_abort) const; | |
| 624 void contents_from_stream(stream_reader * p_stream,abort_callback & p_abort); | |
| 625 fb2k::memBlock::ptr to_blob() const; | |
| 626 void from_blob(const void* p, size_t size); | |
| 627 void from_blob(fb2k::memBlock::ptr); | |
| 628 | |
| 629 pfc::string8 get_name_list() const; | |
| 630 void get_name_list(pfc::string_base & p_out) const; | |
| 631 | |
| 632 static bool equals(dsp_chain_config const & v1, dsp_chain_config const & v2); | |
| 633 static bool equals_debug(dsp_chain_config const& v1, dsp_chain_config const& v2); | |
| 634 | |
| 635 //! Helpers to enable/disable specific DSP in this chain. Return true if the chain has been altered, false otherwise. | |
| 636 bool enable_dsp( const GUID & dspID ); | |
| 637 //! Helpers to enable/disable specific DSP in this chain. Return true if the chain has been altered, false otherwise. | |
| 638 bool disable_dsp( const GUID & dspID ); | |
| 639 //! Helpers to enable/disable specific DSP in this chain. Return true if the chain has been altered, false otherwise. | |
| 640 bool enable_dsp( const dsp_preset & preset ); | |
| 641 | |
| 642 size_t find_first_of_type( const GUID & dspID ) const; | |
| 643 bool contains_dsp( const GUID & dspID ) const; | |
| 644 | |
| 645 pfc::string8 debug() const; | |
| 646 | |
| 647 bool operator==(const dsp_chain_config & other) const {return equals(*this, other);} | |
| 648 bool operator!=(const dsp_chain_config & other) const {return !equals(*this, other);} | |
| 649 }; | |
| 650 | |
| 651 FB2K_STREAM_READER_OVERLOAD(dsp_chain_config) { | |
| 652 value.contents_from_stream(&stream.m_stream, stream.m_abort); return stream; | |
| 653 } | |
| 654 | |
| 655 FB2K_STREAM_WRITER_OVERLOAD(dsp_chain_config) { | |
| 656 value.contents_to_stream(&stream.m_stream, stream.m_abort); return stream; | |
| 657 } | |
| 658 | |
| 659 class dsp_chain_config_impl : public dsp_chain_config | |
| 660 { | |
| 661 public: | |
| 662 dsp_chain_config_impl() {} | |
| 663 dsp_chain_config_impl(const dsp_chain_config & p_source) {copy(p_source);} | |
| 664 dsp_chain_config_impl(const dsp_chain_config_impl & p_source) { copy_v2(p_source);} | |
| 665 dsp_chain_config_impl(dsp_chain_config_impl&& p_source) noexcept : m_data(std::move(p_source.m_data)) {} | |
| 666 t_size get_count() const override; | |
| 667 const dsp_preset & get_item(t_size p_index) const override; | |
| 668 void replace_item(const dsp_preset & p_data,t_size p_index) override; | |
| 669 void insert_item(const dsp_preset & p_data,t_size p_index) override; | |
| 670 void remove_mask(const bit_array & p_mask) override; | |
| 671 | |
| 672 const char* get_dsp_name(size_t idx) const; | |
| 673 void insert_item_v2(const dsp_preset& data, const char* dspName, size_t index); | |
| 674 void add_item_v2(const dsp_preset& data, const char* dspName); | |
| 675 void copy_v2(dsp_chain_config_impl const&); | |
| 676 pfc::string8 debug() const; | |
| 677 | |
| 678 const dsp_chain_config_impl & operator=(const dsp_chain_config & p_source) {copy(p_source); return *this;} | |
| 679 const dsp_chain_config_impl & operator=(const dsp_chain_config_impl & p_source) {copy_v2(p_source); return *this;} | |
| 680 const dsp_chain_config_impl & operator=(dsp_chain_config_impl&& p_source) noexcept { m_data = std::move(p_source.m_data); p_source.m_data.remove_all(); return *this; } | |
| 681 | |
| 682 ~dsp_chain_config_impl(); | |
| 683 | |
| 684 void reorder( const size_t * order, size_t count ); | |
| 685 | |
| 686 void supply_name(size_t idx, pfc::string8 && name) { m_data[idx]->dspName = std::move(name); } | |
| 687 const char* find_dsp_name(const GUID& guid) const; | |
| 688 private: | |
| 689 | |
| 690 struct entry_t { | |
| 691 dsp_preset_impl data; | |
| 692 pfc::string8 dspName; | |
| 693 }; | |
| 694 | |
| 695 | |
| 696 pfc::ptr_list_t<entry_t> m_data; | |
| 697 }; | |
| 698 | |
| 699 //! Helper. | |
| 700 class dsp_preset_parser : public stream_reader_formatter<> { | |
| 701 public: | |
| 702 dsp_preset_parser(const dsp_preset& in) : stream_reader_formatter(_m_stream, fb2k::noAbort), m_data(in), _m_stream(in.get_data(), in.get_data_size()) {} | |
| 703 | |
| 704 void reset() { _m_stream.reset(); } | |
| 705 t_size get_remaining() const { return _m_stream.get_remaining(); } | |
| 706 | |
| 707 void assume_empty() const { | |
| 708 if (get_remaining() != 0) throw exception_io_data(); | |
| 709 } | |
| 710 | |
| 711 GUID get_owner() const { return m_data.get_owner(); } | |
| 712 private: | |
| 713 const dsp_preset& m_data; | |
| 714 stream_reader_memblock_ref _m_stream; | |
| 715 }; | |
| 716 | |
| 717 //! Helper. | |
| 718 class dsp_preset_builder : public stream_writer_formatter<> { | |
| 719 public: | |
| 720 dsp_preset_builder() : stream_writer_formatter(_m_stream, fb2k::noAbort) {} | |
| 721 void finish(const GUID& id, dsp_preset& out) { | |
| 722 out.set_owner(id); | |
| 723 out.set_data(_m_stream.m_buffer.get_ptr(), _m_stream.m_buffer.get_size()); | |
| 724 } | |
| 725 void reset() { | |
| 726 _m_stream.m_buffer.set_size(0); | |
| 727 } | |
| 728 private: | |
| 729 stream_writer_buffer_simple _m_stream; | |
| 730 }; | |
| 731 | |
| 732 #ifdef __APPLE__ | |
| 733 // NSObjectWrapper | |
| 734 #include "commonObjects-Apple.h" | |
| 735 #endif | |
| 736 | |
| 737 #endif |
