mediaMachineAPI.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2019 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #ifndef DFPSR_API_MEDIA_MACHINE
  24. #define DFPSR_API_MEDIA_MACHINE
  25. #include "../math/FixedPoint.h"
  26. #include "../api/types.h"
  27. namespace dsr {
  28. // TODO: Complete VirtualMachine with conditional jumps and document the language dialect used by MediaMachine.
  29. // Side-effect: Creates a media machine from Media Machine Code (*.mmc file).
  30. // Post-condition: Returns a reference counted MediaMachine handle to the virtual machine.
  31. MediaMachine machine_create(const ReadableString& code);
  32. // Post-condition: Returns true iff machine exists.
  33. bool machine_exists(const MediaMachine& machine);
  34. // Pre-condition:
  35. // * The machine must exist.
  36. // machine_exists(machine)
  37. // Returns the index to the method who's name matches methodName with case insensitivity in machine, or -1 if it does not exist in machine.
  38. int machine_findMethod(const MediaMachine& machine, const ReadableString& methodName);
  39. // Assign an input argument of a method before your call to machine_executeMethod.
  40. // Pre-condition:
  41. // * The machine must exist.
  42. // machine_exists(machine)
  43. // * methodIndex must refer to the method to call in machine.
  44. // 0 <= methodIndex < machine_getMethodCount(machine)
  45. // * inputIndex must refer to an input of the method in machine.
  46. // 0 <= inputIndex < machine_getInputCount(machine, methodIndex)
  47. // Side-effect: Sets the input at inputIndex in machine's method at methodIndex to input.
  48. void machine_setInputByIndex(MediaMachine& machine, int methodIndex, int inputIndex, int32_t input);
  49. void machine_setInputByIndex(MediaMachine& machine, int methodIndex, int inputIndex, const FixedPoint& input);
  50. void machine_setInputByIndex(MediaMachine& machine, int methodIndex, int inputIndex, const AlignedImageU8& input);
  51. void machine_setInputByIndex(MediaMachine& machine, int methodIndex, int inputIndex, const OrderedImageRgbaU8& input);
  52. // Call a method in the media machine, reading from input registers and writing to output registers.
  53. // Pre-condition:
  54. // * The machine must exist.
  55. // machine_exists(machine)
  56. // * All inputs of the method must be assigned before the call using the same methodIndex.
  57. // * methodIndex must refer to the method to call in machine.
  58. // 0 <= methodIndex < machine_getMethodCount(machine)
  59. // Side-effect: Writes the results to output arguments.
  60. void machine_executeMethod(MediaMachine& machine, int methodIndex);
  61. // Read output register at outputIndex
  62. // Pre-condition:
  63. // * The machine must exist.
  64. // machine_exists(machine)
  65. // * methodIndex must refer to the method in machine's last call to machine_executeMethod.
  66. // 0 <= methodIndex < machine_getMethodCount(machine)
  67. // * The output index must be within range.
  68. // 0 <= outputIndex < machine_getOutputCount(machine, methodIndex)
  69. // Post-condition: Returns the output at outputIndex.
  70. FixedPoint machine_getFixedPointOutputByIndex(const MediaMachine& machine, int methodIndex, int outputIndex);
  71. AlignedImageU8 machine_getImageU8OutputByIndex(const MediaMachine& machine, int methodIndex, int outputIndex);
  72. OrderedImageRgbaU8 machine_getImageRgbaU8OutputByIndex(const MediaMachine& machine, int methodIndex, int outputIndex);
  73. // Pre-condition:
  74. // * The machine must exist.
  75. // machine_exists(machine)
  76. // Post-condition: Returns the number of methods in machine.
  77. int machine_getMethodCount(const MediaMachine& machine);
  78. // Pre-condition:
  79. // * The machine must exist.
  80. // machine_exists(machine)
  81. // * methodIndex must refer to the method that you want to get the name from.
  82. // 0 <= methodIndex < machine_getMethodCount(machine)
  83. // Post-condition: Returns the name of the method at methodIndex in machine.
  84. String machine_getMethodName(const MediaMachine& machine, int methodIndex);
  85. // Pre-condition:
  86. // * The machine must exist.
  87. // machine_exists(machine)
  88. // * methodIndex must refer to the method that you want to get the input count from.
  89. // 0 <= methodIndex < machine_getMethodCount(machine)
  90. // Post-condition: Returns the input argument count for the method at methodIndex in machine.
  91. int machine_getInputCount(const MediaMachine& machine, int methodIndex);
  92. // Pre-condition:
  93. // * The machine must exist.
  94. // machine_exists(machine)
  95. // * methodIndex must refer to the method that you want to get the output count from.
  96. // 0 <= methodIndex < machine_getMethodCount(machine)
  97. // Post-condition: Returns the output argument count for the method at methodIndex in machine.
  98. int machine_getOutputCount(const MediaMachine& machine, int methodIndex);
  99. // Pre-condition:
  100. // * The machine must exist.
  101. // machine_exists(machine)
  102. // * methodIndex must refer to the method that you want to get an input argument's name from.
  103. // 0 <= methodIndex < machine_getMethodCount(machine)
  104. // * The inputIndex must refer to the input argument that you want to get the name of.
  105. // 0 <= inputIndex < machine_getInputCount(machine, methodIndex)
  106. // Post-condition: Returns the input argument name at inputIndex for the method at methodIndex in machine.
  107. String machine_getInputName(const MediaMachine& machine, int methodIndex, int inputIndex);
  108. // Pre-condition:
  109. // * The machine must exist.
  110. // machine_exists(machine)
  111. // * methodIndex must refer to the method that you want to get an output argument's name from.
  112. // 0 <= methodIndex < machine_getMethodCount(machine)
  113. // * The outputIndex must refer to the output argument that you want to get the name of.
  114. // 0 <= outputIndex < machine_getOutputCount(machine, methodIndex)
  115. // Post-condition: Returns the output argument name at outputIndex for the method at methodIndex in machine.
  116. String machine_getOutputName(const MediaMachine& machine, int methodIndex, int outputIndex);
  117. // Helper function counting the number of arguments given to it.
  118. inline constexpr int machine_argCount() {
  119. return 0;
  120. }
  121. template<typename HEAD, typename... TAIL>
  122. inline constexpr int machine_argCount(HEAD& first, TAIL&... args) {
  123. return machine_argCount(args...) + 1;
  124. }
  125. // A temporary type generated from () calls to MediaMethod, which is used for writing outputs to targets within the next ().
  126. class MediaResult {
  127. private:
  128. // Holding the machine by reference prevents storing MediaResult,
  129. // because it may not be used after other calls to the machine.
  130. // It is only used to assign outputs with the () operator.
  131. MediaMachine &machine;
  132. int methodIndex;
  133. void writeResult(int outputIndex, int8_t& target) {
  134. target = fixedPoint_round(machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex));
  135. }
  136. void writeResult(int outputIndex, int16_t& target) {
  137. target = fixedPoint_round(machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex));
  138. }
  139. void writeResult(int outputIndex, int32_t& target) {
  140. target = fixedPoint_round(machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex));
  141. }
  142. void writeResult(int outputIndex, int64_t& target) {
  143. target = fixedPoint_round(machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex));
  144. }
  145. void writeResult(int outputIndex, FixedPoint& target) {
  146. target = machine_getFixedPointOutputByIndex(this->machine, this->methodIndex, outputIndex);
  147. }
  148. void writeResult(int outputIndex, AlignedImageU8& target) {
  149. target = machine_getImageU8OutputByIndex(this->machine, this->methodIndex, outputIndex);
  150. }
  151. void writeResult(int outputIndex, OrderedImageRgbaU8& target) {
  152. target = machine_getImageRgbaU8OutputByIndex(this->machine, this->methodIndex, outputIndex);
  153. }
  154. inline void writeResults(int firstInputIndex) {}
  155. template<typename HEAD, typename... TAIL>
  156. inline void writeResults(int firstInputIndex, HEAD& first, TAIL&... args) {
  157. this->writeResult(firstInputIndex, first);
  158. this->writeResults(firstInputIndex + 1, args...);
  159. }
  160. public:
  161. MediaResult(MediaMachine& machine, int methodIndex)
  162. : machine(machine), methodIndex(methodIndex) {}
  163. // Write target references within () after a call to assign multiple outputs
  164. template <typename... ARGS>
  165. void operator () (ARGS&... args) {
  166. int givenCount = machine_argCount(args...);
  167. int expectedCount = machine_getOutputCount(this->machine, this->methodIndex);
  168. if (givenCount != expectedCount) {
  169. throwError("The call to ", machine_getMethodName(this->machine, this->methodIndex), " expected ", expectedCount, " outputs, but ", givenCount, " references were assigned.\n");
  170. }
  171. this->writeResults(0, args...);
  172. }
  173. };
  174. // How to call a MediaMethod:
  175. // * Using arguments in the same order as declared in the Media Machine Code.
  176. // myMediaMethod(inputA, inputB, inputC...)(outputX, outputY...)
  177. // This allow returning more than one return value without having to declare any structures in the virtual machine.
  178. // * Using keyword arguments.
  179. // You can also call myMediaMethod and just define a lambda that is called with the argument name and index for each input argument.
  180. // Then your function calls machine_setInputByIndex with the given arguments and the input data identified by name.
  181. class MediaMethod {
  182. public:
  183. MediaMachine machine;
  184. int methodIndex; // Index of the method being called.
  185. int contextIndex; // Index used to know from which context implicit variables are being fetched.
  186. private:
  187. inline void setInputs(int firstInputIndex) {}
  188. template<typename HEAD, typename... TAIL>
  189. inline void setInputs(int firstInputIndex, const HEAD &first, TAIL&&... args) {
  190. machine_setInputByIndex(this->machine, this->methodIndex, firstInputIndex, first);
  191. this->setInputs(firstInputIndex + 1, args...);
  192. }
  193. public:
  194. MediaMethod()
  195. : methodIndex(-1), contextIndex(0) {}
  196. MediaMethod(const MediaMachine& machine, int methodIndex, int contextIndex)
  197. : machine(machine), methodIndex(methodIndex), contextIndex(contextIndex) {}
  198. // MediaMethod can be called like a function using arguments, returning MediaResult for assigning outputs by reference.
  199. // Useful when you know the arguments in advance.
  200. template <typename... ARGS>
  201. MediaResult operator () (ARGS&&... args) {
  202. int givenCount = machine_argCount(args...);
  203. int expectedCount = machine_getInputCount(this->machine, this->methodIndex);
  204. if (givenCount != expectedCount) {
  205. throwError("The call to ", machine_getMethodName(this->machine, this->methodIndex), " expected ", expectedCount, " inputs, but ", givenCount, " values were given.\n");
  206. }
  207. this->setInputs(0, args...);
  208. machine_executeMethod(this->machine, this->methodIndex);
  209. return MediaResult(this->machine, this->methodIndex);
  210. }
  211. // MediaMethod can also take the inputs as keyword arguments by getting a callback with the index and name of each input to assign.
  212. // The function setInputAction should simply make a call to machine_setInputByIndex with the provided machine, methodIndex, inputIndex and the value corresponding to argumentName in setInputAction.
  213. // If you don't recognize argumentName, then throw an exception because default input arguments are currently not implemented.
  214. // Useful when the called function can be extended or reduced with only the arguments needed.
  215. MediaResult callUsingKeywords(std::function<void(MediaMachine &machine, int methodIndex, int inputIndex, const ReadableString &argumentName)> setInputAction);
  216. };
  217. // Post-condition: Returns a MediaMethod structure, which can be stored as a reference counting function pointer that keeps the virtual machine alive.
  218. MediaMethod machine_getMethod(MediaMachine& machine, const ReadableString& methodName, int contextIndex, bool mustExist = true);
  219. }
  220. #endif