⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 foo_input_tak.cpp

📁 foo_input_tak-0.4.2-20080408-src tak文件识别代码
💻 CPP
字号:
#include "stdafx.h"
#include "tak_lib_cpp.h"

////////////////////////////////////////////////////////////////////
// Compile time debug options
#ifdef _DEBUG
#define TRACE_SEEKING
#endif

////////////////////////////////////////////////////////////////////
// Component version information

// Forward declaration
static pfc::string_formatter g_get_component_about();

DECLARE_COMPONENT_VERSION_COPY(
	"TAK Decoder",
	"0.4.2",
	g_get_component_about()
	);

////////////////////////////////////////////////////////////////////
// Supported file type
// Used for "Open File" dialog and file associations.

DECLARE_FILE_TYPE("TAK Files", "*.TAK");

////////////////////////////////////////////////////////////////////
// Settings for "Advanced" preferences page

// {C39E914E-05FB-4743-9FE5-AC50F10A2CE9}
static const GUID guid_branch_decoding_tak = { 0xc39e914e, 0x5fb, 0x4743, { 0x9f, 0xe5, 0xac, 0x50, 0xf1, 0xa, 0x2c, 0xe9 } };

// {82C21989-B97B-40ba-95A1-FA3269FB4AB8}
static const GUID guid_radio_tak_none = { 0x82c21989, 0xb97b, 0x40ba, { 0x95, 0xa1, 0xfa, 0x32, 0x69, 0xfb, 0x4a, 0xb8 } };

// {787EFCB4-EDAC-48b2-BF54-2D3D8159041D}
static const GUID guid_radio_tak_any = { 0x787efcb4, 0xedac, 0x48b2, { 0xbf, 0x54, 0x2d, 0x3d, 0x81, 0x59, 0x4, 0x1d } };

// {A6770E91-867A-4c49-92B7-CB4605B1FEDF}
static const GUID guid_radio_tak_asm = { 0xa6770e91, 0x867a, 0x4c49, { 0x92, 0xb7, 0xcb, 0x46, 0x5, 0xb1, 0xfe, 0xdf } };

// {85987D2C-7A54-47fd-BDAA-90D20167AD1E}
static const GUID guid_radio_tak_mmx = { 0x85987d2c, 0x7a54, 0x47fd, { 0xbd, 0xaa, 0x90, 0xd2, 0x1, 0x67, 0xad, 0x1e } };

// {5EE88C9C-758B-42f4-82ED-6BC255B6084D}
static const GUID guid_radio_tak_sse = { 0x5ee88c9c, 0x758b, 0x42f4, { 0x82, 0xed, 0x6b, 0xc2, 0x55, 0xb6, 0x8, 0x4d } };

static advconfig_branch_factory g_branch_decoding_tak("TAK decoder allowed optimizations", guid_branch_decoding_tak, advconfig_branch::guid_branch_decoding, 0.0);

static advconfig_radio_factory g_radio_tak_any("Any (default)", guid_radio_tak_any, guid_branch_decoding_tak, 1.0, true);
static advconfig_radio_factory g_radio_tak_none("None", guid_radio_tak_none, guid_branch_decoding_tak, 2.0, false);
static advconfig_radio_factory g_radio_tak_asm("ASM only", guid_radio_tak_asm, guid_branch_decoding_tak, 3.0, false);
static advconfig_radio_factory g_radio_tak_mmx("MMX only", guid_radio_tak_mmx, guid_branch_decoding_tak, 4.0, false);
static advconfig_radio_factory g_radio_tak_sse("SSE only", guid_radio_tak_sse, guid_branch_decoding_tak, 5.0, false);

////////////////////////////////////////////////////////////////////
// Helper classes

/**
 * Helper class that formats the TAK library version.
 */
class format_tak_library_version
{
	pfc::string_formatter data;
public:
	explicit format_tak_library_version(TtakInt32 p_version)
	{
		data << pfc::format_int((p_version >> 16) & 0xff)
			<< "." << pfc::format_int((p_version >> 8) & 0xff)
			<< "." << pfc::format_int(p_version& 0xff);
	}

	operator char const *() {return data;}
};

/**
 * Generates the "About" text during component initialization.
 * The About text includes version information about the dynamically linked
 * TAK decoder library.
 */
