1 /*
2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #ifndef SC_CLOCK_INCLUDED
22 #define SC_CLOCK_INCLUDED
23
24 #include "PyrKernel.h"
25 #include "PyrSched.h"
26 #include "GC.h"
27 #include "PyrPrimitive.h"
28 #include "PyrSymbol.h"
29
30 // heaps use an integer timestamp to ensure stable heap order
31 struct PyrHeap : PyrObjectHdr {
32 PyrSlot count; // stability count
33 PyrSlot slots[0]; // slots
34 };
35
36 class TempoClock {
37 public:
38 TempoClock(VMGlobals* inVMGlobals, PyrObject* inTempoClockObj, double inTempo, double inBaseBeats,
39 double inBaseSeconds);
~TempoClock()40 virtual ~TempoClock() {}
41 void StopReq();
42 void Stop();
StopAndDelete()43 void StopAndDelete() {
44 Stop();
45 delete this;
46 }
47
48 void* Run();
49
50 void Add(double inBeats, PyrSlot* inTask);
51 void SetTempoAtBeat(double inTempo, double inBeats);
52 void SetTempoAtTime(double inTempo, double inSeconds);
53 void SetAll(double inTempo, double inBeats, double inSeconds);
54 double ElapsedBeats();
55 void Clear();
56 // void Flush();
GetTempo() const57 double GetTempo() const { return mTempo; }
GetBeatDur() const58 double GetBeatDur() const { return mBeatDur; }
BeatsToSecs(double beats) const59 virtual double BeatsToSecs(double beats) const { return (beats - mBaseBeats) * mBeatDur + mBaseSeconds; }
SecsToBeats(double secs) const60 virtual double SecsToBeats(double secs) const { return (secs - mBaseSeconds) * mTempo + mBaseBeats; }
61 void Dump();
62
GetAll()63 static TempoClock* GetAll() { return sAll; }
GetNext() const64 TempoClock* GetNext() const { return mNext; }
InitAll()65 static void InitAll() { sAll = 0; }
66
67 protected:
68 VMGlobals* g;
69 PyrObject* mTempoClockObj;
70 PyrHeap* mQueue;
71
72 double mTempo; // beats per second
73 double mBeatDur; // 1/tempo
74 double mBeats; // beats
75 double mBaseSeconds;
76 double mBaseBeats;
77 bool mRun;
78 SC_Thread mThread;
79 condition_variable_any mCondition;
80
81 private:
82 TempoClock *mPrev, *mNext;
83 static TempoClock* sAll;
84 };
85
86 // Clock primitives
prClock_New(struct VMGlobals * g,int numArgsPushed)87 template <typename Clock> int prClock_New(struct VMGlobals* g, int numArgsPushed) {
88 PyrSlot* a = g->sp - 3;
89 PyrSlot* b = g->sp - 2;
90 PyrSlot* c = g->sp - 1;
91 PyrSlot* d = g->sp;
92
93 double tempo;
94 int err = slotDoubleVal(b, &tempo);
95 if (err)
96 tempo = 1.;
97 if (tempo <= 0.) {
98 error("negative tempo %g is invalid\n", tempo);
99 SetPtr(slotRawObject(a)->slots + 1, NULL);
100 return errFailed;
101 }
102
103 double beats;
104 err = slotDoubleVal(c, &beats);
105 if (err)
106 beats = 0.;
107
108 double seconds;
109 err = slotDoubleVal(d, &seconds);
110 if (err) {
111 err = slotDoubleVal(&g->thread->seconds, &seconds);
112 if (err)
113 return err;
114 }
115
116 try {
117 Clock* clock = new Clock(g, slotRawObject(a), tempo, beats, seconds);
118 SetPtr(slotRawObject(a)->slots + 1, clock);
119 } catch (int err) {
120 return err;
121 }
122
123 return errNone;
124 }
125
prClock_SetBeats(struct VMGlobals * g,int numArgsPushed)126 template <typename Clock> int prClock_SetBeats(struct VMGlobals* g, int numArgsPushed) {
127 PyrSlot* a = g->sp - 1;
128 PyrSlot* b = g->sp;
129
130 Clock* clock = (Clock*)slotRawPtr(&slotRawObject(a)->slots[1]);
131 if (!clock) {
132 error("clock is not running.\n");
133 return errFailed;
134 }
135
136 double beats, seconds;
137 int err;
138
139 err = slotDoubleVal(b, &beats);
140 if (err)
141 return errWrongType;
142
143 err = slotDoubleVal(&g->thread->seconds, &seconds);
144 if (err)
145 return errWrongType;
146
147 clock->SetAll(clock->GetTempo(), beats, seconds);
148
149 return errNone;
150 }
151
prClock_SetTempoAtBeat(struct VMGlobals * g,int numArgsPushed)152 template <typename Clock> int prClock_SetTempoAtBeat(struct VMGlobals* g, int numArgsPushed) {
153 PyrSlot* a = g->sp - 2;
154 PyrSlot* b = g->sp - 1;
155 PyrSlot* c = g->sp;
156
157 Clock* clock = (Clock*)slotRawPtr(&slotRawObject(a)->slots[1]);
158 if (!clock) {
159 error("clock is not running.\n");
160 return errFailed;
161 }
162 if (clock->GetTempo() <= 0.f) {
163 error("cannot set negative tempo from this method. Use 'etempo_' instead.\n");
164 // prTempoClock_SetTempoAtTime can be used.
165 return errFailed;
166 }
167
168 double tempo, beat;
169 int err = slotDoubleVal(b, &tempo);
170 if (err)
171 return errWrongType;
172 if (tempo <= 0.) {
173 error("negative tempo %g is invalid. Use 'etempo_' instead.\n", tempo);
174 // prTempoClock_SetTempoAtTime can be used.
175 return errFailed;
176 }
177
178 err = slotDoubleVal(c, &beat);
179 if (err)
180 return errWrongType;
181
182 clock->SetTempoAtBeat(tempo, beat);
183
184 return errNone;
185 }
186
prClock_SetAll(struct VMGlobals * g,int numArgsPushed)187 template <typename Clock> int prClock_SetAll(struct VMGlobals* g, int numArgsPushed) {
188 PyrSlot* a = g->sp - 3;
189 PyrSlot* b = g->sp - 2;
190 PyrSlot* c = g->sp - 1;
191 PyrSlot* d = g->sp;
192
193 Clock* clock = (Clock*)slotRawPtr(&slotRawObject(a)->slots[1]);
194 if (!clock) {
195 error("clock is not running.\n");
196 return errFailed;
197 }
198
199 double tempo, beat, secs;
200 int err = slotDoubleVal(b, &tempo);
201 if (err)
202 return errWrongType;
203
204 err = slotDoubleVal(c, &beat);
205 if (err)
206 return errWrongType;
207
208 err = slotDoubleVal(d, &secs);
209 if (err)
210 return errWrongType;
211
212 clock->SetAll(tempo, beat, secs);
213
214 return errNone;
215 }
216
prClock_SetTempoAtTime(struct VMGlobals * g,int numArgsPushed)217 template <typename Clock> int prClock_SetTempoAtTime(struct VMGlobals* g, int numArgsPushed) {
218 PyrSlot* a = g->sp - 2;
219 PyrSlot* b = g->sp - 1;
220 PyrSlot* c = g->sp;
221
222 Clock* clock = (Clock*)slotRawPtr(&slotRawObject(a)->slots[1]);
223 if (!clock) {
224 error("clock is not running.\n");
225 return errFailed;
226 }
227
228 double tempo, sec;
229 int err = slotDoubleVal(b, &tempo);
230 if (err)
231 return errWrongType;
232
233 err = slotDoubleVal(c, &sec);
234 if (err)
235 return errWrongType;
236
237 clock->SetTempoAtTime(tempo, sec);
238
239 return errNone;
240 }
241
242
DurToFloat(DurationType dur)243 template <typename DurationType> inline double DurToFloat(DurationType dur) {
244 using namespace std::chrono;
245 seconds secs = duration_cast<seconds>(dur);
246
247 return duration_cast<duration<double, std::ratio<1>>>(dur).count();
248 }
249
250 #endif
251