fix a few compiler warnings
[ardour.git] / libs / ardour / transform.cc
1 /*
2     Copyright (C) 2014 Paul Davis
3     Author: David Robillard
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <glib.h>
21
22 #include "ardour/transform.h"
23 #include "ardour/midi_model.h"
24
25 namespace ARDOUR {
26
27 Transform::Transform(const Program& prog)
28         : _prog(prog)
29 {}
30
31 Variant
32 Transform::Context::pop()
33 {
34         if (stack.empty()) {
35                 return Variant();
36         }
37
38         const Variant top = stack.top();
39         stack.pop();
40         return top;
41 }
42
43 Variant
44 Transform::Value::eval(const Context& ctx) const
45 {
46         switch (source) {
47         case NOWHERE:
48                 return Variant();
49         case THIS_NOTE:
50                 return MidiModel::NoteDiffCommand::get_value(ctx.this_note, prop);
51         case PREV_NOTE:
52                 if (!ctx.prev_note) {
53                         return Variant();
54                 }
55                 return MidiModel::NoteDiffCommand::get_value(ctx.prev_note, prop);
56         case INDEX:
57                 return Variant(Variant::INT, ctx.index);
58         case N_NOTES:
59                 return Variant(Variant::INT, ctx.n_notes);
60         case LITERAL:
61                 return value;
62         case RANDOM:
63                 return Variant(g_random_double());
64         }
65
66         return Variant ();
67 }
68
69 void
70 Transform::Operation::eval(Context& ctx) const
71 {
72         if (op == PUSH) {
73                 const Variant a = arg.eval(ctx);
74                 if (!!a) {
75                         /* Argument evaluated to a value, push it to the stack.  Otherwise,
76                            there was a reference to the previous note, but this is the
77                            first, so skip this operation and do nothing. */
78                         ctx.stack.push(a);
79                 }
80                 return;
81         }
82
83         // Pop operands off the stack
84         const Variant rhs = ctx.pop();
85         const Variant lhs = ctx.pop();
86         if (!lhs || !rhs) {
87                 // Stack underflow (probably previous note reference), do nothing
88                 return;
89         }
90
91         // We can get away with just using double math and converting twice
92         double value = lhs.to_double();
93         switch (op) {
94         case ADD:
95                 value += rhs.to_double();
96                 break;
97         case SUB:
98                 value -= rhs.to_double();
99                 break;
100         case MULT:
101                 value *= rhs.to_double();
102                 break;
103         case DIV:
104                 if (rhs.to_double() == 0.0) {
105                         return;  // Program will fail safely
106                 }
107                 value /= rhs.to_double();
108                 break;
109         default: break;
110         }
111
112         // Push result on to the stack
113         ctx.stack.push(Variant(lhs.type(), value));
114 }
115
116 Command*
117 Transform::operator()(boost::shared_ptr<MidiModel> model,
118                       Evoral::MusicalTime          position,
119                       std::vector<Notes>&          seqs)
120 {
121         typedef MidiModel::NoteDiffCommand Command;
122
123         Command* cmd = new Command(model, name());
124
125         for (std::vector<Notes>::iterator s = seqs.begin(); s != seqs.end(); ++s) {
126                 Context ctx;
127                 ctx.n_notes = (*s).size();
128                 for (Notes::const_iterator i = (*s).begin(); i != (*s).end(); ++i) {
129                         const NotePtr note = *i;
130
131                         // Clear stack and run program
132                         ctx.stack     = std::stack<Variant>();
133                         ctx.this_note = note;
134                         for (std::list<Operation>::const_iterator o = _prog.ops.begin();
135                              o != _prog.ops.end();
136                              ++o) {
137                                 (*o).eval(ctx);
138                         }
139
140                         // Result is on top of the stack
141                         if (!ctx.stack.empty() && !!ctx.stack.top()) {
142                                 // Get the result from the top of the stack
143                                 Variant result = ctx.stack.top();
144                                 if (result.type() != Command::value_type(_prog.prop)) {
145                                         // Coerce to appropriate type
146                                         result = Variant(Command::value_type(_prog.prop),
147                                                          result.to_double());
148                                 }
149
150                                 // Apply change
151                                 cmd->change(note, _prog.prop, result);
152                         }
153                         // else error or reference to note before the first, skip
154
155                         // Move forward
156                         ctx.prev_note = note;
157                         ++ctx.index;
158                 }
159         }
160
161         return cmd;
162 }
163
164 }  // namespace ARDOUR