|
1
|
1 #include "foobar2000-sdk-pch.h"
|
|
|
2 #include "dsp.h"
|
|
|
3 #include "resampler.h"
|
|
|
4
|
|
|
5 #ifdef FOOBAR2000_HAVE_DSP
|
|
|
6
|
|
|
7 #include <math.h>
|
|
|
8
|
|
|
9 audio_chunk * dsp_chunk_list::add_item(t_size hint_size) { return insert_item(get_count(), hint_size); }
|
|
|
10
|
|
|
11 void dsp_chunk_list::remove_all() { remove_mask(pfc::bit_array_true()); }
|
|
|
12
|
|
|
13 double dsp_chunk_list::get_duration() {
|
|
|
14 double rv = 0;
|
|
|
15 t_size n, m = get_count();
|
|
|
16 for (n = 0; n<m; n++) rv += get_item(n)->get_duration();
|
|
|
17 return rv;
|
|
|
18 }
|
|
|
19
|
|
|
20 void dsp_chunk_list::add_chunk(const audio_chunk * chunk) {
|
|
|
21 audio_chunk * dst = insert_item(get_count(), chunk->get_used_size());
|
|
|
22 if (dst) dst->copy(*chunk);
|
|
|
23 }
|
|
|
24
|
|
|
25 t_size dsp_chunk_list_impl::get_count() const {return m_data.size();}
|
|
|
26
|
|
|
27 audio_chunk * dsp_chunk_list_impl::get_item(t_size n) const {return n<m_data.size() ? m_data[n].get() : 0; }
|
|
|
28
|
|
|
29 void dsp_chunk_list_impl::remove_by_idx(t_size idx)
|
|
|
30 {
|
|
|
31 PFC_ASSERT(idx < get_count());
|
|
|
32 m_recycled.push_back(std::move(m_data[idx]));
|
|
|
33 m_data.erase(m_data.begin() + idx);
|
|
|
34 }
|
|
|
35
|
|
|
36 void dsp_chunk_list_impl::remove_mask(const bit_array & mask)
|
|
|
37 {
|
|
|
38 const auto total = m_data.size();
|
|
|
39 mask.for_each(true, 0, total, [&](size_t idx) {
|
|
|
40 m_recycled.push_back(std::move(m_data[idx]));
|
|
|
41 });
|
|
|
42 pfc::remove_mask_t(m_data, mask);
|
|
|
43 }
|
|
|
44
|
|
|
45 audio_chunk * dsp_chunk_list_impl::insert_item(t_size idx,t_size hint_size)
|
|
|
46 {
|
|
|
47 t_size max = get_count();
|
|
|
48 if (idx>max) idx = max;
|
|
|
49 chunk_ptr_t ret;
|
|
|
50 if (!m_recycled.empty())
|
|
|
51 {
|
|
|
52 t_size best;
|
|
|
53 if (hint_size > 0)
|
|
|
54 {
|
|
|
55 best = 0;
|
|
|
56 t_size best_found = m_recycled[0]->get_data_size(), n, total = m_recycled.size();
|
|
|
57 for (n = 1; n < total; n++)
|
|
|
58 {
|
|
|
59 if (best_found == hint_size) break;
|
|
|
60 t_size size = m_recycled[n]->get_data_size();
|
|
|
61 int delta_old = abs((int)best_found - (int)hint_size), delta_new = abs((int)size - (int)hint_size);
|
|
|
62 if (delta_new < delta_old)
|
|
|
63 {
|
|
|
64 best_found = size;
|
|
|
65 best = n;
|
|
|
66 }
|
|
|
67 }
|
|
|
68 } else best = m_recycled.size() - 1;
|
|
|
69
|
|
|
70 ret = std::move(m_recycled[best]);
|
|
|
71 m_recycled.erase(m_recycled.begin() + best);
|
|
|
72 ret->set_sample_count(0);
|
|
|
73 ret->set_channels(0);
|
|
|
74 ret->set_srate(0);
|
|
|
75 } else ret = std::make_unique<audio_chunk_impl>();
|
|
|
76 auto pRet = &*ret;
|
|
|
77 if (idx == max) m_data.push_back(std::move(ret));
|
|
|
78 else m_data.insert(m_data.begin() + idx, std::move(ret));
|
|
|
79 return pRet;
|
|
|
80 }
|
|
|
81
|
|
|
82 void dsp_chunk_list::remove_bad_chunks()
|
|
|
83 {
|
|
|
84 bool blah = false;
|
|
|
85 t_size idx;
|
|
|
86 for(idx=0;idx<get_count();)
|
|
|
87 {
|
|
|
88 audio_chunk * chunk = get_item(idx);
|
|
|
89 if (!chunk->is_valid())
|
|
|
90 {
|
|
|
91 #if PFC_DEBUG
|
|
|
92 FB2K_console_formatter() << "Removing bad chunk: " << chunk->formatChunkSpec();
|
|
|
93 #endif
|
|
|
94 chunk->reset();
|
|
|
95 remove_by_idx(idx);
|
|
|
96 blah = true;
|
|
|
97 }
|
|
|
98 else idx++;
|
|
|
99 }
|
|
|
100 if (blah) console::info("one or more bad chunks removed from dsp chunk list");
|
|
|
101 }
|
|
|
102
|
|
|
103 bool dsp_entry_hidden::g_dsp_exists(const GUID & p_guid) {
|
|
|
104 dsp_entry_hidden::ptr p;
|
|
|
105 return g_get_interface(p, p_guid);
|
|
|
106 }
|
|
|
107
|
|
|
108 bool dsp_entry_hidden::g_get_interface( dsp_entry_hidden::ptr & out, const GUID & guid ) {
|
|
|
109 for (auto p : enumerate()) {
|
|
|
110 if (p->get_guid() == guid) {
|
|
|
111 out = p; return true;
|
|
|
112 }
|
|
|
113 }
|
|
|
114 return false;
|
|
|
115 }
|
|
|
116
|
|
|
117 bool dsp_entry_hidden::g_instantiate( dsp::ptr & out, const dsp_preset & preset ) {
|
|
|
118 dsp_entry_hidden::ptr i;
|
|
|
119 if (!g_get_interface(i, preset.get_owner())) return false;
|
|
|
120 return i->instantiate(out, preset);
|
|
|
121 }
|
|
|
122
|
|
|
123 bool dsp_entry::g_instantiate(service_ptr_t<dsp> & p_out,const dsp_preset & p_preset, unsigned flags )
|
|
|
124 {
|
|
|
125 service_ptr_t<dsp_entry> ptr;
|
|
|
126 if (!g_get_interface(ptr,p_preset.get_owner())) return false;
|
|
|
127 if ( flags != 0 ) {
|
|
|
128 dsp_entry_v4::ptr v4;
|
|
|
129 if (v4 &= ptr) {
|
|
|
130 p_out = v4->instantiate_v4(p_preset, flags);
|
|
|
131 return true;
|
|
|
132 }
|
|
|
133 }
|
|
|
134 return ptr->instantiate(p_out,p_preset);
|
|
|
135 }
|
|
|
136
|
|
|
137 bool dsp_entry::g_instantiate_default(service_ptr_t<dsp> & p_out,const GUID & p_guid)
|
|
|
138 {
|
|
|
139 service_ptr_t<dsp_entry> ptr;
|
|
|
140 if (!g_get_interface(ptr,p_guid)) return false;
|
|
|
141 dsp_preset_impl preset;
|
|
|
142 if (!ptr->get_default_preset(preset)) return false;
|
|
|
143 return ptr->instantiate(p_out,preset);
|
|
|
144 }
|
|
|
145
|
|
|
146 bool dsp_entry::g_name_from_guid(pfc::string_base & p_out,const GUID & p_guid)
|
|
|
147 {
|
|
|
148 service_ptr_t<dsp_entry> ptr;
|
|
|
149 if (!g_get_interface(ptr,p_guid)) return false;
|
|
|
150 ptr->get_name(p_out);
|
|
|
151 return true;
|
|
|
152 }
|
|
|
153
|
|
|
154 bool dsp_entry::g_dsp_exists(const GUID & p_guid)
|
|
|
155 {
|
|
|
156 service_ptr_t<dsp_entry> blah;
|
|
|
157 return g_get_interface(blah,p_guid);
|
|
|
158 }
|
|
|
159
|
|
|
160 bool dsp_entry::g_get_default_preset(dsp_preset & p_out,const GUID & p_guid)
|
|
|
161 {
|
|
|
162 service_ptr_t<dsp_entry> ptr;
|
|
|
163 if (!g_get_interface(ptr,p_guid)) return false;
|
|
|
164 return ptr->get_default_preset(p_out);
|
|
|
165 }
|
|
|
166
|
|
|
167 void dsp_chain_config::contents_to_stream(stream_writer * p_stream,abort_callback & p_abort) const {
|
|
|
168 uint32_t n, count = pfc::downcast_guarded<uint32_t>( get_count() );
|
|
|
169 p_stream->write_lendian_t(count,p_abort);
|
|
|
170 for(n=0;n<count;n++) {
|
|
|
171 get_item(n).contents_to_stream(p_stream,p_abort);
|
|
|
172 }
|
|
|
173 }
|
|
|
174
|
|
|
175 fb2k::memBlock::ptr dsp_chain_config::to_blob() const {
|
|
|
176 stream_writer_buffer_simple out;
|
|
|
177 this->contents_to_stream(&out, fb2k::noAbort);
|
|
|
178 return fb2k::memBlock::blockWithVector(out.m_buffer);
|
|
|
179 }
|
|
|
180
|
|
|
181 void dsp_chain_config::from_blob(const void* p, size_t size) {
|
|
|
182 if (size == 0) {
|
|
|
183 remove_all(); return;
|
|
|
184 }
|
|
|
185 stream_reader_memblock_ref reader(p, size);
|
|
|
186 this->contents_from_stream(&reader, fb2k::noAbort);
|
|
|
187 }
|
|
|
188
|
|
|
189 void dsp_chain_config::from_blob(fb2k::memBlock::ptr b) {
|
|
|
190 if (b.is_valid()) {
|
|
|
191 from_blob(b->data(), b->size());
|
|
|
192 } else {
|
|
|
193 this->remove_all();
|
|
|
194 }
|
|
|
195 }
|
|
|
196
|
|
|
197 void dsp_chain_config::contents_from_stream(stream_reader * p_stream,abort_callback & p_abort) {
|
|
|
198 t_uint32 n,count;
|
|
|
199
|
|
|
200 remove_all();
|
|
|
201
|
|
|
202 p_stream->read_lendian_t(count,p_abort);
|
|
|
203
|
|
|
204 dsp_preset_impl temp;
|
|
|
205
|
|
|
206 for(n=0;n<count;n++) {
|
|
|
207 temp.contents_from_stream(p_stream,p_abort);
|
|
|
208 add_item(temp);
|
|
|
209 }
|
|
|
210 }
|
|
|
211
|
|
|
212 void dsp_chain_config::remove_item(t_size p_index)
|
|
|
213 {
|
|
|
214 remove_mask(pfc::bit_array_one(p_index));
|
|
|
215 }
|
|
|
216
|
|
|
217 void dsp_chain_config::add_item(const dsp_preset & p_data)
|
|
|
218 {
|
|
|
219 insert_item(p_data,get_count());
|
|
|
220 }
|
|
|
221
|
|
|
222 void dsp_chain_config::remove_all()
|
|
|
223 {
|
|
|
224 remove_mask(pfc::bit_array_true());
|
|
|
225 }
|
|
|
226
|
|
|
227 size_t dsp_chain_config::find_first_of_type( const GUID & dspID ) const {
|
|
|
228 const size_t count = this->get_count();
|
|
|
229 for(size_t w = 0; w < count; ++w) {
|
|
|
230 if (this->get_item(w).get_owner() == dspID) return w;
|
|
|
231 }
|
|
|
232 return SIZE_MAX;
|
|
|
233 }
|
|
|
234
|
|
|
235 bool dsp_chain_config::contains_dsp( const GUID & dspID ) const {
|
|
|
236 return find_first_of_type( dspID ) != pfc_infinite;
|
|
|
237 }
|
|
|
238
|
|
|
239 bool dsp_chain_config::enable_dsp( const GUID & dspID ) {
|
|
|
240 if (this->contains_dsp( dspID )) return false;
|
|
|
241 dsp_preset_impl preset;
|
|
|
242 dsp_entry::g_get_default_preset( preset, dspID );
|
|
|
243 insert_item( preset, 0 );
|
|
|
244 return true;
|
|
|
245 }
|
|
|
246
|
|
|
247 bool dsp_chain_config::disable_dsp( const GUID & dspID ) {
|
|
|
248 const size_t count = this->get_count();
|
|
|
249 if (count == 0) return false;
|
|
|
250 bool rv = false;
|
|
|
251 pfc::bit_array_bittable mask( count );
|
|
|
252 for(size_t w = 0; w < count; ++ w) {
|
|
|
253 if (this->get_item(w).get_owner() == dspID ) {
|
|
|
254 rv = true;
|
|
|
255 mask.set(w, true);
|
|
|
256 }
|
|
|
257 }
|
|
|
258 if (rv) this->remove_mask( mask );
|
|
|
259 return rv;
|
|
|
260 }
|
|
|
261
|
|
|
262 bool dsp_chain_config::enable_dsp( const dsp_preset & preset ) {
|
|
|
263 dsp_chain_config & cfg = *this;
|
|
|
264 bool found = false;
|
|
|
265 bool changed = false;
|
|
|
266 t_size n,m = cfg.get_count();
|
|
|
267 for(n=0;n<m;n++) {
|
|
|
268 if (cfg.get_item(n).get_owner() == preset.get_owner()) {
|
|
|
269 found = true;
|
|
|
270 if (cfg.get_item(n) != preset) {
|
|
|
271 cfg.replace_item(preset,n);
|
|
|
272 changed = true;
|
|
|
273 }
|
|
|
274 break;
|
|
|
275 }
|
|
|
276 }
|
|
|
277 if (!found) {cfg.insert_item(preset,0); changed = true;}
|
|
|
278
|
|
|
279 return changed;
|
|
|
280 }
|
|
|
281
|
|
|
282 void dsp_chain_config_impl::reorder(const size_t * order, size_t count) {
|
|
|
283 PFC_ASSERT( count == m_data.get_count() );
|
|
|
284 m_data.reorder( order );
|
|
|
285 }
|
|
|
286
|
|
|
287 t_size dsp_chain_config_impl::get_count() const
|
|
|
288 {
|
|
|
289 return m_data.get_count();
|
|
|
290 }
|
|
|
291
|
|
|
292 const dsp_preset & dsp_chain_config_impl::get_item(t_size p_index) const
|
|
|
293 {
|
|
|
294 return m_data[p_index]->data;
|
|
|
295 }
|
|
|
296
|
|
|
297 void dsp_chain_config_impl::replace_item(const dsp_preset & p_data,t_size p_index)
|
|
|
298 {
|
|
|
299 auto& obj = *m_data[p_index];
|
|
|
300 if (p_data.get_owner() != obj.data.get_owner()) {
|
|
|
301 obj.dspName = p_data.get_owner_name();
|
|
|
302 }
|
|
|
303 obj.data = p_data;
|
|
|
304 }
|
|
|
305
|
|
|
306 void dsp_chain_config_impl::insert_item(const dsp_preset & p_data,t_size p_index)
|
|
|
307 {
|
|
|
308 this->insert_item_v2(p_data, nullptr, p_index);
|
|
|
309 }
|
|
|
310
|
|
|
311 void dsp_chain_config_impl::remove_mask(const bit_array & p_mask)
|
|
|
312 {
|
|
|
313 m_data.delete_mask(p_mask);
|
|
|
314 }
|
|
|
315
|
|
|
316 dsp_chain_config_impl::~dsp_chain_config_impl()
|
|
|
317 {
|
|
|
318 m_data.delete_all();
|
|
|
319 }
|
|
|
320
|
|
|
321 const char* dsp_chain_config_impl::get_dsp_name(size_t idx) const {
|
|
|
322 auto& n = m_data[idx]->dspName;
|
|
|
323 if (n.is_empty()) return nullptr;
|
|
|
324 return n.c_str();
|
|
|
325 }
|
|
|
326
|
|
|
327 void dsp_chain_config_impl::insert_item_v2(const dsp_preset& data, const char* dspName_, size_t index) {
|
|
|
328 pfc::string8 dspName;
|
|
|
329 if (dspName_) dspName = dspName_;
|
|
|
330 if (dspName.length() == 0) dspName = data.get_owner_name();
|
|
|
331 m_data.insert_item(new entry_t{ data, std::move(dspName) }, index);
|
|
|
332 }
|
|
|
333
|
|
|
334 const char* dsp_chain_config_impl::find_dsp_name(const GUID& guid) const {
|
|
|
335 for (size_t walk = 0; walk < m_data.get_size(); ++walk) {
|
|
|
336 auto& obj = *m_data[walk];
|
|
|
337 if (obj.data.get_owner() == guid && obj.dspName.length() > 0) {
|
|
|
338 return obj.dspName.c_str();
|
|
|
339 }
|
|
|
340 }
|
|
|
341 return nullptr;
|
|
|
342 }
|
|
|
343
|
|
|
344 pfc::string8 dsp_preset::get_owner_name() const {
|
|
|
345 pfc::string8 ret;
|
|
|
346 dsp_entry::ptr obj;
|
|
|
347 if (dsp_entry::g_get_interface(obj, this->get_owner())) {
|
|
|
348 obj->get_name(ret);
|
|
|
349 }
|
|
|
350 return ret;
|
|
|
351 }
|
|
|
352
|
|
|
353 pfc::string8 dsp_preset::get_owner_name_debug() const {
|
|
|
354 pfc::string8 ret;
|
|
|
355 dsp_entry::ptr obj;
|
|
|
356 if (dsp_entry::g_get_interface(obj, this->get_owner())) {
|
|
|
357 obj->get_name(ret);
|
|
|
358 } else {
|
|
|
359 ret = "[unknown]";
|
|
|
360 }
|
|
|
361 return ret;
|
|
|
362 }
|
|
|
363
|
|
|
364 pfc::string8 dsp_preset::debug(const char * knownName) const {
|
|
|
365 pfc::string8 name;
|
|
|
366 if (knownName) name = knownName;
|
|
|
367 else name = this->get_owner_name_debug();
|
|
|
368 pfc::string8 ret;
|
|
|
369 ret << name << " :: " << pfc::print_guid(this->get_owner()) << " :: " << pfc::format_hexdump(this->get_data(), this->get_data_size());
|
|
|
370 return ret;
|
|
|
371 }
|
|
|
372
|
|
|
373 pfc::string8 dsp_chain_config::debug() const {
|
|
|
374 const size_t count = get_count();
|
|
|
375 pfc::string8 ret;
|
|
|
376 ret << "dsp_chain_config: " << count << " items";
|
|
|
377 for (size_t walk = 0; walk < count; ++walk) {
|
|
|
378 ret << "\n" << get_item(walk).debug();
|
|
|
379 }
|
|
|
380 return ret;
|
|
|
381 }
|
|
|
382
|
|
|
383 void dsp_chain_config_impl::add_item_v2(const dsp_preset& data, const char* dspName) {
|
|
|
384 insert_item_v2(data, dspName, get_count());
|
|
|
385 }
|
|
|
386
|
|
|
387 void dsp_chain_config_impl::copy_v2(dsp_chain_config_impl const& p_source) {
|
|
|
388 remove_all();
|
|
|
389 t_size n, m = p_source.get_count();
|
|
|
390 for (n = 0; n < m; n++)
|
|
|
391 add_item_v2(p_source.get_item(n), p_source.get_dsp_name(n));
|
|
|
392 }
|
|
|
393
|
|
|
394 pfc::string8 dsp_chain_config_impl::debug() const {
|
|
|
395 const size_t count = get_count();
|
|
|
396 pfc::string8 ret;
|
|
|
397 ret << "dsp_chain_config_impl: " << count << " items";
|
|
|
398 for (size_t walk = 0; walk < count; ++walk) {
|
|
|
399 ret << "\n" << get_item(walk).debug( this->get_dsp_name(walk) );
|
|
|
400 }
|
|
|
401 return ret;
|
|
|
402 }
|
|
|
403
|
|
|
404 void dsp_preset::contents_to_stream(stream_writer * p_stream,abort_callback & p_abort) const {
|
|
|
405 t_uint32 size = pfc::downcast_guarded<t_uint32>(get_data_size());
|
|
|
406 p_stream->write_lendian_t(get_owner(),p_abort);
|
|
|
407 p_stream->write_lendian_t(size,p_abort);
|
|
|
408 if (size > 0) {
|
|
|
409 p_stream->write_object(get_data(),size,p_abort);
|
|
|
410 }
|
|
|
411 }
|
|
|
412
|
|
|
413 void dsp_preset::contents_from_stream(stream_reader * p_stream,abort_callback & p_abort) {
|
|
|
414 t_uint32 size;
|
|
|
415 GUID guid;
|
|
|
416 p_stream->read_lendian_t(guid,p_abort);
|
|
|
417 set_owner(guid);
|
|
|
418 p_stream->read_lendian_t(size,p_abort);
|
|
|
419 if (size > 1024*1024*32) throw exception_io_data();
|
|
|
420 set_data_from_stream(p_stream,size,p_abort);
|
|
|
421 }
|
|
|
422
|
|
|
423 void dsp_preset::g_contents_from_stream_skip(stream_reader * p_stream,abort_callback & p_abort) {
|
|
|
424 t_uint32 size;
|
|
|
425 GUID guid;
|
|
|
426 p_stream->read_lendian_t(guid,p_abort);
|
|
|
427 p_stream->read_lendian_t(size,p_abort);
|
|
|
428 if (size > 1024*1024*32) throw exception_io_data();
|
|
|
429 p_stream->skip_object(size,p_abort);
|
|
|
430 }
|
|
|
431
|
|
|
432 void dsp_preset_impl::set_data_from_stream(stream_reader * p_stream,t_size p_bytes,abort_callback & p_abort) {
|
|
|
433 m_data.resize(p_bytes);
|
|
|
434 if (p_bytes > 0) p_stream->read_object(m_data.ptr(),p_bytes,p_abort);
|
|
|
435 }
|
|
|
436
|
|
|
437 void dsp_chain_config::add_items(const dsp_chain_config & p_source) {
|
|
|
438 t_size n, m = p_source.get_count();
|
|
|
439 for(n=0;n<m;n++)
|
|
|
440 add_item(p_source.get_item(n));
|
|
|
441 }
|
|
|
442
|
|
|
443 void dsp_chain_config::copy(const dsp_chain_config & p_source) {
|
|
|
444 remove_all();
|
|
|
445 add_items( p_source );
|
|
|
446 }
|
|
|
447
|
|
|
448 bool dsp_entry::g_have_config_popup(const GUID & p_guid)
|
|
|
449 {
|
|
|
450 service_ptr_t<dsp_entry> entry;
|
|
|
451 if (!g_get_interface(entry,p_guid)) return false;
|
|
|
452 return entry->have_config_popup();
|
|
|
453 }
|
|
|
454
|
|
|
455 bool dsp_entry::g_have_config_popup(const dsp_preset & p_preset)
|
|
|
456 {
|
|
|
457 return g_have_config_popup(p_preset.get_owner());
|
|
|
458 }
|
|
|
459
|
|
|
460 #ifdef _WIN32
|
|
|
461 bool dsp_entry::g_show_config_popup(dsp_preset & p_preset,fb2k::hwnd_t p_parent)
|
|
|
462 {
|
|
|
463 service_ptr_t<dsp_entry> entry;
|
|
|
464 if (!g_get_interface(entry,p_preset.get_owner())) return false;
|
|
|
465 return entry->show_config_popup(p_preset,p_parent);
|
|
|
466 }
|
|
|
467
|
|
|
468 bool dsp_entry::show_config_popup_v2_(const dsp_preset& p_preset, fb2k::hwnd_t p_parent, dsp_preset_edit_callback& p_callback) {
|
|
|
469 PFC_ASSERT(p_preset.get_owner() == this->get_guid());
|
|
|
470 try {
|
|
|
471 service_ptr_t<dsp_entry_v2> entry_v2;
|
|
|
472 if (entry_v2 &= this) {
|
|
|
473 entry_v2->show_config_popup_v2(p_preset, p_parent, p_callback);
|
|
|
474 return true;
|
|
|
475 }
|
|
|
476 } catch (pfc::exception_not_implemented const&) {}
|
|
|
477
|
|
|
478 dsp_preset_impl temp(p_preset);
|
|
|
479 bool rv = this->show_config_popup(temp, p_parent);
|
|
|
480 if (rv) p_callback.on_preset_changed(temp);
|
|
|
481 return rv;
|
|
|
482 }
|
|
|
483 namespace {
|
|
|
484 class dsp_preset_edit_callback_callV2 : public dsp_preset_edit_callback {
|
|
|
485 public:
|
|
|
486 dsp_preset_edit_callback_v2::ptr chain;
|
|
|
487 void on_preset_changed(const dsp_preset& arg) override { chain->set_preset(arg); }
|
|
|
488 };
|
|
|
489 }
|
|
|
490 service_ptr dsp_entry::show_config_popup_v3_(fb2k::hwnd_t parent, dsp_preset_edit_callback_v2::ptr callback) {
|
|
|
491 dsp_entry_v3::ptr v3;
|
|
|
492 if (v3 &= this) {
|
|
|
493 try {
|
|
|
494 return v3->show_config_popup_v3(parent, callback);
|
|
|
495 } catch (pfc::exception_not_implemented const &) {
|
|
|
496 }
|
|
|
497 }
|
|
|
498
|
|
|
499 dsp_preset_edit_callback_callV2 cb;
|
|
|
500 cb.chain = callback;
|
|
|
501
|
|
|
502 dsp_preset_impl initPreset; callback->get_preset(initPreset);
|
|
|
503 bool status = this->show_config_popup_v2_(initPreset, parent, cb);
|
|
|
504 callback->dsp_dialog_done(status);
|
|
|
505 return nullptr;
|
|
|
506
|
|
|
507 }
|
|
|
508 void dsp_entry::g_show_config_popup_v2(const dsp_preset & p_preset,fb2k::hwnd_t p_parent,dsp_preset_edit_callback & p_callback) {
|
|
|
509 auto api = g_get_interface(p_preset.get_owner());
|
|
|
510 if (api.is_valid()) api->show_config_popup_v2_(p_preset, p_parent, p_callback);
|
|
|
511 }
|
|
|
512 #endif
|
|
|
513
|
|
|
514 service_ptr_t<dsp_entry> dsp_entry::g_get_interface(const GUID& guid) {
|
|
|
515 for (auto ptr : enumerate()) {
|
|
|
516 if (ptr->get_guid() == guid) return ptr;
|
|
|
517 }
|
|
|
518 return nullptr;
|
|
|
519 }
|
|
|
520
|
|
|
521 bool dsp_entry::g_get_interface(service_ptr_t<dsp_entry> & p_out,const GUID & p_guid)
|
|
|
522 {
|
|
|
523 for (auto ptr : enumerate()) {
|
|
|
524 if (ptr->get_guid() == p_guid) {
|
|
|
525 p_out = ptr;
|
|
|
526 return true;
|
|
|
527 }
|
|
|
528 }
|
|
|
529 return false;
|
|
|
530 }
|
|
|
531
|
|
|
532 bool resampler_entry::g_get_interface(service_ptr_t<resampler_entry> & p_out,unsigned p_srate_from,unsigned p_srate_to)
|
|
|
533 {
|
|
|
534 #if defined(FOOBAR2000_DESKTOP) && FOOBAR2000_TARGET_VERSION >= 79
|
|
|
535 auto r = resampler_manager::get()->get_resampler( p_srate_from, p_srate_to );
|
|
|
536 bool v = r.is_valid();
|
|
|
537 if ( v ) p_out = std::move(r);
|
|
|
538 return v;
|
|
|
539 #else
|
|
|
540
|
|
|
541 #ifdef FOOBAR2000_DESKTOP
|
|
|
542 {
|
|
|
543 resampler_manager::ptr api;
|
|
|
544 if ( resampler_manager::tryGet(api) ) {
|
|
|
545 auto r = api->get_resampler( p_srate_from, p_srate_to );
|
|
|
546 bool v = r.is_valid();
|
|
|
547 if (v) p_out = std::move(r);
|
|
|
548 return v;
|
|
|
549 }
|
|
|
550 }
|
|
|
551 #endif
|
|
|
552
|
|
|
553 resampler_entry::ptr ptr_resampler;
|
|
|
554 service_enum_t<dsp_entry> e;
|
|
|
555 float found_priority = 0;
|
|
|
556 resampler_entry::ptr found;
|
|
|
557 while(e.next(ptr_resampler))
|
|
|
558 {
|
|
|
559 if (p_srate_from == 0 || ptr_resampler->is_conversion_supported(p_srate_from,p_srate_to))
|
|
|
560 {
|
|
|
561 float priority = ptr_resampler->get_priority();
|
|
|
562 if (found.is_empty() || priority > found_priority)
|
|
|
563 {
|
|
|
564 found = ptr_resampler;
|
|
|
565 found_priority = priority;
|
|
|
566 }
|
|
|
567 }
|
|
|
568 }
|
|
|
569 if (found.is_empty()) return false;
|
|
|
570 p_out = found;
|
|
|
571 return true;
|
|
|
572 #endif
|
|
|
573 }
|
|
|
574
|
|
|
575 bool resampler_entry::g_create_preset(dsp_preset & p_out,unsigned p_srate_from,unsigned p_srate_to,float p_qualityscale)
|
|
|
576 {
|
|
|
577 service_ptr_t<resampler_entry> entry;
|
|
|
578 if (!g_get_interface(entry,p_srate_from,p_srate_to)) return false;
|
|
|
579 return entry->create_preset(p_out,p_srate_to,p_qualityscale);
|
|
|
580 }
|
|
|
581
|
|
|
582 bool resampler_entry::g_create(service_ptr_t<dsp> & p_out,unsigned p_srate_from,unsigned p_srate_to,float p_qualityscale)
|
|
|
583 {
|
|
|
584 service_ptr_t<resampler_entry> entry;
|
|
|
585 if (!g_get_interface(entry,p_srate_from,p_srate_to)) return false;
|
|
|
586 dsp_preset_impl preset;
|
|
|
587 if (!entry->create_preset(preset,p_srate_to,p_qualityscale)) return false;
|
|
|
588 return entry->instantiate(p_out,preset);
|
|
|
589 }
|
|
|
590
|
|
|
591
|
|
|
592 bool dsp_chain_config::equals(dsp_chain_config const & v1, dsp_chain_config const & v2) {
|
|
|
593 const t_size count = v1.get_count();
|
|
|
594 if (count != v2.get_count()) return false;
|
|
|
595 for(t_size walk = 0; walk < count; ++walk) {
|
|
|
596 if (v1.get_item(walk) != v2.get_item(walk)) return false;
|
|
|
597 }
|
|
|
598 return true;
|
|
|
599 }
|
|
|
600 bool dsp_chain_config::equals_debug(dsp_chain_config const& v1, dsp_chain_config const& v2) {
|
|
|
601 FB2K_DebugLog() << "Comparing DSP chains";
|
|
|
602 const t_size count = v1.get_count();
|
|
|
603 if (count != v2.get_count()) {
|
|
|
604 FB2K_DebugLog() << "Count mismatch, " << count << " vs " << v2.get_count();
|
|
|
605 return false;
|
|
|
606 }
|
|
|
607 for (t_size walk = 0; walk < count; ++walk) {
|
|
|
608 if (v1.get_item(walk) != v2.get_item(walk)) {
|
|
|
609 FB2K_DebugLog() << "Item " << (walk+1) << " mismatch";
|
|
|
610 FB2K_DebugLog() << "Item 1: " << v1.get_item(walk).debug();
|
|
|
611 FB2K_DebugLog() << "Item 2: " << v2.get_item(walk).debug();
|
|
|
612 return false;
|
|
|
613 }
|
|
|
614 }
|
|
|
615 FB2K_DebugLog() << "DSP chains are identical";
|
|
|
616 return true;
|
|
|
617 }
|
|
|
618
|
|
|
619 void dsp_chain_config::get_name_list(pfc::string_base & p_out) const {
|
|
|
620 p_out = get_name_list();
|
|
|
621 }
|
|
|
622
|
|
|
623 pfc::string8 dsp_chain_config::get_name_list() const {
|
|
|
624 const size_t count = get_count();
|
|
|
625 pfc::string8 output; output.prealloc(1024);
|
|
|
626 for (size_t n = 0; n < count; n++)
|
|
|
627 {
|
|
|
628 const auto& preset = get_item(n);
|
|
|
629 service_ptr_t<dsp_entry> ptr;
|
|
|
630 if (dsp_entry::g_get_interface(ptr, preset.get_owner()))
|
|
|
631 {
|
|
|
632 pfc::string8 temp;
|
|
|
633 ptr->get_display_name_(preset, temp);
|
|
|
634 if (temp.length() > 0) {
|
|
|
635 if (output.length() > 0) output += ", ";
|
|
|
636 output += temp;
|
|
|
637 }
|
|
|
638 }
|
|
|
639 }
|
|
|
640
|
|
|
641 return output;
|
|
|
642 }
|
|
|
643
|
|
|
644 void dsp::run_abortable(dsp_chunk_list * p_chunk_list,const dsp_track_t & p_cur_file,int p_flags,abort_callback & p_abort) {
|
|
|
645 service_ptr_t<dsp_v2> this_v2;
|
|
|
646 if (this->service_query_t(this_v2)) this_v2->run_v2(p_chunk_list,p_cur_file,p_flags,p_abort);
|
|
|
647 else run(p_chunk_list,p_cur_file,p_flags);
|
|
|
648 }
|
|
|
649
|
|
|
650 bool dsp::apply_preset_(const dsp_preset& arg) {
|
|
|
651 dsp_v3::ptr v3;
|
|
|
652 if (v3 &= this) return v3->apply_preset(arg);
|
|
|
653 return false;
|
|
|
654 }
|
|
|
655
|
|
|
656 namespace {
|
|
|
657 class dsp_preset_edit_callback_impl : public dsp_preset_edit_callback {
|
|
|
658 public:
|
|
|
659 dsp_preset_edit_callback_impl(dsp_preset & p_data) : m_data(p_data) {}
|
|
|
660 void on_preset_changed(const dsp_preset & p_data) {m_data = p_data;}
|
|
|
661 private:
|
|
|
662 dsp_preset & m_data;
|
|
|
663 };
|
|
|
664 };
|
|
|
665
|
|
|
666 #ifdef _WIN32
|
|
|
667 bool dsp_entry_v2::show_config_popup(dsp_preset & p_data,fb2k::hwnd_t p_parent) {
|
|
|
668 PFC_ASSERT(p_data.get_owner() == get_guid());
|
|
|
669 dsp_preset_impl temp(p_data);
|
|
|
670
|
|
|
671 {
|
|
|
672 dsp_preset_edit_callback_impl cb(temp);
|
|
|
673 show_config_popup_v2(p_data,p_parent,cb);
|
|
|
674 }
|
|
|
675 PFC_ASSERT(temp.get_owner() == get_guid());
|
|
|
676 if (temp == p_data) return false;
|
|
|
677 p_data = temp;
|
|
|
678 return true;
|
|
|
679 }
|
|
|
680 #endif
|
|
|
681
|
|
|
682 #ifdef FOOBAR2000_MOBILE
|
|
|
683 void dsp_entry::g_show_config_popup( menu_context_ptr ctx, dsp_preset_edit_callback_v2::ptr callback) {
|
|
|
684 GUID dspID;
|
|
|
685 {
|
|
|
686 dsp_preset_impl temp;
|
|
|
687 callback->get_preset( temp );
|
|
|
688 dspID = temp.get_owner();
|
|
|
689 }
|
|
|
690
|
|
|
691 dsp_entry::ptr entry;
|
|
|
692 if (!g_get_interface( entry, dspID)) return;
|
|
|
693 if (!entry->have_config_popup()) return;
|
|
|
694 entry->show_config_popup( ctx, callback );
|
|
|
695 }
|
|
|
696 #endif // FOOBAR2000_MOBILE
|
|
|
697
|
|
|
698 #ifdef FOOBAR2000_DESKTOP
|
|
|
699 void resampler_manager::make_chain_(dsp_chain_config& outChain, unsigned rateFrom, unsigned rateTo, float qualityScale) {
|
|
|
700 resampler_manager_v2::ptr v2;
|
|
|
701 if (v2 &= this) {
|
|
|
702 v2->make_chain(outChain, rateFrom, rateTo, qualityScale);
|
|
|
703 } else {
|
|
|
704 outChain.remove_all();
|
|
|
705 auto obj = this->get_resampler(rateFrom, rateTo);
|
|
|
706 if (obj.is_valid()) {
|
|
|
707 dsp_preset_impl p;
|
|
|
708 if (obj->create_preset(p, rateTo, qualityScale)) {
|
|
|
709 outChain.add_item(p);
|
|
|
710 }
|
|
|
711 }
|
|
|
712 }
|
|
|
713 }
|
|
|
714 #endif
|
|
|
715
|
|
|
716 void dsp_preset_edit_callback_v2::reset() {
|
|
|
717 dsp_preset_impl temp; get_preset( temp );
|
|
|
718 GUID id = temp.get_owner(); temp.set_data(nullptr, 0);
|
|
|
719 if (dsp_entry::g_get_default_preset( temp, id )) {
|
|
|
720 this->set_preset( temp );
|
|
|
721 } else {
|
|
|
722 PFC_ASSERT(!"Should not get here - no such DSP");
|
|
|
723 }
|
|
|
724 }
|
|
|
725
|
|
|
726 bool dsp_entry::get_display_name_supported() {
|
|
|
727 dsp_entry_v3::ptr v3;
|
|
|
728 return v3 &= this;
|
|
|
729 }
|
|
|
730
|
|
|
731 void dsp_entry::get_display_name_(const dsp_preset& arg, pfc::string_base& out) {
|
|
|
732 PFC_ASSERT(arg.get_owner() == this->get_guid());
|
|
|
733 dsp_entry_v3::ptr v3;
|
|
|
734 if (v3 &= this) {
|
|
|
735 v3->get_display_name(arg, out); return;
|
|
|
736 }
|
|
|
737 get_name(out);
|
|
|
738 }
|
|
|
739
|
|
|
740 bool dsp_entry::enumerate_default_presets_(dsp_chain_config& ret) {
|
|
|
741 ret.remove_all();
|
|
|
742 dsp_entry_v5::ptr v5;
|
|
|
743 if (v5 &= this) {
|
|
|
744 bool rv = v5->enumerate_default_presets(ret);
|
|
|
745 #if PFC_DEBUG
|
|
|
746 for (size_t walk = 0; walk < ret.get_count(); ++walk) {
|
|
|
747 PFC_ASSERT(ret.get_item(walk).get_owner() == get_guid());
|
|
|
748 }
|
|
|
749 #endif
|
|
|
750 return rv;
|
|
|
751 }
|
|
|
752 return false;
|
|
|
753 }
|
|
|
754
|
|
|
755 bool dsp_entry::match_preset_subclass_(dsp_preset const& x, dsp_preset const& y) {
|
|
|
756 dsp_entry_v5::ptr v5;
|
|
|
757 if (v5 &= this) return v5->match_preset_subclass(x, y);
|
|
|
758 return true;
|
|
|
759 }
|
|
|
760
|
|
|
761 #endif // FOOBAR2000_HAVE_DSP
|