/[svn]/linuxsampler/trunk/src/scriptvm/ScriptVM.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/scriptvm/ScriptVM.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2879 - (show annotations) (download)
Tue Apr 19 14:07:53 2016 UTC (7 years, 11 months ago) by schoenebeck
File size: 11651 byte(s)
* All engines: Active voices are now internally grouped to "Note" objects,
  instead of being directly assigned to a keyboard key. This allows more
  fine graded processing of voices, which is i.e. required for certain
  instrument script features.
* Built-in script function "play_note()": Added support for passing
  special value -1 for "duration-us" argument, which will cause the
  triggered note to be released once the original note was released.
* Bumped version (2.0.0.svn3).

1 /*
2 * Copyright (c) 2014 - 2016 Christian Schoenebeck
3 *
4 * http://www.linuxsampler.org
5 *
6 * This file is part of LinuxSampler and released under the same terms.
7 * See README file for details.
8 */
9
10 #include "ScriptVM.h"
11
12 #include <string.h>
13 #include <assert.h>
14 #include "../common/global_private.h"
15 #include "tree.h"
16
17 #define DEBUG_SCRIPTVM_CORE 0
18
19 int InstrScript_parse(LinuxSampler::ParserContext*);
20
21 namespace LinuxSampler {
22
23 static void _printIndents(int n) {
24 for (int i = 0; i < n; ++i) printf(" ");
25 fflush(stdout);
26 }
27
28 static int _requiredMaxStackSizeFor(Statement* statement, int depth = 0) {
29 if (!statement) return 1;
30
31 switch (statement->statementType()) {
32 case STMT_LEAF:
33 #if DEBUG_SCRIPTVM_CORE
34 _printIndents(depth);
35 printf("-> STMT_LEAF\n");
36 #endif
37 return 1;
38
39 case STMT_LIST: {
40 #if DEBUG_SCRIPTVM_CORE
41 _printIndents(depth);
42 printf("-> STMT_LIST\n");
43 #endif
44 Statements* stmts = (Statements*) statement;
45 int max = 0;
46 for (int i = 0; stmts->statement(i); ++i) {
47 int size = _requiredMaxStackSizeFor( stmts->statement(i), depth+1 );
48 if (max < size) max = size;
49 }
50 return max + 1;
51 }
52
53 case STMT_BRANCH: {
54 #if DEBUG_SCRIPTVM_CORE
55 _printIndents(depth);
56 printf("-> STMT_BRANCH\n");
57 #endif
58 BranchStatement* branchStmt = (BranchStatement*) statement;
59 int max = 0;
60 for (int i = 0; branchStmt->branch(i); ++i) {
61 int size = _requiredMaxStackSizeFor( branchStmt->branch(i), depth+1 );
62 if (max < size) max = size;
63 }
64 return max + 1;
65 }
66
67 case STMT_LOOP: {
68 #if DEBUG_SCRIPTVM_CORE
69 _printIndents(depth);
70 printf("-> STMT_LOOP\n");
71 #endif
72 While* whileStmt = (While*) statement;
73 if (whileStmt->statements())
74 return _requiredMaxStackSizeFor( whileStmt->statements() ) + 1;
75 else
76 return 1;
77 }
78 }
79
80 return 1; // actually just to avoid compiler warning
81 }
82
83 static int _requiredMaxStackSizeFor(EventHandlers* handlers) {
84 int max = 1;
85 for (int i = 0; i < handlers->size(); ++i) {
86 int size = _requiredMaxStackSizeFor(handlers->eventHandler(i));
87 if (max < size) max = size;
88 }
89 return max;
90 }
91
92 ScriptVM::ScriptVM() : m_eventHandler(NULL), m_parserContext(NULL), fnWait(this) {
93 }
94
95 ScriptVM::~ScriptVM() {
96 }
97
98 VMParserContext* ScriptVM::loadScript(const String& s) {
99 std::istringstream iss(s);
100 return loadScript(&iss);
101 }
102
103 VMParserContext* ScriptVM::loadScript(std::istream* is) {
104 ParserContext* context = new ParserContext(this);
105 //printf("parserCtx=0x%lx\n", (uint64_t)context);
106
107 context->registerBuiltInConstIntVariables( builtInConstIntVariables() );
108 context->registerBuiltInIntVariables( builtInIntVariables() );
109 context->registerBuiltInIntArrayVariables( builtInIntArrayVariables() );
110
111 context->createScanner(is);
112
113 InstrScript_parse(context);
114 dmsg(2,("Allocating %ld bytes of global int VM memory.\n", long(context->globalIntVarCount * sizeof(int))));
115 dmsg(2,("Allocating %d of global VM string variables.\n", context->globalStrVarCount));
116 if (!context->globalIntMemory)
117 context->globalIntMemory = new ArrayList<int>();
118 if (!context->globalStrMemory)
119 context->globalStrMemory = new ArrayList<String>();
120 context->globalIntMemory->resize(context->globalIntVarCount);
121 memset(&((*context->globalIntMemory)[0]), 0, context->globalIntVarCount * sizeof(int));
122
123 context->globalStrMemory->resize(context->globalStrVarCount);
124
125 context->destroyScanner();
126
127 return context;
128 }
129
130 void ScriptVM::dumpParsedScript(VMParserContext* context) {
131 ParserContext* ctx = dynamic_cast<ParserContext*>(context);
132 if (!ctx) {
133 std::cerr << "No VM context. So nothing to dump.\n";
134 return;
135 }
136 if (!ctx->handlers) {
137 std::cerr << "No event handlers defined in script. So nothing to dump.\n";
138 return;
139 }
140 if (!ctx->globalIntMemory) {
141 std::cerr << "Internal error: no global memory assigend to script VM.\n";
142 return;
143 }
144 ctx->handlers->dump();
145 }
146
147 VMExecContext* ScriptVM::createExecContext(VMParserContext* parserContext) {
148 ParserContext* parserCtx = dynamic_cast<ParserContext*>(parserContext);
149 ExecContext* execCtx = new ExecContext();
150
151 if (parserCtx->requiredMaxStackSize < 0) {
152 parserCtx->requiredMaxStackSize =
153 _requiredMaxStackSizeFor(&*parserCtx->handlers);
154 }
155 execCtx->stack.resize(parserCtx->requiredMaxStackSize);
156 dmsg(2,("Created VM exec context with %ld bytes VM stack size.\n",
157 long(parserCtx->requiredMaxStackSize * sizeof(ExecContext::StackFrame))));
158 //printf("execCtx=0x%lx\n", (uint64_t)execCtx);
159 const int polySize = parserCtx->polyphonicIntVarCount;
160 execCtx->polyphonicIntMemory.resize(polySize);
161 memset(&execCtx->polyphonicIntMemory[0], 0, polySize * sizeof(int));
162
163 dmsg(2,("Allocated %ld bytes polyphonic memory.\n", long(polySize * sizeof(int))));
164 return execCtx;
165 }
166
167 VMFunction* ScriptVM::functionByName(const String& name) {
168 if (name == "message") return &fnMessage;
169 else if (name == "exit") return &fnExit;
170 else if (name == "wait") return &fnWait;
171 else if (name == "abs") return &fnAbs;
172 else if (name == "random") return &fnRandom;
173 else if (name == "num_elements") return &fnNumElements;
174 return NULL;
175 }
176
177 std::map<String,VMIntRelPtr*> ScriptVM::builtInIntVariables() {
178 return std::map<String,VMIntRelPtr*>();
179 }
180
181 std::map<String,VMInt8Array*> ScriptVM::builtInIntArrayVariables() {
182 return std::map<String,VMInt8Array*>();
183 }
184
185 std::map<String,int> ScriptVM::builtInConstIntVariables() {
186 return std::map<String,int>();
187 }
188
189 VMEventHandler* ScriptVM::currentVMEventHandler() {
190 return m_eventHandler;
191 }
192
193 VMParserContext* ScriptVM::currentVMParserContext() {
194 return m_parserContext;
195 }
196
197 VMExecContext* ScriptVM::currentVMExecContext() {
198 if (!m_parserContext) return NULL;
199 return m_parserContext->execContext;
200 }
201
202 VMExecStatus_t ScriptVM::exec(VMParserContext* parserContext, VMExecContext* execContex, VMEventHandler* handler) {
203 m_parserContext = dynamic_cast<ParserContext*>(parserContext);
204 if (!m_parserContext) {
205 std::cerr << "No VM parser context provided. Did you load a script?.\n";
206 return VMExecStatus_t(VM_EXEC_NOT_RUNNING | VM_EXEC_ERROR);
207 }
208
209 // a ParserContext object is always tied to exactly one ScriptVM object
210 assert(m_parserContext->functionProvider == this);
211
212 ExecContext* ctx = dynamic_cast<ExecContext*>(execContex);
213 if (!ctx) {
214 std::cerr << "Invalid VM exec context.\n";
215 return VMExecStatus_t(VM_EXEC_NOT_RUNNING | VM_EXEC_ERROR);
216 }
217 EventHandler* h = dynamic_cast<EventHandler*>(handler);
218 if (!h) return VM_EXEC_NOT_RUNNING;
219 m_eventHandler = handler;
220
221 m_parserContext->execContext = ctx;
222
223 ctx->status = VM_EXEC_RUNNING;
224 StmtFlags_t flags = STMT_SUCCESS;
225
226 int& frameIdx = ctx->stackFrame;
227 if (frameIdx < 0) { // start condition ...
228 frameIdx = -1;
229 ctx->pushStack(h);
230 }
231
232 while (flags == STMT_SUCCESS && frameIdx >= 0) {
233 if (frameIdx >= ctx->stack.size()) { // should never happen, otherwise it's a bug ...
234 std::cerr << "CRITICAL: VM stack overflow! (" << frameIdx << ")\n";
235 flags = StmtFlags_t(STMT_ABORT_SIGNALLED | STMT_ERROR_OCCURRED);
236 break;
237 }
238
239 ExecContext::StackFrame& frame = ctx->stack[frameIdx];
240 switch (frame.statement->statementType()) {
241 case STMT_LEAF: {
242 #if DEBUG_SCRIPTVM_CORE
243 _printIndents(frameIdx);
244 printf("-> STMT_LEAF\n");
245 #endif
246 LeafStatement* leaf = (LeafStatement*) frame.statement;
247 flags = leaf->exec();
248 ctx->popStack();
249 break;
250 }
251
252 case STMT_LIST: {
253 #if DEBUG_SCRIPTVM_CORE
254 _printIndents(frameIdx);
255 printf("-> STMT_LIST subidx=%d\n", frame.subindex);
256 #endif
257 Statements* stmts = (Statements*) frame.statement;
258 if (stmts->statement(frame.subindex)) {
259 ctx->pushStack(
260 stmts->statement(frame.subindex++)
261 );
262 } else {
263 #if DEBUG_SCRIPTVM_CORE
264 _printIndents(frameIdx);
265 printf("[END OF LIST] subidx=%d\n", frame.subindex);
266 #endif
267 ctx->popStack();
268 }
269 break;
270 }
271
272 case STMT_BRANCH: {
273 #if DEBUG_SCRIPTVM_CORE
274 _printIndents(frameIdx);
275 printf("-> STMT_BRANCH\n");
276 #endif
277 if (frame.subindex < 0) ctx->popStack();
278 else {
279 BranchStatement* branchStmt = (BranchStatement*) frame.statement;
280 frame.subindex = branchStmt->evalBranch();
281 if (frame.subindex >= 0) {
282 ctx->pushStack(
283 branchStmt->branch(frame.subindex)
284 );
285 frame.subindex = -1;
286 } else ctx->popStack();
287 }
288 break;
289 }
290
291 case STMT_LOOP: {
292 #if DEBUG_SCRIPTVM_CORE
293 _printIndents(frameIdx);
294 printf("-> STMT_LOOP\n");
295 #endif
296 While* whileStmt = (While*) frame.statement;
297 if (whileStmt->evalLoopStartCondition() && whileStmt->statements()) {
298 ctx->pushStack(
299 whileStmt->statements()
300 );
301 } else ctx->popStack();
302 }
303 }
304 }
305
306 if (flags & STMT_SUSPEND_SIGNALLED) {
307 ctx->status = VM_EXEC_SUSPENDED;
308 } else {
309 ctx->status = VM_EXEC_NOT_RUNNING;
310 if (flags & STMT_ERROR_OCCURRED)
311 ctx->status = VM_EXEC_ERROR;
312 ctx->reset();
313 }
314
315 m_eventHandler = NULL;
316 m_parserContext->execContext = NULL;
317 m_parserContext = NULL;
318 return ctx->status;
319 }
320
321 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC