Mercurial > foo_out_sdl
diff foosdk/sdk/foobar2000/helpers/input_helpers.cpp @ 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/helpers/input_helpers.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,660 @@ +#include "StdAfx.h" + +#include "input_helpers.h" +#include "fullFileBuffer.h" +#include "file_list_helper.h" +#include "fileReadAhead.h" +#include <SDK/file_info_impl.h> +#include "readers_lite.h" + +#define LOCAL_DEBUG_PRINT(...) // PFC_DEBUG_PRINT(__VA_ARGS__) + +#define FILE_DECODEDAUDIO_PRINT(...) LOCAL_DEBUG_PRINT("file_decodeaudio(", pfc::format_ptr(this), "): ", __VA_ARGS__) + +input_helper::ioFilter_t input_helper::ioFilter_full_buffer(t_filesize val) { + if (val == 0) return nullptr; + return [val] ( file_ptr & f, const char * path, abort_callback & aborter) { + if (!filesystem::g_is_remote_or_unrecognized(path)) { + if (f.is_empty()) filesystem::g_open_read(f, path, aborter); + if (!f->is_in_memory() && !f->is_remote() && f->can_seek() && f->get_size(aborter) <= val) { + try { + f = createFileMemMirrorAsync(f, nullptr, aborter); + return true; + } catch (std::bad_alloc const &) {} // keep orig file object + } + } + return false; + }; +} + +input_helper::ioFilter_t input_helper::ioFilter_block_buffer(size_t arg) { + if (arg == 0) return nullptr; + + return [arg](file_ptr & p_file, const char * p_path, abort_callback & p_abort) { + if (!filesystem::g_is_remote_or_unrecognized(p_path)) { + if (p_file.is_empty()) filesystem::g_open_read(p_file, p_path, p_abort); + if (!p_file->is_in_memory() && !p_file->is_remote() && p_file->can_seek()) { + file_cached::g_create(p_file, p_file, p_abort, (size_t)arg); + return true; + } + } + return false; + }; +} + +static bool looksLikePlaylist(filesystem::ptr const& fs, const char* path) { + auto ext = fs->get_extension(path); + // match against common internet playlist extensions + // yes, this could query fb2k services instead, but uses a fixed list by design + for (auto walk : { "m3u", "pls", "m3u8", "asx" }) { + if (pfc::stringEqualsI_ascii(walk, ext)) return true; + } + return false; +} +static input_helper::ioFilter_t makeReadAhead(size_t arg, bool bRemote) { + if (arg == 0) return nullptr; + + return [arg, bRemote](file_ptr & p_file, const char * p_path, abort_callback & p_abort) { + if (p_file.is_empty()) { + filesystem::ptr fs; + if (!filesystem::g_get_interface(fs, p_path)) return false; + if (bRemote != fs->is_remote(p_path)) return false; + if (looksLikePlaylist(fs, p_path)) return false; + fs->open(p_file, p_path, filesystem::open_mode_read, p_abort); + } else if (bRemote != p_file->is_remote()) return false; + if (p_file->is_in_memory()) return false; + p_file = fileCreateReadAhead(p_file, (size_t)arg, p_abort); + return true; + }; +} + +input_helper::ioFilter_t input_helper::ioFilter_remote_read_ahead(size_t arg) { + return makeReadAhead(arg, true); +} + +input_helper::ioFilter_t input_helper::ioFilter_local_read_ahead(size_t arg) { + return makeReadAhead(arg, false); +} + +void input_helper::open(trackRef location, abort_callback & abort, decodeOpen_t const & other) { + this->open( trackGetLocation(location), abort, other); +} + +void input_helper::open(service_ptr_t<file> p_filehint,trackRef p_location,unsigned p_flags,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) +{ + open(p_filehint,trackGetLocation( p_location ),p_flags,p_abort,p_from_redirect,p_skip_hints); +} + +bool input_helper::test_if_lockless(abort_callback& a) { + if (m_file_in_memory || extended_param(input_params::is_tag_write_safe) != 0) return true; + +#if 0 + // doesn't work with current components because they don't support seeking_expensive either + if (extended_param(input_params::seeking_expensive)) { + // could be that the decoder doesn't understand is_tag_write_safe + if (matchProtocol("file", m_path)) { + try { + fileOpenWriteExisting(m_path, a); + return true; + } catch (exception_io const &) {} + } + } +#endif + return false; +} +void input_helper::fileOpenTools(service_ptr_t<file> & p_file,const char * p_path,input_helper::ioFilters_t const & filters, abort_callback & p_abort) { + for( auto & f : filters ) { + if (f) { + if (f(p_file, p_path, p_abort)) break; + } + } +} + +bool input_helper::need_file_reopen(const char * newPath) const { + return m_input.is_empty() || playable_location::path_compare(m_path, newPath) != 0; +} + +bool input_helper::open_path(const char * path, abort_callback & abort, decodeOpen_t const & other) { + abort.check(); + + m_logger = other.m_logger; + + if (!need_file_reopen(path)) { + if ( other.m_logger.is_valid() ) { + input_decoder_v2::ptr v2; + if (m_input->service_query_t(v2)) v2->set_logger(other.m_logger); + } + return false; + } + m_input.release(); + + service_ptr_t<file> l_file = other.m_hint; + fileOpenTools(l_file, path, other.m_ioFilters, abort); + + m_file_in_memory = l_file.is_valid() && l_file->is_in_memory(); + + TRACK_CODE("input_entry::g_open_for_decoding", + m_input ^= input_entry::g_open(input_decoder::class_guid, l_file, path, m_logger, abort, other.m_from_redirect ); + ); + + if (!other.m_skip_hints) { + try { + if ( other.m_infoHook ) { + other.m_infoHook( m_input, path, abort ); + } +#ifdef FOOBAR2000_HAVE_METADB + metadb_io::get()->hint_reader(m_input.get_ptr(), path, abort); +#endif + } + catch (exception_io_data const &) { + //Don't fail to decode when this barfs, might be barfing when reading info from another subsong than the one we're trying to decode etc. + m_input.release(); + if (l_file.is_valid()) l_file->reopen(abort); + TRACK_CODE("input_entry::g_open_for_decoding", + m_input ^= input_entry::g_open(input_decoder::class_guid, l_file, path, m_logger, abort, other.m_from_redirect); + ); + } + } + + if (other.m_shim) m_input = other.m_shim(m_input, path, abort); + + m_path = path; + return true; +} + +void input_helper::open_decoding(t_uint32 subsong, t_uint32 flags, abort_callback & p_abort) { + TRACK_CODE("input_decoder::initialize", m_input->initialize(subsong, flags, p_abort)); +} + +void input_helper::open(const playable_location & location, abort_callback & abort, decodeOpen_t const & other) { + open_path(location.get_path(), abort, other); + + if (other.m_setSampleRate != 0) { + this->extended_param(input_params::set_preferred_sample_rate, other.m_setSampleRate, nullptr, 0); + } + + open_decoding(location.get_subsong(), other.m_flags, abort); +} + +void input_helper::attach(input_decoder::ptr dec, const char * path) { + m_input = dec; + m_path = path; +} + +void input_helper::open(service_ptr_t<file> p_filehint, const playable_location & p_location, unsigned p_flags, abort_callback & p_abort, bool p_from_redirect, bool p_skip_hints) { + decodeOpen_t o; + o.m_hint = p_filehint; + o.m_flags = p_flags; + o.m_from_redirect = p_from_redirect; + o.m_skip_hints = p_skip_hints; + this->open(p_location, p_abort, o); +} + + +void input_helper::close() { + m_input.release(); +} + +bool input_helper::is_open() { + return m_input.is_valid(); +} + +void input_helper::set_pause(bool state) { + input_decoder_v3::ptr v3; + if (m_input->service_query_t(v3)) v3->set_pause(state); +} +bool input_helper::flush_on_pause() { + input_decoder_v3::ptr v3; + if (m_input->service_query_t(v3)) return v3->flush_on_pause(); + else return false; +} + + +void input_helper::set_logger(event_logger::ptr ptr) { + m_logger = ptr; + input_decoder_v2::ptr v2; + if (m_input->service_query_t(v2)) v2->set_logger(ptr); +} + +bool input_helper::run_raw_v2(audio_chunk& p_chunk, mem_block_container& p_raw, uint32_t knownBPS,abort_callback& p_abort) { + PFC_ASSERT(knownBPS >= 8 && knownBPS <= 32); + input_decoder_v2::ptr v2; + if (v2 &= m_input) { + try { + return v2->run_raw(p_chunk, p_raw, p_abort); + // UGLY: it may STILL FAIL with exception_not_implemented due to some underlying code not being able to handle the request!! + } catch (pfc::exception_not_implemented const &) {} + } + if (!m_input->run(p_chunk, p_abort)) return false; + + uint32_t pad = knownBPS + 7; pad -= pad % 8; + p_chunk.toFixedPoint(p_raw, pad, knownBPS); + return true; +} + +bool input_helper::run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) { + input_decoder_v2::ptr v2; + if (!m_input->service_query_t(v2)) throw pfc::exception_not_implemented(); + return v2->run_raw(p_chunk, p_raw, p_abort); +} + +bool input_helper::run(audio_chunk & p_chunk, abort_callback & p_abort) { + TRACK_CODE("input_decoder::run", return m_input->run(p_chunk, p_abort)); +} + +void input_helper::seek(double seconds, abort_callback & p_abort) { + TRACK_CODE("input_decoder::seek", m_input->seek(seconds, p_abort)); +} + +bool input_helper::can_seek() { + return m_input->can_seek(); +} + +bool input_helper::query_position( double & val ) { + return extended_param(input_params::query_position, 0, &val, sizeof(val) ) != 0; +} + +size_t input_helper::extended_param(const GUID & type, size_t arg1, void * arg2, size_t arg2size) { + input_decoder_v4::ptr v4; + if (v4 &= m_input) { + return v4->extended_param(type, arg1, arg2, arg2size); + } + return 0; +} +input_helper::decodeInfo_t input_helper::decode_info() { + decodeInfo_t ret = {}; + if (m_input.is_valid()) { + ret.m_can_seek = can_seek(); + ret.m_flush_on_pause = flush_on_pause(); + if (ret.m_can_seek) { + ret.m_seeking_expensive = extended_param(input_params::seeking_expensive, 0, nullptr, 0) != 0; + } + } + return ret; +} + +void input_helper::on_idle(abort_callback & p_abort) { + if (m_input.is_valid()) { + TRACK_CODE("input_decoder::on_idle",m_input->on_idle(p_abort)); + } +} + +bool input_helper::get_dynamic_info(file_info & p_out,double & p_timestamp_delta) { + TRACK_CODE("input_decoder::get_dynamic_info",return m_input->get_dynamic_info(p_out,p_timestamp_delta)); +} + +bool input_helper::get_dynamic_info_track(file_info & p_out,double & p_timestamp_delta) { + TRACK_CODE("input_decoder::get_dynamic_info_track",return m_input->get_dynamic_info_track(p_out,p_timestamp_delta)); +} + +void input_helper::get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort) { + TRACK_CODE("input_decoder::get_info",m_input->get_info(p_subsong,p_info,p_abort)); +} + +const char * input_helper::get_path() const { + return m_path; +} + + +input_helper::input_helper() +{ +} + + +void input_helper::g_get_info(const playable_location & p_location,file_info & p_info,abort_callback & p_abort,bool p_from_redirect) { + service_ptr_t<input_info_reader> instance; + input_entry::g_open_for_info_read(instance,0,p_location.get_path(),p_abort,p_from_redirect); + instance->get_info(p_location.get_subsong_index(),p_info,p_abort); +} + +void input_helper::g_set_info(const playable_location & p_location,file_info & p_info,abort_callback & p_abort,bool p_from_redirect) { + service_ptr_t<input_info_writer> instance; + input_entry::g_open_for_info_write(instance,0,p_location.get_path(),p_abort,p_from_redirect); + instance->set_info(p_location.get_subsong_index(),p_info,p_abort); + instance->commit(p_abort); +} + +#ifdef FOOBAR2000_HAVE_METADB +bool dead_item_filter::run(const pfc::list_base_const_t<metadb_handle_ptr> & p_list,bit_array_var & p_mask) { + file_list_helper::file_list_from_metadb_handle_list path_list; + path_list.init_from_list(p_list); + metadb_handle_list valid_handles; + auto l_metadb = metadb::get(); + for(t_size pathidx=0;pathidx<path_list.get_count();pathidx++) + { + if (is_aborting()) return false; + on_progress(pathidx,path_list.get_count()); + + const char * path = path_list[pathidx]; + + if (filesystem::g_is_remote_safe(path)) { + metadb_handle_ptr temp; + l_metadb->handle_create(temp,make_playable_location(path,0)); + valid_handles.add_item(temp); + } else { + try { + service_ptr_t<input_info_reader> reader; + + input_entry::g_open_for_info_read(reader,0,path,*this); + t_uint32 count = reader->get_subsong_count(); + for(t_uint32 n=0;n<count && !is_aborting();n++) { + metadb_handle_ptr ptr; + t_uint32 index = reader->get_subsong(n); + l_metadb->handle_create(ptr,make_playable_location(path,index)); + valid_handles.add_item(ptr); + } + } catch(...) {} + } + } + + if (is_aborting()) return false; + + valid_handles.sort_by_pointer(); + for(t_size listidx=0;listidx<p_list.get_count();listidx++) { + bool dead = valid_handles.bsearch_by_pointer(p_list[listidx]) == ~0; + if (dead) FB2K_console_formatter() << "Dead item: " << p_list[listidx]; + p_mask.set(listidx,dead); + } + return !is_aborting(); +} + +namespace { + +class dead_item_filter_impl_simple : public dead_item_filter +{ +public: + inline dead_item_filter_impl_simple(abort_callback & p_abort) : m_abort(p_abort) {} + bool is_aborting() const {return m_abort.is_aborting();} + abort_callback_event get_abort_event() const {return m_abort.get_abort_event();} + void on_progress(t_size p_position,t_size p_total) {} +private: + abort_callback & m_abort; +}; + +} + +bool input_helper::g_mark_dead(const pfc::list_base_const_t<metadb_handle_ptr> & p_list,bit_array_var & p_mask,abort_callback & p_abort) +{ + dead_item_filter_impl_simple filter(p_abort); + return filter.run(p_list,p_mask); +} + +#endif // #ifdef FOOBAR2000_HAVE_METADB + +void input_info_read_helper::open(const char * p_path,abort_callback & p_abort) { + if (m_input.is_empty() || playable_location::path_compare(m_path,p_path) != 0) + { + TRACK_CODE("input_entry::g_open_for_info_read",input_entry::g_open_for_info_read(m_input,0,p_path,p_abort)); + + m_path = p_path; + } +} + +void input_info_read_helper::get_info(const playable_location & p_location,file_info & p_info,t_filestats & p_stats,abort_callback & p_abort) { + open(p_location.get_path(),p_abort); + + TRACK_CODE("input_info_reader::get_file_stats",p_stats = m_input->get_file_stats(p_abort)); + + TRACK_CODE("input_info_reader::get_info",m_input->get_info(p_location.get_subsong_index(),p_info,p_abort)); +} + +#ifdef FOOBAR2000_HAVE_METADB +void input_info_read_helper::get_info_check(const playable_location & p_location,file_info & p_info,t_filestats & p_stats,bool & p_reloaded,abort_callback & p_abort) { + open(p_location.get_path(),p_abort); + t_filestats newstats; + TRACK_CODE("input_info_reader::get_file_stats",newstats = m_input->get_file_stats(p_abort)); + if (metadb_handle::g_should_reload(p_stats,newstats,true)) { + p_stats = newstats; + TRACK_CODE("input_info_reader::get_info",m_input->get_info(p_location.get_subsong_index(),p_info,p_abort)); + p_reloaded = true; + } else { + p_reloaded = false; + } +} +#endif // #ifdef FOOBAR2000_HAVE_METADB + +// openAudioData code + +static constexpr openAudioDataFormat formatNative = (sizeof(audio_sample) == sizeof(double)) ? openAudioDataFormat::float64 : openAudioDataFormat::float32; + +namespace { + + class file_decodedaudio : public file_readonly { + public: + void init(const playable_location & loc, input_helper::decodeOpen_t const & arg, openAudioDataFormat format, abort_callback & aborter) { + FILE_DECODEDAUDIO_PRINT("opening: ", loc ); + m_length = -1; m_lengthKnown = false; + m_format = format; + m_subsong = loc.get_subsong(); + m_decoder ^= input_entry::g_open( input_decoder::class_guid, arg.m_hint, loc.get_path(), arg.m_logger, aborter, arg.m_from_redirect); + m_seekable = ( arg.m_flags & input_flag_no_seeking ) == 0 && m_decoder->can_seek(); + m_flags = arg.m_flags; + reopenDecoder(aborter); + readChunk(aborter, true); + } + void init(const playable_location & loc, bool bSeekable, file::ptr fileHint, abort_callback & aborter) { + FILE_DECODEDAUDIO_PRINT("opening: ", loc); + m_length = -1; m_lengthKnown = false; + m_subsong = loc.get_subsong(); + input_entry::g_open_for_decoding(m_decoder, fileHint, loc.get_path(), aborter); + m_seekable = bSeekable && m_decoder->can_seek(); + reopenDecoder(aborter); + readChunk(aborter, true); + } + + void reopen(abort_callback & aborter) override { + + // Have valid chunk and it is the first chunk in the stream? Reset read pointers and bail, no need to reopen + if (m_chunk.get_sample_count() > 0 && m_chunkBytesPtr == m_currentPosition) { + m_currentPosition = 0; + m_chunkBytesPtr = 0; + m_eof = false; + return; + } + + reopenDecoder(aborter); + readChunk(aborter); + } + + bool is_remote() override { + return false; + } + t_filetimestamp get_timestamp(abort_callback & p_abort) override { + return m_decoder->get_file_stats(p_abort).m_timestamp; + } + void on_idle(abort_callback & p_abort) override { + m_decoder->on_idle(p_abort); + } + bool get_content_type(pfc::string_base & p_out) override { + return false; + } + bool can_seek() override { + return m_seekable; + } + void seek(t_filesize p_position, abort_callback & p_abort) override { + if (!m_seekable) throw exception_io_object_not_seekable(); + + + { + t_filesize chunkBegin = m_currentPosition - m_chunkBytesPtr; + t_filesize chunkEnd = chunkBegin + curChunkBytes(); + if (p_position >= chunkBegin && p_position < chunkEnd) { + m_chunkBytesPtr = (size_t)(p_position - chunkBegin); + m_currentPosition = p_position; + m_eof = false; + return; + } + } + + { + t_filesize s = get_size(p_abort); + if (s != filesize_invalid) { + if (p_position > s) throw exception_io_seek_out_of_range(); + if (p_position == s) { + m_chunk.reset(); m_curChunkBytes = 0; m_chunkBytesPtr = 0; + m_currentPosition = p_position; + m_eof = true; + return; + } + } + } + + + + const auto row = m_spec.chanCount * sampleBytes(); + t_filesize samples = p_position / row; + const double seekTime = audio_math::samples_to_time(samples, m_spec.sampleRate); + m_decoder->seek(seekTime, p_abort); + m_eof = false; // do this before readChunk + if (!this->readChunk(p_abort)) { + throw std::runtime_error( ( PFC_string_formatter() << "Premature EOF in referenced audio file at " << pfc::format_time_ex(seekTime, 6) << " out of " << pfc::format_time_ex(length(p_abort), 6) ).get_ptr() ); + } + m_chunkBytesPtr = p_position % row; + if (m_chunkBytesPtr > curChunkBytes()) { + // Should not ever happen + m_chunkBytesPtr = 0; + throw std::runtime_error("Decoder returned invalid data"); + } + + m_currentPosition = p_position; + m_eof = false; + } + + t_filesize get_size(abort_callback & aborter) override { + const double l = length(aborter); + if (l <= 0) return filesize_invalid; + return audio_math::time_to_samples(l, m_spec.sampleRate) * m_spec.chanCount * sampleBytes(); + } + t_filesize get_position(abort_callback & p_abort) override { + return m_currentPosition; + } + t_size read(void * p_buffer, t_size p_bytes, abort_callback & p_abort) override { + FILE_DECODEDAUDIO_PRINT("read(", p_bytes, ")"); + size_t done = 0; + for (;;) { + p_abort.check(); + { + const size_t inChunk = curChunkBytes(); + const size_t inChunkRemaining = inChunk - m_chunkBytesPtr; + const size_t delta = pfc::min_t<size_t>(inChunkRemaining, (p_bytes - done)); + memcpy((uint8_t*)p_buffer + done, (const uint8_t*)m_curChunkPtr + m_chunkBytesPtr, delta); + m_chunkBytesPtr += delta; + done += delta; + m_currentPosition += delta; + + if (done == p_bytes) break; + } + if (!readChunk(p_abort)) break; + } + FILE_DECODEDAUDIO_PRINT("read(", p_bytes, ") returning ", done); + return done; + } + audio_chunk::spec_t const & get_spec() const { return m_spec; } + private: + void reopenDecoder(abort_callback & aborter) { + uint32_t flags = m_flags | input_flag_no_looping; + if (!m_seekable) flags |= input_flag_no_seeking; + + m_decoder->initialize(m_subsong, flags, aborter); + m_eof = false; + m_currentPosition = 0; + } + size_t curChunkBytes() const { + return m_curChunkBytes; + } + bool readChunk(abort_callback & aborter, bool initial = false) { + m_chunkBytesPtr = 0; + FILE_DECODEDAUDIO_PRINT("readChunk()"); + for (;;) { + if (m_eof || !m_decoder->run(m_chunk, aborter)) { + if (initial) throw std::runtime_error("Decoder produced no data"); + m_eof = true; + m_chunk.reset(); m_curChunkBytes = 0; + FILE_DECODEDAUDIO_PRINT("readChunk() EOF"); + return false; + } + if (m_chunk.is_valid()) break; + } + PFC_ASSERT(m_chunk.get_peak() < 100); + audio_chunk::spec_t spec = m_chunk.get_spec(); + if (initial) m_spec = spec; + else if (m_spec != spec) throw std::runtime_error("Sample format change in mid stream"); + + auto inPtr = m_chunk.get_data(); + const size_t inCount = m_chunk.get_used_size(); + m_curChunkBytes = inCount * sampleBytes(); + FILE_DECODEDAUDIO_PRINT("readChunk(): ", m_chunk.get_sample_count(), " samples, ", m_curChunkBytes, " bytes"); + if ( m_format == formatNative ) { + m_curChunkPtr = inPtr; + } else { + m_altFormatBuffer.grow_size(m_curChunkBytes); + m_curChunkPtr = m_altFormatBuffer.get_ptr(); + switch (m_format) { + case openAudioDataFormat::float32: + { auto out = reinterpret_cast<float*>(m_curChunkPtr); for (size_t walk = 0; walk < inCount; ++walk) out[walk] = (float)inPtr[walk]; } + break; + case openAudioDataFormat::float64: + { auto out = reinterpret_cast<double*>(m_curChunkPtr); for (size_t walk = 0; walk < inCount; ++walk) out[walk] = (double)inPtr[walk]; } + break; + default: + PFC_ASSERT(!"???"); + } + } + + return true; + } + double length(abort_callback & aborter) { + if (!m_lengthKnown) { + file_info_impl temp; + m_decoder->get_info(m_subsong, temp, aborter); + m_length = temp.get_length(); + m_lengthKnown = true; + } + return m_length; + } + unsigned sampleBytes() const { + return m_format == openAudioDataFormat::float32 ? 4 : 8; + } + audio_chunk_fast_impl m_chunk; + size_t m_chunkBytesPtr; + audio_chunk::spec_t m_spec; + double m_length; + bool m_lengthKnown; + bool m_seekable; + uint32_t m_subsong; + input_decoder::ptr m_decoder; + bool m_eof; + t_filesize m_currentPosition; + unsigned m_flags = 0; + openAudioDataFormat m_format = formatNative; + + pfc::array_t<uint8_t> m_altFormatBuffer; + void* m_curChunkPtr = nullptr; + size_t m_curChunkBytes = 0; + }; + +} + +openAudioData_t openAudioData3(playable_location const& loc, input_helper::decodeOpen_t const& openArg, openAudioDataFormat format, abort_callback& aborter) { + service_ptr_t<file_decodedaudio> f; f = new service_impl_t < file_decodedaudio >; + f->init(loc, openArg, format, aborter); + + openAudioData_t oad = {}; + oad.audioData = f; + oad.audioSpec = f->get_spec(); + return oad; +} + +openAudioData_t openAudioData2(playable_location const & loc, input_helper::decodeOpen_t const & openArg, abort_callback & aborter) { + return openAudioData3(loc, openArg, formatNative, aborter); +} + +openAudioData_t openAudioData(playable_location const & loc, bool bSeekable, file::ptr fileHint, abort_callback & aborter) { + service_ptr_t<file_decodedaudio> f; f = new service_impl_t < file_decodedaudio > ; + f->init(loc, bSeekable, fileHint, aborter); + + openAudioData_t oad = {}; + oad.audioData = f; + oad.audioSpec = f->get_spec(); + return oad; +}
