root/platform/foobar2000/trunk/foo_loop/looping.h @ 38740

Revision 38740, 42.3 kB (checked in by topia, 2 years ago)

fix duplicate event processing.

Line 
1namespace loop_helper {
2        extern advconfig_checkbox_factory cfg_loop_debug;
3        extern advconfig_checkbox_factory cfg_loop_disable;
4
5        class console_looping_formatter : public console::formatter {
6        public:
7                console_looping_formatter() {
8                        *this << "Looping: ";
9                }
10        };
11
12        class console_looping_debug_formatter : public console::formatter {
13        public:
14                console_looping_debug_formatter() {
15                        *this << "Looping: DBG: ";
16                }
17        };
18
19        void console_complain_looping(const char * what, const char * msg);
20
21        //! format time and sample
22        class format_samples_ex {
23        private:
24                pfc::string_fixed_t<193> m_buffer;
25        public:
26                format_samples_ex(t_uint64 p_samples,t_uint32 p_sample_rate,unsigned p_extra = 3) {
27                        m_buffer << pfc::format_time_ex(audio_math::samples_to_time(p_samples,p_sample_rate),p_extra);
28                        m_buffer << " (" << pfc::format_int(p_samples) << ")";
29                };
30                const char * get_ptr() const {return m_buffer;}
31                operator const char * () const {return m_buffer;}
32        };
33
34        void open_path_helper(input_decoder::ptr & p_input, file::ptr p_file, const char * path,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints);
35        bool parse_entity(const char * & ptr,pfc::string8 & name,pfc::string8 & value);
36        enum {
37                merge_filestats_sum = 1,
38                merge_filestats_max = 2,
39        };
40        t_filestats merge_filestats(const t_filestats & p_src1, const t_filestats & p_src2, int p_merge_type);
41
42        void inline combine_audio_chunks(audio_chunk & p_first,const audio_chunk & p_second) {
43                if (p_first.is_empty()) {
44                        p_first = p_second;
45                        return;
46                }
47
48                // sanity check
49                if (p_first.get_sample_rate() != p_second.get_sample_rate() ||
50                        p_first.get_channel_config() != p_second.get_channel_config() ||
51                        p_first.get_channels() != p_second.get_channels()) {
52                        throw exception_unexpected_audio_format_change();
53                }
54                int nch = p_first.get_channels();
55                t_size first_samples = p_first.get_sample_count();
56                t_size offset = first_samples * nch;
57                t_size second_samples = p_second.get_sample_count();
58                t_size size = second_samples * nch;
59                p_first.set_data_size(offset + size);
60                pfc::memcpy_t(p_first.get_data()+offset,p_second.get_data(),size);
61                p_first.set_sample_count(first_samples + second_samples);
62        }
63
64        void inline combine_audio_chunks(audio_chunk & p_first,mem_block_container * p_raw_first,const audio_chunk & p_second,const mem_block_container * p_raw_second) {
65                if (!p_raw_first != !p_raw_second)
66                        throw exception_unexpected_audio_format_change();
67
68                combine_audio_chunks(p_first, p_second);
69
70                if (p_raw_first == NULL) return;
71
72                t_size offset = p_raw_first->get_size();
73                t_size size = p_raw_second->get_size();
74                // combine mem_block
75                p_raw_first->set_size(offset + size);
76                memcpy(((char*)p_raw_first->get_ptr())+offset,p_raw_second->get_ptr(),size);
77        }
78
79        void inline truncate_chunk(audio_chunk & p_chunk, mem_block_container * p_raw, t_size p_samples) {
80                if (p_raw != NULL) p_raw->set_size(MulDiv_Size(p_raw->get_size(), p_samples, p_chunk.get_sample_count()));
81                p_chunk.set_sample_count(p_samples);
82                p_chunk.set_data_size(p_samples * p_chunk.get_channel_count());
83        }
84
85
86        class NOVTABLE loop_type_base : public service_base {
87        protected:
88                virtual input_decoder::ptr & get_input() = 0;
89                virtual input_decoder_v2::ptr & get_input_v2() = 0;
90                virtual void set_succ(bool val) = 0;
91                virtual bool get_succ() const = 0;
92                virtual void set_cur(t_uint64 val) = 0;
93                virtual void add_cur(t_uint64 add) = 0;
94        public:
95                virtual t_uint64 get_cur() const = 0;
96                virtual t_uint32 get_sample_rate() const = 0;
97                virtual bool get_no_looping() const = 0;
98
99                virtual void raw_seek(t_uint64 samples, abort_callback & p_abort) {
100                        set_succ(true);
101                        get_input()->seek(audio_math::samples_to_time(samples, get_sample_rate()), p_abort);
102                        set_cur(samples);
103                }
104
105                virtual void raw_seek(double seconds, abort_callback & p_abort) {
106                        set_succ(true);
107                        get_input()->seek(seconds, p_abort);
108                        set_cur(audio_math::time_to_samples(seconds, get_sample_rate()));
109                }
110
111                inline void set_eof() {
112                        set_succ(false);
113                }
114
115                inline void get_one_chunk(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
116                        if (!get_succ()) return; // already EOF
117                        if (p_raw != NULL) {
118                                set_succ(get_input_v2()->run_raw(p_chunk,*p_raw,p_abort));
119                        } else {
120                                set_succ(get_input()->run(p_chunk,p_abort));
121                        }
122                        add_cur(p_chunk.get_sample_count());
123                }
124
125                virtual t_size get_more_chunk(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort, t_size candidate) {
126                        t_size samples = 0;
127                        audio_chunk_impl_temporary ptmp_chunk;
128                        mem_block_container_impl tmp_block;
129                        mem_block_container_impl * ptmp_block = NULL;
130                        if (p_raw != NULL) ptmp_block = &tmp_block;
131                        while (get_succ() && candidate > samples) {
132                                bool succ;
133                                if (p_raw != NULL) {
134                                        succ = get_input_v2()->run_raw(ptmp_chunk,tmp_block,p_abort);
135                                } else {
136                                        succ = get_input()->run(ptmp_chunk,p_abort);
137                                }
138                                set_succ(succ);
139                                if (succ) {
140                                        t_size newsamples = ptmp_chunk.get_sample_count();
141                                        if (newsamples)
142                                                combine_audio_chunks(p_chunk, p_raw, ptmp_chunk, ptmp_block);
143                                        samples += newsamples;
144                                        add_cur(newsamples);
145                                }
146                        }
147                        return samples;
148                }
149
150                static const GUID guid_cfg_branch_loop;
151                FB2K_MAKE_SERVICE_INTERFACE(loop_type_base, service_base);
152        };
153
154        PFC_DECLARE_EXCEPTION(exception_loop_bad_point,pfc::exception,"Bad Input Loop Point Error")
155
156        class loop_event_point : public service_base {
157        public:
158                //! Get position of this event occured.
159                virtual t_uint64 get_position() const = 0;
160
161                //! Get position of this event occured with audio chunk includes specified point.
162                virtual t_uint64 get_prepare_position() const = 0;
163
164                virtual void check() const = 0;
165
166                //! Get information of this event.
167                virtual void get_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) = 0;
168
169                virtual bool has_dynamic_info() const = 0;
170                virtual bool set_dynamic_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) = 0;
171                virtual bool reset_dynamic_info(file_info & p_info, const char * p_prefix) = 0;
172
173                virtual bool has_dynamic_track_info() const = 0;
174                virtual bool set_dynamic_track_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) = 0;
175                virtual bool reset_dynamic_track_info(file_info & p_info, const char * p_prefix) = 0;
176
177                //! process this event with specified chunk. return true after seek or switch input, otherwise false.
178                virtual bool process(loop_type_base::ptr p_input, t_uint64 p_start, audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) = 0;
179                //! process this event. return true after seek or switch input, otherwise false.
180                virtual bool process(loop_type_base::ptr p_input, abort_callback & p_abort) = 0;
181
182                FB2K_MAKE_SERVICE_INTERFACE(loop_event_point, service_base);
183        };
184
185        template<typename t_item1, typename t_item2>
186        inline int loop_event_compare(const t_item1 & p_item1, const t_item2 & p_item2);
187
188        template<>
189        inline int loop_event_compare(const loop_event_point & p_item1, const loop_event_point & p_item2) {
190                return pfc::compare_t(p_item1.get_position(), p_item2.get_position());
191        }
192
193        template<>
194        inline int loop_event_compare(const loop_event_point::ptr & p_item1, const loop_event_point::ptr & p_item2) {
195                return pfc::compare_t(p_item1.is_valid() ? p_item1->get_position() : (t_uint64)-1, p_item2.is_valid() ? p_item2->get_position() : (t_uint64)-1);
196        }
197
198        template<>
199        inline int loop_event_compare(const t_uint64 & p_item1, const loop_event_point::ptr & p_item2) {
200                return pfc::compare_t(p_item1, p_item2.is_valid() ? p_item2->get_position() : (t_uint64)-1);
201        }
202
203        template<>
204        inline int loop_event_compare(const loop_event_point::ptr & p_item1, const t_uint64 & p_item2) {
205                return pfc::compare_t(p_item1.is_valid() ? p_item1->get_position() : (t_uint64)-1, p_item2);
206        }
207
208        template<typename t_item1, typename t_item2>
209        inline int loop_event_prepos_compare(const t_item1 & p_item1, const t_item2 & p_item2);
210
211        template<>
212        inline int loop_event_prepos_compare(const loop_event_point & p_item1, const loop_event_point & p_item2) {
213                return pfc::compare_t(p_item1.get_prepare_position(), p_item2.get_prepare_position());
214        }
215
216        template<>
217        inline int loop_event_prepos_compare(const loop_event_point::ptr & p_item1, const loop_event_point::ptr & p_item2) {
218                return pfc::compare_t(p_item1.is_valid() ? p_item1->get_prepare_position() : (t_uint64)-1, p_item2.is_valid() ? p_item2->get_prepare_position() : (t_uint64)-1);
219        }
220
221        template<>
222        inline int loop_event_prepos_compare(const t_uint64 & p_item1, const loop_event_point::ptr & p_item2) {
223                return pfc::compare_t(p_item1, p_item2.is_valid() ? p_item2->get_prepare_position() : (t_uint64)-1);
224        }
225
226        template<>
227        inline int loop_event_prepos_compare(const loop_event_point::ptr & p_item1, const t_uint64 & p_item2) {
228                return pfc::compare_t(p_item1.is_valid() ? p_item1->get_prepare_position() : (t_uint64)-1, p_item2);
229        }
230
231        typedef pfc::list_t<loop_event_point::ptr, pfc::alloc_fast> loop_event_point_list;
232
233        class NOVTABLE loop_type : public loop_type_base {
234        protected:
235                virtual bool is_raw_supported() const = 0;
236                virtual const char * get_info_prefix() const = 0;
237
238        public:
239                virtual bool parse(const char * ptr) = 0;
240                //! process specified event with audio chunk and return true after seek or switch input, otherwise false.
241                virtual bool process_event(loop_event_point::ptr point, t_uint64 start, audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) = 0;
242                //! process specified event and return true after seek or switch input, otherwise false.
243                virtual bool process_event(loop_event_point::ptr point, abort_callback & p_abort) = 0;
244                virtual bool open_path(file::ptr p_filehint,const char * path,t_input_open_reason p_reason,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) = 0;
245                virtual t_uint32 get_subsong_count() = 0;
246                virtual t_uint32 get_subsong(t_uint32 p_index) = 0;
247                virtual void get_info(t_uint32 subsong, file_info & p_info,abort_callback & p_abort) = 0;
248                virtual t_filestats get_file_stats(abort_callback & p_abort) = 0;
249                virtual void open_decoding(t_uint32 subsong, t_uint32 flags, abort_callback & p_abort) = 0;
250                virtual void seek(t_uint64 samples, abort_callback & p_abort) = 0;
251                virtual void seek(double seconds, abort_callback & p_abort) = 0;
252                virtual bool run(audio_chunk & p_chunk,abort_callback & p_abort) = 0;
253                virtual bool run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) = 0;
254                virtual void set_info_prefix(const char * p_prefix) = 0;
255
256                virtual void close() = 0;
257                inline bool can_seek() {return get_input()->can_seek();}
258                virtual void on_idle(abort_callback & p_abort) = 0;
259                virtual bool get_dynamic_info(file_info & p_out,double & p_timestamp_delta) = 0;
260                virtual bool get_dynamic_info_track(file_info & p_out,double & p_timestamp_delta) = 0;
261                virtual void set_logger(event_logger::ptr ptr) = 0;
262
263                FB2K_MAKE_SERVICE_INTERFACE(loop_type, loop_type_base);
264        };
265
266        class loop_event_point_baseimpl : public loop_event_point {
267        public:
268                // default: on looping only
269                loop_event_point_baseimpl() : flags(on_looping) {}
270                loop_event_point_baseimpl(unsigned p_flags) : flags(p_flags) {}
271                enum {
272                        on_looping    = 1 << 0,
273                        on_no_looping = 1 << 1,
274                };
275                unsigned flags;
276                bool check_no_looping(loop_type_base::ptr p_input) const {
277                        if (p_input->get_no_looping()) {
278                                // no_looping
279                                return !(flags & on_no_looping);
280                        } else {
281                                // looping
282                                return !(flags & on_looping);
283                        }
284                }
285                virtual t_uint64 get_prepare_position() const {return (t_uint64)-1;}
286
287                virtual void get_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) {}
288
289                virtual bool has_dynamic_info() const {return false;}
290                virtual bool set_dynamic_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) {return false;}
291                virtual bool reset_dynamic_info(file_info & p_info, const char * p_prefix) {return false;}
292
293                virtual bool has_dynamic_track_info() const {return false;}
294                virtual bool set_dynamic_track_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) {return false;}
295                virtual bool reset_dynamic_track_info(file_info & p_info, const char * p_prefix) {return false;}
296        };
297
298        class loop_event_point_simple : public loop_event_point_baseimpl {
299        public:
300                // this event process on looping only, default
301                loop_event_point_simple() : from(0), to(0), maxrepeats(0), repeats(0), loop_event_point_baseimpl(on_looping) {}
302                t_uint64 from, to;
303                t_size maxrepeats, repeats;
304                virtual t_uint64 get_position() const {return from;}
305                virtual void get_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) {
306                        pfc::string8 name;
307                        t_size prefixlen;
308                        name << p_prefix;
309                        prefixlen = name.get_length();
310
311                        name.truncate(prefixlen);
312                        name << "from";
313                        p_info.info_set(name, format_samples_ex(from, sample_rate));
314
315                        name.truncate(prefixlen);
316                        name << "to";
317                        p_info.info_set(name, format_samples_ex(to, sample_rate));
318
319                        if (maxrepeats) {
320                                name.truncate(prefixlen);
321                                name << "maxrepeats";
322                                p_info.info_set_int(name, maxrepeats);
323                        }
324                }
325                virtual bool has_dynamic_info() const {return true;}
326                virtual bool set_dynamic_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) {
327                        pfc::string8 name;
328                        name << p_prefix << "repeats";
329                        p_info.info_set_int(name, repeats);
330                        return true;
331                }
332                virtual bool reset_dynamic_info(file_info & p_info, const char * p_prefix) {
333                        pfc::string8 name;
334                        name << p_prefix << "repeats";
335                        return p_info.info_remove(name);
336                }
337
338                virtual void check() const {
339                        if (from == to) throw exception_loop_bad_point();
340                }
341
342                virtual bool process(loop_type_base::ptr p_input, t_uint64 p_start, audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
343                        if (check_no_looping(p_input)) return false;
344                        ++repeats;
345                        if (maxrepeats && repeats>=maxrepeats) return false;
346                        t_size newsamples = pfc::downcast_guarded<t_size>(from - p_start);
347                        truncate_chunk(p_chunk,p_raw,newsamples);
348                        p_input->raw_seek(to, p_abort);
349                        return true;
350                }
351                virtual bool process(loop_type_base::ptr p_input, abort_callback & p_abort) {
352                        if (check_no_looping(p_input)) return false;
353                        ++repeats;
354                        if (maxrepeats && repeats>=maxrepeats) return false;
355                        p_input->raw_seek(to, p_abort);
356                        return true;
357                }
358        };
359
360        class loop_event_point_end : public loop_event_point_baseimpl {
361        public:
362                // this event process on no_looping only, default
363                loop_event_point_end() : position(0), loop_event_point_baseimpl(on_no_looping) {}
364                t_uint64 position;
365                virtual t_uint64 get_position() const {return position;}
366                virtual void get_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) {
367                        pfc::string8 name;
368                        name << p_prefix << "position";
369                        p_info.info_set(name, format_samples_ex(position, sample_rate));
370                }
371
372                virtual void check() const {}
373
374                virtual bool process(loop_type_base::ptr p_input, t_uint64 p_start, audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
375                        if (check_no_looping(p_input)) return false;
376                        t_size newsamples = pfc::downcast_guarded<t_size>(position - p_start);
377                        truncate_chunk(p_chunk,p_raw,newsamples);
378                        p_input->set_eof(); // to eof
379                        return true;
380                }
381
382                virtual bool process(loop_type_base::ptr p_input, abort_callback & p_abort) {
383                        if (check_no_looping(p_input)) return false;
384                        p_input->set_eof(); // to_eof
385                        return true;
386                }
387        };
388
389        class loop_type_impl_base : public loop_type {
390        private:
391                t_uint32 m_sample_rate;
392                t_uint64 m_cur;
393                bool m_no_looping;
394                bool m_succ;
395                bool m_raw_support;
396                pfc::string8 m_info_prefix;
397                loop_event_point_list m_cur_points;
398                pfc::list_permutation_t<loop_event_point::ptr> * m_cur_points_by_pos, * m_cur_points_by_prepos;
399                pfc::array_t<t_size> m_perm_by_pos, m_perm_by_prepos;
400                input_decoder::ptr m_current_input;
401                input_decoder_v2::ptr m_current_input_v2;
402                bool m_current_changed;
403                pfc::string8 m_current_path, m_current_fileext;
404                t_uint64 m_nextpointpos;
405                class dynamic_update_tracker {
406                public:
407                        t_uint64 lastupdate;
408                        t_uint64 updateperiod;
409                        loop_event_point_list m_old_points;
410                        bool m_input_switched;
411
412                        dynamic_update_tracker() : lastupdate(0), updateperiod(0), m_input_switched(false) {}
413                        inline bool check(t_uint64 cur) const {
414                                return lastupdate >= cur || (lastupdate + updateperiod) < cur;
415                        }
416                        inline bool check_and_update(t_uint64 cur) {
417                                if (check(cur)) {
418                                        lastupdate = cur;
419                                        return true;
420                                }
421                                return false;
422                        }
423                };
424                dynamic_update_tracker m_dynamic, m_dynamic_track;
425
426                inline t_size get_prepare_length(t_uint64 p_start, t_uint64 p_end, t_size nums) {
427                        t_size n;
428                        bsearch_points_by_prepos(p_start, n);
429                        t_uint64 maxpos = p_end;
430                        while (n < nums) {
431                                loop_event_point::ptr point = get_points_by_prepos()[n];
432                                if (point->get_prepare_position() > p_end) break;
433                                maxpos = pfc::max_t<t_uint64>(maxpos, point->get_position());
434                                n++;
435                        }
436                        return pfc::downcast_guarded<t_size>(maxpos - p_end);
437                }
438
439                inline t_uint64 get_prepare_pos(t_uint64 p_pos, t_size nums) {
440                        t_size n;
441                        bsearch_points_by_prepos(p_pos, n);
442                        if (n < nums) {
443                                return get_points_by_prepos()[n]->get_prepare_position();
444                        }
445                        return (t_uint64)-1;
446                }
447
448        protected:
449                inline bool get_succ() const {return m_succ;}
450                inline void set_succ(bool val) {m_succ = val;}
451                inline void set_cur(t_uint64 val) {m_cur = val;}
452                inline void add_cur(t_uint64 add) {m_cur += add;}
453                inline bool is_raw_supported() const {return m_raw_support;}
454                virtual double get_dynamic_updateperiod() const {return audio_math::samples_to_time(m_dynamic.updateperiod, m_sample_rate);}
455                virtual double get_dynamictrack_updateperiod() const {return audio_math::samples_to_time(m_dynamic_track.updateperiod, m_sample_rate);}
456                virtual void set_dynamic_updateperiod(double p_time) {
457                        m_dynamic.updateperiod = audio_math::time_to_samples(p_time, m_sample_rate);
458                }
459                virtual void set_dynamictrack_updateperiod(double p_time) {
460                        m_dynamic_track.updateperiod = audio_math::time_to_samples(p_time, m_sample_rate);
461                }
462                inline const char * get_info_prefix() const {return m_info_prefix;}
463
464                //! open file. please call switch_input.
465                virtual bool open_path_internal(file::ptr p_filehint,const char * path,t_input_open_reason p_reason,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) = 0;
466                //! open decoding. please call switch_points in this or open_path_internal.
467                virtual void open_decoding_internal(t_uint32 subsong, t_uint32 flags, abort_callback & p_abort) = 0;
468
469                virtual input_decoder::ptr & get_input() {
470                        if (m_current_input.is_empty()) throw pfc::exception_bug_check_v2();
471                        return m_current_input;
472                }
473                virtual input_decoder_v2::ptr & get_input_v2() {
474                        if (m_current_input_v2.is_empty()) throw pfc::exception_not_implemented();
475                        return m_current_input_v2;
476                }
477
478                virtual __declspec(deprecated) void switch_input(input_decoder::ptr p_input) {
479                        switch_input(p_input, NULL);
480                }
481
482                virtual void switch_input(input_decoder::ptr p_input, const char *p_path) {
483                        // please specify reopen'd input...
484                        m_current_input = p_input;
485                        if (m_current_input_v2.is_valid()) m_current_input_v2.release();
486                        m_current_input->service_query_t(m_current_input_v2);
487                        m_dynamic.m_input_switched = m_dynamic_track.m_input_switched = true;
488                        m_current_changed = true;
489                        if (p_path != NULL) {
490                                m_current_path.set_string_(p_path);
491                                m_current_fileext = pfc::string_filename_ext(p_path);
492                        } else {
493                                m_current_path.reset();
494                                m_current_fileext.reset();
495                        }
496                        set_succ(true);
497                }
498
499                virtual void switch_points(loop_event_point_list p_list) {
500                        m_dynamic.m_old_points = m_cur_points;
501                        m_dynamic_track.m_old_points = m_cur_points;
502                        m_cur_points = p_list;
503
504                        m_perm_by_pos.set_size(m_cur_points.get_count());
505                        order_helper::g_fill(m_perm_by_pos.get_ptr(), m_perm_by_pos.get_size());
506                        m_cur_points.sort_get_permutation_t(
507                                loop_event_compare<loop_event_point::ptr, loop_event_point::ptr>,m_perm_by_pos.get_ptr());
508                        if (m_cur_points_by_pos != NULL) delete m_cur_points_by_pos;
509                        m_cur_points_by_pos = new pfc::list_permutation_t<loop_event_point::ptr>(m_cur_points, m_perm_by_pos.get_ptr(), m_perm_by_pos.get_size());
510
511                        m_perm_by_prepos.set_size(m_cur_points.get_count());
512                        order_helper::g_fill(m_perm_by_prepos.get_ptr(), m_perm_by_prepos.get_size());
513                        m_cur_points.sort_get_permutation_t(
514                                loop_event_prepos_compare<loop_event_point::ptr, loop_event_point::ptr>,m_perm_by_prepos.get_ptr());
515                        if (m_cur_points_by_prepos != NULL) delete m_cur_points_by_prepos;
516                        m_cur_points_by_prepos = new pfc::list_permutation_t<loop_event_point::ptr>(m_cur_points, m_perm_by_prepos.get_ptr(), m_perm_by_prepos.get_size());
517                }
518
519                virtual pfc::list_permutation_t<loop_event_point::ptr> get_points_by_pos() {
520                        if (m_cur_points_by_pos == NULL) throw pfc::exception_bug_check_v2();
521                        return *m_cur_points_by_pos;
522                }
523
524                virtual pfc::list_permutation_t<loop_event_point::ptr> get_points_by_prepos() {
525                        if (m_cur_points_by_prepos == NULL) throw pfc::exception_bug_check_v2();
526                        return *m_cur_points_by_prepos;
527                }
528
529                inline loop_event_point_list get_points() {
530                        return m_cur_points;
531                }
532
533                virtual t_size bsearch_points_by_pos(t_uint64 pos, t_size & index) {
534                        return m_cur_points_by_pos->bsearch_t(loop_event_compare<loop_event_point::ptr, t_uint64>, pos, index);
535                }
536
537                virtual t_size bsearch_points_by_prepos(t_uint64 pos, t_size & index) {
538                        return m_cur_points_by_prepos->bsearch_t(loop_event_prepos_compare<loop_event_point::ptr, t_uint64>, pos, index);
539                }
540
541                inline void set_is_raw_supported(bool val) {m_raw_support = val;}
542                inline void set_no_looping(bool val) {m_no_looping = val;}
543
544                virtual t_size get_nearest_point(t_uint64 pos) {
545                        t_size nums = get_points_by_pos().get_count();
546                        if (!nums) return (t_size)-1;
547                        t_size index;
548                        bsearch_points_by_pos(pos, index);
549                        if (index < nums) {
550                                return index;
551                        } else {
552                                return (t_size)-1;
553                        }
554                }
555
556                inline void do_current_events(abort_callback & p_abort) {
557                        do_events(get_cur(), p_abort);
558                }
559
560                inline void do_events(t_uint64 p_pos, abort_callback & p_abort) {
561                        do_events(p_pos, p_pos, p_abort);
562                }
563
564                virtual void do_events(t_uint64 p_start, t_uint64 p_end, abort_callback & p_abort) {
565                        t_size nums = get_points_by_pos().get_count();
566                        t_size n;
567                        loop_event_point::ptr point;
568                        bsearch_points_by_pos(p_start, n);
569                       
570                        while (n < nums) {
571                                point = get_points_by_pos()[n];
572                                m_nextpointpos = point->get_position();
573                                if (m_nextpointpos > p_end) {
574                                        m_nextpointpos = pfc::min_t(m_nextpointpos, get_prepare_pos(p_end, nums));
575                                        return;
576                                }
577                                if (process_event(point, p_abort)) {
578                                        if (p_end != get_cur() || p_start != get_cur()) {
579                                                // current is updated.
580                                                do_current_events(p_abort);
581                                                return;
582                                        }
583                                }
584                                n++;
585                        }
586                        m_nextpointpos = (t_uint64)-1;
587                }
588
589                virtual void do_events(t_uint64 p_start, audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
590                        t_size nums = get_points_by_pos().get_count();
591                        t_size n;
592                        loop_event_point::ptr point;
593                        // skip p_start itself, because it was proceeded as older's end
594                        bsearch_points_by_pos(p_start+1, n);
595                        t_uint64 end = p_start + p_chunk.get_sample_count();
596                        t_size preplen = get_prepare_length(p_start, end, nums);
597                        if (preplen > 0)
598                                end += get_more_chunk(p_chunk, p_raw, p_abort, preplen);
599
600                        while (n < nums) {
601                                point = get_points_by_pos()[n];
602                                m_nextpointpos = point->get_position();
603                                if (m_nextpointpos > end) {
604                                        m_nextpointpos = pfc::min_t(m_nextpointpos, get_prepare_pos(end, nums));
605                                        return;
606                                }
607                                if (process_event(point, p_start, p_chunk, p_raw, p_abort)) {
608                                        // current is updated.
609                                        do_current_events(p_abort);
610                                        return;
611                                }
612                                n++;
613                        }
614                        m_nextpointpos = (t_uint64)-1;
615                }
616
617                inline void user_seek(double seconds, abort_callback & p_abort) {
618                        raw_seek(seconds, p_abort);
619                        do_current_events(p_abort);
620                }
621               
622                inline void user_seek(t_uint64 samples, abort_callback & p_abort) {
623                        raw_seek(samples, p_abort);
624                        do_current_events(p_abort);
625                }
626               
627                virtual bool set_dynamic_info(file_info & p_out) {
628                        t_uint32 sample_rate = get_sample_rate();
629                        pfc::string8 name;
630                        name << get_info_prefix() << "current";
631                        p_out.info_set(name, format_samples_ex(get_cur(), sample_rate));
632                        name.reset();
633                        name << get_info_prefix() << "next_event_pos";
634                        p_out.info_set(name, (m_nextpointpos != (t_uint64)-1) ?
635                                format_samples_ex(m_nextpointpos, sample_rate) : "(nothing or eof)");
636                        return true;
637                }
638
639                //! called after switch_points or switch_input
640                virtual bool reset_dynamic_info(file_info & p_out) {
641                        bool ret = false;
642                        pfc::string8 name;
643                        name << get_info_prefix() << "current";
644                        ret |= p_out.info_remove(name);
645                        name.reset();
646                        name << get_info_prefix() << "next_event_pos";
647                        ret |= p_out.info_remove(name);
648                        return ret;
649                }
650
651                virtual bool set_dynamic_info_track(file_info & p_out) {
652                        if (!m_current_changed) return false;
653                        bool ret = false;
654                        pfc::string8 name;
655                        name << get_info_prefix() << "current_file";
656                        if (!m_current_fileext.is_empty()) {
657                                p_out.info_set(name, m_current_fileext);
658                                ret = true;
659                        } else {
660                                ret |= p_out.info_remove(name);
661                        }
662                        name.reset();
663                        if (!m_current_input.is_empty()) {
664                                name << get_info_prefix() << "current_path_raw";
665                                p_out.info_set(name, m_current_path);
666                                ret = true;
667                        } else {
668                                ret |= p_out.info_remove(name);
669                        }
670                        m_current_changed = false;
671                        return ret;
672                }
673
674                //! called after switch_points or switch_input
675                virtual bool reset_dynamic_info_track(file_info & p_out) {
676                        if (!m_current_changed) return false;
677                        bool ret = false;
678                        pfc::string8 name;
679                        name << get_info_prefix() << "current_file";
680                        ret |= p_out.info_remove(name);
681                        name.reset();
682                        name << get_info_prefix() << "current_path_raw";
683                        ret |= p_out.info_remove(name);
684                        return ret;
685                }
686
687                virtual bool run_common(audio_chunk & p_chunk,mem_block_container * p_raw,abort_callback & p_abort) {
688                        t_uint64 start = get_cur();
689                        t_uint retries = 4; // max retries
690                        while (retries > 0 && get_succ()) {
691                                get_one_chunk(p_chunk,p_raw,p_abort);
692                                if (get_succ()) {
693                                        if (m_nextpointpos <= get_cur()) {
694                                                do_events(start,p_chunk,p_raw,p_abort);
695                                        }
696                                } else {
697                                        if (cfg_loop_debug.get())
698                                                console_looping_debug_formatter() << "dispatch EOF event";
699                                        // try dispatching EOF event;
700                                        do_events((t_uint64)-1, p_abort);
701                                        retries--;
702                                        continue;
703                                }
704                                break;
705                        }
706                        return get_succ();
707                }
708
709                void get_info_for_points(file_info & p_info, loop_event_point_list & points, const char * p_prefix, t_uint32 p_sample_rate) {
710                        for (t_size n = 0, m = points.get_count(); n < m; ++n ) {
711                                loop_event_point::ptr point = points[n];
712                                pfc::string8 name;
713                                name << p_prefix << "point_" << pfc::format_int(n, 2) << "_";
714                                point->get_info(p_info, name, p_sample_rate);
715                        }
716                }
717
718                class dispatch_dynamic_info {
719                public:
720                        inline static bool point_check(loop_event_point::ptr point) {
721                                return point->has_dynamic_info();
722                        }
723                        inline static bool point_set(loop_event_point::ptr point, file_info & p_info, const char * p_prefix, t_uint32 p_sample_rate) {
724                                return point->set_dynamic_info(p_info, p_prefix, p_sample_rate);
725                        }
726                        inline static bool point_reset(loop_event_point::ptr point, file_info & p_info, const char * p_prefix) {
727                                return point->reset_dynamic_info(p_info, p_prefix);
728                        }
729                        inline static bool parent_get(input_decoder::ptr & parent, file_info & p_out, double & p_timestamp_delta) {
730                                return parent->get_dynamic_info(p_out, p_timestamp_delta);
731                        }
732                        inline static bool self_set(loop_type_impl_base & impl, file_info & p_out) {
733                                return impl.set_dynamic_info(p_out);
734                        }
735                        inline static bool self_reset(loop_type_impl_base & impl, file_info & p_out) {
736                                return impl.reset_dynamic_info(p_out);
737                        }
738                };
739
740                class dispatch_dynamic_track_info {
741                public:
742                        inline static bool point_check(loop_event_point::ptr point) {
743                                return point->has_dynamic_track_info();
744                        }
745                        inline static bool point_set(loop_event_point::ptr point, file_info & p_info, const char * p_prefix, t_uint32 p_sample_rate) {
746                                return point->set_dynamic_track_info(p_info, p_prefix, p_sample_rate);
747                        }
748                        inline static bool point_reset(loop_event_point::ptr point, file_info & p_info, const char * p_prefix) {
749                                return point->reset_dynamic_track_info(p_info, p_prefix);
750                        }
751                        inline static bool parent_get(input_decoder::ptr & parent, file_info & p_out, double & p_timestamp_delta) {
752                                return parent->get_dynamic_info_track(p_out, p_timestamp_delta);
753                        }
754                        inline static bool self_set(loop_type_impl_base & impl, file_info & p_out) {
755                                return impl.set_dynamic_info_track(p_out);
756                        }
757                        inline static bool self_reset(loop_type_impl_base & impl, file_info & p_out) {
758                                return impl.reset_dynamic_info_track(p_out);
759                        }
760                };
761
762                template <typename t_dispatcher>
763                inline bool get_dynamic_info_t(file_info & p_out,double & p_timestamp_delta, dynamic_update_tracker & tracker) {
764                        bool ret = t_dispatcher::parent_get(get_input(),p_out,p_timestamp_delta);
765                        loop_event_point_list & oldlist = tracker.m_old_points;
766                        if (oldlist.get_count() != 0 || tracker.m_input_switched) {
767                                ret |= t_dispatcher::self_reset(*this, p_out);
768                                for (t_size n = 0, m = get_points().get_count(); n < m; ++n ) {
769                                        loop_event_point::ptr point = get_points()[n];
770                                        if (t_dispatcher::point_check(point)) {
771                                                pfc::string8 name;
772                                                name << get_info_prefix() << "point_" << pfc::format_int(n, 2) << "_";
773                                                ret |= t_dispatcher::point_reset(point, p_out, name);
774                                        }
775                                }
776                                oldlist.remove_all();
777                                tracker.m_input_switched = false;
778                        }
779                        if (!get_no_looping()) {
780                                if (tracker.check_and_update(get_cur())) {
781                                        p_timestamp_delta = !ret ? 0.5 : pfc::min_t<double>(0.5, p_timestamp_delta);
782                                        ret |= t_dispatcher::self_set(*this, p_out);
783                                        t_uint32 sample_rate = get_sample_rate();
784                                        for (t_size n = 0, m = get_points().get_count(); n < m; ++n ) {
785                                                loop_event_point::ptr point = get_points()[n];
786                                                if (t_dispatcher::point_check(point)) {
787                                                        pfc::string8 name;
788                                                        name << get_info_prefix() << "point_" << pfc::format_int(n, 2) << "_";
789                                                        ret |= t_dispatcher::point_set(point, p_out, name, sample_rate);
790                                                }
791                                        }
792                                }
793                        }
794                        return ret;
795                }
796
797        public:
798                loop_type_impl_base() :
799                  m_sample_rate(0), m_cur(0), m_succ(false), m_raw_support(true), m_current_changed(false),
800                  m_cur_points_by_pos(NULL), m_cur_points_by_prepos(NULL), m_info_prefix("loop_") {
801                }
802                ~loop_type_impl_base() {
803                        if (m_cur_points_by_pos != NULL) delete m_cur_points_by_pos;
804                        if (m_cur_points_by_prepos != NULL) delete m_cur_points_by_prepos;
805                }
806
807                t_uint64 virtual get_cur() const {return m_cur;}
808                t_uint32 virtual get_sample_rate() const {return m_sample_rate;}
809                virtual bool get_no_looping() const {return m_no_looping || cfg_loop_disable.get();}
810                virtual void set_info_prefix(const char *p_prefix) {m_info_prefix = p_prefix;}
811
812                virtual bool open_path(file::ptr p_filehint,const char * path,t_input_open_reason p_reason,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) {
813                        if (p_reason == input_open_info_write) throw exception_io_unsupported_format();//our input does not support retagging.
814                        bool ret = open_path_internal(p_filehint,path,p_reason,p_abort,p_from_redirect,p_skip_hints);
815                        if (ret) {
816                                file_info_impl p_info;
817                                get_input()->get_info(0, p_info, p_abort);
818                                m_sample_rate = pfc::downcast_guarded<t_uint32>(p_info.info_get_int("samplerate"));
819                        }
820                        return ret;
821                }
822
823                void virtual open_decoding(t_uint32 subsong, t_uint32 flags, abort_callback & p_abort) {
824                        m_no_looping = (flags & input_flag_simpledecode) != 0;
825                        if (cfg_loop_debug.get())
826                                console_looping_debug_formatter() << "open decoding (subsong: " << subsong << " / flags: "  << flags << " / no_looping: " << (m_no_looping?"true":"false") << ")";
827                        set_dynamic_updateperiod(0.5);
828                        set_dynamictrack_updateperiod(1.0);
829                        open_decoding_internal(subsong, flags, p_abort);
830                        set_cur(0);
831                        set_succ(true);
832                        do_current_events(p_abort);
833                }
834
835                virtual bool process_event(loop_event_point::ptr point, t_uint64 p_start, audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
836                        return point->process(this, p_start, p_chunk, p_raw, p_abort);
837                }
838
839                virtual bool process_event(loop_event_point::ptr point, abort_callback & p_abort) {
840                        return point->process(this, p_abort);
841                }
842
843
844                virtual bool run(audio_chunk & p_chunk,abort_callback & p_abort) {
845                        return run_common(p_chunk,NULL,p_abort);
846                }
847
848                virtual bool run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) {
849                        if (!get_no_looping() && !is_raw_supported()) throw pfc::exception_not_implemented();
850                        return run_common(p_chunk,&p_raw,p_abort);
851                }
852
853                virtual void seek(double p_seconds,abort_callback & p_abort) {
854                        user_seek(p_seconds,p_abort);
855                }
856
857                virtual void seek(t_uint64 p_samples,abort_callback & p_abort) {
858                        user_seek(p_samples,p_abort);
859                }
860
861                // other input_decoder methods
862                bool virtual get_dynamic_info(file_info & p_out,double & p_timestamp_delta) {
863                        return get_dynamic_info_t<dispatch_dynamic_info>(p_out,p_timestamp_delta,m_dynamic);
864                }
865
866                bool virtual get_dynamic_info_track(file_info & p_out,double & p_timestamp_delta) {
867                        return get_dynamic_info_t<dispatch_dynamic_track_info>(p_out,p_timestamp_delta,m_dynamic_track);
868                }
869                void virtual set_logger(event_logger::ptr ptr) {get_input_v2()->set_logger(ptr);}
870        };
871
872        class loop_type_impl_singleinput_base : public loop_type_impl_base {
873        protected:
874                void virtual open_decoding_internal(t_uint32 subsong, t_uint32 flags, abort_callback & p_abort) {
875                        get_input()->initialize(subsong, flags, p_abort);
876                }
877
878        public:
879                virtual t_uint32 get_subsong_count() {
880                        return get_input()->get_subsong_count();
881                }
882
883                virtual t_uint32 get_subsong(t_uint32 p_index) {
884                        return get_input()->get_subsong(p_index);
885                }
886
887                void virtual get_info(t_uint32 subsong, file_info & p_info,abort_callback & p_abort) {
888                        get_input()->get_info(subsong, p_info, p_abort);
889                }
890
891                virtual t_filestats get_file_stats(abort_callback & p_abort) {
892                        return get_input()->get_file_stats(p_abort);
893                }
894                void virtual close() {get_input().release();}
895                void virtual on_idle(abort_callback & p_abort) {get_input()->on_idle(p_abort);}
896        };
897
898        class loop_type_entry : public service_base {
899        public:
900                virtual const char * get_name() const = 0;
901                virtual const char * get_short_name() const = 0;
902                virtual bool is_our_type(const char * type) const = 0;
903                virtual bool is_explicit() const = 0;
904                virtual loop_type::ptr instantiate() const = 0;
905
906                FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(loop_type_entry);
907        };
908
909        template<typename t_instance_impl>
910        class loop_type_impl_t : public loop_type_entry {
911        public:
912                const char * get_name() const {return t_instance_impl::g_get_name();}
913                const char * get_short_name() const {return t_instance_impl::g_get_short_name();}
914                bool is_our_type(const char * type) const {return t_instance_impl::g_is_our_type(type);}
915                bool is_explicit() const {return t_instance_impl::g_is_explicit();}
916                loop_type::ptr instantiate() const {return new service_impl_t<t_instance_impl>();}
917        };
918
919        template<typename t_instance_impl> class loop_type_factory_t :
920                public service_factory_single_t<loop_type_impl_t<t_instance_impl> > {};
921
922        class loop_type_entry_v2 : public loop_type_entry {
923        public:
924                //! default priority = 100, lower equals faster
925                virtual t_uint8 get_priority() const = 0;
926
927                FB2K_MAKE_SERVICE_INTERFACE(loop_type_entry_v2, loop_type_entry);
928        };
929
930        template<typename t_instance_impl>
931        class loop_type_impl_v2_t : public loop_type_entry_v2  {
932        public:
933                const char * get_name() const {return t_instance_impl::g_get_name();}
934                const char * get_short_name() const {return t_instance_impl::g_get_short_name();}
935                bool is_our_type(const char * type) const {return t_instance_impl::g_is_our_type(type);}
936                bool is_explicit() const {return t_instance_impl::g_is_explicit();}
937                loop_type::ptr instantiate() const {return new service_impl_t<t_instance_impl>();}
938                t_uint8 get_priority() const {return t_instance_impl::g_get_priority();}
939        };
940
941        template<typename t_instance_impl> class loop_type_factory_v2_t :
942                public service_factory_single_t<loop_type_impl_v2_t<t_instance_impl> > {};
943
944        class loop_type_none : public loop_type_impl_singleinput_base
945        {
946        private:
947                input_decoder::ptr m_input;
948                loop_event_point_list m_points;
949        public:
950                static const char * g_get_name() {return "nothing";}
951                static const char * g_get_short_name() {return "none";}
952                static bool g_is_our_type(const char * type) {return !pfc::stringCompareCaseInsensitive(type, "none");}
953                static bool g_is_explicit() {return true;}
954                virtual bool parse(const char * ptr) {return true;}
955                virtual bool open_path_internal(file::ptr p_filehint,const char * path,t_input_open_reason p_reason,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) {
956                        if (p_reason == input_open_info_write) throw exception_io_unsupported_format();//our input does not support retagging.
957                        open_path_helper(m_input, p_filehint, path, p_abort, p_from_redirect, p_skip_hints);
958                        //m_points.remove_all();
959                        switch_input(m_input, path);
960                        switch_points(m_points);
961                        return true;
962                }
963        };
964
965        class loop_type_entire : public loop_type_impl_singleinput_base
966        {
967        private:
968                input_decoder::ptr m_input;
969                loop_event_point_list m_points;
970        public:
971                static const char * g_get_name() {return "Entire File";}
972                static const char * g_get_short_name() {return "entire";}
973                static bool g_is_our_type(const char * type) {return !pfc::stringCompareCaseInsensitive(type, "entire");}
974                static bool g_is_explicit() {return true;}
975                virtual bool parse(const char * ptr) {
976                        return true;
977                }
978                virtual bool open_path_internal(file::ptr p_filehint,const char * path,t_input_open_reason p_reason,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) {
979                        if (p_reason == input_open_info_write) throw exception_io_unsupported_format();//our input does not support retagging.
980                        try {
981                                open_path_helper(m_input, p_filehint, path, p_abort, p_from_redirect,p_skip_hints);
982                        } catch (exception_io_not_found) {
983                                return false;
984                        }
985                        switch_input(m_input, path);
986                        file_info_impl p_info;
987                        get_input()->get_info(0, p_info, p_abort);
988                        m_points.remove_all();
989                        loop_event_point_simple * point = new service_impl_t<loop_event_point_simple>();
990                        point->from = p_info.info_get_length_samples();
991                        point->to = 0;
992                        m_points.add_item(point);
993                        switch_points(m_points);
994                        return true;
995                }
996        };
997
998        class NOVTABLE input_loop_base
999        {
1000        public:
1001                void open(file::ptr p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
1002                        if (p_reason == input_open_info_write) throw exception_io_unsupported_format();//our input does not support retagging.
1003                        open_internal(p_filehint, p_path, p_reason, p_abort);
1004                        PFC_ASSERT(m_looptype.is_valid());
1005                        PFC_ASSERT(m_loopentry.is_valid());
1006                        m_looptype->set_info_prefix(m_info_prefix);
1007                }
1008
1009                void get_info(t_uint32 p_subsong, file_info & p_info,abort_callback & p_abort) {
1010                        m_looptype->get_info(p_subsong,p_info,p_abort);
1011                        pfc::string8 name;
1012                        pfc::string8 buf;
1013                        name << m_info_prefix << "type";
1014                        buf << m_loopentry->get_short_name() << " [" << m_loopentry->get_name() << "]";
1015                        p_info.info_set(name, buf);
1016                        name.reset();
1017                        if (!m_loopcontent.is_empty()) {
1018                                name << m_info_prefix << "content";
1019                                p_info.info_set(name, m_loopcontent);
1020                        }
1021                }
1022
1023                void get_info(file_info & p_info,abort_callback & p_abort) {
1024                        get_info(0,p_info,p_abort);
1025                }
1026
1027                t_uint32 get_subsong_count() {
1028                        return m_looptype->get_subsong_count();
1029                }
1030               
1031                t_uint32 get_subsong(t_uint32 p_index) {
1032                        return m_looptype->get_subsong(p_index);
1033                }
1034
1035                t_filestats get_file_stats(abort_callback & p_abort) {
1036                        if (m_loopfile.is_valid())
1037                                return merge_filestats(
1038                                        m_loopfile->get_stats(p_abort),
1039                                        m_looptype->get_file_stats(p_abort),
1040                                        merge_filestats_sum);
1041                        else
1042                                return m_looptype->get_file_stats(p_abort);
1043                }
1044
1045                void decode_initialize(t_uint32 p_subsong,unsigned p_flags,abort_callback & p_abort) {
1046                        m_looptype->open_decoding(p_subsong,p_flags,p_abort);
1047                }
1048
1049                void decode_initialize(unsigned p_flags,abort_callback & p_abort) {
1050                        decode_initialize(0,p_flags,p_abort);
1051                }
1052
1053                bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort) {
1054                        return m_looptype->run(p_chunk,p_abort);
1055                }
1056
1057                bool decode_run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) {
1058                        return m_looptype->run_raw(p_chunk,p_raw,p_abort);
1059                }
1060
1061                void decode_seek(double p_seconds,abort_callback & p_abort) {
1062                        m_looptype->seek(p_seconds,p_abort);
1063                }
1064
1065                bool decode_can_seek() {return m_looptype->can_seek();}
1066                bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {
1067                        return m_looptype->get_dynamic_info(p_out,p_timestamp_delta);
1068                }
1069                bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {
1070                        return m_looptype->get_dynamic_info_track(p_out, p_timestamp_delta);
1071                }
1072
1073                void decode_on_idle(abort_callback & p_abort) {m_looptype->on_idle(p_abort);}
1074
1075                void retag_set_info(t_uint32 p_subsong,const file_info & p_info,abort_callback & p_abort) {throw exception_io_unsupported_format();}
1076                void retag_commit(abort_callback & p_abort) {throw exception_io_unsupported_format();}
1077                void retag(const file_info & p_info,abort_callback & p_abort) {
1078                        retag_set_info(0,p_info,p_abort);
1079                        retag_commit(p_abort);
1080                }
1081
1082                void set_logger(event_logger::ptr ptr) {m_looptype->set_logger(ptr);}
1083
1084        protected:
1085                input_loop_base(const char * p_info_prefix) : m_info_prefix(p_info_prefix) {}
1086                virtual void open_internal(file::ptr p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) = 0;
1087                //static bool g_is_our_content_type(const char * p_content_type);
1088                //static bool g_is_our_path(const char * p_path,const char * p_extension);
1089                file::ptr m_loopfile;
1090                pfc::string8 m_path;
1091                loop_type_entry::ptr m_loopentry;
1092                loop_type::ptr m_looptype;
1093                pfc::string8 m_loopcontent;
1094                pfc::string8 m_info_prefix;
1095        };
1096}
1097
1098using namespace loop_helper;
Note: See TracBrowser for help on using the browser.