]> git.tdb.fi Git - libs/demoscene.git/blob - source/sequencer.cpp
Move the Action class out of Sequencer
[libs/demoscene.git] / source / sequencer.cpp
1 #include <cmath>
2 #include <msp/core/algorithm.h>
3 #include "action.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 }