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.