From ae880fce341e32cd5c51c6387abb5123f0b00361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludwig=20Fr=C3=BChsch=C3=BCtz?= Date: Tue, 6 Apr 2021 22:22:12 +0000 Subject: [PATCH] Added usage information to readme --- README.md | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 99fa49c..06b41d1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,145 @@ # OSC2AHK -OSC extension for Autohotkey, a DLL \ No newline at end of file +This is a DLL extension for [AutoHotkey](https://www.autohotkey.com/) that enables support for OSC (Open Sound Control). + +## Work in progress +Currently this project is unfinished, there may be missing functionality and errors may occur! +* After multiple calls of [`getStringData()`](https://files.eleton-audio.de/gitea/Ludwig/OSC2AHK#get-string-data) a heap memory fault occurs. This could also be a error in the calling AHK script. +* OSC address wildcards are not supported yet +* Documentation could be improved + +## Usage +### General concept +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. + +### Functions +#### Load DLL +First the DLL should be loaded from within the AHK script by calling +``` +DllCall("LoadLibrary", "Str", "OSC2AHK.dll", "Ptr") +``` +(Assuming the OSC2AHK.dll file is in the same directory as the calling script.) + +#### Open network port +```cpp +int open(HWND targetWindowHandle, unsigned int port); +//targetWindowHandle: Handle to the calling window to which messages on received OSC data is sent +//port: Network port to open +``` +This opens the network port and enables the DLL to receive OSC messages. + +Example AHK snippet: +``` +Gui +LastFound +hWnd := WinExist() +DllCall("OSC2AHK.dll\open", UInt, hWnd, UInt, 7002) +``` + +#### Close network port +If you want to close the network port before termination of the script you may use: +```cpp +int close(unsigned int clearListeners = 1); +//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). +``` +Example AHK call without clearing the listners: +``` +DllCall("OSC2AHK.dll\close", UInt, 0) +``` + +#### Add listener +```cpp +int addListener(LPCSTR address, unsigned int messageID, unsigned int dataType = OSC_TYPE_ALL); +//address: OSC address to listen for +//messageID: ID for the system message that is sent to the AHK script when this listener receives something. +//dataType: Specifies the OSC datatype to listen for. See below. May be omitted, then it defaults to all datatypes. +``` +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. +```cpp +#define OSC_TYPE_NONE 1 +#define OSC_TYPE_INT 2 +#define OSC_TYPE_FLOAT 4 +#define OSC_TYPE_STRING 8 +#define OSC_TYPE_BLOB 16 +#define OSC_TYPE_INT64 32 +#define OSC_TYPE_FLOAT64 64 +#define OSC_TYPE_BOOL 128 +#define OSC_TYPE_CHAR 256 +#define OSC_TYPE_TIMETAG 512 +#define OSC_TYPE_COLOR 1024 +#define OSC_TYPE_ALL UINT_MAX //=0xffffffff +``` +Add listener from AHK script: +``` +; just for convenience, you also could use the "magic numbers" directly +global oscTypeInt := 2 +global oscTypeFloat := 4 +; 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. +DllCall("OSC2AHK.dll\addListener", AStr, "/test1", UInt, 0x1001, UInt, oscTypeInt+oscTypeFloat) +; tell AHK to invoke the function msghandlerTest1 when a windows message with ID 0x1001 is received +OnMessage(0x1001, "msghandlerTest1") +``` +Then, there has to be the handler function in the AHK script: +``` +msghandlerTest1(oscType, data, msgID, hwnd) +{ + ; Check which datatype we received + if (oscType = oscTypeInt) + { + ; Integers can be used "as is" + msgbox,Got Integer: %data% + } + if (oscType = oscTypeFloat) + { + ; Floats have to be "reinterpreted" + VarSetCapacity(buf, 4, 0) + NumPut(data, buf) + theFloat := NumGet(buf, "Float") + msgbox,Got Float: %theFloat% + } +} +``` + +#### Remove listener +Of course, listeners also can be removed in a similar way: +```cpp +int removeListener(LPCSTR address); +``` +Example AHK call: +``` +DllCall("OSC2AHK.dll\removeListener", AStr, "/test1") +``` + +#### Get string data +__Work in progress, this is not working properly for now__ + +As string data carried by OSC messages may be of varying length, they 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 retreived later on by the AHK script by calling this function. +```cpp +char* getStringData(char* targetString, unsigned int targetSize, unsigned int StringID); +``` +This function writes the stored string to the `targetString` and also removes it from the buffer inside the DLL. + +A example AHK snippet could look like this: +``` +global oscTypeString := 8 + +DllCall("OSC2AHK.dll\addListener", AStr, "/test2", UInt, 0x1004, UInt, oscTypeString) +OnMessage(0x1004, "msghandlerString") + +msghandlerString(oscType, data, msgID, hwnd) +{ + if (wParam != oscTypeString) + { ; not a string! + return + } + ; We were notified that a OSC string message with the specified address was received + ; and the string payload was stored with the ID passed by 'data'. + ; Now we will retrieve this string from the DLL. + VarSetCapacity(theStr, 20) + theStr := DllCall("OSC2AHK.dll\getStringData", AStr, theStr, UInt, 20, UInt, lParam, "Cdecl AStr") + + msgbox,%theStr% +} +``` + +## Credits +This DLL uses the [oscpack](http://www.rossbencina.com/code/oscpack) OSC implementation by Ross Bencina. \ No newline at end of file