--- linuxsampler/trunk/src/scriptvm/common.h 2019/08/29 13:44:35 3580 +++ linuxsampler/trunk/src/scriptvm/common.h 2019/08/30 11:40:25 3581 @@ -19,6 +19,7 @@ #include #include #include // offsetof() +#include // std::function<> namespace LinuxSampler { @@ -137,6 +138,14 @@ }; /** + * This constant is used for comparison with Unit::unitFactor() to check + * whether a number does have any metric unit prefix at all. + * + * @see Unit::unitFactor() + */ + static const vmfloat VM_NO_FACTOR = vmfloat(1); + + /** * All measurement unit types supported by this script engine. * * @e Note: there is no standard unit "cents" here (for pitch/tuning), use @@ -153,66 +162,99 @@ VM_BEL, ///< Measuring relation between two energy levels (in logarithmic scale). Since we are using it for accoustics, we are always referring to A-weighted Bels (i.e. dBA). }; + //TODO: see Unit::hasUnitFactorEver() + enum EverTriState_t { + VM_NEVER = 0, + VM_MAYBE, + VM_ALWAYS, + }; + // just symbol prototyping class VMIntExpr; class VMRealExpr; class VMStringExpr; class VMScalarNumberExpr; + class VMArrayExpr; class VMIntArrayExpr; class VMRealArrayExpr; class VMStringArrayExpr; class VMParserContext; - /** @brief Virtual machine measuring unit. + /** @brief Virtual machine standard measuring unit. * * Abstract base class representing standard measurement units throughout - * the script engine. These might be i.e. "dB" (deci Bel) for loudness, - * "Hz" (Hertz) for frequencies or "s" for "seconds". + * the script engine. These might be e.g. "dB" (deci Bel) for loudness, + * "Hz" (Hertz) for frequencies or "s" for "seconds". These unit types can + * combined with metric prefixes, for instance "kHz" (kilo Hertz), + * "us" (micro second), etc. * * Originally the script engine only supported abstract integer values for * controlling any synthesis parameter or built-in function argument or * variable. Under certain situations it makes sense though for an * instrument script author to provide values in real, standard measurement - * units, for example setting the frequency of some LFO directly to "20Hz". - * Hence support for standard units in scripts was added as an extension to - * the NKSP script engine. + * units to provide a more natural and intuitive approach for writing + * instrument scripts, for example by setting the frequency of some LFO + * directly to "20Hz" or reducing loudness by "-4.2dB". Hence support for + * standard units in scripts was added as an extension to the NKSP script + * engine. + * + * So a unit consists of 1) a sequence of metric prefixes as scale factor + * (e.g. "k" for kilo) and 2) the actual unit type (e.g. "Hz" for Hertz). + * The unit type is a constant feature of number literals and variables, so + * once a variable was declared with a unit type (or no unit type at all) + * then that unit type of that variable cannot be changed for the entire + * life time of the script. This is different from the unit's metric + * prefix(es) of variables which may freely be changed at runtime. */ class VMUnit { public: /** - * Returns the metric prefix of this unit. A metric prefix essentially - * is just a mathematical scale factor that should be applied to the - * number associated with the measurement unit. Usually a unit either - * has exactly none or one prefix, but note that there might also be - * units with more than one prefix, for example mdB (mili deci bel) - * is used sometimes which has two prefixes. This is an exception though - * and more than two prefixes is currently not supported by the script - * engine. + * Returns the metric prefix(es) of this unit as unit factor. A metric + * prefix essentially is just a mathematical scale factor that should be + * applied to the number associated with the measurement unit. Consider + * a string literal in an NKSP script like '3kHz' where 'k' (kilo) is + * the metric prefix, which essentically is a scale factor of 1000. * - * Start iterating over the prefixes of this unit by passing @c 0 as - * argument to this method. The prefixes are terminated with return - * value VM_NO_PREFIX being always the last element. + * Usually a unit either has exactly none or one metric prefix, but note + * that there might also be units with more than one prefix, for example + * @c mdB (milli deci Bel) is used sometimes which has two prefixes. The + * latter is an exception though and more than two prefixes is currently + * not supported by the script engine. * - * @param i - index of prefix - * @returns prefix of requested index or VM_NO_PREFIX otherwise - * @see unitFactor() + * The factor returned by this method is the final mathematical factor + * that should be multiplied against the number associated with this + * unit. This factor results from the sequence of metric prefixes of + * this unit. + * + * @see MetricPrefix_t, hasUnitFactorNow(), hasUnitFactorEver(), + * VM_NO_FACTOR + * @returns current metric unit factor */ - virtual MetricPrefix_t unitPrefix(vmuint i) const = 0; + virtual vmfloat unitFactor() const = 0; + + //TODO: this still needs to be implemented in tree.h/.pp, built-in functions and as 2nd pass of parser appropriately + /*virtual*/ EverTriState_t hasUnitFactorEver() const { return VM_NEVER; } /** - * Conveniently returns the final mathematical factor that should be - * multiplied against the number associated with this unit. This factor - * results from the sequence of metric prefixes of this unit. + * Whether this unit currently does have any metric unit prefix. * - * @see unitPrefix() + * This is actually just a convenience method which returns @c true if + * unitFactor() is not @c 1.0. + * + * @see MetricPrefix_t, unitFactor(), hasUnitFactorEver(), VM_NO_FACTOR + * @returns @c true if this unit currently has any metric prefix */ - vmfloat unitFactor() const; + bool hasUnitFactorNow() const; /** * This is the actual fundamental measuring unit base type of this unit, * which might be either Hertz, second or Bel. * - * @returns standard unit type identifier or VM_NO_UNIT if no unit used + * Note that a number without a unit type may still have metric + * prefixes. + * + * @returns standard unit type identifier or VM_NO_UNIT if no unit type + * is used for this object */ virtual StdUnit_t unitType() const = 0; @@ -225,8 +267,21 @@ /** * Returns the actual mathematical factor represented by the passed * two @a prefix1 and @a prefix2 arguments. + * + * @returns scale factor of given metric unit prefixes */ static vmfloat unitFactor(MetricPrefix_t prefix1, MetricPrefix_t prefix2); + + /** + * Returns the actual mathematical factor represented by the passed + * @a prefixes array. The passed array should always be terminated by a + * VM_NO_PREFIX value as last element. + * + * @param prefixes - sequence of metric prefixes + * @param size - max. amount of elements of array @a prefixes + * @returns scale factor of given metric unit prefixes + */ + static vmfloat unitFactor(const MetricPrefix_t* prefixes, vmuint size = 2); }; /** @brief Virtual machine expression @@ -366,6 +421,23 @@ virtual VMRealArrayExpr* asRealArray() const; /** + * This is an alternative to calling either asIntArray() or + * asRealArray(). This method here might be used if the fundamental + * scalar data type (real or integer) of the array is not relevant, + * i.e. for just getting the size of the array. Since all as*() methods + * here are very strict regarding type casting, this asArray() method + * sometimes can reduce code complexity. + * + * Likewise calling this method only returns a valid pointer if the + * expression is some array type (currently either integer array or real + * number array). For any other expression type this method will return + * NULL instead. + * + * @see exprType() + */ + VMArrayExpr* asArray() const; + + /** * Returns true in case this expression can be considered to be a * constant expression. A constant expression will retain the same * value throughout the entire life time of a script and the @@ -432,6 +504,22 @@ * requested synthesis parameter. */ virtual bool isFinal() const = 0; + + /** + * Calling this method evaluates the expression and returns the value + * of the expression as integer. If this scalar number expression is a + * real number expression then this method automatically casts the value + * from real number to integer. + */ + vmint evalCastInt(); + + /** + * Calling this method evaluates the expression and returns the value + * of the expression as real number. If this scalar number expression is + * an integer expression then this method automatically casts the value + * from integer to real number. + */ + vmfloat evalCastReal(); }; /** @brief Virtual machine integer expression @@ -549,6 +637,32 @@ virtual vmint arraySize() const = 0; }; + /** @brief Virtual Machine Number Array Expression + * + * This is the abstract base class for all expressions which either evaluate + * to an integer array or real number array. + */ + class VMNumberArrayExpr : virtual public VMArrayExpr { + public: + /** + * Returns the metric unit factor of the requested array element. + * + * @param i - array element index (must be between 0 .. arraySize() - 1) + * @see VMUnit::unitFactor() for details about metric unit factors + */ + virtual vmfloat unitFactorOfElement(vmuint i) const = 0; + + /** + * Changes the current unit factor of the array element given by element + * index @a i. + * + * @param i - array element index (must be between 0 .. arraySize() - 1) + * @param factor - new unit factor to be assigned + * @see VMUnit::unitFactor() for details about metric unit factors + */ + virtual void assignElementUnitFactor(vmuint i, vmfloat factor) = 0; + }; + /** @brief Virtual Machine Integer Array Expression * * This is the abstract base class for all expressions inside scripts which @@ -556,7 +670,7 @@ * abstract methods arraySize(), evalIntElement() and assignIntElement() to * access the individual integer array values. */ - class VMIntArrayExpr : virtual public VMArrayExpr { + class VMIntArrayExpr : virtual public VMNumberArrayExpr { public: /** * Returns the (scalar) integer value of the array element given by @@ -588,7 +702,7 @@ * classes implement the abstract methods arraySize(), evalRealElement() and * assignRealElement() to access the array's individual real numbers. */ - class VMRealArrayExpr : virtual public VMArrayExpr { + class VMRealArrayExpr : virtual public VMNumberArrayExpr { public: /** * Returns the (scalar) real mumber (floating point value) of the array @@ -707,6 +821,42 @@ virtual ExprType_t returnType(VMFnArgs* args) = 0; /** + * Standard measuring unit type of the function's result value + * (e.g. second, Hertz). + * + * Some functions may have a different standard measuring unit type for + * their return value depending on the arguments to be passed to this + * function. That's what the @a args parameter is for, so that the + * method implementation can look ahead of what kind of parameters are + * going to be passed to the built-in function later on in order to + * decide which return value type would be used and returned by the + * function accordingly in that case. + * + * @param args - function arguments going to be passed for executing + * this built-in function later on + * @see Unit for details about standard measuring units + */ + virtual StdUnit_t returnUnitType(VMFnArgs* args) = 0; + + /** + * Whether the result value returned by this built-in function is + * considered to be a 'final' value. + * + * Some functions may have a different 'final' feature for their return + * value depending on the arguments to be passed to this function. + * That's what the @a args parameter is for, so that the method + * implementation can look ahead of what kind of parameters are going to + * be passed to the built-in function later on in order to decide which + * return value type would be used and returned by the function + * accordingly in that case. + * + * @param args - function arguments going to be passed for executing + * this built-in function later on + * @see VMScalarNumberExpr::isFinal() for details about 'final' values + */ + virtual bool returnsFinal(VMFnArgs* args) = 0; + + /** * Minimum amount of function arguments this function accepts. If a * script is calling this function with less arguments, the script * parser will throw a parser error. @@ -814,7 +964,7 @@ * @return true if a "final" value would be accepted for the respective * function argument by the function * - * @see VMIntExpr::isFinal() + * @see VMScalarNumberExpr::isFinal(), returnsFinal() */ virtual bool acceptsArgFinal(vmint iArg) const; @@ -834,6 +984,40 @@ virtual bool modifiesArg(vmint iArg) const = 0; /** + * This method is called by the parser to let the built-in function + * perform its own, individual parse time checks on the arguments to be + * passed to the built-in function. So this method is the place for + * implementing custom checks which are very specific to the individual + * built-in function's purpose and its individual requirements. + * + * For instance the built-in 'in_range()' function uses this method to + * check whether the last 2 of their 3 arguments are of same data type + * and if not it triggers a parser error. 'in_range()' also checks + * whether all of its 3 arguments do have the same standard measuring + * unit type and likewise raises a parser error if not. + * + * For less critical issues built-in functions may also raise parser + * warnings instead. + * + * It is recommended that classes implementing (that is overriding) this + * method should always call their super class's implementation of this + * method to ensure their potential parse time checks are always + * performed as well. + * + * @param args - function arguments going to be passed for executing + * this built-in function later on + * @param err - the parser's error handler to be called by this method + * implementation to trigger a parser error with the + * respective error message text + * @param wrn - the parser's warning handler to be called by this method + * implementation to trigger a parser warning with the + * respective warning message text + */ + virtual void checkArgs(VMFnArgs* args, + std::function err, + std::function wrn); + + /** * Implements the actual function execution. This exec() method is * called by the VM whenever this function implementation shall be * executed at script runtime. This method blocks until the function @@ -1229,7 +1413,7 @@ */ class VMDynIntVar : virtual public VMDynVar, virtual public VMIntExpr { public: - MetricPrefix_t unitPrefix(vmuint i) const OVERRIDE { return VM_NO_PREFIX; } + vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; } StdUnit_t unitType() const OVERRIDE { return VM_NO_UNIT; } bool isFinal() const OVERRIDE { return false; } };