static pfc::string_formatter g_get_component_about()
{
	pfc::string_formatter about;
	about << "Decodes and tags TAK files.";

	TtakInt32 version = 0, compatible_version = 0;
	if (tak_library::get_library_version(version, compatible_version) != tak_res_Ok)
	{
		about << "\n\nCould not retrieve version of TAK decoder library.";
	}
	else
	{
		about << "\n\nBuilt for TAK library version " << format_tak_library_version(tak_InterfaceVersion);
		about << "\nUsing TAK library version " << format_tak_library_version(version)
			<< " (compatible with versions down to " << format_tak_library_version(compatible_version) << ")";
	}

	about << "\n\nCopyright (c) 2007-2008 Holger Stenger\nTAK icon by Florian Trendelenburg (used with permission)";

	return about;
}

/**
 * Helper class to preserve the read position in a file stream.
 * Usage: {restore_file_position_helper helper(p_file, p_abort); <do file operations here>}
 */
class restore_file_position_helper
{
private:
	service_ptr_t<file> m_file;
	abort_callback & m_abort;
	t_filesize m_saved_position;
public:
	restore_file_position_helper(service_ptr_t<file> p_file, abort_callback & p_abort) : m_file (p_file), m_abort(p_abort)
	{
		m_saved_position = m_file->get_position(m_abort);
	}

	~restore_file_position_helper()
	{
		m_file->seek(m_saved_position, m_abort);
	}
};

/**
 * Helpers class that closes and re-opens a TAK decoder instances.
 * Re-opening the TAK decoder is necessary after updating file tags,
 * because we use the tagging routines from foobar2000. The file size may 
 * change without the TAK decoder noticing, which could lead to crashes
 * in tak_deco_lib. Creating a new decoder instances for the updated file
 * avoids this issue.
 */
class reopen_tak_decoder_helper
{
	pfc::ptrholder_t<tak_lib_cpp::tak_seekable_decoder> &m_decoder;
	service_ptr_t<file> m_file;
	abort_callback & m_abort;
public:
	reopen_tak_decoder_helper(pfc::ptrholder_t<tak_lib_cpp::tak_seekable_decoder> &p_decoder, service_ptr_t<file> p_file, abort_callback & p_abort) : m_decoder(p_decoder), m_file(p_file), m_abort(p_abort)
	{
		m_decoder = 0;
	}

	~reopen_tak_decoder_helper()
	{
		if (m_decoder.is_empty())
		{
			m_file->seek(0, m_abort);
			m_decoder = new tak_lib_cpp::tak_seekable_decoder(m_file);
		}
	}
};


/**
 * Computes TAK CPU flags from advanced settings.
 */
static TtakInt32 g_get_cpu_opt_flags()
{
	TtakInt32 flags = tak_Cpu_Any;

	if (g_radio_tak_any.get_static_instance().get_state() == true)
		flags = tak_Cpu_Any;
	else if (g_radio_tak_none.get_static_instance().get_state() == true)
		flags = tak_Cpu_None;
	else if (g_radio_tak_asm.get_static_instance().get_state() == true)
		flags = tak_Cpu_Asm;
	else if (g_radio_tak_mmx.get_static_instance().get_state() == true)
		flags = tak_Cpu_MMX;
	else if (g_radio_tak_sse.get_static_instance().get_state() == true)
		flags = tak_Cpu_SSE;

	return flags;
}

////////////////////////////////////////////////////////////////////
// Input service implementation

// No inheritance. Our methods get called over input framework templates. See input_singletrack_impl for descriptions of what each method does.
class input_tak
{
	pfc::ptrholder_t<tak_lib_cpp::tak_seekable_decoder> m_decoder;

	t_uint32 m_sample_rate;
	t_uint32 m_bits_per_sample;
	t_uint32 m_channel_count;

	pfc::array_t<t_uint8> m_buffer;
	enum {BUFFER_SIZE_IN_SAMPLES = 1024};

public:
	void open(service_ptr_t<file> p_filehint, const char * p_path, t_input_open_reason p_reason, abort_callback & p_abort)
	{
		m_file = p_filehint;
		// p_filehint may be null; this helper opens the file with appropriate privileges if necessary
		input_open_file_helper(m_file, p_path, p_reason, p_abort);

		//file_cached::g_create(m_file, m_file, p_abort, 1 << 20);

		m_decoder = new tak_lib_cpp::tak_seekable_decoder(m_file, g_get_cpu_opt_flags());
	}

