#include "pbd/xml++.h"
#include "pbd/memento_command.h"
#include "pbd/enumwriter.h"
+#include "pbd/stateful_diff_command.h"
+#include "pbd/stacktrace.h"
#include "ardour/ardour.h"
#include "ardour/audioengine.h"
#include "ardour/smf_source.h"
#include "ardour/utils.h"
#include "ardour/session_playlists.h"
+#include "ardour/route.h"
#include "midi++/types.h"
in_set_state = true;
- init(flag);
+ init ();
use_new_playlist ();
+ use_new_write_source (0);
in_set_state = false;
, _frames_read_from_ringbuffer(0)
{
in_set_state = true;
- init (Recordable);
+
+ init ();
if (set_state (node, Stateful::loading_state_version)) {
in_set_state = false;
throw failed_constructor();
}
- in_set_state = false;
+ use_new_write_source (0);
- if (destructive()) {
- use_destructive_playlist ();
- }
+ in_set_state = false;
}
void
-MidiDiskstream::init (Diskstream::Flag f)
+MidiDiskstream::init ()
{
- Diskstream::init(f);
-
/* there are no channels at this point, so these
two calls just get speed_buffer_size and wrap_buffer
size setup without duplicating their code.
/* implicit unlock */
}
- /* reset capture files */
-
- reset_write_sources (false);
+ /* unlike with audio, there is never any need to reset write sources
+ based on input configuration changes because ... a MIDI track
+ has just 1 MIDI port as input, always.
+ */
/* now refill channel buffers */
#endif
int
-MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can_record, bool rec_monitors_input)
+MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can_record, bool rec_monitors_input, bool& need_butler)
{
int ret = -1;
nframes_t rec_offset = 0;
bool nominally_recording;
bool re = record_enabled ();
- /* if we've already processed the frames corresponding to this call,
- just return. this allows multiple routes that are taking input
- from this diskstream to call our ::process() method, but have
- this stuff only happen once. more commonly, it allows both
- the AudioTrack that is using this AudioDiskstream *and* the Session
- to call process() without problems.
- */
-
- if (_processed) {
- return 0;
- }
-
- commit_should_unlock = false;
+ playback_distance = 0;
check_record_status (transport_frame, nframes, can_record);
nominally_recording = (can_record && re);
if (nframes == 0) {
- _processed = true;
return 0;
}
- /* This lock is held until the end of ::commit, so these two functions
- must always be called as a pair. The only exception is if this function
- returns a non-zero value, in which case, ::commit should not be called.
- */
+ Glib::Mutex::Lock sm (state_lock, Glib::TRY_LOCK);
- // If we can't take the state lock return.
- if (!state_lock.trylock()) {
+ if (!sm.locked()) {
return 1;
}
- commit_should_unlock = true;
+
adjust_capture_position = 0;
if (nominally_recording || (_session.get_record_enabled() && _session.config.get_punch_in())) {
}
- if (can_record && !_last_capture_regions.empty()) {
- _last_capture_regions.clear ();
+ if (can_record && !_last_capture_sources.empty()) {
+ _last_capture_sources.clear ();
}
if (nominally_recording || rec_nframes) {
ret = 0;
- _processed = true;
-
- if (ret) {
-
- /* we're exiting with failure, so ::commit will not
- be called. unlock the state lock.
- */
-
- commit_should_unlock = false;
- state_lock.unlock();
- }
+ if (commit (nframes)) {
+ need_butler = true;
+ }
return ret;
}
" = " << frames_written - frames_read
<< " + " << nframes << " < " << midi_readahead << " = " << need_butler << ")" << endl;*/
- if (commit_should_unlock) {
- state_lock.unlock();
- }
-
- _processed = false;
-
return need_butler;
}
{
/* called from audio thread, so we can use the read ptr and playback sample as we wish */
- pending_overwrite = yn;
+ _pending_overwrite = yn;
overwrite_frame = playback_sample;
}
{
//read(overwrite_frame, disk_io_chunk_frames, false);
overwrite_queued = false;
- pending_overwrite = false;
+ _pending_overwrite = false;
return 0;
}
bool reloop = false;
nframes_t loop_end = 0;
nframes_t loop_start = 0;
- nframes_t loop_length = 0;
Location *loc = 0;
if (!reversed) {
+
+ nframes_t loop_length = 0;
+
/* Make the use of a Location atomic for this read operation.
Note: Locations don't get deleted, so all we care about
}
void
-MidiDiskstream::transport_stopped (struct tm& /*when*/, time_t /*twhen*/, bool abort_capture)
+MidiDiskstream::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<MidiRegion> region;
- nframes_t total_capture;
MidiRegion::SourceList srcs;
MidiRegion::SourceList::iterator src;
vector<CaptureInfo*>::iterator ci;
if (_write_source) {
_write_source->mark_for_remove ();
- _write_source->drop_references ();
_write_source.reset();
}
assert(_write_source);
- for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
+ nframes_t total_capture = 0;
+ for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
total_capture += (*ci)->frames;
}
- /* figure out the name for this take */
+ if (_write_source->length (capture_info.front()->start) != 0) {
+
+ /* phew, we have data */
+
+ /* 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_captured_for (_name);
+ _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);
+ /* flush to disk: this step differs from the audio path,
+ where all the data is already on disk.
+ */
- /* 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!)
- */
+ _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
+ */
- try {
- boost::shared_ptr<Region> rx (RegionFactory::create (srcs, 0,
- total_capture, whole_file_region_name, 0,
- Region::Flag (Region::DefaultFlags|Region::Automatic|Region::WholeFile)));
+ _write_source->mark_nonremovable ();
+
+ 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;
- region = boost::dynamic_pointer_cast<MidiRegion> (rx);
- region->special_set_position (capture_info.front()->start);
- }
+ 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));
- catch (failed_constructor& err) {
- error << string_compose(_("%1: could not create region for complete midi file"), _name) << endmsg;
- /* XXX what now? */
- }
+ region = boost::dynamic_pointer_cast<MidiRegion> (rx);
+ region->special_set_position (capture_info.front()->start);
+ }
- _last_capture_regions.push_back (region);
- // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
+ catch (failed_constructor& err) {
+ error << string_compose(_("%1: could not create region for complete midi file"), _name) << endmsg;
+ /* XXX what now? */
+ }
- XMLNode &before = _playlist->get_state();
- _playlist->freeze ();
+ _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end());
- for (buffer_position = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
+ _playlist->clear_history ();
+ _playlist->freeze ();
- string region_name;
+ uint32_t buffer_position = 0;
+ for (buffer_position = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
- _session.region_name (region_name, _write_source->name(), false);
+ string region_name;
- // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n";
+ RegionFactory::region_name (region_name, _write_source->name(), false);
- try {
- boost::shared_ptr<Region> rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name));
- region = boost::dynamic_pointer_cast<MidiRegion> (rx);
- }
+ // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n";
- catch (failed_constructor& err) {
- error << _("MidiDiskstream: could not create region for captured midi!") << endmsg;
- continue; /* XXX is this OK? */
- }
+ try {
+ PropertyList plist;
+
+ plist.add (Properties::start, buffer_position);
+ plist.add (Properties::length, (*ci)->frames);
+ plist.add (Properties::name, region_name);
+
+ boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist));
+ region = boost::dynamic_pointer_cast<MidiRegion> (rx);
+ }
- region->GoingAway.connect (*this, boost::bind (&Diskstream::remove_region_from_last_capture, this, boost::weak_ptr<Region>(region)));
+ catch (failed_constructor& err) {
+ error << _("MidiDiskstream: could not create region for captured midi!") << endmsg;
+ continue; /* XXX is this OK? */
+ }
- _last_capture_regions.push_back (region);
+ // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl;
- // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl;
+ i_am_the_modifier++;
+ _playlist->add_region (region, (*ci)->start);
+ i_am_the_modifier--;
- i_am_the_modifier++;
- _playlist->add_region (region, (*ci)->start);
- i_am_the_modifier--;
+ buffer_position += (*ci)->frames;
+ }
- buffer_position += (*ci)->frames;
- }
+ _playlist->thaw ();
+ _session.add_command (new StatefulDiffCommand(_playlist));
+ }
- _playlist->thaw ();
- XMLNode &after = _playlist->get_state();
- _session.add_command (new MementoCommand<Playlist>(*_playlist, &before, &after));
+ 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) {
delete *ci;
_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());
RecordEnableChanged (); /* EMIT SIGNAL */
XMLNode&
MidiDiskstream::get_state ()
{
- XMLNode* node = new XMLNode ("MidiDiskstream");
+ XMLNode* node = new XMLNode ("Diskstream");
char buf[64];
LocaleGuard lg (X_("POSIX"));
const XMLProperty* prop;
XMLNodeList nlist = node.children();
XMLNodeIterator niter;
- uint32_t nchans = 1;
XMLNode* capture_pending_node = 0;
LocaleGuard lg (X_("POSIX"));
set_channel_mode(channel_mode, channel_mask);
- if ((prop = node.property ("channels")) != 0) {
- nchans = atoi (prop->value().c_str());
- }
-
if ((prop = node.property ("playlist")) == 0) {
return -1;
}
}
if (!had_playlist) {
- _playlist->set_orig_diskstream_id (_id);
+ _playlist->set_orig_diskstream_id (id());
}
if (capture_pending_node) {
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;
}
assert(n == 0);
- if (_write_source) {
-
- if (_write_source->is_empty ()) {
- _write_source->mark_for_remove ();
- _write_source.reset();
- } else {
- _write_source.reset();
- }
- }
+ _write_source.reset();
try {
- _write_source = boost::dynamic_pointer_cast<SMFSource>(_session.create_midi_source_for_session (*this));
+ _write_source = boost::dynamic_pointer_cast<SMFSource>(_session.create_midi_source_for_session (0, name ()));
if (!_write_source) {
throw failed_constructor();
}
return 0;
}
+list<boost::shared_ptr<Source> >
+MidiDiskstream::steal_write_sources()
+{
+ list<boost::shared_ptr<Source> > ret;
+ ret.push_back (_write_source);
+ reset_write_sources (false);
+ return ret;
+}
+
void
MidiDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
{
MidiDiskstream::rename_write_sources ()
{
if (_write_source != 0) {
- _write_source->set_source_name (_name, destructive());
+ _write_source->set_source_name (_name.val(), destructive());
/* XXX what to do if this fails ? */
}
return 0;