13
0

remove *.orig files (accidentally added in cba53a202)

This commit is contained in:
Robin Gareus 2017-09-29 21:05:06 +02:00
parent ccedb2d44e
commit 70c4977ab3
7 changed files with 0 additions and 14239 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,334 +0,0 @@
/*
Copyright (C) 2007 Paul Davis
Author: David 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_midi_model_h__
#define __ardour_midi_model_h__
#include <deque>
#include <queue>
#include <utility>
#include <boost/utility.hpp>
#include <glibmm/threads.h>
#include "pbd/command.h"
#include "ardour/automatable_sequence.h"
#include "ardour/libardour_visibility.h"
#include "ardour/libardour_visibility.h"
#include "ardour/types.h"
#include "ardour/types.h"
#include "ardour/variant.h"
#include "evoral/Note.hpp"
#include "evoral/Sequence.hpp"
namespace ARDOUR {
class Session;
class MidiSource;
/** This is a higher level (than MidiBuffer) model of MIDI data, with separate
* representations for notes (instead of just unassociated note on/off events)
* and controller data. Controller data is represented as part of the
* Automatable base (i.e. in a map of AutomationList, keyed by Parameter).
* Because of this MIDI controllers and automatable controllers/widgets/etc
* are easily interchangeable.
*/
class LIBARDOUR_API MidiModel : public AutomatableSequence<Evoral::Beats> {
public:
typedef Evoral::Beats TimeType;
MidiModel (boost::shared_ptr<MidiSource>);
NoteMode note_mode() const { return (percussive() ? Percussive : Sustained); }
void set_note_mode(NoteMode mode) { set_percussive(mode == Percussive); };
class LIBARDOUR_API DiffCommand : public Command {
public:
DiffCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
const std::string& name () const { return _name; }
virtual void operator() () = 0;
virtual void undo () = 0;
virtual int set_state (const XMLNode&, int version) = 0;
virtual XMLNode & get_state () = 0;
boost::shared_ptr<MidiModel> model() const { return _model; }
protected:
boost::shared_ptr<MidiModel> _model;
const std::string _name;
};
class LIBARDOUR_API NoteDiffCommand : public DiffCommand {
public:
NoteDiffCommand (boost::shared_ptr<MidiModel> m, const std::string& name) : DiffCommand (m, name) {}
NoteDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node);
enum Property {
NoteNumber,
Velocity,
StartTime,
Length,
Channel
};
void operator() ();
void undo ();
int set_state (const XMLNode&, int version);
XMLNode & get_state ();
void add (const NotePtr note);
void remove (const NotePtr note);
void side_effect_remove (const NotePtr note);
void change (const NotePtr note, Property prop, uint8_t new_value) {
change(note, prop, Variant(new_value));
}
void change (const NotePtr note, Property prop, TimeType new_time) {
change(note, prop, Variant(new_time));
}
void change (const NotePtr note, Property prop, const Variant& new_value);
bool adds_or_removes() const {
return !_added_notes.empty() || !_removed_notes.empty();
}
NoteDiffCommand& operator+= (const NoteDiffCommand& other);
static Variant get_value (const NotePtr note, Property prop);
static Variant::Type value_type (Property prop);
struct NoteChange {
NoteDiffCommand::Property property;
NotePtr note;
uint32_t note_id;
Variant old_value;
Variant new_value;
};
typedef std::list<NoteChange> ChangeList;
typedef std::list< boost::shared_ptr< Evoral::Note<TimeType> > > NoteList;
const ChangeList& changes() const { return _changes; }
const NoteList& added_notes() const { return _added_notes; }
const NoteList& removed_notes() const { return _removed_notes; }
private:
ChangeList _changes;
NoteList _added_notes;
NoteList _removed_notes;
std::set<NotePtr> side_effect_removals;
XMLNode &marshal_change(const NoteChange&);
NoteChange unmarshal_change(XMLNode *xml_note);
XMLNode &marshal_note(const NotePtr note);
NotePtr unmarshal_note(XMLNode *xml_note);
};
/* Currently this class only supports changes of sys-ex time, but could be expanded */
class LIBARDOUR_API SysExDiffCommand : public DiffCommand {
public:
SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node);
enum Property {
Time,
};
int set_state (const XMLNode&, int version);
XMLNode & get_state ();
void remove (SysExPtr sysex);
void operator() ();
void undo ();
void change (boost::shared_ptr<Evoral::Event<TimeType> >, TimeType);
private:
struct Change {
Change () : sysex_id (0) {}
boost::shared_ptr<Evoral::Event<TimeType> > sysex;
gint sysex_id;
SysExDiffCommand::Property property;
TimeType old_time;
TimeType new_time;
};
typedef std::list<Change> ChangeList;
ChangeList _changes;
std::list<SysExPtr> _removed;
XMLNode & marshal_change (const Change &);
Change unmarshal_change (XMLNode *);
};
class LIBARDOUR_API PatchChangeDiffCommand : public DiffCommand {
public:
PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const std::string &);
PatchChangeDiffCommand (boost::shared_ptr<MidiModel>, const XMLNode &);
int set_state (const XMLNode &, int version);
XMLNode & get_state ();
void operator() ();
void undo ();
void add (PatchChangePtr);
void remove (PatchChangePtr);
void change_time (PatchChangePtr, TimeType);
void change_channel (PatchChangePtr, uint8_t);
void change_program (PatchChangePtr, uint8_t);
void change_bank (PatchChangePtr, int);
enum Property {
Time,
Channel,
Program,
Bank
};
private:
struct Change {
PatchChangePtr patch;
Property property;
gint patch_id;
TimeType old_time;
union {
uint8_t old_channel;
int old_bank;
uint8_t old_program;
};
TimeType new_time;
union {
uint8_t new_channel;
uint8_t new_program;
int new_bank;
};
Change() : patch_id (-1) {}
};
typedef std::list<Change> ChangeList;
ChangeList _changes;
std::list<PatchChangePtr> _added;
std::list<PatchChangePtr> _removed;
XMLNode & marshal_change (const Change &);
Change unmarshal_change (XMLNode *);
XMLNode & marshal_patch_change (constPatchChangePtr);
PatchChangePtr unmarshal_patch_change (XMLNode *);
};
MidiModel::NoteDiffCommand* new_note_diff_command (const std::string& name = "midi edit");
MidiModel::SysExDiffCommand* new_sysex_diff_command (const std::string& name = "midi edit");
MidiModel::PatchChangeDiffCommand* new_patch_change_diff_command (const std::string& name = "midi edit");
void apply_command (Session& session, Command* cmd);
void apply_command (Session* session, Command* cmd) { if (session) { apply_command (*session, cmd); } }
void apply_command_as_subcommand (Session& session, Command* cmd);
bool sync_to_source (const Glib::Threads::Mutex::Lock& source_lock);
bool write_to(boost::shared_ptr<MidiSource> source,
const Glib::Threads::Mutex::Lock& source_lock);
bool write_section_to(boost::shared_ptr<MidiSource> source,
const Glib::Threads::Mutex::Lock& source_lock,
Evoral::Beats begin = Evoral::MinBeats,
Evoral::Beats end = Evoral::MaxBeats,
bool offset_events = false);
// MidiModel doesn't use the normal AutomationList serialisation code
// since controller data is stored in the .mid
XMLNode& get_state();
int set_state(const XMLNode&) { return 0; }
PBD::Signal0<void> ContentsChanged;
PBD::Signal1<void, double> ContentsShifted;
boost::shared_ptr<const MidiSource> midi_source ();
void set_midi_source (boost::shared_ptr<MidiSource>);
boost::shared_ptr<Evoral::Note<TimeType> > find_note (NotePtr);
PatchChangePtr find_patch_change (Evoral::event_id_t);
boost::shared_ptr<Evoral::Note<TimeType> > find_note (gint note_id);
boost::shared_ptr<Evoral::Event<TimeType> > find_sysex (gint);
InsertMergePolicy insert_merge_policy () const;
void set_insert_merge_policy (InsertMergePolicy);
boost::shared_ptr<Evoral::Control> control_factory(const Evoral::Parameter& id);
void insert_silence_at_start (TimeType);
void transpose (NoteDiffCommand *, const NotePtr, int);
protected:
int resolve_overlaps_unlocked (const NotePtr, void* arg = 0);
private:
struct WriteLockImpl : public AutomatableSequence<TimeType>::WriteLockImpl {
WriteLockImpl(Glib::Threads::Mutex::Lock* slock, Glib::Threads::RWLock& s, Glib::Threads::Mutex& c)
: AutomatableSequence<TimeType>::WriteLockImpl(s, c)
, source_lock (slock)
{}
~WriteLockImpl() {
delete source_lock;
}
Glib::Threads::Mutex::Lock* source_lock;
};
public:
WriteLock edit_lock();
private:
friend class DeltaCommand;
void source_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
void source_automation_state_changed (Evoral::Parameter, AutoState);
void control_list_interpolation_changed (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
void automation_list_automation_state_changed (Evoral::Parameter, AutoState);
void control_list_marked_dirty ();
PBD::ScopedConnectionList _midi_source_connections;
// We cannot use a boost::shared_ptr here to avoid a retain cycle
boost::weak_ptr<MidiSource> _midi_source;
InsertMergePolicy _insert_merge_policy;
};
} /* namespace ARDOUR */
#endif /* __ardour_midi_model_h__ */

