* recording/playing/saving/loading program changes basically seems to work now
authorHans Baier <hansfbaier@googlemail.com>
Sat, 26 Apr 2008 00:12:54 +0000 (00:12 +0000)
committerHans Baier <hansfbaier@googlemail.com>
Sat, 26 Apr 2008 00:12:54 +0000 (00:12 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@3288 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/midi_model.h
libs/ardour/midi_model.cc
libs/ardour/smf_source.cc

index 7a8dd0bad633000ac7f09a64ff91b3a571166dca..7e5b6aac72996e2b78c7b7e0e2f4b3dcb1913613 100644 (file)
@@ -110,7 +110,7 @@ public:
        inline       Notes& notes()       { return _notes; }
        inline const Notes& notes() const { return _notes; }
 
-       typedef std::vector<MIDI::Event> PgmChanges;
+       typedef std::vector< boost::shared_ptr<MIDI::Event> > PgmChanges;
        inline       PgmChanges& pgm_changes()       { return _pgm_changes; }
        inline const PgmChanges& pgm_changes() const { return _pgm_changes; }
 
@@ -219,6 +219,7 @@ private:
        void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity);
        void append_note_off_unlocked(uint8_t chan, double time, uint8_t note);
        void append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t value);
+       void append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number); 
 
        mutable Glib::RWLock _lock;
 
index 374eaed6d9f05ecefff15c5537a7fb7fd4a1873d..4c811604ee83ccca90a8e80546d91d05fd60a5ad 100644 (file)
@@ -122,7 +122,7 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
        if (_note_iter != model.notes().end()) {
                _event = MIDI::Event((*_note_iter)->on_event(), false);
                _active_notes.push(*_note_iter);
-               cerr << " new const iterator: size active notes: " << _active_notes.size() << " is empty: " << _active_notes.empty() << endl;
+               //cerr << " new const iterator: size active notes: " << _active_notes.size() << " is empty: " << _active_notes.empty() << endl;
                ++_note_iter;
        }
 
@@ -133,16 +133,16 @@ MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
 
        _pgm_change_iter = model.pgm_changes().end();
        // find first program change which begins after t
