summaryrefslogtreecommitdiff
path: root/libs/ardour/transform.cc
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/transform.cc
parentec947ff8fd2cf229284f757b8bd6b0f96cbd6383 (diff)
MIDI transform dialog.
Diffstat (limited to 'libs/ardour/transform.cc')
-rw-r--r--libs/ardour/transform.cc162
1 files changed, 162 insertions, 0 deletions
diff --git a/libs/ardour/transform.cc b/libs/ardour/transform.cc
new file mode 100644
index 0000000000..ddf23aeecc
--- /dev/null
+++ b/libs/ardour/transform.cc
@@ -0,0 +1,162 @@
+/*
+ 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.
+*/
+
+#include <glib.h>
+
+#include "ardour/transform.h"
+#include "ardour/midi_model.h"
+
+namespace ARDOUR {
+
+Transform::Transform(const Program& prog)
+ : _prog(prog)
+{}
+
+Variant
+Transform::Context::pop()
+{
+ if (stack.empty()) {
+ return Variant();
+ }
+
+ const Variant top = stack.top();
+ stack.pop();
+ return top;
+}
+
+Variant
+Transform::Value::eval(const Context& ctx) const
+{
+ switch (source) {
+ case NOWHERE:
+ return Variant();
+ case THIS_NOTE:
+ return MidiModel::NoteDiffCommand::get_value(ctx.this_note, prop);
+ case PREV_NOTE:
+ if (!ctx.prev_note) {
+ return Variant();
+ }
+ return MidiModel::NoteDiffCommand::get_value(ctx.prev_note, prop);
+ case INDEX:
+ return Variant(Variant::INT, ctx.index);
+ case N_NOTES:
+ return Variant(Variant::INT, ctx.n_notes);
+ case LITERAL:
+ return value;
+ case RANDOM:
+ return Variant(g_random_double());
+ }
+}
+
+void
+Transform::Operation::eval(Context& ctx) const
+{
+ if (op == PUSH) {
+ const Variant a = arg.eval(ctx);
+ if (!!a) {
+ /* Argument evaluated to a value, push it to the stack. Otherwise,
+ there was a reference to the previous note, but this is the
+ first, so skip this operation and do nothing. */
+ ctx.stack.push(a);
+ }
+ return;
+ }
+
+ // Pop operands off the stack
+ const Variant rhs = ctx.pop();
+ const Variant lhs = ctx.pop();
+ if (!lhs || !rhs) {
+ // Stack underflow (probably previous note reference), do nothing
+ return;
+ }
+
+ // We can get away with just using double math and converting twice
+ double value = lhs.to_double();
+ switch (op) {
+ case ADD:
+ value += rhs.to_double();
+ break;
+ case SUB:
+ value -= rhs.to_double();
+ break;
+ case MULT:
+ value *= rhs.to_double();
+ break;
+ case DIV:
+ if (rhs.to_double() == 0.0) {
+ return; // Program will fail safely
+ }
+ value /= rhs.to_double();
+ break;
+ default: break;
+ }
+
+ // Push result on to the stack
+ ctx.stack.push(Variant(lhs.type(), value));
+}
+
+Command*
+Transform::operator()(boost::shared_ptr<MidiModel> model,
+ Evoral::MusicalTime position,
+ std::vector<Notes>& seqs)
+{
+ typedef MidiModel::NoteDiffCommand Command;
+
+ Command* cmd = new Command(model, name());
+
+ for (std::vector<Notes>::iterator s = seqs.begin(); s != seqs.end(); ++s) {
+ Context ctx;
+ ctx.n_notes = (*s).size();
+ for (Notes::const_iterator i = (*s).begin(); i != (*s).end(); ++i) {
+ const NotePtr note = *i;
+
+ // Clear stack and run program
+ ctx.stack = std::stack<Variant>();
+ ctx.this_note = note;
+ for (std::list<Operation>::const_iterator o = _prog.ops.begin();
+ o != _prog.ops.end();
+ ++o) {
+ (*o).eval(ctx);
+ }
+
+ // Result is on top of the stack
+ if (!ctx.stack.empty() && !!ctx.stack.top()) {
+ // Get the result from the top of the stack
+ Variant result = ctx.stack.top();
+ if (result.type() != Command::value_type(_prog.prop)) {
+ // Coerce to appropriate type
+ result = Variant(Command::value_type(_prog.prop),
+ result.to_double());
+ }
+
+ // Apply change
+ cmd->change(note, _prog.prop, result);
+ }
+ // else error or reference to note before the first, skip
+
+ // Move forward
+ ctx.prev_note = note;
+ ++ctx.index;
+ }
+ }
+
+ return cmd;
+}
+
+} // namespace ARDOUR