- for those not in the know, this series provides a way to
remove the temporal distortion introduced when using an
audio frame-based gui for music-locked objects.
In short, the gui uses an audio frame representation to move
objects. It displays the object using frame_at_beat(), quantizing
the time value to audio frames. This is fine until the user selects
that frame but expects it to be interpreted as a beat.
Thus beat_at_frame() would not produce the user-expected beat
(temporal quantization error of up to 0.5 audio samples).
This is one method of mapping audio time to music time accurately.
- use exact beats to determine frame position.
- see comments in tempo.cc for more.
- this hasn't been done for split yet, but dragging and
trimming are supported.
Currently when a GhostRegion is deleted by its "parent" RegionView it emits the
static GhostRegion::CatchDeletion signal which is connected to the
RegionView::remove_ghost method of every RegionView instance.
With a static GhostRegion::CatchDeletion signal a particular test session
causes 31 Million calls of RegionView::remove_ghost on Session deletion and the
session takes 70 seconds to close with a debug build.
The lifetime of a ghost region is tied to both the TimeAxisView(TAV) and
RegionView(RV) in that when a RegionView is deleted all GhostRegion instances
associated with the RegionView should be deleted or when a TimeAxisView is
deleted all ghost regions that are contained in the view should be deleted.
This means that there needs to be notification between GhostRegion and both
classes. Instead of using a signal for this as we know there are only two
listeners and GhostRegion already holds a reference to the TimeAxisView, also
take a reference to the parent RegionView in the GhostRegion constructor and
use it to notify the RegionView when GhostRegion destructor is called so it can
drop any references it holds.
Using a direct function call in the GhostRegion destructor to notify the
TimeAxisView and RegionView "parents" brings the unload/close time down for the
test session from 70 seconds to 4.5 seconds.
The GhostRegion also references canvas items that are added to the TimeAxisView
canvas group or at least a canvas group that it manages. So when the
TimeAxisView is destroyed and the canvas group that is the parent of those
items is destroyed, the GhostRegion's canvas items will also be
deleted/destroyed by the parent canvas item/group. This means the GhostRegions
must be destroyed when the TimeAxisView they are contained in is destroyed or
there will be dangling references to canvas items that have already been
deleted and trying to delete them again will be bad.
No functional changes in this one (for easier auditing), but towards having
round up/down only if necessary modes, rather than kludging around that
situation with a double round as we do currently.
Use RegionSelection for MIDI regions as well, since the old dumb stub didn't do
some things correctly. There's probably no reason to have a separate class for
this at all, and some good ones for putting all regions in the same selection,
so we should probably do that. For now they are still separate in the
selection but use the same base class.
Remove Canvas::Layout, use Canvas::Container for the same purpose, move child-rendering into Item::render_children() so that it
could theoretically be used by any derived type.