mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-08 15:54:57 +01:00
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
This commit is contained in:
parent
ff2d51ddd8
commit
a2d2f738cb
36 changed files with 954 additions and 648 deletions
|
|
@ -20,7 +20,7 @@ CanvasProgramChange::CanvasProgramChange(
|
||||||
, _rect(0)
|
, _rect(0)
|
||||||
{
|
{
|
||||||
char pgm_str[4];
|
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 = new Text(*this, 0.0, 0.0, pgm_str);
|
||||||
_text->property_justification() = Gtk::JUSTIFY_CENTER;
|
_text->property_justification() = Gtk::JUSTIFY_CENTER;
|
||||||
_text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiProgramChangeOutline.get();
|
_text->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiProgramChangeOutline.get();
|
||||||
|
|
|
||||||
|
|
@ -366,7 +366,7 @@ MidiRegionView::canvas_event(GdkEvent* ev)
|
||||||
|
|
||||||
/** Add a note to the model, and the view, at a canvas (click) coordinate */
|
/** Add a note to the model, and the view, at a canvas (click) coordinate */
|
||||||
void
|
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<MidiTimeAxisView*>(&trackview);
|
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
|
||||||
MidiStreamView* const view = mtv->midi_view();
|
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 Meter& m = trackview.session().tempo_map().meter_at(new_note_time);
|
||||||
const Tempo& t = trackview.session().tempo_map().tempo_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
|
// 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();
|
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();
|
new_note_time = snap_to_frame(new_note_time_position_relative) + _region->start();
|
||||||
|
|
||||||
// we need to snap the duration too to be sample accurate
|
// we need to snap the length too to be sample accurate
|
||||||
nframes64_t new_note_duration = nframes_t(duration);
|
nframes64_t new_note_length = nframes_t(length);
|
||||||
new_note_duration = snap_to_frame(new_note_time_position_relative + new_note_duration) + _region->start()
|
new_note_length = snap_to_frame(new_note_time_position_relative + new_note_length) + _region->start()
|
||||||
- new_note_time;
|
- new_note_time;
|
||||||
|
|
||||||
const boost::shared_ptr<Evoral::Note> new_note(new Evoral::Note(
|
const boost::shared_ptr<Evoral::Note> 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());
|
view->update_bounds(new_note->note());
|
||||||
|
|
||||||
MidiModel::DeltaCommand* cmd = _model->new_delta_command("add 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) {
|
for (MidiModel::Notes::iterator i = notes.begin(); i != notes.end(); ++i) {
|
||||||
cerr << "NOTE time: " << (*i)->time()
|
cerr << "NOTE time: " << (*i)->time()
|
||||||
<< " pitch: " << int((*i)->note())
|
<< " pitch: " << int((*i)->note())
|
||||||
<< " duration: " << (*i)->duration()
|
<< " length: " << (*i)->length()
|
||||||
<< " end-time: " << (*i)->end_time()
|
<< " end-time: " << (*i)->end_time()
|
||||||
<< " velocity: " << int((*i)->velocity())
|
<< " velocity: " << int((*i)->velocity())
|
||||||
<< endl;
|
<< 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
|
* notes, and resolve_note should be called when the corresponding note off
|
||||||
* event arrives, to properly display the note.
|
* event arrives, to properly display the note.
|
||||||
*/
|
*/
|
||||||
|
|
@ -804,13 +804,13 @@ MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> note)
|
||||||
CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
|
CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
|
||||||
ev_rect->property_x1() = x;
|
ev_rect->property_x1() = x;
|
||||||
ev_rect->property_y1() = y1;
|
ev_rect->property_y1() = y1;
|
||||||
if (note->duration() > 0)
|
if (note->length() > 0)
|
||||||
ev_rect->property_x2() = note_endpixel;
|
ev_rect->property_x2() = note_endpixel;
|
||||||
else
|
else
|
||||||
ev_rect->property_x2() = trackview.editor.frame_to_pixel(_region->length());
|
ev_rect->property_x2() = trackview.editor.frame_to_pixel(_region->length());
|
||||||
ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height());
|
ev_rect->property_y2() = y1 + floor(midi_stream_view()->note_height());
|
||||||
|
|
||||||
if (note->duration() == 0) {
|
if (note->length() == 0) {
|
||||||
|
|
||||||
if (_active_notes) {
|
if (_active_notes) {
|
||||||
assert(note->note() < 128);
|
assert(note->note() < 128);
|
||||||
|
|
@ -819,7 +819,7 @@ MidiRegionView::add_note(const boost::shared_ptr<Evoral::Note> note)
|
||||||
if (_active_notes[note->note()]) {
|
if (_active_notes[note->note()]) {
|
||||||
CanvasNote* const old_rect = _active_notes[note->note()];
|
CanvasNote* const old_rect = _active_notes[note->note()];
|
||||||
boost::shared_ptr<Evoral::Note> old_note = old_rect->note();
|
boost::shared_ptr<Evoral::Note> 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;
|
<< "note " << (int)old_note->note() << " @ " << old_note->time() << endl;
|
||||||
/* FIXME: How large to make it? Make it a diamond? */
|
/* FIXME: How large to make it? Make it a diamond? */
|
||||||
old_rect->property_x2() = old_rect->property_x1() + 2.0;
|
old_rect->property_x2() = old_rect->property_x1() + 2.0;
|
||||||
|
|
|
||||||
|
|
@ -555,7 +555,7 @@ MidiStreamView::update_rec_regions (boost::shared_ptr<MidiModel> data, nframes_t
|
||||||
|
|
||||||
const boost::shared_ptr<Evoral::Note> note = data->note_at(i);
|
const boost::shared_ptr<Evoral::Note> 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());
|
mrv->resolve_note(note->note(), note->end_time());
|
||||||
|
|
||||||
if (note->time() + region->position() < start)
|
if (note->time() + region->position() < start)
|
||||||
|
|
|
||||||
|
|
@ -32,18 +32,18 @@ ardour.Append(CPPPATH = '#libs/surfaces/control_protocol')
|
||||||
ardour_files=Split("""
|
ardour_files=Split("""
|
||||||
amp.cc
|
amp.cc
|
||||||
analyser.cc
|
analyser.cc
|
||||||
audioanalyser.cc
|
|
||||||
audio_buffer.cc
|
audio_buffer.cc
|
||||||
audio_diskstream.cc
|
audio_diskstream.cc
|
||||||
audioengine.cc
|
|
||||||
audiofilesource.cc
|
|
||||||
audiofile_tagger.cc
|
|
||||||
audio_library.cc
|
audio_library.cc
|
||||||
audio_playlist.cc
|
audio_playlist.cc
|
||||||
audio_port.cc
|
audio_port.cc
|
||||||
|
audio_track.cc
|
||||||
|
audioanalyser.cc
|
||||||
|
audioengine.cc
|
||||||
|
audiofile_tagger.cc
|
||||||
|
audiofilesource.cc
|
||||||
audioregion.cc
|
audioregion.cc
|
||||||
audiosource.cc
|
audiosource.cc
|
||||||
audio_track.cc
|
|
||||||
auditioner.cc
|
auditioner.cc
|
||||||
auto_bundle.cc
|
auto_bundle.cc
|
||||||
automatable.cc
|
automatable.cc
|
||||||
|
|
@ -65,13 +65,14 @@ default_click.cc
|
||||||
directory_names.cc
|
directory_names.cc
|
||||||
diskstream.cc
|
diskstream.cc
|
||||||
enums.cc
|
enums.cc
|
||||||
|
event_type_map.cc
|
||||||
export_channel_configuration.cc
|
export_channel_configuration.cc
|
||||||
export_file_io.cc
|
export_file_io.cc
|
||||||
export_filename.cc
|
export_filename.cc
|
||||||
export_format_base.cc
|
export_format_base.cc
|
||||||
export_format_manager.cc
|
export_format_manager.cc
|
||||||
export_formats.cc
|
|
||||||
export_format_specification.cc
|
export_format_specification.cc
|
||||||
|
export_formats.cc
|
||||||
export_handler.cc
|
export_handler.cc
|
||||||
export_preset.cc
|
export_preset.cc
|
||||||
export_processor.cc
|
export_processor.cc
|
||||||
|
|
@ -97,6 +98,7 @@ ladspa_plugin.cc
|
||||||
location.cc
|
location.cc
|
||||||
meter.cc
|
meter.cc
|
||||||
midi_buffer.cc
|
midi_buffer.cc
|
||||||
|
midi_clock_slave.cc
|
||||||
midi_diskstream.cc
|
midi_diskstream.cc
|
||||||
midi_model.cc
|
midi_model.cc
|
||||||
midi_playlist.cc
|
midi_playlist.cc
|
||||||
|
|
@ -107,7 +109,6 @@ midi_stretch.cc
|
||||||
midi_track.cc
|
midi_track.cc
|
||||||
mix.cc
|
mix.cc
|
||||||
mtc_slave.cc
|
mtc_slave.cc
|
||||||
midi_clock_slave.cc
|
|
||||||
named_selection.cc
|
named_selection.cc
|
||||||
onset_detector.cc
|
onset_detector.cc
|
||||||
panner.cc
|
panner.cc
|
||||||
|
|
@ -131,8 +132,8 @@ reverse.cc
|
||||||
route.cc
|
route.cc
|
||||||
route_group.cc
|
route_group.cc
|
||||||
send.cc
|
send.cc
|
||||||
session_butler.cc
|
|
||||||
session.cc
|
session.cc
|
||||||
|
session_butler.cc
|
||||||
session_click.cc
|
session_click.cc
|
||||||
session_command.cc
|
session_command.cc
|
||||||
session_directory.cc
|
session_directory.cc
|
||||||
|
|
@ -150,8 +151,8 @@ silentfilesource.cc
|
||||||
smf_reader.cc
|
smf_reader.cc
|
||||||
smf_source.cc
|
smf_source.cc
|
||||||
sndfile_helpers.cc
|
sndfile_helpers.cc
|
||||||
sndfilesource.cc
|
|
||||||
sndfileimportable.cc
|
sndfileimportable.cc
|
||||||
|
sndfilesource.cc
|
||||||
source.cc
|
source.cc
|
||||||
source_factory.cc
|
source_factory.cc
|
||||||
svn_revision.cc
|
svn_revision.cc
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include <ardour/automation_list.h>
|
#include <ardour/automation_list.h>
|
||||||
#include <ardour/automation_control.h>
|
#include <ardour/automation_control.h>
|
||||||
#include <ardour/parameter.h>
|
#include <ardour/parameter.h>
|
||||||
|
#include <ardour/event_type_map.h>
|
||||||
#include <evoral/ControlSet.hpp>
|
#include <evoral/ControlSet.hpp>
|
||||||
#include <evoral/Sequence.hpp>
|
#include <evoral/Sequence.hpp>
|
||||||
|
|
||||||
|
|
@ -53,9 +54,6 @@ public:
|
||||||
virtual void add_control(boost::shared_ptr<Evoral::Control>);
|
virtual void add_control(boost::shared_ptr<Evoral::Control>);
|
||||||
|
|
||||||
virtual void automation_snapshot(nframes_t now, bool force);
|
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 void transport_stopped(nframes_t now);
|
||||||
|
|
||||||
virtual string describe_parameter(Parameter param);
|
virtual string describe_parameter(Parameter param);
|
||||||
|
|
@ -73,6 +71,11 @@ public:
|
||||||
|
|
||||||
void mark_automation_visible(Parameter, bool);
|
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) {
|
static void set_automation_interval (jack_nframes_t frames) {
|
||||||
_automation_interval = frames;
|
_automation_interval = frames;
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +116,7 @@ public:
|
||||||
AutomatableSequence(Session& s, size_t size)
|
AutomatableSequence(Session& s, size_t size)
|
||||||
: Evoral::ControlSet()
|
: Evoral::ControlSet()
|
||||||
, Automatable(s)
|
, Automatable(s)
|
||||||
, Evoral::Sequence(size)
|
, Evoral::Sequence(EventTypeMap::instance())
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
43
libs/ardour/ardour/event_type_map.h
Normal file
43
libs/ardour/ardour/event_type_map.h
Normal file
|
|
@ -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 <evoral/TypeMap.hpp>
|
||||||
|
|
||||||
|
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__ */
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ public:
|
||||||
|
|
||||||
void copy(const MidiBuffer& copy);
|
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);
|
bool push_back(const jack_midi_event_t& event);
|
||||||
uint8_t* reserve(double time, size_t size);
|
uint8_t* reserve(double time, size_t size);
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ public:
|
||||||
struct iterator {
|
struct iterator {
|
||||||
iterator(MidiBuffer& b, size_t i) : buffer(b), index(i) {}
|
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 iterator& operator++() { ++index; return *this; } // prefix
|
||||||
inline bool operator!=(const iterator& other) const { return index != other.index; }
|
inline bool operator!=(const iterator& other) const { return index != other.index; }
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ public:
|
||||||
struct const_iterator {
|
struct const_iterator {
|
||||||
const_iterator(const MidiBuffer& b, size_t i) : buffer(b), index(i) {}
|
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 const_iterator& operator++() { ++index; return *this; } // prefix
|
||||||
inline bool operator!=(const const_iterator& other) const { return index != other.index; }
|
inline bool operator!=(const const_iterator& other) const { return index != other.index; }
|
||||||
|
|
||||||
|
|
@ -80,8 +80,8 @@ private:
|
||||||
friend class iterator;
|
friend class iterator;
|
||||||
friend class const_iterator;
|
friend class const_iterator;
|
||||||
|
|
||||||
const Evoral::Event& operator[](size_t i) const { assert(i < _size); return _events[i]; }
|
const Evoral::MIDIEvent& operator[](size_t i) const { assert(i < _size); return _events[i]; }
|
||||||
Evoral::Event& operator[](size_t i) { assert(i < _size); return _events[i]; }
|
Evoral::MIDIEvent& operator[](size_t i) { assert(i < _size); return _events[i]; }
|
||||||
|
|
||||||
// FIXME: Eliminate this
|
// FIXME: Eliminate this
|
||||||
static const size_t MAX_EVENT_SIZE = 4; // bytes
|
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 */
|
/* FIXME: this is utter crap. rewrite as a flat/packed buffer like MidiRingBuffer */
|
||||||
|
|
||||||
Evoral::Event* _events; ///< Event structs that point to offsets in _data
|
Evoral::MIDIEvent* _events; ///< Event structs that point to offsets in _data
|
||||||
uint8_t* _data; ///< MIDI, straight up. No time stamps.
|
uint8_t* _data; ///< MIDI, straight up. No time stamps.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,239 +24,30 @@
|
||||||
#include <ardour/types.h>
|
#include <ardour/types.h>
|
||||||
#include <ardour/buffer.h>
|
#include <ardour/buffer.h>
|
||||||
#include <evoral/EventSink.hpp>
|
#include <evoral/EventSink.hpp>
|
||||||
|
#include <evoral/EventRingBuffer.hpp>
|
||||||
|
|
||||||
namespace ARDOUR {
|
namespace ARDOUR {
|
||||||
|
|
||||||
|
|
||||||
/* FIXME: this is probably too much inlined code */
|
/** A RingBuffer for (MIDI) events.
|
||||||
|
|
||||||
|
|
||||||
/** 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
|
* This is simply a wrapper around a raw ringbuffer which writes/reads events
|
||||||
* a bit differently than PBD::Ringbuffer. This could/should be replaced with
|
* as flat placked blobs.
|
||||||
* the PBD ringbuffer to decrease code size, but this code is tested and known to
|
* The buffer looks like this:
|
||||||
* work, so here it sits for now...
|
|
||||||
*
|
*
|
||||||
* Ignore this class, use MidiRingBuffer.
|
* [timestamp][type][size][size bytes of raw MIDI][timestamp][type][size](etc...)
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
class MidiRingBuffer : public Evoral::EventRingBuffer {
|
||||||
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<typename T>
|
|
||||||
size_t
|
|
||||||
MidiRingBufferBase<T>::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<typename T>
|
|
||||||
bool
|
|
||||||
MidiRingBufferBase<T>::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<typename T>
|
|
||||||
size_t
|
|
||||||
MidiRingBufferBase<T>::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<typename T>
|
|
||||||
bool
|
|
||||||
MidiRingBufferBase<T>::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<typename T>
|
|
||||||
bool
|
|
||||||
MidiRingBufferBase<T>::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<typename T>
|
|
||||||
inline void
|
|
||||||
MidiRingBufferBase<T>::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.
|
|
||||||
*
|
|
||||||
* This is timestamps and MIDI packed sequentially into a single buffer, similarly
|
|
||||||
* to LV2 MIDI. The buffer looks like this:
|
|
||||||
*
|
|
||||||
* [timestamp][size][size bytes of raw MIDI][timestamp][size][etc..]
|
|
||||||
*/
|
|
||||||
class MidiRingBuffer : public MidiRingBufferBase<uint8_t>, public Evoral::EventSink {
|
|
||||||
public:
|
public:
|
||||||
/** @param size Size in bytes.
|
/** @param size Size in bytes.
|
||||||
*/
|
*/
|
||||||
MidiRingBuffer(size_t size)
|
MidiRingBuffer(size_t size)
|
||||||
: MidiRingBufferBase<uint8_t>(size), _channel_mask(0x0000FFFF)
|
: Evoral::EventRingBuffer(size)
|
||||||
|
, _channel_mask(0x0000FFFF)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
size_t write(double time, uint32_t size, const uint8_t* buf);
|
inline bool read_prefix(EventTime* time, EventType* type, uint32_t* size);
|
||||||
bool read(double* time, uint32_t* size, uint8_t* buf);
|
inline bool read_contents(uint32_t size, uint8_t* buf);
|
||||||
|
|
||||||
bool read_prefix(double* time, size_t* size);
|
|
||||||
bool read_contents(size_t size, uint8_t* buf);
|
|
||||||
|
|
||||||
size_t read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset=0);
|
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<uint8_t>::full_read(sizeof(double), (uint8_t*)time);
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
success = MidiRingBufferBase<uint8_t>::full_read(sizeof(size_t), (uint8_t*)size);
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
success = MidiRingBufferBase<uint8_t>::full_read(*size, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Read the time and size of an event. This call MUST be immediately proceeded
|
/** 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).
|
* by a call to read_contents (or the read pointer will be garabage).
|
||||||
*/
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
MidiRingBuffer::read_prefix(double* time, size_t* size)
|
MidiRingBuffer::read_prefix(EventTime* time, EventType* type, uint32_t* size)
|
||||||
{
|
{
|
||||||
bool success = MidiRingBufferBase<uint8_t>::full_read(sizeof(double), (uint8_t*)time);
|
bool success = Evoral::EventRingBuffer::full_read(sizeof(EventTime), (uint8_t*)time);
|
||||||
if (success) {
|
if (success)
|
||||||
success = MidiRingBufferBase<uint8_t>::full_read(sizeof(size_t), (uint8_t*)size);
|
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;
|
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).
|
* by a call to read_prefix (or the returned even will be garabage).
|
||||||
*/
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
MidiRingBuffer::read_contents(size_t size, uint8_t* buf)
|
MidiRingBuffer::read_contents(uint32_t size, uint8_t* buf)
|
||||||
{
|
{
|
||||||
return MidiRingBufferBase<uint8_t>::full_read(size, buf);
|
return Evoral::EventRingBuffer::full_read(size, buf);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline size_t
|
|
||||||
MidiRingBuffer::write(double time, uint32_t size, const 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<uint8_t>::write(sizeof(double), (uint8_t*)&time);
|
|
||||||
MidiRingBufferBase<uint8_t>::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<uint8_t>::write(size, tmp_buf);
|
|
||||||
} else {
|
|
||||||
MidiRingBufferBase<uint8_t>::write(size, buf);
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -383,31 +117,31 @@ MidiRingBuffer::write(double time, uint32_t size, const uint8_t* buf)
|
||||||
inline size_t
|
inline size_t
|
||||||
MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset)
|
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;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
double ev_time;
|
EventTime ev_time;
|
||||||
uint32_t ev_size;
|
EventType ev_type;
|
||||||
|
uint32_t ev_size;
|
||||||
|
|
||||||
size_t count = 0;
|
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) {
|
if (ev_time > end) {
|
||||||
|
//std::cerr << "MRB: PAST END (" << ev_time << " : " << end << ")" << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = MidiRingBufferBase<uint8_t>::full_read(sizeof(double), (uint8_t*)&ev_time);
|
bool success = read_prefix(&ev_time, &ev_type, &ev_size);
|
||||||
if (success) {
|
|
||||||
success = MidiRingBufferBase<uint8_t>::full_read(sizeof(size_t), (uint8_t*)&ev_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
std::cerr << "MRB: READ ERROR (time/size)" << std::endl;
|
//std::cerr << "MRB: READ ERROR (time/type/size)" << std::endl;
|
||||||
continue;
|
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) {
|
if (is_channel_event(status) && get_channel_mode() == FilterChannels) {
|
||||||
const uint8_t channel = status & 0x0F;
|
const uint8_t channel = status & 0x0F;
|
||||||
if ( !(get_channel_mask() & (1L << channel)) ) {
|
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
|
skip(ev_size); // Advance read pointer to next event
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -426,36 +161,36 @@ MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t
|
||||||
|
|
||||||
if (ev_time >= start) {
|
if (ev_time >= start) {
|
||||||
|
|
||||||
/*std::cerr << "MRB " << this << " - Reading event, time = "
|
//std::cerr << "MRB " << this << " - Reading event, time = "
|
||||||
<< ev_time << " - " << start << " => " << ev_time - start
|
// << ev_time << " - " << start << " => " << ev_time - start
|
||||||
<< ", size = " << ev_size << std::endl;*/
|
// << ", size = " << ev_size << std::endl;
|
||||||
|
|
||||||
ev_time -= start;
|
ev_time -= start;
|
||||||
|
|
||||||
uint8_t* write_loc = dst.reserve(ev_time, ev_size);
|
uint8_t* write_loc = dst.reserve(ev_time, ev_size);
|
||||||
if (write_loc == NULL) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
success = MidiRingBufferBase<uint8_t>::full_read(ev_size, write_loc);
|
success = Evoral::EventRingBuffer::full_read(ev_size, write_loc);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
|
if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
|
||||||
write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
|
write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
|
||||||
}
|
}
|
||||||
++count;
|
++count;
|
||||||
//printf("MRB - read event at time %lf\n", ev_time);
|
//std::cerr << "MRB - read event at time " << ev_time << std::endl;
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "MRB: READ ERROR (data)" << std::endl;
|
//std::cerr << "MRB: READ ERROR (data)" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,20 +50,6 @@ public:
|
||||||
init_metadata(type);
|
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)
|
Parameter(const Evoral::Parameter& copy)
|
||||||
: Evoral::Parameter(copy)
|
: Evoral::Parameter(copy)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
61
libs/ardour/event_type_map.cc
Normal file
61
libs/ardour/event_type_map.cc
Normal file
|
|
@ -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 <ardour/types.h>
|
||||||
|
#include <ardour/event_type_map.h>
|
||||||
|
#include <evoral/Parameter.hpp>
|
||||||
|
#include <evoral/midi_events.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
@ -305,7 +305,7 @@ static void
|
||||||
write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
|
write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
|
||||||
vector<boost::shared_ptr<Source> >& newfiles)
|
vector<boost::shared_ptr<Source> >& newfiles)
|
||||||
{
|
{
|
||||||
Evoral::Event ev(0.0, 4, NULL, true);
|
Evoral::Event ev(0, 0.0, 4, NULL, true);
|
||||||
|
|
||||||
status.progress = 0.0f;
|
status.progress = 0.0f;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
// GUI needs a better MIDI meter, not much information can be
|
||||||
// expressed through peaks alone
|
// expressed through peaks alone
|
||||||
for (MidiBuffer::iterator i = bufs.get_midi(n).begin(); i != bufs.get_midi(n).end(); ++i) {
|
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()) {
|
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;
|
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);
|
//printf("V %d -> %f\n", (int)((Byte)ev.buffer[2]), this_vel);
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
|
||||||
|
|
||||||
// FIXME: slow
|
// FIXME: slow
|
||||||
for (size_t i=0; i < msrc.size(); ++i) {
|
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) {
|
if (ev.time() >= offset && ev.time() < offset+nframes) {
|
||||||
//cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl;
|
//cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl;
|
||||||
push_back(ev);
|
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)
|
* @return false if operation failed (not enough room)
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
MidiBuffer::push_back(const Evoral::Event& ev)
|
MidiBuffer::push_back(const Evoral::MIDIEvent& ev)
|
||||||
{
|
{
|
||||||
if (_size == _capacity)
|
if (_size == _capacity)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -262,8 +262,8 @@ MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
|
||||||
push_back(b[b_index]);
|
push_back(b[b_index]);
|
||||||
++b_index;
|
++b_index;
|
||||||
} else {
|
} else {
|
||||||
const Evoral::Event& a_ev = a[a_index];
|
const Evoral::MIDIEvent& a_ev = a[a_index];
|
||||||
const Evoral::Event& b_ev = b[b_index];
|
const Evoral::MIDIEvent& b_ev = b[b_index];
|
||||||
|
|
||||||
if (a_ev.time() <= b_ev.time()) {
|
if (a_ev.time() <= b_ev.time()) {
|
||||||
push_back(a_ev);
|
push_back(a_ev);
|
||||||
|
|
|
||||||
|
|
@ -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();
|
MidiBuffer::iterator port_iter = _source_port->get_midi_buffer().begin();
|
||||||
|
|
||||||
for (size_t i=0; i < to_write; ++i) {
|
for (size_t i=0; i < to_write; ++i) {
|
||||||
const Evoral::Event& ev = *port_iter;
|
const Evoral::MIDIEvent& ev = *port_iter;
|
||||||
assert(ev.buffer());
|
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;
|
++port_iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -170,9 +170,9 @@ MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Evoral::Note> note
|
||||||
time_str << int(note->time());
|
time_str << int(note->time());
|
||||||
xml_note->add_property("time", time_str.str());
|
xml_note->add_property("time", time_str.str());
|
||||||
|
|
||||||
ostringstream duration_str(ios::ate);
|
ostringstream length_str(ios::ate);
|
||||||
duration_str <<(unsigned int) note->duration();
|
length_str <<(unsigned int) note->length();
|
||||||
xml_note->add_property("duration", duration_str.str());
|
xml_note->add_property("length", length_str.str());
|
||||||
|
|
||||||
ostringstream velocity_str(ios::ate);
|
ostringstream velocity_str(ios::ate);
|
||||||
velocity_str << (unsigned int) note->velocity();
|
velocity_str << (unsigned int) note->velocity();
|
||||||
|
|
@ -195,15 +195,15 @@ boost::shared_ptr<Evoral::Note> MidiModel::DeltaCommand::unmarshal_note(XMLNode
|
||||||
istringstream time_str(xml_note->property("time")->value());
|
istringstream time_str(xml_note->property("time")->value());
|
||||||
time_str >> time;
|
time_str >> time;
|
||||||
|
|
||||||
unsigned int duration;
|
unsigned int length;
|
||||||
istringstream duration_str(xml_note->property("duration")->value());
|
istringstream length_str(xml_note->property("length")->value());
|
||||||
duration_str >> duration;
|
length_str >> length;
|
||||||
|
|
||||||
unsigned int velocity;
|
unsigned int velocity;
|
||||||
istringstream velocity_str(xml_note->property("velocity")->value());
|
istringstream velocity_str(xml_note->property("velocity")->value());
|
||||||
velocity_str >> velocity;
|
velocity_str >> velocity;
|
||||||
|
|
||||||
boost::shared_ptr<Evoral::Note> note_ptr(new Evoral::Note(channel, time, duration, note, velocity));
|
boost::shared_ptr<Evoral::Note> note_ptr(new Evoral::Note(channel, time, length, note, velocity));
|
||||||
return note_ptr;
|
return note_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -723,7 +723,8 @@ MidiTrack::write_immediate_event(size_t size, const uint8_t* buf)
|
||||||
printf("%X ", buf[i]);
|
printf("%X ", buf[i]);
|
||||||
}
|
}
|
||||||
printf("\n");*/
|
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
|
void
|
||||||
|
|
|
||||||
|
|
@ -72,12 +72,12 @@ Quantize::run (boost::shared_ptr<Region> r)
|
||||||
for (Evoral::Sequence::Notes::iterator i = model->notes().begin();
|
for (Evoral::Sequence::Notes::iterator i = model->notes().begin();
|
||||||
i != model->notes().end(); ++i) {
|
i != model->notes().end(); ++i) {
|
||||||
const double new_time = lrint((*i)->time() / q_frames) * q_frames;
|
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)
|
if (new_dur == 0.0)
|
||||||
new_dur = q_frames;
|
new_dur = q_frames;
|
||||||
|
|
||||||
(*i)->set_time(new_time);
|
(*i)->set_time(new_time);
|
||||||
(*i)->set_duration(new_dur);
|
(*i)->set_length(new_dur);
|
||||||
}
|
}
|
||||||
|
|
||||||
model->set_edited(true);
|
model->set_edited(true);
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@
|
||||||
#include <ardour/tempo.h>
|
#include <ardour/tempo.h>
|
||||||
#include <ardour/audioengine.h>
|
#include <ardour/audioengine.h>
|
||||||
#include <ardour/smf_reader.h>
|
#include <ardour/smf_reader.h>
|
||||||
|
#include <ardour/event_type_map.h>
|
||||||
|
|
||||||
#include "i18n.h"
|
#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)
|
// Output parameters for read_event (which will allocate scratch in buffer as needed)
|
||||||
uint32_t ev_delta_t = 0;
|
uint32_t ev_delta_t = 0;
|
||||||
|
uint32_t ev_type = 0;
|
||||||
uint32_t ev_size = 0;
|
uint32_t ev_size = 0;
|
||||||
uint8_t* ev_buffer = 0;
|
uint8_t* ev_buffer = 0;
|
||||||
|
|
||||||
|
|
@ -407,6 +409,8 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, n
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ev_type = EventTypeMap::instance().midi_event_type(ev_buffer[0]);
|
||||||
|
|
||||||
time += ev_delta_t; // accumulate delta time
|
time += ev_delta_t; // accumulate delta time
|
||||||
|
|
||||||
if (ret == 0) { // meta-event (skipped, just accumulate 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;
|
((time / (double)_ppqn) * frames_per_beat)) + stamp_offset;
|
||||||
|
|
||||||
if (ev_frame_time <= start + cnt)
|
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
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -441,8 +445,9 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
|
||||||
{
|
{
|
||||||
_write_data_count = 0;
|
_write_data_count = 0;
|
||||||
|
|
||||||
double time;
|
EventTime time;
|
||||||
size_t size;
|
EventType type;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
size_t buf_capacity = 4;
|
size_t buf_capacity = 4;
|
||||||
uint8_t* buf = (uint8_t*)malloc(buf_capacity);
|
uint8_t* buf = (uint8_t*)malloc(buf_capacity);
|
||||||
|
|
@ -450,14 +455,14 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
|
||||||
if (_model && ! _model->writing())
|
if (_model && ! _model->writing())
|
||||||
_model->start_write();
|
_model->start_write();
|
||||||
|
|
||||||
Evoral::Event ev(0.0, 4, NULL, true);
|
Evoral::MIDIEvent ev(0, 0.0, 4, NULL, true);
|
||||||
|
|
||||||
while (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)
|
if (!ret || time - _timeline_position > _length + cnt)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = src.read_prefix(&time, &size);
|
ret = src.read_prefix(&time, &type, &size);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -476,6 +481,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
|
||||||
time -= _timeline_position;
|
time -= _timeline_position;
|
||||||
|
|
||||||
ev.set(buf, size, time);
|
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()) ) {
|
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;
|
//cerr << "SMFSource: WARNING: caller tried to write non SMF-Event of type " << std::hex << int(ev.buffer()[0]) << endl;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -21,16 +21,17 @@ if evoral['IS_OSX']:
|
||||||
domain = 'evoral'
|
domain = 'evoral'
|
||||||
|
|
||||||
evoral.Append(DOMAIN=domain, MAJOR=1, MINOR=0, MICRO=0)
|
evoral.Append(DOMAIN=domain, MAJOR=1, MINOR=0, MICRO=0)
|
||||||
evoral.Append(CXXFLAGS="-DEVENT_WITH_XML")
|
evoral.Append(CXXFLAGS="-DEVORAL_MIDI_XML")
|
||||||
|
|
||||||
sources = Split("""
|
sources = Split("""
|
||||||
src/Control.cpp
|
src/Control.cpp
|
||||||
src/ControlList.cpp
|
src/ControlList.cpp
|
||||||
src/ControlSet.cpp
|
src/ControlSet.cpp
|
||||||
|
src/Curve.cpp
|
||||||
src/Event.cpp
|
src/Event.cpp
|
||||||
|
src/MIDIEvent.cpp
|
||||||
src/Note.cpp
|
src/Note.cpp
|
||||||
src/Sequence.cpp
|
src/Sequence.cpp
|
||||||
src/Curve.cpp
|
|
||||||
""")
|
""")
|
||||||
|
|
||||||
libevoral = evoral.SharedLibrary('evoral', [ sources ])
|
libevoral = evoral.SharedLibrary('evoral', [ sources ])
|
||||||
|
|
|
||||||
|
|
@ -24,52 +24,39 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <evoral/midi_events.h>
|
#include <evoral/types.hpp>
|
||||||
#ifdef EVENT_WITH_XML
|
|
||||||
#include <pbd/xml++.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** If this is not defined, all methods of MidiEvent are RT safe
|
/** If this is not defined, all methods of MidiEvent are RT safe
|
||||||
* but MidiEvent will never deep copy and (depending on the scenario)
|
* but MidiEvent will never deep copy and (depending on the scenario)
|
||||||
* may not be usable in STL containers, signals, etc.
|
* may not be usable in STL containers, signals, etc.
|
||||||
*/
|
*/
|
||||||
#define EVENT_ALLOW_ALLOC 1
|
#define EVORAL_EVENT_ALLOC 1
|
||||||
|
|
||||||
//#define EVENT_WITH_XML
|
|
||||||
|
|
||||||
namespace Evoral {
|
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
|
* time is either a frame time (from/to Jack) or a beat time (internal
|
||||||
* tempo time, used in MidiModel) depending on context.
|
* tempo time, used in MidiModel) depending on context.
|
||||||
*/
|
*/
|
||||||
struct Event {
|
struct Event {
|
||||||
#ifdef EVENT_ALLOW_ALLOC
|
#ifdef EVORAL_EVENT_ALLOC
|
||||||
Event(double t=0, uint32_t s=0, uint8_t* b=NULL, bool owns_buffer=false);
|
Event(EventType type=0, EventTime t=0, uint32_t s=0, uint8_t* b=NULL, bool alloc=false);
|
||||||
|
|
||||||
/** Copy \a copy.
|
/** 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
|
* is NOT REALTIME SAFE. Otherwise both events share a buffer and
|
||||||
* memory management semantics are the caller's problem.
|
* memory management semantics are the caller's problem.
|
||||||
*/
|
*/
|
||||||
Event(const Event& copy, bool owns_buffer);
|
Event(const Event& copy, bool alloc);
|
||||||
|
|
||||||
#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<XMLNode> to_xml() const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
~Event();
|
~Event();
|
||||||
|
|
||||||
inline const Event& operator=(const Event& copy) {
|
inline const Event& operator=(const Event& copy) {
|
||||||
|
_type = copy._type;
|
||||||
_time = copy._time;
|
_time = copy._time;
|
||||||
if (_owns_buffer) {
|
if (_owns_buffer) {
|
||||||
if (copy._buffer) {
|
if (copy._buffer) {
|
||||||
|
|
@ -96,12 +83,13 @@ struct Event {
|
||||||
_owns_buffer = false;
|
_owns_buffer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_type = copy._type;
|
||||||
_time = copy._time;
|
_time = copy._time;
|
||||||
_size = copy._size;
|
_size = copy._size;
|
||||||
_buffer = copy._buffer;
|
_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 (_owns_buffer) {
|
||||||
if (_size < size) {
|
if (_size < size) {
|
||||||
_buffer = (uint8_t*) ::realloc(_buffer, size);
|
_buffer = (uint8_t*) ::realloc(_buffer, size);
|
||||||
|
|
@ -111,11 +99,14 @@ struct Event {
|
||||||
_buffer = buf;
|
_buffer = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
_size = size;
|
|
||||||
_time = t;
|
_time = t;
|
||||||
|
_size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const Event& other) const {
|
inline bool operator==(const Event& other) const {
|
||||||
|
if (_type != other._type)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (_time != other._time)
|
if (_time != other._time)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -125,7 +116,7 @@ struct Event {
|
||||||
if (_buffer == other._buffer)
|
if (_buffer == other._buffer)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (size_t i=0; i < _size; ++i)
|
for (uint32_t i=0; i < _size; ++i)
|
||||||
if (_buffer[i] != other._buffer[i])
|
if (_buffer[i] != other._buffer[i])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -136,7 +127,7 @@ struct Event {
|
||||||
|
|
||||||
inline bool owns_buffer() const { return _owns_buffer; }
|
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) {
|
if (_owns_buffer) {
|
||||||
free(_buffer);
|
free(_buffer);
|
||||||
_buffer = NULL;
|
_buffer = NULL;
|
||||||
|
|
@ -146,7 +137,7 @@ struct Event {
|
||||||
_owns_buffer = own;
|
_owns_buffer = own;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void realloc(size_t size) {
|
inline void realloc(uint32_t size) {
|
||||||
if (_owns_buffer) {
|
if (_owns_buffer) {
|
||||||
if (size > _size)
|
if (size > _size)
|
||||||
_buffer = (uint8_t*) ::realloc(_buffer, size);
|
_buffer = (uint8_t*) ::realloc(_buffer, size);
|
||||||
|
|
@ -158,56 +149,36 @@ struct Event {
|
||||||
_size = size;
|
_size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void clear() {
|
||||||
|
_type = 0;
|
||||||
|
_time = 0;
|
||||||
|
_size = 0;
|
||||||
|
_buffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
inline void set_buffer(uint8_t* buf) { _buffer = buf; }
|
inline void set_buffer(uint8_t* buf) { _buffer = buf; }
|
||||||
|
|
||||||
#endif // EVENT_ALLOW_ALLOC
|
#endif // EVORAL_EVENT_ALLOC
|
||||||
|
|
||||||
inline double time() const { return _time; }
|
inline EventType event_type() const { return _type; }
|
||||||
inline double& time() { return _time; }
|
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() const { return _size; }
|
||||||
inline uint32_t& size() { 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 const uint8_t* buffer() const { return _buffer; }
|
||||||
inline uint8_t*& buffer() { return _buffer; }
|
inline uint8_t*& buffer() { return _buffer; }
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
double _time; /**< Sample index (or beat time) at which event is valid */
|
EventType _type; /**< Type of event (application relative, NOT MIDI 'type') */
|
||||||
uint32_t _size; /**< Number of uint8_ts of data in \a buffer */
|
EventTime _time; /**< Sample index (or beat time) at which event is valid */
|
||||||
uint8_t* _buffer; /**< Raw MIDI data */
|
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 */
|
bool _owns_buffer; /**< Whether buffer is locally allocated */
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
|
||||||
95
libs/evoral/evoral/EventRingBuffer.hpp
Normal file
95
libs/evoral/evoral/EventRingBuffer.hpp
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
/* This file is part of Evoral.
|
||||||
|
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||||
|
*
|
||||||
|
* 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 <glib.h>
|
||||||
|
#include <evoral/RingBuffer.hpp>
|
||||||
|
#include <evoral/EventSink.hpp>
|
||||||
|
#include <evoral/types.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
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<uint8_t>, public Evoral::EventSink {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** @param capacity Ringbuffer capacity in bytes.
|
||||||
|
*/
|
||||||
|
EventRingBuffer(size_t capacity) : RingBuffer<uint8_t>(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<uint8_t>::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<uint8_t>::full_read(sizeof(EventTime), (uint8_t*)time);
|
||||||
|
if (success)
|
||||||
|
success = RingBuffer<uint8_t>::full_read(sizeof(EventType), (uint8_t*)type);
|
||||||
|
if (success)
|
||||||
|
success = RingBuffer<uint8_t>::full_read(sizeof(uint32_t), (uint8_t*)size);
|
||||||
|
if (success)
|
||||||
|
success = RingBuffer<uint8_t>::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<uint8_t>::write(sizeof(EventTime), (uint8_t*)&time);
|
||||||
|
RingBuffer<uint8_t>::write(sizeof(EventType), (uint8_t*)&type);
|
||||||
|
RingBuffer<uint8_t>::write(sizeof(uint32_t), (uint8_t*)&size);
|
||||||
|
RingBuffer<uint8_t>::write(size, buf);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Evoral
|
||||||
|
|
||||||
|
#endif // EVORAL_EVENT_RING_BUFFER_HPP
|
||||||
|
|
||||||
|
|
@ -29,9 +29,7 @@ namespace Evoral {
|
||||||
class EventSink {
|
class EventSink {
|
||||||
public:
|
public:
|
||||||
virtual ~EventSink() {}
|
virtual ~EventSink() {}
|
||||||
virtual size_t write(timestamp_t time,
|
virtual uint32_t write(EventTime time, EventType type, uint32_t size, const uint8_t* buf) = 0;
|
||||||
uint32_t size,
|
|
||||||
const uint8_t* buf) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
89
libs/evoral/evoral/MIDIEvent.hpp
Normal file
89
libs/evoral/evoral/MIDIEvent.hpp
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
/* This file is part of Evoral.
|
||||||
|
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||||
|
* 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 <evoral/Event.hpp>
|
||||||
|
#include <evoral/midi_events.h>
|
||||||
|
#ifdef EVORAL_MIDI_XML
|
||||||
|
#include <pbd/xml++.h>
|
||||||
|
#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<XMLNode> 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
|
||||||
|
|
@ -31,8 +31,8 @@ struct ProgramChange : public Parameter {
|
||||||
ProgramChange(uint32_t pc_type, uint8_t channel) : Parameter(pc_type, 0, channel) {}
|
ProgramChange(uint32_t pc_type, uint8_t channel) : Parameter(pc_type, 0, channel) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChannelAftertouch : public Parameter {
|
struct ChannelPressure : public Parameter {
|
||||||
ChannelAftertouch(uint32_t ca_type, uint32_t channel) : Parameter(ca_type, 0, channel) {}
|
ChannelPressure(uint32_t ca_type, uint32_t channel) : Parameter(ca_type, 0, channel) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PitchBender : public Parameter {
|
struct PitchBender : public Parameter {
|
||||||
|
|
|
||||||
|
|
@ -20,18 +20,18 @@
|
||||||
#define EVORAL_NOTE_HPP
|
#define EVORAL_NOTE_HPP
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <evoral/Event.hpp>
|
#include <evoral/MIDIEvent.hpp>
|
||||||
|
|
||||||
namespace Evoral {
|
namespace Evoral {
|
||||||
|
|
||||||
|
|
||||||
/** An abstract (protocol agnostic) note.
|
/** 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 {
|
class Note {
|
||||||
public:
|
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(const Note& copy);
|
||||||
~Note();
|
~Note();
|
||||||
|
|
||||||
|
|
@ -40,26 +40,26 @@ public:
|
||||||
inline bool operator==(const Note& other) {
|
inline bool operator==(const Note& other) {
|
||||||
return time() == other.time() &&
|
return time() == other.time() &&
|
||||||
note() == other.note() &&
|
note() == other.note() &&
|
||||||
duration() == other.duration() &&
|
length() == other.length() &&
|
||||||
velocity() == other.velocity() &&
|
velocity() == other.velocity() &&
|
||||||
channel() == other.channel();
|
channel() == other.channel();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double time() const { return _on_event.time(); }
|
inline EventTime time() const { return _on_event.time(); }
|
||||||
inline double end_time() const { return _off_event.time(); }
|
inline EventTime end_time() const { return _off_event.time(); }
|
||||||
inline uint8_t note() const { return _on_event.note(); }
|
inline uint8_t note() const { return _on_event.note(); }
|
||||||
inline uint8_t velocity() const { return _on_event.velocity(); }
|
inline uint8_t velocity() const { return _on_event.velocity(); }
|
||||||
inline double duration() const { return _off_event.time() - _on_event.time(); }
|
inline EventLength length() const { return _off_event.time() - _on_event.time(); }
|
||||||
inline uint8_t channel() const {
|
inline uint8_t channel() const {
|
||||||
assert(_on_event.channel() == _off_event.channel());
|
assert(_on_event.channel() == _off_event.channel());
|
||||||
return _on_event.channel();
|
return _on_event.channel();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; }
|
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_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_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_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 void set_channel(uint8_t c) { _on_event.set_channel(c); _off_event.set_channel(c); }
|
||||||
|
|
||||||
inline Event& on_event() { return _on_event; }
|
inline Event& on_event() { return _on_event; }
|
||||||
inline const Event& on_event() const { return _on_event; }
|
inline const Event& on_event() const { return _on_event; }
|
||||||
|
|
@ -68,8 +68,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Event buffers are self-contained
|
// Event buffers are self-contained
|
||||||
Event _on_event;
|
MIDIEvent _on_event;
|
||||||
Event _off_event;
|
MIDIEvent _off_event;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
226
libs/evoral/evoral/RingBuffer.hpp
Normal file
226
libs/evoral/evoral/RingBuffer.hpp
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
/* This file is part of Evoral.
|
||||||
|
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||||
|
*
|
||||||
|
* 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 <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
namespace Evoral {
|
||||||
|
|
||||||
|
|
||||||
|
/** A lock-free RingBuffer.
|
||||||
|
* Read/Write realtime safe.
|
||||||
|
* Single-reader Single-writer thread safe.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
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<typename T>
|
||||||
|
size_t
|
||||||
|
RingBuffer<T>::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<typename T>
|
||||||
|
bool
|
||||||
|
RingBuffer<T>::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<typename T>
|
||||||
|
size_t
|
||||||
|
RingBuffer<T>::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<typename T>
|
||||||
|
bool
|
||||||
|
RingBuffer<T>::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<typename T>
|
||||||
|
bool
|
||||||
|
RingBuffer<T>::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<typename T>
|
||||||
|
inline void
|
||||||
|
RingBuffer<T>::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
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -30,30 +30,28 @@
|
||||||
#include <evoral/Note.hpp>
|
#include <evoral/Note.hpp>
|
||||||
#include <evoral/Parameter.hpp>
|
#include <evoral/Parameter.hpp>
|
||||||
#include <evoral/ControlSet.hpp>
|
#include <evoral/ControlSet.hpp>
|
||||||
|
#include <evoral/ControlList.hpp>
|
||||||
|
|
||||||
namespace Evoral {
|
namespace Evoral {
|
||||||
|
|
||||||
|
class TypeMap;
|
||||||
class EventSink;
|
class EventSink;
|
||||||
class Note;
|
class Note;
|
||||||
class Event;
|
class Event;
|
||||||
class ControlList;
|
|
||||||
|
|
||||||
|
/** An iterator over (the x axis of) a 2-d double coordinate space.
|
||||||
/** This class keeps track of the current x and y for a control
|
|
||||||
*/
|
*/
|
||||||
class ControlIterator {
|
class ControlIterator {
|
||||||
public:
|
public:
|
||||||
|
ControlIterator(boost::shared_ptr<const ControlList> al, double ax, double ay)
|
||||||
|
: list(al)
|
||||||
|
, x(ax)
|
||||||
|
, y(ay)
|
||||||
|
{}
|
||||||
|
|
||||||
boost::shared_ptr<const ControlList> list;
|
boost::shared_ptr<const ControlList> list;
|
||||||
double x;
|
double x;
|
||||||
double y;
|
double y;
|
||||||
|
|
||||||
ControlIterator(boost::shared_ptr<const ControlList> 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. */
|
* Controller data is represented as a list of time-stamped float values. */
|
||||||
class Sequence : virtual public ControlSet {
|
class Sequence : virtual public ControlSet {
|
||||||
public:
|
public:
|
||||||
Sequence(size_t size=0);
|
Sequence(const TypeMap& type_map, size_t size=0);
|
||||||
|
|
||||||
bool read_locked() { return _read_iter.locked(); }
|
bool read_locked() { return _read_iter.locked(); }
|
||||||
|
|
||||||
|
|
@ -115,7 +113,7 @@ public:
|
||||||
/** Read iterator */
|
/** Read iterator */
|
||||||
class const_iterator {
|
class const_iterator {
|
||||||
public:
|
public:
|
||||||
const_iterator(const Sequence& seq, double t);
|
const_iterator(const Sequence& seq, EventTime t);
|
||||||
~const_iterator();
|
~const_iterator();
|
||||||
|
|
||||||
inline bool valid() const { return !_is_end && _event; }
|
inline bool valid() const { return !_is_end && _event; }
|
||||||
|
|
@ -144,18 +142,20 @@ public:
|
||||||
|
|
||||||
mutable ActiveNotes _active_notes;
|
mutable ActiveNotes _active_notes;
|
||||||
|
|
||||||
bool _is_end;
|
typedef std::vector<ControlIterator> ControlIterators;
|
||||||
bool _locked;
|
|
||||||
Notes::const_iterator _note_iter;
|
bool _is_end;
|
||||||
std::vector<ControlIterator> _control_iters;
|
bool _locked;
|
||||||
std::vector<ControlIterator>::iterator _control_iter;
|
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_iterator begin(EventTime t=0) const { return const_iterator(*this, t); }
|
||||||
const const_iterator& end() const { return _end_iter; }
|
const const_iterator& end() const { return _end_iter; }
|
||||||
|
|
||||||
void read_seek(double t) { _read_iter = begin(t); }
|
void read_seek(EventTime t) { _read_iter = begin(t); }
|
||||||
double read_time() const { return _read_iter.valid() ? _read_iter->time() : 0.0; }
|
EventTime read_time() const { return _read_iter.valid() ? _read_iter->time() : 0.0; }
|
||||||
|
|
||||||
bool control_to_midi_event(boost::shared_ptr<Event>& ev,
|
bool control_to_midi_event(boost::shared_ptr<Event>& ev,
|
||||||
const ControlIterator& iter) const;
|
const ControlIterator& iter) const;
|
||||||
|
|
@ -177,13 +177,15 @@ protected:
|
||||||
private:
|
private:
|
||||||
friend class const_iterator;
|
friend class const_iterator;
|
||||||
|
|
||||||
void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity);
|
void append_note_on_unlocked(uint8_t chan, EventTime time, uint8_t note, uint8_t velocity);
|
||||||
void append_note_off_unlocked(uint8_t chan, double time, uint8_t note);
|
void append_note_off_unlocked(uint8_t chan, EventTime time, uint8_t note);
|
||||||
void append_control_unlocked(const Parameter& param, double time, double value);
|
void append_control_unlocked(const Parameter& param, EventTime time, double value);
|
||||||
|
|
||||||
mutable Glib::RWLock _lock;
|
mutable Glib::RWLock _lock;
|
||||||
|
|
||||||
Notes _notes;
|
const TypeMap& _type_map;
|
||||||
|
|
||||||
|
Notes _notes;
|
||||||
|
|
||||||
typedef std::vector<size_t> WriteNotes;
|
typedef std::vector<size_t> WriteNotes;
|
||||||
WriteNotes _write_notes[16];
|
WriteNotes _write_notes[16];
|
||||||
|
|
@ -196,14 +198,6 @@ private:
|
||||||
mutable nframes_t _next_read;
|
mutable nframes_t _next_read;
|
||||||
bool _percussive;
|
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<
|
typedef std::priority_queue<
|
||||||
boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
|
boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
|
||||||
LaterNoteEndComparator>
|
LaterNoteEndComparator>
|
||||||
|
|
|
||||||
50
libs/evoral/evoral/TypeMap.hpp
Normal file
50
libs/evoral/evoral/TypeMap.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* This file is part of Evoral.
|
||||||
|
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||||
|
* 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 <evoral/types.hpp>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
#ifndef EVORAL_TYPES_HPP
|
#ifndef EVORAL_TYPES_HPP
|
||||||
#define EVORAL_TYPES_HPP
|
#define EVORAL_TYPES_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/** Frame count (i.e. length of time in audio frames) */
|
/** Frame count (i.e. length of time in audio frames) */
|
||||||
typedef uint32_t nframes_t;
|
typedef uint32_t nframes_t;
|
||||||
|
|
||||||
|
|
@ -28,4 +30,13 @@ typedef double timestamp_t;
|
||||||
/** Duration of time in timestamp_t units */
|
/** Duration of time in timestamp_t units */
|
||||||
typedef timestamp_t timedur_t;
|
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
|
#endif // EVORAL_TYPES_HPP
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ ControlList::reposition_for_rt_add (double when)
|
||||||
void
|
void
|
||||||
ControlList::rt_add (double when, double value)
|
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);
|
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) */
|
* (Optimize for immediate call this cycle within range) */
|
||||||
_search_cache.left = x;
|
_search_cache.left = x;
|
||||||
//++_search_cache.range.first;
|
//++_search_cache.range.first;
|
||||||
|
assert(inclusive ? x >= start : x > start);
|
||||||
return true;
|
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) */
|
* (Optimize for immediate call this cycle within range) */
|
||||||
_search_cache.left = x;
|
_search_cache.left = x;
|
||||||
//++_search_cache.range.first;
|
//++_search_cache.range.first;
|
||||||
|
assert(inclusive ? x >= start : x > start);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
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;
|
x = first->when + (y - first->value) / (double)slope;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*cerr << first->value << " @ " << first->when << " ... "
|
cerr << first->value << " @ " << first->when << " ... "
|
||||||
<< next->value << " @ " << next->when
|
<< next->value << " @ " << next->when
|
||||||
<< " = " << y << " @ " << x << endl;*/
|
<< " = " << y << " @ " << x << endl;
|
||||||
|
|
||||||
assert( (y >= first->value && y <= next->value)
|
assert( (y >= first->value && y <= next->value)
|
||||||
|| (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
|
/* Move left of cache to this point
|
||||||
* (Optimize for immediate call this cycle within range) */
|
* (Optimize for immediate call this cycle within range) */
|
||||||
_search_cache.left = x;
|
_search_cache.left = x;
|
||||||
|
assert(inclusive ? x >= start : x > start);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,11 @@
|
||||||
|
|
||||||
namespace Evoral {
|
namespace Evoral {
|
||||||
|
|
||||||
#ifdef EVENT_ALLOW_ALLOC
|
#ifdef EVORAL_EVENT_ALLOC
|
||||||
Event::Event(double t, uint32_t s, uint8_t* b, bool owns_buffer)
|
|
||||||
: _time(t)
|
Event::Event(uint32_t tid, EventTime t, uint32_t s, uint8_t* b, bool owns_buffer)
|
||||||
|
: _type(tid)
|
||||||
|
, _time(t)
|
||||||
, _size(s)
|
, _size(s)
|
||||||
, _buffer(b)
|
, _buffer(b)
|
||||||
, _owns_buffer(owns_buffer)
|
, _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)
|
Event::Event(const Event& copy, bool owns_buffer)
|
||||||
: _time(copy._time)
|
: _type(copy._type)
|
||||||
|
, _time(copy._time)
|
||||||
, _size(copy._size)
|
, _size(copy._size)
|
||||||
, _buffer(copy._buffer)
|
, _buffer(copy._buffer)
|
||||||
, _owns_buffer(owns_buffer)
|
, _owns_buffer(owns_buffer)
|
||||||
|
|
@ -59,49 +62,7 @@ Event::~Event() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // EVENT_ALLOW_ALLOC
|
#endif // EVORAL_EVENT_ALLOC
|
||||||
|
|
||||||
#ifdef EVENT_WITH_XML
|
|
||||||
|
|
||||||
Event::Event(const XMLNode& event)
|
|
||||||
{
|
|
||||||
string name = event.name();
|
|
||||||
|
|
||||||
if (name == "ControlChange") {
|
|
||||||
|
|
||||||
} else if (name == "ProgramChange") {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
boost::shared_ptr<XMLNode>
|
|
||||||
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<XMLNode>(result);
|
|
||||||
}
|
|
||||||
#endif // EVENT_WITH_XML
|
|
||||||
|
|
||||||
} // namespace MIDI
|
} // namespace MIDI
|
||||||
|
|
||||||
|
|
|
||||||
67
libs/evoral/src/MIDIEvent.cpp
Normal file
67
libs/evoral/src/MIDIEvent.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* This file is part of Evoral.
|
||||||
|
* Copyright (C) 2008 Dave Robillard <http://drobilla.net>
|
||||||
|
* 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 <evoral/MIDIEvent.hpp>
|
||||||
|
|
||||||
|
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<XMLNode>
|
||||||
|
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<XMLNode>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // EVORAL_MIDI_XML
|
||||||
|
|
||||||
|
} // namespace MIDI
|
||||||
|
|
||||||
|
|
@ -21,9 +21,10 @@
|
||||||
|
|
||||||
namespace Evoral {
|
namespace Evoral {
|
||||||
|
|
||||||
Note::Note(uint8_t chan, double t, double d, uint8_t n, uint8_t v)
|
Note::Note(uint8_t chan, EventTime t, EventLength l, uint8_t n, uint8_t v)
|
||||||
: _on_event(t, 3, NULL, true)
|
// FIXME: types?
|
||||||
, _off_event(t + d, 3, NULL, true)
|
: _on_event(0xDE, t, 3, NULL, true)
|
||||||
|
, _off_event(0xAD, t + l, 3, NULL, true)
|
||||||
{
|
{
|
||||||
assert(chan < 16);
|
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;
|
_off_event.buffer()[2] = 0x40;
|
||||||
|
|
||||||
assert(time() == t);
|
assert(time() == t);
|
||||||
assert(duration() == d);
|
assert(length() == l);
|
||||||
assert(note() == n);
|
assert(note() == n);
|
||||||
assert(velocity() == v);
|
assert(velocity() == v);
|
||||||
assert(_on_event.channel() == _off_event.channel());
|
assert(_on_event.channel() == _off_event.channel());
|
||||||
|
|
@ -64,7 +65,7 @@ Note::Note(const Note& copy)
|
||||||
assert(end_time() == copy.end_time());
|
assert(end_time() == copy.end_time());
|
||||||
assert(note() == copy.note());
|
assert(note() == copy.note());
|
||||||
assert(velocity() == copy.velocity());
|
assert(velocity() == copy.velocity());
|
||||||
assert(duration() == copy.duration());
|
assert(length() == copy.length());
|
||||||
assert(_on_event.channel() == _off_event.channel());
|
assert(_on_event.channel() == _off_event.channel());
|
||||||
assert(channel() == copy.channel());
|
assert(channel() == copy.channel());
|
||||||
}
|
}
|
||||||
|
|
@ -80,20 +81,12 @@ Note::operator=(const Note& copy)
|
||||||
{
|
{
|
||||||
_on_event = copy._on_event;
|
_on_event = copy._on_event;
|
||||||
_off_event = copy._off_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(time() == copy.time());
|
||||||
assert(end_time() == copy.end_time());
|
assert(end_time() == copy.end_time());
|
||||||
assert(note() == copy.note());
|
assert(note() == copy.note());
|
||||||
assert(velocity() == copy.velocity());
|
assert(velocity() == copy.velocity());
|
||||||
assert(duration() == copy.duration());
|
assert(length() == copy.length());
|
||||||
assert(_on_event.channel() == _off_event.channel());
|
assert(_on_event.channel() == _off_event.channel());
|
||||||
assert(channel() == copy.channel());
|
assert(channel() == copy.channel());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include <evoral/ControlSet.hpp>
|
#include <evoral/ControlSet.hpp>
|
||||||
#include <evoral/EventSink.hpp>
|
#include <evoral/EventSink.hpp>
|
||||||
#include <evoral/MIDIParameters.hpp>
|
#include <evoral/MIDIParameters.hpp>
|
||||||
|
#include <evoral/TypeMap.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
@ -63,7 +64,7 @@ static ostream& errorout = cerr;
|
||||||
|
|
||||||
// Read iterator (const_iterator)
|
// 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)
|
: _seq(&seq)
|
||||||
, _is_end( (t == DBL_MAX) || seq.empty() )
|
, _is_end( (t == DBL_MAX) || seq.empty() )
|
||||||
, _locked( !_is_end )
|
, _locked( !_is_end )
|
||||||
|
|
@ -76,8 +77,8 @@ Sequence::const_iterator::const_iterator(const Sequence& seq, double t)
|
||||||
|
|
||||||
seq.read_lock();
|
seq.read_lock();
|
||||||
|
|
||||||
_note_iter = seq.notes().end();
|
|
||||||
// find first note which begins after t
|
// 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) {
|
for (Sequence::Notes::const_iterator i = seq.notes().begin(); i != seq.notes().end(); ++i) {
|
||||||
if ((*i)->time() >= t) {
|
if ((*i)->time() >= t) {
|
||||||
_note_iter = i;
|
_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<Event>(new Event((*_note_iter)->on_event(), true));
|
_event = boost::shared_ptr<Event>(new Event((*_note_iter)->on_event(), true));
|
||||||
}
|
_active_notes.push(*_note_iter);
|
||||||
|
++_note_iter;
|
||||||
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 {
|
|
||||||
_control_iter = _control_iters.end();
|
_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) {
|
if ( (! _event.get()) || _event->size() == 0) {
|
||||||
|
|
@ -158,11 +151,12 @@ Sequence::const_iterator::const_iterator(const Sequence& seq, double t)
|
||||||
_locked = false;
|
_locked = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debugout << "New Iterator = " << hex << _event->type();
|
debugout << "New Iterator = " << _event->event_type();
|
||||||
|
debugout << " : " << hex << (int)((MIDIEvent*)_event.get())->type();
|
||||||
debugout << " @ " << _event->time() << endl;
|
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()
|
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");
|
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;
|
//debugout << "const_iterator::operator++: " << _event->to_string() << endl;
|
||||||
|
|
||||||
if (! (_event->is_note() || _event->is_cc() || _event->is_pgm_change()
|
if (! (ev.is_note() || ev.is_cc() || ev.is_pgm_change()
|
||||||
|| _event->is_pitch_bender() || _event->is_channel_aftertouch()) ) {
|
|| ev.is_pitch_bender() || ev.is_channel_pressure()) ) {
|
||||||
errorout << "Unknown event type: " << hex << int(_event->buffer()[0])
|
errorout << "Unknown event type: " << hex << int(ev.buffer()[0])
|
||||||
<< int(_event->buffer()[1]) << int(_event->buffer()[2]) << endl;
|
<< 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
|
// 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;
|
double x = 0.0, y = 0.0;
|
||||||
const bool ret = _control_iter->list->rt_safe_earliest_event_unlocked(
|
const bool ret = _control_iter->list->rt_safe_earliest_event_unlocked(
|
||||||
_control_iter->x, DBL_MAX, x, y, false);
|
_control_iter->x, DBL_MAX, x, y, false);
|
||||||
|
|
||||||
|
assert(!ret || x > _control_iter->x);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
_control_iter->x = x;
|
_control_iter->x = x;
|
||||||
_control_iter->y = y;
|
_control_iter->y = y;
|
||||||
} else {
|
} else {
|
||||||
_control_iter->list.reset();
|
_control_iter->list.reset();
|
||||||
_control_iter->x = DBL_MAX;
|
_control_iter->x = DBL_MAX;
|
||||||
|
_control_iter->y = DBL_MAX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_control_iter = _control_iters.begin();
|
_control_iter = _control_iters.begin();
|
||||||
|
|
||||||
// find the _control_iter with the earliest event time
|
// find the _control_iter with the earliest event time
|
||||||
for (std::vector<ControlIterator>::iterator i = _control_iters.begin();
|
for (ControlIterators::iterator i = _control_iters.begin(); i != _control_iters.end(); ++i) {
|
||||||
i != _control_iters.end(); ++i) {
|
|
||||||
if (i->x < _control_iter->x) {
|
if (i->x < _control_iter->x) {
|
||||||
_control_iter = i;
|
_control_iter = i;
|
||||||
}
|
}
|
||||||
|
|
@ -218,7 +217,7 @@ Sequence::const_iterator& Sequence::const_iterator::operator++()
|
||||||
enum Type {NIL, NOTE_ON, NOTE_OFF, CONTROL};
|
enum Type {NIL, NOTE_ON, NOTE_OFF, CONTROL};
|
||||||
|
|
||||||
Type type = NIL;
|
Type type = NIL;
|
||||||
double t = 0;
|
EventTime t = 0;
|
||||||
|
|
||||||
// Next earliest note on
|
// Next earliest note on
|
||||||
if (_note_iter != _seq->notes().end()) {
|
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
|
// Use the next earliest note off iff it's earlier than the note on
|
||||||
if (!_seq->percussive() && (! _active_notes.empty())) {
|
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;
|
type = NOTE_OFF;
|
||||||
t = _active_notes.top()->end_time();
|
t = _active_notes.top()->end_time();
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +257,7 @@ Sequence::const_iterator& Sequence::const_iterator::operator++()
|
||||||
_is_end = true;
|
_is_end = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(_is_end || _event->size() > 0);
|
assert(_is_end || (_event->size() > 0 && _event->buffer() && _event->buffer()[0] != '\0'));
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -289,8 +288,16 @@ Sequence::const_iterator::operator=(const const_iterator& other)
|
||||||
size_t index = other._control_iter - other._control_iters.begin();
|
size_t index = other._control_iter - other._control_iters.begin();
|
||||||
_control_iter = _control_iters.begin() + index;
|
_control_iter = _control_iters.begin() + index;
|
||||||
|
|
||||||
if (!_is_end) {
|
if (!_is_end && other._event) {
|
||||||
_event = boost::shared_ptr<Event>(new Event(*other._event, true));
|
if (_event) {
|
||||||
|
*_event = *other._event.get();
|
||||||
|
} else {
|
||||||
|
_event = boost::shared_ptr<Event>(new Event(*other._event, true));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_event) {
|
||||||
|
_event->clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -298,9 +305,10 @@ Sequence::const_iterator::operator=(const const_iterator& other)
|
||||||
|
|
||||||
// Sequence
|
// Sequence
|
||||||
|
|
||||||
Sequence::Sequence(size_t size)
|
Sequence::Sequence(const TypeMap& type_map, size_t size)
|
||||||
: _read_iter(*this, DBL_MAX)
|
: _read_iter(*this, DBL_MAX)
|
||||||
, _edited(false)
|
, _edited(false)
|
||||||
|
, _type_map(type_map)
|
||||||
, _notes(size)
|
, _notes(size)
|
||||||
, _writing(false)
|
, _writing(false)
|
||||||
, _end_iter(*this, DBL_MAX)
|
, _end_iter(*this, DBL_MAX)
|
||||||
|
|
@ -319,16 +327,15 @@ Sequence::Sequence(size_t size)
|
||||||
size_t
|
size_t
|
||||||
Sequence::read(EventSink& dst, timestamp_t start, timedur_t nframes, timestamp_t offset) const
|
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 << " # notes: " << n_notes() << endl;
|
||||||
debugout << this << " controls: " << &_controls << endl;
|
|
||||||
debugout << this << " # controls: " << _controls.size() << endl;
|
debugout << this << " # controls: " << _controls.size() << endl;
|
||||||
|
|
||||||
size_t read_events = 0;
|
size_t read_events = 0;
|
||||||
|
|
||||||
if (start != _next_read) {
|
if (start != _next_read) {
|
||||||
_read_iter = const_iterator(*this, (double)start);
|
|
||||||
debugout << "Repositioning iterator from " << _next_read << " to " << start << endl;
|
debugout << "Repositioning iterator from " << _next_read << " to " << start << endl;
|
||||||
|
_read_iter = const_iterator(*this, (double)start);
|
||||||
} else {
|
} else {
|
||||||
debugout << "Using cached iterator at " << _next_read << endl;
|
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->size() > 0);
|
||||||
assert(_read_iter->buffer());
|
assert(_read_iter->buffer());
|
||||||
dst.write(_read_iter->time() + offset,
|
dst.write(_read_iter->time() + offset,
|
||||||
|
_read_iter->event_type(),
|
||||||
_read_iter->size(),
|
_read_iter->size(),
|
||||||
_read_iter->buffer());
|
_read_iter->buffer());
|
||||||
|
|
||||||
debugout << this << " read event @ " << _read_iter->time()
|
debugout << this << " read event type " << _read_iter->event_type()
|
||||||
<< " type: " << hex << int(_read_iter->type()) << dec
|
<< " @ " << _read_iter->time() << " : ";
|
||||||
<< " note: " << int(_read_iter->note())
|
for (size_t i = 0; i < _read_iter->size(); ++i)
|
||||||
<< " velocity: " << int(_read_iter->velocity())
|
debugout << hex << (int)_read_iter->buffer()[i];
|
||||||
<< endl;
|
debugout << endl;
|
||||||
|
|
||||||
++_read_iter;
|
++_read_iter;
|
||||||
++read_events;
|
++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.
|
/** 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 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
|
* \return true on success
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
Sequence::control_to_midi_event(boost::shared_ptr<Event>& ev, const ControlIterator& iter) const
|
Sequence::control_to_midi_event(boost::shared_ptr<Event>& ev, const ControlIterator& iter) const
|
||||||
{
|
{
|
||||||
assert(iter.list.get());
|
assert(iter.list.get());
|
||||||
|
const uint32_t event_type = iter.list->parameter().type();
|
||||||
if (!ev) {
|
if (!ev) {
|
||||||
ev = boost::shared_ptr<Event>(new Event(0, 3, NULL, true));
|
ev = boost::shared_ptr<Event>(new Event(event_type, 0, 3, NULL, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (iter.list->parameter().type()) {
|
uint8_t midi_type = _type_map.parameter_midi_type(iter.list->parameter());
|
||||||
case midi_cc_type:
|
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.get());
|
||||||
assert(iter.list->parameter().channel() < 16);
|
assert(iter.list->parameter().channel() < 16);
|
||||||
assert(iter.list->parameter().id() <= INT8_MAX);
|
assert(iter.list->parameter().id() <= INT8_MAX);
|
||||||
|
|
@ -381,10 +393,9 @@ Sequence::control_to_midi_event(boost::shared_ptr<Event>& ev, const ControlItera
|
||||||
ev->buffer()[2] = (uint8_t)iter.y;
|
ev->buffer()[2] = (uint8_t)iter.y;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case midi_pc_type:
|
case MIDI_CMD_PGM_CHANGE:
|
||||||
assert(iter.list.get());
|
assert(iter.list.get());
|
||||||
assert(iter.list->parameter().channel() < 16);
|
assert(iter.list->parameter().channel() < 16);
|
||||||
assert(iter.list->parameter().id() == 0);
|
|
||||||
assert(iter.y <= INT8_MAX);
|
assert(iter.y <= INT8_MAX);
|
||||||
|
|
||||||
ev->time() = iter.x;
|
ev->time() = iter.x;
|
||||||
|
|
@ -393,10 +404,9 @@ Sequence::control_to_midi_event(boost::shared_ptr<Event>& ev, const ControlItera
|
||||||
ev->buffer()[1] = (uint8_t)iter.y;
|
ev->buffer()[1] = (uint8_t)iter.y;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case midi_pb_type:
|
case MIDI_CMD_BENDER:
|
||||||
assert(iter.list.get());
|
assert(iter.list.get());
|
||||||
assert(iter.list->parameter().channel() < 16);
|
assert(iter.list->parameter().channel() < 16);
|
||||||
assert(iter.list->parameter().id() == 0);
|
|
||||||
assert(iter.y < (1<<14));
|
assert(iter.y < (1<<14));
|
||||||
|
|
||||||
ev->time() = iter.x;
|
ev->time() = iter.x;
|
||||||
|
|
@ -406,10 +416,9 @@ Sequence::control_to_midi_event(boost::shared_ptr<Event>& ev, const ControlItera
|
||||||
ev->buffer()[2] = (uint16_t(iter.y) >> 7) & 0x7F; // MSB
|
ev->buffer()[2] = (uint16_t(iter.y) >> 7) & 0x7F; // MSB
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case midi_ca_type:
|
case MIDI_CMD_CHANNEL_PRESSURE:
|
||||||
assert(iter.list.get());
|
assert(iter.list.get());
|
||||||
assert(iter.list->parameter().channel() < 16);
|
assert(iter.list->parameter().channel() < 16);
|
||||||
assert(iter.list->parameter().id() == 0);
|
|
||||||
assert(iter.y <= INT8_MAX);
|
assert(iter.y <= INT8_MAX);
|
||||||
|
|
||||||
ev->time() = iter.x;
|
ev->time() = iter.x;
|
||||||
|
|
@ -441,10 +450,10 @@ Sequence::clear()
|
||||||
|
|
||||||
/** Begin a write of events to the model.
|
/** 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
|
* on/off events are received. Otherwise (Percussive), only note on events are
|
||||||
* stored; note off events are discarded entirely and all contained notes will
|
* stored; note off events are discarded entirely and all contained notes will
|
||||||
* have duration 0.
|
* have length 0.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
Sequence::start_write()
|
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
|
* 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.
|
* 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
|
void
|
||||||
Sequence::end_write(bool delete_stuck)
|
Sequence::end_write(bool delete_stuck)
|
||||||
|
|
@ -475,7 +484,7 @@ Sequence::end_write(bool delete_stuck)
|
||||||
|
|
||||||
if (!_percussive && delete_stuck) {
|
if (!_percussive && delete_stuck) {
|
||||||
for (Notes::iterator n = _notes.begin(); n != _notes.end() ;) {
|
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;
|
errorout << "WARNING: Stuck note lost: " << (*n)->note() << endl;
|
||||||
n = _notes.erase(n);
|
n = _notes.erase(n);
|
||||||
// we have to break here because erase invalidates the iterator
|
// we have to break here because erase invalidates the iterator
|
||||||
|
|
@ -509,11 +518,13 @@ Sequence::end_write(bool delete_stuck)
|
||||||
* and MUST be >= the latest event currently in the model.
|
* and MUST be >= the latest event currently in the model.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
Sequence::append(const Event& ev)
|
Sequence::append(const Event& event)
|
||||||
{
|
{
|
||||||
write_lock();
|
write_lock();
|
||||||
_edited = true;
|
_edited = true;
|
||||||
|
|
||||||
|
const MIDIEvent& ev = (const MIDIEvent&)event;
|
||||||
|
|
||||||
assert(_notes.empty() || ev.time() >= _notes.back()->time());
|
assert(_notes.empty() || ev.time() >= _notes.back()->time());
|
||||||
assert(_writing);
|
assert(_writing);
|
||||||
|
|
||||||
|
|
@ -522,32 +533,34 @@ Sequence::append(const Event& ev)
|
||||||
ev.velocity());
|
ev.velocity());
|
||||||
} else if (ev.is_note_off()) {
|
} else if (ev.is_note_off()) {
|
||||||
append_note_off_unlocked(ev.channel(), ev.time(), ev.note());
|
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()) {
|
} else if (ev.is_cc()) {
|
||||||
append_control_unlocked(
|
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());
|
ev.time(), ev.cc_value());
|
||||||
} else if (ev.is_pgm_change()) {
|
} else if (ev.is_pgm_change()) {
|
||||||
append_control_unlocked(
|
append_control_unlocked(
|
||||||
Evoral::MIDI::ProgramChange(midi_pc_type, ev.channel()),
|
Evoral::MIDI::ProgramChange(ev.event_type(), ev.channel()),
|
||||||
ev.time(), ev.pgm_number());
|
ev.time(), ev.pgm_number());
|
||||||
} else if (ev.is_pitch_bender()) {
|
} else if (ev.is_pitch_bender()) {
|
||||||
append_control_unlocked(
|
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
|
ev.time(), double( (0x7F & ev.pitch_bender_msb()) << 7
|
||||||
| (0x7F & ev.pitch_bender_lsb()) ));
|
| (0x7F & ev.pitch_bender_lsb()) ));
|
||||||
} else if (ev.is_channel_aftertouch()) {
|
} else if (ev.is_channel_pressure()) {
|
||||||
append_control_unlocked(
|
append_control_unlocked(
|
||||||
Evoral::MIDI::ChannelAftertouch(midi_ca_type, ev.channel()),
|
Evoral::MIDI::ChannelPressure(ev.event_type(), ev.channel()),
|
||||||
ev.time(), ev.channel_aftertouch());
|
ev.time(), ev.channel_pressure());
|
||||||
} else {
|
} else {
|
||||||
printf("WARNING: Sequence: Unknown event type %X\n", ev.type());
|
printf("WARNING: Sequence: Unknown MIDI event type %X\n", ev.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
write_unlock();
|
write_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
debugout << this << " c" << (int)chan << " note " << (int)note_num << " off @ " << time << endl;
|
||||||
assert(note_num <= 127);
|
assert(note_num <= 127);
|
||||||
|
|
@ -566,7 +579,7 @@ Sequence::append_note_on_unlocked(uint8_t chan, double time, uint8_t note_num, u
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
debugout << this << " c" << (int)chan << " note " << (int)note_num << " off @ " << time << endl;
|
||||||
assert(note_num <= 127);
|
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();
|
Note& note = *_notes[*n].get();
|
||||||
if (note.note() == note_num) {
|
if (note.note() == note_num) {
|
||||||
assert(time >= note.time());
|
assert(time >= note.time());
|
||||||
note.set_duration(time - note.time());
|
note.set_length(time - note.time());
|
||||||
_write_notes[chan].erase(n);
|
_write_notes[chan].erase(n);
|
||||||
debugout << "resolved note, duration: " << note.duration() << endl;
|
debugout << "resolved note, length: " << note.length() << endl;
|
||||||
resolved = true;
|
resolved = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -606,10 +619,9 @@ Sequence::append_note_off_unlocked(uint8_t chan, double time, uint8_t note_num)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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
|
debugout << this << " " << param.symbol() << " @ " << time << " \t= \t" << value
|
||||||
<< " controls: " << &_controls
|
|
||||||
<< " # controls: " << _controls.size() << endl;
|
<< " # controls: " << _controls.size() << endl;
|
||||||
control(param, true)->list()->rt_add(time, value);
|
control(param, true)->list()->rt_add(time, value);
|
||||||
}
|
}
|
||||||
|
|
@ -637,8 +649,8 @@ Sequence::remove_note_unlocked(const boost::shared_ptr<const Note> note)
|
||||||
// persisted undo does not work, because of rounding errors in the
|
// persisted undo does not work, because of rounding errors in the
|
||||||
// event times after saving/restoring to/from MIDI files
|
// event times after saving/restoring to/from MIDI files
|
||||||
/*cerr << "======================================= " << endl;
|
/*cerr << "======================================= " << endl;
|
||||||
cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.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.duration()) << "-- #" << int(_note.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 << "Equal: " << bool(_n == _note) << endl;
|
||||||
cerr << endl << endl;*/
|
cerr << endl << endl;*/
|
||||||
if (_n == _note) {
|
if (_n == _note) {
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,12 @@
|
||||||
* but MidiEvent will never deep copy and (depending on the scenario)
|
* but MidiEvent will never deep copy and (depending on the scenario)
|
||||||
* may not be usable in STL containers, signals, etc.
|
* 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 */
|
/** Support serialisation of MIDI events to/from XML */
|
||||||
#define EVENT_WITH_XML 1
|
#define EVORAL_MIDI_XML 1
|
||||||
|
|
||||||
#include <evoral/Event.hpp>
|
#include <evoral/Event.hpp>
|
||||||
|
#include <evoral/MIDIEvent.hpp>
|
||||||
|
|
||||||
#endif /* __libmidipp_midi_event_h__ */
|
#endif /* __libmidipp_midi_event_h__ */
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ Patch::get_state (void)
|
||||||
for (PatchMidiCommands::const_iterator event = _patch_midi_commands.begin();
|
for (PatchMidiCommands::const_iterator event = _patch_midi_commands.begin();
|
||||||
event != _patch_midi_commands.end();
|
event != _patch_midi_commands.end();
|
||||||
++event) {
|
++event) {
|
||||||
commands->add_child_copy(*(event->to_xml()));
|
commands->add_child_copy(*((((Evoral::MIDIEvent&)*event)).to_xml()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return *node;
|
return *node;
|
||||||
|
|
@ -33,7 +33,7 @@ Patch::set_state (const XMLNode& node)
|
||||||
assert(commands);
|
assert(commands);
|
||||||
const XMLNodeList events = commands->children();
|
const XMLNodeList events = commands->children();
|
||||||
for (XMLNodeList::const_iterator i = events.begin(); i != events.end(); ++i) {
|
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;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue