00001
00002
00003
00004 namespace file_info_record_helper {
00005
00006 class __file_info_record__info__enumerator {
00007 public:
00008 __file_info_record__info__enumerator(file_info & p_out) : m_out(p_out) {}
00009 void operator() (const char * p_name,const char * p_value) {m_out.__info_add_unsafe(p_name,p_value);}
00010 private:
00011 file_info & m_out;
00012 };
00013
00014 class __file_info_record__meta__enumerator {
00015 public:
00016 __file_info_record__meta__enumerator(file_info & p_out) : m_out(p_out) {}
00017 template<typename t_value> void operator() (const char * p_name,const t_value & p_value) {
00018 t_size index = infinite;
00019 for(typename t_value::const_iterator iter = p_value.first(); iter.is_valid(); ++iter) {
00020 if (index == infinite) index = m_out.__meta_add_unsafe(p_name,*iter);
00021 else m_out.meta_add_value(index,*iter);
00022 }
00023 }
00024 private:
00025 file_info & m_out;
00026 };
00027
00028 class file_info_record {
00029 public:
00030 typedef pfc::chain_list_v2_t<pfc::string8> t_meta_value;
00031 typedef pfc::map_t<pfc::string8,t_meta_value,file_info::field_name_comparator> t_meta_map;
00032 typedef pfc::map_t<pfc::string8,pfc::string8,file_info::field_name_comparator> t_info_map;
00033
00034 file_info_record() : m_replaygain(replaygain_info_invalid), m_length(0) {}
00035
00036 replaygain_info get_replaygain() const {return m_replaygain;}
00037 void set_replaygain(const replaygain_info & p_replaygain) {m_replaygain = p_replaygain;}
00038 double get_length() const {return m_length;}
00039 void set_length(double p_length) {m_length = p_length;}
00040
00041 void reset() {
00042 m_meta.remove_all(); m_info.remove_all();
00043 m_length = 0;
00044 m_replaygain = replaygain_info_invalid;
00045 }
00046
00047 void from_info_overwrite_info(const file_info & p_info) {
00048 for(t_size infowalk = 0, infocount = p_info.info_get_count(); infowalk < infocount; ++infowalk) {
00049 m_info.set(p_info.info_enum_name(infowalk),p_info.info_enum_value(infowalk));
00050 }
00051 }
00052 void from_info_overwrite_meta(const file_info & p_info) {
00053 for(t_size metawalk = 0, metacount = p_info.meta_get_count(); metawalk < metacount; ++metawalk) {
00054 const t_size valuecount = p_info.meta_enum_value_count(metawalk);
00055 if (valuecount > 0) {
00056 t_meta_value & entry = m_meta.find_or_add(p_info.meta_enum_name(metawalk));
00057 entry.remove_all();
00058 for(t_size valuewalk = 0; valuewalk < valuecount; ++valuewalk) {
00059 entry.add_item(p_info.meta_enum_value(metawalk,valuewalk));
00060 }
00061 }
00062 }
00063 }
00064
00065 void from_info_overwrite_rg(const file_info & p_info) {
00066 m_replaygain = replaygain_info::g_merge(m_replaygain,p_info.get_replaygain());
00067 }
00068
00069 template<typename t_source>
00070 void overwrite_meta(const t_source & p_meta) {
00071 m_meta.overwrite(p_meta);
00072 }
00073 template<typename t_source>
00074 void overwrite_info(const t_source & p_info) {
00075 m_info.overwrite(p_info);
00076 }
00077
00078 void merge_overwrite(const file_info & p_info) {
00079 from_info_overwrite_info(p_info);
00080 from_info_overwrite_meta(p_info);
00081 from_info_overwrite_rg(p_info);
00082 }
00083
00084 void transfer_meta_entry(const char * p_name,const file_info & p_info,t_size p_index) {
00085 const t_size count = p_info.meta_enum_value_count(p_index);
00086 if (count == 0) {
00087 m_meta.remove(p_name);
00088 } else {
00089 t_meta_value & val = m_meta.find_or_add(p_name);
00090 val.remove_all();
00091 for(t_size walk = 0; walk < count; ++walk) {
00092 val.add_item(p_info.meta_enum_value(p_index,walk));
00093 }
00094 }
00095 }
00096
00097 void meta_set(const char * p_name,const char * p_value) {
00098 m_meta.find_or_add(p_name).set_single(p_value);
00099 }
00100
00101 const t_meta_value * meta_query_ptr(const char * p_name) const {
00102 return m_meta.query_ptr(p_name);
00103 }
00104
00105
00106 void from_info_set_meta(const file_info & p_info) {
00107 m_meta.remove_all();
00108 from_info_overwrite_meta(p_info);
00109 }
00110
00111 void from_info(const file_info & p_info) {
00112 reset();
00113 m_length = p_info.get_length();
00114 m_replaygain = p_info.get_replaygain();
00115 from_info_overwrite_meta(p_info);
00116 from_info_overwrite_info(p_info);
00117 }
00118 void to_info(file_info & p_info) const {
00119 p_info.reset();
00120 p_info.set_length(m_length);
00121 p_info.set_replaygain(m_replaygain);
00122 m_info.enumerate(__file_info_record__info__enumerator(p_info));
00123 m_meta.enumerate(__file_info_record__meta__enumerator(p_info));
00124 }
00125
00126 template<typename t_callback> void enumerate_meta(t_callback & p_callback) const {m_meta.enumerate(p_callback);}
00127 template<typename t_callback> void enumerate_meta(t_callback & p_callback) {m_meta.enumerate(p_callback);}
00128
00129
00130 t_meta_map m_meta;
00131 t_info_map m_info;
00132 replaygain_info m_replaygain;
00133 double m_length;
00134 };
00135
00136 }
00137
00138
00139 namespace cue_parser
00140 {
00141 struct cue_entry {
00142 pfc::string8 m_file;
00143 unsigned m_track_number;
00144 t_cuesheet_index_list m_indexes;
00145 };
00146
00147 typedef pfc::chain_list_v2_t<cue_entry> t_cue_entry_list;
00148
00149
00150 PFC_DECLARE_EXCEPTION(exception_bad_cuesheet,exception_io_data,"Invalid cuesheet");
00151
00153 void parse(const char *p_cuesheet,t_cue_entry_list & p_out);
00155 void parse_info(const char *p_cuesheet,file_info & p_info,unsigned p_index);
00157 void parse_full(const char * p_cuesheet,cue_creator::t_entry_list & p_out);
00158
00159
00160
00161 struct track_record {
00162 file_info_record_helper::file_info_record m_info;
00163 pfc::string8 m_file,m_flags;
00164 t_cuesheet_index_list m_index_list;
00165 };
00166
00167 typedef pfc::map_t<unsigned,track_record> track_record_list;
00168
00169 class embeddedcue_metadata_manager {
00170 public:
00171 void get_tag(file_info & p_info) const;
00172 void set_tag(file_info const & p_info);
00173
00174 void get_track_info(unsigned p_track,file_info & p_info) const;
00175 void set_track_info(unsigned p_track,file_info const & p_info);
00176 void query_track_offsets(unsigned p_track,double & p_begin,double & p_length) const;
00177 bool have_cuesheet() const;
00178 unsigned remap_trackno(unsigned p_index) const;
00179 t_size get_cue_track_count() const;
00180 private:
00181 track_record_list m_content;
00182 };
00183
00184
00185
00186
00187
00188
00189 class __decoder_wrapper {
00190 public:
00191 virtual bool run(audio_chunk & p_chunk,abort_callback & p_abort) = 0;
00192 virtual void seek(double p_seconds,abort_callback & p_abort) = 0;
00193 virtual bool get_dynamic_info(file_info & p_out, double & p_timestamp_delta) = 0;
00194 virtual bool get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) = 0;
00195 virtual void on_idle(abort_callback & p_abort) = 0;
00196 virtual ~__decoder_wrapper() {}
00197 };
00198
00199 template<typename t_input>
00200 class __decoder_wrapper_simple : public __decoder_wrapper {
00201 public:
00202 void initialize(service_ptr_t<file> p_filehint,const char * p_path,unsigned p_flags,abort_callback & p_abort) {
00203 m_input.open(p_filehint,p_path,input_open_decode,p_abort);
00204 m_input.decode_initialize(p_flags,p_abort);
00205 }
00206 bool run(audio_chunk & p_chunk,abort_callback & p_abort) {return m_input.decode_run(p_chunk,p_abort);}
00207 void seek(double p_seconds,abort_callback & p_abort) {m_input.decode_seek(p_seconds,p_abort);}
00208 bool get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {return m_input.decode_get_dynamic_info(p_out,p_timestamp_delta);}
00209 bool get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {return m_input.decode_get_dynamic_info_track(p_out,p_timestamp_delta);}
00210 void on_idle(abort_callback & p_abort) {m_input.decode_on_idle(p_abort);}
00211 private:
00212 t_input m_input;
00213 };
00214
00215 class __decoder_wrapper_cue : public __decoder_wrapper {
00216 public:
00217 void open(service_ptr_t<file> p_filehint,const playable_location & p_location,unsigned p_flags,abort_callback & p_abort,double p_start,double p_length) {
00218 m_input.open(p_filehint,p_location,p_flags,p_abort,p_start,p_length);
00219 }
00220 bool run(audio_chunk & p_chunk,abort_callback & p_abort) {return m_input.run(p_chunk,p_abort);}
00221 void seek(double p_seconds,abort_callback & p_abort) {m_input.seek(p_seconds,p_abort);}
00222 bool get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {return m_input.get_dynamic_info(p_out,p_timestamp_delta);}
00223 bool get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {return m_input.get_dynamic_info_track(p_out,p_timestamp_delta);}
00224 void on_idle(abort_callback & p_abort) {m_input.on_idle(p_abort);}
00225
00226 private:
00227 input_helper_cue m_input;
00228 };
00229
00230 template<typename t_base>
00231 class input_wrapper_cue_t {
00232 public:
00233 input_wrapper_cue_t() {}
00234 ~input_wrapper_cue_t() {}
00235
00236 void open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
00237 m_path = p_path;
00238
00239 m_file = p_filehint;
00240 input_open_file_helper(m_file,p_path,p_reason,p_abort);
00241
00242 {
00243 file_info_impl info;
00244 t_base instance;
00245 instance.open(m_file,p_path,p_reason,p_abort);
00246 instance.get_info(info,p_abort);
00247 m_meta.set_tag(info);
00248 }
00249 }
00250
00251 t_uint32 get_subsong_count() {
00252 return m_meta.have_cuesheet() ? m_meta.get_cue_track_count() : 1;
00253 }
00254
00255 t_uint32 get_subsong(t_uint32 p_index) {
00256 return m_meta.have_cuesheet() ? m_meta.remap_trackno(p_index) : 0;
00257 }
00258
00259 void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort) {
00260 if (p_subsong == 0) {
00261 m_meta.get_tag(p_info);
00262 } else {
00263 m_meta.get_track_info(p_subsong,p_info);
00264 }
00265 }
00266
00267 t_filestats get_file_stats(abort_callback & p_abort) {return m_file->get_stats(p_abort);}
00268
00269 void decode_initialize(t_uint32 p_subsong,unsigned p_flags,abort_callback & p_abort) {
00270 m_decoder.release();
00271 if (p_subsong == 0) {
00272 pfc::rcptr_t<__decoder_wrapper_simple<t_base> > temp;
00273 temp.new_t();
00274 m_file->reopen(p_abort);
00275 temp->initialize(m_file,m_path,p_flags,p_abort);
00276 m_decoder = temp;
00277 } else {
00278 double start,length;
00279 m_meta.query_track_offsets(p_subsong,start,length);
00280
00281 pfc::rcptr_t<__decoder_wrapper_cue> temp;
00282 temp.new_t();
00283 m_file->reopen(p_abort);
00284 temp->open(m_file,make_playable_location(m_path,0),p_flags & ~input_flag_no_seeking,p_abort,start,length);
00285 m_decoder = temp;
00286 }
00287 }
00288
00289 bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort) {
00290 return m_decoder->run(p_chunk,p_abort);
00291 }
00292
00293 void decode_seek(double p_seconds,abort_callback & p_abort) {
00294 m_decoder->seek(p_seconds,p_abort);
00295 }
00296
00297 bool decode_can_seek() {return true;}
00298
00299 bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {
00300 return m_decoder->get_dynamic_info(p_out,p_timestamp_delta);
00301 }
00302
00303 bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {
00304 return m_decoder->get_dynamic_info_track(p_out,p_timestamp_delta);
00305 }
00306
00307 void decode_on_idle(abort_callback & p_abort) {
00308 m_decoder->on_idle(p_abort);
00309 }
00310
00311 void retag_set_info(t_uint32 p_subsong,const file_info & p_info,abort_callback & p_abort) {
00312 pfc::dynamic_assert(m_decoder.is_empty());
00313 if (p_subsong == 0) {
00314 m_meta.set_tag(p_info);
00315 } else {
00316 m_meta.set_track_info(p_subsong,p_info);
00317 }
00318 }
00319
00320 void retag_commit(abort_callback & p_abort) {
00321 pfc::dynamic_assert(m_decoder.is_empty());
00322
00323 file_info_impl info;
00324 m_meta.get_tag(info);
00325
00326 {
00327 t_base instance;
00328 m_file->reopen(p_abort);
00329 instance.open(m_file,m_path,input_open_info_write,p_abort);
00330 instance.retag(pfc::safe_cast<const file_info&>(info),p_abort);
00331 info.reset();
00332 instance.get_info(info,p_abort);
00333 m_meta.set_tag(info);
00334 }
00335 }
00336
00337 inline static bool g_is_our_content_type(const char * p_content_type) {return t_base::g_is_our_content_type(p_content_type);}
00338 inline static bool g_is_our_path(const char * p_path,const char * p_extension) {return t_base::g_is_our_path(p_path,p_extension);}
00339
00340 private:
00341 pfc::rcptr_t<__decoder_wrapper> m_decoder;
00342
00343 file_info_impl m_info;
00344 pfc::string8 m_path;
00345 service_ptr_t<file> m_file;
00346
00347 embeddedcue_metadata_manager m_meta;
00348 };
00349
00350 template<typename I>
00351 class chapterizer_impl_t : public chapterizer
00352 {
00353 public:
00354 bool is_our_file(const char * p_path,abort_callback & p_abort)
00355 {
00356 return I::g_is_our_path(p_path,pfc::string_extension(p_path));
00357 }
00358
00359 void set_chapters(const char * p_path,chapter_list const & p_list,abort_callback & p_abort) {
00360
00361
00362 input_wrapper_cue_t<I> instance;
00363 instance.open(0,p_path,input_open_info_write,p_abort);
00364
00365
00366 {
00367 file_info_impl info;
00368 instance.get_info(0,info,p_abort);
00369
00370 pfc::string_formatter cuesheet;
00371
00372 {
00373 cue_creator::t_entry_list entries;
00374 t_size n, m = p_list.get_chapter_count();
00375
00376 double offset_acc = 0;
00377 for(n=0;n<m;n++)
00378 {
00379 cue_creator::t_entry_list::iterator entry;
00380 entry = entries.insert_last();
00381 entry->m_infos = p_list.get_info(n);
00382 entry->m_file = "CDImage.wav";
00383 entry->m_track_number = (unsigned)(n+1);
00384 entry->m_index_list.from_infos(entry->m_infos,offset_acc);
00385 offset_acc += entry->m_infos.get_length();
00386 }
00387 cue_creator::create(cuesheet,entries);
00388 }
00389
00390 info.meta_set("cuesheet",cuesheet);
00391
00392 instance.retag_set_info(0,info,p_abort);
00393 }
00394
00395 for(t_size walk = 0, total = p_list.get_chapter_count(); walk < total; ++walk) {
00396 instance.retag_set_info(walk + 1, p_list.get_info(walk),p_abort);
00397 }
00398
00399 instance.retag_commit(p_abort);
00400 }
00401
00402 void get_chapters(const char * p_path,chapter_list & p_list,abort_callback & p_abort) {
00403
00404 input_wrapper_cue_t<I> instance;
00405
00406
00407 instance.open(0,p_path,input_open_info_read,p_abort);
00408
00409 const t_uint32 total = instance.get_subsong_count();
00410
00411 p_list.set_chapter_count(total);
00412 for(t_uint32 walk = 0; walk < total; ++walk) {
00413 file_info_impl info;
00414 instance.get_info(instance.get_subsong(walk),info,p_abort);
00415 p_list.set_info(walk,info);
00416 }
00417 }
00418 };
00419
00420 };
00421
00425 template<typename t_input_impl>
00426 class input_cuesheet_factory_t {
00427 public:
00428 input_factory_t<cue_parser::input_wrapper_cue_t<t_input_impl> > m_input_factory;
00429 service_factory_single_t<cue_parser::chapterizer_impl_t<t_input_impl> > m_chapterizer_factory;
00430 };