]> git.tdb.fi Git - libs/demoscene.git/blob - source/sequencer.cpp
Allow specifying one-off actions inside segments
[libs/demoscene.git] / source / sequencer.cpp
1 #include <cmath>
2 #include <msp/core/algorithm.h>
3 #include <msp/core/maputils.h>
4 #include "animate.h"
5 #include "fadeoverlay.h"
6 #include "sequencer.h"
7 #include "stage.h"
8
9 using namespace std;
10 using namespace Msp;
11
12 Sequencer::Sequencer(float bpm):
13         started(false),
14         beat(0),
15         next_event(0)
16 {
17         set_beats_per_minute(bpm);
18
19         register_action_type<Animate>("animate");
20         register_action_type<FadeOverlay::Fade>("fade");
21         register_action_type<Stage::UseInView>("use_stage");
22         register_action_type<Stage::SetCamera>("set_camera");
23 }
24
25 void Sequencer::set_beats_per_minute(float bpm)
26 {
27         secs_per_beat = Time::min/bpm;
28 }
29
30 void Sequencer::add_static_action(Action &act)
31 {
32         static_actions.push_back(&act);
33 }
34
35 void Sequencer::add_action(Action &act, float sb, float eb)
36 {
37         if(sb<0 || sb>eb)
38                 throw invalid_argument("Sequencer::add_action");
39
40         Segment seg;
41         seg.action = &act;
42         seg.start_beat = sb;
43         seg.end_beat = eb;
44         auto i = find_if(segments, [=](const Segment &s){ return s.start_beat>sb; });
45         segments.insert(i, seg);
46 }
47
48 void Sequencer::start()
49 {
50         started = true;
51         end = begin = segments.begin();
52         for(auto a: static_actions)
53                 a->start(0, numeric_limits<float>::max());
54         float start_beat = beat;
55         beat = -1;
56         advance_to(start_beat);
57 }
58
59 void Sequencer::seek(float b)
60 {
61         if(b<beat)
62                 throw logic_error("Sequencer::seek");
63
64         if(started)
65                 advance_to(b);
66         else
67                 beat = b;
68 }
69
70 void Sequencer::tick(const Time::TimeDelta &dt)
71 {
72         if(!started)
73                 start();
74         if(begin==segments.end())
75                 return;
76
77         advance_to(beat+dt/secs_per_beat);
78 }
79
80 void Sequencer::advance_to(float target_beat)
81 {
82         while(target_beat>next_event)
83                 advance_to(next_event);
84
85         float prev_beat = beat;
86         beat = target_beat;
87
88         while(end!=segments.end() && end->start_beat<=beat)
89                 ++end;
90
91         int ibeat = static_cast<int>(floor(beat));
92         bool do_beat = (ibeat!=static_cast<int>(floor(prev_beat)));
93
94         for(auto a: static_actions)
95         {
96                 if(do_beat)
97                         a->beat(ibeat);
98                 a->tick(beat, beat-prev_beat);
99         }
100
101         for(auto i=begin; i!=end; ++i)
102         {
103                 if(i->start_beat>prev_beat)
104                         i->action->start(i->start_beat, i->end_beat-i->start_beat);
105                 if(do_beat)
106                         i->action->beat(ibeat);
107                 if(i->end_beat>=prev_beat)
108                 {
109                         float tick_beat = min(beat, i->end_beat);
110                         i->action->tick(tick_beat, tick_beat-max(prev_beat, i->start_beat));
111                         if(i->end_beat<=beat)
112                                 i->action->end(i->end_beat);
113                 }
114         }
115
116         while(begin!=end && begin->end_beat<=beat)
117                 ++begin;
118
119         if(target_beat>=next_event)
120                 update_next_event();
121
122         if(begin==segments.end())
123                 signal_finished.emit();
124 }
125
126 void Sequencer::update_next_event()
127 {
128         next_event = numeric_limits<float>::max();
129
130         if(end!=segments.end())
131                 next_event = min(next_event, end->start_beat);
132
133         for(auto i=begin; i!=end; ++i)
134                 if(i->end_beat>beat)
135                         next_event = min(next_event, i->end_beat);
136 }
137
138
139 Sequencer::Loader::Loader(Sequencer &s, Demo &d):
140         DataFile::ObjectLoader<Sequencer>(s),
141         demo(d)
142 {
143         add("define_action", &Loader::define_action);
144         add("instant", &Loader::instant);
145         add("segment", &Loader::segment);
146 }
147
148 void Sequencer::Loader::define_action(const string &n)
149 {
150         ActionDefLoader ldr(obj, demo);
151         load_sub_with(ldr);
152         obj.named_actions[n] = ldr.get_action();
153 }
154
155 void Sequencer::Loader::instant(float beat)
156 {
157         segment(beat, beat);
158 }
159
160 void Sequencer::Loader::segment(float start, float end)
161 {
162         SegmentLoader ldr(obj, start, end, demo);
163         load_sub_with(ldr);
164 }
165
166
167 Sequencer::ActionDefLoader::ActionDefLoader(Sequencer &s, Demo &d):
168         DataFile::ObjectLoader<Sequencer>(s),
169         demo(d)
170 {
171         for(const auto &t: obj.action_types)
172                 add(t.first, t.second->get_def_loader_func());
173 }
174
175 void Sequencer::ActionDefLoader::finished()
176 {
177         if(action)
178                 action->validate();
179 }
180
181
182 Sequencer::SegmentLoader::SegmentLoader(Sequencer &s, float b, float e, Demo &d):
183         ObjectLoader<Sequencer>(s),
184         start_beat(b),
185         end_beat(e),
186         demo(d)
187 {
188         add("apply", &SegmentLoader::apply);
189         for(const auto &t: obj.action_types)
190                 add(t.first, t.second->get_loader_func());
191 }
192
193 void Sequencer::SegmentLoader::apply(const string &n)
194 {
195         obj.add_action(*get_item(obj.named_actions, n), start_beat, end_beat);
196 }