#include "stdafx.h" #include "input_helper_cue.h" #include "../SDK/mem_block_container.h" namespace { class input_dec_binary : public input_decoder_v2 { enum { m_rate = 44100, m_bps = 16, m_channels = 2, m_channelMask = audio_chunk::channel_config_stereo, m_sampleBytes = (m_bps/8)*m_channels, m_readAtOnce = 588, m_readAtOnceBytes = m_readAtOnce * m_sampleBytes }; public: input_dec_binary( file::ptr f ) : m_file(f) {} t_uint32 get_subsong_count() override {return 0;} t_uint32 get_subsong(t_uint32 p_index) override {return 0;} void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort) override { p_info.reset(); p_info.info_set_int("samplerate", m_rate); p_info.info_set_int("channels", m_channels); p_info.info_set_int("bitspersample", m_bps); p_info.info_set("encoding","lossless"); p_info.info_set_bitrate((m_bps * m_channels * m_rate + 500 /* rounding for bps to kbps*/ ) / 1000 /* bps to kbps */); p_info.info_set("codec", "PCM"); try { auto stats = get_file_stats(p_abort); if ( stats.m_size != filesize_invalid ) { p_info.set_length( audio_math::samples_to_time( stats.m_size / 4, 44100 ) ); } } catch(exception_io) {} } t_filestats get_file_stats(abort_callback & p_abort) override { return m_file->get_stats(p_abort); } void initialize(t_uint32 p_subsong,unsigned p_flags,abort_callback & p_abort) override { m_file->reopen( p_abort ); } bool run(audio_chunk & p_chunk,abort_callback & p_abort) override { mem_block_container_impl stfu; return run_raw(p_chunk, stfu, p_abort); } bool run_raw(audio_chunk & out, mem_block_container & outRaw, abort_callback & abort) override { size_t bytes = m_readAtOnceBytes; outRaw.set_size( bytes ); size_t got = m_file->read(outRaw.get_ptr(), bytes, abort); got -= got % m_sampleBytes; if ( got == 0 ) return false; if ( got < bytes ) outRaw.set_size( got ); out.set_data_fixedpoint_signed( outRaw.get_ptr(), got, m_rate, m_channels, m_bps, m_channelMask); return true; } void seek(double p_seconds,abort_callback & p_abort) override { m_file->seek( audio_math::time_to_samples( p_seconds, m_rate ) * m_sampleBytes, p_abort ); } bool can_seek() override { return m_file->can_seek(); } bool get_dynamic_info(file_info & p_out, double & p_timestamp_delta) override {return false;} bool get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) override {return false;} void on_idle(abort_callback & p_abort) override {} void set_logger(event_logger::ptr ptr) override {} private: const file::ptr m_file; }; } void input_helper_cue::get_info_binary( const char * path, file_info & out, abort_callback & abort ) { auto f = fileOpenReadExisting( path, abort ); auto obj = fb2k::service_new< input_dec_binary > ( f ); obj->get_info( 0, out, abort ); } void input_helper_cue::open(service_ptr_t p_filehint,const playable_location & p_location,unsigned p_flags,abort_callback & p_abort,double p_start,double p_length, bool binary) { p_abort.check(); m_start = p_start; m_position = 0; m_dynamic_info_trigger = false; m_dynamic_info_track_trigger = false; if ( binary ) { { const char * path = p_location.get_path(); auto f = fileOpenReadExisting( path, p_abort ); auto obj = fb2k::service_new< input_dec_binary > ( f ); m_input.attach( obj, path ); m_input.open_decoding( 0, p_flags, p_abort ); } } else { m_input.open(p_filehint,p_location,p_flags,p_abort,true,true); } if (!m_input.can_seek()) throw exception_io_object_not_seekable(); if (m_start > 0) { m_input.seek(m_start,p_abort); } if (p_length > 0) { m_length = p_length; } else { file_info_impl temp; m_input.get_info(0,temp,p_abort); double ref_length = temp.get_length(); if (ref_length <= 0) throw exception_io_data(); m_length = ref_length - m_start + p_length /* negative or zero */; if (m_length <= 0) throw exception_io_data(); } } void input_helper_cue::close() {m_input.close();} bool input_helper_cue::is_open() {return m_input.is_open();} bool input_helper_cue::_m_input_run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) { if (p_raw == NULL) { return m_input.run(p_chunk, p_abort); } else { return m_input.run_raw(p_chunk, *p_raw, p_abort); } } bool input_helper_cue::_run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) { p_abort.check(); if (m_length > 0) { if (m_position >= m_length) return false; if (!_m_input_run(p_chunk, p_raw, p_abort)) return false; m_dynamic_info_trigger = true; m_dynamic_info_track_trigger = true; t_uint64 max = (t_uint64)audio_math::time_to_samples(m_length - m_position, p_chunk.get_sample_rate()); if (max == 0) {//handle rounding accidents, this normally shouldn't trigger m_position = m_length; return false; } t_size samples = p_chunk.get_sample_count(); if ((t_uint64)samples > max) { p_chunk.set_sample_count((unsigned)max); if (p_raw != NULL) { const t_size rawSize = p_raw->get_size(); PFC_ASSERT(rawSize % samples == 0); p_raw->set_size((t_size)((t_uint64)rawSize * max / samples)); } m_position = m_length; } else { m_position += p_chunk.get_duration(); } return true; } else { if (!_m_input_run(p_chunk, p_raw, p_abort)) return false; m_position += p_chunk.get_duration(); return true; } } bool input_helper_cue::run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) { return _run(p_chunk, &p_raw, p_abort); } bool input_helper_cue::run(audio_chunk & p_chunk, abort_callback & p_abort) { return _run(p_chunk, NULL, p_abort); } void input_helper_cue::seek(double p_seconds, abort_callback & p_abort) { m_dynamic_info_trigger = false; m_dynamic_info_track_trigger = false; if (m_length <= 0 || p_seconds < m_length) { m_input.seek(p_seconds + m_start, p_abort); m_position = p_seconds; } else { m_position = m_length; } } bool input_helper_cue::can_seek() { return true; } void input_helper_cue::on_idle(abort_callback & p_abort) { m_input.on_idle(p_abort); } bool input_helper_cue::get_dynamic_info(file_info & p_out, double & p_timestamp_delta) { if (m_dynamic_info_trigger) { m_dynamic_info_trigger = false; return m_input.get_dynamic_info(p_out, p_timestamp_delta); } else { return false; } } bool input_helper_cue::get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) { if (m_dynamic_info_track_trigger) { m_dynamic_info_track_trigger = false; return m_input.get_dynamic_info_track(p_out, p_timestamp_delta); } else { return false; } } const char * input_helper_cue::get_path() const { return m_input.get_path(); } void input_helper_cue::get_info(t_uint32 p_subsong, file_info & p_info, abort_callback & p_abort) { m_input.get_info(p_subsong, p_info, p_abort); }