summaryrefslogtreecommitdiff
path: root/libs/ardour/ardour/transform.h
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-12-28 15:50:57 -0500
committerDavid Robillard <d@drobilla.net>2014-12-28 16:06:44 -0500
commit4c0cebf7f98ecd14873d26b6f4d8bdedd37cb994 (patch)
tree8efbd17e96fc1c56392ff77e2ef3d99e5f3af731 /libs/ardour/ardour/transform.h
parentec947ff8fd2cf229284f757b8bd6b0f96cbd6383 (diff)
MIDI transform dialog.
Diffstat (limited to 'libs/ardour/ardour/transform.h')
-rw-r--r--libs/ardour/ardour/transform.h145
1 files changed, 145 insertions, 0 deletions
diff --git a/libs/ardour/ardour/transform.h b/libs/ardour/ardour/transform.h
new file mode 100644
index 0000000000..2b63bb6af0
--- /dev/null
+++ b/libs/ardour/ardour/transform.h
@@ -0,0 +1,145 @@
+/*
+ Copyright (C) 2014 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_transform_h__
+#define __ardour_transform_h__
+
+#include <stack>
+#include <string>
+
+#include "ardour/libardour_visibility.h"
+#include "ardour/midi_model.h"
+#include "ardour/midi_operator.h"
+#include "ardour/types.h"
+#include "ardour/variant.h"
+
+namespace ARDOUR {
+
+/** Transform notes with a user-defined transformation.
+ *
+ * This is essentially an interpreter for a simple concatenative note
+ * transformation language (as an AST only, no source code). A "program"
+ * calculates a note property value from operations on literal values, and/or
+ * values from the current or previous note in the sequence. This allows
+ * simple things like "set all notes' velocity to 64" or transitions over time
+ * like "set velocity to the previous note's velocity + 10".
+ *
+ * The language is forth-like: everything is on a stack, operations pop their
+ * arguments from the stack and push their result back on to it.
+ *
+ * This is a sweet spot between simplicity and power, it should be simple to
+ * use this (with perhaps some minor extensions) to do most "linear-ish"
+ * transformations, though it could be extended to have random access
+ * and more special values as the need arises.
+ */
+class LIBARDOUR_API Transform : public MidiOperator {
+public:
+ typedef Evoral::Sequence<Evoral::MusicalTime>::NotePtr NotePtr;
+ typedef Evoral::Sequence<Evoral::MusicalTime>::Notes Notes;
+ typedef ARDOUR::MidiModel::NoteDiffCommand::Property Property;
+
+ /** Context while iterating over notes during transformation. */
+ struct Context {
+ Context() : index(0) {}
+
+ Variant pop();
+
+ std::stack<Variant> stack; ///< The stack of everything
+ size_t index; ///< Index of current note
+ size_t n_notes; ///< Total number of notes to process
+ NotePtr prev_note; ///< Previous note
+ NotePtr this_note; ///< Current note
+ };
+
+ /** Value in a transformation expression. */
+ struct Value {
+ /** Value source. Some of these would be better modeled as properties,
+ like note.index or sequence.size, but until the sequence stuff is
+ more fundamentally property based, we special-case them here. */
+ enum Source {
+ NOWHERE, ///< Null
+ THIS_NOTE, ///< Value from this note
+ PREV_NOTE, ///< Value from the previous note
+ INDEX, ///< Index of the current note
+ N_NOTES, ///< Total number of notes to process
+ LITERAL, ///< Given literal value
+ RANDOM ///< Random normal
+ };
+
+ Value() : source(NOWHERE) {}
+ Value(Source s) : source(s) {}
+ Value(const Variant& v) : source(LITERAL), value(v) {}
+ Value(double v) : source(LITERAL), value(Variant(v)) {}
+
+ /** Calculate and return value. */
+ Variant eval(const Context& context) const;
+
+ Source source; ///< Source of value
+ Variant value; ///< Value for LITERAL
+ Property prop; ///< Property for all other sources
+ };
+
+ /** An operation to transform the running result.
+ *
+ * All operations except PUSH take their arguments from the stack, and put
+ * the result back on the stack.
+ */
+ struct Operation {
+ enum Operator {
+ PUSH, ///< Push argument to the stack
+ ADD, ///< Add top two values
+ SUB, ///< Subtract top from second-top
+ MULT, ///< Multiply top two values
+ DIV ///< Divide second-top by top
+ };
+
+ Operation(Operator o, const Value& a=Value()) : op(o), arg(a) {}
+
+ /** Apply operation. */
+ void eval(Context& context) const;
+
+ Operator op;
+ Value arg;
+ };
+
+ /** A transformation program.
+ *
+ * A program is a list of operations to calculate the target property's
+ * final value. The first operation must be a PUSH to seed the stack.
+ */
+ struct Program {
+ Property prop; ///< Property to calculate
+ std::list<Operation> ops; ///< List of operations
+ };
+
+ Transform(const Program& prog);
+
+ Command* operator()(boost::shared_ptr<ARDOUR::MidiModel> model,
+ Evoral::MusicalTime position,
+ std::vector<Notes>& seqs);
+
+ std::string name() const { return std::string ("transform"); }
+
+private:
+ const Program _prog;
+};
+
+} /* namespace */
+
+#endif /* __ardour_transform_h__ */