	void get_info(file_info & p_info, abort_callback & p_abort)
	{
		try
		{
			restore_file_position_helper helper(m_file, p_abort);

			// Read APEv2 tag.
			static_api_ptr_t<tag_processor_trailing>()->read(m_file, p_info, p_abort);
		}
		catch (exception_service_not_found)
		{
			// tag_processor_trailing service not present.
			console::formatter() << "APEv2 tag processor not present. Please make sure you have foo_input_std installed.";
		}
		catch (exception_tag_not_found)
		{
			// No APEv2 tag present. We can ignore this.
		}

		// Mark as lossless encoding.
		p_info.info_set("encoding", "lossless");
		
		m_decoder->get_info(p_info);

		if (p_info.get_length() > 0)
		try
		{
			t_filestats filestats = get_file_stats(p_abort);
			p_info.info_set_bitrate(t_int64((filestats.m_size * 8 / p_info.get_length() + 500) / 1000));
		}
		catch (exception_io)
		{
			// ignore
		}
	}

	t_filestats get_file_stats(abort_callback & p_abort)
	{
		return m_file->get_stats(p_abort);
	}

	void decode_initialize(unsigned p_flags,abort_callback & p_abort)
	{
		m_decoder->seek(0);

		m_sample_rate = m_decoder->get_sample_rate();
		m_bits_per_sample = m_decoder->get_bits_per_sample();
		m_channel_count = m_decoder->get_channel_count();

		const t_size bytes_per_block = ((m_bits_per_sample + 7) / 8) * m_channel_count;
		m_buffer.set_size(bytes_per_block * BUFFER_SIZE_IN_SAMPLES);
	}

	bool decode_run(audio_chunk & p_chunk, abort_callback & p_abort)
	{
		const t_size bytes_per_block = ((m_bits_per_sample + 7) / 8) * m_channel_count;
		
		t_size sample_count = m_decoder->decode(m_buffer.get_ptr(), BUFFER_SIZE_IN_SAMPLES);

		if (sample_count == 0)
			return false;

		p_chunk.set_data_fixedpoint(
			m_buffer.get_ptr(),
			bytes_per_block * sample_count,
			m_sample_rate,
			m_channel_count,
			m_bits_per_sample,
			audio_chunk::g_guess_channel_config(m_channel_count));

		return true;
	}

	void decode_seek(double p_seconds, abort_callback & p_abort)
	{
		m_file->ensure_seekable();//throw exceptions if someone called decode_seek() despite of our input having reported itself as nonseekable.

		t_uint64 sample = audio_math::time_to_samples(p_seconds, m_decoder->get_sample_rate());

#ifdef TRACE_SEEKING
		console::formatter() << "decode_seek(" << p_seconds << ")";
#endif

		m_decoder->seek(sample);
	}

	bool decode_can_seek()
	{
		// Check whether the underlying file is seekable.
		return m_file->can_seek();
	}

	bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta)
	{
		return false;
	}

	bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {return false;}
	void decode_on_idle(abort_callback & p_abort) {m_file->on_idle(p_abort);}

	void retag(const file_info & p_info, abort_callback & p_abort)
	{
		try
		{
			reopen_tak_decoder_helper(m_decoder, m_file, p_abort);

			// Write APEv2 tag.
			static_api_ptr_t<tag_processor_trailing>()->write_apev2(m_file, p_info, p_abort);
		}
		catch (exception_service_not_found)
		{
			console::formatter() << "APEv2 tag processor not present. Please make sure you have foo_input_std installed.";
		}
	}
	
	static bool g_is_our_content_type(const char * p_content_type) {return false;}
	static bool g_is_our_path(const char * p_path,const char * p_extension)
	{
		return stricmp_utf8(p_extension, "tak") == 0;
	}

public:
	service_ptr_t<file> m_file;
};

// Support for embedded cue sheets is provided by the chosen input factory.
static input_cuesheet_factory_t<input_tak> g_input_tak_factory;

////////////////////////////////////////////////////////////////////
// Embdded album art support (new in foobar2000 0.9.5)
// TAK uses APEv2 tags, so we can re-use the standard routines from
// the foobar2000 SDK.

class album_art_extractor_impl_tak : public album_art_extractor_impl_stdtags
{
public:
	album_art_extractor_impl_tak() : album_art_extractor_impl_stdtags("tak")
	{
	}
};

static service_factory_single_t<album_art_extractor_impl_tak> g_album_art_extractor_factory;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -