Anklang-0.3.0.dev1049+gbe9c73bf anklang-0.3.0.dev1049+gbe9c73bf
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_;
23 te::LevelMeasurer *measurer_ = nullptr;
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 if (auto vol = at->getVolumePlugin()) {
43 volume_plugin_state_ = vol->state;
44 volume_plugin_state_.addListener (this);
45 }
46 }
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");
83 if (property == tracktion::engine::IDs::pan)
84 asetrack_.emit_notify ("pan");
85 }
86 asetrack_.update_telemetry();
87 }
88 void
89 valueTreeChildAdded (juce::ValueTree &parent, juce::ValueTree &child) override
90 {
91 if (parent == track_state_ && te::Clip::isClipState (child))
92 asetrack_.emit_notify ("launcher_clips");
93 }
94 void
95 valueTreeChildRemoved (juce::ValueTree &parent, juce::ValueTree &child, int) override
96 {
97 if (parent == track_state_ && te::Clip::isClipState (child))
98 asetrack_.emit_notify ("launcher_clips");
99 }
100 void valueTreeParentChanged (juce::ValueTree&) override {}
101 void
102 valueTreeChildOrderChanged (juce::ValueTree &parent, int, int newIndex) override
103 {
104 // Reordering clips changes launcher_clips() return order, so emit like add/remove
105 if (parent == track_state_ && te::Clip::isClipState (parent.getChild (newIndex)))
106 asetrack_.emit_notify ("launcher_clips");
107 }
108};
109
110// == TrackImpl ==
111static std::string
112trkn_track_type (tracktion::Track &track)
113{
114 if (dynamic_cast<te::FolderTrack*> (&track))
115 return "Folder";
116 if (dynamic_cast<te::AudioTrack*> (&track))
117 return "Audio/Midi";
118 if (track.isTempoTrack())
119 return "Tempo";
120 if (track.isMarkerTrack())
121 return "Marker";
122 if (track.isChordTrack())
123 return "Chord";
124 if (track.isArrangerTrack())
125 return "Arranger";
126 if (track.isMasterTrack())
127 return "Master";
128 return "Unknown";
129}
130
131TrackImplP
132TrackImpl::from_trkn (tracktion::Track &t)
133{
134 TrackImpl *track = find_ase_obj<TrackImpl> (t);
135 if (track)
136 return shared_ptr_cast<TrackImpl> (track);
137 TrackImplP trackp = TrackImpl::make_shared (t);
138 return trackp;
139}
140
141TrackImpl::TrackImpl (tracktion::Track &track) :
142 project_ (find_ase_obj<ProjectImpl> (track.edit)),
143 track_ (&track), te_type_ (trkn_track_type (track))
144{
145 register_ase_obj (this, track);
146 state_listener_ = std::make_unique<TrackStateListener> (*this);
147 update_telemetry();
148}
149
150TrackImpl::TrackImpl (ProjectImpl &project, bool masterflag) :
151 project_ (&project)
152{
153 gadget_flags (masterflag ? MASTER_TRACK : 0);
154}
155
156TrackImpl::~TrackImpl()
157{
158 unregister_ase_obj (this, track_.get());
159 state_listener_ = nullptr;
160 assert_return (_parent() == nullptr);
161}
162
163String
164TrackImpl::name() const
165{
166 if (auto trackp = track_.get())
167 return trackp->getName().toStdString();
168 return "";
169}
170
171void
172TrackImpl::name (const std::string &n)
173{
174 if (auto trackp = track_.get())
175 trackp->setName (juce::String (n));
176}
177
178ProjectImpl*
179TrackImpl::project () const
180{
181 return project_;
182}
183
184String
185TrackImpl::fallback_name() const
186{
187 if (is_master())
188 return "Master";
189 if (auto project_ = project()) {
190 ssize_t i = project_->track_index (*this);
191 return string_format ("Track %u", i >= 0 ? i + 1 : i);
192 }
193 return DeviceImpl::fallback_name();
194}
195
196void
197TrackImpl::midi_channel (int32 midichannel)
198{
199 midichannel = CLAMP (midichannel, 0, 16);
200 return_unless (midichannel != midi_channel_);
201 midi_channel_ = midichannel;
202 emit_notify ("midi_channel");
203}
204
205bool
207{
208 if (auto t = track_.get())
209 return t->isMuted (false);
210 return false;
211}
212
213void
215{
216 if (auto t = track_.get())
217 t->setMute (muted);
218}
219
220bool
222{
223 if (auto t = track_.get())
224 return t->isHidden ();
225 return false;
226}
227
228void
230{
231 if (auto t = track_.get())
232 t->setHidden (hidden);
233}
234
235bool
237{
238 if (auto t = track_.get())
239 return t->isSolo (false);
240 return false;
241}
242
243void
245{
246 if (auto t = track_.get())
247 t->setSolo (solo);
248}
249
250bool
252{
253 if (auto t = track_.get())
254 return t->isMasterTrack();
255 return false;
256}
257
258double
260{
261 if (auto t = track_.get())
262 if (auto at = dynamic_cast<te::AudioTrack*> (t))
263 if (auto vol = at->getVolumePlugin())
264 return vol->getVolumeDb();
265 return 0.0;
266}
267
268void
270{
271 if (auto t = track_.get())
272 if (auto at = dynamic_cast<te::AudioTrack*> (t))
273 if (auto vol = at->getVolumePlugin())
274 vol->setVolumeDb (db);
275}
276
277double
279{
280 if (auto t = track_.get())
281 if (auto at = dynamic_cast<te::AudioTrack*> (t))
282 if (auto vol = at->getVolumePlugin())
283 return vol->getPan();
284 return 0.0;
285}
286
287void
289{
290 if (auto t = track_.get())
291 if (auto at = dynamic_cast<te::AudioTrack*> (t))
292 if (auto vol = at->getVolumePlugin())
293 vol->setPan (pan);
294}
295
296ClipS
298{
299 ClipS clips;
300 if (auto t = track_.get())
301 if (auto ct = dynamic_cast<te::ClipTrack*> (t))
302 for (auto *clip : ct->getClips())
303 if (dynamic_cast<te::MidiClip*> (clip))
304 if (auto clipimpl = ClipImpl::from_trkn (*clip))
305 clips.push_back (clipimpl);
306 return clips;
307}
308
309void
311{
312 // NOP setter: assignments are ignored; clip add/remove is driven by the
313 // backend and emits `notify:launcher_clips` via TrackStateListener.
314}
315
317TrackImpl::clip_index (const ClipImpl &clip) const
318{
319 if (auto t = track_.get())
320 if (auto ct = dynamic_cast<te::ClipTrack *> (t)) {
321 auto &clips = ct->getClips();
322 for (int i = 0; i < clips.size(); i++)
323 if (clips[i] == clip.clip_.get())
324 return i;
325 }
326 return -1;
327}
328
329int
330TrackImpl::clip_succession (const ClipImpl &clip) const
331{
332 ssize_t idx = clip_index (clip);
333 if (idx < 0)
334 return NONE;
335 if (auto t = track_.get())
336 if (auto ct = dynamic_cast<te::ClipTrack *> (t)) {
337 auto &clips = ct->getClips();
338 if (idx + 1 < clips.size())
339 return idx + 1;
340 }
341 return NONE;
342}
343
344DeviceP
346{
347 return nullptr;
348}
349
350MonitorP
351TrackImpl::create_monitor (int32 ochannel)
352{
353 return nullptr;
354}
355
356TelemetryFieldS
358{
359 TelemetryFieldS v;
360 return_unless (state_listener_, v);
361 auto &t = state_listener_->telemetry;
362 v.push_back (telemetry_field ("dbspl0", &t.dbspl0));
363 v.push_back (telemetry_field ("dbspl1", &t.dbspl1));
364 return v;
365}
366
367void
368TrackImpl::update_telemetry()
369{
370 return_unless (state_listener_);
371 auto &t = state_listener_->telemetry;
372 auto [left, right] = state_listener_->get_levels();
373 t.dbspl0 = left;
374 t.dbspl1 = right;
375}
376
377DeviceInfo
379{
380 return {};
381}
382
383void
385{
387 auto *track = track_.get();
388 if (track) {
389 // Remove from edit's track list
390 track_->edit.deleteTrack (track);
391 // Clear references
393 state_listener_ = nullptr;
394 }
396}
397
398// == TrackImpl::ClipScout ==
399TrackImpl::ClipScout::ClipScout() noexcept
400{
401 // PRNG initialization goes here
402}
403
405void
407{
408 indices_ = indices;
409}
410
412int
414{
415 if (previous >= 0 && previous < indices_.size()) {
416 last_ = previous;
417 return indices_[last_];
418 }
419 return NONE;
420}
421
423void
425{
426 last_ = -1;
427}
428
430void
432{
433 indices_ = other.indices_;
434}
435
436ClipP
437TrackImpl::create_midi_clip (const String &name, double start, double length)
438{
439 if (auto t = track_.get()) {
440 if (auto at = dynamic_cast<tracktion::AudioTrack *> (t)) {
441 const tracktion::TimeRange range (tracktion::TimePosition::fromSeconds (start), tracktion::TimeDuration::fromSeconds (length));
442 auto clip = at->insertMIDIClip (juce::String (name), range, nullptr);
443 if (clip)
444 return ClipImpl::from_trkn (*clip);
445 else
446 warning ("insertMIDIClip returned null");
447 }
448 else
449 warning ("dynamic_cast<AudioTrack*> failed for track type: %s", trkn_track_type (*t).c_str());
450 }
451 else
452 warning ("track_.get() returned null");
453 return nullptr;
454}
455
456ClipP
457TrackImpl::create_audio_clip (const String &name, double start, double length)
458{
459 if (auto t = track_.get()) {
460 if (auto ct = dynamic_cast<tracktion::ClipTrack *> (t)) {
461 const tracktion::TimeRange range (tracktion::TimePosition::fromSeconds (start), tracktion::TimeDuration::fromSeconds (length));
462 auto clip = ct->insertNewClip (tracktion::TrackItem::Type::wave, juce::String (name), range, nullptr);
463 if (clip)
464 return ClipImpl::from_trkn (*clip);
465 else
466 warning ("insertNewClip returned null");
467 }
468 else
469 warning ("dynamic_cast<ClipTrack*> failed for track type: %s", trkn_track_type (*t).c_str());
470 }
471 else
472 warning ("track_.get() returned null");
473 return nullptr;
474}
475
476PluginP
478{
479 if (auto t = track_.get()) {
480 auto plugin = t->edit.getPluginCache().createNewPlugin (type.c_str(), {});
481 if (plugin) {
482 t->pluginList.insertPlugin (plugin, 0, nullptr);
483 return PluginImpl::from_trkn (*plugin);
484 }
485 else
486 warning ("createNewPlugin returned null");
487 }
488 else
489 warning ("track_.get() returned null");
490 return nullptr;
491}
492
493PluginS
495{
496 PluginS plugins;
497 if (auto t = track_.get()) {
498 auto &pluginList = t->pluginList;
499 for (int i = 0; i < pluginList.size(); i++) {
500 if (auto *plugin = pluginList[i]) {
501 if (auto pluginimpl = PluginImpl::from_trkn (*plugin))
502 plugins.push_back (pluginimpl);
503 }
504 }
505 }
506 return plugins;
507}
508
509} // 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:28
void remove_self() override
Remove self from parent container.
Definition gadget.cc:113
Mimick tracktion::engine::SafeSelectable<> for tracktion::Selectable descendants.
Definition trkn-utils.hh:27
MIDI clip playback succession generator.
Definition track.hh:63
int advance(int previous)
Determine clip succession.
Definition track.cc:413
void reset()
Reset state (history), preserves succession order.
Definition track.cc:424
void setup(const std::vector< int > &indices)
Setup clip succession order.
Definition track.cc:406
void update(const ClipScout &other)
Assign new succession order, preserves history.
Definition track.cc:431
Ase::Track implementation.
Definition track.hh:10
double volume() const override
Get track volume in dB.
Definition track.cc:259
double pan() const override
Get track pan (-1.0 to 1.0).
Definition track.cc:278
bool is_muted() const override
Check if track is muted.
Definition track.cc:206
void set_hidden(bool hidden) override
Set track hidden state.
Definition track.cc:229
void set_muted(bool muted) override
Set track muted state.
Definition track.cc:214
ClipS launcher_clips() const override
Retrieve the list of clips that can be directly played; changes on notify:launcher_clips.
Definition track.cc:297
ClipP create_audio_clip(const String &name, double start, double length) override
Create a new audio clip on this track.
Definition track.cc:457
bool is_master() const override
Flag set on the main output track.
Definition track.cc:251
DeviceP access_device() override
Retrieve Device handle for this track.
Definition track.cc:345
DeviceInfo device_info() override
Describe this Device type.
Definition track.cc:378
TelemetryFieldS telemetry() const override
Create signal monitor for an output channel.
Definition track.cc:357
PluginS list_plugins() override
List plugins on this track.
Definition track.cc:494
ClipP create_midi_clip(const String &name, double start, double length) override
Create a new MIDI clip on this track.
Definition track.cc:437
bool is_solo() const override
Check if track is soloed.
Definition track.cc:236
PluginP create_plugin(const String &type) override
Create a new plugin on this track by type identifier.
Definition track.cc:477
bool is_hidden() const override
Check if track is hidden from view.
Definition track.cc:221
int32 midi_channel() const override
Midi channel assigned to this track, 0 uses internal per-track channel.
Definition track.hh:45
void set_solo(bool solo) override
Set track solo state.
Definition track.cc:244
void remove_self() override
Remove self from parent container.
Definition track.cc:384
ValueTree getChild(int index) const
bool isValid() const noexcept
void addListener(Listener *listener)
void removeListener(Listener *listener)
#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
#define CLAMP(v, mi, ma)
Yield v clamped to [mi … ma].
Definition internal.hh:59
T left(T... args)
The Anklang C++ API namespace.
Definition api.hh:8
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
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_.
Definition trkn-utils.cc:62
AseType * find_ase_obj(tracktion::Selectable &selectable)
Helper: lookup AseType from tracktion Selectable via ase_obj_.
Definition trkn-utils.hh:53
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)
Definition trkn-utils.cc:70
RangeType< TimePosition > TimeRange
Reference for an allocated memory block.
Definition memory.hh:89
typedef ssize_t