Sort various things to reduce merge hell. No functional changes.
[ardour.git] / libs / ardour / midi_diskstream.cc
index 552311115da6e85e28914c05a90963fb6da2fe24..ea340598acbeebb369678aafc1497ebfdedf6993 100644 (file)
@@ -68,6 +68,7 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
        , _source_port(0)
        , _capture_transition_buf(0)
        , _last_flush_frame(0)
+       , _note_mode(Sustained)
 {
        /* prevent any write sources from being created */
 
@@ -92,6 +93,7 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
        , _source_port(0)
        , _capture_transition_buf(0)
        , _last_flush_frame(0)
+       , _note_mode(Sustained)
 {
        in_set_state = true;
        init (Recordable);
@@ -121,8 +123,6 @@ MidiDiskstream::init (Diskstream::Flag f)
        set_block_size (_session.get_block_size());
        allocate_temporary_buffers ();
 
-       //_playback_wrap_buffer = new RawMidi[wrap_buffer_size];
-       //_capture_wrap_buffer = new RawMidi[wrap_buffer_size];
        _playback_buf = new MidiRingBuffer (_session.diskstream_buffer_size());
        _capture_buf = new MidiRingBuffer (_session.diskstream_buffer_size());
        _capture_transition_buf = new RingBufferNPT<CaptureTransition> (128);
@@ -137,6 +137,17 @@ MidiDiskstream::~MidiDiskstream ()
        Glib::Mutex::Lock lm (state_lock);
 }
 
+       
+void
+MidiDiskstream::non_realtime_locate (nframes_t position)
+{
+       //cerr << "MDS: non_realtime_locate: " << position << endl;
+       assert(_write_source);
+       _write_source->set_timeline_position (position);
+       seek(position, true); // correct?
+}
+
+
 void
 MidiDiskstream::non_realtime_input_change ()
 {
@@ -148,7 +159,6 @@ MidiDiskstream::non_realtime_input_change ()
                }
 
                if (input_change_pending & ConfigurationChanged) {
-
                        assert(_io->n_inputs() == _n_channels);
                } 
 
@@ -163,6 +173,8 @@ MidiDiskstream::non_realtime_input_change ()
                }
 
                input_change_pending = NoChange;
+               
+               /* implicit unlock */
        }
 
        /* reset capture files */
@@ -184,7 +196,7 @@ MidiDiskstream::non_realtime_input_change ()
 void
 MidiDiskstream::get_input_sources ()
 {
-       uint32_t ni = _io->n_inputs().get(DataType::MIDI);
+       uint32_t ni = _io->n_inputs().n_midi();
 
        if (ni == 0) {
                return;
@@ -195,25 +207,7 @@ MidiDiskstream::get_input_sources ()
 
        _source_port = _io->midi_input(0);
 
-       /* I don't get it....
-       const char **connections = _io->input(0)->get_connections ();
-
-       if (connections == 0 || connections[0] == 0) {
-
-               if (_source_port) {
-                       // _source_port->disable_metering ();
-               }
-
-               _source_port = 0;
-
-       } else {
-               _source_port = dynamic_cast<MidiPort*>(
-                       _session.engine().get_port_by_name (connections[0]) );
-       }
-
-       if (connections) {
-               free (connections);
-       }*/
+       // do... stuff?
 }              
 
 int
@@ -306,6 +300,15 @@ MidiDiskstream::set_destructive (bool yn)
        assert( ! yn);
        return -1;
 }
+       
+void
+MidiDiskstream::set_note_mode (NoteMode m)
+{
+       _note_mode = m;
+       midi_playlist()->set_note_mode(m);
+       if (_write_source && _write_source->model())
+               _write_source->model()->set_note_mode(m);
+}
 
 void
 MidiDiskstream::check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record)
@@ -353,7 +356,7 @@ MidiDiskstream::check_record_status (nframes_t transport_frame, nframes_t nframe
                        } else {
                                first_recordable_frame += _roll_delay;
                        }
