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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2879 - (hide annotations) (download)
Tue Apr 19 14:07:53 2016 UTC (8 years 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 schoenebeck 2581 /*
2 schoenebeck 2879 * Copyright (c) 2014 - 2016 Christian Schoenebeck
3 schoenebeck 2581 *
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 schoenebeck 2588 #include <string.h>
13 schoenebeck 2619 #include <assert.h>
14 schoenebeck 2581 #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 schoenebeck 2879 ScriptVM::ScriptVM() : m_eventHandler(NULL), m_parserContext(NULL), fnWait(this) {
93 schoenebeck 2581 }
94    
95     ScriptVM::~ScriptVM() {
96     }
97    
98 schoenebeck 2588 VMParserContext* ScriptVM::loadScript(const String& s) {
99 schoenebeck 2581 std::istringstream iss(s);
100 schoenebeck 2588 return loadScript(&iss);
101 schoenebeck 2581 }
102    
103 schoenebeck 2588 VMParserContext* ScriptVM::loadScript(std::istream* is) {
104     ParserContext* context = new ParserContext(this);
105     //printf("parserCtx=0x%lx\n", (uint64_t)context);
106 schoenebeck 2594
107     context->registerBuiltInConstIntVariables( builtInConstIntVariables() );
108     context->registerBuiltInIntVariables( builtInIntVariables() );
109     context->registerBuiltInIntArrayVariables( builtInIntArrayVariables() );
110    
111 schoenebeck 2588 context->createScanner(is);
112 schoenebeck 2581
113 schoenebeck 2588 InstrScript_parse(context);
114 persson 2837 dmsg(2,("Allocating %ld bytes of global int VM memory.\n", long(context->globalIntVarCount * sizeof(int))));
115 schoenebeck 2611 dmsg(2,("Allocating %d of global VM string variables.\n", context->globalStrVarCount));
116 schoenebeck 2588 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 schoenebeck 2581
125 schoenebeck 2588 context->destroyScanner();
126 schoenebeck 2581
127 schoenebeck 2588 return context;
128 schoenebeck 2581 }
129    
130 schoenebeck 2588 void ScriptVM::dumpParsedScript(VMParserContext* context) {
131     ParserContext* ctx = dynamic_cast<ParserContext*>(context);
132     if (!ctx) {
133 schoenebeck 2581 std::cerr << "No VM context. So nothing to dump.\n";
134     return;
135     }
136 schoenebeck 2588 if (!ctx->handlers) {
137 schoenebeck 2581 std::cerr << "No event handlers defined in script. So nothing to dump.\n";
138     return;
139     }
140 schoenebeck 2588 if (!ctx->globalIntMemory) {
141 schoenebeck 2581 std::cerr << "Internal error: no global memory assigend to script VM.\n";
142     return;
143     }
144 schoenebeck 2588 ctx->handlers->dump();
145 schoenebeck 2581 }
146    
147 schoenebeck 2588 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 persson 2837 dmsg(2,("Created VM exec context with %ld bytes VM stack size.\n",
157     long(parserCtx->requiredMaxStackSize * sizeof(ExecContext::StackFrame))));
158 schoenebeck 2588 //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 persson 2837 dmsg(2,("Allocated %ld bytes polyphonic memory.\n", long(polySize * sizeof(int))));
164 schoenebeck 2588 return execCtx;
165 schoenebeck 2581 }
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 schoenebeck 2619 else if (name == "abs") return &fnAbs;
172     else if (name == "random") return &fnRandom;
173     else if (name == "num_elements") return &fnNumElements;
174 schoenebeck 2581 return NULL;
175     }
176 schoenebeck 2588
177 schoenebeck 2594 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 schoenebeck 2879 VMEventHandler* ScriptVM::currentVMEventHandler() {
190     return m_eventHandler;
191     }
192    
193 schoenebeck 2588 VMParserContext* ScriptVM::currentVMParserContext() {
194     return m_parserContext;
195     }
196    
197 schoenebeck 2581 VMExecContext* ScriptVM::currentVMExecContext() {
198 schoenebeck 2588 if (!m_parserContext) return NULL;
199     return m_parserContext->execContext;
200 schoenebeck 2581 }
201    
202 schoenebeck 2588 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 schoenebeck 2581 return VMExecStatus_t(VM_EXEC_NOT_RUNNING | VM_EXEC_ERROR);
207     }
208    
209 schoenebeck 2611 // a ParserContext object is always tied to exactly one ScriptVM object
210     assert(m_parserContext->functionProvider == this);
211    
212 schoenebeck 2581 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 schoenebeck 2879 m_eventHandler = handler;
220 schoenebeck 2581
221 schoenebeck 2588 m_parserContext->execContext = ctx;
222 schoenebeck 2581
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 schoenebeck 2879 m_eventHandler = NULL;
316 schoenebeck 2588 m_parserContext->execContext = NULL;
317     m_parserContext = NULL;
318 schoenebeck 2581 return ctx->status;
319     }
320    
321     } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC