MIDI region forking, plus Playlist::regions_to_read() fix forward ported from 2.X. region forking requires a few cleanups
git-svn-id: svn://localhost/ardour2/branches/3.0@7118 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
c25c7598c1
commit
e258b2622a
@ -1792,6 +1792,7 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> regi
|
||||
|
||||
} else if (mr) {
|
||||
items.push_back (MenuElem (_("Quantize"), sigc::mem_fun(*this, &Editor::quantize_region)));
|
||||
items.push_back (MenuElem (_("Fork"), sigc::mem_fun(*this, &Editor::fork_region)));
|
||||
items.push_back (SeparatorElem());
|
||||
}
|
||||
|
||||
|
@ -1075,6 +1075,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
|
||||
void reset_region_scale_amplitude ();
|
||||
void adjust_region_scale_amplitude (bool up);
|
||||
void quantize_region ();
|
||||
void fork_region ();
|
||||
|
||||
void do_insert_time ();
|
||||
void insert_time (nframes64_t, nframes64_t, Editing::InsertTimeOption, bool, bool, bool);
|
||||
|
@ -4648,6 +4648,47 @@ Editor::apply_midi_note_edit_op (MidiOperator& op)
|
||||
rs.clear ();
|
||||
}
|
||||
|
||||
void
|
||||
Editor::fork_region ()
|
||||
{
|
||||
RegionSelection rs;
|
||||
|
||||
get_regions_for_action (rs);
|
||||
|
||||
if (rs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
begin_reversible_command (_("Fork Region(s)"));
|
||||
|
||||
track_canvas->get_window()->set_cursor (*wait_cursor);
|
||||
gdk_flush ();
|
||||
|
||||
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
|
||||
RegionSelection::iterator tmp = r;
|
||||
++tmp;
|
||||
|
||||
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
|
||||
|
||||
if (mrv) {
|
||||
boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
|
||||
boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
|
||||
|
||||
playlist->clear_history ();
|
||||
cerr << "Replace region with " << newregion->name() << endl;
|
||||
playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
|
||||
_session->add_command(new StatefulDiffCommand (playlist));
|
||||
}
|
||||
|
||||
r = tmp;
|
||||
}
|
||||
|
||||
commit_reversible_command ();
|
||||
rs.clear ();
|
||||
|
||||
track_canvas->get_window()->set_cursor (*current_canvas_cursor);
|
||||
}
|
||||
|
||||
void
|
||||
Editor::quantize_region ()
|
||||
{
|
||||
|
@ -21,7 +21,7 @@
|
||||
using namespace ARDOUR;
|
||||
|
||||
MidiCutBuffer::MidiCutBuffer (Session* s)
|
||||
: AutomatableSequence<MidiModel::TimeType> (*s, 0)
|
||||
: AutomatableSequence<MidiModel::TimeType> (*s)
|
||||
, _origin (0)
|
||||
{
|
||||
|
||||
|
@ -107,7 +107,6 @@ class AudioRegion : public Region
|
||||
};
|
||||
|
||||
virtual framecnt_t read (Sample*, framepos_t pos, framecnt_t cnt, int channel) const;
|
||||
virtual framecnt_t read_with_ops (Sample*, framepos_t pos, framecnt_t cnt, int channel, ReadOps rops) const;
|
||||
virtual framecnt_t readable_length() const { return length(); }
|
||||
|
||||
virtual framecnt_t read_at (Sample *buf, Sample *mixdown_buf, float *gain_buf,
|
||||
|
@ -42,6 +42,7 @@ class Automatable : virtual public Evoral::ControlSet
|
||||
{
|
||||
public:
|
||||
Automatable(Session&);
|
||||
Automatable (const Automatable& other);
|
||||
Automatable();
|
||||
|
||||
virtual ~Automatable() {}
|
||||
|
@ -29,11 +29,18 @@ namespace ARDOUR {
|
||||
template<typename T>
|
||||
class AutomatableSequence : public Automatable, public Evoral::Sequence<T> {
|
||||
public:
|
||||
AutomatableSequence(Session& s, size_t /*size*/)
|
||||
AutomatableSequence(Session& s)
|
||||
: Evoral::ControlSet()
|
||||
, Automatable(s)
|
||||
, Evoral::Sequence<T>(EventTypeMap::instance())
|
||||
{}
|
||||
|
||||
AutomatableSequence(const AutomatableSequence<T>& other)
|
||||
: Evoral::ControlSet(other)
|
||||
, Automatable(other._a_session)
|
||||
, Evoral::Sequence<T>(other)
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
@ -46,6 +46,7 @@ namespace PBD {
|
||||
extern uint64_t MidiClock;
|
||||
extern uint64_t Monitor;
|
||||
extern uint64_t Solo;
|
||||
extern uint64_t AudioPlayback;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ class MidiModel : public AutomatableSequence<Evoral::MusicalTime> {
|
||||
public:
|
||||
typedef double TimeType;
|
||||
|
||||
MidiModel(MidiSource* s, size_t size=0);
|
||||
MidiModel(MidiSource* s);
|
||||
|
||||
NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
|
||||
void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
|
||||
@ -151,7 +151,8 @@ public:
|
||||
void apply_command(Session& session, Command* cmd);
|
||||
void apply_command_as_subcommand(Session& session, Command* cmd);
|
||||
|
||||
bool write_to(boost::shared_ptr<MidiSource> source);
|
||||
bool write_to(boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin = Evoral::MinMusicalTime,
|
||||
Evoral::MusicalTime end = Evoral::MaxMusicalTime);
|
||||
|
||||
// MidiModel doesn't use the normal AutomationList serialisation code
|
||||
// since controller data is stored in the .mid
|
||||
|
@ -50,6 +50,8 @@ class MidiRegion : public Region
|
||||
public:
|
||||
~MidiRegion();
|
||||
|
||||
boost::shared_ptr<MidiRegion> clone ();
|
||||
|
||||
boost::shared_ptr<MidiSource> midi_source (uint32_t n=0) const;
|
||||
|
||||
/* Stub Readable interface */
|
||||
|
@ -47,6 +47,9 @@ class MidiSource : virtual public Source
|
||||
MidiSource (Session& session, const XMLNode&);
|
||||
virtual ~MidiSource ();
|
||||
|
||||
boost::shared_ptr<MidiSource> clone (Evoral::MusicalTime begin = Evoral::MinMusicalTime,
|
||||
Evoral::MusicalTime end = Evoral::MaxMusicalTime);
|
||||
|
||||
/** Read the data in a given time range from the MIDI source.
|
||||
* All time stamps in parameters are in audio frames (even if the source has tempo time).
|
||||
* \param dst Ring buffer where read events are written
|
||||
|
@ -28,7 +28,7 @@ template <typename T> class EventSink;
|
||||
}
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class MidiSource;
|
||||
|
||||
/** Tracks played notes, so they can be resolved in potential stuck note
|
||||
* situations (e.g. looping, transport stop, etc).
|
||||
@ -43,10 +43,14 @@ public:
|
||||
void remove (uint8_t note, uint8_t chn);
|
||||
void resolve_notes (MidiBuffer& buffer, nframes64_t time);
|
||||
void resolve_notes (Evoral::EventSink<nframes_t>& buffer, nframes64_t time);
|
||||
void resolve_notes (MidiSource& src, Evoral::MusicalTime time);
|
||||
void dump (std::ostream&);
|
||||
void reset ();
|
||||
bool empty() const { return _on == 0; }
|
||||
uint16_t on() const { return _on; }
|
||||
bool active (uint8_t note, uint8_t channel) {
|
||||
return _active_notes[(channel*128)+note] > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void track_note_onoffs(const Evoral::MIDIEvent<MidiBuffer::TimeType>& event);
|
||||
|
@ -50,7 +50,7 @@ static inline float f_max(float x, float a) {
|
||||
return (x);
|
||||
}
|
||||
|
||||
std::string bump_name_once(std::string s);
|
||||
std::string bump_name_once(const std::string& s, char delimiter);
|
||||
|
||||
int cmp_nocase (const std::string& s, const std::string& s2);
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/configuration.h"
|
||||
#include "ardour/audioplaylist.h"
|
||||
#include "ardour/audioregion.h"
|
||||
@ -163,7 +164,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
|
||||
if ((*i)->coverage (start, end) != OverlapNone) {
|
||||
relevant_regions[(*i)->layer()].push_back (*i);
|
||||
relevant_layers.push_back ((*i)->layer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
|
||||
@ -188,8 +189,10 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
|
||||
vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
|
||||
vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
|
||||
|
||||
|
||||
for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
|
||||
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
|
||||
assert(ar);
|
||||
ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
|
||||
_read_data_count += ar->read_data_count();
|
||||
|
@ -317,12 +317,6 @@ AudioRegion::read (Sample* buf, framepos_t timeline_position, framecnt_t cnt, in
|
||||
return _read_at (_sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, 0, 0, ReadOps (0));
|
||||
}
|
||||
|
||||
framecnt_t
|
||||
AudioRegion::read_with_ops (Sample* buf, framepos_t file_position, framecnt_t cnt, int channel, ReadOps rops) const
|
||||
{
|
||||
return _read_at (_sources, _length, buf, 0, 0, file_position, cnt, channel, 0, 0, rops);
|
||||
}
|
||||
|
||||
framecnt_t
|
||||
AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
|
||||
framepos_t file_position, framecnt_t cnt, uint32_t chan_n,
|
||||
|
@ -49,6 +49,18 @@ Automatable::Automatable(Session& session)
|
||||
{
|
||||
}
|
||||
|
||||
Automatable::Automatable (const Automatable& other)
|
||||
: ControlSet (other)
|
||||
, _a_session (other._a_session)
|
||||
, _last_automation_snapshot (0)
|
||||
{
|
||||
Glib::Mutex::Lock lm (other._control_lock);
|
||||
|
||||
for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
|
||||
boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
|
||||
_controls[ac->parameter()] = ac;
|
||||
}
|
||||
}
|
||||
int
|
||||
Automatable::old_set_automation_state (const XMLNode& node)
|
||||
{
|
||||
|
@ -43,4 +43,5 @@ uint64_t PBD::DEBUG::MackieControl = PBD::new_debug_bit ("mackiecontrol");
|
||||
uint64_t PBD::DEBUG::MidiClock = PBD::new_debug_bit ("midiclock");
|
||||
uint64_t PBD::DEBUG::Monitor = PBD::new_debug_bit ("monitor");
|
||||
uint64_t PBD::DEBUG::Solo = PBD::new_debug_bit ("solo");
|
||||
uint64_t PBD::DEBUG::AudioPlayback = PBD::new_debug_bit ("audioplayback");
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "ardour/midi_model.h"
|
||||
#include "ardour/midi_source.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
#include "ardour/smf_source.h"
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/session.h"
|
||||
@ -38,8 +39,8 @@ using namespace std;
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
|
||||
MidiModel::MidiModel(MidiSource* s, size_t size)
|
||||
: AutomatableSequence<TimeType>(s->session(), size)
|
||||
MidiModel::MidiModel(MidiSource* s)
|
||||
: AutomatableSequence<TimeType>(s->session())
|
||||
, _midi_source(s)
|
||||
{
|
||||
}
|
||||
@ -675,7 +676,7 @@ MidiModel::DiffCommand::get_state ()
|
||||
return *diff_command;
|
||||
}
|
||||
|
||||
/** Write the model to a MidiSource (i.e. save the model).
|
||||
/** Write part or all of the model to a MidiSource (i.e. save the model).
|
||||
* This is different from manually using read to write to a source in that
|
||||
* note off events are written regardless of the track mode. This is so the
|
||||
* user can switch a recorded track (with note durations from some instrument)
|
||||
@ -683,9 +684,11 @@ MidiModel::DiffCommand::get_state ()
|
||||
* destroying the original note durations.
|
||||
*/
|
||||
bool
|
||||
MidiModel::write_to(boost::shared_ptr<MidiSource> source)
|
||||
MidiModel::write_to (boost::shared_ptr<MidiSource> source, Evoral::MusicalTime begin_time, Evoral::MusicalTime end_time)
|
||||
{
|
||||
ReadLock lock(read_lock());
|
||||
MidiStateTracker mst;
|
||||
Evoral::MusicalTime extra_note_on_time = end_time;
|
||||
|
||||
const bool old_percussive = percussive();
|
||||
set_percussive(false);
|
||||
@ -694,9 +697,58 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
|
||||
source->mark_streaming_midi_write_started(note_mode(), _midi_source->timeline_position());
|
||||
|
||||
for (Evoral::Sequence<TimeType>::const_iterator i = begin(); i != end(); ++i) {
|
||||
source->append_event_unlocked_beats(*i);
|
||||
const Evoral::Event<Evoral::MusicalTime>& ev (*i);
|
||||
|
||||
if (ev.time() >= begin_time && ev.time() < end_time) {
|
||||
|
||||
const Evoral::MIDIEvent<Evoral::MusicalTime>* mev =
|
||||
static_cast<const Evoral::MIDIEvent<Evoral::MusicalTime>* > (&ev);
|
||||
|
||||
if (!mev) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (mev->is_note_off()) {
|
||||
|
||||
if (!mst.active (mev->note(), mev->channel())) {
|
||||
|
||||
/* add a note-on at the start of the range we're writing
|
||||
to the file. velocity is just an arbitary reasonable value.
|
||||
*/
|
||||
|
||||
Evoral::MIDIEvent<Evoral::MusicalTime> on (mev->event_type(), extra_note_on_time, 3, 0, true);
|
||||
on.set_type (mev->type());
|
||||
on.set_note (mev->note());
|
||||
on.set_channel (mev->channel());
|
||||
on.set_velocity (mev->velocity());
|
||||
|
||||
cerr << "Add note on for odd note off, note = " << (int) on.note() << endl;
|
||||
source->append_event_unlocked_beats (on);
|
||||
mst.add (on.note(), on.channel());
|
||||
mst.dump (cerr);
|
||||
extra_note_on_time += 1.0/128.0;
|
||||
}
|
||||
|
||||
cerr << "MIDI Note off (note = " << (int) mev->note() << endl;
|
||||
source->append_event_unlocked_beats (*i);
|
||||
mst.remove (mev->note(), mev->channel());
|
||||
mst.dump (cerr);
|
||||
|
||||
} else if (mev->is_note_on()) {
|
||||
cerr << "MIDI Note on (note = " << (int) mev->note() << endl;
|
||||
mst.add (mev->note(), mev->channel());
|
||||
source->append_event_unlocked_beats(*i);
|
||||
mst.dump (cerr);
|
||||
} else {
|
||||
cerr << "MIDI other event type\n";
|
||||
source->append_event_unlocked_beats(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mst.resolve_notes (*source, end_time);
|
||||
|
||||
set_percussive(old_percussive);
|
||||
source->mark_streaming_write_completed();
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "ardour/dB.h"
|
||||
#include "ardour/playlist.h"
|
||||
#include "ardour/midi_source.h"
|
||||
#include "ardour/region_factory.h"
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/midi_ring_buffer.h"
|
||||
|
||||
@ -68,6 +69,28 @@ MidiRegion::~MidiRegion ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Create a new MidiRegion that has its own version of some/all of the Source used by another.
|
||||
*/
|
||||
boost::shared_ptr<MidiRegion>
|
||||
MidiRegion::clone ()
|
||||
{
|
||||
BeatsFramesConverter bfc (_session.tempo_map(), _position);
|
||||
double bbegin = bfc.from (_position);
|
||||
double bend = bfc.from (last_frame() + 1);
|
||||
|
||||
boost::shared_ptr<MidiSource> ms = midi_source(0)->clone (bbegin, bend);
|
||||
|
||||
PropertyList plist;
|
||||
|
||||
plist.add (Properties::name, ms->name());
|
||||
plist.add (Properties::whole_file, true);
|
||||
plist.add (Properties::start, 0);
|
||||
plist.add (Properties::length, _length);
|
||||
plist.add (Properties::layer, 0);
|
||||
|
||||
return boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (ms, plist, true));
|
||||
}
|
||||
|
||||
void
|
||||
MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
|
||||
{
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
#include <glibmm/fileutils.h>
|
||||
|
||||
#include "pbd/xml++.h"
|
||||
#include "pbd/pthread_utils.h"
|
||||
#include "pbd/basename.h"
|
||||
@ -81,6 +83,7 @@ MidiSource::MidiSource (Session& s, const XMLNode& node)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MidiSource::~MidiSource ()
|
||||
{
|
||||
}
|
||||
@ -223,44 +226,59 @@ MidiSource::mark_streaming_write_completed ()
|
||||
_writing = false;
|
||||
}
|
||||
|
||||
boost::shared_ptr<MidiSource>
|
||||
MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
|
||||
{
|
||||
string newname = PBD::basename_nosuffix(_name.val());
|
||||
string newpath;
|
||||
|
||||
/* get a new name for the MIDI file we're going to write to
|
||||
*/
|
||||
|
||||
do {
|
||||
|
||||
newname = bump_name_once (newname, '-');
|
||||
/* XXX build path safely */
|
||||
newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid";
|
||||
|
||||
} while (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS));
|
||||
|
||||
boost::shared_ptr<MidiSource> newsrc = boost::dynamic_pointer_cast<MidiSource>(
|
||||
SourceFactory::createWritable(DataType::MIDI, _session,
|
||||
newpath, false, _session.frame_rate()));
|
||||
|
||||
newsrc->set_timeline_position(_timeline_position);
|
||||
|
||||
if (_model) {
|
||||
_model->write_to (newsrc, begin, end);
|
||||
} else {
|
||||
error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
|
||||
return boost::shared_ptr<MidiSource>();
|
||||
}
|
||||
|
||||
newsrc->flush_midi();
|
||||
|
||||
/* force a reload of the model if the range is partial */
|
||||
|
||||
if (begin != Evoral::MinMusicalTime || end != Evoral::MaxMusicalTime) {
|
||||
newsrc->load_model (true, true);
|
||||
}
|
||||
|
||||
return newsrc;
|
||||
}
|
||||
|
||||
void
|
||||
MidiSource::session_saved()
|
||||
{
|
||||
flush_midi();
|
||||
|
||||
if (_model && _model->edited()) {
|
||||
string newname;
|
||||
const string basename = PBD::basename_nosuffix(_name.val());
|
||||
string::size_type last_dash = basename.find_last_of("-");
|
||||
if (last_dash == string::npos || last_dash == basename.find_first_of("-")) {
|
||||
newname = basename + "-1";
|
||||
} else {
|
||||
stringstream ss(basename.substr(last_dash+1));
|
||||
unsigned write_count = 0;
|
||||
ss >> write_count;
|
||||
// cerr << "WRITE COUNT: " << write_count << endl;
|
||||
++write_count; // start at 1
|
||||
ss.clear();
|
||||
ss << basename.substr(0, last_dash) << "-" << write_count;
|
||||
newname = ss.str();
|
||||
}
|
||||
boost::shared_ptr<MidiSource> newsrc = clone ();
|
||||
|
||||
string newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid";
|
||||
|
||||
boost::shared_ptr<MidiSource> newsrc = boost::dynamic_pointer_cast<MidiSource>(
|
||||
SourceFactory::createWritable(DataType::MIDI, _session,
|
||||
newpath, false, _session.frame_rate()));
|
||||
|
||||
newsrc->set_timeline_position(_timeline_position);
|
||||
_model->write_to(newsrc);
|
||||
|
||||
// cyclic dependency here, ugly :(
|
||||
newsrc->set_model(_model);
|
||||
_model->set_midi_source(newsrc.get());
|
||||
|
||||
newsrc->flush_midi();
|
||||
|
||||
Switched (newsrc); /* EMIT SIGNAL */
|
||||
if (newsrc) {
|
||||
_model->set_midi_source (newsrc.get());
|
||||
Switched (newsrc); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <iostream>
|
||||
#include "ardour/event_type_map.h"
|
||||
#include "ardour/midi_ring_buffer.h"
|
||||
#include "ardour/midi_source.h"
|
||||
#include "ardour/midi_state_tracker.h"
|
||||
|
||||
using namespace std;
|
||||
@ -132,6 +133,35 @@ MidiStateTracker::resolve_notes (Evoral::EventSink<nframes_t> &dst, nframes64_t
|
||||
_on = 0;
|
||||
}
|
||||
|
||||
void
|
||||
MidiStateTracker::resolve_notes (MidiSource& src, Evoral::MusicalTime time)
|
||||
{
|
||||
if (!_on) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* NOTE: the src must be locked */
|
||||
|
||||
for (int channel = 0; channel < 16; ++channel) {
|
||||
for (int note = 0; note < 128; ++note) {
|
||||
while (_active_notes[note + 128 * channel]) {
|
||||
Evoral::MIDIEvent<Evoral::MusicalTime> ev ((MIDI_CMD_NOTE_OFF|channel), time, 3, 0, true);
|
||||
ev.set_type (MIDI_CMD_NOTE_OFF);
|
||||
ev.set_channel (channel);
|
||||
ev.set_note (note);
|
||||
ev.set_velocity (0);
|
||||
src.append_event_unlocked_beats (ev);
|
||||
_active_notes[note + 128 * channel]--;
|
||||
cerr << "Resolved " << ev << endl;
|
||||
/* don't stack events up at the same time
|
||||
*/
|
||||
time += 1.0/128.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
_on = 0;
|
||||
}
|
||||
|
||||
void
|
||||
MidiStateTracker::dump (ostream& o)
|
||||
{
|
||||
|
@ -1724,6 +1724,8 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
|
||||
to_check.insert (start);
|
||||
to_check.insert (end);
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
|
||||
|
||||
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
|
||||
|
||||
/* find all/any regions that span start+end */
|
||||
@ -1734,22 +1736,38 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
|
||||
|
||||
case OverlapInternal:
|
||||
covering.push_back (*i);
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
|
||||
break;
|
||||
|
||||
case OverlapStart:
|
||||
to_check.insert ((*i)->position());
|
||||
if ((*i)->position() != 0) {
|
||||
to_check.insert ((*i)->position()-1);
|
||||
}
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
|
||||
covering.push_back (*i);
|
||||
break;
|
||||
|
||||
case OverlapEnd:
|
||||
to_check.insert ((*i)->last_frame());
|
||||
to_check.insert ((*i)->last_frame()+1);
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
|
||||
covering.push_back (*i);
|
||||
break;
|
||||
|
||||
case OverlapExternal:
|
||||
covering.push_back (*i);
|
||||
to_check.insert ((*i)->position());
|
||||
if ((*i)->position() != 0) {
|
||||
to_check.insert ((*i)->position()-1);
|
||||
}
|
||||
to_check.insert ((*i)->last_frame());
|
||||
to_check.insert ((*i)->last_frame()+1);
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1767,6 +1785,7 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
|
||||
if (covering.size() == 1) {
|
||||
|
||||
rlist->push_back (covering.front());
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
|
||||
|
||||
} else {
|
||||
|
||||
@ -1775,11 +1794,21 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
|
||||
|
||||
here.clear ();
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
|
||||
|
||||
for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
|
||||
|
||||
if ((*x)->covers (*t)) {
|
||||
here.push_back (*x);
|
||||
}
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
|
||||
(*x)->name(),
|
||||
(*t)));
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
|
||||
(*x)->name(),
|
||||
(*t)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RegionSortByLayer cmp;
|
||||
@ -1794,7 +1823,8 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
|
||||
if ((*c)->opaque()) {
|
||||
|
||||
/* the other regions at this position are hidden by this one */
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
|
||||
(*c)->name()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1812,6 +1842,8 @@ Playlist::regions_to_read (framepos_t start, framepos_t end)
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
|
||||
|
||||
return rlist;
|
||||
}
|
||||
|
||||
@ -2311,7 +2343,7 @@ Playlist::bump_name (string name, Session &session)
|
||||
string newname = name;
|
||||
|
||||
do {
|
||||
newname = bump_name_once (newname);
|
||||
newname = bump_name_once (newname, '.');
|
||||
} while (session.playlists->by_name (newname)!=NULL);
|
||||
|
||||
return newname;
|
||||
|
@ -272,7 +272,7 @@ Route::ensure_track_or_route_name(string name, Session &session)
|
||||
string newname = name;
|
||||
|
||||
while (!session.io_name_is_legal (newname)) {
|
||||
newname = bump_name_once (newname);
|
||||
newname = bump_name_once (newname, '.');
|
||||
}
|
||||
|
||||
return newname;
|
||||
|
@ -728,8 +728,9 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
|
||||
|
||||
/* tell sources we're saving first, in case they write out to a new file
|
||||
* which should be saved with the state rather than the old one */
|
||||
for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i)
|
||||
for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
|
||||
i->second->session_saved();
|
||||
}
|
||||
|
||||
tree.set_root (&get_state());
|
||||
|
||||
|
@ -72,17 +72,19 @@ legalize_for_path (ustring str)
|
||||
return legal;
|
||||
}
|
||||
|
||||
string bump_name_once(std::string name)
|
||||
string
|
||||
bump_name_once (const std::string& name, char delimiter)
|
||||
{
|
||||
string::size_type period;
|
||||
string::size_type delim;
|
||||
string newname;
|
||||
|
||||
if ((period = name.find_last_of ('.')) == string::npos) {
|
||||
if ((delim = name.find_last_of (delimiter)) == string::npos) {
|
||||
newname = name;
|
||||
newname += ".1";
|
||||
newname += delimiter;
|
||||
newname += "1";
|
||||
} else {
|
||||
int isnumber = 1;
|
||||
const char *last_element = name.c_str() + period + 1;
|
||||
const char *last_element = name.c_str() + delim + 1;
|
||||
for (size_t i = 0; i < strlen(last_element); i++) {
|
||||
if (!isdigit(last_element[i])) {
|
||||
isnumber = 0;
|
||||
@ -91,18 +93,19 @@ string bump_name_once(std::string name)
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
|
||||
long int version = strtol (name.c_str()+delim+1, (char **)NULL, 10);
|
||||
|
||||
if (isnumber == 0 || errno != 0) {
|
||||
// last_element is not a number, or is too large
|
||||
newname = name;
|
||||
newname += ".1";
|
||||
newname += delimiter;
|
||||
newname += "1";
|
||||
} else {
|
||||
char buf[32];
|
||||
|
||||
snprintf (buf, sizeof(buf), "%ld", version+1);
|
||||
|
||||
newname = name.substr (0, period+1);
|
||||
newname = name.substr (0, delim+1);
|
||||
newname += buf;
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ class ControlEvent;
|
||||
class ControlSet : public boost::noncopyable {
|
||||
public:
|
||||
ControlSet();
|
||||
ControlSet (const ControlSet&);
|
||||
virtual ~ControlSet() {}
|
||||
|
||||
virtual boost::shared_ptr<Evoral::Control>
|
||||
|
@ -68,6 +68,7 @@ struct MIDIEvent : public Event<Time> {
|
||||
inline bool is_aftertouch() const { return (type() == MIDI_CMD_NOTE_PRESSURE); }
|
||||
inline bool is_channel_pressure() const { return (type() == MIDI_CMD_CHANNEL_PRESSURE); }
|
||||
inline uint8_t note() const { return (this->_buf[1]); }
|
||||
inline void set_note(uint8_t n) { this->_buf[1] = n; }
|
||||
inline uint8_t velocity() const { return (this->_buf[2]); }
|
||||
inline void set_velocity(uint8_t value) { this->_buf[2] = value; }
|
||||
inline void scale_velocity(float factor) {
|
||||
|
@ -61,6 +61,7 @@ template<typename Time>
|
||||
class Sequence : virtual public ControlSet {
|
||||
public:
|
||||
Sequence(const TypeMap& type_map);
|
||||
Sequence(const Sequence<Time>& other);
|
||||
|
||||
protected:
|
||||
struct WriteLockImpl {
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <stdint.h>
|
||||
#include <list>
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
@ -30,6 +31,8 @@ typedef uint32_t FrameTime;
|
||||
|
||||
/** Musical time: beats relative to some defined origin */
|
||||
typedef double MusicalTime;
|
||||
const MusicalTime MaxMusicalTime = DBL_MAX;
|
||||
const MusicalTime MinMusicalTime = DBL_MIN;
|
||||
|
||||
static inline bool musical_time_equal (MusicalTime a, MusicalTime b) {
|
||||
/* acceptable tolerance is 1 tick. Nice if there was no magic number here */
|
||||
|
@ -31,6 +31,12 @@ ControlSet::ControlSet()
|
||||
{
|
||||
}
|
||||
|
||||
ControlSet::ControlSet (const ControlSet& other)
|
||||
: noncopyable ()
|
||||
{
|
||||
/* derived class must copy controls */
|
||||
}
|
||||
|
||||
void
|
||||
ControlSet::add_control(boost::shared_ptr<Control> ac)
|
||||
{
|
||||
|
@ -398,6 +398,32 @@ Sequence<Time>::Sequence(const TypeMap& type_map)
|
||||
assert( ! _end_iter._lock);
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
Sequence<Time>::Sequence(const Sequence<Time>& other)
|
||||
: ControlSet (other)
|
||||
, _edited(false)
|
||||
, _type_map(other._type_map)
|
||||
, _writing(false)
|
||||
, _end_iter(*this, DBL_MAX)
|
||||
, _percussive(other._percussive)
|
||||
, _lowest_note(other._lowest_note)
|
||||
, _highest_note(other._highest_note)
|
||||
{
|
||||
for (typename Notes::const_iterator i = other._notes.begin(); i != other._notes.end(); ++i) {
|
||||
boost::shared_ptr<Note<Time> > n (new Note<Time> (**i));
|
||||
_notes.insert (n);
|
||||
}
|
||||
|
||||
for (typename SysExes::const_iterator i = other._sysexes.begin(); i != other._sysexes.end(); ++i) {
|
||||
boost::shared_ptr<Event<Time> > n (new Event<Time> (**i, true));
|
||||
_sysexes.push_back (n);
|
||||
}
|
||||
|
||||
DUMP(format("Sequence copied: %1%\n") % this);
|
||||
assert(_end_iter._is_end);
|
||||
assert(! _end_iter._lock);
|
||||
}
|
||||
|
||||
/** Write the controller event pointed to by \a iter to \a ev.
|
||||
* The buffer of \a ev will be allocated or resized as necessary.
|
||||
* The event_type of \a ev should be set to the expected output type.
|
||||
|
Loading…
Reference in New Issue
Block a user