-
+               
                } else {
 
                        /* was rolling, but record state changed */
@@ -434,15 +437,12 @@ int
 MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t offset, bool can_record, bool rec_monitors_input)
 {
        // FIXME: waay too much code to duplicate (AudioDiskstream::process)
-       int            ret = -1;
+       int       ret = -1;
        nframes_t rec_offset = 0;
        nframes_t rec_nframes = 0;
-       bool           nominally_recording;
-       bool           re = record_enabled ();
-       bool           collect_playback = false;
-
-       /*_current_capture_buffer = 0;
-         _current_playback_buffer = 0;*/
+       bool      nominally_recording;
+       bool      re = record_enabled ();
+       bool      collect_playback = false;
 
        /* if we've already processed the frames corresponding to this call,
           just return. this allows multiple routes that are taking input
@@ -455,6 +455,8 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t
        if (_processed) {
                return 0;
        }
+       
+       commit_should_unlock = false;
 
        check_record_status (transport_frame, nframes, can_record);
 
@@ -474,7 +476,7 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t
        if (!state_lock.trylock()) {
                return 1;
        }
-
+       commit_should_unlock = true;
        adjust_capture_position = 0;
 
        if (nominally_recording || (_session.get_record_enabled() && Config->get_punch_in())) {
@@ -537,19 +539,19 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t
 
                assert(_source_port);
 
-               // Pump entire port buffer into the ring buffer (FIXME!)
-               _capture_buf->write(_source_port->get_midi_buffer(), transport_frame);
+               // Pump entire port buffer into the ring buffer (FIXME: split cycles?)
+               //_capture_buf->write(_source_port->get_midi_buffer(), transport_frame);
+               size_t num_events = _source_port->get_midi_buffer().size();
+               size_t to_write = std::min(_capture_buf->write_space(), num_events);
 
-               // FIXME: hackitty hack, don't come back
-               //_write_source->ViewDataRangeReady (_write_source->length(), rec_nframes); /* EMIT SIGNAL */
-               /*
-                  for (size_t i=0; i < _source_port->size(); ++i) {
-                  cerr << "DISKSTREAM GOT EVENT(1) " << i << "!!\n";
-                  }
+               MidiBuffer::iterator port_iter = _source_port->get_midi_buffer().begin();
 
-                  if (_source_port->size() == 0)
-                  cerr << "No events :/ (1)\n";
-                  */
+               for (size_t i=0; i < to_write; ++i) {
+                       const MidiEvent& ev = *port_iter;
+                       _capture_buf->write(ev.time() + transport_frame, ev.size(), ev.buffer());
+                       ++port_iter;
+               }
+       
        } else {
 
                if (was_recording) {
@@ -561,16 +563,23 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t
        if (rec_nframes) {
 
                /* XXX XXX XXX XXX XXX XXX XXX XXX */
+               
                /* data will be written to disk */
 
+               if (rec_nframes == nframes && rec_offset == 0) {
+
+                       playback_distance = nframes;
+               } else {
+               
+                       collect_playback = true;
+               }
+
                adjust_capture_position = rec_nframes;
 
        } else if (nominally_recording) {
 
                /* can't do actual capture yet - waiting for latency effects to finish before we start*/
 
-               // Ummm.. well, I suppose we'll just hang out for a bit?
-
                playback_distance = nframes;
 
        } else {
@@ -593,7 +602,8 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t
                }
 
                // XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
-               // Write into playback buffer here, and whatnot
+               // Write into playback buffer here, and whatnot?
+               //cerr << "MDS FIXME: collect playback" << endl;
 
        }
 
@@ -607,14 +617,11 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t
                   be called. unlock the state lock.
                   */
 
+               commit_should_unlock = false;
                state_lock.unlock();
        } 
 
        return ret;
-
-       _processed = true;
-
-       return 0;
 }
 
 bool
@@ -628,15 +635,6 @@ MidiDiskstream::commit (nframes_t nframes)
                playback_sample += playback_distance;
        }
 
-       /* XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX */
-
-       /*
-       _playback_buf->increment_read_ptr (playback_distance);
-
-       if (adjust_capture_position) {
-               _capture_buf->increment_write_ptr (adjust_capture_position);
-       }
-*/
        if (adjust_capture_position != 0) {
                capture_captured += adjust_capture_position;
                adjust_capture_position = 0;
@@ -649,7 +647,9 @@ MidiDiskstream::commit (nframes_t nframes)
                        || _capture_buf->read_space() >= disk_io_chunk_frames;
        }
        
