diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2017-06-22 19:18:57 -0400 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2017-09-18 11:40:53 -0400 |
commit | d4280997fb1764ae0df619fbb34031bebd4b0670 (patch) | |
tree | 43a52031ea6e0eab44bdfab8efc715e15248e2b2 /libs/ardour/disk_writer.cc | |
parent | 5ac7d733c2e34e6dfb3ad42d3c9a5d6169120784 (diff) |
the return of MIDI recording, plus refactor to move post-capture playlist manipulations into Track
Diffstat (limited to 'libs/ardour/disk_writer.cc')
-rw-r--r-- | libs/ardour/disk_writer.cc | 395 |
1 files changed, 96 insertions, 299 deletions
diff --git a/libs/ardour/disk_writer.cc b/libs/ardour/disk_writer.cc index 3bc8bbff69..4eb4393861 100644 --- a/libs/ardour/disk_writer.cc +++ b/libs/ardour/disk_writer.cc @@ -706,6 +706,8 @@ DiskWriter::finish_capture (boost::shared_ptr<ChannelList> c) ci->start = capture_start_frame; ci->frames = capture_captured; + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->frames)); + /* XXX theoretical race condition here. Need atomic exchange ? However, the circumstances when this is called right now (either on record-disable or transport_stopped) @@ -715,8 +717,6 @@ DiskWriter::finish_capture (boost::shared_ptr<ChannelList> c) accessors, so that invalidation will not occur (both non-realtime). */ - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->frames)); - capture_info.push_back (ci); capture_captured = 0; @@ -992,6 +992,48 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush) /* MIDI*/ + if (_midi_write_source) { + + const framecnt_t total = g_atomic_int_get(const_cast<gint*> (&_frames_pending_write)); + + if (total == 0 || + _midi_buf->read_space() == 0 || + (!force_flush && (total < _chunk_frames) && was_recording)) { + goto out; + } + + /* 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. + + if we are forcing a flush, then if there is* any* extra + work, let the caller know. + + if we are no longer recording and there is any extra work, + let the caller know too. + */ + + if (total >= 2 * _chunk_frames || ((force_flush || !was_recording) && total > _chunk_frames)) { + ret = 1; + } + + if (force_flush) { + /* push out everything we have, right now */ + to_write = UINT32_MAX; + } else { + to_write = _chunk_frames; + } + + if (record_enabled() && ((total > _chunk_frames) || force_flush)) { + Source::Lock lm(_midi_write_source->mutex()); + if (_midi_write_source->midi_write (lm, *_midi_buf, get_capture_start_frame (0), to_write) != to_write) { + error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg; + return -1; + } + g_atomic_int_add(const_cast<gint*> (&_frames_pending_write), -to_write); + } + } + out: return ret; @@ -1127,13 +1169,11 @@ DiskWriter::use_new_write_source (DataType dt, uint32_t n) void DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abort_capture) { - uint32_t buffer_position; bool more_work = true; int err = 0; - boost::shared_ptr<AudioRegion> region; framecnt_t total_capture; - SourceList srcs; - SourceList::iterator src; + SourceList audio_srcs; + SourceList midi_srcs; ChannelList::iterator chan; vector<CaptureInfo*>::iterator ci; boost::shared_ptr<ChannelList> c = channels.reader(); @@ -1142,7 +1182,6 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo finish_capture (c); - boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist> (_playlists[DataType::AUDIO]); /* butler is already stopped, but there may be work to do to flush remaining data to disk. @@ -1186,6 +1225,12 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo /* new source set up in "out" below */ } + if (_midi_write_source) { + _midi_write_source->mark_for_remove (); + _midi_write_source->drop_references (); + _midi_write_source.reset(); + } + goto out; } @@ -1197,117 +1242,76 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) { - boost::shared_ptr<AudioFileSource> s = (*chan)->write_source; + boost::shared_ptr<AudioFileSource> as = (*chan)->write_source; - if (s) { - srcs.push_back (s); - s->update_header (capture_info.front()->start, when, twhen); - s->set_captured_for (_name.val()); - s->mark_immutable (); + if (as) { + audio_srcs.push_back (as); + as->update_header (capture_info.front()->start, when, twhen); + as->set_captured_for (_name.val()); + as->mark_immutable (); if (Config->get_auto_analyse_audio()) { - Analyser::queue_source_for_analysis (s, true); + Analyser::queue_source_for_analysis (as, true); } - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("newly captured source %1 length %2\n", s->path(), s->length (0))); + DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("newly captured source %1 length %2\n", as->path(), as->length (0))); } - } - if (!pl) { - goto midi; + if (_midi_write_source) { + midi_srcs.push_back (_midi_write_source); + } } - /* destructive tracks have a single, never changing region */ - - if (destructive()) { - - /* send a signal that any UI can pick up to do the right thing. there is - a small problem here in that a UI may need the peak data to be ready - for the data that was recorded and this isn't interlocked with that - process. this problem is deferred to the UI. - */ - - pl->LayeringChanged(); // XXX this may not get the UI to do the right thing - - } else { - - string whole_file_region_name; - whole_file_region_name = region_name_from_path (c->front()->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 { - PropertyList plist; - plist.add (Properties::start, c->front()->write_source->last_capture_start_frame()); - plist.add (Properties::length, total_capture); - plist.add (Properties::name, whole_file_region_name); - boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist)); - rx->set_automatic (true); - rx->set_whole_file (true); + /* MIDI */ - region = boost::dynamic_pointer_cast<AudioRegion> (rx); - region->special_set_position (capture_info.front()->start); - } + if (_midi_write_source) { + if (_midi_write_source->length (capture_info.front()->start) == 0) { + /* No data was recorded, so this capture will + effectively be aborted; do the same as we + do for an explicit abort. + */ + if (_midi_write_source) { + _midi_write_source->mark_for_remove (); + _midi_write_source->drop_references (); + _midi_write_source.reset(); + } - catch (failed_constructor& err) { - error << string_compose(_("%1: could not create region for complete audio file"), _name) << endmsg; - /* XXX what now? */ + goto out; } - _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end()); - - pl->clear_changes (); - pl->set_capture_insertion_in_progress (true); - pl->freeze (); - - const framepos_t preroll_off = _session.preroll_record_trim_len (); - for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) { + /* phew, we have data */ - string region_name; + Source::Lock source_lock(_midi_write_source->mutex()); - RegionFactory::region_name (region_name, whole_file_region_name, false); + /* figure out the name for this take */ - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture bufpos %5 start @ %2 length %3 add new region %4\n", - _name, (*ci)->start, (*ci)->frames, region_name, buffer_position)); + midi_srcs.push_back (_midi_write_source); - try { + _midi_write_source->set_timeline_position (capture_info.front()->start); + _midi_write_source->set_captured_for (_name); - PropertyList plist; + /* set length in beats to entire capture length */ - plist.add (Properties::start, buffer_position); - plist.add (Properties::length, (*ci)->frames); - plist.add (Properties::name, region_name); + BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start); + const Evoral::Beats total_capture_beats = converter.from (total_capture); + _midi_write_source->set_length_beats (total_capture_beats); - boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist)); - region = boost::dynamic_pointer_cast<AudioRegion> (rx); - if (preroll_off > 0) { - region->trim_front (buffer_position + preroll_off); - } - } - - catch (failed_constructor& err) { - error << _("AudioDiskstream: could not create region for captured audio!") << endmsg; - continue; /* XXX is this OK? */ - } + /* flush to disk: this step differs from the audio path, + where all the data is already on disk. + */ - i_am_the_modifier++; + _midi_write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence<Evoral::Beats>::ResolveStuckNotes, total_capture_beats); + } - pl->add_region (region, (*ci)->start + preroll_off, 1, non_layered()); - pl->set_layer (region, DBL_MAX); - i_am_the_modifier--; + _last_capture_sources.insert (_last_capture_sources.end(), audio_srcs.begin(), audio_srcs.end()); + _last_capture_sources.insert (_last_capture_sources.end(), midi_srcs.begin(), midi_srcs.end()); - buffer_position += (*ci)->frames; - } - pl->thaw (); - pl->set_capture_insertion_in_progress (false); - _session.add_command (new StatefulDiffCommand (pl)); + if (_route) { + _route->use_captured_sources (audio_srcs, capture_info); + _route->use_captured_sources (midi_srcs, capture_info); } mark_write_completed = true; @@ -1323,215 +1327,8 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo capture_info.clear (); capture_start_frame = 0; - - midi: - return; } -#if 0 // MIDI PART -void -DiskWriter::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen*/, bool abort_capture) -{ - bool more_work = true; - int err = 0; - boost::shared_ptr<MidiRegion> region; - MidiRegion::SourceList srcs; - MidiRegion::SourceList::iterator src; - vector<CaptureInfo*>::iterator ci; - - finish_capture (); - - /* butler is already stopped, but there may be work to do - to flush remaining data to disk. - */ - - while (more_work && !err) { - switch (do_flush (TransportContext, true)) { - case 0: - more_work = false; - break; - case 1: - break; - case -1: - error << string_compose(_("MidiDiskstream \"%1\": cannot flush captured data to disk!"), _name) << endmsg; - err++; - } - } - - /* XXX is there anything we can do if err != 0 ? */ - Glib::Threads::Mutex::Lock lm (capture_info_lock); - - if (capture_info.empty()) { - goto no_capture_stuff_to_do; - } - - if (abort_capture) { - - if (_write_source) { - _write_source->mark_for_remove (); - _write_source->drop_references (); - _write_source.reset(); - } - - /* new source set up in "out" below */ - - } else { - - framecnt_t total_capture = 0; - for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { - total_capture += (*ci)->frames; - } - - if (_write_source->length (capture_info.front()->start) != 0) { - - /* phew, we have data */ - - Source::Lock source_lock(_write_source->mutex()); - - /* figure out the name for this take */ - - srcs.push_back (_write_source); - - _write_source->set_timeline_position (capture_info.front()->start); - _write_source->set_captured_for (_name); - - /* set length in beats to entire capture length */ - - BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start); - const Evoral::Beats total_capture_beats = converter.from (total_capture); - _write_source->set_length_beats (total_capture_beats); - - /* flush to disk: this step differs from the audio path, - where all the data is already on disk. - */ - - _write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence<Evoral::Beats>::ResolveStuckNotes, total_capture_beats); - - /* 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. luckily, we can rely on - MidiSource::mark_streaming_write_completed() to have - already done the necessary work for that. - */ - - 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 { - PropertyList plist; - - plist.add (Properties::name, whole_file_region_name); - plist.add (Properties::whole_file, true); - plist.add (Properties::automatic, true); - plist.add (Properties::start, 0); - plist.add (Properties::length, total_capture); - plist.add (Properties::layer, 0); - - boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist)); - - region = boost::dynamic_pointer_cast<MidiRegion> (rx); - region->special_set_position (capture_info.front()->start); - } - - - catch (failed_constructor& err) { - error << string_compose(_("%1: could not create region for complete midi file"), _name) << endmsg; - /* XXX what now? */ - } - - _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end()); - - _playlist->clear_changes (); - _playlist->freeze (); - - /* Session frame time of the initial capture in this pass, which is where the source starts */ - framepos_t initial_capture = 0; - if (!capture_info.empty()) { - initial_capture = capture_info.front()->start; - } - - const framepos_t preroll_off = _session.preroll_record_trim_len (); - for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { - - string region_name; - - RegionFactory::region_name (region_name, _write_source->name(), false); - - DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n", - _name, (*ci)->start, (*ci)->frames, region_name)); - - - // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n"; - - try { - PropertyList plist; - - /* start of this region is the offset between the start of its capture and the start of the whole pass */ - plist.add (Properties::start, (*ci)->start - initial_capture); - plist.add (Properties::length, (*ci)->frames); - plist.add (Properties::length_beats, converter.from((*ci)->frames).to_double()); - plist.add (Properties::name, region_name); - - boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist)); - region = boost::dynamic_pointer_cast<MidiRegion> (rx); - if (preroll_off > 0) { - region->trim_front ((*ci)->start - initial_capture + preroll_off); - } - } - - catch (failed_constructor& err) { - error << _("MidiDiskstream: could not create region for captured midi!") << endmsg; - continue; /* XXX is this OK? */ - } - - // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl; - - i_am_the_modifier++; - _playlist->add_region (region, (*ci)->start + preroll_off); - i_am_the_modifier--; - } - - _playlist->thaw (); - _session.add_command (new StatefulDiffCommand(_playlist)); - - } else { - - /* No data was recorded, so this capture will - effectively be aborted; do the same as we - do for an explicit abort. - */ - - if (_write_source) { - _write_source->mark_for_remove (); - _write_source->drop_references (); - _write_source.reset(); - } - } - - } - - reset_write_sources (); - - for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) { - delete *ci; - } - - capture_info.clear (); - capture_start_frame = 0; - - no_capture_stuff_to_do: - - reset_tracker (); -} -#endif - void DiskWriter::transport_looped (framepos_t transport_frame) { |