View File

@ -1,258 +0,0 @@
/*
Copyright (C) 2006 Paul Davis
Author: David 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_midi_source_h__
#define __ardour_midi_source_h__
#include <string>
#include <time.h>
#include <glibmm/threads.h>
#include <boost/enable_shared_from_this.hpp>
#include "pbd/stateful.h"
#include "pbd/xml++.h"
#include "evoral/Sequence.hpp"
#include "evoral/Range.hpp"
#include "ardour/ardour.h"
#include "ardour/buffer.h"
#include "ardour/midi_cursor.h"
#include "ardour/source.h"
#include "ardour/beats_frames_converter.h"
namespace ARDOUR {
class MidiChannelFilter;
class MidiModel;
class MidiStateTracker;
template<typename T> class MidiRingBuffer;
/** Source for MIDI data */
class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_shared_from_this<MidiSource>
{
public:
typedef Evoral::Beats TimeType;
MidiSource (Session& session, std::string name, Source::Flag flags = Source::Flag(0));
MidiSource (Session& session, const XMLNode&);
virtual ~MidiSource ();
/** Write the data in the given time range to another MidiSource
* \param newsrc MidiSource to which data will be written. Should be a
* new, empty source. If it already has contents, the results are
* undefined. Source must be writable.
* \param begin time of earliest event that can be written.
* \param end time of latest event that can be written.
* \return zero on success, non-zero if the write failed for any reason.
*/
int write_to (const Lock& lock,
boost::shared_ptr<MidiSource> newsrc,
Evoral::Beats begin = Evoral::MinBeats,
Evoral::Beats end = Evoral::MaxBeats);
/** Export the midi data in the given time range to another MidiSource
* \param newsrc MidiSource to which data will be written. Should be a
* new, empty source. If it already has contents, the results are
* undefined. Source must be writable.
* \param begin time of earliest event that can be written.
* \param end time of latest event that can be written.
* \return zero on success, non-zero if the write failed for any reason.
*/
int export_write_to (const Lock& lock,
boost::shared_ptr<MidiSource> newsrc,
Evoral::Beats begin,
Evoral::Beats end);
/** Read the data in a given time range from the MIDI source.
* All time stamps in parameters are in audio frames (even if the source has tempo time).
* \param dst Ring buffer where read events are written.
* \param source_start Start position of the SOURCE in this read context.
* \param start Start of range to be read.
* \param cnt Length of range to be read (in audio frames).
* \param loop_range If non-null, all event times will be mapped into this loop range.
* \param tracker an optional pointer to MidiStateTracker object, for note on/off tracking.
* \param filtered Parameters whose MIDI messages will not be returned.
*/
virtual framecnt_t midi_read (const Lock& lock,
Evoral::EventSink<framepos_t>& dst,
framepos_t source_start,
framepos_t start,
framecnt_t cnt,
Evoral::Range<framepos_t>* loop_range,
MidiCursor& cursor,
MidiStateTracker* tracker,
MidiChannelFilter* filter,
const std::set<Evoral::Parameter>& filtered,
const double pulse,
const double start_beats) const;
/** Write data from a MidiRingBuffer to this source.
* @param source Source to read from.
* @param source_start This source's start position in session frames.
* @param cnt The length of time to write.
*/
virtual framecnt_t midi_write (const Lock& lock,
MidiRingBuffer<framepos_t>& src,
framepos_t source_start,
framecnt_t cnt);
/** Append a single event with a timestamp in beats.
*
* Caller must ensure that the event is later than the last written event.
*/
virtual void append_event_beats(const Lock& lock,
const Evoral::Event<Evoral::Beats>& ev) = 0;
/** Append a single event with a timestamp in frames.
*
* Caller must ensure that the event is later than the last written event.
*/
virtual void append_event_frames(const Lock& lock,
const Evoral::Event<framepos_t>& ev,
framepos_t source_start) = 0;
virtual bool empty () const;
virtual framecnt_t length (framepos_t pos) const;
virtual void update_length (framecnt_t);
virtual void mark_streaming_midi_write_started (const Lock& lock, NoteMode mode);
virtual void mark_streaming_write_started (const Lock& lock);
virtual void mark_streaming_write_completed (const Lock& lock);
/** Mark write starting with the given time parameters.
*
* This is called by MidiDiskStream::process before writing to the capture
* buffer which will be later read by midi_read().
*
* @param position The timeline position the source now starts at.
* @param capture_length The current length of the capture, which may not
* be zero if record is armed while rolling.
* @param loop_length The loop length if looping, otherwise zero.
*/
void mark_write_starting_now (framecnt_t position,
framecnt_t capture_length,
framecnt_t loop_length);
/* like ::mark_streaming_write_completed() but with more arguments to
* allow control over MIDI-specific behaviour. Expected to be used only
* when recording actual MIDI input, rather then when importing files
* etc.
*/
virtual void mark_midi_streaming_write_completed (
const Lock& lock,
Evoral::Sequence<Evoral::Beats>::StuckNoteOption stuck_option,
Evoral::Beats when = Evoral::Beats());
virtual void session_saved();
std::string captured_for() const { return _captured_for; }
void set_captured_for (std::string str) { _captured_for = str; }
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
bool length_mutable() const { return true; }
void set_length_beats(TimeType l) { _length_beats = l; }
TimeType length_beats() const { return _length_beats; }
virtual void load_model(const Glib::Threads::Mutex::Lock& lock, bool force_reload=false) = 0;
virtual void destroy_model(const Glib::Threads::Mutex::Lock& lock) = 0;
/** Reset cached information (like iterators) when things have changed.
* @param lock Source lock, which must be held by caller.
*/
void invalidate(const Glib::Threads::Mutex::Lock& lock);
/** Thou shalt not emit this directly, use invalidate() instead. */
mutable PBD::Signal1<void, bool> Invalidated;
void set_note_mode(const Glib::Threads::Mutex::Lock& lock, NoteMode mode);
boost::shared_ptr<MidiModel> model() { return _model; }
void set_model(const Glib::Threads::Mutex::Lock& lock, boost::shared_ptr<MidiModel>);
void drop_model(const Glib::Threads::Mutex::Lock& lock);
Evoral::ControlList::InterpolationStyle interpolation_of (Evoral::Parameter) const;
void set_interpolation_of (Evoral::Parameter, Evoral::ControlList::InterpolationStyle);
void copy_interpolation_from (boost::shared_ptr<MidiSource>);
void copy_interpolation_from (MidiSource *);
AutoState automation_state_of (Evoral::Parameter) const;
void set_automation_state_of (Evoral::Parameter, AutoState);
void copy_automation_state_from (boost::shared_ptr<MidiSource>);
void copy_automation_state_from (MidiSource *);
/** Emitted when a different MidiModel is set */
PBD::Signal0<void> ModelChanged;
/** Emitted when a parameter's interpolation style is changed */
PBD::Signal2<void, Evoral::Parameter, Evoral::ControlList::InterpolationStyle> InterpolationChanged;
/** Emitted when a parameter's automation state is changed */
PBD::Signal2<void, Evoral::Parameter, AutoState> AutomationStateChanged;
protected:
virtual void flush_midi(const Lock& lock) = 0;
virtual framecnt_t read_unlocked (const Lock& lock,
Evoral::EventSink<framepos_t>& dst,
framepos_t position,
framepos_t start,
framecnt_t cnt,
Evoral::Range<framepos_t>* loop_range,
MidiStateTracker* tracker,
MidiChannelFilter* filter) const = 0;
/** Write data to this source from a MidiRingBuffer.
* @param source Buffer to read from.
* @param position This source's start position in session frames.
* @param cnt The duration of this block to write for.
*/
virtual framecnt_t write_unlocked (const Lock& lock,
MidiRingBuffer<framepos_t>& source,
framepos_t position,
framecnt_t cnt) = 0;
std::string _captured_for;
boost::shared_ptr<MidiModel> _model;
bool _writing;
Evoral::Beats _length_beats;
/** The total duration of the current capture. */
framepos_t _capture_length;
/** Length of transport loop during current capture, or zero. */
framepos_t _capture_loop_length;
/** Map of interpolation styles to use for Parameters; if they are not in this map,
* the correct interpolation style can be obtained from EventTypeMap::interpolation_of ()
*/
typedef std::map<Evoral::Parameter, Evoral::ControlList::InterpolationStyle> InterpolationStyleMap;
InterpolationStyleMap _interpolation_style;
/** Map of automation states to use for Parameters; if they are not in this map,
* the correct automation state is Off.
*/
typedef std::map<Evoral::Parameter, AutoState> AutomationStateMap;
AutomationStateMap _automation_state;
};
}
#endif /* __ardour_midi_source_h__ */

