root/platform/foobar2000/trunk/foo_sli/foo_sli.cpp @ 27779

Revision 27779, 30.7 kB (checked in by topia, 4 years ago)

fix whitespace

Line 
1// foo_sli.cpp : Defines the exported functions for the DLL application.
2//
3
4// Licence: GPL v2 (check kirikiri 2 SDK License)
5
6/*
7see also:
8        * http://devdoc.kikyou.info/tvp/docs/kr2doc/contents/LoopTuner.html
9        * https://sv.kikyou.info/trac/kirikiri/browser/kirikiri2/trunk/license.txt
10        * https://sv.kikyou.info/trac/kirikiri/browser/kirikiri2/trunk/kirikiri2/src/core/sound/WaveLoopManager.h
11        * https://sv.kikyou.info/trac/kirikiri/browser/kirikiri2/trunk/kirikiri2/src/core/sound/WaveLoopManager.cpp
12        * https://sv.kikyou.info/trac/kirikiri/browser/kirikiri2/trunk/kirikiri2/src/core/sound/WaveSegmentQueue.h
13        * https://sv.kikyou.info/trac/kirikiri/browser/kirikiri2/trunk/kirikiri2/src/core/sound/WaveSegmentQueue.cpp
14*/
15
16#include "stdafx.h"
17
18#define SLI_FLAGS 16
19#define SLI_MIN_FLAG_VALUE 0
20#define SLI_MAX_FLAG_VALUE 9999
21
22namespace {
23        typedef pfc::array_staticsize_t<int> t_flags_array;
24        class loop_condition : public pfc::refcounted_object_root {
25        public:
26                const char * const confname;
27                const char * const symbol;
28                const bool is_valid;
29                loop_condition(const char * confname, const char * symbol, bool is_valid) :
30                  confname(confname), symbol(symbol), is_valid(is_valid) {}
31                virtual bool check(unsigned int a, unsigned int b) = 0;
32        };
33
34        class loop_condition_no : public loop_condition {
35        public:
36                loop_condition_no() : loop_condition("no", NULL, false) {};
37                virtual bool check(unsigned int a, unsigned int b) { return true; }
38        };
39
40#define DEFINE_LOOP_CONDITION(name, op) \
41        class loop_condition_ ##name : public loop_condition { \
42        public: \
43                loop_condition_ ##name () : loop_condition( #name, #op, true ) {}; \
44                virtual bool check(unsigned int a, unsigned int b) { return a op b; } \
45        };
46
47        DEFINE_LOOP_CONDITION(eq, ==);
48        DEFINE_LOOP_CONDITION(ne, !=);
49        DEFINE_LOOP_CONDITION(gt, >);
50        DEFINE_LOOP_CONDITION(ge, >=);
51        DEFINE_LOOP_CONDITION(lt, <);
52        DEFINE_LOOP_CONDITION(le, <=);
53#undef DEFINE_LOOP_CONDITION
54
55        class formula_operator : public pfc::refcounted_object_root {
56        public:
57                const char * const symbol;
58                const bool require_operand;
59                formula_operator(const char * symbol, bool require_operand) :
60                        symbol(symbol), require_operand(require_operand) {}
61                virtual unsigned int calculate(unsigned int original, unsigned int operand) = 0;
62        };
63
64        class formula_operator_set : public formula_operator {
65        public:
66                formula_operator_set() : formula_operator("=", true) {}
67                virtual unsigned int calculate(unsigned int original, unsigned int operand) {
68                        return operand;
69                }
70        };
71
72        class formula_operator_add : public formula_operator {
73        public:
74                formula_operator_add() : formula_operator("+=", true) {}
75                virtual unsigned int calculate(unsigned int original, unsigned int operand) {
76                        return pfc::min_t<unsigned int>(original + operand, SLI_MAX_FLAG_VALUE);
77                }
78        };
79
80        class formula_operator_sub : public formula_operator {
81        public:
82                formula_operator_sub() : formula_operator("-=", true) {}
83                virtual unsigned int calculate(unsigned int original, unsigned int operand) {
84                        if (original >= operand) return original - operand;
85                        else return 0;
86                }
87        };
88
89        class formula_operator_inc : public formula_operator {
90        public:
91                formula_operator_inc() : formula_operator("++", false) {}
92                virtual unsigned int calculate(unsigned int original, unsigned int operand) {
93                        if (original == SLI_MAX_FLAG_VALUE) return original;
94                        else return original + 1;
95                }
96        };
97
98        class formula_operator_dec : public formula_operator {
99        public:
100                formula_operator_dec() : formula_operator("--", true) {}
101                virtual unsigned int calculate(unsigned int original, unsigned int operand) {
102                        if (original >= 1) return original - 1;
103                        else return 0;
104                }
105        };
106
107        class formula_operand {
108        public:
109                bool indirect;
110                unsigned int value;
111                unsigned int resolve(t_flags_array & flags) {
112                        if (!indirect) return value;
113                        if (flags.get_size() < value)
114                                throw pfc::exception_overflow();
115                        return flags[value];
116                }
117        };
118       
119        struct sli_link {
120                unsigned __int64 from;
121                unsigned __int64 to;
122                bool smooth;
123                pfc::refcounted_object_ptr_t<loop_condition> condition;
124                unsigned int refvalue;
125                unsigned int condvar;
126
127                bool operator<(const sli_link& p_other) const {return this->from < p_other.from;}
128                bool operator<(const unsigned __int64 other) const {return this->from < other;}
129                bool operator>(const sli_link& p_other) const {return this->from > p_other.from;}
130                bool operator>(const unsigned __int64 other) const {return this->from > other;}
131                bool operator<=(const sli_link& p_other) const {return this->from <= p_other.from;}
132                bool operator<=(const unsigned __int64 other) const {return this->from <= other;}
133                bool operator>=(const sli_link& p_other) const {return this->from >= p_other.from;}
134                bool operator>=(const unsigned __int64 other) const {return this->from >= other;}
135        };
136
137        struct sli_label {
138                unsigned __int64 position;
139                pfc::string8 name;
140
141                bool operator<(const sli_label& p_other) const {return this->position < p_other.position;}
142                bool operator<(const unsigned __int64 other) const {return this->position < other;}
143                bool operator>(const sli_label& p_other) const {return this->position > p_other.position;}
144                bool operator>(const unsigned __int64 other) const {return this->position > other;}
145                bool operator<=(const sli_label& p_other) const {return this->position <= p_other.position;}
146                bool operator<=(const unsigned __int64 other) const {return this->position <= other;}
147                bool operator>=(const sli_label& p_other) const {return this->position >= p_other.position;}
148                bool operator>=(const unsigned __int64 other) const {return this->position >= other;}
149        };
150
151        struct sli_label_formula {
152                unsigned int flag;
153                pfc::refcounted_object_ptr_t<formula_operator> oper;
154                bool indirect;
155                unsigned int value;
156        };
157
158        class t_sli_link_list : public pfc::list_t<sli_link> {
159        public:
160                void sort() {
161                        return this->sort_t(pfc::compare_t<sli_link, sli_link>);
162                }
163                bool bsearch(unsigned __int64 from,t_size &p_index) {
164                        return this->bsearch_t(pfc::compare_t<sli_link, unsigned __int64>, from, p_index);
165                }
166        };
167
168        class t_sli_label_list : public pfc::list_t<sli_label> {
169        public:
170                void sort() {
171                        return this->sort_t(pfc::compare_t<sli_label, sli_label>);
172                }
173                bool bsearch(unsigned __int64 from,t_size &p_index) {
174                        return this->bsearch_t(pfc::compare_t<sli_label, unsigned __int64>, from, p_index);
175                }
176        };
177
178        bool parse_sli_entity(const char * & parseptr,pfc::string8 & name,pfc::string8 & value) {
179                char delimiter = '\0';
180                char tmp;
181                t_size ptr = 0;
182                while(tmp = parseptr[ptr], tmp && !isspace(tmp) && tmp != '=') ptr++;
183                if (!parseptr[ptr]) return false;
184                name.set_string(parseptr, ptr);
185                parseptr += ptr;
186                while (isspace(*parseptr)) parseptr++;
187                if (*parseptr != '=') return false;
188                parseptr++;
189                // check delimiter
190                if (*parseptr == '\'') {
191                        delimiter = *parseptr;
192                        parseptr++;
193                }
194                if (!*parseptr) false;
195
196                ptr = 0;
197                if (delimiter == '\0') {
198                        while (tmp = parseptr[ptr], tmp && !isspace(tmp) && tmp != ';') ptr++;
199                } else {
200                        while (tmp = parseptr[ptr], tmp && tmp != delimiter) ptr++;
201                }
202                if (!parseptr[ptr]) return false;
203                value.set_string(parseptr, ptr);
204                parseptr += ptr;
205                if (*parseptr == delimiter) parseptr++;
206                return true;
207        }
208
209        bool parse_sli_link(const char * & parseptr,sli_link &link) {
210                // must point '{' , which indicates start of the block.
211                if(*parseptr != '{') return false;
212                parseptr++;
213
214                while (*parseptr) {
215                        if (isspace(*parseptr)) {
216                                while (isspace(*parseptr)) parseptr++;
217                        } else if (*parseptr == '}') {
218                                break;
219                        } else {
220                                pfc::string8 name, value;
221                                if (!parse_sli_entity(parseptr, name, value)) return false;
222                                if (!pfc::stricmp_ascii(name, "From")) {
223                                        if (!pfc::string_is_numeric(value)) return false;
224                                        link.from = pfc::atoui64_ex(value, strlen(value));
225                                } else if (!pfc::stricmp_ascii(name, "To")) {
226                                        if (!pfc::string_is_numeric(value)) return false;
227                                        link.to = pfc::atoui64_ex(value, strlen(value));
228                                } else if (!pfc::stricmp_ascii(name, "Smooth")) {
229                                        if (!pfc::stricmp_ascii(value, "True")) {
230                                                link.smooth = true;
231                                        } else if (!pfc::stricmp_ascii(value, "False")) {
232                                                link.smooth = false;
233                                        } else if (!pfc::stricmp_ascii(value, "Yes")) {
234                                                link.smooth = true;
235                                        } else if (!pfc::stricmp_ascii(value, "No")) {
236                                                link.smooth = false;
237                                        } else {
238                                                // parse error
239                                                return false;
240                                        }
241                                } else if (!pfc::stricmp_ascii(name, "Condition")) {
242                                        if (!pfc::stricmp_ascii(value, "no")) {
243                                                link.condition = new loop_condition_no();
244                                        } else if (!pfc::stricmp_ascii(value, "eq")) {
245                                                link.condition = new loop_condition_eq();
246                                        } else if (!pfc::stricmp_ascii(value, "ne")) {
247                                                link.condition = new loop_condition_ne();
248                                        } else if (!pfc::stricmp_ascii(value, "gt")) {
249                                                link.condition = new loop_condition_gt();
250                                        } else if (!pfc::stricmp_ascii(value, "ge")) {
251                                                link.condition = new loop_condition_ge();
252                                        } else if (!pfc::stricmp_ascii(value, "lt")) {
253                                                link.condition = new loop_condition_lt();
254                                        } else if (!pfc::stricmp_ascii(value, "le")) {
255                                                link.condition = new loop_condition_le();
256                                        } else {
257                                                return false;
258                                        }
259                                } else if (!pfc::stricmp_ascii(name, "RefValue")) {
260                                        if (!pfc::string_is_numeric(value)) return false;
261                                        link.refvalue = pfc::clip_t(atoi(value), SLI_MIN_FLAG_VALUE, SLI_MAX_FLAG_VALUE);
262                                } else if (!pfc::stricmp_ascii(name, "CondVar")) {
263                                        if (!pfc::string_is_numeric(value)) return false;
264                                        link.condvar = pfc::clip_t(atoi(value), 0, SLI_FLAGS-1);
265                                } else {
266                                        return false;
267                                }
268                                while (isspace(*parseptr)) parseptr++;
269                                if (*parseptr != ';') return false;
270                                parseptr++;
271                        }
272                }
273
274                if (*parseptr != '}') return false;
275                parseptr++;
276                return true;
277        }
278       
279        bool parse_sli_label(const char * & parseptr,sli_label &label) {
280                // must point '{' , which indicates start of the block.
281                if(*parseptr != '{') return false;
282                parseptr++;
283
284                while (*parseptr) {
285                        if (isspace(*parseptr)) {
286                                while (isspace(*parseptr)) parseptr++;
287                        } else if (*parseptr == '}') {
288                                break;
289                        } else {
290                                pfc::string8 name, value;
291                                if (!parse_sli_entity(parseptr, name, value)) return false;
292                                if (!pfc::stricmp_ascii(name, "Position")) {
293                                        if (!pfc::string_is_numeric(value)) return false;
294                                        label.position = pfc::atoui64_ex(value, strlen(value));
295                                } else if (!pfc::stricmp_ascii(name, "Name")) {
296                                        label.name.set_string(value);
297                                } else {
298                                        return false;
299                                }
300                                while (isspace(*parseptr)) parseptr++;
301                                if (*parseptr != ';') return false;
302                                parseptr++;
303                        }
304                }
305
306                if (*parseptr != '}') return false;
307                parseptr++;
308                return true;
309        }
310       
311        bool parse_sli(const char * p_slitext,t_sli_link_list & p_outlinks,t_sli_label_list &p_outlabels) {
312                const char * parseptr = p_slitext;
313                if (!*parseptr) { return false; }
314                if (*parseptr != '#') {
315                        // v1
316                        const char * p_length = strstr(parseptr, "LoopLength=");
317                        const char * p_start = strstr(parseptr, "LoopStart=");
318                        if (!p_length || !p_start) return false;
319                        p_length += 11;
320                        p_start += 10;
321                        if (!pfc::char_is_numeric(*p_length) || !pfc::char_is_numeric(*p_start)) return false;
322                        sli_link link;
323                        link.smooth = false;
324                        link.condition = new loop_condition_no();
325                        link.refvalue = 0;
326                        link.condvar = 0;
327                        __int64 start = _atoi64(p_start);
328                        link.from = start + _atoi64(p_length);
329                        link.to = start;
330                        p_outlinks.add_item(link);
331
332                        return true;
333                }
334                if (!pfc::strcmp_partial(parseptr, "#2.00")) {
335                        // v2
336                        while (*parseptr) {
337                                if (*parseptr == '#') {
338                                        // FIXME: original source checks only beginning-of-line...
339                                        while (*parseptr && *parseptr != '\n') parseptr++;
340                                } else if (isspace(*parseptr)) {
341                                        while (isspace(*parseptr)) parseptr++;
342                                } else if (pfc::stricmp_ascii(parseptr, "Link") && !pfc::char_is_ascii_alpha(parseptr[4])) {
343                                        parseptr += 4;
344                                        while (isspace(*parseptr)) parseptr++;
345                                        if (!*parseptr) return false;
346                                        sli_link link;
347                                        if (!parse_sli_link(parseptr, link)) return false;
348                                        p_outlinks.add_item(link);
349                                } else if (pfc::stricmp_ascii(parseptr, "Label") && !pfc::char_is_ascii_alpha(parseptr[5])) {
350                                        parseptr += 5;
351                                        while (isspace(*parseptr)) parseptr++;
352                                        if (!*parseptr) return false;
353                                        sli_label label;
354                                        if (!parse_sli_label(parseptr, label)) return false;
355                                        p_outlabels.add_item(label);
356                                } else {
357                                        return false;
358                                }
359                        }
360                        return true;
361                }
362                return false;           
363        }
364
365        bool parse_sli_label_formula(const char * p_formula, sli_label_formula & p_out) {
366                const char * p = p_formula;
367                t_size ptr;
368                // starts with '['
369                if (*p != '[') return false;
370                p++;
371                ptr = 0;
372                while (pfc::char_is_numeric(p[ptr])) ptr++;
373                if (ptr == 0) return false;
374                p_out.flag = pfc::clip_t<unsigned int>(pfc::atoui_ex(p, ptr), 0, SLI_FLAGS-1);
375                p += ptr;
376                // after flag, should be ']'
377                if (*p != ']') return false;
378                p++;
379                bool sign = false;
380                switch (*p) {
381                        case '=':
382                                p_out.oper = new formula_operator_set();
383                                break;
384                        case '+':
385                                sign = true;
386                        case '-':
387                                if (*p == *(p+1)) {
388                                        if (sign)
389                                                p_out.oper = new formula_operator_inc();
390                                        else
391                                                p_out.oper = new formula_operator_dec();
392                                        p++;
393                                        break;
394                                } else if (*(p+1) == '=') {
395                                        if (sign)
396                                                p_out.oper = new formula_operator_add();
397                                        else
398                                                p_out.oper = new formula_operator_sub();
399                                        p++;
400                                        break;
401                                }
402                        default:
403                                // unknown operator
404                                return false;
405                }
406                p++;
407                if (!p_out.oper->require_operand) return true;
408                p_out.indirect = false;
409                if (*p == '[') {
410                        p_out.indirect = true;
411                        p++;
412                }
413                ptr = 0;
414                while (pfc::char_is_numeric(p[ptr])) ptr++;
415                if (ptr == 0) return false;
416                p_out.value = pfc::clip_t<unsigned int>(pfc::atoui_ex(p, ptr), SLI_MIN_FLAG_VALUE,
417                        (p_out.indirect ? SLI_FLAGS-1 : SLI_MAX_FLAG_VALUE));
418                p += ptr;
419                if (p_out.indirect) {
420                        if (*p != ']') return false;
421                        p++;
422                }
423                if (*p) return false;
424                return true;
425        }
426
427        void combine_audio_chunks(audio_chunk & p_first,const audio_chunk & p_second) {
428                if (p_first.is_empty()) {
429                        p_first = p_second;
430                        return;
431                }
432
433                // sanity check
434                if (p_first.get_sample_rate() != p_second.get_sample_rate() ||
435                        p_first.get_channel_config() != p_second.get_channel_config() ||
436                        p_first.get_channels() != p_second.get_channels()) {
437                        throw exception_unexpected_audio_format_change();
438                }
439                int nch = p_first.get_channels();
440                t_size first_samples = p_first.get_sample_count();
441                t_size offset = first_samples * nch;
442                t_size second_samples = p_second.get_sample_count();
443                t_size size = second_samples * nch;
444                p_first.set_data_size(offset + size);
445                pfc::memcpy_t(p_first.get_data()+offset,p_second.get_data(),size);
446                p_first.set_sample_count(first_samples + second_samples);
447        }
448
449        void do_crossfade(audio_sample * p_dest, const audio_sample * p_src1, const audio_sample * p_src2,
450                int nch, t_size samples, t_uint ratiostart, t_uint ratioend) {
451                audio_sample blend_step =
452                        (audio_sample)((ratioend - ratiostart) / 100.0 / samples);
453                audio_sample ratio = (audio_sample)ratiostart / 100;
454                while (samples) {
455                        for (int ch = nch-1; ch >= 0; ch--) {
456                                *p_dest = *p_src1 + (*p_src2 - *p_src1) * ratio;
457                                p_dest++; p_src1++; p_src2++;
458                        }
459                        samples--;
460                        ratio += blend_step;
461                }
462        }
463
464        void do_crossfade(audio_chunk & p_dest,t_size destpos,const audio_chunk & p_src1,t_size src1pos,
465                const audio_chunk & p_src2,t_size src2pos,t_size samples,t_uint ratiostart,t_uint ratioend)
466        {
467                if(samples == 0) return; // nothing to do
468
469                // sanity check
470                if (p_src1.get_srate() != p_src2.get_srate() ||
471                        p_src1.get_channel_config() != p_src2.get_channel_config() ||
472                        p_src1.get_channels() != p_src2.get_channels() ||
473                        p_dest.get_srate() != p_src1.get_srate() ||
474                        p_dest.get_channel_config() != p_src1.get_channel_config() ||
475                        p_dest.get_channels() != p_src1.get_channels()) {
476                        throw exception_unexpected_audio_format_change();
477                }
478                // length check
479                if (p_src1.get_sample_count() < (src1pos + samples) ||
480                        p_src2.get_sample_count() < (src2pos + samples)) {
481                        throw exception_io("p_src1 or p_src2 unsufficient sample");
482                }
483                p_dest.pad_with_silence(destpos + samples);
484                int nch = p_dest.get_channels();
485                audio_sample * pd = p_dest.get_data() + (destpos*nch);
486                const audio_sample * ps1 = p_src1.get_data() + (src1pos*nch);
487                const audio_sample * ps2 = p_src2.get_data() + (src2pos*nch);
488                do_crossfade(pd, ps1, ps2, nch, samples, ratiostart, ratioend);
489        }
490
491        void do_crossfade(audio_chunk & p_dest,t_size destpos,const audio_chunk & p_src,t_size srcpos,
492                t_size samples,t_uint ratiostart,t_uint ratioend)
493        {
494                if(samples == 0) return; // nothing to do
495
496                // sanity check
497                if (p_dest.get_srate() != p_src.get_srate() ||
498                        p_dest.get_channel_config() != p_src.get_channel_config() ||
499                        p_dest.get_channels() != p_src.get_channels()) {
500                        throw exception_unexpected_audio_format_change();
501                }
502                // length check
503                if (p_dest.get_sample_count() < (destpos + samples) ||
504                        p_src.get_sample_count() < (srcpos + samples)) {
505                        throw exception_io("p_dest or p_src unsufficient sample");
506                }
507                int nch = p_dest.get_channels();
508                audio_sample * pd = p_dest.get_data() + (destpos*nch);
509                const audio_sample * ps = p_src.get_data() + (srcpos*nch);
510                do_crossfade(pd, pd, ps, nch, samples, ratiostart, ratioend);
511        }
512
513        class format_samples_ex {
514        private:
515                pfc::string_fixed_t<193> m_buffer;
516        public:
517                format_samples_ex(t_uint64 p_samples,t_uint32 p_sample_rate,unsigned p_extra = 3) {
518                        m_buffer << pfc::format_time_ex(audio_math::samples_to_time(p_samples,p_sample_rate),p_extra);
519                        m_buffer << " (";
520                        m_buffer << pfc::format_int(p_samples);
521                        m_buffer << ")";
522                };
523                const char * get_ptr() const {return m_buffer;}
524                operator const char * () const {return m_buffer;}
525        };
526}
527
528
529class input_sli
530{
531public:
532        void open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
533                if (p_reason == input_open_info_write) throw exception_io_unsupported_format();//our input does not support retagging.
534                m_slifile = p_filehint;
535                m_path = p_path;
536                input_open_file_helper(m_slifile,p_path,p_reason,p_abort);//if m_file is null, opens file with appropriate privileges for our operation (read/write for writing tags, read-only otherwise).
537                {
538                        t_sli_link_list sli_links;
539                        t_sli_label_list sli_labels;
540                        bool is_utf8;
541                        text_file_loader::read(m_slifile,p_abort,m_slicontent,is_utf8);
542
543                        if (!parse_sli(m_slicontent, sli_links, sli_labels)) {
544                                throw exception_io_unsupported_format();
545                        }
546                        m_links = sli_links;
547                        m_links.sort();
548                        m_labels = sli_labels;
549                        m_labels.sort();
550                        {
551                                no_flags = true;
552                                t_size num = m_links.get_count();
553                                for ( t_size i = 0; i < num; i++ ) {
554                                        loop_condition * cond = m_links[i].condition.get_ptr();
555                                        if (cond && cond->is_valid) {
556                                                no_flags = false;
557                                                break;
558                                        }
559                                }
560                        }
561                        if (!no_flags) {
562                                m_flags.set_size_discard(SLI_FLAGS);
563                                pfc::fill_array_t(m_flags, 0);
564                        }
565                }
566                pfc::string8 p_content_path;
567                p_content_path.set_string(p_path, pfc::strlen_utf8(p_path) - 4); // .sli
568                in.open_path(NULL, p_content_path, p_abort, false, false);
569                file_info_impl p_info;
570                in.get_info(0, p_info, p_abort);
571                sample_rate = (t_uint32) p_info.info_get_int("samplerate");
572                crossfade_samples_half = MulDiv_Size(sample_rate, 25 /* ms */, 1000);
573        }
574
575        void get_info(file_info & p_info,abort_callback & p_abort) {
576                in.get_info(0, p_info, p_abort);
577                p_info.info_set("sli_content", m_slicontent);
578                for (t_size n = 0, m = m_links.get_count(); n < m; ++n ) {
579                        sli_link link = m_links[n];
580                        pfc::string8 name;
581                        pfc::string8 buf;
582                        t_size prefixlen;
583                        name << "sli_link_" << pfc::format_int(n, 2) << "_";
584                        prefixlen = name.get_length();
585
586                        name.truncate(prefixlen);
587                        name << "from";
588                        p_info.info_set(name, format_samples_ex(link.from, sample_rate));
589
590                        name.truncate(prefixlen);
591                        name << "to";
592                        p_info.info_set(name, format_samples_ex(link.to, sample_rate));
593
594                        name.truncate(prefixlen);
595                        name << "extra";
596                        if (link.smooth) {
597                                buf << "smooth";
598                        }
599                        if (link.condition->is_valid) {
600                                if (!buf.is_empty())
601                                        buf << "; ";
602                                buf << "cond:";
603                                buf << "[" << link.condvar << "]";
604                                buf << link.condition->symbol << link.refvalue;
605                        }
606
607                        if (!buf.is_empty())
608                                p_info.info_set(name, buf);
609                }
610                for (t_size n = 0, m = m_labels.get_count(); n < m; ++n ) {
611                        sli_label label = m_labels[n];
612                        pfc::string8 name;
613                        t_size prefixlen;
614                        name << "sli_label_" << pfc::format_int(n, 2) << "_";
615                        prefixlen = name.get_length();
616
617                        name.truncate(prefixlen);
618                        name << "pos";
619                        p_info.info_set(name, format_samples_ex(label.position, sample_rate));
620
621                        if (label.name) {
622                                if (label.name[0] == ':') {
623                                        name.truncate(prefixlen);
624                                        name << "formula";
625                                        p_info.info_set(name, label.name.get_ptr() + 1);
626                                } else {
627                                        name.truncate(prefixlen);
628                                        name << "name";
629                                        p_info.info_set(name, label.name);
630                                }
631                        }
632                }
633                // FIXME
634        }
635        t_filestats get_file_stats(abort_callback & p_abort) {return m_slifile->get_stats(p_abort);}
636
637        void decode_initialize(unsigned p_flags,abort_callback & p_abort) {
638                no_looping = (p_flags & input_flag_simpledecode) != 0;
639                in.open_decoding(0,p_flags,p_abort);
640                if (!no_looping) {
641                        cur = 0;
642                        schedule_nextevent(cur);
643                        check_event(p_abort);
644                }
645        }
646
647        t_size get_nearest_link(t_uint64 pos) {
648                t_size nums = m_links.get_count();
649                if (!nums) return infinite_size;
650                t_size index;
651                m_links.bsearch(pos, index);
652                if (index < nums) {
653                        return index;
654                } else {
655                        return infinite_size;
656                }
657        }
658
659        t_size get_nearest_label(t_uint64 pos) {
660                t_size nums = m_labels.get_count();
661                if (!nums) return infinite_size;
662                t_size index;
663                m_labels.bsearch(pos, index);
664                if (index < nums) {
665                        return index;
666                } else {
667                        return infinite_size;
668                }
669        }
670
671        inline t_uint64 link_get_process_start_samples(sli_link & link) {
672                if (!link.smooth) return link.from;
673                else if (link.from > crossfade_samples_half) return link.from - crossfade_samples_half;
674                else return 0;
675        }
676
677        void schedule_nextevent(t_uint64 pos) {
678                t_size index;
679                if (no_looping) return;
680               
681                index = get_nearest_link(pos);
682                if (index != infinite_size) {
683                        sli_link link = m_links[index];
684                        m_nextlinkpos = link_get_process_start_samples(link);
685                } else {
686                        m_nextlinkpos = infinite64;
687                }
688
689                if (no_flags) return;
690                index = get_nearest_label(pos);
691                if (index != infinite_size) {
692                        m_nextlabelpos = m_labels[index].position;
693                } else {
694                        m_nextlabelpos = infinite64;
695                }
696        }
697
698        inline void check_event(abort_callback & p_abort) {
699                if (no_looping) return;
700                if (m_nextlinkpos == cur) {
701                        // seeking (ignore crossfade)
702                        t_size num = m_links.get_count();
703                        t_size index = get_nearest_link(cur);
704                        while (index < num) {
705                                sli_link nextlink = m_links[index];
706                                if (nextlink.from != cur) break;
707                                loop_condition * cond = nextlink.condition.get_ptr();
708                                if (!cond || !cond->is_valid || cond->check(m_flags[nextlink.condvar], nextlink.refvalue)) {
709                                        in.seek(audio_math::samples_to_time(nextlink.to,sample_rate),p_abort);
710                                        cur = nextlink.to;
711                                        schedule_nextevent(cur);
712                                        check_event(p_abort);
713                                        return;
714                                }
715                                index++;
716                        }
717                }
718        }
719
720        inline bool process_labels(t_uint64 p_start, t_uint64 p_end) {
721                if (no_looping || no_flags) return false;
722                if (p_start == cur && m_nextlabelpos > p_end) {
723                        // use cached result and no match
724                        return false;
725                }
726                t_size num = m_labels.get_count();
727                t_size index = get_nearest_label(p_start);
728                while (index < num) {
729                        sli_label label = m_labels[index];
730                        if (label.position > p_end) break;
731                        if (!label.name.is_empty() && label.name[0] == ':') {
732                                sli_label_formula formula;
733                                if (parse_sli_label_formula(label.name.get_ptr() + 1, formula)) {
734                                        t_size flagnum = m_flags.get_size();
735                                        t_size flag = formula.flag;
736                                        unsigned int value = formula.value;
737                                        if (formula.indirect) {
738                                                value = m_flags[value];
739                                        }
740                                        m_flags[flag] = formula.oper->calculate(m_flags[flag], value);
741                                }
742                        }
743                        index++;
744                }
745                return true;
746        }
747
748        bool decode_run(audio_chunk & p_chunk,abort_callback & p_abort) {
749                if (no_looping) return in.run(p_chunk,p_abort);
750                check_event(p_abort);
751                bool succ = in.run(p_chunk,p_abort);
752                t_size samples = p_chunk.get_sample_count();
753                t_uint64 end = cur + samples;
754                if (m_nextlinkpos <= end) {
755                        process_link(succ, p_chunk, p_abort);
756                } else {
757                        if (process_labels(cur, end)) {
758                                schedule_nextevent(end);
759                        }
760                        cur = end;
761                }
762                check_event(p_abort);
763                return succ;
764        }
765
766        void process_link(bool & succ,audio_chunk & p_chunk,abort_callback & p_abort) {
767                // FIXME: be able to dispatch first matched event only
768                t_size num = m_links.get_count();
769                t_size index = get_nearest_link(cur);
770                t_size samples = p_chunk.get_sample_count();
771                t_uint64 end = cur + samples;
772                t_uint64 labelprocessed = cur;
773                while (index < num) {
774                        sli_link nextlink = m_links[index];
775                        if (link_get_process_start_samples(nextlink) > end) break;
776                        // process labels until nextlink
777                        process_labels(labelprocessed, nextlink.from);
778                        labelprocessed = nextlink.from;
779                        loop_condition * cond = nextlink.condition.get_ptr();
780                        if (cond && cond->is_valid && !cond->check(m_flags[nextlink.condvar], nextlink.refvalue)) {
781                                // condition failed. dispatch next event
782                                index++;
783                                continue;
784                        }
785                        // must not occured event on start point (processed in previous decode)
786                        PFC_ASSERT(cur != nextlink.from);
787                        // ok; continue..
788                        if (!nextlink.smooth) {
789                                // tiny seeking
790                                in.seek(audio_math::samples_to_time(nextlink.to,sample_rate),p_abort);
791                                // cut tail
792                                p_chunk.set_sample_count(pfc::downcast_guarded<t_size>(nextlink.from - cur));
793                                cur = end = nextlink.to;
794                                break;
795                        }
796                        // crossfading
797                        t_size cf_samples_first = pfc::downcast_guarded<t_size>(
798                                nextlink.from - link_get_process_start_samples(nextlink));
799                        t_size cf_samples_latter = crossfade_samples_half;
800                        t_size src_cf_center_samples = pfc::downcast_guarded<t_size>(nextlink.from - cur);
801                        t_size candidate = src_cf_center_samples + cf_samples_latter;
802                        t_size tmpsize;
803                        while (succ && candidate > p_chunk.get_sample_count()) {
804                                // more data
805                                audio_chunk_impl_temporary ptmp_chunk;
806                                succ = in.run(ptmp_chunk,p_abort);
807                                combine_audio_chunks(p_chunk, ptmp_chunk);
808                        }
809                        tmpsize = p_chunk.get_sample_count();
810                        if (candidate > tmpsize) {
811                                // encount eof
812                                cf_samples_latter = tmpsize - src_cf_center_samples;
813                        }
814                        t_uint64 dest_first = nextlink.to;
815                        if (cf_samples_first > dest_first) {
816                                cf_samples_first = static_cast<t_size>(dest_first);
817                                dest_first = 0;
818                        } else {
819                                dest_first -= cf_samples_first;
820                        }
821                        // seeking to destination
822                        in.seek(audio_math::samples_to_time(dest_first,sample_rate),p_abort);
823                        succ = true;
824                        audio_chunk_impl_temporary ptmp_dest_chunk;
825                        candidate = cf_samples_first + cf_samples_latter;
826                        while (succ && candidate > ptmp_dest_chunk.get_sample_count()) {
827                                // get data
828                                audio_chunk_impl_temporary ptmp_chunk;
829                                succ = in.run(ptmp_chunk,p_abort);
830                                combine_audio_chunks(ptmp_dest_chunk,ptmp_chunk);
831                        }
832                        t_size destsamples = ptmp_dest_chunk.get_sample_count();
833                        if (destsamples < candidate) {
834                                cf_samples_latter = destsamples - cf_samples_first;
835                        }
836                        t_size cf_samples = cf_samples_first + cf_samples_latter;
837                        do_crossfade(
838                                p_chunk, src_cf_center_samples-cf_samples_first,
839                                ptmp_dest_chunk, 0,
840                                cf_samples_first, 0, 50);
841                        do_crossfade(
842                                p_chunk, src_cf_center_samples,
843                                ptmp_dest_chunk, cf_samples_first,
844                                cf_samples_latter, 50, 100);
845                        p_chunk.set_sample_count(src_cf_center_samples+cf_samples_latter);
846                        audio_chunk_partial_ref latter = audio_chunk_partial_ref(
847                                ptmp_dest_chunk, cf_samples, destsamples - cf_samples);
848                        combine_audio_chunks(p_chunk, latter);
849                        samples = p_chunk.get_sample_count();
850                        cur = end = nextlink.to + destsamples - cf_samples_first;
851                        process_labels(nextlink.to, cur);
852                        break;
853                }
854                if (cur != end) {
855                        // dispatch labels (because all link not matched)
856                        process_labels(labelprocessed, end);
857                }
858                schedule_nextevent(end);
859        }
860
861        void decode_seek(double p_seconds,abort_callback & p_abort) {
862                in.seek(p_seconds,p_abort);
863                if (no_looping) return;
864                cur = audio_math::time_to_samples(p_seconds,sample_rate);
865                schedule_nextevent(cur);
866                check_event(p_abort);
867        }
868
869        bool decode_can_seek() {return in.can_seek();}
870        bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {
871                bool ret = in.get_dynamic_info(p_out, p_timestamp_delta);
872                if (!no_looping && (cur < m_lastupdatedynamic || cur - m_lastupdatedynamic > (sample_rate / 2))) {
873                        if (p_timestamp_delta == 0) {
874                                p_timestamp_delta = 0.5;
875                        } else {
876                                p_timestamp_delta = pfc::min_t<double>(0.5, p_timestamp_delta);
877                        }
878                        p_out.info_set("sli_current", format_samples_ex(cur, sample_rate));
879                        p_out.info_set("sli_wait_link_pos", (m_nextlinkpos != infinite64) ?
880                                format_samples_ex(m_nextlinkpos, sample_rate) : "None");
881                        if (!no_flags) {
882                                p_out.info_set("sli_wait_label_pos", (m_nextlabelpos != infinite64) ?
883                                        format_samples_ex(m_nextlabelpos, sample_rate) : "None");
884                                pfc::string8 buf;
885                                t_size num = m_flags.get_size();
886                                for ( t_size i = 0; i < num; i++ ) {
887                                        if (!buf.is_empty())
888                                                buf << "/";
889                                        buf << "[" << i << "]=" << m_flags[i];
890                                }
891                                p_out.info_set("sli_flags", buf);
892                        }
893                        m_lastupdatedynamic = cur;
894                        ret = true;
895                }
896                return ret;
897        }
898        bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {
899                return in.get_dynamic_info_track(p_out, p_timestamp_delta);
900        }
901
902        void decode_on_idle(abort_callback & p_abort) {in.on_idle(p_abort);}
903
904        void retag(const file_info & p_info,abort_callback & p_abort) {throw exception_io_unsupported_format();}
905
906        void set_logger(event_logger::ptr ptr) { in.set_logger(ptr);}
907
908        static bool g_is_our_content_type(const char * p_content_type) {return false;}
909        static bool g_is_our_path(const char * p_path,const char * p_extension) {return stricmp_utf8(p_extension, "sli") == 0;}
910
911private:
912        t_uint64 m_nextlabelpos;
913        t_uint64 m_nextlinkpos;
914        t_uint64 m_lastupdatedynamic;
915        service_ptr_t<file> m_slifile;
916        pfc::string8 m_path;
917        pfc::string8 m_slicontent;
918        t_uint64 cur;
919        t_size crossfade_samples_half;
920        bool no_looping;
921        bool no_flags;
922        t_sli_link_list m_links;
923        t_sli_label_list m_labels;
924        pfc::array_staticsize_t<int> m_flags;
925
926        t_uint32 sample_rate;
927
928        input_helper in;
929};
930
931
932static input_singletrack_factory_t<input_sli> g_input_sli_factory;
933
934
935DECLARE_COMPONENT_VERSION("sli repeator","0.1-test",NULL);
936DECLARE_FILE_TYPE("sli loop information file","*.SLI");
Note: See TracBrowser for help on using the browser.