cue_parser.h

Go to the documentation of this file.
00001 //HINT: for info on how to generate an embedded cuesheet enabled input, see the end of this header.
00002 
00003 //to be moved somewhere else later
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         //private:
00130                 t_meta_map m_meta;
00131                 t_info_map m_info;
00132                 replaygain_info m_replaygain;
00133                 double m_length;
00134         };
00135 
00136 }//namespace file_info_record_helper
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 bool run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) = 0;
00197                 virtual void set_logger(event_logger::ptr ptr) = 0;
00198                 virtual ~_decoder_wrapper() {}
00199         };
00200 
00201         template<typename t_input>
00202         class _decoder_wrapper_simple : public _decoder_wrapper {
00203         public:
00204                 void initialize(service_ptr_t<file> p_filehint,const char * p_path,unsigned p_flags,abort_callback & p_abort) {
00205                         m_input.open(p_filehint,p_path,input_open_decode,p_abort);
00206                         m_input.decode_initialize(p_flags,p_abort);
00207                 }
00208                 bool run(audio_chunk & p_chunk,abort_callback & p_abort) {return m_input.decode_run(p_chunk,p_abort);}
00209                 void seek(double p_seconds,abort_callback & p_abort) {m_input.decode_seek(p_seconds,p_abort);}
00210                 bool get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {return m_input.decode_get_dynamic_info(p_out,p_timestamp_delta);}
00211                 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);}
00212                 void on_idle(abort_callback & p_abort) {m_input.decode_on_idle(p_abort);}
00213                 bool run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) {return m_input.decode_run_raw(p_chunk, p_raw, p_abort);}
00214                 void set_logger(event_logger::ptr ptr) {m_input.set_logger(ptr);}
00215         private:
00216                 t_input m_input;
00217         };
00218 
00219         class _decoder_wrapper_cue : public _decoder_wrapper {
00220         public:
00221                 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) {
00222                         m_input.open(p_filehint,p_location,p_flags,p_abort,p_start,p_length);
00223                 }
00224                 bool run(audio_chunk & p_chunk,abort_callback & p_abort) {return m_input.run(p_chunk,p_abort);}
00225                 void seek(double p_seconds,abort_callback & p_abort) {m_input.seek(p_seconds,p_abort);}
00226                 bool get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {return m_input.get_dynamic_info(p_out,p_timestamp_delta);}
00227                 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);}
00228                 void on_idle(abort_callback & p_abort) {m_input.on_idle(p_abort);}
00229                 bool run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) {return m_input.run_raw(p_chunk, p_raw, p_abort);}
00230                 void set_logger(event_logger::ptr ptr) {m_input.set_logger(ptr);}
00231         private:
00232                 input_helper_cue m_input;
00233         };
00234 
00235         template<typename t_base>
00236         class input_wrapper_cue_t {
00237         public:
00238                 input_wrapper_cue_t() {}
00239                 ~input_wrapper_cue_t() {}
00240 
00241                 void open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
00242                         m_path = p_path;
00243 
00244                         m_file = p_filehint;
00245                         input_open_file_helper(m_file,p_path,p_reason,p_abort);
00246 
00247                         {
00248                                 file_info_impl info;
00249                                 t_base instance;
00250                                 instance.open(m_file,p_path,p_reason,p_abort);
00251                                 instance.get_info(info,p_abort);
00252                                 m_meta.set_tag(info);
00253                         }
00254                 }
00255 
00256                 t_uint32 get_subsong_count() {
00257                         return m_meta.have_cuesheet() ? m_meta.get_cue_track_count() : 1;
00258                 }
00259 
00260                 t_uint32 get_subsong(t_uint32 p_index) {
00261                         return m_meta.have_cuesheet() ? m_meta.remap_trackno(p_index) : 0;
00262                 }
00263 
00264                 void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort) {
00265                         if (p_subsong == 0) {
00266                                 m_meta.get_tag(p_info);
00267                         } else {
00268                                 m_meta.get_track_info(p_subsong,p_info);
00269                         }
00270                 }
00271 
00272                 t_filestats get_file_stats(abort_callback & p_abort) {return m_file->get_stats(p_abort);}
00273 
00274                 void decode_initialize(t_uint32 p_subsong,unsigned p_flags,abort_callback & p_abort) {
00275                         m_decoder.release();
00276                         if (p_subsong == 0) {
00277                                 pfc::rcptr_t<_decoder_wrapper_simple<t_base> > temp;
00278                                 temp.new_t();
00279                                 m_file->reopen(p_abort);
00280                                 temp->initialize(m_file,m_path,p_flags,p_abort);
00281                                 m_decoder = temp;
00282                         } else {
00283                                 double start,length;
00284                                 m_meta.query_track_offsets(p_subsong,start,length);
00285 
00286                                 pfc::rcptr_t<_decoder_wrapper_cue> temp;
00287                                 temp.new_t();
00288                                 m_file->reopen(p_abort);
00289                                 temp->open(m_file,make_playable_location(m_path,0),p_flags & ~input_flag_no_seeking,p_abort,start,length);
00290                                 m_decoder = temp;
00291                         }
00292                         if (m_logger.is_valid()) m_decoder->set_logger(m_logger);
00293                 }
00294 
00295                 bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort) {
00296                         return m_decoder->run(p_chunk,p_abort);
00297                 }
00298                 
00299                 void decode_seek(double p_seconds,abort_callback & p_abort) {
00300                         m_decoder->seek(p_seconds,p_abort);
00301                 }
00302                 
00303                 bool decode_can_seek() {return true;}
00304                 
00305                 bool decode_run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) {
00306                         return m_decoder->run_raw(p_chunk, p_raw, p_abort);
00307                 }
00308                 void set_logger(event_logger::ptr ptr) {
00309                         m_logger = ptr;
00310                         if (m_decoder.is_valid()) m_decoder->set_logger(ptr);
00311                 }
00312 
00313                 bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {
00314                         return m_decoder->get_dynamic_info(p_out,p_timestamp_delta);
00315                 }
00316 
00317                 bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {
00318                         return m_decoder->get_dynamic_info_track(p_out,p_timestamp_delta);
00319                 }
00320 
00321                 void decode_on_idle(abort_callback & p_abort) {
00322                         m_decoder->on_idle(p_abort);
00323                 }
00324 
00325                 void retag_set_info(t_uint32 p_subsong,const file_info & p_info,abort_callback & p_abort) {
00326                         pfc::dynamic_assert(m_decoder.is_empty());
00327                         if (p_subsong == 0) {
00328                                 m_meta.set_tag(p_info);
00329                         } else {
00330                                 m_meta.set_track_info(p_subsong,p_info);
00331                         }
00332                 }
00333 
00334                 void retag_commit(abort_callback & p_abort) {
00335                         pfc::dynamic_assert(m_decoder.is_empty());
00336 
00337                         file_info_impl info;
00338                         m_meta.get_tag(info);
00339                         
00340                         {
00341                                 t_base instance;
00342                                 m_file->reopen(p_abort);
00343                                 instance.open(m_file,m_path,input_open_info_write,p_abort);
00344                                 instance.retag(pfc::safe_cast<const file_info&>(info),p_abort);
00345                                 info.reset();
00346                                 instance.get_info(info,p_abort);
00347                                 m_meta.set_tag(info);
00348                         }
00349                 }
00350 
00351                 inline static bool g_is_our_content_type(const char * p_content_type) {return t_base::g_is_our_content_type(p_content_type);}
00352                 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);}
00353 
00354         private:
00355                 pfc::rcptr_t<_decoder_wrapper> m_decoder;
00356 
00357                 file_info_impl m_info;
00358                 pfc::string8 m_path;
00359                 service_ptr_t<file> m_file;
00360 
00361                 embeddedcue_metadata_manager m_meta;
00362                 event_logger::ptr m_logger;
00363         };
00364 
00365         template<typename I>
00366         class chapterizer_impl_t : public chapterizer
00367         {
00368         public:
00369                 bool is_our_file(const char * p_path,abort_callback & p_abort) 
00370                 {
00371                         return I::g_is_our_path(p_path,pfc::string_extension(p_path));
00372                 }
00373 
00374                 void set_chapters(const char * p_path,chapter_list const & p_list,abort_callback & p_abort) {
00375                         
00376                         
00377                         input_wrapper_cue_t<I> instance;
00378                         instance.open(0,p_path,input_open_info_write,p_abort);
00379 
00380                         //stamp the cuesheet first
00381                         {
00382                                 file_info_impl info;
00383                                 instance.get_info(0,info,p_abort);
00384 
00385                                 pfc::string_formatter cuesheet;
00386                                                         
00387                                 {
00388                                         cue_creator::t_entry_list entries;
00389                                         t_size n, m = p_list.get_chapter_count();
00390                                                                         
00391                                         double offset_acc = 0;
00392                                         for(n=0;n<m;n++)
00393                                         {
00394                                                 cue_creator::t_entry_list::iterator entry;
00395                                                 entry = entries.insert_last();
00396                                                 entry->m_infos = p_list.get_info(n);
00397                                                 entry->m_file = "CDImage.wav";
00398                                                 entry->m_track_number = (unsigned)(n+1);
00399                                                 entry->m_index_list.from_infos(entry->m_infos,offset_acc);
00400                                                 offset_acc += entry->m_infos.get_length();
00401                                         }
00402                                         cue_creator::create(cuesheet,entries);
00403                                 }
00404 
00405                                 info.meta_set("cuesheet",cuesheet);
00406 
00407                                 instance.retag_set_info(0,info,p_abort);
00408                         }
00409                         //stamp per-chapter infos
00410                         for(t_size walk = 0, total = p_list.get_chapter_count(); walk < total; ++walk) {
00411                                 instance.retag_set_info(walk + 1, p_list.get_info(walk),p_abort);
00412                         }
00413 
00414                         instance.retag_commit(p_abort);
00415                 }
00416 
00417                 void get_chapters(const char * p_path,chapter_list & p_list,abort_callback & p_abort) {
00418 
00419                         input_wrapper_cue_t<I> instance;
00420 
00421 
00422                         instance.open(0,p_path,input_open_info_read,p_abort);
00423 
00424                         const t_uint32 total = instance.get_subsong_count();
00425 
00426                         p_list.set_chapter_count(total);
00427                         for(t_uint32 walk = 0; walk < total; ++walk) {
00428                                 file_info_impl info;
00429                                 instance.get_info(instance.get_subsong(walk),info,p_abort);
00430                                 p_list.set_info(walk,info);
00431                         }
00432                 }
00433         };
00434 
00435 };
00436 
00440 template<typename t_input_impl>
00441 class input_cuesheet_factory_t {
00442 public:
00443         input_factory_ex_t<cue_parser::input_wrapper_cue_t<t_input_impl>,0,input_decoder_v2> m_input_factory;
00444         service_factory_single_t<cue_parser::chapterizer_impl_t<t_input_impl> > m_chapterizer_factory;  
00445 };

Generated on Thu Aug 28 21:15:30 2008 for foobar2000 SDK by  doxygen 1.5.5