summaryrefslogtreecommitdiff
path: root/libs/ardour/midi_diskstream.cc
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2006-08-16 20:36:14 +0000
committerDavid Robillard <d@drobilla.net>2006-08-16 20:36:14 +0000
commit7250433f50236a05fc652fa41c23bf53fbf6a0fd (patch)
tree0371dfe1b6ce5a9eb1769d10505a6ca658ca1a7b /libs/ardour/midi_diskstream.cc
parent5952c48a848926edb02b5d630e36cc461c893964 (diff)
Progress on the disk side of things:
- MidiRingBuffer implementation - MidiDiskstream reading from playlists - MidiPlaylist reading from regions - MidiRegions returning random notes for the time being, but the inter-thread stuff works.. Horrible awful mess, not really commit worthy, but I need to move machines. Nothing to see here.. :) git-svn-id: svn://localhost/ardour2/branches/midi@835 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour/midi_diskstream.cc')
-rw-r--r--libs/ardour/midi_diskstream.cc537
1 files changed, 324 insertions, 213 deletions
diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc
index 86a95a3cc0..223cfc15f6 100644
--- a/libs/ardour/midi_diskstream.cc
+++ b/libs/ardour/midi_diskstream.cc
@@ -61,10 +61,10 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
: 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)
@@ -86,10 +86,10 @@ 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)
@@ -124,10 +124,10 @@ 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 RingBufferNPT<RawMidi> (_session.diskstream_buffer_size());
- _capture_buf = new RingBufferNPT<RawMidi> (_session.diskstream_buffer_size());
+ //_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);
_n_channels = ChanCount(DataType::MIDI, 1);
@@ -435,9 +435,9 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
bool nominally_recording;
bool re = record_enabled ();
bool collect_playback = false;
-
- _current_capture_buffer = 0;
- _current_playback_buffer = 0;
+
+ /*_current_capture_buffer = 0;
+ _current_playback_buffer = 0;*/
/* if we've already processed the frames corresponding to this call,
just return. this allows multiple routes that are taking input
@@ -445,7 +445,7 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
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;
@@ -463,58 +463,58 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
/* 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;
}
-
+
adjust_capture_position = 0;
if (nominally_recording || (_session.get_record_enabled() && _session.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) {
@@ -529,64 +529,20 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
}
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";
- }
-
- if (_source_port->size() == 0)
- cerr << "No events :/ (1)\n";
- */
+ assert(_source_port);
- } 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";
- */
+ // Pump entire port buffer into the ring buffer (FIXME!)
+ _capture_buf->write(_source_port->get_midi_buffer(), transport_frame);
- RawMidi* buf = NULL; // FIXME FIXME FIXME (make it compile)
- assert(false);
- jack_nframes_t first = _capture_vector.len[0];
+ /*
+ for (size_t i=0; i < _source_port->size(); ++i) {
+ cerr << "DISKSTREAM GOT EVENT(1) " << i << "!!\n";
+ }
- 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;
- }
+ if (_source_port->size() == 0)
+ cerr << "No events :/ (1)\n";
+ */
} else {
if (was_recording) {
@@ -594,38 +550,19 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
}
}
-
+
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;
- }
-
adjust_capture_position = rec_nframes;
} else if (nominally_recording) {
/* can't do actual capture yet - waiting for latency effects to finish before we start*/
- _current_playback_buffer = _current_capture_buffer;
+ throw; // forget all that jazz!
playback_distance = nframes;
@@ -647,84 +584,22 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
} 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
}
ret = 0;
- out:
_processed = true;
if (ret) {
/* we're exiting with failure, so ::commit will not
be called. unlock the state lock.
- */
-
+ */
+
state_lock.unlock();
}
@@ -746,24 +621,27 @@ MidiDiskstream::commit (jack_nframes_t nframes)
playback_sample += playback_distance;
}
- _playback_buf->increment_read_ptr (playback_distance);
-
- if (adjust_capture_position) {
- _capture_buf->increment_write_ptr (adjust_capture_position);
- }
-
+ /* 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;
}
-
+
if (_slaved) {
need_butler = _playback_buf->write_space() >= _playback_buf->bufsize() / 2;
} else {
need_butler = _playback_buf->write_space() >= disk_io_chunk_frames
|| _capture_buf->read_space() >= disk_io_chunk_frames;
}
-
+ */
state_lock.unlock();
_processed = false;
@@ -792,24 +670,126 @@ int
MidiDiskstream::seek (jack_nframes_t frame, bool complete_refill)
{
Glib::Mutex::Lock lm (state_lock);
- return 0;
+ int ret = -1;
+
+ _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)
{
- return 0;
+ if (_playback_buf->read_space() < distance) {
+ return false;
+ } else {
+ return true;
+ }
}
int
MidiDiskstream::internal_playback_seek (jack_nframes_t distance)
{
+ 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 (jack_nframes_t& start, jack_nframes_t dur, bool reversed)
+{
+ jack_nframes_t this_read = 0;
+ bool reloop = false;
+ jack_nframes_t loop_end = 0;
+ jack_nframes_t loop_start = 0;
+ jack_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) {
+
+ cerr << "Reversed MIDI.. that's just crazy talk." << endl;
+ // Swap note ons with note offs here
+
+ } 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;
+ }
+
return 0;
}
@@ -822,8 +802,94 @@ MidiDiskstream::do_refill_with_alloc ()
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 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.
+
+ 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;
+ }
+
+#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:
+ 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:
+
+ jack_nframes_t file_frame_tmp = file_frame;
+ jack_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.
@@ -1338,14 +1404,14 @@ float
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());
}
@@ -1354,3 +1420,48 @@ 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
+ */
+void
+MidiDiskstream::get_playback(MidiBuffer& dst, jack_nframes_t start, jack_nframes_t end)
+{
+ assert(end > start);
+ dst.clear();
+ assert(dst.size() == 0);
+/*
+ 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;
+*/
+ _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);
+ dst[i].time -= start;
+ }
+}