1 | // Copyright (C) 2014 Robin Burchell <robin.burchell@viroteck.net> |
2 | // Copyright (C) 2016 The Qt Company Ltd. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #include "qoscmessage_p.h" |
6 | #include "qtuio_p.h" |
7 | |
8 | #include <QDebug> |
9 | #include <QtEndian> |
10 | #include <QLoggingCategory> |
11 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | Q_LOGGING_CATEGORY(lcTuioMessage, "qt.qpa.tuio.message" ) |
15 | |
16 | QOscMessage::QOscMessage() {} |
17 | |
18 | // TUIO packets are transmitted using the OSC protocol, located at: |
19 | // http://opensoundcontrol.org/specification |
20 | // Snippets of this specification have been pasted into the source as a means of |
21 | // easily communicating requirements. |
22 | |
23 | QOscMessage::QOscMessage(const QByteArray &data) |
24 | : m_isValid(false) |
25 | { |
26 | qCDebug(lcTuioMessage) << data.toHex(); |
27 | quint32 parsedBytes = 0; |
28 | |
29 | // "An OSC message consists of an OSC Address Pattern" |
30 | QByteArray addressPattern; |
31 | if (!qt_readOscString(source: data, dest&: addressPattern, pos&: parsedBytes) || addressPattern.size() == 0) |
32 | return; |
33 | |
34 | // "followed by an OSC Type Tag String" |
35 | QByteArray typeTagString; |
36 | if (!qt_readOscString(source: data, dest&: typeTagString, pos&: parsedBytes)) |
37 | return; |
38 | |
39 | // "Note: some older implementations of OSC may omit the OSC Type Tag string. |
40 | // Until all such implementations are updated, OSC implementations should be |
41 | // robust in the case of a missing OSC Type Tag String." |
42 | // |
43 | // (although, the editor notes one may question how exactly the hell one is |
44 | // supposed to be robust when the behavior is unspecified.) |
45 | if (typeTagString.size() == 0 || typeTagString.at(i: 0) != ',') |
46 | return; |
47 | |
48 | QList<QVariant> arguments; |
49 | |
50 | // "followed by zero or more OSC Arguments." |
51 | for (int i = 1; i < typeTagString.size(); ++i) { |
52 | char typeTag = typeTagString.at(i); |
53 | if (typeTag == 's') { // osc-string |
54 | QByteArray aString; |
55 | if (!qt_readOscString(source: data, dest&: aString, pos&: parsedBytes)) |
56 | return; |
57 | arguments.append(t: aString); |
58 | } else if (typeTag == 'i') { // int32 |
59 | if (parsedBytes > (quint32)data.size() || data.size() - parsedBytes < qsizetype(sizeof(quint32))) |
60 | return; |
61 | |
62 | quint32 anInt = qFromBigEndian<quint32>(src: data.constData() + parsedBytes); |
63 | parsedBytes += sizeof(quint32); |
64 | |
65 | // TODO: is int32 in OSC signed, or unsigned? |
66 | arguments.append(t: (int)anInt); |
67 | } else if (typeTag == 'f') { // float32 |
68 | if (parsedBytes > (quint32)data.size() || data.size() - parsedBytes < qsizetype(sizeof(quint32))) |
69 | return; |
70 | |
71 | static_assert(sizeof(float) == sizeof(quint32)); |
72 | union { |
73 | quint32 u; |
74 | float f; |
75 | } value; |
76 | value.u = qFromBigEndian<quint32>(src: data.constData() + parsedBytes); |
77 | parsedBytes += sizeof(quint32); |
78 | arguments.append(t: value.f); |
79 | } else { |
80 | qCWarning(lcTuioMessage) << "Reading argument of unknown type " << typeTag; |
81 | return; |
82 | } |
83 | } |
84 | |
85 | m_isValid = true; |
86 | m_addressPattern = addressPattern; |
87 | m_arguments = arguments; |
88 | |
89 | qCDebug(lcTuioMessage) << "Message with address pattern: " << addressPattern << " arguments: " << arguments; |
90 | } |
91 | |
92 | QT_END_NAMESPACE |
93 | |
94 | |