From: David Robillard Date: Mon, 22 Sep 2008 16:28:02 +0000 (+0000) Subject: Move event specific ringbuffer stuff to evoral. X-Git-Tag: 3.0-alpha5~4118 X-Git-Url: https://main.carlh.net/gitweb/?p=ardour.git;a=commitdiff_plain;h=a2d2f738cb63dbf0fb89e0a00c424ce883fb7888 Move event specific ringbuffer stuff to evoral. Sane event type interface between evoral and libardour (no more shared magic numbers). Cleanup Evoral::Sequence iterator, fix bugs, probably introduce new ones. Move MIDI specific event functions to Evoral::MIDIEvent (is-a Evoral::Event). git-svn-id: svn://localhost/ardour2/branches/3.0@3785 d708f5d6-7413-0410-9779-e7cbd77b26cf --- diff --git a/gtk2_ardour/canvas-program-change.cc b/gtk2_ardour/canvas-program-change.cc index e5a7768f34..bd9c0e89db 100644 --- a/gtk2_ardour/canvas-program-change.cc +++ b/gtk2_ardour/canvas-program-change.cc @@ -20,7 +20,7 @@ CanvasProgramChange::CanvasProgramChange( , _rect(0) { char pgm_str[4]; - snprintf(pgm_str, 4, "%d", (int)event->pgm_number()); + snprintf(pgm_str, 4, "%d", (int)(((Evoral::MIDIEvent*)event.get())->pgm_number())); _text = new Text(*this, 0.0, 0.0, pgm_str); _text->property_justification() = Gtk::JUSTIFY_CENTER; _text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiProgramChangeOutline.get(); diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index bfd23f900e..c67d0a5c5e 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -366,7 +366,7 @@ MidiRegionView::canvas_event(GdkEvent* ev) /** Add a note to the model, and the view, at a canvas (click) coordinate */ void -MidiRegionView::create_note_at(double x, double y, double duration) +MidiRegionView::create_note_at(double x, double y, double length) { MidiTimeAxisView* const mtv = dynamic_cast(&trackview); MidiStreamView* const view = mtv->midi_view(); @@ -383,7 +383,7 @@ MidiRegionView::create_note_at(double x, double y, double duration) /* const Meter& m = trackview.session().tempo_map().meter_at(new_note_time); const Tempo& t = trackview.session().tempo_map().tempo_at(new_note_time); - double duration = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar(); + double length = m.frames_per_bar(t, trackview.session().frame_rate()) / m.beats_per_bar(); */ // we need to snap here again in nframes64_t in order to be sample accurate @@ -392,13 +392,13 @@ MidiRegionView::create_note_at(double x, double y, double duration) nframes64_t new_note_time_position_relative = new_note_time - _region->start(); new_note_time = snap_to_frame(new_note_time_position_relative) + _region->start(); - // we need to snap the duration too to be sample accurate - nframes64_t new_note_duration = nframes_t(duration); - new_note_duration = snap_to_frame(new_note_time_position_relative + new_note_duration) + _region->start() + // we need to snap the length too to be sample accurate + nframes64_t new_note_length = nframes_t(length); + new_note_length = snap_to_frame(new_note_time_position_relative + new_note_length) + _region->start() - new_note_time; const boost::shared_ptr new_note(new Evoral::Note( - 0, new_note_time, new_note_duration, (uint8_t)note, 0x40)); + 0, new_note_time, new_note_length, (uint8_t)note, 0x40)); view->update_bounds(new_note->note()); MidiModel::DeltaCommand* cmd = _model->new_delta_command("add note"); @@ -508,7 +508,7 @@ MidiRegionView::redisplay_model() for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) { cerr << "NOTE time: " << (*i)->time() << " pitch: " << int((*i)->note()) - << " duration: " << (*i)->duration() + << " length: " << (*i)->length() << " end-time: " << (*i)->end_time() << " velocity: " << int((*i)->velocity()) << endl; @@ -769,9 +769,9 @@ MidiRegionView::extend_active_notes() } -/** Add a MIDI note to the view (with duration). +/** Add a MIDI note to the view (with length). * - * If in sustained mode, notes with duration 0 will be considered active + * If in sustained mode, notes with length 0 will be considered active * notes, and resolve_note should be called when the corresponding note off * event arrives, to properly display the note. */ @@ -804,13 +804,13 @@ MidiRegionView::add_note(const boost::shared_ptr note) CanvasNote* ev_rect = new CanvasNote(*this, *group, note); ev_rect->property_x1() = x; ev_rect->property_y1() = y1; - if (note->duration() > 0) + if (note->length() > 0) ev_rect->property_x2() = note_endpixel; else ev_rect->property_x2() = trackview.editor.frame_to_pixel(_region->length()); ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height()); - if (note->duration() == 0) { + if (note->length() == 0) { if (_active_notes) { assert(note->note() < 128); @@ -819,7 +819,7 @@ MidiRegionView::add_note(const boost::shared_ptr note) if (_active_notes[note->note()]) { CanvasNote* const old_rect = _active_notes[note->note()]; boost::shared_ptr old_note = old_rect->note(); - cerr << "MidiModel: WARNING: Note has duration 0: chan " << old_note->channel() + cerr << "MidiModel: WARNING: Note has length 0: chan " << old_note->channel() << "note " << (int)old_note->note() << " @ " << old_note->time() << endl; /* FIXME: How large to make it? Make it a diamond? */ old_rect->property_x2() = old_rect->property_x1() + 2.0; diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index 8d2986b6de..6c620128bc 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -555,7 +555,7 @@ MidiStreamView::update_rec_regions (boost::shared_ptr data, nframes_t const boost::shared_ptr note = data->note_at(i); - if (note->duration() > 0 && note->end_time() + region->position() > start) + if (note->length() > 0 && note->end_time() + region->position() > start) mrv->resolve_note(note->note(), note->end_time()); if (note->time() + region->position() < start) diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index c351510d19..352d1b26f6 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -32,18 +32,18 @@ ardour.Append(CPPPATH = '#libs/surfaces/control_protocol') ardour_files=Split(""" amp.cc analyser.cc -audioanalyser.cc audio_buffer.cc audio_diskstream.cc -audioengine.cc -audiofilesource.cc -audiofile_tagger.cc audio_library.cc audio_playlist.cc audio_port.cc +audio_track.cc +audioanalyser.cc +audioengine.cc +audiofile_tagger.cc +audiofilesource.cc audioregion.cc audiosource.cc -audio_track.cc auditioner.cc auto_bundle.cc automatable.cc @@ -65,13 +65,14 @@ default_click.cc directory_names.cc diskstream.cc enums.cc +event_type_map.cc export_channel_configuration.cc export_file_io.cc export_filename.cc export_format_base.cc export_format_manager.cc -export_formats.cc export_format_specification.cc +export_formats.cc export_handler.cc export_preset.cc export_processor.cc @@ -97,6 +98,7 @@ ladspa_plugin.cc location.cc meter.cc midi_buffer.cc +midi_clock_slave.cc midi_diskstream.cc midi_model.cc midi_playlist.cc @@ -107,7 +109,6 @@ midi_stretch.cc midi_track.cc mix.cc mtc_slave.cc -midi_clock_slave.cc named_selection.cc onset_detector.cc panner.cc @@ -131,8 +132,8 @@ reverse.cc route.cc route_group.cc send.cc -session_butler.cc session.cc +session_butler.cc session_click.cc session_command.cc session_directory.cc @@ -150,8 +151,8 @@ silentfilesource.cc smf_reader.cc smf_source.cc sndfile_helpers.cc -sndfilesource.cc sndfileimportable.cc +sndfilesource.cc source.cc source_factory.cc svn_revision.cc diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h index 837bbbc617..dbce7de0bf 100644 --- a/libs/ardour/ardour/automatable.h +++ b/libs/ardour/ardour/automatable.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -53,9 +54,6 @@ public: virtual void add_control(boost::shared_ptr); virtual void automation_snapshot(nframes_t now, bool force); - bool should_snapshot (nframes_t now) { - return (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval); - } virtual void transport_stopped(nframes_t now); virtual string describe_parameter(Parameter param); @@ -73,6 +71,11 @@ public: void mark_automation_visible(Parameter, bool); + inline bool should_snapshot (nframes_t now) { + return (_last_automation_snapshot > now + || (now - _last_automation_snapshot) > _automation_interval); + } + static void set_automation_interval (jack_nframes_t frames) { _automation_interval = frames; } @@ -113,7 +116,7 @@ public: AutomatableSequence(Session& s, size_t size) : Evoral::ControlSet() , Automatable(s) - , Evoral::Sequence(size) + , Evoral::Sequence(EventTypeMap::instance()) {} }; diff --git a/libs/ardour/ardour/event_type_map.h b/libs/ardour/ardour/event_type_map.h new file mode 100644 index 0000000000..6736feeb86 --- /dev/null +++ b/libs/ardour/ardour/event_type_map.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2000-2007 Paul Davis + Author: Dave Robillard + + 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_event_type_map_h__ +#define __ardour_event_type_map_h__ + +#include + +namespace ARDOUR { + +class EventTypeMap : public Evoral::TypeMap { +public: + bool type_is_midi(uint32_t type) const; + uint8_t parameter_midi_type(const Evoral::Parameter& param) const; + uint32_t midi_event_type(uint8_t status) const; + + static EventTypeMap& instance() { return event_type_map; } + +private: + static EventTypeMap event_type_map; +}; + +} // namespace ARDOUR + +#endif /* __ardour_event_type_map_h__ */ + diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h index 0c13199825..606cbd0ec8 100644 --- a/libs/ardour/ardour/midi_buffer.h +++ b/libs/ardour/ardour/midi_buffer.h @@ -39,7 +39,7 @@ public: void copy(const MidiBuffer& copy); - bool push_back(const Evoral::Event& event); + bool push_back(const Evoral::MIDIEvent& event); bool push_back(const jack_midi_event_t& event); uint8_t* reserve(double time, size_t size); @@ -50,7 +50,7 @@ public: struct iterator { iterator(MidiBuffer& b, size_t i) : buffer(b), index(i) {} - inline Evoral::Event& operator*() const { return buffer[index]; } + inline Evoral::MIDIEvent& operator*() const { return buffer[index]; } inline iterator& operator++() { ++index; return *this; } // prefix inline bool operator!=(const iterator& other) const { return index != other.index; } @@ -61,7 +61,7 @@ public: struct const_iterator { const_iterator(const MidiBuffer& b, size_t i) : buffer(b), index(i) {} - inline const Evoral::Event& operator*() const { return buffer[index]; } + inline const Evoral::MIDIEvent& operator*() const { return buffer[index]; } inline const_iterator& operator++() { ++index; return *this; } // prefix inline bool operator!=(const const_iterator& other) const { return index != other.index; } @@ -80,8 +80,8 @@ private: friend class iterator; friend class const_iterator; - const Evoral::Event& operator[](size_t i) const { assert(i < _size); return _events[i]; } - Evoral::Event& operator[](size_t i) { assert(i < _size); return _events[i]; } + 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 @@ -92,8 +92,8 @@ private: /* FIXME: this is utter crap. rewrite as a flat/packed buffer like MidiRingBuffer */ - Evoral::Event* _events; ///< Event structs that point to offsets in _data - uint8_t* _data; ///< MIDI, straight up. No time stamps. + Evoral::MIDIEvent* _events; ///< Event structs that point to offsets in _data + uint8_t* _data; ///< MIDI, straight up. No time stamps. }; diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h index db0c3029a8..ae82dc14ab 100644 --- a/libs/ardour/ardour/midi_ring_buffer.h +++ b/libs/ardour/ardour/midi_ring_buffer.h @@ -24,239 +24,30 @@ #include #include #include +#include namespace ARDOUR { -/* FIXME: this is probably too much inlined code */ - - -/** A RingBuffer. - * Read/Write realtime safe. - * Single-reader Single-writer thread safe. - * - * This is Raul::RingBuffer, lifted for MIDIRingBuffer to inherit from as it works - * a bit differently than PBD::Ringbuffer. This could/should be replaced with - * the PBD ringbuffer to decrease code size, but this code is tested and known to - * work, so here it sits for now... - * - * Ignore this class, use MidiRingBuffer. - */ -template -class MidiRingBufferBase { -public: - - /** @param size Size in bytes. - */ - MidiRingBufferBase(size_t size) - : _size(size) - , _buf(new T[size]) - { - reset(); - assert(read_space() == 0); - assert(write_space() == size - 1); - } - - virtual ~MidiRingBufferBase() { - delete[] _buf; - } - - /** Reset(empty) the ringbuffer. - * NOT thread safe. - */ - void reset() { - g_atomic_int_set(&_write_ptr, 0); - g_atomic_int_set(&_read_ptr, 0); - } - - size_t write_space() const { - - const size_t w = g_atomic_int_get(&_write_ptr); - const size_t 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() const { - - const size_t w = g_atomic_int_get(&_write_ptr); - const size_t 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; } - - size_t peek(size_t size, T* dst); - bool full_peek(size_t size, T* dst); - - size_t read(size_t size, T* dst); - bool full_read(size_t size, T* dst); - - bool skip(size_t size); - - void write(size_t size, const T* src); - -protected: - mutable int _write_ptr; - mutable int _read_ptr; - - size_t _size; ///< Size (capacity) in bytes - T* _buf; ///< size, event, size, event... -}; - - -/** Peek at the ringbuffer (read w/o advancing read pointer). - * - * Note that a full read may not be done if the data wraps around. - * Caller must check return value and call again if necessary, or use the - * full_peek method which does this automatically. - */ -template -size_t -MidiRingBufferBase::peek(size_t size, T* dst) -{ - const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); - - const size_t read_size = (priv_read_ptr + size < _size) - ? size - : _size - priv_read_ptr; - - memcpy(dst, &_buf[priv_read_ptr], read_size); - - return read_size; -} - - -template -bool -MidiRingBufferBase::full_peek(size_t size, T* dst) -{ - if (read_space() < size) { - return false; - } - - const size_t read_size = peek(size, dst); - - if (read_size < size) { - peek(size - read_size, dst + read_size); - } - - return true; -} - - -/** Read from the ringbuffer. - * - * Note that a full read may not be done if the data wraps around. - * Caller must check return value and call again if necessary, or use the - * full_read method which does this automatically. - */ -template -size_t -MidiRingBufferBase::read(size_t size, T* dst) -{ - const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); - - const size_t read_size = (priv_read_ptr + size < _size) - ? size - : _size - priv_read_ptr; - - memcpy(dst, &_buf[priv_read_ptr], read_size); - - g_atomic_int_set(&_read_ptr, (priv_read_ptr + read_size) % _size); - - return read_size; -} - - -template -bool -MidiRingBufferBase::full_read(size_t size, T* dst) -{ - if (read_space() < size) { - return false; - } - - const size_t read_size = read(size, dst); - - if (read_size < size) { - read(size - read_size, dst + read_size); - } - - return true; -} - - -template -bool -MidiRingBufferBase::skip(size_t size) -{ - if (read_space() < size) { - std::cerr << "WARNING: Attempt to skip past end of MIDI ring buffer" << std::endl; - return false; - } - - const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); - g_atomic_int_set(&_read_ptr, (priv_read_ptr + size) % _size); - - return true; -} - - -template -inline void -MidiRingBufferBase::write(size_t size, const T* src) -{ - const size_t priv_write_ptr = g_atomic_int_get(&_write_ptr); - - if (priv_write_ptr + size <= _size) { - memcpy(&_buf[priv_write_ptr], src, size); - g_atomic_int_set(&_write_ptr, (priv_write_ptr + size) % _size); - } else { - const size_t this_size = _size - priv_write_ptr; - assert(this_size < size); - assert(priv_write_ptr + this_size <= _size); - memcpy(&_buf[priv_write_ptr], src, this_size); - memcpy(&_buf[0], src+this_size, size - this_size); - g_atomic_int_set(&_write_ptr, size - this_size); - } -} - - -/* ******************************************************************** */ - - -/** A MIDI RingBuffer. +/** A RingBuffer for (MIDI) events. * - * This is timestamps and MIDI packed sequentially into a single buffer, similarly - * to LV2 MIDI. The buffer looks like this: + * This is simply a wrapper around a raw ringbuffer which writes/reads events + * as flat placked blobs. + * The buffer looks like this: * - * [timestamp][size][size bytes of raw MIDI][timestamp][size][etc..] + * [timestamp][type][size][size bytes of raw MIDI][timestamp][type][size](etc...) */ -class MidiRingBuffer : public MidiRingBufferBase, public Evoral::EventSink { +class MidiRingBuffer : public Evoral::EventRingBuffer { public: /** @param size Size in bytes. */ MidiRingBuffer(size_t size) - : MidiRingBufferBase(size), _channel_mask(0x0000FFFF) + : Evoral::EventRingBuffer(size) + , _channel_mask(0x0000FFFF) {} - size_t write(double time, uint32_t size, const uint8_t* buf); - bool read(double* time, uint32_t* size, uint8_t* buf); - - bool read_prefix(double* time, size_t* size); - bool read_contents(size_t size, uint8_t* buf); + inline bool read_prefix(EventTime* time, EventType* type, uint32_t* size); + inline bool read_contents(uint32_t size, uint8_t* buf); size_t read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset=0); @@ -292,32 +83,17 @@ private: }; -inline bool -MidiRingBuffer::read(double* time, uint32_t* size, uint8_t* buf) -{ - bool success = MidiRingBufferBase::full_read(sizeof(double), (uint8_t*)time); - - if (success) { - success = MidiRingBufferBase::full_read(sizeof(size_t), (uint8_t*)size); - } - if (success) { - success = MidiRingBufferBase::full_read(*size, buf); - } - - return success; -} - - /** Read the time and size of an event. This call MUST be immediately proceeded * by a call to read_contents (or the read pointer will be garabage). */ inline bool -MidiRingBuffer::read_prefix(double* time, size_t* size) +MidiRingBuffer::read_prefix(EventTime* time, EventType* type, uint32_t* size) { - bool success = MidiRingBufferBase::full_read(sizeof(double), (uint8_t*)time); - if (success) { - success = MidiRingBufferBase::full_read(sizeof(size_t), (uint8_t*)size); - } + bool success = Evoral::EventRingBuffer::full_read(sizeof(EventTime), (uint8_t*)time); + if (success) + success = Evoral::EventRingBuffer::full_read(sizeof(EventType), (uint8_t*)type); + if (success) + success = Evoral::EventRingBuffer::full_read(sizeof(uint32_t), (uint8_t*)size); return success; } @@ -327,51 +103,9 @@ MidiRingBuffer::read_prefix(double* time, size_t* size) * by a call to read_prefix (or the returned even will be garabage). */ inline bool -MidiRingBuffer::read_contents(size_t size, uint8_t* buf) -{ - return MidiRingBufferBase::full_read(size, buf); -} - - -inline size_t -MidiRingBuffer::write(double time, uint32_t size, const uint8_t* buf) +MidiRingBuffer::read_contents(uint32_t size, uint8_t* buf) { - /*fprintf(stderr, "MRB %p write (t = %f) ", this, time); - for (size_t i = 0; i < size; ++i) - fprintf(stderr, "%X", (char)buf[i]); - fprintf(stderr, "\n");*/ - - assert(size > 0); - - // Don't write event if it doesn't match channel filter - if (is_channel_event(buf[0]) && get_channel_mode() == FilterChannels) { - uint8_t channel = buf[0] & 0x0F; - if ( !(get_channel_mask() & (1L << channel)) ) { - return 0; - } - } - - if (write_space() < (sizeof(double) + sizeof(size_t) + size)) { - return 0; - } else { - MidiRingBufferBase::write(sizeof(double), (uint8_t*)&time); - MidiRingBufferBase::write(sizeof(size_t), (uint8_t*)&size); - if (is_channel_event(buf[0]) && get_channel_mode() == ForceChannel) { - assert(size == 2 || size == 3); - uint8_t tmp_buf[3]; - // Force event to channel - tmp_buf[0] = (buf[0] & 0xF0) | (get_channel_mask() & 0x0F); - tmp_buf[1] = buf[1]; - if (size == 3) { - tmp_buf[2] = buf[2]; - } - MidiRingBufferBase::write(size, tmp_buf); - } else { - MidiRingBufferBase::write(size, buf); - } - return size; - } - + return Evoral::EventRingBuffer::full_read(size, buf); } @@ -383,31 +117,31 @@ MidiRingBuffer::write(double time, uint32_t size, const uint8_t* buf) inline size_t MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset) { - if (read_space() == 0) + if (read_space() == 0) { + //std::cerr << "MRB: NO READ SPACE" << std::endl; return 0; + } - double ev_time; - uint32_t ev_size; + EventTime ev_time; + EventType ev_type; + uint32_t ev_size; size_t count = 0; - //printf("---- MRB read %u .. %u + %u\n", start, end, offset); + //std::cerr << "MRB read " << start << " .. " << end << " + " << offset << std::endl; - while (read_space() > sizeof(double) + sizeof(size_t)) { + while (read_space() > sizeof(EventTime) + sizeof(EventType) + sizeof(uint32_t)) { - full_peek(sizeof(double), (uint8_t*)&ev_time); + full_peek(sizeof(EventTime), (uint8_t*)&ev_time); if (ev_time > end) { + //std::cerr << "MRB: PAST END (" << ev_time << " : " << end << ")" << std::endl; break; } - bool success = MidiRingBufferBase::full_read(sizeof(double), (uint8_t*)&ev_time); - if (success) { - success = MidiRingBufferBase::full_read(sizeof(size_t), (uint8_t*)&ev_size); - } - + bool success = read_prefix(&ev_time, &ev_type, &ev_size); if (!success) { - std::cerr << "MRB: READ ERROR (time/size)" << std::endl; + //std::cerr << "MRB: READ ERROR (time/type/size)" << std::endl; continue; } @@ -419,6 +153,7 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t if (is_channel_event(status) && get_channel_mode() == FilterChannels) { const uint8_t channel = status & 0x0F; if ( !(get_channel_mask() & (1L << channel)) ) { + //std::cerr << "MRB skipping event due to channel mask" << std::endl; skip(ev_size); // Advance read pointer to next event continue; } @@ -426,36 +161,36 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t if (ev_time >= start) { - /*std::cerr << "MRB " << this << " - Reading event, time = " - << ev_time << " - " << start << " => " << ev_time - start - << ", size = " << ev_size << std::endl;*/ + //std::cerr << "MRB " << this << " - Reading event, time = " + // << ev_time << " - " << start << " => " << ev_time - start + // << ", size = " << ev_size << std::endl; ev_time -= start; uint8_t* write_loc = dst.reserve(ev_time, ev_size); if (write_loc == NULL) { - std::cerr << "MRB: Unable to reserve space in buffer, event skipped"; + //std::cerr << "MRB: Unable to reserve space in buffer, event skipped"; continue; } - success = MidiRingBufferBase::full_read(ev_size, write_loc); + success = Evoral::EventRingBuffer::full_read(ev_size, write_loc); if (success) { if (is_channel_event(status) && get_channel_mode() == ForceChannel) { write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F); } ++count; - //printf("MRB - read event at time %lf\n", ev_time); + //std::cerr << "MRB - read event at time " << ev_time << std::endl; } else { - std::cerr << "MRB: READ ERROR (data)" << std::endl; + //std::cerr << "MRB: READ ERROR (data)" << std::endl; } } else { - printf("MRB (start %u) - Skipping event at (too early) time %f\n", start, ev_time); + //std::cerr << "MRB (start " << start << ") - Skipping event at (too early) time " << ev_time << std::endl; } } - //printf("(R) read space: %zu\n", read_space()); + //std::cerr << "MTB read space: " << read_space() << std::endl; return count; } diff --git a/libs/ardour/ardour/parameter.h b/libs/ardour/ardour/parameter.h index 5c390c1039..5ccbd4a6e3 100644 --- a/libs/ardour/ardour/parameter.h +++ b/libs/ardour/ardour/parameter.h @@ -50,20 +50,6 @@ public: init_metadata(type); } -#if 0 - Parameter(AutomationType type, double min, double max, double normal) - : Evoral::Parameter((uint32_t)type, 0, 0, min, max, normal) - {} - - Parameter(const Parameter& copy) - : Evoral::Parameter(copy) - { - _min = copy._min; - _max = copy._max; - _normal = copy._max; - } -#endif - Parameter(const Evoral::Parameter& copy) : Evoral::Parameter(copy) { diff --git a/libs/ardour/event_type_map.cc b/libs/ardour/event_type_map.cc new file mode 100644 index 0000000000..cd78fd02f7 --- /dev/null +++ b/libs/ardour/event_type_map.cc @@ -0,0 +1,61 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Dave Robillard + + 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. + +*/ + +#include +#include +#include +#include + +namespace ARDOUR { + +EventTypeMap EventTypeMap::event_type_map; + +bool +EventTypeMap::type_is_midi(uint32_t type) const +{ + return (type >= MidiCCAutomation) && (type <= MidiChannelPressureAutomation); +} + +uint8_t +EventTypeMap::parameter_midi_type(const Evoral::Parameter& param) const +{ + switch (param.type()) { + case MidiCCAutomation: return MIDI_CMD_CONTROL; break; + case MidiPgmChangeAutomation: return MIDI_CMD_PGM_CHANGE; break; + case MidiChannelPressureAutomation: return MIDI_CMD_CHANNEL_PRESSURE; break; + case MidiPitchBenderAutomation: return MIDI_CMD_BENDER; break; + default: return 0; + } +} + +uint32_t +EventTypeMap::midi_event_type(uint8_t status) const +{ + switch (status & 0xF0) { + case MIDI_CMD_CONTROL: return MidiCCAutomation; break; + case MIDI_CMD_PGM_CHANGE: return MidiPgmChangeAutomation; break; + case MIDI_CMD_CHANNEL_PRESSURE: return MidiChannelPressureAutomation; break; + case MIDI_CMD_BENDER: return MidiPitchBenderAutomation; break; + default: return 0; + } +} + +} // namespace ARDOUR + diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 199a2fd1a4..370991be5f 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -305,7 +305,7 @@ static void write_midi_data_to_new_files (SMFReader* source, Session::import_status& status, vector >& newfiles) { - Evoral::Event ev(0.0, 4, NULL, true); + Evoral::Event ev(0, 0.0, 4, NULL, true); status.progress = 0.0f; diff --git a/libs/ardour/meter.cc b/libs/ardour/meter.cc index 363cbe242f..5dee1d9845 100644 --- a/libs/ardour/meter.cc +++ b/libs/ardour/meter.cc @@ -47,7 +47,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::Event& ev = *i; + const Evoral::MIDIEvent& ev = *i; 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 afd8481795..f1f6b9b633 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -115,7 +115,7 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset) // FIXME: slow for (size_t i=0; i < msrc.size(); ++i) { - const Evoral::Event& ev = msrc[i]; + const Evoral::MIDIEvent& ev = msrc[i]; if (ev.time() >= offset && ev.time() < offset+nframes) { //cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl; push_back(ev); @@ -136,7 +136,7 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset) * @return false if operation failed (not enough room) */ bool -MidiBuffer::push_back(const Evoral::Event& ev) +MidiBuffer::push_back(const Evoral::MIDIEvent& ev) { if (_size == _capacity) return false; @@ -262,8 +262,8 @@ MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b) push_back(b[b_index]); ++b_index; } else { - const Evoral::Event& a_ev = a[a_index]; - const Evoral::Event& b_ev = b[b_index]; + 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); diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index b2991c1a7f..bb7f2931f4 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -520,9 +520,9 @@ MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t MidiBuffer::iterator port_iter = _source_port->get_midi_buffer().begin(); for (size_t i=0; i < to_write; ++i) { - const Evoral::Event& ev = *port_iter; + const Evoral::MIDIEvent& ev = *port_iter; assert(ev.buffer()); - _capture_buf->write(ev.time() + transport_frame, ev.size(), ev.buffer()); + _capture_buf->write(ev.time() + transport_frame, ev.type(), ev.size(), ev.buffer()); ++port_iter; } diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc index c22820c83c..18b37689bd 100644 --- a/libs/ardour/midi_model.cc +++ b/libs/ardour/midi_model.cc @@ -170,9 +170,9 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr note time_str << int(note->time()); xml_note->add_property("time", time_str.str()); - ostringstream duration_str(ios::ate); - duration_str <<(unsigned int) note->duration(); - xml_note->add_property("duration", duration_str.str()); + ostringstream length_str(ios::ate); + length_str <<(unsigned int) note->length(); + xml_note->add_property("length", length_str.str()); ostringstream velocity_str(ios::ate); velocity_str << (unsigned int) note->velocity(); @@ -195,15 +195,15 @@ boost::shared_ptr MidiModel::DeltaCommand::unmarshal_note(XMLNode istringstream time_str(xml_note->property("time")->value()); time_str >> time; - unsigned int duration; - istringstream duration_str(xml_note->property("duration")->value()); - duration_str >> duration; + unsigned int length; + istringstream length_str(xml_note->property("length")->value()); + length_str >> length; unsigned int velocity; istringstream velocity_str(xml_note->property("velocity")->value()); velocity_str >> velocity; - boost::shared_ptr note_ptr(new Evoral::Note(channel, time, duration, note, velocity)); + boost::shared_ptr note_ptr(new Evoral::Note(channel, time, length, note, velocity)); return note_ptr; } diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 745e5bf1b7..d60aba8bbc 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -723,7 +723,8 @@ MidiTrack::write_immediate_event(size_t size, const uint8_t* buf) printf("%X ", buf[i]); } printf("\n");*/ - return (_immediate_events.write(0, size, buf) == size); + const uint32_t type = EventTypeMap::instance().midi_event_type(buf[0]); + return (_immediate_events.write(0, type, size, buf) == size); } void diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc index ff8925edd9..3b5a264a7b 100644 --- a/libs/ardour/quantize.cc +++ b/libs/ardour/quantize.cc @@ -72,12 +72,12 @@ Quantize::run (boost::shared_ptr r) for (Evoral::Sequence::Notes::iterator i = model->notes().begin(); i != model->notes().end(); ++i) { const double new_time = lrint((*i)->time() / q_frames) * q_frames; - double new_dur = lrint((*i)->duration() / q_frames) * q_frames; + double new_dur = lrint((*i)->length() / q_frames) * q_frames; if (new_dur == 0.0) new_dur = q_frames; (*i)->set_time(new_time); - (*i)->set_duration(new_dur); + (*i)->set_length(new_dur); } model->set_edited(true); diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index 2d5c0af0d5..03a8b98199 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -39,6 +39,7 @@ #include #include #include +#include #include "i18n.h" @@ -385,6 +386,7 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, n // Output parameters for read_event (which will allocate scratch in buffer as needed) uint32_t ev_delta_t = 0; + uint32_t ev_type = 0; uint32_t ev_size = 0; uint8_t* ev_buffer = 0; @@ -407,6 +409,8 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, n break; } + ev_type = EventTypeMap::instance().midi_event_type(ev_buffer[0]); + time += ev_delta_t; // accumulate delta time if (ret == 0) { // meta-event (skipped, just accumulate time) @@ -419,7 +423,7 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, n ((time / (double)_ppqn) * frames_per_beat)) + stamp_offset; if (ev_frame_time <= start + cnt) - dst.write(ev_frame_time - negative_stamp_offset, ev_size, ev_buffer); + dst.write(ev_frame_time - negative_stamp_offset, ev_type, ev_size, ev_buffer); else break; } @@ -441,8 +445,9 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) { _write_data_count = 0; - double time; - size_t size; + EventTime time; + EventType type; + uint32_t size; size_t buf_capacity = 4; uint8_t* buf = (uint8_t*)malloc(buf_capacity); @@ -450,14 +455,14 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) if (_model && ! _model->writing()) _model->start_write(); - Evoral::Event ev(0.0, 4, NULL, true); + Evoral::MIDIEvent ev(0, 0.0, 4, NULL, true); while (true) { - bool ret = src.full_peek(sizeof(double), (uint8_t*)&time); + bool ret = src.peek_time(&time); if (!ret || time - _timeline_position > _length + cnt) break; - ret = src.read_prefix(&time, &size); + ret = src.read_prefix(&time, &type, &size); if (!ret) break; @@ -476,6 +481,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt) time -= _timeline_position; ev.set(buf, size, time); + ev.set_event_type(EventTypeMap::instance().midi_event_type(ev.buffer()[0])); if (! (ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex()) ) { //cerr << "SMFSource: WARNING: caller tried to write non SMF-Event of type " << std::hex << int(ev.buffer()[0]) << endl; continue; diff --git a/libs/evoral/SConscript b/libs/evoral/SConscript index 6141f10e4f..2b18d59107 100644 --- a/libs/evoral/SConscript +++ b/libs/evoral/SConscript @@ -21,16 +21,17 @@ if evoral['IS_OSX']: domain = 'evoral' evoral.Append(DOMAIN=domain, MAJOR=1, MINOR=0, MICRO=0) -evoral.Append(CXXFLAGS="-DEVENT_WITH_XML") +evoral.Append(CXXFLAGS="-DEVORAL_MIDI_XML") sources = Split(""" src/Control.cpp src/ControlList.cpp src/ControlSet.cpp +src/Curve.cpp src/Event.cpp +src/MIDIEvent.cpp src/Note.cpp src/Sequence.cpp -src/Curve.cpp """) libevoral = evoral.SharedLibrary('evoral', [ sources ]) diff --git a/libs/evoral/evoral/Event.hpp b/libs/evoral/evoral/Event.hpp index beffb01eb5..1dd081a5b9 100644 --- a/libs/evoral/evoral/Event.hpp +++ b/libs/evoral/evoral/Event.hpp @@ -24,52 +24,39 @@ #include #include #include -#include -#ifdef EVENT_WITH_XML - #include -#endif +#include + /** If this is not defined, all methods of MidiEvent are RT safe * but MidiEvent will never deep copy and (depending on the scenario) * may not be usable in STL containers, signals, etc. */ -#define EVENT_ALLOW_ALLOC 1 - -//#define EVENT_WITH_XML +#define EVORAL_EVENT_ALLOC 1 namespace Evoral { -/** Identical to jack_midi_event_t, but with double timestamp +/** An event (much like a type generic jack_midi_event_t) * * time is either a frame time (from/to Jack) or a beat time (internal * tempo time, used in MidiModel) depending on context. */ struct Event { -#ifdef EVENT_ALLOW_ALLOC - Event(double t=0, uint32_t s=0, uint8_t* b=NULL, bool owns_buffer=false); +#ifdef EVORAL_EVENT_ALLOC + Event(EventType type=0, EventTime t=0, uint32_t s=0, uint8_t* b=NULL, bool alloc=false); /** Copy \a copy. * - * If \a owns_buffer is true, the buffer will be copied and this method + * If \a alloc is true, the buffer will be copied and this method * is NOT REALTIME SAFE. Otherwise both events share a buffer and * memory management semantics are the caller's problem. */ - Event(const Event& copy, bool owns_buffer); - -#ifdef EVENT_WITH_XML - /** Event from XML ala http://www.midi.org/dtds/MIDIEvents10.dtd - */ - Event(const XMLNode& event); - - /** Event to XML ala http://www.midi.org/dtds/MIDIEvents10.dtd - */ - boost::shared_ptr to_xml() const; -#endif + Event(const Event& copy, bool alloc); ~Event(); inline const Event& operator=(const Event& copy) { + _type = copy._type; _time = copy._time; if (_owns_buffer) { if (copy._buffer) { @@ -96,12 +83,13 @@ struct Event { _owns_buffer = false; } + _type = copy._type; _time = copy._time; _size = copy._size; _buffer = copy._buffer; } - inline void set(uint8_t* buf, size_t size, double t) { + inline void set(uint8_t* buf, uint32_t size, EventTime t) { if (_owns_buffer) { if (_size < size) { _buffer = (uint8_t*) ::realloc(_buffer, size); @@ -111,11 +99,14 @@ struct Event { _buffer = buf; } - _size = size; _time = t; + _size = size; } inline bool operator==(const Event& other) const { + if (_type != other._type) + return false; + if (_time != other._time) return false; @@ -125,7 +116,7 @@ struct Event { if (_buffer == other._buffer) return true; - for (size_t i=0; i < _size; ++i) + for (uint32_t i=0; i < _size; ++i) if (_buffer[i] != other._buffer[i]) return false; @@ -136,7 +127,7 @@ struct Event { inline bool owns_buffer() const { return _owns_buffer; } - inline void set_buffer(size_t size, uint8_t* buf, bool own) { + inline void set_buffer(uint32_t size, uint8_t* buf, bool own) { if (_owns_buffer) { free(_buffer); _buffer = NULL; @@ -146,7 +137,7 @@ struct Event { _owns_buffer = own; } - inline void realloc(size_t size) { + inline void realloc(uint32_t size) { if (_owns_buffer) { if (size > _size) _buffer = (uint8_t*) ::realloc(_buffer, size); @@ -157,57 +148,37 @@ struct Event { _size = size; } - + + inline void clear() { + _type = 0; + _time = 0; + _size = 0; + _buffer = NULL; + } #else inline void set_buffer(uint8_t* buf) { _buffer = buf; } -#endif // EVENT_ALLOW_ALLOC +#endif // EVORAL_EVENT_ALLOC - inline double time() const { return _time; } - inline double& time() { return _time; } + inline EventType event_type() const { return _type; } + inline void set_event_type(EventType t) { _type = t; } + inline EventTime time() const { return _time; } + inline EventTime& time() { return _time; } inline uint32_t size() const { return _size; } inline uint32_t& size() { return _size; } - inline uint8_t type() const { return (_buffer[0] & 0xF0); } - inline void set_type(uint8_t type) { _buffer[0] = (0x0F & _buffer[0]) - | (0xF0 & type); } - inline uint8_t channel() const { return (_buffer[0] & 0x0F); } - inline void set_channel(uint8_t channel) { _buffer[0] = (0xF0 & _buffer[0]) - | (0x0F & channel); } - inline bool is_note_on() const { return (type() == MIDI_CMD_NOTE_ON); } - inline bool is_note_off() const { return (type() == MIDI_CMD_NOTE_OFF); } - inline bool is_cc() const { return (type() == MIDI_CMD_CONTROL); } - inline bool is_pitch_bender() const { return (type() == MIDI_CMD_BENDER); } - inline bool is_pgm_change() const { return (type() == MIDI_CMD_PGM_CHANGE); } - inline bool is_note() const { return (is_note_on() || is_note_off()); } - inline bool is_aftertouch() const { return (type() == MIDI_CMD_NOTE_PRESSURE); } - inline bool is_channel_aftertouch() const { return (type() == MIDI_CMD_CHANNEL_PRESSURE); } - inline uint8_t note() const { return (_buffer[1]); } - inline uint8_t velocity() const { return (_buffer[2]); } - inline uint8_t cc_number() const { return (_buffer[1]); } - inline uint8_t cc_value() const { return (_buffer[2]); } - inline uint8_t pitch_bender_lsb() const { return (_buffer[1]); } - inline uint8_t pitch_bender_msb() const { return (_buffer[2]); } - inline uint16_t pitch_bender_value() const { return ( ((0x7F & _buffer[2]) << 7) - | (0x7F & _buffer[1]) ); } - inline uint8_t pgm_number() const { return (_buffer[1]); } - inline void set_pgm_number(uint8_t number){ _buffer[1] = number; } - inline uint8_t aftertouch() const { return (_buffer[1]); } - inline uint8_t channel_aftertouch() const { return (_buffer[1]); } - inline bool is_channel_event() const { return (0x80 <= type()) && (type() <= 0xE0); } - inline bool is_smf_meta_event() const { return _buffer[0] == 0xFF; } - inline bool is_sysex() const { return _buffer[0] == 0xF0 - || _buffer[0] == 0xF7; } + inline const uint8_t* buffer() const { return _buffer; } inline uint8_t*& buffer() { return _buffer; } -private: - double _time; /**< Sample index (or beat time) at which event is valid */ - uint32_t _size; /**< Number of uint8_ts of data in \a buffer */ - uint8_t* _buffer; /**< Raw MIDI data */ +protected: + EventType _type; /**< Type of event (application relative, NOT MIDI 'type') */ + EventTime _time; /**< Sample index (or beat time) at which event is valid */ + uint32_t _size; /**< Number of uint8_ts of data in \a buffer */ + uint8_t* _buffer; /**< Raw MIDI data */ -#ifdef EVENT_ALLOW_ALLOC +#ifdef EVORAL_EVENT_ALLOC bool _owns_buffer; /**< Whether buffer is locally allocated */ #endif }; diff --git a/libs/evoral/evoral/EventRingBuffer.hpp b/libs/evoral/evoral/EventRingBuffer.hpp new file mode 100644 index 0000000000..05ec9217b8 --- /dev/null +++ b/libs/evoral/evoral/EventRingBuffer.hpp @@ -0,0 +1,95 @@ +/* This file is part of Evoral. + * Copyright (C) 2008 Dave Robillard + * + * Evoral 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. + * + * Evoral 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 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVORAL_EVENT_RING_BUFFER_HPP +#define EVORAL_EVENT_RING_BUFFER_HPP + +#include +#include +#include +#include + +#include +using namespace std; + +namespace Evoral { + + +/** A RingBuffer of events (generic time-stamped binary "blobs"). + * + * This packs a timestamp, size, and size bytes of data flat into the buffer. + * Useful for MIDI events, OSC messages, etc. + */ +class EventRingBuffer : public Evoral::RingBuffer, public Evoral::EventSink { +public: + + /** @param capacity Ringbuffer capacity in bytes. + */ + EventRingBuffer(size_t capacity) : RingBuffer(capacity) + {} + + size_t capacity() const { return _size; } + + bool peek_time(EventTime* time); + + uint32_t write(EventTime time, EventType type, uint32_t size, const uint8_t* buf); + bool read (EventTime* time, EventType* type, uint32_t* size, uint8_t* buf); +}; + + +inline bool +EventRingBuffer::peek_time(EventTime* time) +{ + bool success = RingBuffer::full_peek(sizeof(EventTime), (uint8_t*)time); + return success; +} + + +inline bool +EventRingBuffer::read(EventTime* time, EventType* type, uint32_t* size, uint8_t* buf) +{ + bool success = RingBuffer::full_read(sizeof(EventTime), (uint8_t*)time); + if (success) + success = RingBuffer::full_read(sizeof(EventType), (uint8_t*)type); + if (success) + success = RingBuffer::full_read(sizeof(uint32_t), (uint8_t*)size); + if (success) + success = RingBuffer::full_read(*size, buf); + + return success; +} + + +inline uint32_t +EventRingBuffer::write(EventTime time, EventType type, uint32_t size, const uint8_t* buf) +{ + if (write_space() < (sizeof(EventTime) + sizeof(EventType) + sizeof(uint32_t) + size)) { + return 0; + } else { + RingBuffer::write(sizeof(EventTime), (uint8_t*)&time); + RingBuffer::write(sizeof(EventType), (uint8_t*)&type); + RingBuffer::write(sizeof(uint32_t), (uint8_t*)&size); + RingBuffer::write(size, buf); + return size; + } +} + + +} // namespace Evoral + +#endif // EVORAL_EVENT_RING_BUFFER_HPP + diff --git a/libs/evoral/evoral/EventSink.hpp b/libs/evoral/evoral/EventSink.hpp index 5f0610e15f..dfa746d870 100644 --- a/libs/evoral/evoral/EventSink.hpp +++ b/libs/evoral/evoral/EventSink.hpp @@ -29,9 +29,7 @@ namespace Evoral { class EventSink { public: virtual ~EventSink() {} - virtual size_t write(timestamp_t time, - uint32_t size, - const uint8_t* buf) = 0; + virtual uint32_t write(EventTime time, EventType type, uint32_t size, const uint8_t* buf) = 0; }; diff --git a/libs/evoral/evoral/MIDIEvent.hpp b/libs/evoral/evoral/MIDIEvent.hpp new file mode 100644 index 0000000000..863103aa74 --- /dev/null +++ b/libs/evoral/evoral/MIDIEvent.hpp @@ -0,0 +1,89 @@ +/* This file is part of Evoral. + * Copyright (C) 2008 Dave Robillard + * Copyright (C) 2000-2008 Paul Davis + * + * Evoral 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. + * + * Evoral 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 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVORAL_MIDI_EVENT_HPP +#define EVORAL_MIDI_EVENT_HPP + +#include +#include +#ifdef EVORAL_MIDI_XML + #include +#endif + +namespace Evoral { + +/** MIDI helper functions for an Event. + * + * This class contains no data, an event can be cast to a MIDIEvent + * but the application must make sure the event actually contains + * valid MIDI data for these functions to make sense. + */ +struct MIDIEvent : public Event { + MIDIEvent(EventType type=0, EventTime t=0, uint32_t s=0, uint8_t* b=NULL, bool alloc=false) + : Event(type, t, s, b, alloc) + {} + + MIDIEvent(const Event& copy, bool alloc) + : Event(copy, alloc) + {} + +#ifdef EVORAL_MIDI_XML + /** Event from XML ala http://www.midi.org/dtds/MIDIEvents10.dtd + */ + MIDIEvent(const XMLNode& event); + + /** Event to XML ala http://www.midi.org/dtds/MIDIEvents10.dtd + */ + boost::shared_ptr to_xml() const; +#endif + + inline uint8_t type() const { return (_buffer[0] & 0xF0); } + inline void set_type(uint8_t type) { _buffer[0] = (0x0F & _buffer[0]) + | (0xF0 & type); } + inline uint8_t channel() const { return (_buffer[0] & 0x0F); } + inline void set_channel(uint8_t channel) { _buffer[0] = (0xF0 & _buffer[0]) + | (0x0F & channel); } + inline bool is_note_on() const { return (type() == MIDI_CMD_NOTE_ON); } + inline bool is_note_off() const { return (type() == MIDI_CMD_NOTE_OFF); } + inline bool is_cc() const { return (type() == MIDI_CMD_CONTROL); } + inline bool is_pitch_bender() const { return (type() == MIDI_CMD_BENDER); } + inline bool is_pgm_change() const { return (type() == MIDI_CMD_PGM_CHANGE); } + inline bool is_note() const { return (is_note_on() || is_note_off()); } + inline bool is_aftertouch() const { return (type() == MIDI_CMD_NOTE_PRESSURE); } + inline bool is_channel_pressure() const { return (type() == MIDI_CMD_CHANNEL_PRESSURE); } + inline uint8_t note() const { return (_buffer[1]); } + inline uint8_t velocity() const { return (_buffer[2]); } + inline uint8_t cc_number() const { return (_buffer[1]); } + inline uint8_t cc_value() const { return (_buffer[2]); } + inline uint8_t pitch_bender_lsb() const { return (_buffer[1]); } + inline uint8_t pitch_bender_msb() const { return (_buffer[2]); } + inline uint16_t pitch_bender_value() const { return ( ((0x7F & _buffer[2]) << 7) + | (0x7F & _buffer[1]) ); } + inline uint8_t pgm_number() const { return (_buffer[1]); } + inline void set_pgm_number(uint8_t number){ _buffer[1] = number; } + inline uint8_t aftertouch() const { return (_buffer[1]); } + inline uint8_t channel_pressure() const { return (_buffer[1]); } + inline bool is_channel_event() const { return (0x80 <= type()) && (type() <= 0xE0); } + inline bool is_smf_meta_event() const { return _buffer[0] == 0xFF; } + inline bool is_sysex() const { return _buffer[0] == 0xF0 + || _buffer[0] == 0xF7; } +}; + +} // namespace Evoral + +#endif // EVORAL_MIDI_EVENT_HPP diff --git a/libs/evoral/evoral/MIDIParameters.hpp b/libs/evoral/evoral/MIDIParameters.hpp index e2bfdc3fb0..744357f5b8 100644 --- a/libs/evoral/evoral/MIDIParameters.hpp +++ b/libs/evoral/evoral/MIDIParameters.hpp @@ -31,8 +31,8 @@ struct ProgramChange : public Parameter { ProgramChange(uint32_t pc_type, uint8_t channel) : Parameter(pc_type, 0, channel) {} }; -struct ChannelAftertouch : public Parameter { - ChannelAftertouch(uint32_t ca_type, uint32_t channel) : Parameter(ca_type, 0, channel) {} +struct ChannelPressure : public Parameter { + ChannelPressure(uint32_t ca_type, uint32_t channel) : Parameter(ca_type, 0, channel) {} }; struct PitchBender : public Parameter { diff --git a/libs/evoral/evoral/Note.hpp b/libs/evoral/evoral/Note.hpp index 76095e733c..75a8046237 100644 --- a/libs/evoral/evoral/Note.hpp +++ b/libs/evoral/evoral/Note.hpp @@ -20,18 +20,18 @@ #define EVORAL_NOTE_HPP #include -#include +#include namespace Evoral { /** An abstract (protocol agnostic) note. * - * Currently a note is defined as (on event, duration, off event). + * Currently a note is defined as (on event, length, off event). */ class Note { public: - Note(uint8_t chan=0, double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40); + Note(uint8_t chan=0, EventTime time=0, EventLength len=0, uint8_t note=0, uint8_t vel=0x40); Note(const Note& copy); ~Note(); @@ -40,26 +40,26 @@ public: inline bool operator==(const Note& other) { return time() == other.time() && note() == other.note() && - duration() == other.duration() && + length() == other.length() && velocity() == other.velocity() && channel() == other.channel(); } - inline double time() const { return _on_event.time(); } - inline double end_time() const { return _off_event.time(); } - inline uint8_t note() const { return _on_event.note(); } - inline uint8_t velocity() const { return _on_event.velocity(); } - inline double duration() const { return _off_event.time() - _on_event.time(); } - inline uint8_t channel() const { + inline EventTime time() const { return _on_event.time(); } + inline EventTime end_time() const { return _off_event.time(); } + inline uint8_t note() const { return _on_event.note(); } + inline uint8_t velocity() const { return _on_event.velocity(); } + inline EventLength length() const { return _off_event.time() - _on_event.time(); } + inline uint8_t channel() const { assert(_on_event.channel() == _off_event.channel()); return _on_event.channel(); } - inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; } - inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; } - inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; } - inline void set_duration(double d) { _off_event.time() = _on_event.time() + d; } - inline void set_channel(uint8_t c) { _on_event.set_channel(c); _off_event.set_channel(c); } + inline void set_time(EventTime t) { _off_event.time() = t + length(); _on_event.time() = t; } + inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; } + inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; } + inline void set_length(EventLength l) { _off_event.time() = _on_event.time() + l; } + inline void set_channel(uint8_t c) { _on_event.set_channel(c); _off_event.set_channel(c); } inline Event& on_event() { return _on_event; } inline const Event& on_event() const { return _on_event; } @@ -68,8 +68,8 @@ public: private: // Event buffers are self-contained - Event _on_event; - Event _off_event; + MIDIEvent _on_event; + MIDIEvent _off_event; }; diff --git a/libs/evoral/evoral/RingBuffer.hpp b/libs/evoral/evoral/RingBuffer.hpp new file mode 100644 index 0000000000..a27e97adb2 --- /dev/null +++ b/libs/evoral/evoral/RingBuffer.hpp @@ -0,0 +1,226 @@ +/* This file is part of Evoral. + * Copyright (C) 2008 Dave Robillard + * + * Evoral 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. + * + * Evoral 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 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVORAL_RING_BUFFER_HPP +#define EVORAL_RING_BUFFER_HPP + +#include +#include +#include + +namespace Evoral { + + +/** A lock-free RingBuffer. + * Read/Write realtime safe. + * Single-reader Single-writer thread safe. + */ +template +class RingBuffer { +public: + + /** @param size Size in bytes. + */ + RingBuffer(size_t size) + : _size(size) + , _buf(new T[size]) + { + reset(); + assert(read_space() == 0); + assert(write_space() == size - 1); + } + + virtual ~RingBuffer() { + delete[] _buf; + } + + /** Reset(empty) the ringbuffer. + * NOT thread safe. + */ + void reset() { + g_atomic_int_set(&_write_ptr, 0); + g_atomic_int_set(&_read_ptr, 0); + } + + size_t write_space() const { + const size_t w = g_atomic_int_get(&_write_ptr); + const size_t 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() const { + const size_t w = g_atomic_int_get(&_write_ptr); + const size_t 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; } + + size_t peek(size_t size, T* dst); + bool full_peek(size_t size, T* dst); + + size_t read(size_t size, T* dst); + bool full_read(size_t size, T* dst); + + bool skip(size_t size); + + void write(size_t size, const T* src); + +protected: + mutable int _write_ptr; + mutable int _read_ptr; + + size_t _size; ///< Size (capacity) in bytes + T* _buf; ///< size, event, size, event... +}; + + +/** Peek at the ringbuffer (read w/o advancing read pointer). + * + * Note that a full read may not be done if the data wraps around. + * Caller must check return value and call again if necessary, or use the + * full_peek method which does this automatically. + */ +template +size_t +RingBuffer::peek(size_t size, T* dst) +{ + const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); + + const size_t read_size = (priv_read_ptr + size < _size) + ? size + : _size - priv_read_ptr; + + memcpy(dst, &_buf[priv_read_ptr], read_size); + + return read_size; +} + + +template +bool +RingBuffer::full_peek(size_t size, T* dst) +{ + if (read_space() < size) { + return false; + } + + const size_t read_size = peek(size, dst); + + if (read_size < size) { + peek(size - read_size, dst + read_size); + } + + return true; +} + + +/** Read from the ringbuffer. + * + * Note that a full read may not be done if the data wraps around. + * Caller must check return value and call again if necessary, or use the + * full_read method which does this automatically. + */ +template +size_t +RingBuffer::read(size_t size, T* dst) +{ + const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); + + const size_t read_size = (priv_read_ptr + size < _size) + ? size + : _size - priv_read_ptr; + + memcpy(dst, &_buf[priv_read_ptr], read_size); + + g_atomic_int_set(&_read_ptr, (priv_read_ptr + read_size) % _size); + + return read_size; +} + + +template +bool +RingBuffer::full_read(size_t size, T* dst) +{ + if (read_space() < size) { + return false; + } + + const size_t read_size = read(size, dst); + + if (read_size < size) { + read(size - read_size, dst + read_size); + } + + return true; +} + + +template +bool +RingBuffer::skip(size_t size) +{ + if (read_space() < size) { + std::cerr << "WARNING: Attempt to skip past end of MIDI ring buffer" << std::endl; + return false; + } + + const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr); + g_atomic_int_set(&_read_ptr, (priv_read_ptr + size) % _size); + + return true; +} + + +template +inline void +RingBuffer::write(size_t size, const T* src) +{ + const size_t priv_write_ptr = g_atomic_int_get(&_write_ptr); + + if (priv_write_ptr + size <= _size) { + memcpy(&_buf[priv_write_ptr], src, size); + g_atomic_int_set(&_write_ptr, (priv_write_ptr + size) % _size); + } else { + const size_t this_size = _size - priv_write_ptr; + assert(this_size < size); + assert(priv_write_ptr + this_size <= _size); + memcpy(&_buf[priv_write_ptr], src, this_size); + memcpy(&_buf[0], src+this_size, size - this_size); + g_atomic_int_set(&_write_ptr, size - this_size); + } +} + + +} // namespace Evoral + +#endif // EVORAL_RING_BUFFER_HPP + + diff --git a/libs/evoral/evoral/Sequence.hpp b/libs/evoral/evoral/Sequence.hpp index 73ddaca21b..b029e51f33 100644 --- a/libs/evoral/evoral/Sequence.hpp +++ b/libs/evoral/evoral/Sequence.hpp @@ -30,30 +30,28 @@ #include #include #include +#include namespace Evoral { +class TypeMap; class EventSink; class Note; class Event; -class ControlList; - -/** This class keeps track of the current x and y for a control +/** An iterator over (the x axis of) a 2-d double coordinate space. */ class ControlIterator { public: + ControlIterator(boost::shared_ptr al, double ax, double ay) + : list(al) + , x(ax) + , y(ay) + {} + boost::shared_ptr list; double x; double y; - - ControlIterator(boost::shared_ptr a_list, - double a_x, - double a_y) - : list(a_list) - , x(a_x) - , y(a_y) - {} }; @@ -62,7 +60,7 @@ public: * Controller data is represented as a list of time-stamped float values. */ class Sequence : virtual public ControlSet { public: - Sequence(size_t size=0); + Sequence(const TypeMap& type_map, size_t size=0); bool read_locked() { return _read_iter.locked(); } @@ -115,7 +113,7 @@ public: /** Read iterator */ class const_iterator { public: - const_iterator(const Sequence& seq, double t); + const_iterator(const Sequence& seq, EventTime t); ~const_iterator(); inline bool valid() const { return !_is_end && _event; } @@ -144,18 +142,20 @@ public: mutable ActiveNotes _active_notes; - bool _is_end; - bool _locked; - Notes::const_iterator _note_iter; - std::vector _control_iters; - std::vector::iterator _control_iter; + typedef std::vector ControlIterators; + + bool _is_end; + bool _locked; + Notes::const_iterator _note_iter; + ControlIterators _control_iters; + ControlIterators::iterator _control_iter; }; - const_iterator begin(double t=0) const { return const_iterator(*this, t); } - const const_iterator& end() const { return _end_iter; } + const_iterator begin(EventTime t=0) const { return const_iterator(*this, t); } + const const_iterator& end() const { return _end_iter; } - void read_seek(double t) { _read_iter = begin(t); } - double read_time() const { return _read_iter.valid() ? _read_iter->time() : 0.0; } + void read_seek(EventTime t) { _read_iter = begin(t); } + EventTime read_time() const { return _read_iter.valid() ? _read_iter->time() : 0.0; } bool control_to_midi_event(boost::shared_ptr& ev, const ControlIterator& iter) const; @@ -177,13 +177,15 @@ protected: private: friend class const_iterator; - void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity); - void append_note_off_unlocked(uint8_t chan, double time, uint8_t note); - void append_control_unlocked(const Parameter& param, double time, double value); + void append_note_on_unlocked(uint8_t chan, EventTime time, uint8_t note, uint8_t velocity); + void append_note_off_unlocked(uint8_t chan, EventTime time, uint8_t note); + void append_control_unlocked(const Parameter& param, EventTime time, double value); mutable Glib::RWLock _lock; - Notes _notes; + const TypeMap& _type_map; + + Notes _notes; typedef std::vector WriteNotes; WriteNotes _write_notes[16]; @@ -196,14 +198,6 @@ private: mutable nframes_t _next_read; bool _percussive; - /** FIXME: Make fully dynamic, map to URIs */ - enum EventTypes { - midi_cc_type=0x20, // FIXME FIXME FIXME eeww - midi_pc_type, - midi_pb_type, - midi_ca_type - }; - typedef std::priority_queue< boost::shared_ptr, std::deque< boost::shared_ptr >, LaterNoteEndComparator> diff --git a/libs/evoral/evoral/TypeMap.hpp b/libs/evoral/evoral/TypeMap.hpp new file mode 100644 index 0000000000..3d082fc4ef --- /dev/null +++ b/libs/evoral/evoral/TypeMap.hpp @@ -0,0 +1,50 @@ +/* This file is part of Evoral. + * Copyright (C) 2008 Dave Robillard + * Copyright (C) 2000-2008 Paul Davis + * + * Evoral 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. + * + * Evoral 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 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef EVORAL_TYPE_MAP_HPP +#define EVORAL_TYPE_MAP_HPP + +#include + +namespace Evoral { + +class Parameter; + +/** The applications passes one of these which provide the implementation + * with required information about event types in an opaque, type neutral way + */ +class TypeMap { +public: + /** Return true iff the type is a MIDI event. + * The contents of the event will be used for specific ID + */ + virtual bool type_is_midi(uint32_t type) const = 0; + + /** Return the MIDI type (ie status byte with channel 0) for a + * parameter, or 0 if parameter can not be expressed as a MIDI event + */ + virtual uint8_t parameter_midi_type(const Parameter& param) const = 0; + + /** The type ID for a MIDI event with the given status byte + */ + virtual uint32_t midi_event_type(uint8_t status) const = 0; +}; + +} // namespace Evoral + +#endif // EVORAL_TYPE_MAP_HPP diff --git a/libs/evoral/evoral/types.hpp b/libs/evoral/evoral/types.hpp index e078a69a03..a722484186 100644 --- a/libs/evoral/evoral/types.hpp +++ b/libs/evoral/evoral/types.hpp @@ -19,6 +19,8 @@ #ifndef EVORAL_TYPES_HPP #define EVORAL_TYPES_HPP +#include + /** Frame count (i.e. length of time in audio frames) */ typedef uint32_t nframes_t; @@ -28,4 +30,13 @@ typedef double timestamp_t; /** Duration of time in timestamp_t units */ typedef timestamp_t timedur_t; +/** Time stamp of an event */ +typedef double EventTime; + +/** Time stamp of an event */ +typedef double EventLength; + +/** Type of an event (opaque, mapped by application) */ +typedef uint32_t EventType; + #endif // EVORAL_TYPES_HPP diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index fd0a2e52bd..63d96083d4 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -203,7 +203,7 @@ ControlList::reposition_for_rt_add (double when) void ControlList::rt_add (double when, double value) { - cerr << "RT: alist " << this << " add " << value << " @ " << when << endl; + //cerr << "RT: alist " << this << " add " << value << " @ " << when << endl; { Glib::Mutex::Lock lm (_lock); @@ -1058,6 +1058,7 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d * (Optimize for immediate call this cycle within range) */ _search_cache.left = x; //++_search_cache.range.first; + assert(inclusive ? x >= start : x > start); return true; } @@ -1069,6 +1070,7 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d * (Optimize for immediate call this cycle within range) */ _search_cache.left = x; //++_search_cache.range.first; + assert(inclusive ? x >= start : x > start); return true; } else { return false; @@ -1098,9 +1100,9 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d x = first->when + (y - first->value) / (double)slope; } - /*cerr << first->value << " @ " << first->when << " ... " + cerr << first->value << " @ " << first->when << " ... " << next->value << " @ " << next->when - << " = " << y << " @ " << x << endl;*/ + << " = " << y << " @ " << x << endl; assert( (y >= first->value && y <= next->value) || (y <= first->value && y >= next->value) ); @@ -1111,9 +1113,8 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d /* Move left of cache to this point * (Optimize for immediate call this cycle within range) */ _search_cache.left = x; - + assert(inclusive ? x >= start : x > start); return true; - } else { return false; } diff --git a/libs/evoral/src/Event.cpp b/libs/evoral/src/Event.cpp index 71d1808628..99a94e0215 100644 --- a/libs/evoral/src/Event.cpp +++ b/libs/evoral/src/Event.cpp @@ -20,9 +20,11 @@ namespace Evoral { -#ifdef EVENT_ALLOW_ALLOC -Event::Event(double t, uint32_t s, uint8_t* b, bool owns_buffer) - : _time(t) +#ifdef EVORAL_EVENT_ALLOC + +Event::Event(uint32_t tid, EventTime t, uint32_t s, uint8_t* b, bool owns_buffer) + : _type(tid) + , _time(t) , _size(s) , _buffer(b) , _owns_buffer(owns_buffer) @@ -38,7 +40,8 @@ Event::Event(double t, uint32_t s, uint8_t* b, bool owns_buffer) } Event::Event(const Event& copy, bool owns_buffer) - : _time(copy._time) + : _type(copy._type) + , _time(copy._time) , _size(copy._size) , _buffer(copy._buffer) , _owns_buffer(owns_buffer) @@ -59,49 +62,7 @@ Event::~Event() { } } -#endif // EVENT_ALLOW_ALLOC - -#ifdef EVENT_WITH_XML - -Event::Event(const XMLNode& event) -{ - string name = event.name(); - - if (name == "ControlChange") { - - } else if (name == "ProgramChange") { - - } -} - - -boost::shared_ptr -Event::to_xml() const -{ - XMLNode *result = 0; - - switch (type()) { - case MIDI_CMD_CONTROL: - result = new XMLNode("ControlChange"); - result->add_property("Channel", channel()); - result->add_property("Control", cc_number()); - result->add_property("Value", cc_value()); - break; - - case MIDI_CMD_PGM_CHANGE: - result = new XMLNode("ProgramChange"); - result->add_property("Channel", channel()); - result->add_property("Number", pgm_number()); - break; - - default: - // The implementation is continued as needed - break; - } - - return boost::shared_ptr(result); -} -#endif // EVENT_WITH_XML +#endif // EVORAL_EVENT_ALLOC } // namespace MIDI diff --git a/libs/evoral/src/MIDIEvent.cpp b/libs/evoral/src/MIDIEvent.cpp new file mode 100644 index 0000000000..35161515d6 --- /dev/null +++ b/libs/evoral/src/MIDIEvent.cpp @@ -0,0 +1,67 @@ +/* This file is part of Evoral. + * Copyright (C) 2008 Dave Robillard + * Copyright (C) 2000-2008 Paul Davis + * + * Evoral 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. + * + * Evoral 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 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +namespace Evoral { + +#ifdef EVORAL_MIDI_XML + +MIDIEvent::MIDIEvent(const XMLNode& event) +{ + string name = event.name(); + + if (name == "ControlChange") { + + } else if (name == "ProgramChange") { + + } +} + + +boost::shared_ptr +MIDIEvent::to_xml() const +{ + XMLNode *result = 0; + + switch (type()) { + case MIDI_CMD_CONTROL: + result = new XMLNode("ControlChange"); + result->add_property("Channel", channel()); + result->add_property("Control", cc_number()); + result->add_property("Value", cc_value()); + break; + + case MIDI_CMD_PGM_CHANGE: + result = new XMLNode("ProgramChange"); + result->add_property("Channel", channel()); + result->add_property("Number", pgm_number()); + break; + + default: + // The implementation is continued as needed + break; + } + + return boost::shared_ptr(result); +} + +#endif // EVORAL_MIDI_XML + +} // namespace MIDI + diff --git a/libs/evoral/src/Note.cpp b/libs/evoral/src/Note.cpp index 88be34fb36..894945c422 100644 --- a/libs/evoral/src/Note.cpp +++ b/libs/evoral/src/Note.cpp @@ -21,9 +21,10 @@ namespace Evoral { -Note::Note(uint8_t chan, double t, double d, uint8_t n, uint8_t v) - : _on_event(t, 3, NULL, true) - , _off_event(t + d, 3, NULL, true) +Note::Note(uint8_t chan, EventTime t, EventLength l, uint8_t n, uint8_t v) + // FIXME: types? + : _on_event(0xDE, t, 3, NULL, true) + , _off_event(0xAD, t + l, 3, NULL, true) { assert(chan < 16); @@ -36,7 +37,7 @@ Note::Note(uint8_t chan, double t, double d, uint8_t n, uint8_t v) _off_event.buffer()[2] = 0x40; assert(time() == t); - assert(duration() == d); + assert(length() == l); assert(note() == n); assert(velocity() == v); assert(_on_event.channel() == _off_event.channel()); @@ -64,7 +65,7 @@ Note::Note(const Note& copy) assert(end_time() == copy.end_time()); assert(note() == copy.note()); assert(velocity() == copy.velocity()); - assert(duration() == copy.duration()); + assert(length() == copy.length()); assert(_on_event.channel() == _off_event.channel()); assert(channel() == copy.channel()); } @@ -80,20 +81,12 @@ Note::operator=(const Note& copy) { _on_event = copy._on_event; _off_event = copy._off_event; - /*_on_event.time = copy._on_event.time; - assert(copy._on_event.size == 3); - memcpy(_on_event_buffer, copy._on_event_buffer, 3); - - _off_event.time = copy._off_event.time; - assert(copy._off_event.size == 3); - memcpy(_off_event_buffer, copy._off_event_buffer, 3); - */ assert(time() == copy.time()); assert(end_time() == copy.end_time()); assert(note() == copy.note()); assert(velocity() == copy.velocity()); - assert(duration() == copy.duration()); + assert(length() == copy.length()); assert(_on_event.channel() == _off_event.channel()); assert(channel() == copy.channel()); diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp index a41e191eba..07a0601550 100644 --- a/libs/evoral/src/Sequence.cpp +++ b/libs/evoral/src/Sequence.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace std; @@ -63,7 +64,7 @@ static ostream& errorout = cerr; // Read iterator (const_iterator) -Sequence::const_iterator::const_iterator(const Sequence& seq, double t) +Sequence::const_iterator::const_iterator(const Sequence& seq, EventTime t) : _seq(&seq) , _is_end( (t == DBL_MAX) || seq.empty() ) , _locked( !_is_end ) @@ -76,8 +77,8 @@ Sequence::const_iterator::const_iterator(const Sequence& seq, double t) seq.read_lock(); - _note_iter = seq.notes().end(); // find first note which begins after t + _note_iter = seq.notes().end(); for (Sequence::Notes::const_iterator i = seq.notes().begin(); i != seq.notes().end(); ++i) { if ((*i)->time() >= t) { _note_iter = i; @@ -124,26 +125,18 @@ Sequence::const_iterator::const_iterator(const Sequence& seq, double t) } } - if (_note_iter != seq.notes().end()) { + if (_note_iter != seq.notes().end() + && (*_note_iter)->on_event().time() >= t + && (!earliest_control.list + || (*_note_iter)->on_event().time() < earliest_control.x)) { + debugout << "Reading note on event @ " << (*_note_iter)->on_event().time() << endl; _event = boost::shared_ptr(new Event((*_note_iter)->on_event(), true)); - } - - double time = DBL_MAX; - // in case we have no notes in the region, we still want to get controller messages - if (_event.get()) { - time = _event->time(); - // if the note is going to make it this turn, advance _note_iter - if (earliest_control.x > time) { - _active_notes.push(*_note_iter); - ++_note_iter; - } - } - - // <=, because we probably would want to send control events first - if (earliest_control.list.get() && earliest_control.x <= time) { - seq.control_to_midi_event(_event, earliest_control); - } else { + _active_notes.push(*_note_iter); + ++_note_iter; _control_iter = _control_iters.end(); + } else if (earliest_control.list) { + debugout << "Reading control event @ " << earliest_control.x << endl; + seq.control_to_midi_event(_event, earliest_control); } if ( (! _event.get()) || _event->size() == 0) { @@ -158,11 +151,12 @@ Sequence::const_iterator::const_iterator(const Sequence& seq, double t) _locked = false; } } else { - debugout << "New Iterator = " << hex << _event->type(); + debugout << "New Iterator = " << _event->event_type(); + debugout << " : " << hex << (int)((MIDIEvent*)_event.get())->type(); debugout << " @ " << _event->time() << endl; } - assert(_is_end || (_event->buffer() && _event->buffer()[0] != '\0')); + //assert(_is_end || (_event->buffer() && _event->buffer()[0] != '\0')); } Sequence::const_iterator::~const_iterator() @@ -179,37 +173,42 @@ Sequence::const_iterator& Sequence::const_iterator::operator++() throw std::logic_error("Attempt to iterate past end of Sequence"); } - assert(_event->buffer() && _event->buffer()[0] != '\0'); + debugout << "Iterator ++" << endl; + assert(_event->buffer() && _event->size() > 0); + + const MIDIEvent& ev = *((MIDIEvent*)_event.get()); //debugout << "const_iterator::operator++: " << _event->to_string() << endl; - if (! (_event->is_note() || _event->is_cc() || _event->is_pgm_change() - || _event->is_pitch_bender() || _event->is_channel_aftertouch()) ) { - errorout << "Unknown event type: " << hex << int(_event->buffer()[0]) - << int(_event->buffer()[1]) << int(_event->buffer()[2]) << endl; + if (! (ev.is_note() || ev.is_cc() || ev.is_pgm_change() + || ev.is_pitch_bender() || ev.is_channel_pressure()) ) { + errorout << "Unknown event type: " << hex << int(ev.buffer()[0]) + << int(ev.buffer()[1]) << int(ev.buffer()[2]) << endl; } - assert((_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch())); + assert((ev.is_note() || ev.is_cc() || ev.is_pgm_change() || ev.is_pitch_bender() || ev.is_channel_pressure())); // Increment past current control event - if (!_event->is_note() && _control_iter != _control_iters.end() && _control_iter->list.get()) { + if (!ev.is_note() && _control_iter != _control_iters.end() && _control_iter->list.get()) { double x = 0.0, y = 0.0; const bool ret = _control_iter->list->rt_safe_earliest_event_unlocked( _control_iter->x, DBL_MAX, x, y, false); + assert(!ret || x > _control_iter->x); + if (ret) { _control_iter->x = x; _control_iter->y = y; } else { _control_iter->list.reset(); _control_iter->x = DBL_MAX; + _control_iter->y = DBL_MAX; } } _control_iter = _control_iters.begin(); // find the _control_iter with the earliest event time - for (std::vector::iterator i = _control_iters.begin(); - i != _control_iters.end(); ++i) { + for (ControlIterators::iterator i = _control_iters.begin(); i != _control_iters.end(); ++i) { if (i->x < _control_iter->x) { _control_iter = i; } @@ -218,7 +217,7 @@ Sequence::const_iterator& Sequence::const_iterator::operator++() enum Type {NIL, NOTE_ON, NOTE_OFF, CONTROL}; Type type = NIL; - double t = 0; + EventTime t = 0; // Next earliest note on if (_note_iter != _seq->notes().end()) { @@ -228,7 +227,7 @@ Sequence::const_iterator& Sequence::const_iterator::operator++() // Use the next earliest note off iff it's earlier than the note on if (!_seq->percussive() && (! _active_notes.empty())) { - if (type == NIL || _active_notes.top()->end_time() <= (*_note_iter)->time()) { + if (type == NIL || _active_notes.top()->end_time() <= t) { type = NOTE_OFF; t = _active_notes.top()->end_time(); } @@ -258,7 +257,7 @@ Sequence::const_iterator& Sequence::const_iterator::operator++() _is_end = true; } - assert(_is_end || _event->size() > 0); + assert(_is_end || (_event->size() > 0 && _event->buffer() && _event->buffer()[0] != '\0')); return *this; } @@ -289,8 +288,16 @@ Sequence::const_iterator::operator=(const const_iterator& other) size_t index = other._control_iter - other._control_iters.begin(); _control_iter = _control_iters.begin() + index; - if (!_is_end) { - _event = boost::shared_ptr(new Event(*other._event, true)); + if (!_is_end && other._event) { + if (_event) { + *_event = *other._event.get(); + } else { + _event = boost::shared_ptr(new Event(*other._event, true)); + } + } else { + if (_event) { + _event->clear(); + } } return *this; @@ -298,9 +305,10 @@ Sequence::const_iterator::operator=(const const_iterator& other) // Sequence -Sequence::Sequence(size_t size) +Sequence::Sequence(const TypeMap& type_map, size_t size) : _read_iter(*this, DBL_MAX) , _edited(false) + , _type_map(type_map) , _notes(size) , _writing(false) , _end_iter(*this, DBL_MAX) @@ -319,16 +327,15 @@ Sequence::Sequence(size_t size) size_t Sequence::read(EventSink& dst, timestamp_t start, timedur_t nframes, timestamp_t offset) const { - debugout << this << " read ev @ " << start << " * " << nframes << " + " << offset << endl; + debugout << this << " read @ " << start << " * " << nframes << " + " << offset << endl; debugout << this << " # notes: " << n_notes() << endl; - debugout << this << " controls: " << &_controls << endl; debugout << this << " # controls: " << _controls.size() << endl; size_t read_events = 0; if (start != _next_read) { - _read_iter = const_iterator(*this, (double)start); debugout << "Repositioning iterator from " << _next_read << " to " << start << endl; + _read_iter = const_iterator(*this, (double)start); } else { debugout << "Using cached iterator at " << _next_read << endl; } @@ -339,14 +346,15 @@ Sequence::read(EventSink& dst, timestamp_t start, timedur_t nframes, timestamp_t assert(_read_iter->size() > 0); assert(_read_iter->buffer()); dst.write(_read_iter->time() + offset, + _read_iter->event_type(), _read_iter->size(), _read_iter->buffer()); - debugout << this << " read event @ " << _read_iter->time() - << " type: " << hex << int(_read_iter->type()) << dec - << " note: " << int(_read_iter->note()) - << " velocity: " << int(_read_iter->velocity()) - << endl; + debugout << this << " read event type " << _read_iter->event_type() + << " @ " << _read_iter->time() << " : "; + for (size_t i = 0; i < _read_iter->size(); ++i) + debugout << hex << (int)_read_iter->buffer()[i]; + debugout << endl; ++_read_iter; ++read_events; @@ -357,18 +365,22 @@ Sequence::read(EventSink& dst, timestamp_t start, timedur_t nframes, timestamp_t /** Write the controller event pointed to by \a iter to \a ev. * The buffer of \a ev will be allocated or resized as necessary. + * The event_type of \a ev should be set to the expected output type. * \return true on success */ bool Sequence::control_to_midi_event(boost::shared_ptr& ev, const ControlIterator& iter) const { assert(iter.list.get()); + const uint32_t event_type = iter.list->parameter().type(); if (!ev) { - ev = boost::shared_ptr(new Event(0, 3, NULL, true)); + ev = boost::shared_ptr(new Event(event_type, 0, 3, NULL, true)); } - switch (iter.list->parameter().type()) { - case midi_cc_type: + uint8_t midi_type = _type_map.parameter_midi_type(iter.list->parameter()); + ev->set_event_type(_type_map.midi_event_type(midi_type)); + switch (midi_type) { + case MIDI_CMD_CONTROL: assert(iter.list.get()); assert(iter.list->parameter().channel() < 16); assert(iter.list->parameter().id() <= INT8_MAX); @@ -381,10 +393,9 @@ Sequence::control_to_midi_event(boost::shared_ptr& ev, const ControlItera ev->buffer()[2] = (uint8_t)iter.y; break; - case midi_pc_type: + case MIDI_CMD_PGM_CHANGE: assert(iter.list.get()); assert(iter.list->parameter().channel() < 16); - assert(iter.list->parameter().id() == 0); assert(iter.y <= INT8_MAX); ev->time() = iter.x; @@ -393,10 +404,9 @@ Sequence::control_to_midi_event(boost::shared_ptr& ev, const ControlItera ev->buffer()[1] = (uint8_t)iter.y; break; - case midi_pb_type: + case MIDI_CMD_BENDER: assert(iter.list.get()); assert(iter.list->parameter().channel() < 16); - assert(iter.list->parameter().id() == 0); assert(iter.y < (1<<14)); ev->time() = iter.x; @@ -406,10 +416,9 @@ Sequence::control_to_midi_event(boost::shared_ptr& ev, const ControlItera ev->buffer()[2] = (uint16_t(iter.y) >> 7) & 0x7F; // MSB break; - case midi_ca_type: + case MIDI_CMD_CHANNEL_PRESSURE: assert(iter.list.get()); assert(iter.list->parameter().channel() < 16); - assert(iter.list->parameter().id() == 0); assert(iter.y <= INT8_MAX); ev->time() = iter.x; @@ -441,10 +450,10 @@ Sequence::clear() /** Begin a write of events to the model. * - * If \a mode is Sustained, complete notes with duration are constructed as note + * If \a mode is Sustained, complete notes with length are constructed as note * on/off events are received. Otherwise (Percussive), only note on events are * stored; note off events are discarded entirely and all contained notes will - * have duration 0. + * have length 0. */ void Sequence::start_write() @@ -463,7 +472,7 @@ Sequence::start_write() * * If \a delete_stuck is true and the current mode is Sustained, note on events * that were never resolved with a corresonding note off will be deleted. - * Otherwise they will remain as notes with duration 0. + * Otherwise they will remain as notes with length 0. */ void Sequence::end_write(bool delete_stuck) @@ -475,7 +484,7 @@ Sequence::end_write(bool delete_stuck) if (!_percussive && delete_stuck) { for (Notes::iterator n = _notes.begin(); n != _notes.end() ;) { - if ((*n)->duration() == 0) { + if ((*n)->length() == 0) { errorout << "WARNING: Stuck note lost: " << (*n)->note() << endl; n = _notes.erase(n); // we have to break here because erase invalidates the iterator @@ -509,10 +518,12 @@ Sequence::end_write(bool delete_stuck) * and MUST be >= the latest event currently in the model. */ void -Sequence::append(const Event& ev) +Sequence::append(const Event& event) { write_lock(); _edited = true; + + const MIDIEvent& ev = (const MIDIEvent&)event; assert(_notes.empty() || ev.time() >= _notes.back()->time()); assert(_writing); @@ -522,32 +533,34 @@ Sequence::append(const Event& ev) ev.velocity()); } else if (ev.is_note_off()) { append_note_off_unlocked(ev.channel(), ev.time(), ev.note()); + } else if (!_type_map.type_is_midi(ev.event_type())) { + printf("WARNING: Sequence: Unknown event type %X\n", ev.event_type()); } else if (ev.is_cc()) { append_control_unlocked( - Evoral::MIDI::ContinuousController(midi_cc_type, ev.channel(), ev.cc_number()), + Evoral::MIDI::ContinuousController(ev.event_type(), ev.channel(), ev.cc_number()), ev.time(), ev.cc_value()); } else if (ev.is_pgm_change()) { append_control_unlocked( - Evoral::MIDI::ProgramChange(midi_pc_type, ev.channel()), + Evoral::MIDI::ProgramChange(ev.event_type(), ev.channel()), ev.time(), ev.pgm_number()); } else if (ev.is_pitch_bender()) { append_control_unlocked( - Evoral::MIDI::PitchBender(midi_pb_type, ev.channel()), + Evoral::MIDI::PitchBender(ev.event_type(), ev.channel()), ev.time(), double( (0x7F & ev.pitch_bender_msb()) << 7 - | (0x7F & ev.pitch_bender_lsb()) )); - } else if (ev.is_channel_aftertouch()) { + | (0x7F & ev.pitch_bender_lsb()) )); + } else if (ev.is_channel_pressure()) { append_control_unlocked( - Evoral::MIDI::ChannelAftertouch(midi_ca_type, ev.channel()), - ev.time(), ev.channel_aftertouch()); + Evoral::MIDI::ChannelPressure(ev.event_type(), ev.channel()), + ev.time(), ev.channel_pressure()); } else { - printf("WARNING: Sequence: Unknown event type %X\n", ev.type()); + printf("WARNING: Sequence: Unknown MIDI event type %X\n", ev.type()); } write_unlock(); } void -Sequence::append_note_on_unlocked(uint8_t chan, double time, uint8_t note_num, uint8_t velocity) +Sequence::append_note_on_unlocked(uint8_t chan, EventTime time, uint8_t note_num, uint8_t velocity) { debugout << this << " c" << (int)chan << " note " << (int)note_num << " off @ " << time << endl; assert(note_num <= 127); @@ -566,7 +579,7 @@ Sequence::append_note_on_unlocked(uint8_t chan, double time, uint8_t note_num, u } void -Sequence::append_note_off_unlocked(uint8_t chan, double time, uint8_t note_num) +Sequence::append_note_off_unlocked(uint8_t chan, EventTime time, uint8_t note_num) { debugout << this << " c" << (int)chan << " note " << (int)note_num << " off @ " << time << endl; assert(note_num <= 127); @@ -591,9 +604,9 @@ Sequence::append_note_off_unlocked(uint8_t chan, double time, uint8_t note_num) Note& note = *_notes[*n].get(); if (note.note() == note_num) { assert(time >= note.time()); - note.set_duration(time - note.time()); + note.set_length(time - note.time()); _write_notes[chan].erase(n); - debugout << "resolved note, duration: " << note.duration() << endl; + debugout << "resolved note, length: " << note.length() << endl; resolved = true; break; } @@ -606,10 +619,9 @@ Sequence::append_note_off_unlocked(uint8_t chan, double time, uint8_t note_num) } void -Sequence::append_control_unlocked(const Parameter& param, double time, double value) +Sequence::append_control_unlocked(const Parameter& param, EventTime time, double value) { - debugout << this << " " << param.symbol() << " @ " << time << " = " << value - << " controls: " << &_controls + debugout << this << " " << param.symbol() << " @ " << time << " \t= \t" << value << " # controls: " << _controls.size() << endl; control(param, true)->list()->rt_add(time, value); } @@ -637,8 +649,8 @@ Sequence::remove_note_unlocked(const boost::shared_ptr note) // persisted undo does not work, because of rounding errors in the // event times after saving/restoring to/from MIDI files /*cerr << "======================================= " << endl; - cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.velocity()) << endl; - cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.duration()) << "-- #" << int(_note.velocity()) << endl; + cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.length()) << "-- #" << int(_n.velocity()) << endl; + cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.length()) << "-- #" << int(_note.velocity()) << endl; cerr << "Equal: " << bool(_n == _note) << endl; cerr << endl << endl;*/ if (_n == _note) { diff --git a/libs/midi++2/midi++/event.h b/libs/midi++2/midi++/event.h index 8973ef48bd..24a8db37ee 100644 --- a/libs/midi++2/midi++/event.h +++ b/libs/midi++2/midi++/event.h @@ -35,11 +35,12 @@ * but MidiEvent will never deep copy and (depending on the scenario) * may not be usable in STL containers, signals, etc. */ -#define EVENT_ALLOW_ALLOC 1 +#define EVORAL_EVENT_ALLOC 1 /** Support serialisation of MIDI events to/from XML */ -#define EVENT_WITH_XML 1 +#define EVORAL_MIDI_XML 1 #include +#include #endif /* __libmidipp_midi_event_h__ */ diff --git a/libs/midi++2/midnam_patch.cc b/libs/midi++2/midnam_patch.cc index 3434db896a..d08911966b 100644 --- a/libs/midi++2/midnam_patch.cc +++ b/libs/midi++2/midnam_patch.cc @@ -17,7 +17,7 @@ Patch::get_state (void) for (PatchMidiCommands::const_iterator event = _patch_midi_commands.begin(); event != _patch_midi_commands.end(); ++event) { - commands->add_child_copy(*(event->to_xml())); + commands->add_child_copy(*((((Evoral::MIDIEvent&)*event)).to_xml())); } return *node; @@ -33,7 +33,7 @@ Patch::set_state (const XMLNode& node) assert(commands); const XMLNodeList events = commands->children(); for (XMLNodeList::const_iterator i = events.begin(); i != events.end(); ++i) { - _patch_midi_commands.push_back(*(new Evoral::Event(*(*i)))); + _patch_midi_commands.push_back(*(new Evoral::MIDIEvent(*(*i)))); } return 0;