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