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