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