diff options
author | David Robillard <d@drobilla.net> | 2009-02-01 21:04:12 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2009-02-01 21:04:12 +0000 |
commit | 44ac5f5097af43ec6bf60489d73a532f43702bf3 (patch) | |
tree | 03e859a131be8e9187f5a95fb7eba813f9d761eb /libs/ardour | |
parent | 554d46896bf60fdf0804225acb26a9fd79e592c4 (diff) |
Removed fixed/maximum event size assumption/limitation from MIDI buffer.
git-svn-id: svn://localhost/ardour2/branches/3.0@4471 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour')
-rw-r--r-- | libs/ardour/ardour/midi_buffer.h | 42 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_port.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/midi_state_tracker.h | 2 | ||||
-rw-r--r-- | libs/ardour/meter.cc | 2 | ||||
-rw-r--r-- | libs/ardour/midi_buffer.cc | 169 | ||||
-rw-r--r-- | libs/ardour/midi_diskstream.cc | 14 | ||||
-rw-r--r-- | libs/ardour/midi_port.cc | 11 | ||||
-rw-r--r-- | libs/ardour/midi_ring_buffer.cc | 5 | ||||
-rw-r--r-- | libs/ardour/midi_state_tracker.cc | 7 | ||||
-rw-r--r-- | libs/ardour/midi_track.cc | 69 | ||||
-rw-r--r-- | libs/ardour/smf_source.cc | 8 |
11 files changed, 86 insertions, 245 deletions
diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h index 2a368673a9..36610c61cb 100644 --- a/libs/ardour/ardour/midi_buffer.h +++ b/libs/ardour/ardour/midi_buffer.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2006 Paul Davis + Copyright (C) 2006-2009 Paul Davis Author: Dave Robillard This program is free software; you can redistribute it and/or modify it @@ -20,8 +20,10 @@ #ifndef __ardour_midi_buffer_h__ #define __ardour_midi_buffer_h__ +#include <evoral/midi_util.h> #include <midi++/event.h> #include <ardour/buffer.h> +#include <ardour/event_type_map.h> namespace ARDOUR { @@ -50,16 +52,23 @@ public: template<typename B, typename E> struct iterator_base { - iterator_base<B,E>(B& b, size_t i) : buffer(b), index(i) {} - - inline E& operator*() const { return buffer[index]; } - inline iterator_base<B,E>& operator++() { ++index; return *this; } // prefix + iterator_base<B,E>(B& b, size_t o) : buffer(b), offset(o) {} + inline E operator*() const { + uint8_t* ev_start = buffer._data + offset + sizeof(Evoral::EventTime); + return E(EventTypeMap::instance().midi_event_type(*ev_start), + *(Evoral::EventTime*)(buffer._data + offset), + Evoral::midi_event_size(*ev_start) + 1, ev_start); + } + inline iterator_base<B,E>& operator++() { + uint8_t* ev_start = buffer._data + offset + sizeof(Evoral::EventTime); + offset += sizeof(Evoral::EventTime) + Evoral::midi_event_size(*ev_start) + 1; + return *this; + } inline bool operator!=(const iterator_base<B,E>& other) const { - return index != other.index; + return (&buffer != &other.buffer) || (offset != other.offset); } - B& buffer; - size_t index; + size_t offset; }; typedef iterator_base<MidiBuffer, Evoral::MIDIEvent> iterator; @@ -72,24 +81,11 @@ public: const_iterator end() const { return const_iterator(*this, _size); } private: - friend class iterator_base<MidiBuffer, Evoral::MIDIEvent>; friend class iterator_base<const MidiBuffer, const Evoral::MIDIEvent>; - const Evoral::MIDIEvent& operator[](size_t i) const { assert(i < _size); return _events[i]; } - Evoral::MIDIEvent& operator[](size_t i) { assert(i < _size); return _events[i]; } - - // FIXME: Eliminate 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) - */ - - /* FIXME: this is utter crap. rewrite as a flat/packed buffer like MidiRingBuffer */ - - Evoral::MIDIEvent* _events; ///< Event structs that point to offsets in _data - uint8_t* _data; ///< MIDI, straight up. No time stamps. + size_t _size; ///< Size in bytes of used portion of _data + uint8_t* _data; ///< timestamp, event, timestamp, event, ... }; diff --git a/libs/ardour/ardour/midi_port.h b/libs/ardour/ardour/midi_port.h index a3c87ff702..34cffd7f3d 100644 --- a/libs/ardour/ardour/midi_port.h +++ b/libs/ardour/ardour/midi_port.h @@ -44,7 +44,7 @@ class MidiPort : public Port { return get_midi_buffer (nframes, offset); } - MidiBuffer& get_midi_buffer( nframes_t nframes, nframes_t offset ); + MidiBuffer& get_midi_buffer (nframes_t nframes, nframes_t offset); protected: friend class AudioEngine; diff --git a/libs/ardour/ardour/midi_state_tracker.h b/libs/ardour/ardour/midi_state_tracker.h index ff9fb71ccc..bd8bdb650a 100644 --- a/libs/ardour/ardour/midi_state_tracker.h +++ b/libs/ardour/ardour/midi_state_tracker.h @@ -40,7 +40,7 @@ public: void resolve_notes (MidiBuffer& buffer, nframes_t time); private: - void track_note_onoffs(Evoral::MIDIEvent& event); + void track_note_onoffs(const Evoral::MIDIEvent& event); std::bitset<128*16> _active_notes; }; diff --git a/libs/ardour/meter.cc b/libs/ardour/meter.cc index 3a2ac2e77b..4839223f40 100644 --- a/libs/ardour/meter.cc +++ b/libs/ardour/meter.cc @@ -50,7 +50,7 @@ PeakMeter::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_f // GUI needs a better MIDI meter, not much information can be // expressed through peaks alone for (MidiBuffer::iterator i = bufs.get_midi(n).begin(); i != bufs.get_midi(n).end(); ++i) { - const Evoral::MIDIEvent& ev = *i; + const Evoral::MIDIEvent ev(*i, false); if (ev.is_note_on()) { const float this_vel = log(ev.buffer()[2] / 127.0 * (M_E*M_E-M_E) + M_E) - 1.0; //printf("V %d -> %f\n", (int)((Byte)ev.buffer[2]), this_vel); diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index 51f7dd43e6..df4ee13558 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -33,7 +33,7 @@ using namespace ARDOUR; // FIXME: mirroring for MIDI buffers? MidiBuffer::MidiBuffer(size_t capacity) : Buffer(DataType::MIDI, capacity) - , _events(0) + , _size(0) , _data(0) { if (capacity) { @@ -44,12 +44,7 @@ MidiBuffer::MidiBuffer(size_t capacity) MidiBuffer::~MidiBuffer() { - if (_events) { - free(_events); - } - if (_data) { - free(_data); - } + free(_data); } void @@ -62,30 +57,24 @@ MidiBuffer::resize(size_t size) } free(_data); - free(_events); _size = 0; _capacity = size; #ifdef NO_POSIX_MEMALIGN - _events = (Evoral::MIDIEvent *) malloc(sizeof(Evoral::MIDIEvent) * _capacity); - _data = (uint8_t *) malloc(sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE); + _data = (uint8_t*)malloc(_capacity); #else - posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(Evoral::Event) * _capacity); - posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE); + posix_memalign((void**)&_data, CPU_CACHE_ALIGN, _capacity); #endif assert(_data); - assert(_events); } void MidiBuffer::copy(const MidiBuffer& copy) { - assert(_capacity >= copy._capacity); - _size = 0; - - for (size_t i = 0; i < copy.size(); ++i) - push_back(copy[i]); + assert(_capacity >= copy._size); + _size = copy._size; + memcpy(_data, copy._data, copy._size); } @@ -109,11 +98,11 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset) assert(_size == 0); } - // FIXME: slow - for (size_t i = 0; i < msrc.size(); ++i) { - const Evoral::MIDIEvent& ev = msrc[i]; - //cout << "MidiBuffer::read_from event type: " << int(ev.type()) - // << " time: " << ev.time() << " buffer size: " << _size << endl; + for (MidiBuffer::const_iterator i = msrc.begin(); i != msrc.end(); ++i) { + const Evoral::MIDIEvent ev(*i, false); + /*cout << this << " MidiBuffer::read_from event type: " << int(ev.type()) + << " time: " << ev.time() << " size: " << ev.size() + << " status: " << (int)*ev.buffer() << " buffer size: " << _size << endl;*/ if (ev.time() < offset) { //cout << "MidiBuffer::read_from skipped event before " << offset << endl; } else if (ev.time() < (nframes + offset)) { @@ -139,27 +128,19 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset) bool MidiBuffer::push_back(const Evoral::MIDIEvent& ev) { - if (_size == _capacity) { + const size_t stamp_size = sizeof(Evoral::EventTime); + if (_size + stamp_size + ev.size() >= _capacity) { cerr << "MidiBuffer::push_back failed (buffer is full)" << endl; return false; } - uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE); + uint8_t* const write_loc = _data + _size; + *((Evoral::EventTime*)write_loc) = ev.time(); + memcpy(write_loc + stamp_size, ev.buffer(), ev.size()); - memcpy(write_loc, ev.buffer(), ev.size()); - _events[_size] = ev; - _events[_size].set_buffer(ev.size(), write_loc, false); - - /*cerr << "MidiBuffer: pushed @ " << _events[_size].time() - << " size = " << _size << endl; - for (size_t i = 0; i < _events[_size].size(); ++i) { - printf("%X ", _events[_size].buffer()[i]); - } - printf("\n");*/ - - ++_size; + _size += stamp_size + ev.size(); _silent = false; - + return true; } @@ -174,27 +155,19 @@ MidiBuffer::push_back(const Evoral::MIDIEvent& ev) bool MidiBuffer::push_back(const jack_midi_event_t& ev) { - if (_size == _capacity) { + const size_t stamp_size = sizeof(Evoral::EventTime); + if (_size + stamp_size + ev.size >= _capacity) { cerr << "MidiBuffer::push_back failed (buffer is full)" << endl; return false; } - uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE); + uint8_t* const write_loc = _data + _size; + *((Evoral::EventTime*)write_loc) = ev.time; + memcpy(write_loc + stamp_size, ev.buffer, ev.size); - memcpy(write_loc, ev.buffer, ev.size); - _events[_size].time() = (double)ev.time; - _events[_size].set_buffer(ev.size, write_loc, false); - - /*cerr << "MidiBuffer: pushed @ " << _events[_size].time() - << " size = " << _size << endl; - for (size_t i = 0; i < _events[_size].size(); ++i) { - printf("%X ", _events[_size].buffer()[i]); - } - printf("\n");*/ - - ++_size; + _size += stamp_size + ev.size; _silent = false; - + return true; } @@ -207,27 +180,19 @@ MidiBuffer::push_back(const jack_midi_event_t& ev) * location, or the buffer will be corrupted and very nasty things will happen. */ uint8_t* -MidiBuffer::reserve(double time, size_t size) +MidiBuffer::reserve(Evoral::EventTime time, size_t size) { - if (size > MAX_EVENT_SIZE) { - cerr << "WARNING: Failed to reserve " << size << " bytes for event"; + const size_t stamp_size = sizeof(Evoral::EventTime); + if (_size + stamp_size + size >= _capacity) { return 0; } - if (_size == _capacity) { - return 0; - } - - uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE); - - _events[_size].time() = time; - _events[_size].set_buffer(size, write_loc, false); - ++_size; - - //cerr << "MidiBuffer: reserved, size = " << _size << endl; + uint8_t* const write_loc = _data + _size; + *((Evoral::EventTime*)write_loc) = time; + _size += stamp_size + size; _silent = false; - + return write_loc; } @@ -239,12 +204,11 @@ MidiBuffer::silence(nframes_t dur, nframes_t offset) if (offset != 0) cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl; - memset(_events, 0, sizeof(Evoral::Event) * _capacity); - memset(_data, 0, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE); _size = 0; _silent = true; } +/** Merge \a other into this buffer. Realtime safe. */ bool MidiBuffer::merge_in_place(const MidiBuffer &other) { @@ -252,37 +216,22 @@ MidiBuffer::merge_in_place(const MidiBuffer &other) return true; } - if (this->size() == 0) { + if (_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; + if (_size + other.size() > _capacity) { + cerr << "MidiBuffer::merge failed (no space)" << endl; + return false; } + + cerr << "FIXME: MIDI BUFFER IN-PLACE MERGE" << endl; + return true; } /** Clear, and merge \a a and \a b into this buffer. * - * FIXME: This is slow. - * * \return true if complete merge was successful */ bool @@ -297,40 +246,8 @@ MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& 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) { - - if (size() == capacity()) { - cerr << "WARNING: MIDI buffer overrun, events lost!" << endl; - return false; - } - - if (a_index == a.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]; - - if (a_ev.time() <= b_ev.time()) { - push_back(a_ev); - ++a_index; - } else { - push_back(b_ev); - ++b_index; - } - } - - --count; - } - + + cerr << "FIXME: MIDI BUFFER MERGE" << endl; return true; } diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index 959e687882..cd50fc950a 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -518,20 +518,12 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t if (nominally_recording || rec_nframes) { - assert(_source_port); - // 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( nframes, offset ).size(); - size_t to_write = std::min(_capture_buf->write_space(), num_events); - - 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; + MidiBuffer& buf = _source_port->get_midi_buffer(nframes, offset); + for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) { + const Evoral::MIDIEvent ev(*i, false); assert(ev.buffer()); _capture_buf->write(ev.time() + transport_frame, ev.type(), ev.size(), ev.buffer()); - ++port_iter; } } else { diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index 23fff7c976..c69dc7fc05 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -31,7 +31,7 @@ MidiPort::MidiPort (const std::string& name, Flags flags, bool ext, nframes_t ca { // FIXME: size kludge (see BufferSet::ensure_buffers) // Jack needs to tell us this - _buffer = new MidiBuffer (capacity * 8); + _buffer = new MidiBuffer (capacity * 32); } MidiPort::~MidiPort() @@ -54,8 +54,8 @@ MidiPort::cycle_start (nframes_t nframes, nframes_t offset) } MidiBuffer & -MidiPort::get_midi_buffer( nframes_t nframes, nframes_t offset ) { - +MidiPort::get_midi_buffer (nframes_t nframes, nframes_t offset) +{ if (_has_been_mixed_down) { return *_buffer; } @@ -80,8 +80,6 @@ MidiPort::get_midi_buffer( nframes_t nframes, nframes_t offset ) { _buffer->push_back (ev); } - assert(_buffer->size() == event_count); - if (nframes) { _has_been_mixed_down = true; } @@ -167,7 +165,7 @@ MidiPort::mixdown (nframes_t cnt, nframes_t offset, bool first_overwrite) if (first_overwrite) { _buffer->read_from ((dynamic_cast<MidiPort*>(*p))->get_midi_buffer (cnt, offset), cnt, offset); - p++; + ++p; } // XXX DAVE: this is just a guess @@ -176,3 +174,4 @@ MidiPort::mixdown (nframes_t cnt, nframes_t offset, bool first_overwrite) _buffer->merge (*_buffer, (dynamic_cast<MidiPort*>(*p))->get_midi_buffer (cnt, offset)); } } + diff --git a/libs/ardour/midi_ring_buffer.cc b/libs/ardour/midi_ring_buffer.cc index 6486889f44..e09406a683 100644 --- a/libs/ardour/midi_ring_buffer.cc +++ b/libs/ardour/midi_ring_buffer.cc @@ -33,7 +33,6 @@ size_t MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset) { if (read_space() == 0) { - //cerr << "MRB: NO READ SPACE" << endl; return 0; } @@ -100,7 +99,7 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t uint8_t* write_loc = dst.reserve(ev_time, ev_size); if (write_loc == NULL) { - //cerr << "MRB: Unable to reserve space in buffer, event skipped"; + cerr << "MRB: Unable to reserve space in buffer, event skipped"; continue; } @@ -113,7 +112,7 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t ++count; //cerr << "MRB - read event at time " << ev_time << endl; } else { - //cerr << "WARNING: error reading event contents from MIDI ring" << endl; + cerr << "WARNING: error reading event contents from MIDI ring" << endl; } } diff --git a/libs/ardour/midi_state_tracker.cc b/libs/ardour/midi_state_tracker.cc index c2aa19e5c8..3871c139df 100644 --- a/libs/ardour/midi_state_tracker.cc +++ b/libs/ardour/midi_state_tracker.cc @@ -31,7 +31,7 @@ MidiStateTracker::MidiStateTracker () } void -MidiStateTracker::track_note_onoffs (Evoral::MIDIEvent &event) +MidiStateTracker::track_note_onoffs (const Evoral::MIDIEvent &event) { if (event.is_note_on()) { _active_notes [event.note() + 128 * event.channel()] = true; @@ -46,12 +46,13 @@ MidiStateTracker::track (const MidiBuffer::iterator &from, const MidiBuffer::ite bool ret = false; for (MidiBuffer::iterator i = from; i != to; ++i) { - if ((*i).event_type() == LoopEventType) { + const Evoral::MIDIEvent ev(*i, false); + if (ev.event_type() == LoopEventType) { ret = true; continue; } - track_note_onoffs (*i); + track_note_onoffs (ev); } return ret; } diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index af126e1f95..12bbc1d15e 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -586,76 +586,11 @@ MidiTrack::process_output_buffers (BufferSet& bufs, } void -MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start_frame, nframes_t end_frame, +MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start, nframes_t end, nframes_t nframes, nframes_t offset) { -#if 0 - BufferSet& mix_buffers = _session.get_mix_buffers(ChanCount(DataType::MIDI, 2)); - - /* FIXME: this could be more realtimey */ - - // Write immediate events (UI controls) - MidiBuffer& cc_buf = mix_buffers.get_midi(0); - cc_buf.silence(nframes, offset); - - uint8_t buf[3]; // CC = 3 bytes - buf[0] = MIDI_CMD_CONTROL; - Evoral::Event ev(0, 3, buf, false); - - // Write track controller automation - // This now lives in MidiModel. Any need for track automation like this? - // Relative Velocity? - if (_session.transport_rolling()) { - for (Controls::const_iterator i = _controls.begin(); i != _controls.end(); ++i) { - const boost::shared_ptr<AutomationList> list = (*i).second->list(); - - if ( (!list->automation_playback()) - || (list->parameter().type() != MidiCCAutomation)) - continue; - - double start = start_frame; - double x, y; - while ((*i).second->list()->rt_safe_earliest_event(start, end_frame, x, y)) { - assert(x >= start_frame); - assert(x <= end_frame); - - const nframes_t stamp = (nframes_t)floor(x - start_frame); - assert(stamp < nframes); - - assert(y >= 0.0); - assert(y <= 127.0); - - ev.time() = stamp; - ev.buffer()[1] = (uint8_t)list->parameter().id(); - ev.buffer()[2] = (uint8_t)y; - - cc_buf.push_back(ev); - - start = x + 1; // FIXME? maybe? - } - } - } - - /* FIXME: too much copying! */ - - // Merge cc buf into output - if (cc_buf.size() > 0) { - - // Both CC and route, must merge - if (output_buf.size() > 0) { - - MidiBuffer& mix_buf = mix_buffers.get_midi(1); - mix_buf.merge(output_buf, cc_buf); - output_buf.copy(mix_buf); - - } else { - output_buf.copy(cc_buf); - } - } -#endif - // Append immediate events (UI controls) - _immediate_events.read(output_buf, 0, 0, offset + nframes-1); // all stamps = 0 + _immediate_events.read(output_buf, 0, 0, offset + nframes - 1); // all stamps = 0 } int diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index d379d52a3d..978192dc0a 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -266,11 +266,13 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) void SMFSource::append_event_unlocked(EventTimeUnit unit, const Evoral::Event& ev) { - if (ev.size() == 0) + if (ev.size() == 0) { + cerr << "SMFSource: Warning: skipping empty event" << endl; return; + } - /*printf("SMFSource: %s - append_event_unlocked chan = %u, time = %lf, size = %u, data = ", - name().c_str(), (unsigned)ev.channel(), ev.time(), ev.size()); + /*printf("SMFSource: %s - append_event_unlocked time = %lf, size = %u, data = ", + name().c_str(), ev.time(), ev.size()); for (size_t i=0; i < ev.size(); ++i) { printf("%X ", ev.buffer()[i]); } |