lots of details relating to MIDI file management; try to ignore ALSA sequencer MIDI ports named "Midi-Through"

git-svn-id: svn://localhost/ardour2/branches/3.0@7305 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2010-06-26 13:45:59 +00:00
parent ecb0cd5d11
commit 37978aa214
17 changed files with 126 additions and 82 deletions

View file

@ -423,6 +423,16 @@ PortGroupList::gather (ARDOUR::Session* session, bool inputs, bool allow_dups)
!ardour->has_port(p) && !ardour->has_port(p) &&
!other->has_port(p)) { !other->has_port(p)) {
/* special hack: ignore MIDI ports labelled Midi-Through. these
are basically useless and mess things up for default
connections.
*/
if (p.find ("MIDI-Through") != string::npos) {
++n;
continue;
}
if (port_has_prefix (p, "system:") || if (port_has_prefix (p, "system:") ||
port_has_prefix (p, "alsa_pcm") || port_has_prefix (p, "alsa_pcm") ||
port_has_prefix (p, "ardour:")) { port_has_prefix (p, "ardour:")) {

View file

@ -41,6 +41,7 @@ public:
int move_to_trash (const Glib::ustring& trash_dir_name); int move_to_trash (const Glib::ustring& trash_dir_name);
void mark_take (const Glib::ustring& id); void mark_take (const Glib::ustring& id);
void mark_immutable (); void mark_immutable ();
void mark_nonremovable ();
const Glib::ustring& take_id () const { return _take_id; } const Glib::ustring& take_id () const { return _take_id; }
bool within_session () const { return _within_session; } bool within_session () const { return _within_session; }

View file

@ -353,7 +353,7 @@ class Region
void register_properties (); void register_properties ();
private: protected:
void use_sources (SourceList const &); void use_sources (SourceList const &);
}; };

View file

@ -106,15 +106,7 @@ class Source : public SessionObject
Flag flags() const { return _flags; } Flag flags() const { return _flags; }
void inc_use_count () { g_atomic_int_inc (&_use_count); } void inc_use_count () { g_atomic_int_inc (&_use_count); }
void dec_use_count () { void dec_use_count ();
#ifndef NDEBUG
gint oldval = g_atomic_int_exchange_and_add (&_use_count, -1);
assert (oldval > 0);
#else
g_atomic_int_exchange_and_add (&_use_count, -1);
#endif
}
int use_count() const { return g_atomic_int_get (&_use_count); } int use_count() const { return g_atomic_int_get (&_use_count); }
bool used() const { return use_count() > 0; } bool used() const { return use_count() > 0; }

View file

@ -1917,8 +1917,6 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
boost::shared_ptr<ChannelList> c = channels.reader(); boost::shared_ptr<ChannelList> c = channels.reader();
uint32_t n; uint32_t n;
cerr << name() << " resetting write sources, recrodable " << recordable() << " chans = " << c->size() << endl;
if (!_session.writable() || !recordable()) { if (!_session.writable() || !recordable()) {
return; return;
} }

View file

@ -1109,16 +1109,21 @@ AudioEngine::n_physical_outputs (DataType type) const
{ {
GET_PRIVATE_JACK_POINTER_RET (_jack,0); GET_PRIVATE_JACK_POINTER_RET (_jack,0);
const char ** ports; const char ** ports;
uint32_t i = 0; uint32_t cnt = 0;
if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsInput)) == 0) { if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsInput)) == 0) {
return 0; return 0;
} }
for (i = 0; ports[i]; ++i) {} for (uint32_t i = 0; ports[i]; ++i) {
if (!strstr (ports[i], "Midi-Through")) {
cnt++;
}
}
free (ports); free (ports);
return i; return cnt;
} }
uint32_t uint32_t
@ -1126,16 +1131,21 @@ AudioEngine::n_physical_inputs (DataType type) const
{ {
GET_PRIVATE_JACK_POINTER_RET (_jack,0); GET_PRIVATE_JACK_POINTER_RET (_jack,0);
const char ** ports; const char ** ports;
uint32_t i = 0; uint32_t cnt = 0;
if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsOutput)) == 0) { if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical|JackPortIsOutput)) == 0) {
return 0; return 0;
} }
for (i = 0; ports[i]; ++i) {} for (uint32_t i = 0; ports[i]; ++i) {
if (!strstr (ports[i], "Midi-Through")) {
cnt++;
}
}
free (ports); free (ports);
return i; return cnt;
} }
void void
@ -1150,6 +1160,9 @@ AudioEngine::get_physical_inputs (DataType type, vector<string>& ins)
if (ports) { if (ports) {
for (uint32_t i = 0; ports[i]; ++i) { for (uint32_t i = 0; ports[i]; ++i) {
if (strstr (ports[i], "Midi-Through")) {
continue;
}
ins.push_back (ports[i]); ins.push_back (ports[i]);
} }
free (ports); free (ports);
@ -1168,6 +1181,9 @@ AudioEngine::get_physical_outputs (DataType type, vector<string>& outs)
} }
for (i = 0; ports[i]; ++i) { for (i = 0; ports[i]; ++i) {
if (strstr (ports[i], "Midi-Through")) {
continue;
}
outs.push_back (ports[i]); outs.push_back (ports[i]);
} }
free (ports); free (ports);
@ -1179,6 +1195,7 @@ AudioEngine::get_nth_physical (DataType type, uint32_t n, int flag)
GET_PRIVATE_JACK_POINTER_RET (_jack,""); GET_PRIVATE_JACK_POINTER_RET (_jack,"");
const char ** ports; const char ** ports;
uint32_t i; uint32_t i;
uint32_t idx;
string ret; string ret;
assert(type != DataType::NIL); assert(type != DataType::NIL);
@ -1187,10 +1204,14 @@ AudioEngine::get_nth_physical (DataType type, uint32_t n, int flag)
return ret; return ret;
} }
for (i = 0; i < n && ports[i]; ++i) {} for (i = 0, idx = 0; idx < n && ports[i]; ++i) {
if (!strstr (ports[i], "Midi-Through")) {
++idx;
}
}
if (ports[i]) { if (ports[idx]) {
ret = ports[i]; ret = ports[idx];
} }
free ((const char **) ports); free ((const char **) ports);

