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