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