View File

@ -1,580 +0,0 @@
/*
Copyright (C) 2006 Paul Davis
Author: David 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 <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <float.h>
#include <cerrno>
#include <ctime>
#include <cmath>
#include <iomanip>
#include <algorithm>
#include <glibmm/fileutils.h>
#include <glibmm/miscutils.h>
#include "pbd/xml++.h"
#include "pbd/pthread_utils.h"
#include "pbd/basename.h"
#include "evoral/Control.hpp"
#include "evoral/EventSink.hpp"
#include "ardour/debug.h"
#include "ardour/file_source.h"
#include "ardour/midi_channel_filter.h"
#include "ardour/midi_cursor.h"
#include "ardour/midi_model.h"
#include "ardour/midi_source.h"
#include "ardour/midi_state_tracker.h"
#include "ardour/session.h"
#include "ardour/session_directory.h"
#include "ardour/source_factory.h"
#include "ardour/tempo.h"
#include "pbd/i18n.h"
namespace ARDOUR { template <typename T> class MidiRingBuffer; }
using namespace std;
using namespace ARDOUR;
using namespace PBD;
MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
: Source(s, DataType::MIDI, name, flags)
, _writing(false)
, _length_beats(0.0)
, _capture_length(0)
, _capture_loop_length(0)
{
}
MidiSource::MidiSource (Session& s, const XMLNode& node)
: Source(s, node)
, _writing(false)
, _length_beats(0.0)
, _capture_length(0)
, _capture_loop_length(0)
{
if (set_state (node, Stateful::loading_state_version)) {
throw failed_constructor();
}
}
MidiSource::~MidiSource ()
{
/* invalidate any existing iterators */
Invalidated (false);
}
XMLNode&
MidiSource::get_state ()
{
XMLNode& node (Source::get_state());
if (_captured_for.length()) {
node.set_property ("captured-for", _captured_for);
}
for (InterpolationStyleMap::const_iterator i = _interpolation_style.begin(); i != _interpolation_style.end(); ++i) {
XMLNode* child = node.add_child (X_("InterpolationStyle"));
child->set_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
child->set_property (X_("style"), enum_2_string (i->second));
}
for (AutomationStateMap::const_iterator i = _automation_state.begin(); i != _automation_state.end(); ++i) {
XMLNode* child = node.add_child (X_("AutomationState"));
child->set_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
child->set_property (X_("state"), enum_2_string (i->second));
}
return node;
}
int
MidiSource::set_state (const XMLNode& node, int /*version*/)
{
node.get_property ("captured-for", _captured_for);
std::string str;
XMLNodeList children = node.children ();
for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
if ((*i)->name() == X_("InterpolationStyle")) {
if (!(*i)->get_property (X_("parameter"), str)) {
error << _("Missing parameter property on InterpolationStyle") << endmsg;
return -1;
}
Evoral::Parameter p = EventTypeMap::instance().from_symbol (str);
if (!(*i)->get_property (X_("style"), str)) {
error << _("Missing style property on InterpolationStyle") << endmsg;
return -1;
}
Evoral::ControlList::InterpolationStyle s =
static_cast<Evoral::ControlList::InterpolationStyle>(string_2_enum (str, s));
set_interpolation_of (p, s);
} else if ((*i)->name() == X_("AutomationState")) {
if (!(*i)->get_property (X_("parameter"), str)) {
error << _("Missing parameter property on AutomationState") << endmsg;
return -1;
}
Evoral::Parameter p = EventTypeMap::instance().from_symbol (str);
if (!(*i)->get_property (X_("state"), str)) {
error << _("Missing state property on AutomationState") << endmsg;
return -1;
}
AutoState s = static_cast<AutoState>(string_2_enum (str, s));
set_automation_state_of (p, s);
}
}
return 0;
}
bool
MidiSource::empty () const
{
return !_length_beats;
}
framecnt_t
MidiSource::length (framepos_t pos) const
{
if (!_length_beats) {
return 0;
}
BeatsFramesConverter converter(_session.tempo_map(), pos);
return converter.to(_length_beats);
}
void
MidiSource::update_length (framecnt_t)
{
// You're not the boss of me!
}
void
MidiSource::invalidate (const Lock& lock)
{
Invalidated(_session.transport_rolling());
}
framecnt_t
MidiSource::midi_read (const Lock& lm,
Evoral::EventSink<framepos_t>& dst,
framepos_t source_start,
framepos_t start,
framecnt_t cnt,
Evoral::Range<framepos_t>* loop_range,
MidiCursor& cursor,
MidiStateTracker* tracker,
MidiChannelFilter* filter,
const std::set<Evoral::Parameter>& filtered,
const double pos_beats,
const double start_beats) const
{
BeatsFramesConverter converter(_session.tempo_map(), source_start);
const double start_qn = pos_beats - start_beats;
DEBUG_TRACE (DEBUG::MidiSourceIO,
string_compose ("MidiSource::midi_read() %5 sstart %1 start %2 cnt %3 tracker %4\n",
source_start, start, cnt, tracker, name()));
if (!_model) {
return read_unlocked (lm, dst, source_start, start, cnt, loop_range, tracker, filter);
}
// Find appropriate model iterator
Evoral::Sequence<Evoral::Beats>::const_iterator& i = cursor.iter;
const bool linear_read = cursor.last_read_end != 0 && start == cursor.last_read_end;
if (!linear_read || !i.valid()) {
/* Cached iterator is invalid, search for the first event past start.
Note that multiple tracks can use a MidiSource simultaneously, so
all playback state must be in parameters (the cursor) and must not
be cached in the source of model itself.
See http://tracker.ardour.org/view.php?id=6541
*/
cursor.connect(Invalidated);
cursor.iter = _model->begin(converter.from(start), false, filtered, &cursor.active_notes);
cursor.active_notes.clear();
}
cursor.last_read_end = start + cnt;
// Copy events in [start, start + cnt) into dst
for (; i != _model->end(); ++i) {
// Offset by source start to convert event time to session time
framepos_t time_frames = _session.tempo_map().frame_at_quarter_note (i->time().to_double() + start_qn);
if (time_frames < start + source_start) {
/* event too early */
continue;
} else if (time_frames >= start + cnt + source_start) {
DEBUG_TRACE (DEBUG::MidiSourceIO,
string_compose ("%1: reached end with event @ %2 vs. %3\n",
_name, time_frames, start+cnt));
break;
} else {
/* in range */
if (loop_range) {
time_frames = loop_range->squish (time_frames);
}
const uint8_t status = i->buffer()[0];
const bool is_channel_event = (0x80 <= (status & 0xF0)) && (status <= 0xE0);
if (filter && is_channel_event) {
/* Copy event so the filter can modify the channel. I'm not
sure if this is necessary here (channels are mapped later in
buffers anyway), but it preserves existing behaviour without
destroying events in the model during read. */
Evoral::Event<Evoral::Beats> ev(*i, true);
if (!filter->filter(ev.buffer(), ev.size())) {
dst.write(time_frames, ev.event_type(), ev.size(), ev.buffer());
} else {
DEBUG_TRACE (DEBUG::MidiSourceIO,
string_compose ("%1: filter event @ %2 type %3 size %4\n",
_name, time_frames, i->event_type(), i->size()));
}
} else {
dst.write (time_frames, i->event_type(), i->size(), i->buffer());
}
#ifndef NDEBUG
if (DEBUG_ENABLED(DEBUG::MidiSourceIO)) {
DEBUG_STR_DECL(a);
DEBUG_STR_APPEND(a, string_compose ("%1 added event @ %2 sz %3 within %4 .. %5 ",
_name, time_frames, i->size(),
start + source_start, start + cnt + source_start));
for (size_t n=0; n < i->size(); ++n) {
DEBUG_STR_APPEND(a,hex);
DEBUG_STR_APPEND(a,"0x");
DEBUG_STR_APPEND(a,(int)i->buffer()[n]);
DEBUG_STR_APPEND(a,' ');
}
DEBUG_STR_APPEND(a,'\n');
DEBUG_TRACE (DEBUG::MidiSourceIO, DEBUG_STR(a).str());
}
#endif
if (tracker) {
tracker->track (*i);
}
}
}
return cnt;
}
framecnt_t
MidiSource::midi_write (const Lock& lm,
MidiRingBuffer<framepos_t>& source,
framepos_t source_start,
framecnt_t cnt)
{
const framecnt_t ret = write_unlocked (lm, source, source_start, cnt);
if (cnt == max_framecnt) {
invalidate(lm);
} else {
_capture_length += cnt;
}
return ret;
}
void
MidiSource::mark_streaming_midi_write_started (const Lock& lock, NoteMode mode)
{
if (_model) {
_model->set_note_mode (mode);
_model->start_write ();
}
_writing = true;
}
void
MidiSource::mark_write_starting_now (framecnt_t position,
framecnt_t capture_length,
framecnt_t loop_length)
{
/* I'm not sure if this is the best way to approach this, but
_capture_length needs to be set up with the transport frame
when a record actually starts, as it is used by
SMFSource::write_unlocked to decide whether incoming notes
are within the correct time range.
mark_streaming_midi_write_started (perhaps a more logical
place to do this) is not called at exactly the time when
record starts, and I don't think it necessarily can be
because it is not RT-safe.
*/
set_timeline_position(position);
_capture_length = capture_length;
_capture_loop_length = loop_length;
TempoMap& map (_session.tempo_map());
BeatsFramesConverter converter(map, position);
_length_beats = converter.from(capture_length);
}
void
MidiSource::mark_streaming_write_started (const Lock& lock)
{
NoteMode note_mode = _model ? _model->note_mode() : Sustained;
mark_streaming_midi_write_started (lock, note_mode);
}
void
MidiSource::mark_midi_streaming_write_completed (const Lock& lock,
Evoral::Sequence<Evoral::Beats>::StuckNoteOption option,
Evoral::Beats end)
{
if (_model) {
_model->end_write (option, end);
/* Make captured controls discrete to play back user input exactly. */
for (MidiModel::Controls::iterator i = _model->controls().begin(); i != _model->controls().end(); ++i) {
if (i->second->list()) {
i->second->list()->set_interpolation(Evoral::ControlList::Discrete);
_interpolation_style.insert(std::make_pair(i->second->parameter(), Evoral::ControlList::Discrete));
}
}
}
invalidate(lock);
_writing = false;
}
void
MidiSource::mark_streaming_write_completed (const Lock& lock)
{
mark_midi_streaming_write_completed (lock, Evoral::Sequence<Evoral::Beats>::DeleteStuckNotes);
}
int
MidiSource::export_write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Evoral::Beats begin, Evoral::Beats end)
{
Lock newsrc_lock (newsrc->mutex ());
if (!_model) {
error << string_compose (_("programming error: %1"), X_("no model for MidiSource during export"));
return -1;
}
_model->write_section_to (newsrc, newsrc_lock, begin, end, true);
newsrc->flush_midi(newsrc_lock);
return 0;
}
int
MidiSource::write_to (const Lock& lock, boost::shared_ptr<MidiSource> newsrc, Evoral::Beats begin, Evoral::Beats end)
{
Lock newsrc_lock (newsrc->mutex ());
newsrc->set_timeline_position (_timeline_position);
newsrc->copy_interpolation_from (this);
newsrc->copy_automation_state_from (this);
if (_model) {
if (begin == Evoral::MinBeats && end == Evoral::MaxBeats) {
_model->write_to (newsrc, newsrc_lock);
} else {
_model->write_section_to (newsrc, newsrc_lock, begin, end);
}
} else {
error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
return -1;
}
newsrc->flush_midi(newsrc_lock);
/* force a reload of the model if the range is partial */
if (begin != Evoral::MinBeats || end != Evoral::MaxBeats) {
newsrc->load_model (newsrc_lock, true);
} else {
newsrc->set_model (newsrc_lock, _model);
}
/* this file is not removable (but since it is MIDI, it is mutable) */
boost::dynamic_pointer_cast<FileSource> (newsrc)->prevent_deletion ();
return 0;
}
void
MidiSource::session_saved()
{
Lock lm (_lock);
/* this writes a copy of the data to disk.
XXX do we need to do this every time?
*/
if (_model && _model->edited()) {
/* The model is edited, write its contents into the current source
file (overwiting previous contents). */
/* Temporarily drop our reference to the model so that as the model
pushes its current state to us, we don't try to update it. */
boost::shared_ptr<MidiModel> mm = _model;
_model.reset ();
/* Flush model contents to disk. */
mm->sync_to_source (lm);
/* Reacquire model. */
_model = mm;
} else {
flush_midi(lm);
}
}
void
MidiSource::set_note_mode(const Lock& lock, NoteMode mode)
{
if (_model) {
_model->set_note_mode(mode);
}
}
void
MidiSource::drop_model (const Lock& lock)
{
_model.reset();
invalidate(lock);
ModelChanged (); /* EMIT SIGNAL */
}
void
MidiSource::set_model (const Lock& lock, boost::shared_ptr<MidiModel> m)
{
_model = m;
invalidate(lock);
ModelChanged (); /* EMIT SIGNAL */
}
Evoral::ControlList::InterpolationStyle
MidiSource::interpolation_of (Evoral::Parameter p) const
{
InterpolationStyleMap::const_iterator i = _interpolation_style.find (p);
if (i == _interpolation_style.end()) {
return EventTypeMap::instance().interpolation_of (p);
}
return i->second;
}
AutoState
MidiSource::automation_state_of (Evoral::Parameter p) const
{
AutomationStateMap::const_iterator i = _automation_state.find (p);
if (i == _automation_state.end()) {
/* default to `play', otherwise if MIDI is recorded /
imported with controllers etc. they are by default
not played back, which is a little surprising.
*/
return Play;
}
return i->second;
}
/** Set interpolation style to be used for a given parameter. This change will be
* propagated to anyone who needs to know.
*/
void
MidiSource::set_interpolation_of (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
{
if (interpolation_of (p) == s) {
return;
}
if (EventTypeMap::instance().interpolation_of (p) == s) {
/* interpolation type is being set to the default, so we don't need a note in our map */
_interpolation_style.erase (p);
} else {
_interpolation_style[p] = s;
}
InterpolationChanged (p, s); /* EMIT SIGNAL */
}
void
MidiSource::set_automation_state_of (Evoral::Parameter p, AutoState s)
{
if (automation_state_of (p) == s) {
return;
}
if (s == Play) {
/* automation state is being set to the default, so we don't need a note in our map */
_automation_state.erase (p);
} else {
_automation_state[p] = s;
}
AutomationStateChanged (p, s); /* EMIT SIGNAL */
}
void
MidiSource::copy_interpolation_from (boost::shared_ptr<MidiSource> s)
{
copy_interpolation_from (s.get ());
}
void
MidiSource::copy_automation_state_from (boost::shared_ptr<MidiSource> s)
{
copy_automation_state_from (s.get ());
}
void
MidiSource::copy_interpolation_from (MidiSource* s)
{
_interpolation_style = s->_interpolation_style;
/* XXX: should probably emit signals here */
}
void
MidiSource::copy_automation_state_from (MidiSource* s)
{
_automation_state = s->_automation_state;
/* XXX: should probably emit signals here */
}

View File

@ -1,247 +0,0 @@
/* This file is part of Evoral.
* Copyright (C) 2008-2015 David 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_BEATS_HPP
#define EVORAL_BEATS_HPP
#include <float.h>
#include <math.h>
#include <stdint.h>
#include <iostream>
#include <limits>
#include "evoral/visibility.h"
namespace Evoral {
/** Musical time in beats. */
class /*LIBEVORAL_API*/ Beats {
public:
LIBEVORAL_API static const double PPQN;
Beats() : _time(0.0) {}
/** Create from a real number of beats. */
explicit Beats(double time) : _time(time) {}
/** Create from an integer number of beats. */
static Beats beats(int32_t beats) {
return Beats((double)beats);
}
/** Create from ticks at the standard PPQN. */
static Beats ticks(uint32_t ticks) {
return Beats(ticks / PPQN);
}
/** Create from ticks at a given rate.
*
* Note this can also be used to create from frames by setting ppqn to the
* number of samples per beat.
*/
static Beats ticks_at_rate(uint64_t ticks, uint32_t ppqn) {
return Beats((double)ticks / (double)ppqn);
}
Beats& operator=(const Beats& other) {
_time = other._time;
return *this;
}
Beats round_up_to_beat() const {
return Evoral::Beats(ceil(_time));
}
Beats round_down_to_beat() const {
return Evoral::Beats(floor(_time));
}
Beats snap_to(const Evoral::Beats& snap) const {
return Beats(ceil(_time / snap._time) * snap._time);
}
inline bool operator==(const Beats& b) const {
/* Acceptable tolerance is 1 tick. */
return fabs(_time - b._time) <= (1.0 / PPQN);
}
inline bool operator==(double t) const {
/* Acceptable tolerance is 1 tick. */
return fabs(_time - t) <= (1.0 / PPQN);
}
inline bool operator==(int beats) const {
/* Acceptable tolerance is 1 tick. */
return fabs(_time - beats) <= (1.0 / PPQN);
}
inline bool operator!=(const Beats& b) const {
return !operator==(b);
}
inline bool operator<(const Beats& b) const {
/* Acceptable tolerance is 1 tick. */
if (fabs(_time - b._time) <= (1.0 / PPQN)) {
return false; /* Effectively identical. */
} else {
return _time < b._time;
}
}
inline bool operator<=(const Beats& b) const {
return operator==(b) || operator<(b);
}
inline bool operator>(const Beats& b) const {
/* Acceptable tolerance is 1 tick. */
if (fabs(_time - b._time) <= (1.0 / PPQN)) {
return false; /* Effectively identical. */
} else {
return _time > b._time;
}
}
inline bool operator>=(const Beats& b) const {
return operator==(b) || operator>(b);
}
inline bool operator<(double b) const {
/* Acceptable tolerance is 1 tick. */
if (fabs(_time - b) <= (1.0 / PPQN)) {
return false; /* Effectively identical. */
} else {
return _time < b;
}
}
inline bool operator<=(double b) const {
return operator==(b) || operator<(b);
}
inline bool operator>(double b) const {
/* Acceptable tolerance is 1 tick. */
if (fabs(_time - b) <= (1.0 / PPQN)) {
return false; /* Effectively identical. */
} else {
return _time > b;
}
}
inline bool operator>=(double b) const {
return operator==(b) || operator>(b);
}
Beats operator+(const Beats& b) const {
return Beats(_time + b._time);
}
Beats operator-(const Beats& b) const {
return Beats(_time - b._time);
}
Beats operator+(double d) const {
return Beats(_time + d);
}
Beats operator-(double d) const {
return Beats(_time - d);
}
Beats operator-() const {
return Beats(-_time);
}
template<typename Number>
Beats operator*(Number factor) const {
return Beats(_time * factor);
}
Beats& operator+=(const Beats& b) {
_time += b._time;
return *this;
}
Beats& operator-=(const Beats& b) {
_time -= b._time;
return *this;
}
double to_double() const { return _time; }
uint64_t to_ticks() const { return lrint(_time * PPQN); }
uint64_t to_ticks(uint32_t ppqn) const { return lrint(_time * ppqn); }
uint32_t get_beats() const { return floor(_time); }
uint32_t get_ticks() const { return (uint32_t)lrint(fmod(_time, 1.0) * PPQN); }
bool operator!() const { return _time == 0; }
static Beats min() { return Beats(DBL_MIN); }
static Beats max() { return Beats(DBL_MAX); }
static Beats tick() { return Beats(1.0 / PPQN); }
private:
double _time;
};
extern LIBEVORAL_API const Beats MaxBeats;
extern LIBEVORAL_API const Beats MinBeats;
/*
TIL, several horrible hours later, that sometimes the compiler looks in the
namespace of a type (Evoral::Beats in this case) for an operator, and
does *NOT* look in the global namespace.
C++ is proof that hell exists and we are living in it. In any case, move
these to the global namespace and PBD::Property's loopy
virtual-method-in-a-template will bite you.
*/
inline std::ostream&
operator<<(std::ostream& os, const Beats& t)
{
os << t.to_double();
return os;
}
inline std::istream&
operator>>(std::istream& is, Beats& t)
{
double beats;
is >> beats;
t = Beats(beats);
return is;
}
} // namespace Evoral
namespace PBD {
namespace DEBUG {
LIBEVORAL_API extern uint64_t Beats;
}
}
namespace std {
template<>
struct numeric_limits<Evoral::Beats> {
static Evoral::Beats min() { return Evoral::Beats::min(); }
static Evoral::Beats max() { return Evoral::Beats::max(); }
};
}
#endif // EVORAL_BEATS_HPP