-       for (vector<MIDI::Event>::const_iterator i = model.pgm_changes().begin(); i != model.pgm_changes().end(); ++i) {
-               if (i->time() >= t) {
+       for (PgmChanges::const_iterator i = model.pgm_changes().begin(); i != model.pgm_changes().end(); ++i) {
+               if ((*i)->time() >= t) {
                        _pgm_change_iter = i;
                        break;
                }
        }
        
        if(_pgm_change_iter != model.pgm_changes().end()) {
-               if(_pgm_change_iter->time() <= _event.time()) {
-                       _event = MIDI::Event((*_pgm_change_iter), true);
+               if((*_pgm_change_iter)->time() <= _event.time()) {
+                       _event = MIDI::Event(*(*_pgm_change_iter), false);
                }
        }
        
@@ -174,7 +174,15 @@ MidiModel::const_iterator::operator++()
        if (_is_end)
                throw std::logic_error("Attempt to iterate past end of MidiModel");
 
-       assert(_event.is_note() || _event.is_cc());
+       cerr << "const_iterator::operator++: _event type:" << hex << "0x" << int(_event.type()) 
+            << "   buffer: 0x" << int(_event.buffer()[0]) << " 0x" << int(_event.buffer()[1]) 
+            << " 0x" << int(_event.buffer()[2]) << endl;
+
+       if(! (_event.is_note() || _event.is_cc() || _event.is_pgm_change())) {
+               cerr << "FAILED pgm change vector size: " << _model->pgm_changes().size() << endl;
+               cerr << "FAILED event buffer: " << hex << int(_event.buffer()[0]) << int(_event.buffer()[1]) << int(_event.buffer()[2]) << endl;
+       }
+       assert(_event.is_note() || _event.is_cc() || _event.is_pgm_change());
 
        // TODO: This code crashes at the marked section
        /*
@@ -237,9 +245,12 @@ MidiModel::const_iterator::operator++()
                        type = CC;
        */
        if(_pgm_change_iter != _model->pgm_changes().end()) {
-               if(_pgm_change_iter->time() <= t) {
+               if((*_pgm_change_iter)->time() <= t) {
                        type = PGM_CHANGE;
-                       t = _pgm_change_iter->time();
+                       t = (*_pgm_change_iter)->time();
+                       cerr << "operator++ got PGM CHANGE with time " << t << " and type " << hex
+                       << int((*_pgm_change_iter)->type()) << " and channel " << int((*_pgm_change_iter)->channel()) 
+                       << " and program " << int((*_pgm_change_iter)->pgm_number()) << endl;
                }
        }
 
@@ -257,7 +268,8 @@ MidiModel::const_iterator::operator++()
                _model->control_to_midi_event(_event, *_control_iter);
        } else if (type == PGM_CHANGE) {
                cerr << "********** MIDI Iterator = program change" << endl;
-               _event = MIDI::Event(*_pgm_change_iter, true);
+               _event = MIDI::Event(*(*_pgm_change_iter), false);
+               ++_pgm_change_iter;
        } else {
                cerr << "********** MIDI Iterator = END" << endl;
                _is_end = true;
@@ -327,16 +339,16 @@ MidiModel::MidiModel(MidiSource *s, size_t size)
 size_t
 MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const
 {
-       cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl;
-       cerr << this << " MM # notes: " << n_notes() << endl;
+       //cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl;
+       //cerr << this << " MM # notes: " << n_notes() << endl;
 
        size_t read_events = 0;
 
        if (start != _next_read) {
                _read_iter = const_iterator(*this, (double)start);
-               cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
+               //cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
        } else {
-               cerr << "Using cached iterator at " << _next_read << endl;
+               //cerr << "Using cached iterator at " << _next_read << endl;
        }
 
        _next_read = start + nframes;
@@ -344,11 +356,13 @@ MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes
        while (_read_iter != end() && _read_iter->time() < start + nframes) {
                assert(_read_iter->size() > 0);
                dst.write(_read_iter->time() + stamp_offset - negative_stamp_offset, _read_iter->size(), _read_iter->buffer());
-               cerr << this << " MM::read event @ " << _read_iter->time()  
+               
+               cerr << this << " MidiModel::read event @ " << _read_iter->time()  
                     << " type: " << hex << int(_read_iter->type()) << dec 
                     << " note: " << int(_read_iter->note()) 
                     << " velocity: " << int(_read_iter->velocity()) 
                     << endl;
+               
                ++_read_iter;
                ++read_events;
        }
@@ -451,18 +465,23 @@ MidiModel::append(const MIDI::Event& ev)
 {
        write_lock();
        _edited = true;
+       
+       cerr << "MidiModel::append event type: " << hex << "0x" << int(ev.type()) << endl;
 
        assert(_notes.empty() || ev.time() >= _notes.back()->time());
        assert(_writing);
 
-       if (ev.is_note_on())
+       if (ev.is_note_on()) {
                append_note_on_unlocked(ev.channel(), ev.time(), ev.note(), ev.velocity());
-       else if (ev.is_note_off())
+       } else if (ev.is_note_off()) {
                append_note_off_unlocked(ev.channel(), ev.time(), ev.note());
-       else if (ev.is_cc())
+       } else if (ev.is_cc()) {
                append_cc_unlocked(ev.channel(), ev.time(), ev.cc_number(), ev.cc_value());
-       else
+       } else if (ev.is_pgm_change()) {
+               append_pgm_change_unlocked(ev.channel(), ev.time(), ev.pgm_number());
+       } else { 
                printf("MM Unknown event type %X\n", ev.type());
+       }
        
        write_unlock();
 }
@@ -532,8 +551,8 @@ MidiModel::append_note_off_unlocked(uint8_t chan, double time, uint8_t note_num)
 void
 MidiModel::append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t value)
 {
-       cerr << "MidiModel " << this << " chan " << (int)chan <<
-                       " CC " << (int)number << " = " << (int)value << " @ " << time << endl;
+       //cerr << "MidiModel " << this << " chan " << (int)chan <<
+       //              " CC " << (int)number << " = " << (int)value << " @ " << time << endl;
        
        assert(chan < 16);
        assert(_writing);
@@ -545,6 +564,25 @@ MidiModel::append_cc_unlocked(uint8_t chan, double time, uint8_t number, uint8_t
        control->list()->fast_simple_add(time, (double)value);
 }
 
+void
+MidiModel::append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number) 
+{
+       cerr << "MidiModel::append_pgm_change_unlocked: channel " << int(chan) << " time: " << time << " program number: " << int(number) <<endl; 
+       assert(chan < 16);
+       assert(_writing);
+       _edited = true;
+       
+       boost::shared_ptr<MIDI::Event> event_ptr(new MIDI::Event(time, 3, 0, true));
+       event_ptr->set_type(MIDI_CMD_PGM_CHANGE);
+       event_ptr->set_channel(chan);
+       event_ptr->set_pgm_number(number);
+       _pgm_changes.push_back(event_ptr);
+       cerr << "MidiModel::append_pgm_change_unlocked: appended pgm change" << endl;
+       for(PgmChanges::iterator i = _pgm_changes.begin(); i != _pgm_changes.end(); ++i) {
+               cerr << "_pgm_changes contents: channel " << int((*i)->channel()) << dec << " time: " << int((*i)->time()) << hex << " program number: " << int(int((*i)->pgm_number())) <<endl;
+       }
+       //<< int(_pgm_changes.) << " time: " << time << " program number: " << int(number) <<endl;
+}
 
 void
 MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note)
@@ -820,10 +858,18 @@ MidiModel::DeltaCommand::get_state ()
 }
 
 
+struct EventTimeComparator {
+       typedef const MIDI::Event* value_type;
+       inline bool operator()(const MIDI::Event* a,
+                              const MIDI::Event* b) const { 
+               return a->time() >= b->time();
+       }
+};
+
 bool
 MidiModel::write_to(boost::shared_ptr<MidiSource> source)
 {
-       //cerr << "Writing model to " << source->name() << endl;
+       cerr << "Writing model to " << source->name() << endl;
 
        /* This could be done using a temporary MidiRingBuffer and using
         * MidiModel::read and MidiSource::write, but this is more efficient
@@ -845,9 +891,39 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
 
        read_lock();
 
-       LaterNoteEndComparator cmp;
-       ActiveNotes active_notes(cmp);
+       //LaterNoteEndComparator cmp;
+       //ActiveNotes active_notes(cmp);
+       
+       EventTimeComparator comp;
+       typedef std::priority_queue<
+                               MIDI::Event*, 
+                               std::deque<MIDI::Event*>,
+                               EventTimeComparator> MidiEvents;
+       
+       MidiEvents events(comp);
+       
+
+       for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
+               events.push(&(*n)->on_event());
+               events.push(&(*n)->off_event());
+       }
+       
+       for (PgmChanges::const_iterator p = _pgm_changes.begin(); p != _pgm_changes.end(); ++p) {
+               events.push((*p).get());                
+       }
+
+       while(!events.empty()) {
+               source->append_event_unlocked(Frames, *events.top());
+               cerr << "MidiModel::write_to appending event with time:" << dec << int(events.top()->time()) << hex 
+                    << "   buffer: 0x" << int(events.top()->buffer()[0]) << " 0x" << int(events.top()->buffer()[1]) 
+                    << " 0x" << int(events.top()->buffer()[2]) << endl;
+               events.pop();
+       }
+       
                
+       /* Why sort manyally, when a priority queue does the job for us,
+        * or am I missing something???
+        * 
        // Foreach note
        for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n) {
 
@@ -862,6 +938,13 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
                                break;
                        }
                }
+               
+               // Write program changes preceding this note on
+               if(p != _pgm_changes.end() && ((*p)->time() <= (*n)->time())) {
+                       const MIDI::Event& pgm_change_event = *(*p);
+                       source->append_event_unlocked(Frames, pgm_change_event);
+                       ++p;
+               }
 
                // Write this note on
                source->append_event_unlocked(Frames, (*n)->on_event());
@@ -874,7 +957,8 @@ MidiModel::write_to(boost::shared_ptr<MidiSource> source)
                source->append_event_unlocked(Frames, active_notes.top()->off_event());
                active_notes.pop();
        }
-
+       */
+       
        _edited = false;
        
        read_unlock();
index 432addd93ae85d0e523bedfb84358908d9730919..132cff27d954188cbda865e711cbc136f8899ef2 100644 (file)
@@ -451,12 +451,12 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
 void
 SMFSource::append_event_unlocked(EventTimeUnit unit, const MIDI::Event& 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());
        for (size_t i=0; i < ev.size(); ++i) {
                printf("%X ", ev.buffer()[i]);
        }
-       printf("\n");*/
+       printf("\n");
 
        assert(ev.time() >= 0);
        assert(ev.time() >= _last_ev_time);