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