Allolib  1.0
C++ Components For Interactive Multimedia
al_SynthSequencer.hpp
1 
2 #ifndef AL_SYNTHSEQUENCER_HPP
3 #define AL_SYNTHSEQUENCER_HPP
4 
5 /* Allolib --
6  Multimedia / virtual environment application class library
7 
8  Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, UCSB.
9  Copyright (C) 2012-2018. The Regents of the University of California.
10  All rights reserved.
11 
12  Redistribution and use in source and binary forms, with or without
13  modification, are permitted provided that the following conditions are
14  met:
15 
16  Redistributions of source code must retain the above copyright
17  notice, this list of conditions and the following disclaimer.
18 
19  Redistributions in binary form must reproduce the above
20  copyright notice, this list of conditions and the following disclaimer in the
21  documentation and/or other materials provided with the
22  distribution.
23 
24  Neither the name of the University of California nor the names
25  of its contributors may be used to endorse or promote products derived from
26  this software without specific prior written permission.
27 
28  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
29  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
32  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
35  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
36  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
37  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
38  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 
40  File description:
41  Event Voice and Sequencer
42 
43  File author(s):
44  AndrĂ©s Cabrera mantaraya36@gmail.com
45 */
46 
47 #include <atomic>
48 #include <cassert>
49 #include <functional>
50 #include <list>
51 #include <memory>
52 #include <mutex>
53 #include <string>
54 #include <thread>
55 #include <vector>
56 
57 #include "al/graphics/al_Graphics.hpp"
58 #include "al/io/al_AudioIOData.hpp"
59 #include "al/io/al_File.hpp"
60 #include "al/scene/al_PolySynth.hpp"
61 #include "al/ui/al_Parameter.hpp"
62 
63 namespace al {
64 
70 public:
72 
74 
75  typedef enum { EVENT_VOICE, EVENT_PFIELDS, EVENT_TEMPO } EventType;
76 
77  double startTime{0};
78  double duration{-1};
79  int offsetCounter{0}; // To offset event within audio buffer
80 
81  EventType type{EVENT_VOICE};
82 
83  typedef struct {
84  std::string name; // instrument name
85  std::vector<ParameterField> pFields;
86  } ParamFields;
87 
88  SynthVoice *voice{nullptr};
89  ParamFields fields;
90  float tempo;
91  int voiceId;
92 };
93 
94 enum SynthEventType { TRIGGER_ON, TRIGGER_OFF, PARAMETER_CHANGE };
95 
96 struct SynthEvent {
97  std::string synthName;
98  double time;
99  int id;
100  double duration = -1;
101  std::vector<ParameterField> pFields;
102  SynthEventType type;
103 };
104 
177 public:
178  SynthSequencer(TimeMasterMode masterMode = TimeMasterMode::TIME_MASTER_CPU) {
179  mMasterMode = masterMode;
180  mInternalSynth = std::make_unique<PolySynth>(masterMode);
181  registerSynth(*mInternalSynth.get());
182  }
183 
184  SynthSequencer(PolySynth &synth) { registerSynth(synth); }
185 
186  ~SynthSequencer() {
187  stopSequence();
188  if (mCpuThread) {
189  mCpuThread->join();
190  }
191  }
192 
194  void render(AudioIOData &io);
195 
197  void render(Graphics &g);
198 
199  void update(double dt);
200 
203  void setGraphicsFrameRate(float fps) { mFps = fps; }
204 
218  template <class TSynthVoice>
219  TSynthVoice &add(double startTime, double duration = -1);
220 
224  template <class TSynthVoice>
225  void addVoice(TSynthVoice *voice, double startTime, double duration = -1);
226 
231  template <class TSynthVoice>
232  void addVoiceFromNow(TSynthVoice *voice, double startTime,
233  double duration = -1);
234 
242  static void audioCB(AudioIOData &io) { io.user<SynthSequencer>().render(io); }
243 
247  void print();
248 
249  bool playing() { return mPlaying; }
250 
251  bool verbose() { return mVerbose; }
252  void verbose(bool verbose) { mVerbose = verbose; }
253 
254  void setTempo(float tempo) { mNormalizedTempo = tempo / 60.; }
255 
256  bool playSequence(std::string sequenceName = "", float startTime = 0.0f);
257 
258  void stopSequence();
259  void setTime(float newTime);
260 
261  void setDirectory(std::string directory);
262 
263  std::string buildFullPath(std::string sequenceName);
264 
265  std::list<SynthSequencerEvent> loadSequence(std::string sequenceName,
266  double timeOffset = 0,
267  double timeScale = 1.0);
268 
272  void playEvents(std::list<SynthSequencerEvent> events,
273  double timeOffset = 0.1);
274 
275  std::vector<std::string> getSequenceList();
276 
277  double getSequenceDuration(std::string sequenceName);
278 
279  PolySynth &synth() { return *mPolySynth; }
280 
281  // Callbacks
282  void registerTimeChangeCallback(std::function<void(float)> func,
283  float minTimeDeltaSec = 0);
284 
285  void registerSequenceBeginCallback(std::function<void(std::string)> func);
296  void registerSequenceEndCallback(std::function<void(std::string)> func);
297 
298  void registerSynth(PolySynth &synth);
299 
300  void operator<<(PolySynth &synth) { return registerSynth(synth); }
301 
302 private:
303  PolySynth *mPolySynth;
304  std::unique_ptr<PolySynth> mInternalSynth;
305 
306  std::string mDirectory{"."};
307  bool mVerbose{false};
308  std::string mLastSequencePlayed;
309 
310  double mFps{0}; // graphics frames per second
311 
312  unsigned int mNextEvent{0};
313  std::list<SynthSequencerEvent>
314  mEvents; // List of events sorted by start time.
315  std::mutex mEventLock;
316  std::mutex mLoadingLock;
317  bool mPlaying{false};
318 
319  TimeMasterMode mMasterMode{TimeMasterMode::TIME_MASTER_AUDIO};
320  double mMasterTime{0.0};
321  double mPlaybackStartTime{0.0};
322 
323  double mNormalizedTempo{1.0}; // Linearly normalized inverted around 60 bpm
324  // (1.0 = 60bpm, 0.5 = 120 bpm)
325 
326  // Time change callback
327  std::vector<std::pair<std::function<void(float)>, float>>
328  mTimeChangeCallbacks;
329  // float mTimeChangeMinTimeDelta = 0;
330  std::vector<double> mTimeAccumCallbackNs; // Accumulator for tirggering
331  // time change callback.
332 
333  std::vector<std::function<void(std::string sequenceName)>>
334  mSequenceBeginCallbacks;
335  std::vector<std::function<void(std::string sequenceName)>>
336  mSequenceEndCallbacks;
337 
338  // CPU processing thread. Used when TIME_MASTER_CPU
339  std::shared_ptr<std::thread> mCpuThread;
340 
341  void processEvents(double blockStartTime, double fps);
342 };
343 
344 // Implementations -------------
345 
346 template <class TSynthVoice>
347 TSynthVoice &SynthSequencer::add(double startTime, double duration) {
348  TSynthVoice *newVoice = mPolySynth->getVoice<TSynthVoice>();
349  if (newVoice) {
350  addVoice<TSynthVoice>(newVoice, startTime, duration);
351  }
352  return *newVoice;
353 }
354 
355 template <class TSynthVoice>
356 void SynthSequencer::addVoice(TSynthVoice *voice, double startTime,
357  double duration) {
358  std::list<SynthSequencerEvent>::iterator insertedEvent;
359  std::unique_lock<std::mutex> lk(mEventLock);
360  // Insert into event list, sorted.
361  auto position = mEvents.begin();
362  while (position != mEvents.end() && position->startTime < startTime) {
363  position++;
364  }
365  insertedEvent = mEvents.insert(position, SynthSequencerEvent());
366  insertedEvent->startTime = startTime;
367  insertedEvent->duration = duration;
368  insertedEvent->voice = voice;
369 }
370 
371 template <class TSynthVoice>
372 void SynthSequencer::addVoiceFromNow(TSynthVoice *voice, double startTime,
373  double duration) {
374  float triggerOffset = 0.05;
375  addVoice(voice, mMasterTime + startTime + triggerOffset, duration);
376 }
377 
378 } // namespace al
379 
380 #endif // AL_SYNTHSEQUENCER_HPP
void * user() const
Get pointer to user data.
Interface for loading fonts and rendering text.
Definition: al_Graphics.hpp:63
A PolySynth manages polyphony and rendering of SynthVoice instances.
TSynthVoice * getVoice(bool forceAlloc=false)
Get a reference to a voice.
SynthSequencerEvent class.
Event Sequencer triggering audio visual "notes".
void setGraphicsFrameRate(float fps)
void addVoice(TSynthVoice *voice, double startTime, double duration=-1)
void playEvents(std::list< SynthSequencerEvent > events, double timeOffset=0.1)
play the event list provided all other events in list are discarded
void render(AudioIOData &io)
Insert this function within the audio callback.
void render(Graphics &g)
Insert this function within the graphics callback.
void registerSequenceEndCallback(std::function< void(std::string)> func)
registerSequenceEndCallback
TSynthVoice & add(double startTime, double duration=-1)
insert an event in the sequencer
static void audioCB(AudioIOData &io)
Basic audio callback for quick prototyping.
void addVoiceFromNow(TSynthVoice *voice, double startTime, double duration=-1)
void print()
print current sequence
The SynthVoice class.
Definition: al_App.hpp:23