📄 foo_input_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 + -