You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Id: diskstream.cc 567 2006-06-07 14:54:12Z trutkin $
*/
#include <fstream>
#include <glibmm/thread.h>
#include <pbd/xml++.h>
#include <pbd/memento_command.h>
+#include <pbd/enumwriter.h>
#include <ardour/ardour.h>
#include <ardour/audioengine.h>
#include <ardour/utils.h>
#include <ardour/configuration.h>
#include <ardour/smf_source.h>
-#include <ardour/destructive_filesource.h>
#include <ardour/send.h>
+#include <ardour/region_factory.h>
#include <ardour/midi_playlist.h>
+#include <ardour/playlist_factory.h>
#include <ardour/cycle_timer.h>
#include <ardour/midi_region.h>
#include <ardour/midi_port.h>
: Diskstream(sess, name, flag)
, _playback_buf(0)
, _capture_buf(0)
- , _current_playback_buffer(0)
- , _current_capture_buffer(0)
- , _playback_wrap_buffer(0)
- , _capture_wrap_buffer(0)
+ //, _current_playback_buffer(0)
+ //, _current_capture_buffer(0)
+ //, _playback_wrap_buffer(0)
+ //, _capture_wrap_buffer(0)
, _source_port(0)
- , _write_source(0)
, _capture_transition_buf(0)
+ , _last_flush_frame(0)
+ , _note_mode(Sustained)
{
/* prevent any write sources from being created */
in_set_state = false;
assert(!destructive());
- DiskstreamCreated (this); /* EMIT SIGNAL */
}
MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
: Diskstream(sess, node)
, _playback_buf(0)
, _capture_buf(0)
- , _current_playback_buffer(0)
- , _current_capture_buffer(0)
- , _playback_wrap_buffer(0)
- , _capture_wrap_buffer(0)
+ //, _current_playback_buffer(0)
+ //, _current_capture_buffer(0)
+ //, _playback_wrap_buffer(0)
+ //, _capture_wrap_buffer(0)
, _source_port(0)
- , _write_source(0)
, _capture_transition_buf(0)
+ , _last_flush_frame(0)
+ , _note_mode(Sustained)
{
in_set_state = true;
init (Recordable);
if (destructive()) {
use_destructive_playlist ();
}
-
- DiskstreamCreated (this); /* EMIT SIGNAL */
}
void
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 RingBufferNPT<RawMidi> (_session.diskstream_buffer_size());
- _capture_buf = new RingBufferNPT<RawMidi> (_session.diskstream_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);
_n_channels = ChanCount(DataType::MIDI, 1);
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 ()
{
}
if (input_change_pending & ConfigurationChanged) {
-
assert(_io->n_inputs() == _n_channels);
}
}
input_change_pending = NoChange;
+
+ /* implicit unlock */
}
/* reset capture files */
/* now refill channel buffers */
if (speed() != 1.0f || speed() != -1.0f) {
- seek ((jack_nframes_t) (_session.transport_frame() * (double) speed()));
+ seek ((nframes_t) (_session.transport_frame() * (double) speed()));
}
else {
seek (_session.transport_frame());
}
+
+ _last_flush_frame = _session.transport_frame();
}
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;
_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
MidiDiskstream::find_and_use_playlist (const string& name)
{
- Playlist* pl;
- MidiPlaylist* playlist;
+ boost::shared_ptr<MidiPlaylist> playlist;
- if ((pl = _session.playlist_by_name (name)) == 0) {
- playlist = new MidiPlaylist(_session, name);
- pl = playlist;
+ if ((playlist = boost::dynamic_pointer_cast<MidiPlaylist> (_session.playlist_by_name (name))) == 0) {
+ playlist = boost::dynamic_pointer_cast<MidiPlaylist> (PlaylistFactory::create (DataType::MIDI, _session, name));
}
- if ((playlist = dynamic_cast<MidiPlaylist*> (pl)) == 0) {
- error << string_compose(_("MidiDiskstream: Playlist \"%1\" isn't a midi playlist"), name) << endmsg;
+ if (!playlist) {
+ error << string_compose(_("MidiDiskstream: Playlist \"%1\" isn't an midi playlist"), name) << endmsg;
return -1;
}
}
int
-MidiDiskstream::use_playlist (Playlist* playlist)
-{
- assert(dynamic_cast<MidiPlaylist*>(playlist));
+MidiDiskstream::use_playlist (boost::shared_ptr<Playlist> playlist)
+{
+ assert(boost::dynamic_pointer_cast<MidiPlaylist>(playlist));
+
+ Diskstream::use_playlist(playlist);
- return Diskstream::use_playlist(playlist);
+ return 0;
}
int
MidiDiskstream::use_new_playlist ()
-{
+{
string newname;
- MidiPlaylist* playlist;
+ boost::shared_ptr<MidiPlaylist> playlist;
if (!in_set_state && destructive()) {
return 0;
newname = Playlist::bump_name (_name, _session);
}
- if ((playlist = new MidiPlaylist (_session, newname, hidden())) != 0) {
+ if ((playlist = boost::dynamic_pointer_cast<MidiPlaylist> (PlaylistFactory::create (
+ DataType::MIDI, _session, newname, hidden()))) != 0) {
+
playlist->set_orig_diskstream_id (id());
return use_playlist (playlist);
+
} else {
return -1;
}
int
MidiDiskstream::use_copy_playlist ()
{
+ assert(midi_playlist());
+
if (destructive()) {
return 0;
}
}
string newname;
- MidiPlaylist* playlist;
+ boost::shared_ptr<MidiPlaylist> playlist;
newname = Playlist::bump_name (_playlist->name(), _session);
- if ((playlist = new MidiPlaylist (*midi_playlist(), newname)) != 0) {
+ if ((playlist = boost::dynamic_pointer_cast<MidiPlaylist>(PlaylistFactory::create (midi_playlist(), newname))) != 0) {
playlist->set_orig_diskstream_id (id());
return use_playlist (playlist);
} else {
/** Overloaded from parent to die horribly
*/
-void
+int
MidiDiskstream::set_destructive (bool yn)
{
assert( ! destructive());
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 (jack_nframes_t transport_frame, jack_nframes_t nframes, bool can_record)
+MidiDiskstream::check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record)
{
// FIXME: waaay too much code to duplicate (AudioDiskstream)
} else {
first_recordable_frame += _roll_delay;
}
-
+
} else {
/* was rolling, but record state changed */
if (_alignment_style == ExistingMaterial) {
- if (!_session.get_punch_in()) {
+ if (!Config->get_punch_in()) {
/* manual punch in happens at the correct transport frame
because the user hit a button. but to get alignment correct
} else {
- if (_session.get_punch_in()) {
+ if (Config->get_punch_in()) {
first_recordable_frame += _roll_delay;
} else {
capture_start_frame -= _roll_delay;
}
int
-MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes, jack_nframes_t offset, bool can_record, bool rec_monitors_input)
+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;
- jack_nframes_t rec_offset = 0;
- jack_nframes_t rec_nframes = 0;
- bool nominally_recording;
- bool re = record_enabled ();
- bool collect_playback = false;
-
- _current_capture_buffer = 0;
- _current_playback_buffer = 0;
+ int ret = -1;
+ nframes_t rec_offset = 0;
+ nframes_t rec_nframes = 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
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;
check_record_status (transport_frame, nframes, can_record);
/* This lock is held until the end of AudioDiskstream::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.
- */
+ */
// If we can't take the state lock return.
if (!state_lock.trylock()) {
return 1;
}
-
+ commit_should_unlock = true;
adjust_capture_position = 0;
- if (nominally_recording || (_session.get_record_enabled() && _session.get_punch_in())) {
+ if (nominally_recording || (_session.get_record_enabled() && Config->get_punch_in())) {
OverlapType ot;
-
+
ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
switch (ot) {
- case OverlapNone:
- rec_nframes = 0;
- break;
-
- case OverlapInternal:
- /* ---------- recrange
- |---| transrange
- */
- rec_nframes = nframes;
- rec_offset = 0;
- break;
-
- case OverlapStart:
- /* |--------| recrange
- -----| transrange
- */
- rec_nframes = transport_frame + nframes - first_recordable_frame;
- if (rec_nframes) {
+ case OverlapNone:
+ rec_nframes = 0;
+ break;
+
+ case OverlapInternal:
+ /* ---------- recrange
+ |---| transrange
+ */
+ rec_nframes = nframes;
+ rec_offset = 0;
+ break;
+
+ case OverlapStart:
+ /* |--------| recrange
+ -----| transrange
+ */
+ rec_nframes = transport_frame + nframes - first_recordable_frame;
+ if (rec_nframes) {
+ rec_offset = first_recordable_frame - transport_frame;
+ }
+ break;
+
+ case OverlapEnd:
+ /* |--------| recrange
+ |-------- transrange
+ */
+ rec_nframes = last_recordable_frame - transport_frame;
+ rec_offset = 0;
+ break;
+
+ case OverlapExternal:
+ /* |--------| recrange
+ -------------- transrange
+ */
+ rec_nframes = last_recordable_frame - last_recordable_frame;
rec_offset = first_recordable_frame - transport_frame;
- }
- break;
-
- case OverlapEnd:
- /* |--------| recrange
- |-------- transrange
- */
- rec_nframes = last_recordable_frame - transport_frame;
- rec_offset = 0;
- break;
-
- case OverlapExternal:
- /* |--------| recrange
- -------------- transrange
- */
- rec_nframes = last_recordable_frame - last_recordable_frame;
- rec_offset = first_recordable_frame - transport_frame;
- break;
+ break;
}
if (rec_nframes && !was_recording) {
}
if (nominally_recording || rec_nframes) {
- _capture_buf->get_write_vector (&_capture_vector);
-
- if (rec_nframes <= _capture_vector.len[0]) {
-
- _current_capture_buffer = _capture_vector.buf[0];
- /* note: grab the entire port buffer, but only copy what we were supposed to for recording, and use
- rec_offset
- */
-
- // FIXME: midi buffer size?
-
- // FIXME: reading from a MIDI port is different, can't just memcpy
- //memcpy (_current_capture_buffer, _io->input(0)->get_buffer (rec_nframes) + offset + rec_offset, sizeof (RawMidi) * rec_nframes);
- assert(_source_port);
-
- /*for (size_t i=0; i < _source_port->size(); ++i) {
- cerr << "DISKSTREAM GOT EVENT(1) " << i << "!!\n";
- }
+ assert(_source_port);
- if (_source_port->size() == 0)
- cerr << "No events :/ (1)\n";
- */
+ // 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);
+ MidiBuffer::iterator port_iter = _source_port->get_midi_buffer().begin();
- } else {
-
- jack_nframes_t total = _capture_vector.len[0] + _capture_vector.len[1];
-
- if (rec_nframes > total) {
- cerr << "DiskOverrun\n";
- //DiskOverrun (); // FIXME
- goto out;
- }
-
- // FIXME (see above)
- //RawMidi* buf = _io->input (0)->get_buffer (nframes) + offset;
- assert(_source_port);
-
- /*
- for (size_t i=0; i < _source_port->size(); ++i) {
- cerr << "DISKSTREAM GOT EVENT(2) " << i << "!!\n";
- }
- if (_source_port->size() == 0)
- cerr << "No events :/ (2)\n";
- */
-
- RawMidi* buf = NULL; // FIXME FIXME FIXME (make it compile)
- assert(false);
- jack_nframes_t first = _capture_vector.len[0];
-
- memcpy (_capture_wrap_buffer, buf, sizeof (RawMidi) * first);
- memcpy (_capture_vector.buf[0], buf, sizeof (RawMidi) * first);
- memcpy (_capture_wrap_buffer+first, buf + first, sizeof (RawMidi) * (rec_nframes - first));
- memcpy (_capture_vector.buf[1], buf + first, sizeof (RawMidi) * (rec_nframes - first));
-
- _current_capture_buffer = _capture_wrap_buffer;
+ 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) {
}
}
-
+
if (rec_nframes) {
-
- // FIXME: filthy hack to fool the GUI into thinking we're doing something
- //if (_write_source)
- // _write_source->ViewDataRangeReady (transport_frame, rec_nframes); /* EMIT SIGNAL */
+ /* XXX XXX XXX XXX XXX XXX XXX XXX */
+
/* data will be written to disk */
if (rec_nframes == nframes && rec_offset == 0) {
- _current_playback_buffer = _current_capture_buffer;
playback_distance = nframes;
-
} else {
-
-
- /* we can't use the capture buffer as the playback buffer, because
- we recorded only a part of the current process' cycle data
- for capture.
- */
-
+
collect_playback = true;
}
/* can't do actual capture yet - waiting for latency effects to finish before we start*/
- _current_playback_buffer = _current_capture_buffer;
-
playback_distance = nframes;
} else {
/* we're doing playback */
- jack_nframes_t necessary_samples;
+ nframes_t necessary_samples;
/* no varispeed playback if we're recording, because the output .... TBD */
if (rec_nframes == 0 && _actual_speed != 1.0f) {
- necessary_samples = (jack_nframes_t) floor ((nframes * fabs (_actual_speed))) + 1;
+ necessary_samples = (nframes_t) floor ((nframes * fabs (_actual_speed))) + 1;
} else {
necessary_samples = nframes;
}
-
- _playback_buf->get_read_vector (&_playback_vector);
-
- if (necessary_samples <= _playback_vector.len[0]) {
-
- _current_playback_buffer = _playback_vector.buf[0];
-
- } else {
- jack_nframes_t total = _playback_vector.len[0] + _playback_vector.len[1];
-
- if (necessary_samples > total) {
- //cerr << "DiskUnderrun\n";
- //DiskUnderrun (); // FIXME
- //goto out;
-
- } else {
-
- memcpy (_playback_wrap_buffer, _playback_vector.buf[0],
- _playback_vector.len[0] * sizeof (RawMidi));
- memcpy (_playback_wrap_buffer + _playback_vector.len[0], _playback_vector.buf[1],
- (necessary_samples - _playback_vector.len[0]) * sizeof (RawMidi));
-
- _current_playback_buffer = _playback_wrap_buffer;
- }
- }
-
-#if 0
- if (rec_nframes == 0 && _actual_speed != 1.0f && _actual_speed != -1.0f) {
-
- uint64_t phase = last_phase;
- jack_nframes_t i = 0;
- // Linearly interpolate into the alt buffer
- // using 40.24 fixp maths (swh)
-
- for (c = channels.begin(); c != channels.end(); ++c) {
-
- float fr;
- ChannelInfo& chan (*c);
-
- i = 0;
- phase = last_phase;
-
- for (jack_nframes_t outsample = 0; outsample < nframes; ++outsample) {
- i = phase >> 24;
- fr = (phase & 0xFFFFFF) / 16777216.0f;
- chan.speed_buffer[outsample] =
- chan._current_playback_buffer[i] * (1.0f - fr) +
- chan._current_playback_buffer[i+1] * fr;
- phase += phi;
- }
-
- chan._current_playback_buffer = chan.speed_buffer;
- }
-
- playback_distance = i + 1;
- last_phase = (phase & 0xFFFFFF);
-
- } else {
- playback_distance = nframes;
- }
-#endif
-
- playback_distance = nframes;
+ // XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
+ // Write into playback buffer here, and whatnot?
+ //cerr << "MDS FIXME: collect playback" << endl;
}
ret = 0;
- out:
_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();
}
return ret;
-
- _processed = true;
-
- return 0;
}
bool
-MidiDiskstream::commit (jack_nframes_t nframes)
+MidiDiskstream::commit (nframes_t nframes)
{
bool need_butler = false;
playback_sample += playback_distance;
}
- _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;
}
-
+
if (_slaved) {
- need_butler = _playback_buf->write_space() >= _playback_buf->bufsize() / 2;
+ need_butler = _playback_buf->write_space() >= _playback_buf->capacity() / 2;
} else {
need_butler = _playback_buf->write_space() >= disk_io_chunk_frames
|| _capture_buf->read_space() >= disk_io_chunk_frames;
}
-
- state_lock.unlock();
+
+ if (commit_should_unlock) {
+ state_lock.unlock();
+ }
_processed = false;
int
MidiDiskstream::overwrite_existing_buffers ()
{
+ cerr << "MDS: overwrite_existing_buffers() (does nothing)" << endl;
return 0;
}
int
-MidiDiskstream::seek (jack_nframes_t frame, bool complete_refill)
+MidiDiskstream::seek (nframes_t frame, bool complete_refill)
{
Glib::Mutex::Lock lm (state_lock);
- return 0;
+ int ret = -1;
+
+ //cerr << "MDS: seek: " << frame << endl;
+
+ _playback_buf->reset();
+ _capture_buf->reset();
+
+ playback_sample = frame;
+ file_frame = frame;
+
+ if (complete_refill) {
+ while ((ret = do_refill_with_alloc ()) > 0) ;
+ } else {
+ ret = do_refill_with_alloc ();
+ }
+
+ return ret;
}
int
-MidiDiskstream::can_internal_playback_seek (jack_nframes_t distance)
+MidiDiskstream::can_internal_playback_seek (nframes_t distance)
{
- return 0;
+ if (_playback_buf->read_space() < distance) {
+ return false;
+ } else {
+ return true;
+ }
}
int
-MidiDiskstream::internal_playback_seek (jack_nframes_t distance)
+MidiDiskstream::internal_playback_seek (nframes_t distance)
{
+ cerr << "MDS: internal_playback_seek " << distance << endl;
+
+ first_recordable_frame += distance;
+ playback_sample += distance;
+
return 0;
}
+/** @a start is set to the new frame position (TIME) read up to */
int
-MidiDiskstream::read (MidiBuffer& dst, jack_nframes_t& start, jack_nframes_t cnt, bool reversed)
-{
+MidiDiskstream::read (nframes_t& start, nframes_t dur, bool reversed)
+{
+ nframes_t this_read = 0;
+ bool reloop = false;
+ nframes_t loop_end = 0;
+ nframes_t loop_start = 0;
+ nframes_t loop_length = 0;
+ Location *loc = 0;
+
+ if (!reversed) {
+ /* Make the use of a Location atomic for this read operation.
+
+ Note: Locations don't get deleted, so all we care about
+ when I say "atomic" is that we are always pointing to
+ the same one and using a start/length values obtained
+ just once.
+ */
+
+ if ((loc = loop_location) != 0) {
+ loop_start = loc->start();
+ loop_end = loc->end();
+ loop_length = loop_end - loop_start;
+ }
+
+ /* if we are looping, ensure that the first frame we read is at the correct
+ position within the loop.
+ */
+
+ if (loc && start >= loop_end) {
+ //cerr << "start adjusted from " << start;
+ start = loop_start + ((start - loop_start) % loop_length);
+ //cerr << "to " << start << endl;
+ }
+ //cerr << "start is " << start << " loopstart: " << loop_start << " loopend: " << loop_end << endl;
+ }
+
+ while (dur) {
+
+ /* take any loop into account. we can't read past the end of the loop. */
+
+ if (loc && (loop_end - start < dur)) {
+ this_read = loop_end - start;
+ //cerr << "reloop true: thisread: " << this_read << " dur: " << dur << endl;
+ reloop = true;
+ } else {
+ reloop = false;
+ this_read = dur;
+ }
+
+ if (this_read == 0) {
+ break;
+ }
+
+ this_read = min(dur,this_read);
+
+ if (midi_playlist()->read (*_playback_buf, start, this_read) != this_read) {
+ error << string_compose(_("MidiDiskstream %1: cannot read %2 from playlist at frame %3"), _id, this_read,
+ start) << endmsg;
+ return -1;
+ }
+
+ _read_data_count = _playlist->read_data_count();
+
+ if (reversed) {
+
+ // Swap note ons with note offs here. etc?
+ // Fully reversing MIDI required look-ahead (well, behind) to find previous
+ // CC values etc. hard.
+
+ } else {
+
+ /* if we read to the end of the loop, go back to the beginning */
+
+ if (reloop) {
+ start = loop_start;
+ } else {
+ start += this_read;
+ }
+ }
+
+ dur -= this_read;
+ //offset += this_read;
+ }
+
return 0;
}
int
MidiDiskstream::do_refill ()
{
- // yeah, the data's ready. promise.
- return 0;
+ int32_t ret = 0;
+ size_t write_space = _playback_buf->write_space();
+
+ bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
+
+ if (write_space == 0) {
+ return 0;
+ }
+
+ /* 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.
+
+ at higher speeds, just do it because the sync between butler
+ and audio thread may not be good enough.
+ */
+
+ if ((write_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) {
+ //cerr << "No refill 1\n";
+ return 0;
+ }
+
+ /* when slaved, don't try to get too close to the read pointer. this
+ leaves space for the buffer reversal to have something useful to
+ work with.
+ */
+
+ if (_slaved && write_space < (_playback_buf->capacity() / 2)) {
+ //cerr << "No refill 2\n";
+ return 0;
+ }
+
+ if (reversed) {
+ //cerr << "No refill 3 (reverse)\n";
+ return 0;
+ }
+
+ if (file_frame == max_frames) {
+ //cerr << "No refill 4 (EOF)\n";
+
+ /* at end: nothing to do */
+
+ return 0;
+ }
+
+ // At this point we...
+ assert(_playback_buf->write_space() > 0); // ... have something to write to, and
+ assert(file_frame <= max_frames); // ... something to write
+
+ nframes_t file_frame_tmp = file_frame;
+ nframes_t to_read = min(disk_io_chunk_frames, (max_frames - file_frame));
+
+ // FIXME: read count?
+ if (read (file_frame_tmp, to_read, reversed)) {
+ ret = -1;
+ goto out;
+ }
+
+ file_frame = file_frame_tmp;
+
+out:
+
+ return ret;
}
/** Flush pending data to disk.
int
MidiDiskstream::do_flush (Session::RunContext context, bool force_flush)
{
- /* hey, so did you write that data? */
+ uint32_t to_write;
+ int32_t ret = 0;
+ // FIXME: I'd be lying if I said I knew what this thing was
+ //RingBufferNPT<CaptureTransition>::rw_vector transvec;
+ nframes_t total;
- // oh yeah, you bet. wrote it good. honest.
-
- return 0;
+ _write_data_count = 0;
+
+ 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;
+
+ if (total == 0 || _capture_buf->read_space() == 0 && _session.transport_speed() == 0 || (total < disk_io_chunk_frames && !force_flush && 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 * disk_io_chunk_frames || ((force_flush || !was_recording) && total > disk_io_chunk_frames)) {
+ ret = 1;
+ }
+
+ //to_write = min (disk_io_chunk_frames, (nframes_t) vector.len[0]);
+ to_write = disk_io_chunk_frames;
+
+ assert(!destructive());
+
+ if (record_enabled() && _session.transport_frame() - _last_flush_frame > disk_io_chunk_frames) {
+ if ((!_write_source) || _write_source->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();
+ }
+ }
+
+out:
+ //return ret;
+ return 0; // FIXME: everything's fine! always! honest!
}
void
uint32_t buffer_position;
bool more_work = true;
int err = 0;
- MidiRegion* region = 0;
- jack_nframes_t total_capture;
+ boost::shared_ptr<MidiRegion> region;
+ nframes_t total_capture;
MidiRegion::SourceList srcs;
MidiRegion::SourceList::iterator src;
vector<CaptureInfo*>::iterator ci;
if (abort_capture) {
- list<Source*>* deletion_list = new list<Source*>;
-
if (_write_source) {
- _write_source->mark_for_remove ();
- _write_source->release ();
-
- deletion_list->push_back (_write_source);
- _write_source = 0;
+ _write_source->mark_for_remove ();
+ _write_source->drop_references ();
+ _write_source.reset();
}
/* new source set up in "out" below */
- if (!deletion_list->empty()) {
- DeleteSources (deletion_list);
- } else {
- delete deletion_list;
- }
-
} else {
assert(_write_source);
}
/* 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);
- 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);
-
- }
+ 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);
- region = new MidiRegion (srcs, _write_source->last_capture_start_frame(), total_capture,
- region_name_from_path (_write_source->name()),
- 0, Region::Flag (Region::DefaultFlags|Region::Automatic|Region::WholeFile));
+ 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)));
+ region = boost::dynamic_pointer_cast<MidiRegion> (rx);
region->special_set_position (capture_info.front()->start);
}
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";
try {
- region = new MidiRegion (srcs, buffer_position, (*ci)->frames, region_name);
+ boost::shared_ptr<Region> rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name));
+ region = boost::dynamic_pointer_cast<MidiRegion> (rx);
}
catch (failed_constructor& err) {
- error << _("MidiDiskstream: could not create region for captured audio!") << endmsg;
+ 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);
// cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl;
i_am_the_modifier++;
- _playlist->add_region (*region, (*ci)->start);
+ _playlist->add_region (region, (*ci)->start);
i_am_the_modifier--;
buffer_position += (*ci)->frames;
_playlist->thaw ();
XMLNode &after = _playlist->get_state();
- _session.add_command (new MementoCommand<Playlist>(*_playlist, before, after));
+ _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;
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)
{
CaptureInfo* ci = new CaptureInfo;
- ci->start = capture_start_frame;
+ ci->start = capture_start_frame;
ci->frames = capture_captured;
/* XXX theoretical race condition here. Need atomic exchange ?
g_atomic_int_set (&_record_enabled, 1);
- if (Config->get_use_hardware_monitoring() && _source_port) {
- _source_port->request_monitor_input (!(_session.get_auto_input() && rolling));
+ if (_source_port && Config->get_monitoring_model() == HardwareMonitoring) {
+ _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 */
}
MidiDiskstream::disengage_record_enable ()
{
g_atomic_int_set (&_record_enabled, 0);
- if (Config->get_use_hardware_monitoring()) {
+ if (_source_port && Config->get_monitoring_model() == HardwareMonitoring) {
if (_source_port) {
_source_port->request_monitor_input (false);
}
node->add_property ("speed", buf);
node->add_property("name", _name);
- id().print(buf);
+ id().print(buf, sizeof(buf));
node->add_property("id", buf);
if (_write_source && _session.get_record_enabled()) {
Location* pi;
- if (_session.get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
+ if (Config->get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
snprintf (buf, sizeof (buf), "%" PRIu32, pi->start());
} else {
snprintf (buf, sizeof (buf), "%" PRIu32, _session.transport_frame());
}
if ((prop = node.property ("flags")) != 0) {
- _flags = strtol (prop->value().c_str(), 0, 0);
+ _flags = Flag (string_2_enum (prop->value(), _flags));
}
if ((prop = node.property ("channels")) != 0) {
if (_write_source) {
- if (SMFSource::is_empty (_write_source->path())) {
+ if (_write_source->is_empty ()) {
_write_source->mark_for_remove ();
- _write_source->release();
- delete _write_source;
+ _write_source.reset();
} else {
- _write_source->release();
- _write_source = 0;
+ _write_source.reset();
}
}
try {
- _write_source = dynamic_cast<SMFSource*>(_session.create_midi_source_for_session (*this));
+ _write_source = boost::dynamic_pointer_cast<SMFSource>(_session.create_midi_source_for_session (*this));
if (!_write_source) {
throw failed_constructor();
}
catch (failed_constructor &err) {
error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg;
- _write_source = 0;
+ _write_source.reset();
return -1;
}
- _write_source->use ();
_write_source->set_allow_remove_if_empty (true);
return 0;
if (_write_source && mark_write_complete) {
_write_source->mark_streaming_write_completed ();
}
- use_new_write_source ();
- assert(_write_source);
+
+ use_new_write_source (0);
+
+ if (record_enabled()) {
+ //_capturing_sources.push_back (_write_source);
+ }
}
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;
}
void
-MidiDiskstream::set_block_size (jack_nframes_t nframes)
+MidiDiskstream::set_block_size (nframes_t nframes)
{
}
MidiDiskstream::playback_buffer_load () const
{
return (float) ((double) _playback_buf->read_space()/
- (double) _playback_buf->bufsize());
+ (double) _playback_buf->capacity());
}
float
MidiDiskstream::capture_buffer_load () const
{
return (float) ((double) _capture_buf->write_space()/
- (double) _capture_buf->bufsize());
+ (double) _capture_buf->capacity());
}
{
return 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)
+{
+ dst.clear();
+ assert(dst.size() == 0);
+
+ // Reverse. ... We just don't do reverse, ok? Back off.
+ if (end <= start) {
+ return;
+ }
+
+ // Translates stamps to be relative to start
+ _playback_buf->read(dst, start, end);
+}