-       state_lock.unlock();
+       if (commit_should_unlock) {
+               state_lock.unlock();
+       }
 
        _processed = false;
 
@@ -670,6 +670,7 @@ MidiDiskstream::set_pending_overwrite (bool yn)
 int
 MidiDiskstream::overwrite_existing_buffers ()
 {
+       cerr << "MDS: overwrite_existing_buffers() (does nothing)" << endl;
        return 0;
 }
 
@@ -678,13 +679,14 @@ MidiDiskstream::seek (nframes_t frame, bool complete_refill)
 {
        Glib::Mutex::Lock lm (state_lock);
        int ret = -1;
+       
+       //cerr << "MDS: seek: " << frame << endl;
 
        _playback_buf->reset();
        _capture_buf->reset();
 
        playback_sample = frame;
        file_frame = frame;
-       _last_flush_frame = frame;
 
        if (complete_refill) {
                while ((ret = do_refill_with_alloc ()) > 0) ;
@@ -708,6 +710,8 @@ MidiDiskstream::can_internal_playback_seek (nframes_t distance)
 int
 MidiDiskstream::internal_playback_seek (nframes_t distance)
 {
+       cerr << "MDS: internal_playback_seek " << distance << endl;
+
        first_recordable_frame += distance;
        playback_sample += distance;
 
@@ -781,8 +785,9 @@ MidiDiskstream::read (nframes_t& start, nframes_t dur, bool reversed)
                
                if (reversed) {
 
-                       cerr << "Reversed MIDI.. that's just crazy talk." << endl;
-                       // Swap note ons with note offs here
+                       // Swap note ons with note offs here.  etc?
+                       // Fully reversing MIDI required look-ahead (well, behind) to find previous
+                       // CC values etc.  hard.
 
                } else {
                        
@@ -796,6 +801,7 @@ MidiDiskstream::read (nframes_t& start, nframes_t dur, bool reversed)
                } 
 
                dur -= this_read;
+               //offset += this_read;
        }
 
        return 0;
@@ -819,16 +825,6 @@ MidiDiskstream::do_refill ()
                return 0;
        }
 
-       /* if there are 2+ chunks of disk i/o possible for
-          this track, let the caller know so that it can arrange
-          for us to be called again, ASAP.
-          */
-
-       // FIXME: using disk_io_chunk_frames as an event count, not good
-       if (_playback_buf->write_space() >= (_slaved?3:2) * disk_io_chunk_frames) {
-               ret = 1;
-       }
-
        /* if we're running close to normal speed and there isn't enough 
           space to do disk_io_chunk_frames of I/O, then don't bother.  
 
@@ -837,7 +833,7 @@ MidiDiskstream::do_refill ()
           */
 
        if ((write_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) {
-               cerr << "No refill 1\n";
+               //cerr << "No refill 1\n";
                return 0;
        }
 
@@ -847,12 +843,12 @@ MidiDiskstream::do_refill ()
           */
 
        if (_slaved && write_space < (_playback_buf->capacity() / 2)) {
-               cerr << "No refill 2\n";
+               //cerr << "No refill 2\n";
                return 0;
        }
 
        if (reversed) {
-               cerr << "No refill 3 (reverse)\n";
+               //cerr << "No refill 3 (reverse)\n";
                return 0;
        }
 
@@ -864,26 +860,10 @@ MidiDiskstream::do_refill ()
                return 0;
        }
 
-#if 0
-       // or this
-       if (file_frame > max_frames - total_space) {
-
-               /* to close to the end: read what we can, and zero fill the rest */
-
-               zero_fill = total_space - (max_frames - file_frame);
-               total_space = max_frames - file_frame;
-
-       } else {
-               zero_fill = 0;
-       }
-#endif
-
-       // At this point we:
+       // At this point we...
        assert(_playback_buf->write_space() > 0); // ... have something to write to, and
        assert(file_frame <= max_frames); // ... something to write
 
-       // So (read it, then) write it:
-       
        nframes_t file_frame_tmp = file_frame;
        nframes_t to_read = min(disk_io_chunk_frames, (max_frames - file_frame));
        
@@ -921,16 +901,14 @@ MidiDiskstream::do_flush (Session::RunContext context, bool force_flush)
 
        _write_data_count = 0;
 
-       if (_last_flush_frame > _session.transport_frame()) {
+       if (_last_flush_frame > _session.transport_frame()
+                       || _last_flush_frame < capture_start_frame) {
                _last_flush_frame = _session.transport_frame();
        }
 
        total = _session.transport_frame() - _last_flush_frame;
 
-
-       // FIXME: put this condition back in! (removed for testing)
-       if (total == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) {
-               //cerr << "MDS - no flush 1\n";
+       if (total == 0 || _capture_buf->read_space() == 0  && _session.transport_speed() == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) {
                goto out;
        }
 
@@ -954,20 +932,18 @@ MidiDiskstream::do_flush (Session::RunContext context, bool force_flush)
 
        assert(!destructive());
 
-       if ((!_write_source) || _write_source->write (*_capture_buf, to_write) != to_write) {
-               //cerr << "MDS - no flush 2\n";
-               error << string_compose(_("MidiDiskstream %1: cannot write to disk"), _id) << endmsg;
-               return -1;
-       } else {
-               _last_flush_frame = _session.transport_frame();
-               //cerr << "MDS - flushed\n";
+       if (record_enabled() && _session.transport_frame() - _last_flush_frame > disk_io_chunk_frames) {
+               if ((!_write_source) || _write_source->midi_write (*_capture_buf, to_write) != to_write) {
+                       error << string_compose(_("MidiDiskstream %1: cannot write to disk"), _id) << endmsg;
+                       return -1;
+               } else {
+                       _last_flush_frame = _session.transport_frame();
+               }
        }
 
-       //(*chan).curr_capture_cnt += to_write;
-
 out:
        //return ret;
-       return 0;
+       return 0; // FIXME: everything's fine!  always!  honest!
 }
 
 void
@@ -1029,30 +1005,21 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
                }
 
                /* figure out the name for this take */
-
-               boost::shared_ptr<SMFSource> s = _write_source;
-
-               if (s) {
-
-                       srcs.push_back (s);
-
-                       cerr << "MidiDiskstream: updating source after capture\n";
-                       s->update_header (capture_info.front()->start, when, twhen);
-
-                       s->set_captured_for (_name);
-
-               }
+       
+               srcs.push_back (_write_source);
+               _write_source->set_timeline_position (capture_info.front()->start);
+               _write_source->set_captured_for (_name);
 
                string whole_file_region_name;
                whole_file_region_name = region_name_from_path (_write_source->name(), true);
+
                /* Register a new region with the Session that
                   describes the entire source. Do this first
                   so that any sub-regions will obviously be
                   children of this one (later!)
                   */
+
                try {
-                       assert(_write_source);
-                       
                        boost::shared_ptr<Region> rx (RegionFactory::create (srcs, _write_source->last_capture_start_frame(), total_capture, 
                                                                             whole_file_region_name, 
                                                                             0, Region::Flag (Region::DefaultFlags|Region::Automatic|Region::WholeFile)));
@@ -1077,6 +1044,7 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
                for (buffer_position = _write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
 
                        string region_name;
+
                        _session.region_name (region_name, _write_source->name(), false);
 
                        // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n";
@@ -1090,6 +1058,8 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
                                error << _("MidiDiskstream: could not create region for captured midi!") << endmsg;
                                continue; /* XXX is this OK? */
                        }
+                       
+                       region->GoingAway.connect (bind (mem_fun (*this, &Diskstream::remove_region_from_last_capture), boost::weak_ptr<Region>(region)));
 
                        _last_capture_regions.push_back (region);
 
@@ -1106,11 +1076,11 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
                XMLNode &after = _playlist->get_state();
                _session.add_command (new MementoCommand<Playlist>(*_playlist, &before, &after));
 
-               mark_write_completed = true;
+       }
 
-               reset_write_sources (mark_write_completed);
+       mark_write_completed = true;
 
-       }
+       reset_write_sources (mark_write_completed);
 
        for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
                delete *ci;
@@ -1120,6 +1090,35 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
        capture_start_frame = 0;
 }
 
