void cycle_start (nframes_t nframes, nframes_t offset);
void cycle_end (nframes_t nframes, nframes_t offset);
+ AudioBuffer& get_audio_buffer( nframes_t nframes, nframes_t offset );
+
protected:
friend class AudioEngine;
AudioPort (const std::string&, Flags, bool external, nframes_t);
+ private:
+ bool _has_been_mixed_down;
};
} // namespace ARDOUR
DataType type() const { return DataType::AUDIO; }
- virtual Buffer& get_buffer () {
- assert (_buffer);
- return *_buffer;
+ virtual Buffer& get_buffer ( nframes_t nframes, nframes_t offset ) {
+ return get_audio_buffer( nframes, offset);
}
- virtual AudioBuffer& get_audio_buffer() {
- assert (_buffer);
- return *_buffer;
- }
+ virtual AudioBuffer& get_audio_buffer (nframes_t nframes, nframes_t offset) = 0;
void reset ();
DataType type() const { return DataType::MIDI; }
- Buffer& get_buffer() {
- assert (_buffer);
- return *_buffer;
+ Buffer& get_buffer( nframes_t nframes, nframes_t offset ) {
+ return get_midi_buffer( nframes, offset );
}
- MidiBuffer& get_midi_buffer() {
- assert (_buffer);
- return *_buffer;
- }
+ virtual MidiBuffer& get_midi_buffer (nframes_t nframes, nframes_t offset ) = 0;
size_t capacity() { return _buffer->capacity(); }
size_t size() { return _buffer->size(); }
bool _public_ports;
virtual void prepare_inputs (nframes_t nframes, nframes_t offset);
+ virtual void flush_outputs (nframes_t nframes, nframes_t offset);
virtual void set_deferred_state() {}
class AudioEngine;
class JackAudioPort : public JackPort, public BaseAudioPort {
public:
- void cycle_start (nframes_t nframes, nframes_t offset) {
- _buffer->set_data ((Sample*) jack_port_get_buffer (_port, nframes) + offset, nframes);
- }
+ void cycle_end (nframes_t nframes, nframes_t offset);
+ void cycle_start (nframes_t nframes, nframes_t offset);
int reestablish ();
+ AudioBuffer& get_audio_buffer( nframes_t nframes, nframes_t offset );
+
protected:
friend class AudioPort;
JackAudioPort (const std::string& name, Flags flags, AudioBuffer* buf);
AudioBuffer* _source_buffer;
+ private:
+ bool _has_been_mixed_down;
};
} // namespace ARDOUR
public:
void cycle_start (nframes_t nframes, nframes_t offset);
void cycle_end (nframes_t nframes, nframes_t offset);
+ void flush_buffers (nframes_t nframes, nframes_t offset);
void set_buffer (MidiBuffer& buf);
+ MidiBuffer& get_midi_buffer( nframes_t nframes, nframes_t offset );
+
protected:
friend class MidiPort;
JackMidiPort (const std::string&, Flags, MidiBuffer*);
+ private:
+ bool _has_been_mixed_down;
};
} // namespace ARDOUR
void resize(size_t);
bool merge(const MidiBuffer& a, const MidiBuffer& b);
+ bool merge_in_place( const MidiBuffer &other );
struct iterator {
iterator(MidiBuffer& b, size_t i) : buffer(b), index(i) {}
void cycle_start (nframes_t nframes, nframes_t offset);
void cycle_end (nframes_t nframes, nframes_t offset);
+ void flush_buffers (nframes_t nframes, nframes_t offset);
+
+ MidiBuffer& get_midi_buffer( nframes_t nframes, nframes_t offset );
protected:
friend class AudioEngine;
MidiPort (const std::string& name, Flags, bool external, nframes_t bufsize);
+ private:
+ bool _has_been_mixed_down;
};
} // namespace ARDOUR
virtual void cycle_start (nframes_t nframes, nframes_t offset) {}
virtual void cycle_end (nframes_t nframes, nframes_t offset) {}
+ virtual void flush_buffers (nframes_t nframes, nframes_t offset ) {}
virtual DataType type() const = 0;
- virtual Buffer& get_buffer() = 0;
+ virtual Buffer& get_buffer( nframes_t nframes, nframes_t offset ) = 0;
virtual bool connected () const;
virtual bool connected_to (const std::string& portname) const;
AudioPort* const ap = _io->audio_input(n);
assert(ap);
- assert(rec_nframes <= ap->get_audio_buffer().capacity());
- memcpy (chaninfo->current_capture_buffer, ap->get_audio_buffer().data(rec_nframes, offset + rec_offset), sizeof (Sample) * rec_nframes);
+ assert(rec_nframes <= ap->get_audio_buffer( nframes, offset ).capacity());
+ memcpy (chaninfo->current_capture_buffer, ap->get_audio_buffer( nframes, offset ).data(rec_nframes, offset + rec_offset), sizeof (Sample) * rec_nframes);
} else {
AudioPort* const ap = _io->audio_input(n);
assert(ap);
- Sample* buf = ap->get_audio_buffer().data(nframes, offset);
+ Sample* buf = ap->get_audio_buffer( nframes, offset ).data(nframes, offset);
nframes_t first = chaninfo->capture_vector.len[0];
memcpy (chaninfo->capture_wrap_buffer, buf, sizeof (Sample) * first);
: Port (name, flags)
, BaseAudioPort (name, flags)
, PortFacade (name, flags)
+ , _has_been_mixed_down( false )
{
if (!external || receives_input()) {
_ext_port = new JackAudioPort (name, flags, 0);
- if (sends_output()) {
- _buffer = &dynamic_cast<JackAudioPort*>(_ext_port)->get_audio_buffer();
- }
+ //if (sends_output()) {
+ // _buffer = &dynamic_cast<JackAudioPort*>(_ext_port)->get_audio_buffer( nframes, offset );
+ //}
Port::set_name (_ext_port->name());
}
if (_ext_port) {
_ext_port->cycle_start (nframes, offset);
}
+ _has_been_mixed_down = false;
+}
+
+AudioBuffer &
+AudioPort::get_audio_buffer( nframes_t nframes, nframes_t offset ) {
+
+ if (_has_been_mixed_down)
+ return *_buffer;
if (_flags & IsInput) {
if (_ext_port) {
- _buffer->read_from (dynamic_cast<BaseAudioPort*>(_ext_port)->get_audio_buffer(), nframes, offset);
+ _buffer->read_from (dynamic_cast<BaseAudioPort*>(_ext_port)->get_audio_buffer (nframes, offset), nframes, offset);
if (!_connections.empty()) {
(*_mixdown) (_connections, _buffer, nframes, offset, false);
// XXX if we could get the output stage to not purely mix into, but also
// to initially overwrite the buffer, we could avoid this silence step.
-
- _buffer->silence (nframes, offset);
+ if (_ext_port) {
+ _buffer = & (dynamic_cast<BaseAudioPort*>(_ext_port)->get_audio_buffer( nframes, offset ));
+ }
+ if (nframes)
+ _buffer->silence (nframes, offset);
}
+ if (nframes)
+ _has_been_mixed_down = true;
+
+ return *_buffer;
}
void
AudioPort::cycle_end (nframes_t nframes, nframes_t offset)
{
+ _has_been_mixed_down=false;
}
transport_frame = _session.transport_frame();
+ prepare_inputs( nframes, offset );
+
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
which requires interleaving with route processing.
*/
- if ((*i)->sends_output()) {
- (*i)->cycle_start (nframes, 0);
- }
+ (*i)->cycle_start (nframes, 0);
}
if (_freewheeling) {
}
}
- // Finalize ports (ie write data if necessary)
-
- for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- (*i)->cycle_end (nframes, 0);
- }
-
if (_freewheeling) {
return 0;
}
Port *port = (*i);
if (port->sends_output()) {
- port->get_buffer().silence(nframes);
+ port->get_buffer(nframes, 0 ).silence(nframes);
}
}
}
+ // Finalize ports (ie write data if necessary)
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+
+ (*i)->cycle_end (nframes, 0);
+ }
+
_processed_frames = next_processed_frames;
return 0;
}
boost::shared_ptr<Ports> p = ports.reader();
for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
- if ((*i)->sends_output()) {
- (*i)->cycle_start (blocksize, 0);
- }
+ (*i)->cycle_start (blocksize, 0);
}
s->process (blocksize);
set<Port*>::const_iterator p = ports.begin();
if (first_overwrite) {
- dest->read_from ((dynamic_cast<BaseAudioPort*>(*p))->get_audio_buffer(), cnt, offset);
+ dest->read_from ((dynamic_cast<BaseAudioPort*>(*p))->get_audio_buffer( cnt, offset ), cnt, offset);
p++;
}
for (; p != ports.end(); ++p) {
- dest->accumulate_from ((dynamic_cast<BaseAudioPort*>(*p))->get_audio_buffer(), cnt, offset);
+ dest->accumulate_from ((dynamic_cast<BaseAudioPort*>(*p))->get_audio_buffer( cnt, offset ), cnt, offset);
}
}
if (first_overwrite) {
cout << "first overwrite" << endl;
- dest->read_from ((dynamic_cast<BaseMidiPort*>(*p))->get_midi_buffer(), cnt, offset);
+ dest->read_from ((dynamic_cast<BaseMidiPort*>(*p))->get_midi_buffer(cnt, offset), cnt, offset);
p++;
}
// XXX DAVE: this is just a guess
for (; p != ports.end(); ++p) {
- cout << "merge" << endl;
- dest->merge (*dest, (dynamic_cast<BaseMidiPort*>(*p))->get_midi_buffer());
+ //cout << "merge" << endl;
+ dest->merge (*dest, (dynamic_cast<BaseMidiPort*>(*p))->get_midi_buffer(cnt, offset));
}
}
for (PortSet::iterator p = ports.begin(*t); p != ports.end(*t); ++p) {
assert(p->type() == *t);
- v.push_back(&(p->get_buffer()));
+ v.push_back(&(p->get_buffer(0,0)));
}
}
for (iterator it = begin(); it != end(); ++it) {
if (*it != 0) {
- Sample* port_buffer = (*it)->get_audio_buffer().data();
+ Sample* port_buffer = (*it)->get_audio_buffer( frames, 0).data();
for (uint32_t i = 0; i < frames; ++i) {
data[i] += (float) port_buffer[i];
/* io_lock, not taken: function must be called from Session::process() calltree */
for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
- i->get_buffer().silence (nframes, offset);
+ i->get_buffer(nframes,offset).silence (nframes, offset);
}
}
BufferSet::iterator o = outs.begin(*t);
for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) {
- (*i).cycle_start (nframes, offset);
- o->read_from(i->get_buffer(), nframes, offset);
+ o->read_from(i->get_buffer(nframes,offset), nframes, offset);
}
}
IO::prepare_inputs (nframes_t nframes, nframes_t offset)
{
/* io_lock, not taken: function must be called from Session::process() calltree */
+}
- for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
- (*i).cycle_start (nframes, offset);
+void
+IO::flush_outputs (nframes_t nframes, nframes_t offset)
+{
+ /* io_lock, not taken: function must be called from Session::process() calltree */
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+
+ /* Only run cycle_start() on output ports, because
+ inputs must be done in the correct processing order,
+ which requires interleaving with route processing.
+ */
+
+ (*i).flush_buffers (nframes, offset);
}
+
}
: Port (name, flgs)
, JackPort (name, DataType::AUDIO, flgs)
, BaseAudioPort (name, flgs)
+ , _has_been_mixed_down( false )
{
if (buf) {
return ret;
}
+AudioBuffer&
+JackAudioPort::get_audio_buffer (nframes_t nframes, nframes_t offset) {
+ assert (_buffer);
+
+ if (_has_been_mixed_down)
+ return *_buffer;
+
+ if( _flags & IsInput )
+ _buffer->set_data ((Sample*) jack_port_get_buffer (_port, nframes), nframes+offset);
+
+
+ if (nframes)
+ _has_been_mixed_down = true;
+
+ return *_buffer;
+}
+
+void
+JackAudioPort::cycle_start (nframes_t nframes, nframes_t offset) {
+ if( _flags & IsOutput )
+ _buffer->set_data ((Sample*) jack_port_get_buffer (_port, nframes), nframes+offset);
+}
+void
+JackAudioPort::cycle_end (nframes_t nframes, nframes_t offset) {
+ _has_been_mixed_down=false;
+}
: Port (name, flgs)
, JackPort (name, DataType::MIDI, flgs)
, BaseMidiPort (name, flgs)
+ , _has_been_mixed_down (false)
{
// MIDI ports always need a buffer since jack buffer format is different
assert(buf);
_buffer->clear();
assert(_buffer->size() == 0);
- if (_flags & IsOutput) {
+ if (_flags & IsInput) {
return;
}
+ // We're an output - delete the midi_events.
+
+ void* jack_buffer = jack_port_get_buffer (_port, nframes);
+
+ jack_midi_clear_buffer (jack_buffer);
+}
+
+MidiBuffer &
+JackMidiPort::get_midi_buffer( nframes_t nframes, nframes_t offset ) {
+
+ if (_has_been_mixed_down)
+ return *_buffer;
+
+ if (_flags & IsOutput) {
+ return *_buffer;
+ }
+
// We're an input - copy Jack events to internal buffer
void* jack_buffer = jack_port_get_buffer(_port, nframes);
jack_midi_event_get (&ev, jack_buffer, i);
- _buffer->push_back (ev);
+ // i guess this should do but i leave it off to test the rest first.
+ //if (ev.time > offset && ev.time < offset+nframes)
+ _buffer->push_back (ev);
}
assert(_buffer->size() == event_count);
/*if (_buffer->size() > 0)
cerr << "JackMIDIPort got " << event_count << " events (buf " << _buffer << ")" << endl;*/
+ if (nframes)
+ _has_been_mixed_down = true;
+
+ return *_buffer;
}
void
{
/* FIXME: offset */
+ _has_been_mixed_down = false;
+
+#if 0
if (_flags & IsInput) {
return;
}
jack_midi_clear_buffer (jack_buffer);
+ for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) {
+ const Evoral::Event& ev = *i;
+ // event times should be frames, relative to cycle start
+ assert(ev.time() >= 0);
+ assert(ev.time() < nframes);
+ jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size());
+ }
+#endif
+}
+
+void
+JackMidiPort::flush_buffers (nframes_t nframes, nframes_t offset)
+{
+ /* FIXME: offset */
+
+ if (_flags & IsInput) {
+ return;
+ }
+
+ void* jack_buffer = jack_port_get_buffer (_port, nframes);
+
for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) {
const Evoral::Event& ev = *i;
// event times should be frames, relative to cycle start
_silent = true;
}
+bool
+MidiBuffer::merge_in_place( const MidiBuffer &other )
+{
+ if( other.size() == 0 )
+ return true;
+
+ if( this->size() == 0 ) {
+ copy( other );
+ return true;
+ }
+
+ {
+ MidiBuffer merge_buffer( 0 );
+ Evoral::MIDIEvent onstack_events[_capacity];
+ uint8_t onstack_data[_capacity * MAX_EVENT_SIZE];
+ merge_buffer._events = onstack_events;
+ merge_buffer._data = onstack_data;
+ merge_buffer._size = 0;
+
+ bool retval = merge_buffer.merge( *this, other );
+
+ copy( merge_buffer );
+
+ // set pointers to zero again, so destructor
+ // does not end in calling free() for memory
+ // on the stack;
+ merge_buffer._events = 0;
+ merge_buffer._data = 0;
+
+ return retval;
+ }
+}
/** Clear, and merge \a a and \a b into this buffer.
*
{
_size = 0;
- // Die if a merge isn't necessary as it's expensive
- assert(a.size() > 0 && b.size() > 0);
+ // This is mostly the case :(
+ if( this == &a )
+ merge_in_place( b );
+
+ if( this == &b )
+ merge_in_place( a );
size_t a_index = 0;
size_t b_index = 0;
size_t count = a.size() + b.size();
- while (count > 0 && a_index < a.size() && b_index < b.size()) {
+ while (count > 0) {
if (size() == capacity()) {
cerr << "WARNING: MIDI buffer overrun, events lost!" << endl;
}
if (a_index == a.size()) {
- push_back(a[a_index]);
- ++a_index;
- } else if (b_index == b.size()) {
push_back(b[b_index]);
++b_index;
+ } else if (b_index == b.size()) {
+ push_back(a[a_index]);
+ ++a_index;
} else {
const Evoral::MIDIEvent& a_ev = a[a_index];
const Evoral::MIDIEvent& b_ev = b[b_index];
// 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 num_events = _source_port->get_midi_buffer( nframes, offset ).size();
size_t to_write = std::min(_capture_buf->write_space(), num_events);
- MidiBuffer::iterator port_iter = _source_port->get_midi_buffer().begin();
+ MidiBuffer::iterator port_iter = _source_port->get_midi_buffer( nframes, offset ).begin();
for (size_t i=0; i < to_write; ++i) {
const Evoral::MIDIEvent& ev = *port_iter;
: Port (name, flags)
, BaseMidiPort (name, flags)
, PortFacade (name, flags)
+ , _has_been_mixed_down (false)
{
// FIXME: size kludge (see BufferSet::ensure_buffers)
// Jack needs to tell us this
if (_ext_port) {
_ext_port->cycle_start (nframes, offset);
}
+}
+
+MidiBuffer &
+MidiPort::get_midi_buffer( nframes_t nframes, nframes_t offset ) {
+ if (_has_been_mixed_down)
+ return *_buffer;
+
if (_flags & IsInput) {
if (_ext_port) {
BaseMidiPort* mprt = dynamic_cast<BaseMidiPort*>(_ext_port);
assert(mprt);
- assert(&mprt->get_midi_buffer() == _buffer);
+ assert(&mprt->get_midi_buffer(nframes,offset) == _buffer);
if (!_connections.empty()) {
(*_mixdown) (_connections, _buffer, nframes, offset, false);
}
} else {
-
_buffer->silence (nframes, offset);
}
+ if (nframes)
+ _has_been_mixed_down = true;
+
+ return *_buffer;
}
if (_ext_port) {
_ext_port->cycle_end (nframes, offset);
}
+ _has_been_mixed_down = false;
}
+void
+MidiPort::flush_buffers (nframes_t nframes, nframes_t offset)
+{
+ if (_ext_port) {
+ _ext_port->flush_buffers (nframes, offset);
+ }
+}
}
MidiTrack::MidiTrack (Session& sess, const XMLNode& node)
- : Track (sess, node)
+ : Track (sess, node, DataType::MIDI )
, _immediate_events(1024) // FIXME: size?
, _note_mode(Sustained)
{
int dret;
boost::shared_ptr<MidiDiskstream> diskstream = midi_diskstream();
+ // I guess this is the right place to call cycle_start for our ports.
+ // but it actually sucks, to directly mess with the IO.... oh well.
+
+ prepare_inputs( nframes, offset );
+
{
Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
if (lm.locked()) {
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
}
+ flush_outputs( nframes, offset );
+
return 0;
}
XMLNodeConstIterator niter;
const XMLProperty *prop;
uint32_t i;
+ uint32_t num_panners = 0;
StreamPanner* sp;
LocaleGuard lg (X_("POSIX"));
set_bypassed (prop->value() == "yes");
}
- if ((prop = node.property (X_("ins"))) != 0) {
- ins.set_audio(atoi(prop->value().c_str()));
- }
-
- if ((prop = node.property (X_("outs"))) != 0) {
- outs.set_audio(atoi(prop->value().c_str()));
- }
-
-
if ((prop = node.property (X_("link_direction"))) != 0) {
LinkDirection ld; /* here to provide type information */
set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
assumption, but its still an assumption.
*/
- sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, i));
+ sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners));
+ num_panners++;
if (sp->set_state (**niter) == 0) {
_streampanners.push_back (sp);
}
}
- reset(ins.n_audio(), outs.n_audio());
+ reset(num_panners, outputs.size());
/* don't try to do old-school automation loading if it wasn't marked as existing */
if ((prop = node.property (X_("automation")))) {
void
Session::process (nframes_t nframes)
{
+ // This is no more the appriopriate place to call cycle
+ // start. cycle_start needs to be called at the Route::roll()
+ // where the signals which we want to mixdown have been calculated.
+ //
MIDI::Manager::instance()->cycle_start(nframes);
_silent = false;