View File

@ -1,168 +0,0 @@
#!/usr/bin/env python
from waflib.extras import autowaf as autowaf
from waflib import Options
import os
# Version of this package (even if built as a child)
EVORAL_VERSION = '0.0.0'
# Library version (UNIX style major, minor, micro)
# major increment <=> incompatible changes
# minor increment <=> compatible changes (additions)
# micro increment <=> no interface changes
# Version history:
# 0.0.0 = 0,0,0
EVORAL_LIB_VERSION = '0.0.0'
# Variables for 'waf dist'
APPNAME = 'evoral'
VERSION = EVORAL_VERSION
# Mandatory variables
top = '.'
out = 'build'
def options(opt):
opt.load('compiler_c')
opt.load('compiler_cxx')
autowaf.set_options(opt)
opt.add_option('--test', action='store_true', default=False, dest='build_tests',
help="Build unit tests")
opt.add_option('--test-coverage', action='store_true', default=False, dest='test_coverage',
help="Use gcov to test for code coverage")
opt.add_option('--internal-shared-libs', action='store_true', default=True, dest='internal_shared_libs',
help='Build internal libs as shared libraries')
def configure(conf):
conf.load('compiler_c')
conf.load('compiler_cxx')
autowaf.configure(conf)
#autowaf.display_header('Evoral Configuration')
autowaf.check_pkg(conf, 'cppunit', uselib_store='CPPUNIT', atleast_version='1.12.0', mandatory=False)
autowaf.check_pkg(conf, 'glib-2.0', uselib_store='GLIB', atleast_version='2.2')
autowaf.check_pkg(conf, 'glibmm-2.4', uselib_store='GLIBMM', atleast_version='2.14.0')
autowaf.check_pkg(conf, 'gthread-2.0', uselib_store='GTHREAD', atleast_version='2.14.0')
if not autowaf.is_child():
autowaf.check_pkg(conf, 'libpbd-4', uselib_store='LIBPBD', atleast_version='4.0.0', mandatory=True)
# Boost headers
autowaf.check_header(conf, 'cxx', 'boost/shared_ptr.hpp')
autowaf.check_header(conf, 'cxx', 'boost/weak_ptr.hpp')
conf.env['BUILD_TESTS'] = Options.options.build_tests
conf.env['TEST_COVERAGE'] = Options.options.test_coverage
if Options.options.internal_shared_libs:
conf.define('INTERNAL_SHARED_LIBS', 1)
#autowaf.display_msg(conf, "Unit tests", str(conf.env['BUILD_TESTS']))
#print
def build(bld):
# Headers
#bld.install_files('${INCLUDEDIR}/evoral', 'evoral/*.h')
#bld.install_files('${INCLUDEDIR}/evoral', 'evoral/*.hpp')
# Pkgconfig file
#autowaf.build_pc(bld, 'EVORAL', EVORAL_VERSION, 'GLIBMM GTHREAD')
libsmf = bld(features = 'c cstlib')
libsmf.source = '''
src/libsmf/smf.c
src/libsmf/smf_decode.c
src/libsmf/smf_load.c
src/libsmf/smf_save.c
src/libsmf/smf_tempo.c
'''
libsmf.export_includes = ['./src/libsmf']
libsmf.defines = ['SMF_VERSION="1.2"', 'LIBSMF_DLL_EXPORTS']
libsmf.includes = ['./src']
libsmf.name = 'libsmf'
libsmf.target = 'smf'
libsmf.uselib = 'GLIB'
libsmf.install_path = None
if bld.env['build_target'] != 'mingw':
libsmf.cxxflags = [ '-fPIC' ]
libsmf.cflags = [ '-fPIC' ]
lib_source = '''
src/Control.cpp
src/ControlList.cpp
src/ControlSet.cpp
src/Curve.cpp
src/Event.cpp
src/Note.cpp
src/SMF.cpp
src/Sequence.cpp
src/TimeConverter.cpp
src/debug.cpp
src/types.cpp
'''
# Library
if bld.is_defined ('INTERNAL_SHARED_LIBS'):
obj = bld.shlib(features = 'c cxx cshlib cxxshlib', source=lib_source)
# DLL exports for this library
obj.defines = [ 'LIBEVORAL_DLL_EXPORTS' ]
else:
obj = bld.stlib(features = 'c cxx cstlib cxxstlib', source=lib_source)
obj.cxxflags = [ '-fPIC' ]
obj.cflags = [ '-fPIC' ]
obj.defines = [ ]
obj.export_includes = ['.']
obj.includes = ['.', './src']
obj.name = 'libevoral'
obj.target = 'evoral'
obj.uselib = 'GLIBMM GTHREAD SMF XML LIBPBD'
obj.use = 'libsmf libpbd'
obj.vnum = EVORAL_LIB_VERSION
obj.install_path = bld.env['LIBDIR']
obj.defines += [ 'PACKAGE="libevoral"' ]
if bld.env['BUILD_TESTS'] and bld.is_defined('HAVE_CPPUNIT'):
# Static library (for unit test code coverage)
obj = bld(features = 'cxx cstlib')
obj.source = lib_source
obj.export_includes = ['.']
obj.includes = ['.', './src']
obj.name = 'libevoral_static'
obj.target = 'evoral_static'
obj.uselib = 'GLIBMM GTHREAD SMF XML LIBPBD'
obj.use = 'libsmf libpbd'
obj.vnum = EVORAL_LIB_VERSION
obj.install_path = ''
if bld.env['TEST_COVERAGE']:
obj.linkflags = ['--coverage']
obj.cflags = ['--coverage']
obj.cxxflags = ['--coverage']
obj.defines = ['PACKAGE="libevoral"']
# Unit tests
obj = bld(features = 'cxx cxxprogram')
obj.source = '''
test/SequenceTest.cpp
test/SMFTest.cpp
test/RangeTest.cpp
test/NoteTest.cpp
test/CurveTest.cpp
test/testrunner.cpp
'''
obj.includes = ['.', './src']
obj.use = 'libevoral_static'
obj.uselib = 'CPPUNIT SNDFILE LIBPBD'
obj.target = 'run-tests'
obj.name = 'libevoral-tests'
obj.install_path = ''
obj.defines = ['PACKAGE="libevoraltest"']
if bld.env['TEST_COVERAGE']:
obj.linkflags = ['--coverage']
obj.cflags = ['--coverage']
obj.cxxflags = ['--coverage']
def test(ctx):
autowaf.pre_test(ctx, APPNAME)
print(os.getcwd())
os.environ['EVORAL_TEST_PATH'] = os.path.abspath('../test/testdata/')
autowaf.run_tests(ctx, APPNAME, ['./run-tests'])
autowaf.post_test(ctx, APPNAME)