13#define CDEBUG(...) Ase::debug ("ClipNote", __VA_ARGS__)
14#define UDEBUG(...) Ase::debug ("undo", __VA_ARGS__)
20 all_notes (this,
"all_notes"),
21 end_tick (this,
"end_tick")
28 return (tick == o.
tick &&
42 notifytrack_ = on_event (
"notify", [
this] (
const Event &event) {
44 track_->update_clips();
52ClipImpl::project ()
const
54 return track_ ? track_->project() :
nullptr;
58ClipImpl::needs_serialize()
const
60 return notes_.size() > 0;
68 GadgetImpl::serialize (xs);
80 if (field.name ==
"id" || field.name ==
"selected")
94 for (
const auto &cnote : cnotes)
97 note.id = next_noteid++;
101 notes_.insert (note);
103 emit_notify (
"notes");
110ClipImpl::clip_index ()
const
112 return track_ ? track_->clip_index (*
this) : -1;
120 const auto last_starttick_ = starttick_;
121 const auto last_stoptick_ = stoptick_;
122 const auto last_endtick_ = endtick_;
123 starttick_ = starttick;
124 stoptick_ = stoptick;
125 endtick_ =
std::max (starttick_, stoptick_);
126 if (last_endtick_ != endtick_)
127 emit_notify (
"end_tick");
128 if (last_stoptick_ != stoptick_)
129 emit_notify (
"stop_tick");
130 if (last_starttick_ != starttick_)
131 emit_notify (
"start_tick");
135ClipImpl::list_all_notes ()
138 auto events = tick_events();
139 cnotes.assign (events->begin(), events->end());
144ClipImpl::set_all_notes (
const ClipNoteS ¬es)
151ClipImpl::get_all_notes ()
const
153 auto events = tick_events();
155 notes.assign (events->begin(), events->end());
160ClipImpl::get_end_tick ()
const
166ClipImpl::set_end_tick (int64 etick)
173ClipImpl::OrderedEventsP
174ClipImpl::tick_events ()
const
179ClipImpl::EventImage::EventImage (
const ClipNoteS &clipnotes)
181 const size_t clipnotes_bytes = clipnotes.
size() *
sizeof (clipnotes[0]);
182 cbuffer = zstd_compress (clipnotes.data(), clipnotes_bytes, 4);
184 ProjectImpl::undo_mem_counter +=
sizeof (*this) + cbuffer.size();
185 UDEBUG (
"ClipImpl: store undo (notes=%d): %d->%d (%f%%)", clipnotes.size(),
186 clipnotes_bytes, cbuffer.size(), cbuffer.size() * 100.0 / clipnotes_bytes);
189ClipImpl::EventImage::~EventImage()
191 ProjectImpl::undo_mem_counter -=
sizeof (*this) + cbuffer.size();
192 UDEBUG (
"ClipImpl: free undo mem: %d\n",
sizeof (*
this) + cbuffer.size());
196ClipImpl::push_undo (
const ClipNoteS &clipnotes,
const String &undogroup)
200 undo_scope (undogroup) += [thisp, imagep, undogroup] () { thisp->apply_undo (*imagep, undogroup); };
204ClipImpl::apply_undo (
const EventImage &image,
const String &undogroup)
206 push_undo (notes_.copy(), undogroup);
208 const ssize_t osize = zstd_target_size (image.cbuffer);
209 assert_return (osize >= 0 && osize ==
sizeof (onotes[0]) * (osize /
sizeof (onotes[0])));
210 onotes.resize (osize /
sizeof (onotes[0]));
211 const ssize_t rsize = zstd_uncompress (image.cbuffer, onotes.data(), osize);
213 notes_.clear_silently();
214 for (
const ClipNote ¬e : onotes)
215 notes_.insert (note);
216 emit_notify (
"notes");
221ClipImpl::collapse_notes (EventsById &inotes,
const bool preserve_selected)
223 ClipNoteS copies = inotes.copy();
224 size_t collapsed = 0;
226 std::stable_sort (copies.begin(), copies.end(), [] (
const ClipNote &a,
const ClipNote &b) {
227 return a.tick < b.tick;
230 for (
size_t i = 0; i < copies.size(); i++) {
231 const ClipNote ¬e = copies[i];
232 for (
size_t j = i + 1; j < copies.size(); j++) {
233 if (note.tick != copies[j].tick)
235 if (note.key == copies[j].key && note.channel == copies[j].channel) {
236 if (note.selected != copies[j].selected && preserve_selected)
239 collapsed += inotes.remove (note);
247ClipImpl::change_batch (
const ClipNoteS &batch,
const String &undogroup)
249 bool changes =
false, selections =
false;
251 const ClipNoteS orig_notes = notes_.copy();
253 for (
const auto ¬e : batch)
254 if (note.id > 0 && (note.duration == 0 || note.channel < 0)) {
255 changes |= notes_.remove (note);
256 CDEBUG (
"%s: delete notes: %d\n", __func__, note.id);
259 for (
const auto ¬e : batch)
260 if (note.id > 0 && note.duration > 0 && note.channel >= 0) {
262 if (notes_.replace (note, &replaced) && !(note == replaced)) {
264 if (note == replaced)
268 CDEBUG (
"%s: %s %d: new=%s old=%s\n", __func__, note == replaced ?
"toggle" :
"replace", note.id,
269 stringify_clip_note (note), stringify_clip_note (replaced));
273 for (
const auto ¬e : batch)
274 if (note.id <= 0 && note.duration > 0 && note.channel >= 0) {
276 ev.id = next_noteid++;
278 const bool replaced = notes_.insert (ev);
279 changes |= !replaced;
280 CDEBUG (
"%s: insert: %s%s\n", __func__, stringify_clip_note (ev), replaced ?
" (REPLACED?)" :
"");
283 if (changes || selections) {
284 const size_t collapsed = collapse_notes (notes_,
true);
285 changes = changes || collapsed;
286 if (collapsed) CDEBUG (
"%s: collapsed=%d\n", __func__, collapsed);
289 if (!notes_.equals (orig_notes)) {
291 push_undo (orig_notes, undogroup.
empty() ?
"Change Notes" : undogroup);
292 if (changes) CDEBUG (
"%s: notes=%d undo_size: %fMB\n", __func__, notes_.size(), project()->undo_size_guess() / (1024. * 1024));
293 emit_notify (
"notes");
307 tsig = p->signature();
312 loop_end_ = tsig.bar_ticks() * 2;
314 last_ = loop_end_ - start_offset_ + LOOPS * (loop_end_ - loop_start_);
321ClipImpl::Generator::jumpto (
int64 target_tick)
326 xtick_ = target_tick;
331 xtick_ =
std::min (target_tick, play_length());
333 itick_ = start_offset_;
336 if (itick_ >= loop_end_)
342 int64 delta = xtick_;
346 if (itick_ == loop_end_)
348 itick_ = loop_start_;
352 const int64 frac = delta % (loop_end_ - loop_start_);
363 printerr (
"generate: %d < %d (%+d) && %d > %d (%+d) (loop: %d %d) i=%d\n", xtick_, last_, xtick_ < last_,
364 target_tick, xtick_, target_tick > xtick_,
365 loop_start_, loop_end_, itick_);
366 const int64 old_xtick = xtick_;
367 return_unless (xtick_ < last_ && target_tick > xtick_, xtick_ - old_xtick);
377 itick_ = start_offset_;
383 const int64 delta = itick_ < loop_end_ ?
std::min (ticks, loop_end_ - itick_) : ticks;
385 const int64 x = xtick_;
387 const int64 a = itick_;
389 const int64 b = itick_;
390 if (itick_ == loop_end_)
391 itick_ = loop_start_;
393 if (receiver && !muted_)
396 const ClipNote *
event = events_->lookup_after (index);
397 while (event && event->tick < b)
399 MidiEvent midievent = make_note_on (event->channel, event->key, event->velocity, event->fine_tune, event->id);
400 const int64 noteon_tick = x +
event->tick - a;
401 receiver (noteon_tick, midievent);
402 midievent.
type = MidiEvent::NOTE_OFF;
403 receiver (noteon_tick + event->duration, midievent);
405 if (event == &*events_->end())
410 return xtick_ - old_xtick;
414stringify_clip_note (
const ClipNote &n)
419 n.tick, n.duration, n.velocity, n.fine_tune);
OrderedEventsP tick_events() const
Retrieve const vector with all notes ordered by tick.
Ase::Track implementation.
One entry in a Writ serialization document.
WritNode push()
Append new WritNode for serializing arrays during in_save().
Value & value()
Access the Value of this node.
bool in_load() const
Return true during deserialization.
bool in_save() const
Return true during serialization.
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
#define assert_warn(expr)
Issue an assertion warning if expr evaluates to false.
The Anklang C++ API namespace.
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.
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
constexpr const uint MIDI_NOTE_ID_LAST
Last valid (internal) MIDI note event ID.
constexpr const uint MIDI_NOTE_ID_FIRST
First (internal) MIDI note event ID (lower IDs are reserved for external notes).
std::shared_ptr< typename std::remove_pointer< Source >::type > shared_ptr_from(Source *object)
Use shared_ptr_cast<>() to convert an object pointer into a shared_ptr<>.
constexpr const int64 TRANSPORT_PPQN
Maximum number of sample frames to calculate in Processor::render().
Part specific note event representation.
int64 tick
UI selection flag.
bool selected
Musical note as MIDI key, 0 .. 127.
float velocity
Duration in number of ticks.
int64 duration
Position in ticks.
float fine_tune
Velocity, 0 .. +1.
Structure for callback based notifications.
MidiEvent data structure.
MidiEventType type
MidiEvent type, one of the MidiEventType members.
Container for a sorted array of opaque Event structures with binary lookup.
Musical time signature and tick conversions.
void filter(const std::function< bool(const ValueField &)> &pred)
Recursively purge/remove RECORD elements iff to pred (recordfield) == true.