root/platform/foobar2000/trunk/foo_loop/looping.cpp @ 28420

Revision 28420, 15.1 kB (checked in by topia, 4 years ago)

remove redundant code.

Line 
1
2#include <stdafx.h>
3#include "looping.h"
4
5
6
7bool 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) {
8        p_abort.check();
9        p_input.release();
10
11        TRACK_CODE("input_entry::g_open_for_decoding",
12                input_entry::g_open_for_decoding(p_input,p_file,path,p_abort,p_from_redirect)
13                );
14
15       
16        if (!p_skip_hints) {
17                try {
18                        static_api_ptr_t<metadb_io>()->hint_reader(p_input.get_ptr(),path,p_abort);
19                } catch(exception_io_data) {
20                        //Don't fail to decode when this barfs, might be barfing when reading info from another subsong than the one we're trying to decode etc.
21                        p_input.release();
22                        if (p_file.is_valid()) p_file->reopen(p_abort);
23                        TRACK_CODE("input_entry::g_open_for_decoding",
24                                input_entry::g_open_for_decoding(p_input,p_file,path,p_abort,p_from_redirect)
25                                );
26                }
27        }
28
29        return true;
30}
31
32bool parse_entity(const char * & ptr,pfc::string8 & name,pfc::string8 & value) {
33        char delimiter = '\0';
34        char tmp;
35        t_size n = 0;
36        while(isspace(*ptr)) ptr++;
37        while(tmp = ptr[n], tmp && !isspace(tmp) && tmp != '=') n++;
38        if (!ptr[n]) return false;
39        name.set_string(ptr, n);
40        ptr += n;
41        while(isspace(*ptr)) ptr++;
42        if (*ptr != '=') return false;
43        ptr++;
44        // check delimiter
45        if (*ptr == '\'' || *ptr == '"') {
46                delimiter = *ptr;
47                ptr++;
48        }
49        if (!*ptr) false;
50
51        n = 0;
52        if (delimiter == '\0') {
53                while(tmp = ptr[n], tmp && !isspace(tmp) && tmp != ';') n++;
54        } else {
55                while(tmp = ptr[n], tmp && tmp != delimiter) n++;
56        }
57        if (!ptr[n]) return false;
58        value.set_string(ptr, n);
59        ptr += n;
60        if (*ptr == delimiter) ptr++;
61        while(*ptr == ';' || isspace(*ptr)) ptr++;
62        return true;
63}
64
65t_filestats merge_filestats(const t_filestats & p_src1, const t_filestats & p_src2, int p_merge_type) {
66        t_filestats dest;
67        dest.m_timestamp = pfc::max_t(p_src1.m_timestamp, p_src2.m_timestamp);
68        if (p_merge_type == merge_filestats_sum) {
69                dest.m_size = p_src1.m_size + p_src2.m_size;
70        } else if (p_merge_type == merge_filestats_max) {
71                dest.m_size = pfc::max_t(p_src1.m_size, p_src2.m_size);
72        } else {
73                throw pfc::exception_not_implemented();
74        }
75        return dest;
76}
77
78// some looping type class
79class input_loop_type_loopstartlength : public input_loop_type_impl_singlefile_base
80{
81private:
82        input_decoder::ptr m_input;
83        input_loop_event_point_list m_points;
84public:
85        static const char * g_get_name() {return "LoopStart/LoopLength";}
86        static const char * g_get_short_name() {return "loopstartlength";}
87        static bool g_is_our_type(const char * type) {return !pfc::stringCompareCaseInsensitive(type, "loopstartlength");}
88        static bool g_is_explicit() {return false;}
89        virtual bool parse(const char * ptr) {
90                return true;
91        }
92        virtual bool open_path_internal(file::ptr p_filehint,const char * path,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) {
93                try {
94                        open_path_helper(m_input, p_filehint, path, p_abort, p_from_redirect,p_skip_hints);
95                } catch (exception_io_not_found) {
96                        return false;
97                }
98                switch_input(m_input);
99                file_info_impl p_info;
100                get_input()->get_info(0, p_info, p_abort);
101                m_points.remove_all();
102                if (p_info.meta_get_count_by_name("LOOPSTART") != 1 ||
103                        p_info.meta_get_count_by_name("LOOPLENGTH") != 1)
104                        return false;
105                input_loop_event_point_simple * point = new service_impl_t<input_loop_event_point_simple>();
106                t_uint64 start = pfc::atoui64_ex(p_info.meta_get("LOOPSTART", 0), infinite_size);
107                t_uint64 length = pfc::atoui64_ex(p_info.meta_get("LOOPLENGTH", 0), infinite_size);
108                point->from = start + length;
109                point->to = start;
110                m_points.add_item(point);
111                switch_points(m_points);
112                return true;
113        }
114        virtual void get_info(t_uint32 subsong, file_info & p_info,abort_callback & p_abort) {
115                get_input()->get_info(subsong, p_info, p_abort);
116                get_info_for_points(p_info, m_points, get_info_prefix(), get_sample_rate());
117        }
118};
119
120static input_loop_type_factory_t<input_loop_type_loopstartlength> g_input_loop_type_loopstartlength;
121
122class input_loop_event_point_twofiles_eof : public input_loop_event_point_baseimpl {
123public:
124        virtual t_uint64 get_position() const {return infinite64;}
125        virtual t_uint64 get_prepare_position() const {return infinite64;}
126        virtual void get_info(file_info & p_info, const char * p_prefix, t_uint32 sample_rate) {}
127        virtual bool process(input_loop_type_base::ptr p_input, t_uint64 p_start, audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
128                // no truncate (because EOF)
129                return process(p_input, p_abort);
130        }
131        virtual bool process(input_loop_type_base::ptr p_input, abort_callback & p_abort);
132};
133
134
135class input_loop_type_twofiles : public input_loop_type_impl_base
136{
137private:
138        struct somefile {
139                input_decoder::ptr input;
140                pfc::string8 suffix;
141                pfc::string8 path;
142                t_uint64 samples;
143                bool samples_known;
144        };
145        input_loop_event_point_list m_points;
146        somefile * m_current;
147        somefile m_head, m_body;
148        pfc::string8 m_current_fileext;
149        bool m_current_changed;
150
151        inline void switch_to(somefile & newfile) {
152                if (m_current != &newfile) {
153                        m_current_changed = true;
154                }
155                m_current = &newfile;
156                switch_input(newfile.input);
157                m_current_fileext = pfc::string_filename_ext(newfile.path);
158        }
159
160protected:
161        void virtual open_decoding_internal(t_uint32 subsong, t_uint32 flags, abort_callback & p_abort) {
162                m_head.input->initialize(subsong, flags, p_abort);
163                m_body.input->initialize(subsong, flags, p_abort);             
164        }
165        virtual bool set_dynamic_info_track(file_info & p_out) {
166                bool ret = input_loop_type_impl_base::set_dynamic_info_track(p_out);
167                if (m_current_changed) {
168                        p_out.info_set("loop_current_file", m_current_fileext);
169                        return true;
170                } else {
171                        return ret;
172                }
173        }
174       
175        virtual bool reset_dynamic_info_track(file_info & p_out) {
176                return input_loop_type_impl_base::reset_dynamic_info_track(p_out) |
177                        p_out.info_remove("loop_current_file");
178        }
179public:
180        static const char * g_get_name() {return "Two Files (head and body)";}
181        static const char * g_get_short_name() {return "twofiles";}
182        static bool g_is_our_type(const char * type) {return !pfc::stringCompareCaseInsensitive(type, "twofiles");}
183        static bool g_is_explicit() {return true;}
184        virtual bool parse(const char * ptr) {
185                pfc::string8 name, value;
186                while (parse_entity(ptr, name, value)) {
187                        if (!pfc::stringCompareCaseInsensitive(name, "head-suffix")) {
188                                m_head.suffix = value;
189                        } else if (!pfc::stringCompareCaseInsensitive(name, "body-suffix")) {
190                                m_body.suffix = value;
191                        } else {
192                                // ignore unknown entities
193                                //return false;
194                        }
195                }
196                if (m_head.suffix == m_body.suffix) return false;
197                return true;
198        }
199        virtual bool open_path_internal(file::ptr p_filehint,const char * p_path,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) {
200                pfc::string8 base, ext;
201                base = p_path;
202                t_size extpos = base.find_last('.');
203                if (extpos >= base.scan_filename()) {
204                        // is ext
205                        ext.set_string(base + extpos);
206                        base.truncate(extpos);
207                } else {
208                        ext = "";
209                }
210                m_head.path.reset();
211                m_head.path << base << m_head.suffix << ext;
212                try {
213                        open_path_helper(m_head.input, NULL, m_head.path, p_abort, p_from_redirect, p_skip_hints);
214                } catch (exception_io_not_found) {
215                        console::formatter() << "loop twofiles: head file not found: \"" << file_path_display(m_head.path) << "\"";
216                        return false;
217                }
218                m_body.path.reset();
219                m_body.path << base << m_body.suffix << ext;
220                try {
221                        open_path_helper(m_body.input, NULL, m_body.path, p_abort, p_from_redirect, p_skip_hints);
222                } catch (exception_io_not_found) {
223                        console::formatter() << "loop twofiles: body file not found: \"" << file_path_display(m_body.path) << "\"";
224                        return false;
225                }
226                if (m_head.input.is_empty() || m_body.input.is_empty()) return false;
227                m_points.remove_all();
228                input_loop_event_point_twofiles_eof * point = new service_impl_t<input_loop_event_point_twofiles_eof>();
229                m_points.add_item(point);
230                switch_points(m_points);
231                file_info_impl p_info;
232                m_head.input->get_info(0, p_info, p_abort);
233                m_head.samples = p_info.info_get_length_samples();
234                p_info.reset();
235                m_body.input->get_info(0, p_info, p_abort);
236                m_body.samples = p_info.info_get_length_samples();
237                switch_to(m_head);
238                return true;
239        }
240        virtual void get_info(t_uint32 subsong, file_info & p_info,abort_callback & p_abort) {
241                t_uint32 sample_rate = get_sample_rate();
242                m_head.input->get_info(subsong, p_info, p_abort);
243                pfc::string8 name;
244                name << get_info_prefix() << "head_length";
245                p_info.info_set(name, format_samples_ex(m_head.samples, sample_rate));
246                name.reset();
247                name << get_info_prefix() << "body_length";
248                p_info.info_set(name, format_samples_ex(m_body.samples, sample_rate));
249                p_info.set_length(audio_math::samples_to_time(m_head.samples + m_body.samples, sample_rate));
250        }
251        virtual t_filestats get_file_stats(abort_callback & p_abort) {
252                return merge_filestats(
253                        m_head.input->get_file_stats(p_abort),
254                        m_body.input->get_file_stats(p_abort),
255                        merge_filestats_sum);
256        }
257        void virtual close() {
258                m_head.input.release();
259                m_body.input.release();
260        }
261        void virtual on_idle(abort_callback & p_abort) {
262                m_head.input->on_idle(p_abort);
263                m_body.input->on_idle(p_abort);
264        }
265        virtual bool on_eof_event(abort_callback & p_abort) {
266                switch_to(m_body);
267                if (get_no_looping()) switch_points(input_loop_event_point_list()); // clear event for disable looping
268                raw_seek((t_uint64)0,p_abort);
269                return true;
270        }
271        virtual void seek(double p_seconds,abort_callback & p_abort) {
272                seek(audio_math::time_to_samples(p_seconds, get_sample_rate()),p_abort);
273        }
274        virtual void seek(t_uint64 p_samples,abort_callback & p_abort) {
275                if (p_samples < m_head.samples) {
276                        switch_to(m_head);
277                        user_seek(p_samples,p_abort);
278                } else {
279                        p_samples -= m_head.samples;
280                        switch_to(m_body);
281                        user_seek(p_samples,p_abort);
282                }
283        }
284        FB2K_MAKE_SERVICE_INTERFACE(input_loop_type_twofiles, input_loop_type);
285};
286
287bool input_loop_event_point_twofiles_eof::process(input_loop_type_base::ptr p_input, abort_callback & p_abort) {
288        input_loop_type_twofiles::ptr p_input_special;
289        if (p_input->service_query_t<input_loop_type_twofiles>(p_input_special)) {
290                return p_input_special->on_eof_event(p_abort);
291        }
292        return false;
293}
294
295static input_loop_type_factory_t<input_loop_type_twofiles> g_input_loop_type_twofiles;
296
297class input_loop_type_sampler : public input_loop_type_impl_singlefile_base
298{
299private:
300        input_decoder::ptr m_input;
301        input_loop_event_point_list m_points;
302public:
303        static const char * g_get_name() {return "Wave(RIFF) Sampler";}
304        static const char * g_get_short_name() {return "sampler";}
305        static bool g_is_our_type(const char * type) {return !pfc::stringCompareCaseInsensitive(type, "sampler");}
306        static bool g_is_explicit() {return false;}
307        virtual bool parse(const char * ptr) {
308                return true;
309        }
310        virtual bool open_path_internal(file::ptr p_filehint,const char * path,abort_callback & p_abort,bool p_from_redirect,bool p_skip_hints) {
311                if (p_filehint.is_empty()) {
312                        try {
313                                filesystem::g_open_read(p_filehint,path,p_abort);
314                        } catch (exception_io_not_found) {
315                                return false;
316                        }
317                }
318                p_filehint->reopen(p_abort);
319                if (p_filehint->is_remote()) {
320                        console::formatter() << "loop sampler: file must be local: \"" << file_path_display(path) << "\"";
321                        return false;
322                }
323                pfc::string8 buf;
324                t_uint32 size;
325                p_filehint->read_string_ex(buf, 4, p_abort); // chunkname: RIFF
326                if (!!pfc::strcmp_ex(buf, 4, "RIFF", 4))
327                        return false;
328                p_filehint->read_lendian_t(size, p_abort); // chunksize
329                p_filehint->read_string_ex(buf, 4, p_abort); // filetype: WAVE
330                while (!p_filehint->is_eof(p_abort)) {
331                        p_filehint->read_string_ex(buf, 4, p_abort); // chunkname
332                        p_filehint->read_lendian_t(size, p_abort); // chunksize
333                        if (!!pfc::strcmp_ex(buf, 4, "smpl", 4)) {
334                                p_filehint->skip(size, p_abort);
335                                continue;
336                        }
337                        stream_reader_limited_ref chunk_reader(p_filehint.get_ptr(), size);
338                        // chunk found;
339/*
340        UnsignedInt('manufacturer'),
341        UnsignedInt('product'),
342        UnsignedInt('sample_period'),
343        UnsignedInt('midi_unity_note'),
344        UnsignedInt('midi_pitch_fraction'),
345        UnsignedInt('smpte_format'),
346        UnsignedInt('smpte_offset'),
347        UnsignedInt('num_sample_loops'),
348        UnsignedInt('sampler_data'),
349*/
350                        t_uint32 i;
351                        chunk_reader.skip(4 * 7, p_abort);
352                        chunk_reader.read_lendian_t(i, p_abort);
353                        chunk_reader.skip(4, p_abort); // sampler_data
354                        for (; i>0; i--) {
355/*
356        UnsignedInt('cue_point_id'),
357        UnsignedInt('type'),
358        UnsignedInt('start'),
359        UnsignedInt('end'),
360        UnsignedInt('fraction'),
361        UnsignedInt('playcount'),
362*/
363                                input_loop_event_point_simple * point = new service_impl_t<input_loop_event_point_simple>();
364                                t_uint32 temp;
365                                chunk_reader.read_lendian_t(temp, p_abort); // cue_point_id: ignore
366                                chunk_reader.read_lendian_t(temp, p_abort); // type: currently known: 0 only
367                                if (temp != 0) {
368                                        pfc::string8 errmsg;
369                                        errmsg << "Unknown sampleloop type: " << temp;
370                                        throw exception_io_data(errmsg);
371                                }
372                                chunk_reader.read_lendian_t(temp, p_abort); // start
373                                point->to = temp;
374                                chunk_reader.read_lendian_t(temp, p_abort); // end
375                                point->from = temp;
376                                chunk_reader.read_lendian_t(temp, p_abort); // fraction
377                                if (temp != 0) {
378                                        pfc::string8 errmsg;
379                                        errmsg << "Unknown sampleloop fraction: " << temp;
380                                        throw exception_io_data(errmsg);
381                                }
382                                chunk_reader.read_lendian_t(temp, p_abort); // playcount
383                                point->maxrepeats = temp;
384                                m_points.add_item(point);
385                        }
386                }
387                if (m_points.get_count() == 0)
388                        return false;
389                try {
390                        open_path_helper(m_input, p_filehint, path, p_abort, p_from_redirect,p_skip_hints);
391                } catch (exception_io_not_found) {
392                        return false;
393                }
394                switch_input(m_input);
395                switch_points(m_points);
396                return true;
397        }
398        virtual void get_info(t_uint32 subsong, file_info & p_info,abort_callback & p_abort) {
399                get_input()->get_info(subsong, p_info, p_abort);
400                get_info_for_points(p_info, m_points, get_info_prefix(), get_sample_rate());
401        }
402};
403
404static input_loop_type_factory_t<input_loop_type_sampler> g_input_loop_type_sampler;
405
406#pragma region GUIDs
407// {C9E7AF50-FDF8-4a2f-99A6-8DE4D2B49D0C}
408FOOGUIDDECL const GUID input_loop_type::class_guid =
409{ 0xc9e7af50, 0xfdf8, 0x4a2f, { 0x99, 0xa6, 0x8d, 0xe4, 0xd2, 0xb4, 0x9d, 0xc } };
410
411// {CA8E32C1-1A2D-4679-87AB-03292A97D890}
412FOOGUIDDECL const GUID input_loop_type_base::class_guid =
413{ 0xca8e32c1, 0x1a2d, 0x4679, { 0x87, 0xab, 0x3, 0x29, 0x2a, 0x97, 0xd8, 0x90 } };
414
415// {2910A6A6-A12B-414f-971B-90A65F79439B}
416FOOGUIDDECL const GUID input_loop_event_point::class_guid =
417{ 0x2910a6a6, 0xa12b, 0x414f, { 0x97, 0x1b, 0x90, 0xa6, 0x5f, 0x79, 0x43, 0x9b } };
418
419//// {CFBEBA19-9B94-46b0-AA21-D2906B139EDE}
420FOOGUIDDECL const GUID input_loop_type_twofiles::class_guid =
421{ 0xcfbeba19, 0x9b94, 0x46b0, { 0xaa, 0x21, 0xd2, 0x90, 0x6b, 0x13, 0x9e, 0xde } };
422
423//// {566BCC79-7370-48c0-A7CB-5E47C4C17A86}
424FOOGUIDDECL const GUID input_loop_type_entry::class_guid =
425{ 0x566bcc79, 0x7370, 0x48c0, { 0xa7, 0xcb, 0x5e, 0x47, 0xc4, 0xc1, 0x7a, 0x86 } };
426
427#pragma endregion
428
Note: See TracBrowser for help on using the browser.