Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/SDK/decode_postprocessor.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 | |
| 3 #ifdef FOOBAR2000_HAVE_DSP | |
| 4 #include "dsp.h" | |
| 5 #include "file_info_impl.h" | |
| 6 #include "input.h" | |
| 7 | |
| 8 //! \since 1.1 | |
| 9 //! This service is essentially a special workaround to easily decode DTS/HDCD content stored in files pretending to contain plain PCM data. \n | |
| 10 //! Callers: Instead of calling this directly, you probably want to use input_postprocessed template. \n | |
| 11 //! Implementers: This service is called only by specific decoders, not by all of them! Implementing your own to provide additional functionality is not recommended! | |
| 12 class decode_postprocessor_instance : public service_base { | |
| 13 FB2K_MAKE_SERVICE_INTERFACE(decode_postprocessor_instance, service_base); | |
| 14 public: | |
| 15 enum { | |
| 16 //! End of stream. Flush any buffered data during this call. | |
| 17 flag_eof = 1 << 0, | |
| 18 //! Stream has already been altered by another instance. | |
| 19 flag_altered = 1 << 1, | |
| 20 }; | |
| 21 //! @returns True if the chunk list has been altered by the call, false if not - to tell possible other running instances whether the stream has already been altered or not. | |
| 22 virtual bool run(dsp_chunk_list & p_chunk_list,t_uint32 p_flags,abort_callback & p_abort) = 0; | |
| 23 //! Manipulate live info (bit depth, bitrate, whatever) that might be altered by us. | |
| 24 virtual bool get_dynamic_info(file_info & p_out) = 0; | |
| 25 //! Called after seek. | |
| 26 virtual void flush() = 0; | |
| 27 //! Return >0 to signal that, after each flush(), you require <amount> of audio before your output becomes valid. \n | |
| 28 //! This causes decoder to seek to position-<amount> instead of <position> then discard first <amount> of your output. | |
| 29 virtual double get_buffer_ahead() = 0; | |
| 30 }; | |
| 31 | |
| 32 //! \since 1.1 | |
| 33 //! Entrypoint class for instantiating decode_postprocessor_instance. See decode_postprocessor_instance documentation for more information. \n | |
| 34 //! Instead of calling this directly, you probably want to use input_postprocessed template. | |
| 35 class decode_postprocessor_entry : public service_base { | |
| 36 FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(decode_postprocessor_entry) | |
| 37 public: | |
| 38 virtual bool instantiate(const file_info & info, decode_postprocessor_instance::ptr & out) = 0; | |
| 39 }; | |
| 40 | |
| 41 | |
| 42 //! Helper class for managing decode_postprocessor_instance objects. See also: input_postprocessed. | |
| 43 class decode_postprocessor { | |
| 44 public: | |
| 45 typedef decode_postprocessor_instance::ptr item; | |
| 46 void initialize(const file_info & info) { | |
| 47 m_items.remove_all(); | |
| 48 for (auto ptr : decode_postprocessor_entry::enumerate()) { | |
| 49 item i; | |
| 50 if (ptr->instantiate(info, i)) m_items += i; | |
| 51 } | |
| 52 } | |
| 53 void run(dsp_chunk_list & p_chunk_list,bool p_eof,abort_callback & p_abort) { | |
| 54 t_uint32 flags = p_eof ? decode_postprocessor_instance::flag_eof : 0; | |
| 55 for (auto& item : m_items) { | |
| 56 if (item->run(p_chunk_list, flags, p_abort)) flags |= decode_postprocessor_instance::flag_altered; | |
| 57 } | |
| 58 } | |
| 59 void flush() { | |
| 60 for (auto& item : m_items) item->flush(); | |
| 61 } | |
| 62 static bool should_bother() { | |
| 63 return service_factory_base::is_service_present(decode_postprocessor_entry::class_guid); | |
| 64 } | |
| 65 bool is_active() const { | |
| 66 return m_items.get_size() > 0; | |
| 67 } | |
| 68 bool get_dynamic_info(file_info & p_out) { | |
| 69 bool rv = false; | |
| 70 for (auto& item : m_items) { | |
| 71 if (item->get_dynamic_info(p_out)) rv = true; | |
| 72 } | |
| 73 return rv; | |
| 74 } | |
| 75 void close() { | |
| 76 m_items.remove_all(); | |
| 77 } | |
| 78 double get_buffer_ahead() { | |
| 79 double acc = 0; | |
| 80 for (auto& item : m_items) { | |
| 81 pfc::max_acc(acc, item->get_buffer_ahead()); | |
| 82 } | |
| 83 return acc; | |
| 84 } | |
| 85 private: | |
| 86 pfc::list_t<item> m_items; | |
| 87 }; | |
| 88 | |
| 89 //! Generic template to add decode_postprocessor support to your input class. Works with both single-track and multi-track inputs. | |
| 90 template<typename baseclass> class input_postprocessed : public baseclass { | |
| 91 public: | |
| 92 void decode_initialize(t_uint32 p_subsong,unsigned p_flags,abort_callback & p_abort) { | |
| 93 m_chunks.remove_all(); | |
| 94 m_haveEOF = false; | |
| 95 m_toSkip = 0; | |
| 96 m_postproc.close(); | |
| 97 if ((p_flags & input_flag_no_postproc) == 0 && m_postproc.should_bother()) { | |
| 98 file_info_impl info; | |
| 99 this->get_info(p_subsong, info, p_abort); | |
| 100 m_postproc.initialize(info); | |
| 101 } | |
| 102 baseclass::decode_initialize(p_subsong, p_flags, p_abort); | |
| 103 } | |
| 104 void decode_initialize(unsigned p_flags,abort_callback & p_abort) { | |
| 105 m_chunks.remove_all(); | |
| 106 m_haveEOF = false; | |
| 107 m_toSkip = 0; | |
| 108 m_postproc.close(); | |
| 109 if ((p_flags & input_flag_no_postproc) == 0 && m_postproc.should_bother()) { | |
| 110 file_info_impl info; | |
| 111 this->get_info(info, p_abort); | |
| 112 m_postproc.initialize(info); | |
| 113 } | |
| 114 baseclass::decode_initialize(p_flags, p_abort); | |
| 115 } | |
| 116 bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort) { | |
| 117 if (m_postproc.is_active()) { | |
| 118 for(;;) { | |
| 119 p_abort.check(); | |
| 120 if (m_chunks.get_count() > 0) { | |
| 121 audio_chunk * c = m_chunks.get_item(0); | |
| 122 if (m_toSkip > 0) { | |
| 123 if (!c->process_skip(m_toSkip)) { | |
| 124 m_chunks.remove_by_idx(0); | |
| 125 continue; | |
| 126 } | |
| 127 } | |
| 128 p_chunk = *c; | |
| 129 m_chunks.remove_by_idx(0); | |
| 130 return true; | |
| 131 } | |
| 132 if (m_haveEOF) return false; | |
| 133 if (!baseclass::decode_run(*m_chunks.insert_item(0), p_abort)) { | |
| 134 m_haveEOF = true; | |
| 135 m_chunks.remove_by_idx(0); | |
| 136 } | |
| 137 m_postproc.run(m_chunks, m_haveEOF, p_abort); | |
| 138 } | |
| 139 } else { | |
| 140 return baseclass::decode_run(p_chunk, p_abort); | |
| 141 } | |
| 142 } | |
| 143 bool decode_run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) { | |
| 144 if (m_postproc.is_active()) { | |
| 145 throw pfc::exception_not_implemented(); | |
| 146 } else { | |
| 147 return baseclass::decode_run_raw(p_chunk, p_raw, p_abort); | |
| 148 } | |
| 149 } | |
| 150 bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) { | |
| 151 bool rv = baseclass::decode_get_dynamic_info(p_out, p_timestamp_delta); | |
| 152 if (m_postproc.get_dynamic_info(p_out)) rv = true; | |
| 153 return rv; | |
| 154 } | |
| 155 void decode_seek(double p_seconds,abort_callback & p_abort) { | |
| 156 m_chunks.remove_all(); | |
| 157 m_haveEOF = false; | |
| 158 m_postproc.flush(); | |
| 159 double target = pfc::max_t<double>(0, p_seconds - m_postproc.get_buffer_ahead()); | |
| 160 m_toSkip = p_seconds - target; | |
| 161 baseclass::decode_seek(target, p_abort); | |
| 162 } | |
| 163 private: | |
| 164 dsp_chunk_list_impl m_chunks; | |
| 165 bool m_haveEOF; | |
| 166 double m_toSkip; | |
| 167 decode_postprocessor m_postproc; | |
| 168 }; | |
| 169 | |
| 170 #else // FOOBAR2000_HAVE_DSP | |
| 171 | |
| 172 template<typename baseclass> class input_postprocessed : public baseclass {}; | |
| 173 | |
| 174 #endif // FOOBAR2000_HAVE_DSP |