+void
+MidiDiskstream::transport_looped (nframes_t transport_frame)
+{
+       if (was_recording) {
+
+               // adjust the capture length knowing that the data will be recorded to disk
+               // only necessary after the first loop where we're recording
+               if (capture_info.size() == 0) {
+                       capture_captured += _capture_offset;
+
+                       if (_alignment_style == ExistingMaterial) {
+                               capture_captured += _session.worst_output_latency();
+                       } else {
+                               capture_captured += _roll_delay;
+                       }
+               }
+
+               finish_capture (true);
+
+               // the next region will start recording via the normal mechanism
+               // we'll set the start position to the current transport pos
+               // no latency adjustment or capture offset needs to be made, as that already happened the first time
+               capture_start_frame = transport_frame;
+               first_recordable_frame = transport_frame; // mild lie
+               last_recordable_frame = max_frames;
+               was_recording = true;
+       }
+}
+
 void
 MidiDiskstream::finish_capture (bool rec_monitors_input)
 {
@@ -1194,6 +1193,12 @@ MidiDiskstream::engage_record_enable ()
                _source_port->request_monitor_input (!(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());
+
        RecordEnableChanged (); /* EMIT SIGNAL */
 }
 
@@ -1358,7 +1363,7 @@ MidiDiskstream::use_new_write_source (uint32_t n)
 
        if (_write_source) {
 
-               if (SMFSource::is_empty (_write_source->path())) {
+               if (_write_source->is_empty ()) {
                        _write_source->mark_for_remove ();
                        _write_source.reset();
                } else {
@@ -1395,8 +1400,10 @@ MidiDiskstream::reset_write_sources (bool mark_write_complete, bool force)
                _write_source->mark_streaming_write_completed ();
        }
 
-       if (!_write_source) {
-               use_new_write_source ();
+       use_new_write_source (0);
+                       
+       if (record_enabled()) {
+               //_capturing_sources.push_back (_write_source);
        }
 }
 
@@ -1404,7 +1411,7 @@ int
 MidiDiskstream::rename_write_sources ()
 {
        if (_write_source != 0) {
-               _write_source->set_name (_name, destructive());
+               _write_source->set_source_name (_name, destructive());
                /* XXX what to do if this fails ? */
        }
        return 0;
@@ -1473,8 +1480,8 @@ MidiDiskstream::use_pending_capture_data (XMLNode& node)
        return 0;
 }
 
-/** Writes playback events in the given range to dst, translating time stamps
- * so that an event at start has time = 0
+/** Writes playback events in the given range to \a dst, translating time stamps
+ * so that an event at \a start has time = 0
  */
 void
 MidiDiskstream::get_playback(MidiBuffer& dst, nframes_t start, nframes_t end)
@@ -1482,46 +1489,11 @@ MidiDiskstream::get_playback(MidiBuffer& dst, nframes_t start, nframes_t end)
        dst.clear();
        assert(dst.size() == 0);
        
-       // I think this happens with reverse varispeed?  maybe?
+       // Reverse.  ... We just don't do reverse, ok?  Back off.
        if (end <= start) {
                return;
        }
 
-/*
-       cerr << "MIDI Diskstream pretending to read" << endl;
-
-       MidiEvent ev;
-       RawMidi data[4];
-
-       const char note = rand()%30 + 30;
-       
-       ev.buffer = data;
-       ev.time = 0;
-       ev.size = 3;
-
-       data[0] = 0x90;
-       data[1] = note;
-       data[2] = 120;
-
-       dst.push_back(ev);
-       
-       ev.buffer = data;
-       ev.time = (end - start) / 2;
-       ev.size = 3;
-
-       data[0] = 0x80;
-       data[1] = note;
-       data[2] = 64;
-*/
+       // Translates stamps to be relative to start
        _playback_buf->read(dst, start, end);
-
-       // Translate time stamps to be relative to the start of this cycle
-       for (size_t i=0; i < dst.size(); ++i) {
-               assert(dst[i].time >= start);
-               assert(dst[i].time <= end);
-               //cerr << "Translating event stamp " << dst[i].time << " to ";
-               dst[i].time -= start;
-               //cerr << dst[i].time << endl;
-
-       }
 }