diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foosdk/sdk/foobar2000/SDK/decode_postprocessor.h	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,174 @@
+#pragma once
+
+#ifdef FOOBAR2000_HAVE_DSP
+#include "dsp.h"
+#include "file_info_impl.h"
+#include "input.h"
+
+//! \since 1.1
+//! This service is essentially a special workaround to easily decode DTS/HDCD content stored in files pretending to contain plain PCM data. \n
+//! Callers: Instead of calling this directly, you probably want to use input_postprocessed template. \n
+//! Implementers: This service is called only by specific decoders, not by all of them! Implementing your own to provide additional functionality is not recommended!
+class decode_postprocessor_instance : public service_base {
+	FB2K_MAKE_SERVICE_INTERFACE(decode_postprocessor_instance, service_base);
+public:
+	enum {
+		//! End of stream. Flush any buffered data during this call.
+		flag_eof = 1 << 0,
+		//! Stream has already been altered by another instance.
+		flag_altered = 1 << 1,
+	};
+	//! @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.
+	virtual bool run(dsp_chunk_list & p_chunk_list,t_uint32 p_flags,abort_callback & p_abort) = 0;
+	//! Manipulate live info (bit depth, bitrate, whatever) that might be altered by us.
+	virtual bool get_dynamic_info(file_info & p_out) = 0;
+	//! Called after seek.
+	virtual void flush() = 0;
+	//! Return >0 to signal that, after each flush(), you require <amount> of audio before your output becomes valid. \n
+	//! This causes decoder to seek to position-<amount> instead of <position> then discard first <amount> of your output.
+	virtual double get_buffer_ahead() = 0;
+};
+
+//! \since 1.1
+//! Entrypoint class for instantiating decode_postprocessor_instance. See decode_postprocessor_instance documentation for more information. \n
+//! Instead of calling this directly, you probably want to use input_postprocessed template.
+class decode_postprocessor_entry : public service_base {
+	FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(decode_postprocessor_entry)
+public:
+	virtual bool instantiate(const file_info & info, decode_postprocessor_instance::ptr & out) = 0;
+};
+
+
+//! Helper class for managing decode_postprocessor_instance objects. See also: input_postprocessed.
+class decode_postprocessor {
+public:
+	typedef decode_postprocessor_instance::ptr item;
+	void initialize(const file_info & info) {
+		m_items.remove_all();
+		for (auto ptr : decode_postprocessor_entry::enumerate()) {
+			item i;
+			if (ptr->instantiate(info, i)) m_items += i;
+		}
+	}
+	void run(dsp_chunk_list & p_chunk_list,bool p_eof,abort_callback & p_abort) {
+		t_uint32 flags = p_eof ? decode_postprocessor_instance::flag_eof : 0;
+		for (auto& item : m_items) {
+			if (item->run(p_chunk_list, flags, p_abort)) flags |= decode_postprocessor_instance::flag_altered;
+		}
+	}
+	void flush() {
+		for (auto& item : m_items) item->flush();
+	}
+	static bool should_bother() { 
+		return service_factory_base::is_service_present(decode_postprocessor_entry::class_guid);
+	}
+	bool is_active() const {
+		return m_items.get_size() > 0;
+	}
+	bool get_dynamic_info(file_info & p_out) {
+		bool rv = false;
+		for (auto& item : m_items) {
+			if (item->get_dynamic_info(p_out)) rv = true;
+		}
+		return rv;
+	}
+	void close() {
+		m_items.remove_all();
+	}
+	double get_buffer_ahead() {
+		double acc = 0;
+		for (auto& item : m_items) {
+			pfc::max_acc(acc, item->get_buffer_ahead());
+		}
+		return acc;
+	}
+private:
+	pfc::list_t<item> m_items;
+};
+
+//! Generic template to add decode_postprocessor support to your input class. Works with both single-track and multi-track inputs.
+template<typename baseclass> class input_postprocessed : public baseclass {
+public:
+	void decode_initialize(t_uint32 p_subsong,unsigned p_flags,abort_callback & p_abort) {
+		m_chunks.remove_all();
+		m_haveEOF = false;
+		m_toSkip = 0;
+		m_postproc.close();
+		if ((p_flags & input_flag_no_postproc) == 0 && m_postproc.should_bother()) {
+			file_info_impl info;
+			this->get_info(p_subsong, info, p_abort);
+			m_postproc.initialize(info);
+		}
+		baseclass::decode_initialize(p_subsong, p_flags, p_abort);
+	}
+	void decode_initialize(unsigned p_flags,abort_callback & p_abort) {
+		m_chunks.remove_all();
+		m_haveEOF = false;
+		m_toSkip = 0;
+		m_postproc.close();
+		if ((p_flags & input_flag_no_postproc) == 0 && m_postproc.should_bother()) {
+			file_info_impl info;
+			this->get_info(info, p_abort);
+			m_postproc.initialize(info);
+		}
+		baseclass::decode_initialize(p_flags, p_abort);
+	}
+	bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort) {
+		if (m_postproc.is_active()) {
+			for(;;) {
+				p_abort.check();
+				if (m_chunks.get_count() > 0) {
+					audio_chunk * c = m_chunks.get_item(0);
+					if (m_toSkip > 0) {
+						if (!c->process_skip(m_toSkip)) {
+							m_chunks.remove_by_idx(0);
+							continue;
+						}
+					}
+					p_chunk = *c;
+					m_chunks.remove_by_idx(0);
+					return true;
+				}
+				if (m_haveEOF) return false;
+				if (!baseclass::decode_run(*m_chunks.insert_item(0), p_abort)) {
+					m_haveEOF = true;
+					m_chunks.remove_by_idx(0);
+				}
+				m_postproc.run(m_chunks, m_haveEOF, p_abort);
+			}
+		} else {
+			return baseclass::decode_run(p_chunk, p_abort);
+		}
+	}
+	bool decode_run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) {
+		if (m_postproc.is_active()) {
+			throw pfc::exception_not_implemented();
+		} else {
+			return baseclass::decode_run_raw(p_chunk, p_raw, p_abort);
+		}
+	}
+	bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {
+		bool rv = baseclass::decode_get_dynamic_info(p_out, p_timestamp_delta);
+		if (m_postproc.get_dynamic_info(p_out)) rv = true;
+		return rv;
+	}
+	void decode_seek(double p_seconds,abort_callback & p_abort) {
+		m_chunks.remove_all();
+		m_haveEOF = false;
+		m_postproc.flush();
+		double target = pfc::max_t<double>(0, p_seconds - m_postproc.get_buffer_ahead());
+		m_toSkip = p_seconds - target;
+		baseclass::decode_seek(target, p_abort);
+	}
+private:
+	dsp_chunk_list_impl m_chunks;
+	bool m_haveEOF;
+	double m_toSkip;
+	decode_postprocessor m_postproc;
+};
+
+#else // FOOBAR2000_HAVE_DSP
+
+template<typename baseclass> class input_postprocessed : public baseclass {};
+
+#endif // FOOBAR2000_HAVE_DSP