mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-16 03:36:32 +01:00
dramatic change in logic and naming for operations related to adding a MIDI region on demand and cloning/unlinking
Existing code would cause data loss due to creation of two Source objects referring the same path, one with removable flags and one without. Careful code review suggested a variety of thinkos, function naming problems and other confusion that caused this. I have tried ot extensively comment what is going on with these operations, because it is one key area in which MIDI differs from audio: with audio, capture is the only way to add a new audio region, but for MIDI there are GUI input events that can add a new region.
This commit is contained in:
parent
384c0a9fac
commit
0d5f4c553a
12 changed files with 160 additions and 96 deletions
|
|
@ -4772,12 +4772,17 @@ Editor::fork_region ()
|
||||||
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
|
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
|
||||||
|
|
||||||
if (mrv) {
|
if (mrv) {
|
||||||
boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
|
try {
|
||||||
boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
|
boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
|
||||||
|
boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
|
||||||
playlist->clear_changes ();
|
boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
|
||||||
playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
|
|
||||||
_session->add_command(new StatefulDiffCommand (playlist));
|
playlist->clear_changes ();
|
||||||
|
playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
|
||||||
|
_session->add_command(new StatefulDiffCommand (playlist));
|
||||||
|
} catch (...) {
|
||||||
|
error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r = tmp;
|
r = tmp;
|
||||||
|
|
|
||||||
|
|
@ -1524,8 +1524,7 @@ MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
|
||||||
|
|
||||||
real_editor->snap_to (pos, 0);
|
real_editor->snap_to (pos, 0);
|
||||||
|
|
||||||
boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
|
boost::shared_ptr<Source> src = _session->create_midi_source_by_stealing_name (view()->trackview().track());
|
||||||
view()->trackview().track().get(), view()->trackview().track()->name());
|
|
||||||
PropertyList plist;
|
PropertyList plist;
|
||||||
|
|
||||||
plist.add (ARDOUR::Properties::start, 0);
|
plist.add (ARDOUR::Properties::start, 0);
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,6 @@ class AudioFileSource : public AudioSource, public FileSource {
|
||||||
public:
|
public:
|
||||||
virtual ~AudioFileSource ();
|
virtual ~AudioFileSource ();
|
||||||
|
|
||||||
bool set_name (const std::string& newname) {
|
|
||||||
return (set_source_name(newname, destructive()) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string peak_path (std::string audio_path);
|
std::string peak_path (std::string audio_path);
|
||||||
std::string find_broken_peakfile (std::string missing_peak_path,
|
std::string find_broken_peakfile (std::string missing_peak_path,
|
||||||
std::string audio_path);
|
std::string audio_path);
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ class MidiRegion : public Region
|
||||||
~MidiRegion();
|
~MidiRegion();
|
||||||
|
|
||||||
boost::shared_ptr<MidiRegion> clone (std::string path = std::string()) const;
|
boost::shared_ptr<MidiRegion> clone (std::string path = std::string()) const;
|
||||||
|
boost::shared_ptr<MidiRegion> clone (boost::shared_ptr<MidiSource>) const;
|
||||||
|
|
||||||
boost::shared_ptr<MidiSource> midi_source (uint32_t n=0) const;
|
boost::shared_ptr<MidiSource> midi_source (uint32_t n=0) const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,21 @@ class MidiSource : virtual public Source, public boost::enable_shared_from_this<
|
||||||
MidiSource (Session& session, const XMLNode&);
|
MidiSource (Session& session, const XMLNode&);
|
||||||
virtual ~MidiSource ();
|
virtual ~MidiSource ();
|
||||||
|
|
||||||
boost::shared_ptr<MidiSource> clone (const std::string& path,
|
/** Write the data in the given time range to another MidiSource
|
||||||
Evoral::MusicalTime begin = Evoral::MinMusicalTime,
|
* \param newsrc MidiSource to which data will be written. Should be a
|
||||||
Evoral::MusicalTime end = Evoral::MaxMusicalTime);
|
* new, empty source. If it already has contents, the results are
|
||||||
|
* undefined. Source must be writable.
|
||||||
|
*
|
||||||
|
* \param begin time of earliest event that can be written.
|
||||||
|
* \param end time of latest event that can be written.
|
||||||
|
*
|
||||||
|
* Returns zero on success, non-zero if the write failed for any
|
||||||
|
* reason.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int write_to (boost::shared_ptr<MidiSource> newsrc,
|
||||||
|
Evoral::MusicalTime begin = Evoral::MinMusicalTime,
|
||||||
|
Evoral::MusicalTime end = Evoral::MaxMusicalTime);
|
||||||
|
|
||||||
/** Read the data in a given time range from the MIDI source.
|
/** 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).
|
* All time stamps in parameters are in audio frames (even if the source has tempo time).
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
|
||||||
|
|
||||||
std::string peak_path (std::string) const;
|
std::string peak_path (std::string) const;
|
||||||
|
|
||||||
std::string change_source_path_by_name (std::string oldpath, std::string oldname, std::string newname, bool destructive);
|
std::string generate_new_source_path_from_name (std::string oldpath, std::string oldname, std::string newname, bool destructive);
|
||||||
|
|
||||||
std::string peak_path_from_audio_path (std::string) const;
|
std::string peak_path_from_audio_path (std::string) const;
|
||||||
std::string new_audio_source_name (const std::string&, uint32_t nchans, uint32_t chan, bool destructive);
|
std::string new_audio_source_name (const std::string&, uint32_t nchans, uint32_t chan, bool destructive);
|
||||||
|
|
@ -582,11 +582,12 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
|
||||||
boost::shared_ptr<AudioFileSource> create_audio_source_for_session (
|
boost::shared_ptr<AudioFileSource> create_audio_source_for_session (
|
||||||
size_t, std::string const &, uint32_t, bool destructive);
|
size_t, std::string const &, uint32_t, bool destructive);
|
||||||
|
|
||||||
boost::shared_ptr<MidiSource> create_midi_source_for_session (
|
boost::shared_ptr<MidiSource> create_midi_source_for_session (std::string const &);
|
||||||
Track*, std::string const &);
|
boost::shared_ptr<MidiSource> create_midi_source_by_stealing_name (boost::shared_ptr<Track>);
|
||||||
|
|
||||||
boost::shared_ptr<Source> source_by_id (const PBD::ID&);
|
boost::shared_ptr<Source> source_by_id (const PBD::ID&);
|
||||||
boost::shared_ptr<Source> source_by_path_and_channel (const std::string&, uint16_t);
|
boost::shared_ptr<AudioFileSource> source_by_path_and_channel (const std::string&, uint16_t) const;
|
||||||
|
boost::shared_ptr<MidiSource> source_by_path (const std::string&) const;
|
||||||
uint32_t count_sources_by_origin (const std::string&);
|
uint32_t count_sources_by_origin (const std::string&);
|
||||||
|
|
||||||
void add_playlist (boost::shared_ptr<Playlist>, bool unused = false);
|
void add_playlist (boost::shared_ptr<Playlist>, bool unused = false);
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,6 @@ public:
|
||||||
return safe_midi_file_extension(path);
|
return safe_midi_file_extension(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); }
|
|
||||||
|
|
||||||
void append_event_unlocked_beats (const Evoral::Event<Evoral::MusicalTime>& ev);
|
void append_event_unlocked_beats (const Evoral::Event<Evoral::MusicalTime>& ev);
|
||||||
void append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, framepos_t source_start);
|
void append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, framepos_t source_start);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -529,7 +529,7 @@ FileSource::set_source_name (const string& newname, bool destructive)
|
||||||
{
|
{
|
||||||
Glib::Threads::Mutex::Lock lm (_lock);
|
Glib::Threads::Mutex::Lock lm (_lock);
|
||||||
string oldpath = _path;
|
string oldpath = _path;
|
||||||
string newpath = _session.change_source_path_by_name (oldpath, _name, newname, destructive);
|
string newpath = _session.generate_new_source_path_from_name (oldpath, _name, newname, destructive);
|
||||||
|
|
||||||
if (newpath.empty()) {
|
if (newpath.empty()) {
|
||||||
error << string_compose (_("programming error: %1"), "cannot generate a changed file path") << endmsg;
|
error << string_compose (_("programming error: %1"), "cannot generate a changed file path") << endmsg;
|
||||||
|
|
|
||||||
|
|
@ -1199,7 +1199,7 @@ MidiDiskstream::use_new_write_source (uint32_t n)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_write_source = boost::dynamic_pointer_cast<SMFSource>(
|
_write_source = boost::dynamic_pointer_cast<SMFSource>(
|
||||||
_session.create_midi_source_for_session (0, name ()));
|
_session.create_midi_source_for_session (name ()));
|
||||||
|
|
||||||
if (!_write_source) {
|
if (!_write_source) {
|
||||||
throw failed_constructor();
|
throw failed_constructor();
|
||||||
|
|
@ -1224,13 +1224,19 @@ MidiDiskstream::use_new_write_source (uint32_t n)
|
||||||
std::string
|
std::string
|
||||||
MidiDiskstream::steal_write_source_name ()
|
MidiDiskstream::steal_write_source_name ()
|
||||||
{
|
{
|
||||||
std::string our_new_name = _session.new_midi_source_name (_write_source->name());
|
string our_old_name = _write_source->name();
|
||||||
std::string our_old_name = _write_source->name();
|
|
||||||
|
/* this will bump the name of the current write source to the next one
|
||||||
if (_write_source->set_source_name (our_new_name, false)) {
|
* (e.g. "MIDI 1-1" gets renamed to "MIDI 1-2"), thus leaving the
|
||||||
|
* current write source name (e.g. "MIDI 1-1" available). See the
|
||||||
|
* comments in Session::create_midi_source_for_track() about why we do
|
||||||
|
* this.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (_write_source->set_source_name (name(), false)) {
|
||||||
return string();
|
return string();
|
||||||
}
|
}
|
||||||
|
|
||||||
return our_old_name;
|
return our_old_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <glibmm/threads.h>
|
#include <glibmm/threads.h>
|
||||||
|
#include <glibmm/fileutils.h>
|
||||||
|
#include <glibmm/miscutils.h>
|
||||||
|
|
||||||
#include "pbd/xml++.h"
|
#include "pbd/xml++.h"
|
||||||
#include "pbd/basename.h"
|
#include "pbd/basename.h"
|
||||||
|
|
@ -36,6 +38,7 @@
|
||||||
#include "ardour/midi_source.h"
|
#include "ardour/midi_source.h"
|
||||||
#include "ardour/region_factory.h"
|
#include "ardour/region_factory.h"
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
|
#include "ardour/source_factory.h"
|
||||||
#include "ardour/tempo.h"
|
#include "ardour/tempo.h"
|
||||||
#include "ardour/types.h"
|
#include "ardour/types.h"
|
||||||
|
|
||||||
|
|
@ -126,16 +129,31 @@ MidiRegion::~MidiRegion ()
|
||||||
*/
|
*/
|
||||||
boost::shared_ptr<MidiRegion>
|
boost::shared_ptr<MidiRegion>
|
||||||
MidiRegion::clone (string path) const
|
MidiRegion::clone (string path) const
|
||||||
|
{
|
||||||
|
boost::shared_ptr<MidiSource> newsrc;
|
||||||
|
|
||||||
|
/* caller must check for pre-existing file */
|
||||||
|
assert (!Glib::file_test (path, Glib::FILE_TEST_EXISTS));
|
||||||
|
newsrc = boost::dynamic_pointer_cast<MidiSource>(
|
||||||
|
SourceFactory::createWritable(DataType::MIDI, _session,
|
||||||
|
path, false, _session.frame_rate()));
|
||||||
|
return clone (newsrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<MidiRegion>
|
||||||
|
MidiRegion::clone (boost::shared_ptr<MidiSource> newsrc) const
|
||||||
{
|
{
|
||||||
BeatsFramesConverter bfc (_session.tempo_map(), _position);
|
BeatsFramesConverter bfc (_session.tempo_map(), _position);
|
||||||
Evoral::MusicalTime const bbegin = bfc.from (_start);
|
Evoral::MusicalTime const bbegin = bfc.from (_start);
|
||||||
Evoral::MusicalTime const bend = bfc.from (_start + _length);
|
Evoral::MusicalTime const bend = bfc.from (_start + _length);
|
||||||
|
|
||||||
boost::shared_ptr<MidiSource> ms = midi_source(0)->clone (path, bbegin, bend);
|
if (midi_source(0)->write_to (newsrc, bbegin, bend)) {
|
||||||
|
return boost::shared_ptr<MidiRegion> ();
|
||||||
|
}
|
||||||
|
|
||||||
PropertyList plist;
|
PropertyList plist;
|
||||||
|
|
||||||
plist.add (Properties::name, PBD::basename_nosuffix (ms->name()));
|
plist.add (Properties::name, PBD::basename_nosuffix (newsrc->name()));
|
||||||
plist.add (Properties::whole_file, true);
|
plist.add (Properties::whole_file, true);
|
||||||
plist.add (Properties::start, _start);
|
plist.add (Properties::start, _start);
|
||||||
plist.add (Properties::start_beats, _start_beats);
|
plist.add (Properties::start_beats, _start_beats);
|
||||||
|
|
@ -143,7 +161,7 @@ MidiRegion::clone (string path) const
|
||||||
plist.add (Properties::length_beats, _length_beats);
|
plist.add (Properties::length_beats, _length_beats);
|
||||||
plist.add (Properties::layer, 0);
|
plist.add (Properties::layer, 0);
|
||||||
|
|
||||||
return boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (ms, plist, true));
|
return boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (newsrc, plist, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -333,29 +333,9 @@ MidiSource::mark_streaming_write_completed ()
|
||||||
mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
|
mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr<MidiSource>
|
int
|
||||||
MidiSource::clone (const string& path, Evoral::MusicalTime begin, Evoral::MusicalTime end)
|
MidiSource::write_to (boost::shared_ptr<MidiSource> newsrc, Evoral::MusicalTime begin, Evoral::MusicalTime end)
|
||||||
{
|
{
|
||||||
string newpath;
|
|
||||||
|
|
||||||
/* get a new name for the MIDI file we're going to write to
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (path.empty()) {
|
|
||||||
string newname = PBD::basename_nosuffix(_name.val());
|
|
||||||
newname = bump_name_once (newname, '-');
|
|
||||||
newname += ".mid";
|
|
||||||
newpath = _session.new_source_path_from_name (DataType::MIDI, newname);
|
|
||||||
} else {
|
|
||||||
/* caller must check for pre-existing file */
|
|
||||||
assert (!Glib::file_test (path, Glib::FILE_TEST_EXISTS));
|
|
||||||
newpath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
newsrc->set_timeline_position(_timeline_position);
|
||||||
newsrc->copy_interpolation_from (this);
|
newsrc->copy_interpolation_from (this);
|
||||||
newsrc->copy_automation_state_from (this);
|
newsrc->copy_automation_state_from (this);
|
||||||
|
|
@ -368,7 +348,7 @@ MidiSource::clone (const string& path, Evoral::MusicalTime begin, Evoral::Musica
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
|
error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
|
||||||
return boost::shared_ptr<MidiSource>();
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
newsrc->flush_midi();
|
newsrc->flush_midi();
|
||||||
|
|
@ -385,7 +365,7 @@ MidiSource::clone (const string& path, Evoral::MusicalTime begin, Evoral::Musica
|
||||||
|
|
||||||
boost::dynamic_pointer_cast<FileSource> (newsrc)->prevent_deletion ();
|
boost::dynamic_pointer_cast<FileSource> (newsrc)->prevent_deletion ();
|
||||||
|
|
||||||
return newsrc;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -3304,12 +3304,16 @@ Session::source_by_id (const PBD::ID& id)
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr<Source>
|
boost::shared_ptr<AudioFileSource>
|
||||||
Session::source_by_path_and_channel (const string& path, uint16_t chn)
|
Session::source_by_path_and_channel (const string& path, uint16_t chn) const
|
||||||
{
|
{
|
||||||
|
/* Restricted to audio files because only audio sources have channel
|
||||||
|
as a property.
|
||||||
|
*/
|
||||||
|
|
||||||
Glib::Threads::Mutex::Lock lm (source_lock);
|
Glib::Threads::Mutex::Lock lm (source_lock);
|
||||||
|
|
||||||
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
|
for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
|
||||||
boost::shared_ptr<AudioFileSource> afs
|
boost::shared_ptr<AudioFileSource> afs
|
||||||
= boost::dynamic_pointer_cast<AudioFileSource>(i->second);
|
= boost::dynamic_pointer_cast<AudioFileSource>(i->second);
|
||||||
|
|
||||||
|
|
@ -3317,7 +3321,31 @@ Session::source_by_path_and_channel (const string& path, uint16_t chn)
|
||||||
return afs;
|
return afs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return boost::shared_ptr<Source>();
|
|
||||||
|
return boost::shared_ptr<AudioFileSource>();
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<MidiSource>
|
||||||
|
Session::source_by_path (const std::string& path) const
|
||||||
|
{
|
||||||
|
/* Restricted to MIDI files because audio sources require a channel
|
||||||
|
for unique identification, in addition to a path.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Glib::Threads::Mutex::Lock lm (source_lock);
|
||||||
|
|
||||||
|
for (SourceMap::const_iterator s = sources.begin(); s != sources.end(); ++s) {
|
||||||
|
boost::shared_ptr<MidiSource> ms
|
||||||
|
= boost::dynamic_pointer_cast<MidiSource>(s->second);
|
||||||
|
boost::shared_ptr<FileSource> fs
|
||||||
|
= boost::dynamic_pointer_cast<FileSource>(s->second);
|
||||||
|
|
||||||
|
if (ms && fs && fs->path() == path) {
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return boost::shared_ptr<MidiSource>();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
|
|
@ -3338,9 +3366,8 @@ Session::count_sources_by_origin (const string& path)
|
||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string
|
string
|
||||||
Session::change_source_path_by_name (string path, string oldname, string newname, bool destructive)
|
Session::generate_new_source_path_from_name (string path, string oldname, string newname, bool destructive)
|
||||||
{
|
{
|
||||||
string look_for;
|
string look_for;
|
||||||
string old_basename = PBD::basename_nosuffix (oldname);
|
string old_basename = PBD::basename_nosuffix (oldname);
|
||||||
|
|
@ -3427,7 +3454,9 @@ Session::change_source_path_by_name (string path, string oldname, string newname
|
||||||
|
|
||||||
if (!matching_unsuffixed_filename_exists_in (dir, buf)) {
|
if (!matching_unsuffixed_filename_exists_in (dir, buf)) {
|
||||||
path = Glib::build_filename (dir, buf);
|
path = Glib::build_filename (dir, buf);
|
||||||
break;
|
if (!source_by_path (path)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
path = "";
|
path = "";
|
||||||
|
|
@ -3575,17 +3604,18 @@ Session::create_audio_source_for_session (size_t n_chans, string const & n, uint
|
||||||
SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
|
SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a unique name based on \a base for a new internal MIDI source */
|
/** Return a unique name based on \a owner_name for a new internal MIDI source */
|
||||||
string
|
string
|
||||||
Session::new_midi_source_name (const string& base)
|
Session::new_midi_source_name (const string& owner_name)
|
||||||
{
|
{
|
||||||
uint32_t cnt;
|
uint32_t cnt;
|
||||||
char buf[PATH_MAX+1];
|
char buf[PATH_MAX+1];
|
||||||
const uint32_t limit = 10000;
|
const uint32_t limit = 10000;
|
||||||
string legalized;
|
string legalized;
|
||||||
|
string possible_name;
|
||||||
|
|
||||||
buf[0] = '\0';
|
buf[0] = '\0';
|
||||||
legalized = legalize_for_path (base);
|
legalized = legalize_for_path (owner_name);
|
||||||
|
|
||||||
// Find a "version" of the file name that doesn't exist in any of the possible directories.
|
// Find a "version" of the file name that doesn't exist in any of the possible directories.
|
||||||
|
|
||||||
|
|
@ -3597,12 +3627,17 @@ Session::new_midi_source_name (const string& base)
|
||||||
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
|
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
|
||||||
|
|
||||||
SessionDirectory sdir((*i).path);
|
SessionDirectory sdir((*i).path);
|
||||||
|
|
||||||
|
snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt);
|
||||||
|
possible_name = buf;
|
||||||
|
|
||||||
std::string p = Glib::build_filename (sdir.midi_path(), legalized);
|
std::string possible_path = Glib::build_filename (sdir.midi_path(), possible_name);
|
||||||
|
|
||||||
|
if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) {
|
||||||
|
existing++;
|
||||||
|
}
|
||||||
|
|
||||||
snprintf (buf, sizeof(buf), "%s-%u.mid", p.c_str(), cnt);
|
if (source_by_path (possible_path)) {
|
||||||
|
|
||||||
if (Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) {
|
|
||||||
existing++;
|
existing++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3614,49 +3649,62 @@ Session::new_midi_source_name (const string& base)
|
||||||
if (cnt > limit) {
|
if (cnt > limit) {
|
||||||
error << string_compose(
|
error << string_compose(
|
||||||
_("There are already %1 recordings for %2, which I consider too many."),
|
_("There are already %1 recordings for %2, which I consider too many."),
|
||||||
limit, base) << endmsg;
|
limit, owner_name) << endmsg;
|
||||||
destroy ();
|
destroy ();
|
||||||
throw failed_constructor();
|
throw failed_constructor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Glib::path_get_basename(buf);
|
return possible_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Create a new within-session MIDI source */
|
/** Create a new within-session MIDI source */
|
||||||
boost::shared_ptr<MidiSource>
|
boost::shared_ptr<MidiSource>
|
||||||
Session::create_midi_source_for_session (Track* track, string const & n)
|
Session::create_midi_source_for_session (string const & basic_name)
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
if (track) {
|
if (name.empty()) {
|
||||||
/* the caller passes in the track the source will be used in,
|
name = new_midi_source_name (basic_name);
|
||||||
so that we can keep the numbering sane.
|
|
||||||
|
|
||||||
Rationale: a track with the name "Foo" that has had N
|
|
||||||
captures carried out so far will already have a write source
|
|
||||||
named "Foo-N+1.mid" waiting to be used for the next capture.
|
|
||||||
|
|
||||||
If we call new_midi_source_name() we will get "Foo-N+2". But
|
|
||||||
there is no region corresponding to "Foo-N+1", so when
|
|
||||||
"Foo-N+2" appears in the track, the gap presents the user
|
|
||||||
with odd behaviour - why did it skip past Foo-N+1?
|
|
||||||
|
|
||||||
We could explain this to the user in some odd way, but
|
|
||||||
instead we rename "Foo-N+1.mid" as "Foo-N+2.mid", and then
|
|
||||||
use "Foo-N+1" here.
|
|
||||||
|
|
||||||
If that attempted rename fails, we get "Foo-N+2.mid" anyway.
|
|
||||||
*/
|
|
||||||
|
|
||||||
MidiTrack* mt = dynamic_cast<MidiTrack*> (track);
|
|
||||||
assert (mt);
|
|
||||||
name = track->steal_write_source_name ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const string path = new_source_path_from_name (DataType::MIDI, name);
|
||||||
|
|
||||||
|
return boost::dynamic_pointer_cast<SMFSource> (
|
||||||
|
SourceFactory::createWritable (
|
||||||
|
DataType::MIDI, *this, path, false, frame_rate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create a new within-session MIDI source */
|
||||||
|
boost::shared_ptr<MidiSource>
|
||||||
|
Session::create_midi_source_by_stealing_name (boost::shared_ptr<Track> track)
|
||||||
|
{
|
||||||
|
/* the caller passes in the track the source will be used in,
|
||||||
|
so that we can keep the numbering sane.
|
||||||
|
|
||||||
|
Rationale: a track with the name "Foo" that has had N
|
||||||
|
captures carried out so far will ALREADY have a write source
|
||||||
|
named "Foo-N+1.mid" waiting to be used for the next capture.
|
||||||
|
|
||||||
|
If we call new_midi_source_name() we will get "Foo-N+2". But
|
||||||
|
there is no region corresponding to "Foo-N+1", so when
|
||||||
|
"Foo-N+2" appears in the track, the gap presents the user
|
||||||
|
with odd behaviour - why did it skip past Foo-N+1?
|
||||||
|
|
||||||
|
We could explain this to the user in some odd way, but
|
||||||
|
instead we rename "Foo-N+1.mid" as "Foo-N+2.mid", and then
|
||||||
|
use "Foo-N+1" here.
|
||||||
|
|
||||||
|
If that attempted rename fails, we get "Foo-N+2.mid" anyway.
|
||||||
|
*/
|
||||||
|
|
||||||
|
boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (track);
|
||||||
|
assert (mt);
|
||||||
|
std::string name = track->steal_write_source_name ();
|
||||||
|
|
||||||
if (name.empty()) {
|
if (name.empty()) {
|
||||||
name = new_midi_source_name (n);
|
return boost::shared_ptr<MidiSource>();
|
||||||
}
|
}
|
||||||
|
|
||||||
const string path = new_source_path_from_name (DataType::MIDI, name);
|
const string path = new_source_path_from_name (DataType::MIDI, name);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue