Less crash-happy MIDI reading on weird MIDI files.

Make "show existing automation" create/show automation tracks for all contained CC in MIDI tracks.
Fix staggered time when importing multi-track MIDI files.


git-svn-id: svn://localhost/ardour2/branches/3.0@3086 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2008-02-19 03:24:44 +00:00
parent b79d5bfad3
commit 012292b4bd
12 changed files with 92 additions and 53 deletions

View File

@ -529,7 +529,7 @@ Editor::register_actions ()
/* the next two are duplicate items with different names for use in two different contexts */ /* the next two are duplicate items with different names for use in two different contexts */
ActionManager::register_action (editor_actions, X_("addExistingAudioFiles"), _("Add Existing Media"), mem_fun (*this, &Editor::external_audio_dialog)); ActionManager::register_action (editor_actions, X_("addExistingAudioFiles"), _("Import Existing Media"), mem_fun (*this, &Editor::external_audio_dialog));
act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Add External Media"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion)); act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Add External Media"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion));
ActionManager::session_sensitive_actions.push_back (act); ActionManager::session_sensitive_actions.push_back (act);

View File

@ -734,7 +734,9 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
} }
} }
cout << "TARGET REGIONS: " << target_regions << endl; // kludge (for MIDI we're abusing "channel" for "track" here)
if (paths.front().rfind(".mid") != Glib::ustring::npos)
target_regions = -1;
if (target_regions == 1) { if (target_regions == 1) {

View File

@ -262,7 +262,8 @@ MidiTimeAxisView::set_note_range(MidiStreamView::VisibleNoteRange range)
void void
MidiTimeAxisView::update_range() { MidiTimeAxisView::update_range()
{
MidiGhostRegion* mgr; MidiGhostRegion* mgr;
for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) { for(list<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
@ -272,6 +273,20 @@ MidiTimeAxisView::update_range() {
} }
} }
void
MidiTimeAxisView::show_existing_automation ()
{
if (midi_track()) {
const set<Parameter> params = midi_track()->midi_diskstream()->
midi_playlist()->contained_automation();
for (set<Parameter>::const_iterator i = params.begin(); i != params.end(); ++i)
create_automation_child(*i, true);
}
RouteTimeAxisView::show_existing_automation ();
}
/** Prompt for a controller with a dialog and add an automation track for it /** Prompt for a controller with a dialog and add an automation track for it
*/ */
void void
@ -298,7 +313,8 @@ MidiTimeAxisView::create_automation_child (Parameter param, bool show)
{ {
if (param.type() == MidiCCAutomation) { if (param.type() == MidiCCAutomation) {
/* FIXME: don't create AutomationList for track itself */ /* FIXME: don't create AutomationList for track itself
* (not actually needed or used, since the automation is region-ey) */
boost::shared_ptr<AutomationControl> c = _route->control(param); boost::shared_ptr<AutomationControl> c = _route->control(param);
@ -308,6 +324,10 @@ MidiTimeAxisView::create_automation_child (Parameter param, bool show)
_route->add_control(c); _route->add_control(c);
} }
AutomationTracks::iterator existing = _automation_tracks.find(param);
if (existing != _automation_tracks.end())
return;
boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session, boost::shared_ptr<AutomationTimeAxisView> track(new AutomationTimeAxisView (_session,
_route, _route, c, _route, _route, c,
editor, editor,

View File

@ -65,6 +65,7 @@ class MidiTimeAxisView : public RouteTimeAxisView
guint32 show_at (double y, int& nth, Gtk::VBox *parent); guint32 show_at (double y, int& nth, Gtk::VBox *parent);
void hide (); void hide ();
void show_existing_automation ();
void add_controller_track (); void add_controller_track ();
void create_automation_child (ARDOUR::Parameter param, bool show); void create_automation_child (ARDOUR::Parameter param, bool show);

View File

@ -25,6 +25,7 @@
#include <ardour/ardour.h> #include <ardour/ardour.h>
#include <ardour/playlist.h> #include <ardour/playlist.h>
#include <ardour/parameter.h>
namespace ARDOUR namespace ARDOUR
{ {
@ -56,6 +57,8 @@ public:
void set_note_mode (NoteMode m) { _note_mode = m; } void set_note_mode (NoteMode m) { _note_mode = m; }
std::set<Parameter> contained_automation();
protected: protected:
/* playlist "callbacks" */ /* playlist "callbacks" */

View File

@ -65,13 +65,13 @@ public:
throw (std::logic_error, PrematureEOF, CorruptFile); throw (std::logic_error, PrematureEOF, CorruptFile);
void close(); void close();
static uint32_t read_var_len(FILE* fd) throw (PrematureEOF);
protected: protected:
/** size of SMF header, including MTrk chunk header */ /** size of SMF header, including MTrk chunk header */
static const uint32_t HEADER_SIZE = 22; static const uint32_t HEADER_SIZE = 22;
uint32_t read_var_len() const throw(PrematureEOF);
std::string _filename; std::string _filename;
FILE* _fd; FILE* _fd;
//TimeUnit _unit; //TimeUnit _unit;

View File

@ -1029,9 +1029,9 @@ AutomationList::build_search_cache_if_necessary(double start, double end) const
{ {
/* Only do the range lookup if x is in a different range than last time /* Only do the range lookup if x is in a different range than last time
* this was called (or if the search cache has been marked "dirty" (left<0) */ * this was called (or if the search cache has been marked "dirty" (left<0) */
if ((_search_cache.left < 0) || if (!_events.empty() && ((_search_cache.left < 0) ||
((_search_cache.left > start) || ((_search_cache.left > start) ||
(_search_cache.right < end))) { (_search_cache.right < end)))) {
const ControlEvent start_point (start, 0); const ControlEvent start_point (start, 0);
const ControlEvent end_point (end, 0); const ControlEvent end_point (end, 0);
@ -1140,7 +1140,9 @@ AutomationList::rt_safe_earliest_event_linear_unlocked (double start, double end
{ {
//cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl; //cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl;
if (_events.size() < 2) if (_events.size() == 0)
return false;
else if (_events.size() == 1)
return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive); return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
// Hack to avoid infinitely repeating the same event // Hack to avoid infinitely repeating the same event

View File

@ -270,10 +270,6 @@ write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
{ {
MidiEvent ev(0.0, 4, NULL, true); MidiEvent ev(0.0, 4, NULL, true);
uint64_t t = 0;
uint32_t delta_t = 0;
uint32_t size = 0;
status.progress = 0.0f; status.progress = 0.0f;
try { try {
@ -283,6 +279,10 @@ write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource>(newfiles[i-1]); boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource>(newfiles[i-1]);
source->seek_to_track(i); source->seek_to_track(i);
uint64_t t = 0;
uint32_t delta_t = 0;
uint32_t size = 0;
while (!status.cancel) { while (!status.cancel) {

View File

@ -276,12 +276,6 @@ MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes
//cerr << "Using cached iterator at " << _next_read << endl; //cerr << "Using cached iterator at " << _next_read << endl;
} }
if (_read_iter == end()) {
//cerr << this << " MM::read: at end @ " << _read_iter->time() << endl;
} else {
//cerr << this << " MM::read: at " << _read_iter->time() << endl;
}
_next_read = start + nframes; _next_read = start + nframes;
while (_read_iter != end() && _read_iter->time() < start + nframes) { while (_read_iter != end() && _read_iter->time() < start + nframes) {

View File

@ -265,6 +265,30 @@ MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
return changed; return changed;
} }
set<Parameter>
MidiPlaylist::contained_automation()
{
/* this function is never called from a realtime thread, so
its OK to block (for short intervals).
*/
Glib::Mutex::Lock rm (region_lock);
set<Parameter> ret;
for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
for (Automatable::Controls::iterator c = mr->controls().begin();
c != mr->controls().end(); ++c) {
ret.insert(c->first);
}
}
return ret;
}
bool bool
MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region) MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
{ {

View File

@ -194,7 +194,7 @@ SMFReader::read_event(size_t buf_len,
static uint8_t last_status = 0; static uint8_t last_status = 0;
static uint32_t last_size = 0; static uint32_t last_size = 0;
*delta_time = read_var_len(); *delta_time = read_var_len(_fd);
int status = fgetc(_fd); int status = fgetc(_fd);
if (status == EOF) if (status == EOF)
throw PrematureEOF(); throw PrematureEOF();
@ -222,7 +222,7 @@ SMFReader::read_event(size_t buf_len,
if (feof(_fd)) if (feof(_fd))
throw PrematureEOF(); throw PrematureEOF();
uint8_t type = fgetc(_fd); uint8_t type = fgetc(_fd);
const uint32_t size = read_var_len(); const uint32_t size = read_var_len(_fd);
/*cerr.flags(ios::hex); /*cerr.flags(ios::hex);
cerr << "SMF - meta 0x" << (int)type << ", size = "; cerr << "SMF - meta 0x" << (int)type << ", size = ";
cerr.flags(ios::dec); cerr.flags(ios::dec);
@ -269,20 +269,20 @@ SMFReader::close()
uint32_t uint32_t
SMFReader::read_var_len() const throw(PrematureEOF) SMFReader::read_var_len(FILE* fd) throw (PrematureEOF)
{ {
if (feof(_fd)) if (feof(fd))
throw PrematureEOF(); throw PrematureEOF();
uint32_t value; uint32_t value;
uint8_t c; uint8_t c;
if ( (value = getc(_fd)) & 0x80 ) { if ( (value = getc(fd)) & 0x80 ) {
value &= 0x7F; value &= 0x7F;
do { do {
if (feof(_fd)) if (feof(fd))
throw PrematureEOF(); throw PrematureEOF();
value = (value << 7) + ((c = getc(_fd)) & 0x7F); value = (value << 7) + ((c = getc(fd)) & 0x7F);
} while (c & 0x80); } while (c & 0x80);
} }

View File

@ -38,6 +38,7 @@
#include <ardour/midi_util.h> #include <ardour/midi_util.h>
#include <ardour/tempo.h> #include <ardour/tempo.h>
#include <ardour/audioengine.h> #include <ardour/audioengine.h>
#include <ardour/smf_reader.h>
#include "i18n.h" #include "i18n.h"
@ -272,20 +273,31 @@ SMFSource::read_event(uint32_t* delta_t, uint32_t* size, Byte** buf) const
assert(size); assert(size);
assert(buf); assert(buf);
*delta_t = read_var_len(); try {
assert(!feof(_fd)); *delta_t = SMFReader::read_var_len(_fd);
} catch (...) {
return -1; // Premature EOF
}
if (feof(_fd)) {
return -1; // Premature EOF
}
const int status = fgetc(_fd); const int status = fgetc(_fd);
assert(status != EOF); // FIXME die gracefully
if (status == EOF) {
return -1; // Premature EOF
}
//printf("Status @ %X = %X\n", (unsigned)ftell(_fd) - 1, status); //printf("Status @ %X = %X\n", (unsigned)ftell(_fd) - 1, status);
if (status == 0xFF) { if (status == 0xFF) {
assert(!feof(_fd)); if (feof(_fd)) {
return -1; // Premature EOF
}
const int type = fgetc(_fd); const int type = fgetc(_fd);
if ((unsigned char)type == 0x2F) { if ((unsigned char)type == 0x2F) {
//cerr << _name << " hit EOT" << endl; return -1; // hit end of track
return -1;
} else { } else {
*size = 0; *size = 0;
return 0; return 0;
@ -444,12 +456,12 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
void void
SMFSource::append_event_unlocked(const MidiEvent& ev) SMFSource::append_event_unlocked(const MidiEvent& ev)
{ {
printf("%s - append chan = %u, time = %lf, size = %u, data = ", _path.c_str(), /*printf("%s - append chan = %u, time = %lf, size = %u, data = ", _path.c_str(),
(unsigned)ev.channel(), ev.time(), ev.size()); (unsigned)ev.channel(), ev.time(), ev.size());
for (size_t i=0; i < ev.size(); ++i) { for (size_t i=0; i < ev.size(); ++i) {
printf("%X ", ev.buffer()[i]); printf("%X ", ev.buffer()[i]);
} }
printf("\n"); printf("\n");*/
assert(ev.time() >= 0); assert(ev.time() >= 0);
assert(ev.time() >= _last_ev_time); assert(ev.time() >= _last_ev_time);
@ -838,25 +850,6 @@ SMFSource::write_var_len(uint32_t value)
return ret; return ret;
} }
uint32_t
SMFSource::read_var_len() const
{
assert(!feof(_fd));
uint32_t value;
unsigned char c;
if ( (value = getc(_fd)) & 0x80 ) {
value &= 0x7F;
do {
assert(!feof(_fd));
value = (value << 7) + ((c = getc(_fd)) & 0x7F);
} while (c & 0x80);
}
return value;
}
void void
SMFSource::load_model(bool lock, bool force_reload) SMFSource::load_model(bool lock, bool force_reload)
{ {