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:
Paul Davis 2010-05-19 03:03:28 +00:00
parent c25c7598c1
commit e258b2622a
31 changed files with 331 additions and 63 deletions

View File

@ -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());
}

View File

@ -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);

View File

@ -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 ()
{

View File

@ -21,7 +21,7 @@
using namespace ARDOUR;
MidiCutBuffer::MidiCutBuffer (Session* s)
: AutomatableSequence<MidiModel::TimeType> (*s, 0)
: AutomatableSequence<MidiModel::TimeType> (*s)
, _origin (0)
{

View File

@ -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,

View File

@ -42,6 +42,7 @@ class Automatable : virtual public Evoral::ControlSet
{
public:
Automatable(Session&);
Automatable (const Automatable& other);
Automatable();
virtual ~Automatable() {}

View File

@ -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

View File

@ -46,6 +46,7 @@ namespace PBD {
extern uint64_t MidiClock;
extern uint64_t Monitor;
extern uint64_t Solo;
extern uint64_t AudioPlayback;
}
}

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -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,

View File

@ -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)
{

View File

@ -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");

View File

@ -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();

View File

@ -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)
{

View File

@ -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 */
}
}
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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());

View File

@ -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;
}
}

View File

@ -36,6 +36,7 @@ class ControlEvent;
class ControlSet : public boost::noncopyable {
public:
ControlSet();
ControlSet (const ControlSet&);
virtual ~ControlSet() {}
virtual boost::shared_ptr<Evoral::Control>

View File

@ -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) {

View File

@ -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 {

View File

@ -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 */

View File

@ -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)
{

View File

@ -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.