OSC extension for Autohotkey, a DLL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

210 lines
10 KiB

3 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. ## OSC2AHK
  2. This is a DLL extension for [AutoHotkey](https://www.autohotkey.com/) that enables support for OSC (Open Sound Control).
  3. ## Table of contents
  4. - [OSC2AHK](#osc2ahk)
  5. - [Work in progress](#work-in-progress)
  6. - [Table of contents](#table-of-contents)
  7. - [Installation](#installation)
  8. - [Usage](#usage)
  9. - [General concept](#general-concept)
  10. - [Receive functions](#receive-functions)
  11. - [Load DLL](#load-dll)
  12. - [Open network port](#open-network-port)
  13. - [Close network port](#close-network-port)
  14. - [Add listener](#add-listener)
  15. - [Remove listener](#remove-listener)
  16. - [Get string data](#get-string-data)
  17. - [Send functions](#send-functions)
  18. - [Send integer message](#send-integer-message)
  19. - [Send float message](#send-float-message)
  20. - [Send string message](#send-string-message)
  21. - [Credits](#credits)
  22. ## Installation
  23. Just download the latest version of OSC2AHK.dll from the [releases page](https://files.eleton-audio.de/gitea/Ludwig/OSC2AHK/releases) and place it in the same directory as your AutoHotkey scripts. The DLL provided in the Releases section at the moment only works on 64 bit systems, Windows 10 or Windows 11. Other systems are not supported right now. Maybe you also want to check out the provided [usage examples](https://files.eleton-audio.de/gitea/Ludwig/OSC2AHK/src/branch/master/examples).
  24. ## Usage
  25. ### General concept
  26. After the DLL is loaded and a network port is opened with the [`open()`](https://files.eleton-audio.de/gitea/Ludwig/OSC2AHK#open-network-port) function, it receives all incoming OSC messages. To specify the messages that should be passed to the AutoHotkey (AHK) script, "listeners" are specified and created by the ['addListener()'](https://files.eleton-audio.de/gitea/Ludwig/OSC2AHK#add-listener) function. When a OSC message is received that matches a existing listener, a "system message" is sent to the AHK script. This happens via the Windows message queue and the system message ID is used to tell the script which OSC address was received. Therefore with [`addListener()`](https://files.eleton-audio.de/gitea/Ludwig/OSC2AHK#add-listener) a ID has to be passed to the DLL which then uses this ID to post the system message.
  27. ### Receive functions
  28. These functions are the main functions used to receive OSC messages in AHK.
  29. #### Load DLL
  30. First the DLL should be loaded from within the AHK script by calling
  31. ```
  32. DllCall("LoadLibrary", "Str", "OSC2AHK.dll", "Ptr")
  33. ```
  34. (Assuming the OSC2AHK.dll file is in the same directory as the calling script.)
  35. #### Open network port
  36. ```cpp
  37. int open(HWND targetWindowHandle, unsigned int port);
  38. //targetWindowHandle: Handle to the calling window to which messages on received OSC data is sent
  39. //port: Network port to open
  40. //returns: Zero on success, something else on failure
  41. ```
  42. This opens the network port and enables the DLL to receive OSC messages.
  43. Example AHK snippet:
  44. ```
  45. Gui +LastFound
  46. hWnd := WinExist()
  47. success := DllCall("OSC2AHK.dll\open", UInt, hWnd, UInt, 7002)
  48. if (success != 0)
  49. msgbox, Failed to open port!
  50. ```
  51. #### Close network port
  52. If you want to close the network port before termination of the script you may use:
  53. ```cpp
  54. int close(unsigned int clearListeners = 1);
  55. //clearListeners: If this is not zero, all the listeners will also be removed (for future open() call). May be omitted, then defaults to 1 (remove listeners).
  56. ```
  57. Example AHK call without clearing the listners:
  58. ```
  59. DllCall("OSC2AHK.dll\close", UInt, 0)
  60. ```
  61. #### Add listener
  62. ```cpp
  63. int addListener(LPCSTR address, unsigned int messageID, unsigned int dataType = OSC_TYPE_ALL);
  64. //address: OSC address to listen for
  65. //messageID: ID for the system message that is sent to the AHK script when this listener receives something.
  66. //dataType: Specifies the OSC datatype to listen for. See below. May be omitted, then it defaults to all datatypes.
  67. ```
  68. This adds a new listener for a OSC address pattern, only OSC messages with a existing listener will be passed to the calling script. The (system) message ID may be in the range 0x0400 (the value of WM_USER) through 0x7FFF for general purpose use but others can be used for special purposes as well (maybe see [Microsoft's page about messages](https://docs.microsoft.com/en-us/windows/win32/winmsg/about-messages-and-message-queues#application-defined-messages)). Only if the address and the datatype of a incoming OSC message match the ones of the listeners, a message is posted to the AHK script. Note that the datatype can be one of the following values or the sum of multiple ones.
  69. ```cpp
  70. #define OSC_TYPE_NONE 1
  71. #define OSC_TYPE_INT 2
  72. #define OSC_TYPE_FLOAT 4
  73. #define OSC_TYPE_STRING 8
  74. #define OSC_TYPE_BLOB 16
  75. #define OSC_TYPE_INT64 32
  76. #define OSC_TYPE_FLOAT64 64
  77. #define OSC_TYPE_BOOL 128
  78. #define OSC_TYPE_CHAR 256
  79. #define OSC_TYPE_TIMETAG 512
  80. #define OSC_TYPE_COLOR 1024
  81. #define OSC_TYPE_ALL UINT_MAX //=0xffffffff
  82. ```
  83. Add a listener from the AHK script:
  84. ```
  85. ; just for convenience, you also could use the "magic numbers" directly
  86. global oscTypeInt := 2
  87. global oscTypeFloat := 4
  88. ; Tell the DLL to post a system message with ID 0x1001 when a OSC message with address "/test1" and type Integer or Float is received.
  89. DllCall("OSC2AHK.dll\addListener", AStr, "/test1", UInt, 0x1001, UInt, oscTypeInt+oscTypeFloat)
  90. ; tell AHK to invoke the function msghandlerTest1 when a windows message with ID 0x1001 is received
  91. OnMessage(0x1001, "msghandlerTest1")
  92. ```
  93. Then, there has to be the handler function in the AHK script:
  94. ```
  95. msghandlerTest1(oscType, data, msgID, hwnd)
  96. {
  97. ; Check which datatype we received
  98. if (oscType = oscTypeInt)
  99. {
  100. ; Integers can be used "as is"
  101. msgbox,Got Integer: %data%
  102. }
  103. if (oscType = oscTypeFloat)
  104. {
  105. ; Floats have to be "reinterpreted"
  106. VarSetCapacity(buf, 4, 0)
  107. NumPut(data, buf)
  108. theFloat := NumGet(buf, "Float")
  109. msgbox,Got Float: %theFloat%
  110. }
  111. }
  112. ```
  113. Note that you may add many listeners, for any combination of the same/different OSC addresses, datatypes and system message IDs. Also, in AutoHotkey you may add multiple callback functions for the same system message ID or multiple system message IDs to the same callback function. This creates a big flexibility but to keep it less clutterd, such multiple routing paths should be avoided where possible.
  114. For advanced usage, the address pattern passed to `addListener()` supports standard OSC wildcards, so one listener can match multiple messages that share some simliarities. These wildcards are supported by the OSC 1.0 standard:
  115. > * '?' in the OSC Address Pattern matches any single character
  116. > * '*' in the OSC Address Pattern matches any sequence of zero or more characters
  117. > * A string of characters in square brackets (e.g., "[string]") in the OSC Address Pattern matches any character in the string. Inside square brackets, the minus sign (-) and exclamation point (!) have special meanings:
  118. > * two characters separated by a minus sign indicate the range of characters between the given two in ASCII collating sequence. (A minus sign at the end of the string has no special meaning.)
  119. > * An exclamation point at the beginning of a bracketed string negates the sense of the list, meaning that the list matches any character not in the list. (An exclamation point anywhere besides the first character after the open bracket has no special meaning.)
  120. > * A comma-separated list of strings enclosed in curly braces (e.g., "{foo,bar}") in the OSC Address Pattern matches any of the strings in the list.
  121. > * Any other character in an OSC Address Pattern can match only the same character.
  122. >
  123. > -- <cite>[http://opensoundcontrol.org/]</cite>
  124. #### Remove listener
  125. Of course, listeners also can be removed in a similar way:
  126. ```cpp
  127. int removeListener(LPCSTR address);
  128. ```
  129. Example AHK call:
  130. ```
  131. DllCall("OSC2AHK.dll\removeListener", AStr, "/test1")
  132. ```
  133. #### Get string data
  134. As string data carried by OSC messages may be of varying length and cannot directly be passed by system messages. Therefore, the string is buffered in the DLL and the `data` variable of the callback function receives a unique identifier to the stored string. This string then can be retrieved later on from the AHK script by calling this function.
  135. ```cpp
  136. char* getStringData(char* targetString, unsigned int targetSize, unsigned int StringID);
  137. ```
  138. This function writes the stored string to the `targetString` and also removes it from the buffer inside the DLL.
  139. A callback functin in AHK could look like this (also look at the [string example](https://files.eleton-audio.de/gitea/Ludwig/OSC2AHK/src/branch/master/examples/string_example.ahk)):
  140. ```
  141. msghandlerString(oscType, data, msgID, hwnd)
  142. {
  143. VarSetCapacity(theString, 20)
  144. theString := DllCall("OSC2AHK.dll\getStringData", str, theString, UInt, 20, UInt, data, AStr)
  145. msgbox,Got string: %theString%
  146. }
  147. ```
  148. ### Send functions
  149. The DLL also can be used to transmit OSC messages from AutoHotkey by using the following functions. Usually there is only one argument (payload) per message, but the functions ending in "...2" allow the sending of two arguments in one message. Only two arguments of the same type are supported at the moment.
  150. #### Send integer message
  151. ```cpp
  152. void sendOscMessageInt(char* ip, unsigned int port, char* address, int payload);
  153. void sendOscMessageInt2(char* ip, unsigned int port, char* address, int payload1, int payload2);
  154. ```
  155. Example call from AHK:
  156. ```
  157. ip := "192.168.1.2"
  158. port := 8002
  159. addr := "/testmsg"
  160. data := 42
  161. DllCall("OSC2AHK.dll\sendOscMessageInt", AStr, ip, UInt, port, AStr, addr, Int, data)
  162. ```
  163. #### Send float message
  164. ```cpp
  165. void sendOscMessageFloat(char* ip, unsigned int port, char* address, float payload);
  166. void sendOscMessageFloat2(char* ip, unsigned int port, char* address, float payload1, float payload2);
  167. ```
  168. Example call from AHK:
  169. ```
  170. ip := "192.168.1.2"
  171. port := 8002
  172. addr := "/my/msg"
  173. data := 42.3
  174. DllCall("OSC2AHK.dll\sendOscMessageFloat", AStr, ip, UInt, port, AStr, addr, Float, data)
  175. ```
  176. #### Send string message
  177. ```cpp
  178. void sendOscMessageString(char* ip, unsigned int port, char* address, char* payload);
  179. void sendOscMessageString2(char* ip, unsigned int port, char* address, char* payload1, char* payload2);
  180. ```
  181. Example call from AHK:
  182. ```
  183. ip := "192.168.1.2"
  184. port := 8002
  185. addr := "/some/message"
  186. data := "This is the string"
  187. DllCall("OSC2AHK.dll\sendOscMessageString", AStr, ip, UInt, port, AStr, addr, AStr, data)
  188. ```
  189. ## Credits
  190. This DLL uses the [oscpack](http://www.rossbencina.com/code/oscpack) OSC implementation by Ross Bencina.