diff options
author | David Robillard <d@drobilla.net> | 2014-12-28 15:50:57 -0500 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2014-12-28 16:06:44 -0500 |
commit | 4c0cebf7f98ecd14873d26b6f4d8bdedd37cb994 (patch) | |
tree | 8efbd17e96fc1c56392ff77e2ef3d99e5f3af731 /libs/ardour/ardour/transform.h | |
parent | ec947ff8fd2cf229284f757b8bd6b0f96cbd6383 (diff) |
MIDI transform dialog.
Diffstat (limited to 'libs/ardour/ardour/transform.h')
-rw-r--r-- | libs/ardour/ardour/transform.h | 145 |
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__ */ |