Allolib  1.0
C++ Components For Interactive Multimedia
al_SynthRecorder.hpp
1 #ifndef AL_SYNTHRECORDER_HPP
2 #define AL_SYNTHRECORDER_HPP
3 
4 /* Allocore --
5  Multimedia / virtual environment application class library
6 
7  Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology,
8  UCSB. Copyright (C) 2012-2018. The Regents of the University of California.
9  All rights reserved.
10 
11  Redistribution and use in source and binary forms, with or without
12  modification, are permitted provided that the following conditions are
13  met:
14 
15  Redistributions of source code must retain the above copyright
16  notice, this list of conditions and the following disclaimer.
17 
18  Redistributions in binary form must reproduce the above
19  copyright notice, this list of conditions and the following disclaimer in the
20  documentation and/or other materials provided with the
21  distribution.
22 
23  Neither the name of the University of California nor the names
24  of its contributors may be used to endorse or promote products derived from
25  this software without specific prior written permission.
26 
27  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
28  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
31  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
34  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
35  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
36  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
37  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 
39  File description:
40  SynthVoice sequence recorder
41 
42  File author(s):
43  AndrĂ©s Cabrera mantaraya36@gmail.com
44 */
45 
46 #include <chrono>
47 #include <fstream>
48 
49 #include "al/io/al_File.hpp"
50 #include "al/scene/al_SynthSequencer.hpp"
51 
52 namespace al {
53 
93  public:
94  typedef enum {
95  SEQUENCER_EVENT, // Events have duration (uses '@' command only)
96  SEQUENCER_TRIGGERS, // Store events as they were received trigger on and
97  // trigger off can be separate entries (uses '+' and
98  // '-' text commands)
99  CPP_FORMAT, // Saves code that can be copy-pasted into C++
100  NONE
101  } TextFormat;
102 
103  SynthRecorder(TextFormat format = SEQUENCER_EVENT) { mFormat = format; }
104 
105  void setDirectory(std::string path) {
106  if (!File::exists(path)) {
107  if (!Dir::make(path)) {
108  std::cout << "Error creating directory: " << path << std::endl;
109  }
110  }
111  mDirectory = path;
112  }
113 
114  void startRecord(std::string name = "", bool overwrite = false,
115  bool startOnEvent = true);
116 
117  void stopRecord();
118 
119  void setMaxRecordTime(al_sec maxTime) { mMaxRecordTime = maxTime; }
120 
121  void verbose(bool verbose) { mVerbose = true; }
122  bool verbose() { return mVerbose; }
123 
124  // std::string lastSequenceName();
125  // std::string lastSequenceSubDir();
126 
127  // std::string getCurrentPath() { return mPresetHandler->getCurrentPath();
128  //}
129 
130  SynthRecorder &operator<<(PolySynth &handler) {
131  registerPolySynth(handler);
132  return *this;
133  }
134 
135  void registerPolySynth(PolySynth &polySynth);
136 
144  static bool onTriggerOn(SynthVoice *voice, int offsetFrames, int id,
145  void *userData) {
146  std::chrono::high_resolution_clock::time_point now =
147  std::chrono::high_resolution_clock::now();
148  SynthRecorder *rec = static_cast<SynthRecorder *>(userData);
149  if (rec->mRecording) {
150  std::unique_lock<std::mutex> lk(rec->mSequenceLock);
151  if (rec->mStartOnEvent) {
152  rec->mSequenceStart = now;
153  rec->mStartOnEvent = false;
154  }
155  std::chrono::duration<double> diff = now - rec->mSequenceStart;
156  std::vector<ParameterField> pFields = voice->getTriggerParams();
157 
158  SynthEvent event;
159  event.type = SynthEventType::TRIGGER_ON;
160  event.id = voice->id();
161  event.time = diff.count();
162  event.synthName = demangle(typeid(*voice).name());
163  event.duration = -1;
164  event.pFields = pFields;
165  rec->mSequence.push_back(event);
166  if (rec->verbose()) {
167  std::cout << "trigger at " << event.time << ":" << event.synthName
168  << ":" << voice->id() << std::endl;
169  }
170  }
171  return true;
172  }
173 
179  static bool onTriggerOff(int id, void *userData) {
180  std::chrono::high_resolution_clock::time_point now =
181  std::chrono::high_resolution_clock::now();
182  SynthRecorder *rec = static_cast<SynthRecorder *>(userData);
183  if (rec->mRecording) {
184  std::unique_lock<std::mutex> lk(rec->mSequenceLock);
185  // if (rec->mStartOnEvent) {
186  // rec->mSequenceStart = now;
187  // rec->mStartOnEvent = false;
188  // }
189  std::chrono::duration<double> diff = now - rec->mSequenceStart;
190 
191  SynthEvent event;
192  event.type = SynthEventType::TRIGGER_OFF;
193  event.id = id;
194  event.time = diff.count();
195  // event.synthName = demangle(typeid (*voice).name() );
196  event.duration = -1;
197  // for (int i = 0; i < numFields; i++) {
198  // event.pFields.push_back(pFields[i]);
199  // }
200  rec->mSequence.push_back(event);
201  if (rec->verbose()) {
202  std::cout << "trigger OFF at " << event.time << ":" << event.synthName
203  << ":" << id << std::endl;
204  }
205  }
206  return true;
207  }
208 
209  private:
210  std::string mDirectory;
211  PolySynth *mPolySynth{nullptr};
212  TextFormat mFormat;
213  bool mVerbose;
214 
215  bool mOverwrite;
216  std::string mSequenceName;
217 
218  std::mutex mSequenceLock;
219 
220  bool mRecording{false};
221  bool mStartOnEvent{true};
222 
223  al_sec mMaxRecordTime;
224  std::chrono::high_resolution_clock::time_point mSequenceStart;
225  std::vector<SynthEvent> mSequence;
226 };
227 
228 // Implementation
229 
230 } // namespace al
231 
232 #endif // AL_SYNTHSEQUENCER_HPP
static bool make(const std::string &path)
Constructor. This does not attempt to open the directory.
static bool exists(const std::string &path)
Returns whether a file or directory exists.
A PolySynth manages polyphony and rendering of SynthVoice instances.
The SynthRecorder class records the events arriving at a PolySynth.
static bool onTriggerOn(SynthVoice *voice, int offsetFrames, int id, void *userData)
onTriggerOn callback for trigger on events
static bool onTriggerOff(int id, void *userData)
onTriggerOff callback for trigger off events
The SynthVoice class.
void id(int idValue)
Set the id for this voice.
virtual int getTriggerParams(float *pFields, int maxParams=-1)
Get this instance's parameter fields.
Definition: al_App.hpp:23