diff options
author | David Robillard <d@drobilla.net> | 2006-08-16 20:36:14 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2006-08-16 20:36:14 +0000 |
commit | 7250433f50236a05fc652fa41c23bf53fbf6a0fd (patch) | |
tree | 0371dfe1b6ce5a9eb1769d10505a6ca658ca1a7b /libs | |
parent | 5952c48a848926edb02b5d630e36cc461c893964 (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')
-rw-r--r-- | libs/ardour/ardour/audio_track.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/buffer.h | 27 | ||||
-rw-r--r-- | libs/ardour/ardour/buffer_set.h | 6 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_diskstream.h | 23 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_playlist.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_port.h | 16 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_region.h | 14 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_ring_buffer.h | 242 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_track.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/route.h | 5 | ||||
-rw-r--r-- | libs/ardour/ardour/track.h | 3 | ||||
-rw-r--r-- | libs/ardour/ardour/types.h | 3 | ||||
-rw-r--r-- | libs/ardour/audio_diskstream.cc | 2 | ||||
-rw-r--r-- | libs/ardour/audio_track.cc | 6 | ||||
-rw-r--r-- | libs/ardour/buffer.cc | 93 | ||||
-rw-r--r-- | libs/ardour/io.cc | 9 | ||||
-rw-r--r-- | libs/ardour/midi_diskstream.cc | 537 | ||||
-rw-r--r-- | libs/ardour/midi_playlist.cc | 32 | ||||
-rw-r--r-- | libs/ardour/midi_port.cc | 40 | ||||
-rw-r--r-- | libs/ardour/midi_region.cc | 44 | ||||
-rw-r--r-- | libs/ardour/midi_track.cc | 81 | ||||
-rw-r--r-- | libs/ardour/route.cc | 6 | ||||
-rw-r--r-- | libs/ardour/track.cc | 2 |
23 files changed, 816 insertions, 387 deletions
diff --git a/libs/ardour/ardour/audio_track.h b/libs/ardour/ardour/audio_track.h index 54a8c19a22..bca79b0d7d 100644 --- a/libs/ardour/ardour/audio_track.h +++ b/libs/ardour/ardour/audio_track.h @@ -64,10 +64,6 @@ class AudioTrack : public Track protected: XMLNode& state (bool full); - void passthru_silence (jack_nframes_t start_frame, jack_nframes_t end_frame, - jack_nframes_t nframes, jack_nframes_t offset, int declick, - bool meter); - ChanCount n_process_buffers (); private: diff --git a/libs/ardour/ardour/buffer.h b/libs/ardour/ardour/buffer.h index df7f57455b..896d6e7616 100644 --- a/libs/ardour/ardour/buffer.h +++ b/libs/ardour/ardour/buffer.h @@ -181,23 +181,36 @@ public: ~MidiBuffer(); - // FIXME: clear events starting at offset in time - void silence(jack_nframes_t len, jack_nframes_t offset=0) { assert(offset == 0); _size = 0; } + void silence(jack_nframes_t dur, jack_nframes_t offset=0); void read_from(const Buffer& src, jack_nframes_t nframes, jack_nframes_t offset); - void set_size(size_t size) { _size = size; } + bool push_back(const MidiEvent& event); + + const MidiEvent& operator[](size_t i) const { assert(i < _size); return _events[i]; } + MidiEvent& operator[](size_t i) { assert(i < _size); return _events[i]; } + + static size_t max_event_size() { return MAX_EVENT_SIZE; } + + //void set_size(size_t size) { _size = size; } - const RawMidi* data() const { return _data; } - RawMidi* data() { return _data; } + //const RawMidi* data() const { return _data; } + //RawMidi* data() { return _data; } private: // These are undefined (prevent copies) MidiBuffer(const MidiBuffer& copy); MidiBuffer& operator=(const MidiBuffer& copy); - bool _owns_data; - RawMidi* _data; ///< Actual buffer contents + // FIXME: Jack needs to tell us this + static const size_t MAX_EVENT_SIZE = 4; // bytes + + /* We use _size as "number of events", so the size of _data is + * (_size * MAX_EVENT_SIZE) + */ + + MidiEvent* _events; ///< Event structs that point to offsets in _data + RawMidi* _data; ///< MIDI, straight up. No time stamps. }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/buffer_set.h b/libs/ardour/ardour/buffer_set.h index dfb5dd3611..43df729e05 100644 --- a/libs/ardour/ardour/buffer_set.h +++ b/libs/ardour/ardour/buffer_set.h @@ -28,6 +28,7 @@ namespace ARDOUR { class Buffer; class AudioBuffer; +class MidiBuffer; class PortSet; @@ -77,6 +78,11 @@ public: { return (AudioBuffer&)get(DataType::AUDIO, i); } + + MidiBuffer& get_midi(size_t i) + { + return (MidiBuffer&)get(DataType::MIDI, i); + } void read_from(BufferSet& in, jack_nframes_t nframes, jack_nframes_t offset=0) { diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h index 901baf3c64..ccff993638 100644 --- a/libs/ardour/ardour/midi_diskstream.h +++ b/libs/ardour/ardour/midi_diskstream.h @@ -45,6 +45,8 @@ #include <ardour/utils.h> #include <ardour/diskstream.h> #include <ardour/midi_playlist.h> +#include <ardour/midi_ring_buffer.h> + struct tm; namespace ARDOUR { @@ -65,8 +67,7 @@ class MidiDiskstream : public Diskstream float playback_buffer_load() const; float capture_buffer_load() const; - RawMidi* playback_buffer () { return _current_playback_buffer; } - RawMidi* capture_buffer () { return _current_capture_buffer; } + void get_playback(MidiBuffer& dst, jack_nframes_t start, jack_nframes_t end); void set_record_enabled (bool yn); @@ -124,7 +125,7 @@ class MidiDiskstream : public Diskstream int do_refill_with_alloc(); - int read (MidiBuffer& dst, jack_nframes_t& start, jack_nframes_t cnt, bool reversed); + int read (jack_nframes_t& start, jack_nframes_t cnt, bool reversed); void finish_capture (bool rec_monitors_input); void transport_stopped (struct tm&, time_t, bool abort); @@ -147,17 +148,17 @@ class MidiDiskstream : public Diskstream void disengage_record_enable (); // FIXME: This is basically a single ChannelInfo.. abstractify that concept? - RingBufferNPT<RawMidi>* _playback_buf; - RingBufferNPT<RawMidi>* _capture_buf; - RawMidi* _current_playback_buffer; - RawMidi* _current_capture_buffer; - RawMidi* _playback_wrap_buffer; - RawMidi* _capture_wrap_buffer; + MidiRingBuffer* _playback_buf; + MidiRingBuffer* _capture_buf; + //RawMidi* _current_playback_buffer; + //RawMidi* _current_capture_buffer; + //RawMidi* _playback_wrap_buffer; + //RawMidi* _capture_wrap_buffer; MidiPort* _source_port; SMFSource* _write_source; ///< aka capturing source RingBufferNPT<CaptureTransition>* _capture_transition_buf; - RingBufferNPT<RawMidi>::rw_vector _playback_vector; - RingBufferNPT<RawMidi>::rw_vector _capture_vector; + //RingBufferNPT<RawMidi>::rw_vector _playback_vector; + //RingBufferNPT<RawMidi>::rw_vector _capture_vector; }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/midi_playlist.h b/libs/ardour/ardour/midi_playlist.h index 9e9cf250e2..11627b5a07 100644 --- a/libs/ardour/ardour/midi_playlist.h +++ b/libs/ardour/ardour/midi_playlist.h @@ -33,7 +33,7 @@ class Session; class Region; class MidiRegion; class Source; -class MidiBuffer; +class MidiRingBuffer; class MidiPlaylist : public ARDOUR::Playlist { @@ -56,7 +56,7 @@ public: MidiPlaylist (const MidiPlaylist&, jack_nframes_t start, jack_nframes_t cnt, string name, bool hidden = false); - jack_nframes_t read (MidiBuffer& buf, + jack_nframes_t read (MidiRingBuffer& buf, jack_nframes_t start, jack_nframes_t cnt, uint32_t chan_n=0); int set_state (const XMLNode&); diff --git a/libs/ardour/ardour/midi_port.h b/libs/ardour/ardour/midi_port.h index 3c48f0ce9e..f6eafe11bd 100644 --- a/libs/ardour/ardour/midi_port.h +++ b/libs/ardour/ardour/midi_port.h @@ -39,19 +39,23 @@ class MidiPort : public Port { DataType type() const { return DataType(DataType::MIDI); } - MidiBuffer& get_buffer() { - return *_buffer; + Buffer& get_buffer() { + return _buffer; + } + + MidiBuffer& get_midi_buffer() { + return _buffer; } void cycle_start(jack_nframes_t nframes); void cycle_end(); - size_t capacity() { return _buffer->capacity(); } - size_t size() { return _buffer->size(); } + size_t capacity() { return _buffer.capacity(); } + size_t size() { return _buffer.size(); } /** Assumes that the port is an output port */ void silence (jack_nframes_t nframes, jack_nframes_t offset) { - _buffer->silence(nframes, offset); + _buffer.silence(nframes, offset); } protected: @@ -61,7 +65,7 @@ class MidiPort : public Port { /* engine isn't supposed to access below here */ - MidiBuffer* _buffer; + MidiBuffer _buffer; jack_nframes_t _nframes_this_cycle; }; diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h index 0144f52473..745d3fa4a8 100644 --- a/libs/ardour/ardour/midi_region.h +++ b/libs/ardour/ardour/midi_region.h @@ -41,7 +41,7 @@ class Playlist; class Session; class MidiFilter; class MidiSource; -class MidiBuffer; +class MidiRingBuffer; class MidiRegion : public Region { @@ -57,16 +57,16 @@ class MidiRegion : public Region MidiSource& midi_source (uint32_t n=0) const; - jack_nframes_t read_at (MidiBuffer& out, + jack_nframes_t read_at (MidiRingBuffer& dst, jack_nframes_t position, - jack_nframes_t cnt, + jack_nframes_t dur, uint32_t chan_n = 0, jack_nframes_t read_frames = 0, jack_nframes_t skip_frames = 0) const; - jack_nframes_t master_read_at (MidiBuffer& buf, + jack_nframes_t master_read_at (MidiRingBuffer& dst, jack_nframes_t position, - jack_nframes_t cnt, + jack_nframes_t dur, uint32_t chan_n=0) const; XMLNode& state (bool); @@ -83,9 +83,9 @@ class MidiRegion : public Region StateManager::State* state_factory (std::string why) const; Change restore_state (StateManager::State&); - jack_nframes_t _read_at (const SourceList&, MidiBuffer& buf, + jack_nframes_t _read_at (const SourceList&, MidiRingBuffer& dst, jack_nframes_t position, - jack_nframes_t cnt, + jack_nframes_t dur, uint32_t chan_n = 0, jack_nframes_t read_frames = 0, jack_nframes_t skip_frames = 0) const; diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h new file mode 100644 index 0000000000..53057dbcba --- /dev/null +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -0,0 +1,242 @@ +/* + Copyright (C) 2006 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + 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. +*/ + +#ifndef __ardour_midi_ring_buffer_h__ +#define __ardour_midi_ring_buffer_h__ + +#include <algorithm> +#include <ardour/types.h> +#include <pbd/ringbufferNPT.h> +#include <ardour/buffer.h> + +namespace ARDOUR { + +/** A MIDI RingBuffer + * (necessary because MIDI events are variable sized so a generic RB won't do). + * + * ALL publically accessible sizes refer to event COUNTS. What actually goes + * on in here is none of the callers business :) + */ +class MidiRingBuffer { +public: + MidiRingBuffer (size_t size) + : _size(size) + , _max_event_size(MidiBuffer::max_event_size()) + , _ev_buf(new MidiEvent[size]) + , _raw_buf(new RawMidi[size * _max_event_size]) + { + reset (); + assert(read_space() == 0); + assert(write_space() == size - 1); + } + + virtual ~MidiRingBuffer() { + delete[] _ev_buf; + delete[] _raw_buf; + } + + void reset () { + /* !!! NOT THREAD SAFE !!! */ + g_atomic_int_set (&_write_ptr, 0); + g_atomic_int_set (&_read_ptr, 0); + } + + size_t write_space () { + size_t w, r; + + w = g_atomic_int_get (&_write_ptr); + r = g_atomic_int_get (&_read_ptr); + + if (w > r) { + return ((r - w + _size) % _size) - 1; + } else if (w < r) { + return (r - w) - 1; + } else { + return _size - 1; + } + } + + size_t read_space () { + size_t w, r; + + w = g_atomic_int_get (&_write_ptr); + r = g_atomic_int_get (&_read_ptr); + + if (w > r) { + return w - r; + } else { + return (w - r + _size) % _size; + } + } + + size_t capacity() const { return _size; } + + /** Read one event and appends it to @a out. */ + //size_t read(MidiBuffer& out); + + /** Write one event (@a in) */ + size_t write(const MidiEvent& in); // deep copies in + + /** Read events all events up to time @a end into @a out, leaving stamps intact. + * Any events before @a start will be dropped. */ + size_t read(MidiBuffer& out, jack_nframes_t start, jack_nframes_t end); + + /** Write all events from @a in, applying @a offset to all time stamps */ + size_t write(const MidiBuffer& in, jack_nframes_t offset = 0); + + inline void clear_event(size_t index); + +private: + + // _event_ indices + mutable gint _write_ptr; + mutable gint _read_ptr; + + size_t _size; // size (capacity) in events + size_t _max_event_size; // ratio of raw_buf size to ev_buf size + MidiEvent* _ev_buf; // these point into... + RawMidi* _raw_buf; // this + +}; + +/** Just for sanity checking */ +inline void +MidiRingBuffer::clear_event(size_t index) +{ + memset(&_ev_buf[index].buffer, 0, _max_event_size); + _ev_buf[index].time = 0; + _ev_buf[index].size = 0; + _ev_buf[index].buffer = 0; + +} +#if 0 +inline size_t +MidiRingBuffer::read (MidiBuffer& buf) +{ + const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); + + if (read_space() == 0) { + return 0; + } else { + MidiEvent* const read_ev = &_ev_buf[priv_read_ptr]; + assert(read_ev->size > 0); + buf.push_back(*read_ev); + printf("MRB - read %xd %d %d with time %u at index %zu\n", + read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time, + priv_read_ptr); + clear_event(priv_read_ptr); + g_atomic_int_set(&_read_ptr, (priv_read_ptr + 1) % _size); + return 1; + } +} +#endif +inline size_t +MidiRingBuffer::write (const MidiEvent& ev) +{ + //static jack_nframes_t last_write_time = 0; + + assert(ev.size > 0); + + size_t priv_write_ptr = g_atomic_int_get(&_write_ptr); + + if (write_space () == 0) { + return 0; + } else { + //assert(ev.time >= last_write_time); + + const size_t raw_index = priv_write_ptr * _max_event_size; + + MidiEvent* const write_ev = &_ev_buf[priv_write_ptr]; + *write_ev = ev; + + memcpy(&_raw_buf[raw_index], ev.buffer, ev.size); + write_ev->buffer = &_raw_buf[raw_index]; + g_atomic_int_set(&_write_ptr, (priv_write_ptr + 1) % _size); + + printf("MRB - wrote %xd %d %d with time %u at index %zu (raw index %zu)\n", + write_ev->buffer[0], write_ev->buffer[1], write_ev->buffer[2], write_ev->time, + priv_write_ptr, raw_index); + + assert(write_ev->size = ev.size); + + //last_write_time = ev.time; + printf("(W) read space: %zu\n", read_space()); + + return 1; + } +} + +inline size_t +MidiRingBuffer::read(MidiBuffer& dst, jack_nframes_t start, jack_nframes_t end) +{ + if (read_space() == 0) + return 0; + + size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); + jack_nframes_t time = _ev_buf[priv_read_ptr].time; + size_t count = 0; + size_t limit = read_space(); + + while (time <= end && limit > 0) { + MidiEvent* const read_ev = &_ev_buf[priv_read_ptr]; + if (time >= start) { + dst.push_back(*read_ev); + printf("MRB - read %xd %d %d with time %u at index %zu\n", + read_ev->buffer[0], read_ev->buffer[1], read_ev->buffer[2], read_ev->time, + priv_read_ptr); + } else { + cerr << "MRB: LOST EVENT!" << endl; + } + + clear_event(priv_read_ptr); + + ++count; + --limit; + + priv_read_ptr = (priv_read_ptr + 1) % _size; + + assert(read_ev->time <= end); + time = _ev_buf[priv_read_ptr].time; + } + + g_atomic_int_set(&_read_ptr, priv_read_ptr); + printf("(R) read space: %zu\n", read_space()); + + return count; +} + +inline size_t +MidiRingBuffer::write(const MidiBuffer& in, jack_nframes_t offset) +{ + size_t num_events = in.size(); + size_t to_write = std::min(write_space(), num_events); + + // FIXME: double copy :/ + for (size_t i=0; i < to_write; ++i) { + MidiEvent ev = in[i]; + ev.time += offset; + write(ev); + } + + return to_write; +} + +} // namespace ARDOUR + +#endif // __ardour_midi_ring_buffer_h__ + diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index 654eb98e30..409aa171e9 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -76,10 +76,6 @@ public: protected: XMLNode& state (bool full); - void passthru_silence (jack_nframes_t start_frame, jack_nframes_t end_frame, - jack_nframes_t nframes, jack_nframes_t offset, int declick, - bool meter); - ChanCount n_process_buffers (); private: diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index db5cddca39..107abb7b48 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -309,10 +309,13 @@ class Route : public IO bool meter); protected: - /* for derived classes */ virtual XMLNode& state(bool); + void passthru_silence (jack_nframes_t start_frame, jack_nframes_t end_frame, + jack_nframes_t nframes, jack_nframes_t offset, int declick, + bool meter); + void silence (jack_nframes_t nframes, jack_nframes_t offset); sigc::connection input_signal_connection; diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index 15a0a28aab..2b317a0299 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -95,9 +95,6 @@ class Track : public Route virtual XMLNode& state (bool full) = 0; - virtual void passthru_silence (jack_nframes_t start_frame, jack_nframes_t end_frame, - jack_nframes_t nframes, jack_nframes_t offset, int declick, bool meter) = 0; - virtual ChanCount n_process_buffers () = 0; Diskstream *_diskstream; diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 00833f6547..d5bfded460 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -50,7 +50,8 @@ namespace ARDOUR { typedef uint32_t layer_t; typedef uint64_t microseconds_t; - typedef jack_midi_event_t RawMidi; + typedef jack_midi_event_t MidiEvent; + typedef unsigned char RawMidi; enum IOChange { NoChange = 0, diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 784a14bb2b..40c8e3301b 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -968,7 +968,7 @@ AudioDiskstream::seek (jack_nframes_t frame, bool complete_refill) { Glib::Mutex::Lock lm (state_lock); uint32_t n; - int ret; + int ret = -1; ChannelList::iterator chan; for (n = 0, chan = channels.begin(); chan != channels.end(); ++chan, ++n) { diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 52cc7726c9..b12b79858e 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -406,12 +406,6 @@ AudioTrack::n_process_buffers () return max (_diskstream->n_channels(), redirect_max_outs); } -void -AudioTrack::passthru_silence (jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t nframes, jack_nframes_t offset, int declick, bool meter) -{ - process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, offset, true, declick, meter); -} - int AudioTrack::no_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, bool session_state_changing, bool can_record, bool rec_monitors_input) diff --git a/libs/ardour/buffer.cc b/libs/ardour/buffer.cc index fa4669cef6..7b99f2a900 100644 --- a/libs/ardour/buffer.cc +++ b/libs/ardour/buffer.cc @@ -16,8 +16,11 @@ 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <ardour/buffer.h> +#include <algorithm> #include <iostream> +using std::cerr; using std::endl; + +#include <ardour/buffer.h> namespace ARDOUR { @@ -62,47 +65,95 @@ AudioBuffer::~AudioBuffer() // FIXME: mirroring for MIDI buffers? MidiBuffer::MidiBuffer(size_t capacity) : Buffer(DataType::MIDI, capacity) - , _owns_data(true) +// , _owns_data(true) + , _events(NULL) , _data(NULL) { assert(capacity > 0); - _size = capacity; // For audio buffers, size = capacity (always) + _size = 0; + #ifdef NO_POSIX_MEMALIGN - _data = (RawMidi *) malloc(sizeof(RawMidi) * capacity); + _events = (MidiEvent *) malloc(sizeof(MidiEvent) * capacity); + _data = (RawMidi *) malloc(sizeof(RawMidi) * capacity * MAX_EVENT_SIZE); #else - posix_memalign((void**)&_data, 16, sizeof(RawMidi) * capacity); + posix_memalign((void**)&_events, 16, sizeof(MidiEvent) * capacity); + posix_memalign((void**)&_data, 16, sizeof(RawMidi) * capacity * MAX_EVENT_SIZE); #endif assert(_data); - memset(_data, 0, sizeof(RawMidi) * capacity); + assert(_events); + silence(_capacity); } MidiBuffer::~MidiBuffer() { - if (_owns_data) - free(_data); + free(_events); + free(_data); } -/** Note that offset and nframes refer to sample time, not actual buffer locations */ +/** Read events from @a src starting at time @a offset into the START of this buffer, for + * time direction @a nframes. Relative time, where 0 = start of buffer. + * + * Note that offset and nframes refer to sample time, NOT buffer offsets or event counts. + */ void MidiBuffer::read_from(const Buffer& src, jack_nframes_t nframes, jack_nframes_t offset) { - // FIXME: offsets? param semantics? - assert(src.type() == _type); assert(src.type() == DataType::MIDI); - assert(offset == 0); - MidiBuffer& msrc = (MidiBuffer&)src; - _size = 0; - for (size_t i=0; i < msrc.size() && msrc.data()[i].time < nframes; ++i) { - assert(i < _capacity); - _data[i] = msrc.data()[i]; - ++_size; + const MidiBuffer& msrc = (MidiBuffer&)src; + + assert(_capacity >= src.size()); + + clear(); + assert(_size == 0); + + // FIXME: This is embarrassingly slow. branch branch branch + for (size_t i=0; i < src.size(); ++i) { + const MidiEvent& ev = msrc[i]; + if (ev.time >= offset && ev.time < offset+nframes) { + push_back(ev); + } } - assert(_size == msrc.size()); +} - //if (_size > 0) - // std::cerr << "MidiBuffer wrote " << _size << " events.\n"; + +/** Push an event into the buffer. + * + * Note that the raw MIDI pointed to by ev will be COPIED and unmodified. + * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM). + * Realtime safe. + * @return false if operation failed (not enough room) + */ +bool +MidiBuffer::push_back(const MidiEvent& ev) +{ + if (_size == _capacity) + return false; + + RawMidi* const write_loc = _data + (_size * MAX_EVENT_SIZE); + + memcpy(write_loc, ev.buffer, ev.size); + _events[_size] = ev; + _events[_size].buffer = write_loc; + ++_size; + + //cerr << "MidiBuffer: pushed, size = " << _size << endl; + + return true; +} + + +void +MidiBuffer::silence(jack_nframes_t dur, jack_nframes_t offset) +{ + // FIXME use parameters + assert(offset == 0); + //assert(dur == _capacity); + + memset(_events, 0, sizeof(MidiEvent) * _capacity); + memset(_data, 0, sizeof(RawMidi) * _capacity * MAX_EVENT_SIZE); + _size = 0; } diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index fce8c3f1d8..430801310d 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -213,15 +213,12 @@ IO::deliver_output (BufferSet& bufs, jack_nframes_t start_frame, jack_nframes_t return; } - const DataType type = DataType::MIDI; // type type type type... + const DataType type = DataType::MIDI; // Just dump any MIDI 1-to-1, we're not at all clever with MIDI routing yet BufferSet::iterator o = output_buffers().begin(type); - for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i) { - - for (PortSet::iterator i = _inputs.begin(type); i != _inputs.end(type); ++i, ++o) { - o->read_from(i->get_buffer(), nframes, offset); - } + for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i, ++o) { + o->read_from(*i, nframes, offset); } } 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; + } +} diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc index 582df6f5be..051203e55e 100644 --- a/libs/ardour/midi_playlist.cc +++ b/libs/ardour/midi_playlist.cc @@ -30,6 +30,7 @@ #include <ardour/midi_playlist.h> #include <ardour/midi_region.h> #include <ardour/session.h> +#include <ardour/midi_ring_buffer.h> #include <pbd/error.h> @@ -118,8 +119,8 @@ MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, string name, bool hidden) } } -MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, jack_nframes_t start, jack_nframes_t cnt, string name, bool hidden) - : Playlist (other, start, cnt, name, hidden) +MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, jack_nframes_t start, jack_nframes_t dur, string name, bool hidden) + : Playlist (other, start, dur, name, hidden) { save_state (_("initial state")); @@ -168,10 +169,10 @@ struct RegionSortByLayer } }; -/** FIXME: semantics of return value? */ +/** Returns the number of frames in time duration read (eg could be large when 0 events are read) */ jack_nframes_t -MidiPlaylist::read (MidiBuffer& buf, jack_nframes_t start, - jack_nframes_t cnt, unsigned chan_n) +MidiPlaylist::read (MidiRingBuffer& dst, jack_nframes_t start, + jack_nframes_t dur, unsigned chan_n) { /* this function is never called from a realtime thread, so its OK to block (for short intervals). @@ -180,11 +181,11 @@ MidiPlaylist::read (MidiBuffer& buf, jack_nframes_t start, Glib::Mutex::Lock rm (region_lock); jack_nframes_t ret = 0; - jack_nframes_t end = start + cnt - 1; - jack_nframes_t read_frames = 0; - jack_nframes_t skip_frames = 0; + jack_nframes_t end = start + dur - 1; + //jack_nframes_t read_frames = 0; + //jack_nframes_t skip_frames = 0; - _read_data_count = 0; + //_read_data_count = 0; vector<MidiRegion*> regs; // relevent regions overlapping start <--> end @@ -199,13 +200,15 @@ MidiPlaylist::read (MidiBuffer& buf, jack_nframes_t start, sort(regs.begin(), regs.end(), layer_cmp); for (vector<MidiRegion*>::iterator i = regs.begin(); i != regs.end(); ++i) { - (*i)->read_at (buf, start, cnt, chan_n, read_frames, skip_frames); - ret += (*i)->read_data_count(); + // FIXME: ensure time is monotonic here + (*i)->read_at (dst, start, dur, chan_n, 0, 0);// FIXME read_frames, skip_frames); + ret += (*i)->read_data_count(); } _read_data_count += ret; - return ret; + //return ret; FIXME? + return dur; } @@ -386,8 +389,7 @@ MidiPlaylist::drop_all_states () /* delete every region that is left - these are all things that are part of our "history" */ - for (set - <Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) { + for (set<Region *>::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) { (*ar)->unlock_sources (); delete *ar; } @@ -587,6 +589,8 @@ MidiPlaylist::region_changed (Change what_changed, Region* region) return false; } + // Feeling rather uninterested today, but thanks for the heads up anyway! + Change our_interests = Change (/*MidiRegion::FadeInChanged| MidiRegion::FadeOutChanged| MidiRegion::FadeInActiveChanged| diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index 83322dedb6..f4d1912061 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -26,7 +26,7 @@ using namespace std; MidiPort::MidiPort(jack_port_t* p) : Port(p) - , _buffer(NULL) + , _buffer(4096) // FIXME FIXME FIXME Jack needs to tell us this , _nframes_this_cycle(0) { DataType dt(_type); @@ -34,22 +34,25 @@ MidiPort::MidiPort(jack_port_t* p) reset(); - _buffer = new MidiBuffer(4096); // FIXME FIXME FIXME + } MidiPort::~MidiPort() { - delete _buffer; } void MidiPort::cycle_start (jack_nframes_t nframes) { + _buffer.clear(); + assert(_buffer.size() == 0); + _nframes_this_cycle = nframes; if (_flags & JackPortIsOutput) { - _buffer->set_size(0); + _buffer.silence(nframes); + assert(_buffer.size() == 0); return; } @@ -60,21 +63,26 @@ MidiPort::cycle_start (jack_nframes_t nframes) const jack_nframes_t event_count = jack_midi_port_get_info(jack_buffer, nframes)->event_count; - assert(event_count < _buffer->capacity()); + assert(event_count < _buffer.capacity()); + MidiEvent ev; + + // FIXME: too slow, event struct is copied twice (here and MidiBuffer::push_back) for (jack_nframes_t i=0; i < event_count; ++i) { - jack_midi_event_t* const ev = &_buffer->data()[i]; - jack_midi_event_get(ev, jack_buffer, i, nframes); + // This will fail to compile if we change MidiEvent to our own class + jack_midi_event_get(static_cast<jack_midi_event_t*>(&ev), jack_buffer, i, nframes); + + _buffer.push_back(ev); // Convert note ons with velocity 0 to proper note offs // FIXME: Jack MIDI should guarantee this - does it? //if (ev->buffer[0] == MIDI_CMD_NOTE_ON && ev->buffer[2] == 0) // ev->buffer[0] = MIDI_CMD_NOTE_OFF; } - _buffer->set_size(event_count); - - //if (_buffer->size() > 0) + assert(_buffer.size() == event_count); + + //if (_buffer.size() > 0) // cerr << "MIDIPort got " << event_count << " events." << endl; } @@ -82,7 +90,7 @@ void MidiPort::cycle_end() { if (_flags & JackPortIsInput) { - _nframes_this_cycle = 0; // catch any oopses + _nframes_this_cycle = 0; return; } @@ -90,13 +98,17 @@ MidiPort::cycle_end() void* jack_buffer = jack_port_get_buffer(_port, _nframes_this_cycle); - const jack_nframes_t event_count = _buffer->size(); + const jack_nframes_t event_count = _buffer.size(); + + //if (event_count > 0) + // cerr << "MIDIPort writing " << event_count << " events." << endl; jack_midi_clear_buffer(jack_buffer, _nframes_this_cycle); for (jack_nframes_t i=0; i < event_count; ++i) { - const jack_midi_event_t& ev = _buffer->data()[i]; + const jack_midi_event_t& ev = _buffer[i]; + assert(ev.time < _nframes_this_cycle); jack_midi_event_write(jack_buffer, ev.time, ev.buffer, ev.size, _nframes_this_cycle); } - _nframes_this_cycle = 0; // catch oopses + _nframes_this_cycle = 0; } diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc index 84b03b9aa9..9098bf6f40 100644 --- a/libs/ardour/midi_region.cc +++ b/libs/ardour/midi_region.cc @@ -39,6 +39,7 @@ #include <ardour/playlist.h> #include <ardour/midi_source.h> #include <ardour/types.h> +#include <ardour/midi_ring_buffer.h> #include "i18n.h" #include <locale.h> @@ -178,25 +179,51 @@ MidiRegion::get_memento() const } jack_nframes_t -MidiRegion::read_at (MidiBuffer& out, jack_nframes_t position, - jack_nframes_t cnt, +MidiRegion::read_at (MidiRingBuffer& out, jack_nframes_t position, + jack_nframes_t dur, uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const { - return _read_at (_sources, out, position, cnt, chan_n, read_frames, skip_frames); + return _read_at (_sources, out, position, dur, chan_n, read_frames, skip_frames); } jack_nframes_t -MidiRegion::master_read_at (MidiBuffer& out, jack_nframes_t position, - jack_nframes_t cnt, uint32_t chan_n) const +MidiRegion::master_read_at (MidiRingBuffer& out, jack_nframes_t position, + jack_nframes_t dur, uint32_t chan_n) const { - return _read_at (_master_sources, out, position, cnt, chan_n, 0, 0); + return _read_at (_master_sources, out, position, dur, chan_n, 0, 0); } jack_nframes_t -MidiRegion::_read_at (const SourceList& srcs, MidiBuffer& buf, - jack_nframes_t position, jack_nframes_t cnt, +MidiRegion::_read_at (const SourceList& srcs, MidiRingBuffer& dst, + jack_nframes_t position, jack_nframes_t dur, uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const { + MidiEvent ev; + RawMidi data[4]; + + const char note = rand()%30 + 30; + + ev.buffer = data; + ev.time = position; + ev.size = 3; + data[0] = 0x90; + data[1] = note; + data[2] = 120; + dst.write(ev); + + ev.buffer = data; + ev.time = (jack_nframes_t)(position + (9/10.0 * dur)); + assert(ev.time < position + dur); + ev.size = 3; + data[0] = 0x80; + data[1] = note; + data[2] = 64; + dst.write(ev); + + _read_data_count += dur; + + return dur; +#if 0 jack_nframes_t internal_offset; jack_nframes_t buf_offset; jack_nframes_t to_read; @@ -240,6 +267,7 @@ MidiRegion::_read_at (const SourceList& srcs, MidiBuffer& buf, _read_data_count += src.read_data_count(); return to_read; +#endif } XMLNode& diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 470759f3ac..91aad5ea80 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -32,6 +32,7 @@ #include <ardour/midi_playlist.h> #include <ardour/panner.h> #include <ardour/utils.h> +#include <ardour/buffer_set.h> #include "i18n.h" @@ -354,23 +355,18 @@ MidiTrack::n_process_buffers () return max (_diskstream->n_channels(), redirect_max_outs); } -void -MidiTrack::passthru_silence (jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t nframes, jack_nframes_t offset, int declick, bool meter) -{ - process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, offset, true, declick, meter); -} - int MidiTrack::no_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, bool session_state_changing, bool can_record, bool rec_monitors_input) { if (n_outputs().get(DataType::MIDI) == 0) { - return 0; + //return 0; + throw; // FIXME } if (!_active) { silence (nframes, offset); - return 0; + //return 0; // FIXME } if (session_state_changing) { @@ -444,13 +440,9 @@ int MidiTrack::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, int declick, bool can_record, bool rec_monitors_input) { - //passthru (start_frame, end_frame, nframes, offset, declick, false); int dret; - RawMidi* b; // FIXME: this won't work, duh - //Sample* tmpb; - jack_nframes_t transport_frame; MidiDiskstream& diskstream = midi_diskstream(); - + { Glib::RWLock::ReaderLock lm (redirect_lock, Glib::TRY_LOCK); if (lm.locked()) { @@ -459,7 +451,7 @@ MidiTrack::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframe automation_snapshot (start_frame); } } - + if (n_outputs().get_total() == 0 && _redirects.empty()) { return 0; } @@ -469,28 +461,27 @@ MidiTrack::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframe return 0; } - transport_frame = _session.transport_frame(); + jack_nframes_t transport_frame = _session.transport_frame(); if ((nframes = check_initial_delay (nframes, offset, transport_frame)) == 0) { /* need to do this so that the diskstream sets its playback distance to zero, thus causing diskstream::commit to do nothing. - */ + */ return diskstream.process (transport_frame, 0, 0, can_record, rec_monitors_input); } _silent = false; - //apply_gain_automation = false; if ((dret = diskstream.process (transport_frame, nframes, offset, can_record, rec_monitors_input)) != 0) { - + silence (nframes, offset); return dret; } /* special condition applies */ - + if (_meter_point == MeterInput) { just_meter_input (start_frame, end_frame, nframes, offset); } @@ -499,53 +490,30 @@ MidiTrack::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframe /* not actually recording, but we want to hear the input material anyway, at least potentially (depending on monitoring options) - */ + */ passthru (start_frame, end_frame, nframes, offset, 0, true); - } else if ((b = diskstream.playback_buffer()) != 0) { + } else { /* - XXX is it true that the earlier test on n_outputs() - means that we can avoid checking it again here? i think - so, because changing the i/o configuration of an IO - requires holding the AudioEngine lock, which we hold - while in the process() tree. - */ + XXX is it true that the earlier test on n_outputs() + means that we can avoid checking it again here? i think + so, because changing the i/o configuration of an IO + requires holding the AudioEngine lock, which we hold + while in the process() tree. + */ + - /* copy the diskstream data to all output buffers */ - + //const size_t limit = n_process_buffers().get(DataType::AUDIO); BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers()); - - //uint32_t n; - //uint32_t i; -#if 0 - for (i = 0, n = 1; i < limit; ++i, ++n) { - memcpy (bufs.get_audio(i).data(nframes), b, sizeof (Sample) * nframes); - if (n < diskstream.n_channels().get(DataType::AUDIO)) { - tmpb = diskstream.playback_buffer(n); - if (tmpb!=0) { - b = tmpb; - } - } - } - /* don't waste time with automation if we're recording or we've just stopped (yes it can happen) */ + diskstream.get_playback(bufs.get_midi(0), start_frame, end_frame); - if (!diskstream.record_enabled() && _session.transport_rolling()) { - Glib::Mutex::Lock am (automation_lock, Glib::TRY_LOCK); - - if (am.locked() && gain_automation_playback()) { - apply_gain_automation = _gain_automation_curve.rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes); - } - } -#endif - process_output_buffers (bufs, start_frame, end_frame, nframes, offset, (!_session.get_record_enabled() || !_session.get_do_not_record_plugins()), declick, (_meter_point != MeterInput)); - - } else { - /* problem with the diskstream; just be quiet for a bit */ - silence (nframes, offset); + process_output_buffers (bufs, start_frame, end_frame, nframes, offset, + (!_session.get_record_enabled() || !_session.get_do_not_record_plugins()), declick, (_meter_point != MeterInput)); + } return 0; @@ -584,7 +552,6 @@ MidiTrack::process_output_buffers (BufferSet& bufs, // Main output stage is the only stage we've got. // I think it's a pretty good stage though, wouldn't you say? - if (muted()) { IO::silence(nframes, offset); diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 757c40452f..2ea6c314c1 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -680,6 +680,12 @@ Route::passthru (jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nfra } void +Route::passthru_silence (jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t nframes, jack_nframes_t offset, int declick, bool meter) +{ + process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, offset, true, declick, meter); +} + +void Route::set_solo (bool yn, void *src) { if (_solo_safe) { diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index aa2f12aa65..28816541f1 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -224,7 +224,7 @@ Track::set_name (string str, void *src) if ((ret = IO::set_name (str, src)) == 0) { _session.save_state (""); - _session.save_history (""); + _session.save_history (""); } return ret; } |