View file

@ -417,6 +417,12 @@ FileSource::mark_immutable ()
} }
} }
void
FileSource::mark_nonremovable ()
{
_flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));
}
void void
FileSource::set_within_session_from_path (const std::string& path) FileSource::set_within_session_from_path (const std::string& path)
{ {

View file

@ -36,6 +36,7 @@
#include "pbd/memento_command.h" #include "pbd/memento_command.h"
#include "pbd/enumwriter.h" #include "pbd/enumwriter.h"
#include "pbd/stateful_diff_command.h" #include "pbd/stateful_diff_command.h"
#include "pbd/stacktrace.h"
#include "ardour/ardour.h" #include "ardour/ardour.h"
#include "ardour/audioengine.h" #include "ardour/audioengine.h"
@ -84,6 +85,7 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
init (); init ();
use_new_playlist (); use_new_playlist ();
use_new_write_source (0);
in_set_state = false; in_set_state = false;
@ -101,6 +103,7 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
, _frames_read_from_ringbuffer(0) , _frames_read_from_ringbuffer(0)
{ {
in_set_state = true; in_set_state = true;
init (); init ();
if (set_state (node, Stateful::loading_state_version)) { if (set_state (node, Stateful::loading_state_version)) {
@ -108,11 +111,9 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
throw failed_constructor(); throw failed_constructor();
} }
in_set_state = false; use_new_write_source (0);
if (destructive()) { in_set_state = false;
use_destructive_playlist ();
}
} }
void void
@ -183,9 +184,10 @@ MidiDiskstream::non_realtime_input_change ()
/* implicit unlock */ /* implicit unlock */
} }
/* reset capture files */ /* unlike with audio, there is never any need to reset write sources
based on input configuration changes because ... a MIDI track
reset_write_sources (false); has just 1 MIDI port as input, always.
*/
/* now refill channel buffers */ /* now refill channel buffers */
@ -945,9 +947,24 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
/* figure out the name for this take */ /* figure out the name for this take */
srcs.push_back (_write_source); srcs.push_back (_write_source);
_write_source->set_timeline_position (capture_info.front()->start); _write_source->set_timeline_position (capture_info.front()->start);
_write_source->set_captured_for (_name); _write_source->set_captured_for (_name);
/* flush to disk: this step differs from the audio path,
where all the data is already on disk.
*/
_write_source->mark_streaming_write_completed ();
/* we will want to be able to keep (over)writing the source
but we don't want it to be removable. this also differs
from the audio situation, where the source at this point
must be considered immutable
*/
_write_source->mark_nonremovable ();
string whole_file_region_name; string whole_file_region_name;
whole_file_region_name = region_name_from_path (_write_source->name(), true); whole_file_region_name = region_name_from_path (_write_source->name(), true);
@ -1021,11 +1038,11 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
_playlist->thaw (); _playlist->thaw ();
_session.add_command (new StatefulDiffCommand(_playlist)); _session.add_command (new StatefulDiffCommand(_playlist));
} }
}
mark_write_completed = true; mark_write_completed = true;
}
reset_write_sources (mark_write_completed); use_new_write_source (0);
for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
delete *ci; delete *ci;
@ -1133,10 +1150,6 @@ MidiDiskstream::engage_record_enable ()
_source_port->request_monitor_input (!(_session.config.get_auto_input() && rolling)); _source_port->request_monitor_input (!(_session.config.get_auto_input() && rolling));
} }
// FIXME: Why is this necessary? Isn't needed for AudioDiskstream...
if (!_write_source)
use_new_write_source();
_write_source->mark_streaming_midi_write_started (_note_mode, _session.transport_frame()); _write_source->mark_streaming_midi_write_started (_note_mode, _session.transport_frame());
RecordEnableChanged (); /* EMIT SIGNAL */ RecordEnableChanged (); /* EMIT SIGNAL */
@ -1293,45 +1306,19 @@ MidiDiskstream::set_state (const XMLNode& node, int /*version*/)
in_set_state = false; in_set_state = false;
/* make sure this is clear before we do anything else */
// FIXME?
//_capturing_source = 0;
/* write sources are handled when we handle the input set
up of the IO that owns this DS (::non_realtime_input_change())
*/
in_set_state = false;
return 0; return 0;
} }
int int
MidiDiskstream::use_new_write_source (uint32_t n) MidiDiskstream::use_new_write_source (uint32_t n)
{ {
cerr << name() << " use new write source for n = " << n << " recordable ? " << recordable() << endl;
if (!recordable()) { if (!recordable()) {
return 1; return 1;
} }
assert(n == 0); assert(n == 0);
if (_write_source) {
if (_write_source->is_empty ()) {
/* remove any region that is using this empty source; they can result when MIDI recordings
are made, but no MIDI data is received.
*/
_playlist->remove_region_by_source (_write_source);
_write_source->mark_for_remove ();
_write_source->drop_references ();
_write_source.reset(); _write_source.reset();
} else {
_write_source.reset();
}
}
try { try {
_write_source = boost::dynamic_pointer_cast<SMFSource>(_session.create_midi_source_for_session (0, name ())); _write_source = boost::dynamic_pointer_cast<SMFSource>(_session.create_midi_source_for_session (0, name ()));

View file

@ -51,7 +51,7 @@ using namespace PBD;
MidiRegion::MidiRegion (const SourceList& srcs) MidiRegion::MidiRegion (const SourceList& srcs)
: Region (srcs) : Region (srcs)
{ {
midi_source(0)->Switched.connect_same_thread (*this, boost::bind (&MidiRegion::switch_source, this, _1)); // midi_source(0)->Switched.connect_same_thread (*this, boost::bind (&MidiRegion::switch_source, this, _1));
midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this)); midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
model_changed (); model_changed ();
assert(_name.val().find("/") == string::npos); assert(_name.val().find("/") == string::npos);
@ -63,7 +63,7 @@ MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other, frameoffset_t
: Region (other, offset, offset_relative) : Region (other, offset, offset_relative)
{ {
assert(_name.val().find("/") == string::npos); assert(_name.val().find("/") == string::npos);
midi_source(0)->Switched.connect_same_thread (*this, boost::bind (&MidiRegion::switch_source, this, _1)); // midi_source(0)->Switched.connect_same_thread (*this, boost::bind (&MidiRegion::switch_source, this, _1));
midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this)); midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
model_changed (); model_changed ();
} }
@ -258,8 +258,11 @@ MidiRegion::switch_source(boost::shared_ptr<Source> src)
} }
// MIDI regions have only one source // MIDI regions have only one source
_sources.clear(); SourceList srcs;
_sources.push_back(msrc); srcs.push_back (msrc);
drop_sources ();
use_sources (srcs);
set_name (msrc->name()); set_name (msrc->name());

