Anklang-0.3.0-1416-gfa8ce8d2 anklang-0.3.0-1416-gfa8ce8d2
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
track.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "trkn/tracktion.hh" // PCH include must come first
3
4#include "track.hh"
5#include "project.hh"
6#include "clip.hh"
7#include "plugin.hh"
8#include "server.hh"
9#include "main.hh"
10#include "jsonipc/jsonipc.hh"
11#include "internal.hh"
12
13namespace te = tracktion::engine;
14
15namespace Ase {
16
17// == TrackStateListener ==
19 TrackImpl &asetrack_;
20 juce::ValueTree track_state_;
21 juce::ValueTree volume_plugin_state_;
22 te::LevelMeasurer::Client meter_client_;
24 FastMemory::Block telemetry_block_;
25public:
26 struct Telemetry {
27 float dbspl0 = -100.0f;
28 float dbspl1 = -100.0f;
29 } &telemetry;
30 TrackStateListener (TrackImpl &asetrack) :
31 asetrack_ (asetrack), track_state_ (asetrack_.track_->state),
32 telemetry_block_ (SERVER->telemem_allocate (sizeof (Telemetry))),
33 telemetry (*new (telemetry_block_.block_start) Telemetry{})
34 {
35 track_state_.addListener (this);
36 if (auto t = asetrack_.track_.get()) {
37 if (auto at = dynamic_cast<te::AudioTrack *> (t)) {
38 if (auto lmp = at->getLevelMeterPlugin()) {
39 measurer_ = &lmp->measurer;
40 measurer_->addClient (meter_client_);
41 }
42 }
43 }
44 if (auto vol = asetrack_.volume_plugin()) {
45 volume_plugin_state_ = vol->state;
46 volume_plugin_state_.addListener (this);
47 }
48 }
49 ~TrackStateListener() override
50 {
51 if (volume_plugin_state_.isValid())
52 volume_plugin_state_.removeListener (this);
53 if (measurer_)
54 measurer_->removeClient (meter_client_);
55 track_state_.removeListener (this);
56 SERVER->telemem_release (telemetry_block_);
57 }
59 get_levels()
60 {
61 if (!measurer_)
62 return { -100.0f, -100.0f };
63 te::DbTimePair left = meter_client_.getAndClearAudioLevel (0);
64 te::DbTimePair right = meter_client_.getAndClearAudioLevel (1);
65 return { left.dB, right.dB };
66 }
67 void
68 valueTreePropertyChanged (juce::ValueTree &tree, const juce::Identifier &property) override
69 {
70 if (tree == track_state_) {
71 if (property == tracktion::engine::IDs::name)
72 asetrack_.emit_notify ("name");
73 if (property == tracktion::engine::IDs::mute)
74 asetrack_.emit_notify ("muted");
75 if (property == tracktion::engine::IDs::hidden)
76 asetrack_.emit_notify ("hidden");
77 if (property == tracktion::engine::IDs::solo)
78 asetrack_.emit_notify ("solo");
79 }
80 if (tree == volume_plugin_state_) {
81 if (property == tracktion::engine::IDs::volume) {
82 asetrack_.emit_notify ("volume_db");
83 asetrack_.emit_notify ("slider_pos");
84 }
85 if (property == tracktion::engine::IDs::pan)
86 asetrack_.emit_notify ("pan");
87 }
88 asetrack_.update_telemetry();
89 }
90 void
91 valueTreeChildAdded (juce::ValueTree &parent, juce::ValueTree &child) override
92 {
93 if (parent == track_state_ && te::Clip::isClipState (child))
94 asetrack_.emit_notify ("launcher_clips");
95 }
96 void
97 valueTreeChildRemoved (juce::ValueTree &parent, juce::ValueTree &child, int) override
98 {
99 if (parent == track_state_ && te::Clip::isClipState (child))
100 asetrack_.emit_notify ("launcher_clips");
101 }
102 void valueTreeParentChanged (juce::ValueTree&) override {}
103 void
104 valueTreeChildOrderChanged (juce::ValueTree &parent, int, int newIndex) override
105 {
106 // Reordering clips changes launcher_clips() return order, so emit like add/remove
107 if (parent == track_state_ && te::Clip::isClipState (parent.getChild (newIndex)))
108 asetrack_.emit_notify ("launcher_clips");
109 }
110};
111
112// == TrackImpl ==
113// Tracktion tracks whose getName() ignores setName()
114static bool
115trkn_fixed_name (const te::Track &track)
116{
117 return track.isMasterTrack() || track.isTempoTrack() || track.isMarkerTrack() || track.isArrangerTrack();
118}
119
120static std::string
121trkn_track_type (tracktion::Track &track)
122{
123 if (dynamic_cast<te::FolderTrack*> (&track))
124 return "Folder";
125 if (dynamic_cast<te::AudioTrack*> (&track))
126 return "Audio/Midi";
127 if (track.isTempoTrack())
128 return "Tempo";
129 if (track.isMarkerTrack())
130 return "Marker";
131 if (track.isChordTrack())
132 return "Chord";
133 if (track.isArrangerTrack())
134 return "Arranger";
135 if (track.isMasterTrack())
136 return "Master";
137 return "Unknown";
138}
139
140TrackImplP
141TrackImpl::from_trkn (tracktion::Track &t)
142{
143 TrackImpl *track = find_ase_obj<TrackImpl> (t);
144 if (track)
145 return shared_ptr_cast<TrackImpl> (track);
146 TrackImplP trackp = TrackImpl::make_shared (t);
147 return trackp;
148}
149
150TrackImpl::TrackImpl (tracktion::Track &track) :
151 project_ (find_ase_obj<ProjectImpl> (track.edit)),
152 track_ (&track), te_type_ (trkn_track_type (track))
153{
154 register_ase_obj (this, track);
155 state_listener_ = std::make_unique<TrackStateListener> (*this);
156 update_telemetry();
157}
158
159TrackImpl::TrackImpl (ProjectImpl &project, bool masterflag) :
160 project_ (&project)
161{
162 gadget_flags (masterflag ? MASTER_TRACK : 0);
163}
164
165TrackImpl::~TrackImpl()
166{
167 unregister_ase_obj (this, track_.get());
168 state_listener_ = nullptr;
169 assert_return (_parent() == nullptr);
170}
171
172String
173TrackImpl::name() const
174{
175 if (auto trackp = track_.get())
176 return trackp->getName().toStdString();
177 return "";
178}
179
180void
181TrackImpl::name (const std::string &n)
182{
183 if (auto trackp = track_.get())
184 if (!trkn_fixed_name (*trackp))
185 {
186 trackp->edit.getUndoManager().beginNewTransaction ("Set Track Name");
187 trackp->setName (juce::String (n));
188 }
189}
190
191ProjectImpl*
192TrackImpl::project () const
193{
194 return project_;
195}
196
197bool
199{
200 if (auto t = track_.get())
201 return t->isMuted (false);
202 return false;
203}
204
205void
207{
208 if (auto t = track_.get())
209 t->setMute (muted);
210}
211
212bool
214{
215 if (auto t = track_.get())
216 return t->isHidden ();
217 return false;
218}
219
220void
222{
223 if (auto t = track_.get())
224 t->setHidden (hidden);
225}
226
227bool
229{
230 if (auto t = track_.get())
231 return t->isSolo (false);
232 return false;
233}
234
235void
237{
238 if (auto t = track_.get())
239 t->setSolo (solo);
240}
241
242bool
244{
245 if (auto t = track_.get())
246 return t->isMasterTrack();
247 return false;
248}
249
250bool
252{
253 if (auto t = track_.get())
254 return t->isTempoTrack() || t->isMarkerTrack() || t->isChordTrack() ||
255 t->isArrangerTrack() || t->isMasterTrack();
256 return is_master();
257}
258
260TrackImpl::volume_plugin () const
261{
262 if (auto t = track_.get()) {
263 if (t->isMasterTrack())
264 return t->edit.getMasterVolumePlugin().get();
265 if (auto at = dynamic_cast<te::AudioTrack*> (t))
266 return at->getVolumePlugin();
267 }
268 return nullptr;
269}
270
271float
273{
274 if (auto vol = volume_plugin())
275 return trkn_volume_db (*vol);
276 return 0;
277}
278
279void
281{
282 if (auto vol = volume_plugin())
283 trkn_volume_db (*vol, db);
284}
285
286float
288{
289 if (auto vol = volume_plugin())
290 return trkn_slider_pos (*vol);
291 return 0;
292}
293
294void
296{
297 if (auto vol = volume_plugin())
298 trkn_slider_pos (*vol, pos);
299}
300
301float
303{
304 if (auto vol = volume_plugin())
305 return trkn_pan (*vol);
306 return 0;
307}
308
309void
311{
312 if (auto vol = volume_plugin())
313 trkn_pan (*vol, pan);
314}
315
318{
319 StringS props = { "launcher_clips" };
320 auto t = track_.get();
321 if (t && trkn_fixed_name (*t))
322 props.push_back ("name");
323 if (!dynamic_cast<te::AudioTrack*> (t) && !dynamic_cast<te::FolderTrack*> (t))
324 props.insert (props.end(), { "muted", "solo" });
325 if (!volume_plugin())
326 props.insert (props.end(), { "pan", "slider_pos", "volume_db" });
327 return props;
328}
329
330ClipS
332{
333 ClipS clips;
334 if (auto t = track_.get())
335 if (auto ct = dynamic_cast<te::ClipTrack*> (t))
336 for (auto *clip : ct->getClips())
337 if (dynamic_cast<te::MidiClip*> (clip))
338 if (auto clipimpl = ClipImpl::from_trkn (*clip))
339 clips.push_back (clipimpl);
340 return clips;
341}
342
343void
345{
346 // NOP setter: assignments are ignored; clip add/remove is driven by the
347 // backend and emits `notify:launcher_clips` via TrackStateListener.
348}
349
351TrackImpl::clip_index (const ClipImpl &clip) const
352{
353 if (auto t = track_.get())
354 if (auto ct = dynamic_cast<te::ClipTrack *> (t)) {
355 auto &clips = ct->getClips();
356 for (int i = 0; i < clips.size(); i++)
357 if (clips[i] == clip.clip_.get())
358 return i;
359 }
360 return -1;
361}
362
363int
364TrackImpl::clip_succession (const ClipImpl &clip) const
365{
366 ssize_t idx = clip_index (clip);
367 if (idx < 0)
368 return NONE;
369 if (auto t = track_.get())
370 if (auto ct = dynamic_cast<te::ClipTrack *> (t)) {
371 auto &clips = ct->getClips();
372 if (idx + 1 < clips.size())
373 return idx + 1;
374 }
375 return NONE;
376}
377
378DeviceP
380{
381 return nullptr;
382}
383
384MonitorP
385TrackImpl::create_monitor (int32 ochannel)
386{
387 return nullptr;
388}
389
390TelemetryFieldS
392{
393 TelemetryFieldS v;
394 return_unless (state_listener_, v);
395 auto &t = state_listener_->telemetry;
396 v.push_back (telemetry_field ("dbspl0", &t.dbspl0));
397 v.push_back (telemetry_field ("dbspl1", &t.dbspl1));
398 return v;
399}
400
401void
402TrackImpl::update_telemetry()
403{
404 return_unless (state_listener_);
405 auto &t = state_listener_->telemetry;
406 auto [left, right] = state_listener_->get_levels();
407 t.dbspl0 = left;
408 t.dbspl1 = right;
409}
410
411DeviceInfo
413{
414 return {};
415}
416
417void
419{
421 auto *track = track_.get();
422 if (track) {
423 // Remove from edit's track list
424 track_->edit.deleteTrack (track);
425 // Clear references
427 state_listener_ = nullptr;
428 }
430}
431
432// == TrackImpl::ClipScout ==
433TrackImpl::ClipScout::ClipScout() noexcept
434{
435 // PRNG initialization goes here
436}
437
439void
441{
442 indices_ = indices;
443}
444
446int
448{
449 if (previous >= 0 && previous < indices_.size()) {
450 last_ = previous;
451 return indices_[last_];
452 }
453 return NONE;
454}
455
457void
459{
460 last_ = -1;
461}
462
464void
466{
467 indices_ = other.indices_;
468}
469
470ClipP
471TrackImpl::create_midi_clip (const String &name, double start, double length)
472{
473 if (auto t = track_.get()) {
474 if (auto at = dynamic_cast<tracktion::AudioTrack *> (t)) {
475 const tracktion::TimeRange range (tracktion::TimePosition::fromSeconds (start), tracktion::TimeDuration::fromSeconds (length));
476 auto clip = at->insertMIDIClip (juce::String (name), range, nullptr);
477 if (clip)
478 return ClipImpl::from_trkn (*clip);
479 else
480 warning ("insertMIDIClip returned null");
481 }
482 else
483 warning ("dynamic_cast<AudioTrack*> failed for track type: %s", trkn_track_type (*t).c_str());
484 }
485 else
486 warning ("track_.get() returned null");
487 return nullptr;
488}
489
490ClipP
491TrackImpl::create_audio_clip (const String &name, double start, double length)
492{
493 if (auto t = track_.get()) {
494 if (auto ct = dynamic_cast<tracktion::ClipTrack *> (t)) {
495 const tracktion::TimeRange range (tracktion::TimePosition::fromSeconds (start), tracktion::TimeDuration::fromSeconds (length));
496 auto clip = ct->insertNewClip (tracktion::TrackItem::Type::wave, juce::String (name), range, nullptr);
497 if (clip)
498 return ClipImpl::from_trkn (*clip);
499 else
500 warning ("insertNewClip returned null");
501 }
502 else
503 warning ("dynamic_cast<ClipTrack*> failed for track type: %s", trkn_track_type (*t).c_str());
504 }
505 else
506 warning ("track_.get() returned null");
507 return nullptr;
508}
509
510PluginP
512{
513 if (auto t = track_.get()) {
514 auto plugin = t->edit.getPluginCache().createNewPlugin (type.c_str(), {});
515 if (plugin) {
516 t->pluginList.insertPlugin (plugin, 0, nullptr);
517 return PluginImpl::from_trkn (*plugin);
518 }
519 else
520 warning ("createNewPlugin returned null");
521 }
522 else
523 warning ("track_.get() returned null");
524 return nullptr;
525}
526
527PluginS
529{
530 PluginS plugins;
531 if (auto t = track_.get()) {
532 auto &pluginList = t->pluginList;
533 for (int i = 0; i < pluginList.size(); i++) {
534 if (auto *plugin = pluginList[i]) {
535 if (auto pluginimpl = PluginImpl::from_trkn (*plugin))
536 plugins.push_back (pluginimpl);
537 }
538 }
539 }
540 return plugins;
541}
542
543} // Ase
T c_str(T... args)
void emit_notify(const String &detail) override
Emit notify:detail, multiple notifications maybe coalesced if a CoalesceNotifies instance exists.
Definition object.cc:164
GadgetImpl * _parent() const override
Retrieve parent container.
Definition gadget.hh:25
void remove_self() override
Remove self from parent container.
Definition gadget.cc:86
Mimick tracktion::engine::SafeSelectable<> for tracktion::Selectable descendants.
Definition trkn-utils.hh:35
MIDI clip playback succession generator.
Definition track.hh:64
int advance(int previous)
Determine clip succession.
Definition track.cc:447
void reset()
Reset state (history), preserves succession order.
Definition track.cc:458
void setup(const std::vector< int > &indices)
Setup clip succession order.
Definition track.cc:440
void update(const ClipScout &other)
Assign new succession order, preserves history.
Definition track.cc:465
Ase::Track implementation.
Definition track.hh:10
float volume_db() const override
Get track volume in dB.
Definition track.cc:272
StringS read_only_properties() const override
List x()/x (v) properties that this object can't change, their setters ignore the value or warn.
Definition track.cc:317
ClipS launcher_clips() const override
Retrieve the list of clips that can be directly played; changes on notify:launcher_clips.
Definition track.cc:331
ClipP create_audio_clip(const String &name, double start, double length) override
Create a new audio clip on this track.
Definition track.cc:491
float pan() const override
Get track pan (-1.0 to 1.0).
Definition track.cc:302
bool is_master() const override
Flag set on the main output track.
Definition track.cc:243
DeviceP access_device() override
Retrieve Device handle for this track.
Definition track.cc:379
DeviceInfo device_info() override
Describe this Device type.
Definition track.cc:412
TelemetryFieldS telemetry() const override
Create signal monitor for an output channel.
Definition track.cc:391
PluginS list_plugins() override
List plugins on this track.
Definition track.cc:528
bool is_control_track() const override
Flag set on tempo/marker/chord/arranger/master tracks.
Definition track.cc:251
ClipP create_midi_clip(const String &name, double start, double length) override
Create a new MIDI clip on this track.
Definition track.cc:471
float slider_pos() const override
Get track volume fader position.
Definition track.cc:287
bool muted() const override
Check if track is muted.
Definition track.cc:198
PluginP create_plugin(const String &type) override
Create a new plugin on this track by type identifier.
Definition track.cc:511
bool hidden() const override
Check if track is hidden from view.
Definition track.cc:213
void remove_self() override
Remove self from parent container.
Definition track.cc:418
bool solo() const override
Check if track is soloed.
Definition track.cc:228
ValueTree getChild(int index) const
bool isValid() const noexcept
void addListener(Listener *listener)
void removeListener(Listener *listener)
virtual bool isMarkerTrack() const
virtual bool isTempoTrack() const
virtual bool isMasterTrack() const
virtual bool isArrangerTrack() const
T end(T... args)
T insert(T... args)
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:28
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:72
T left(T... args)
The Anklang C++ API namespace.
Definition api.hh:8
int32_t int32
A 32-bit signed integer.
Definition cxxaux.hh:27
void register_ase_obj(VirtualBase *ase_impl, tracktion::Selectable &selectable)
Helper: register AseImpl with a tracktion Selectable via ase_obj_.
AseType * find_ase_obj(tracktion::Selectable &selectable)
Helper: lookup AseType from tracktion Selectable via ase_obj_.
Definition trkn-utils.hh:61
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:34
void unregister_ase_obj(VirtualBase *ase_impl, tracktion::Selectable *selectable)
Helper: unregister AseImpl from a tracktion Selectable (selectable may be nullptr)
RangeType< TimePosition > TimeRange
T push_back(T... args)
Reference for an allocated memory block.
Definition memory.hh:89
typedef ssize_t