View file

@ -288,9 +288,19 @@ MidiSource::clone (Evoral::MusicalTime begin, Evoral::MusicalTime end)
void void
MidiSource::session_saved() MidiSource::session_saved()
{ {
/* this writes a copy of the data to disk.
XXX do we need to do this every time?
*/
flush_midi(); flush_midi();
cerr << name() << " @ " << this << " length at save = " << _length_beats << endl;
#if 0 // old style: clone the source if necessary on every session save
// and switch to the new source
if (_model && _model->edited()) { if (_model && _model->edited()) {
cerr << "Model exists and is edited\n";
boost::shared_ptr<MidiSource> newsrc = clone (); boost::shared_ptr<MidiSource> newsrc = clone ();
if (newsrc) { if (newsrc) {
@ -298,6 +308,7 @@ MidiSource::session_saved()
Switched (newsrc); /* EMIT SIGNAL */ Switched (newsrc); /* EMIT SIGNAL */
} }
} }
#endif
} }
void void

View file

@ -1381,6 +1381,7 @@ void
Region::set_master_sources (const SourceList& srcs) Region::set_master_sources (const SourceList& srcs)
{ {
for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) { for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
cerr << name() << " " << id() << " DEC M SMS\n";
(*i)->dec_use_count (); (*i)->dec_use_count ();
} }
@ -1535,12 +1536,14 @@ void
Region::drop_sources () Region::drop_sources ()
{ {
for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) { for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
cerr << name() << " " << id() << " DEC DS\n";
(*i)->dec_use_count (); (*i)->dec_use_count ();
} }
_sources.clear (); _sources.clear ();
for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) { for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
cerr << name() << " " << id() << " DEC MDS \n";
(*i)->dec_use_count (); (*i)->dec_use_count ();
} }

View file

@ -73,7 +73,6 @@ RegionFactory::create (boost::shared_ptr<const Region> region)
} }
if (ret) { if (ret) {
cerr << "Pure copy constructor region " << ret << " named " << ret->name() << endl;
map_add (ret); map_add (ret);
/* pure copy constructor - no property list */ /* pure copy constructor - no property list */
@ -125,7 +124,6 @@ RegionFactory::create (boost::shared_ptr<Region> region, frameoffset_t offset, b
if (ret) { if (ret) {
ret->set_properties (plist); ret->set_properties (plist);
cerr << "Partial copy constructor region\n";
map_add (ret); map_add (ret);
if (announce) { if (announce) {
@ -165,7 +163,6 @@ RegionFactory::create (boost::shared_ptr<Region> region, const SourceList& srcs,
if (ret) { if (ret) {
ret->set_properties (plist); ret->set_properties (plist);
cerr << "New sources copy constructor region\n";
map_add (ret); map_add (ret);
if (announce) { if (announce) {
@ -211,7 +208,6 @@ RegionFactory::create (const SourceList& srcs, const PropertyList& plist, bool a
if (ret) { if (ret) {
ret->set_properties (plist); ret->set_properties (plist);
cerr << "de-novo constructor region " << ret << " named " << ret->name() << endl;
map_add (ret); map_add (ret);
if (announce) { if (announce) {
@ -285,8 +281,6 @@ RegionFactory::map_add (boost::shared_ptr<Region> r)
boost::bind (&RegionFactory::region_changed, _1, boost::weak_ptr<Region> (r)) boost::bind (&RegionFactory::region_changed, _1, boost::weak_ptr<Region> (r))
); );
cerr << "Added region with ID = " << r->id() << " named " << r->name() << endl;
update_region_name_map (r); update_region_name_map (r);
} }
@ -298,7 +292,6 @@ RegionFactory::map_remove (boost::shared_ptr<Region> r)
if (i != region_map.end()) { if (i != region_map.end()) {
region_map.erase (i); region_map.erase (i);
cerr << "Removed region with ID = " << r->id() << " named " << r->name() << endl;;
} }
} }
@ -313,10 +306,8 @@ RegionFactory::map_remove_with_equivalents (boost::shared_ptr<Region> r)
++tmp; ++tmp;
if (r->region_list_equivalent (i->second)) { if (r->region_list_equivalent (i->second)) {
cerr << "Removed equivalent region " << i->second->name() << '/' << i->first << endl;
region_map.erase (i); region_map.erase (i);
} else if (r == i->second) { } else if (r == i->second) {
cerr << "Removed actual region " << i->second->name() << '/' << i->first << endl;
region_map.erase (i); region_map.erase (i);
} }

View file

@ -99,6 +99,7 @@ SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
SMFSource::~SMFSource () SMFSource::~SMFSource ()
{ {
if (removable()) { if (removable()) {
cerr << name() << " is removable, empty ? " << empty() << " UC " << use_count() << endl;
unlink (_path.c_str()); unlink (_path.c_str());
} }
} }
@ -383,6 +384,7 @@ SMFSource::mark_streaming_write_completed ()
MidiSource::mark_streaming_write_completed(); MidiSource::mark_streaming_write_completed();
if (!writable()) { if (!writable()) {
cerr << "\n\n\n[[[[[[[[[ This SMFS is not writable! ]]]]]]]]]]]\n\n\n";
return; return;
} }
@ -495,6 +497,11 @@ SMFSource::destroy_model ()
void void
SMFSource::flush_midi () SMFSource::flush_midi ()
{ {
if (!writable()) {
cerr << "\n\n\n\n " << name() << " CANNOT FLUSH - not writable\n\n\n\n";
return;
}
Evoral::SMF::end_write(); Evoral::SMF::end_write();
} }

View file

@ -272,3 +272,17 @@ Source::set_allow_remove_if_empty (bool yn)
} }
} }
void
Source::dec_use_count ()
{
#ifndef NDEBUG
gint oldval = g_atomic_int_exchange_and_add (&_use_count, -1);
cerr << "Bad use dec for " << name() << endl;
if (oldval <= 0) {
abort ();
}
assert (oldval > 0);
#else
g_atomic_int_exchange_and_add (&_use_count, -1);
#endif
}

View file

@ -285,8 +285,8 @@ SourceFactory::createWritable (DataType type, Session& s, const std::string& pat
return ret; return ret;
} else if (type == DataType::MIDI) { } else if (type == DataType::MIDI) {
// XXX writable flags should belong to MidiSource too
Source* src = new SMFSource (s, path, Source::Flag(0)); Source* src = new SMFSource (s, path, SndFileSource::default_writable_flags);
// boost_debug_shared_ptr_mark_interesting (src, "Source"); // boost_debug_shared_ptr_mark_interesting (src, "Source");
boost::shared_ptr<Source> ret (src); boost::shared_ptr<Source> ret (src);

View file

@ -275,7 +275,6 @@ SMF::begin_write()
void void
SMF::end_write() THROW_FILE_ERROR SMF::end_write() THROW_FILE_ERROR
{ {
#if 0
/* don't create empty MIDI files /* don't create empty MIDI files
*/ */
@ -283,7 +282,6 @@ SMF::end_write() THROW_FILE_ERROR
if (smf_peek_next_event (_smf) == 0) { if (smf_peek_next_event (_smf) == 0) {
return; return;
} }
#endif
PBD::StdioFileDescriptor d (_file_path, "w+"); PBD::StdioFileDescriptor d (_file_path, "w+");
FILE* f = d.allocate (); FILE* f = d.allocate ();
@ -291,6 +289,8 @@ SMF::end_write() THROW_FILE_ERROR
throw FileError (); throw FileError ();
} }
cerr << "\n\n\nSAVE SMF to " << _file_path << "\n\n";
if (smf_save(_smf, f) != 0) { if (smf_save(_smf, f) != 0) {
throw FileError(); throw FileError();
} }