/src/mozilla-central/dom/midi/TestMIDIPlatformService.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
3 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #include "TestMIDIPlatformService.h" |
6 | | #include "mozilla/dom/MIDIPort.h" |
7 | | #include "mozilla/dom/MIDITypes.h" |
8 | | #include "mozilla/dom/MIDIPortInterface.h" |
9 | | #include "mozilla/dom/MIDIPortParent.h" |
10 | | #include "mozilla/dom/MIDIPlatformRunnables.h" |
11 | | #include "mozilla/dom/MIDIUtils.h" |
12 | | #include "mozilla/ipc/BackgroundParent.h" |
13 | | #include "mozilla/Unused.h" |
14 | | |
15 | | using namespace mozilla; |
16 | | using namespace mozilla::dom; |
17 | | using namespace mozilla::ipc; |
18 | | |
19 | | /** |
20 | | * Runnable used for making sure ProcessMessages only happens on the IO thread. |
21 | | * |
22 | | */ |
23 | | class ProcessMessagesRunnable : public mozilla::Runnable |
24 | | { |
25 | | public: |
26 | | explicit ProcessMessagesRunnable(const nsAString& aPortID) : |
27 | | Runnable("ProcessMessagesRunnable"), |
28 | | mPortID(aPortID) |
29 | 0 | {} |
30 | 0 | ~ProcessMessagesRunnable() {} |
31 | | NS_IMETHOD Run() |
32 | 0 | { |
33 | 0 | // If service is no longer running, just exist without processing. |
34 | 0 | if (!MIDIPlatformService::IsRunning()) { |
35 | 0 | return NS_OK; |
36 | 0 | } |
37 | 0 | TestMIDIPlatformService* srv = static_cast<TestMIDIPlatformService*>(MIDIPlatformService::Get()); |
38 | 0 | srv->ProcessMessages(mPortID); |
39 | 0 | return NS_OK; |
40 | 0 | } |
41 | | private: |
42 | | nsString mPortID; |
43 | | }; |
44 | | |
45 | | /** |
46 | | * Runnable used for allowing IO thread to queue more messages for processing, |
47 | | * since it can't access the service object directly. |
48 | | * |
49 | | */ |
50 | | class QueueMessagesRunnable : public MIDIBackgroundRunnable |
51 | | { |
52 | | public: |
53 | | QueueMessagesRunnable(const nsAString& aPortID, |
54 | | const nsTArray<MIDIMessage>& aMsgs) : |
55 | | MIDIBackgroundRunnable("QueueMessagesRunnable"), |
56 | | mPortID(aPortID), |
57 | | mMsgs(aMsgs) |
58 | 0 | {} |
59 | 0 | ~QueueMessagesRunnable() {} |
60 | | virtual void RunInternal() |
61 | 0 | { |
62 | 0 | AssertIsOnBackgroundThread(); |
63 | 0 | MIDIPlatformService::Get()->QueueMessages(mPortID, mMsgs); |
64 | 0 | } |
65 | | private: |
66 | | nsString mPortID; |
67 | | nsTArray<MIDIMessage> mMsgs; |
68 | | }; |
69 | | |
70 | | TestMIDIPlatformService::TestMIDIPlatformService() : |
71 | | mBackgroundThread(NS_GetCurrentThread()), |
72 | | mControlInputPort(NS_LITERAL_STRING("b744eebe-f7d8-499b-872b-958f63c8f522"), |
73 | | NS_LITERAL_STRING("Test Control MIDI Device Input Port"), |
74 | | NS_LITERAL_STRING("Test Manufacturer"), |
75 | | NS_LITERAL_STRING("1.0.0"), |
76 | | static_cast<uint32_t>(MIDIPortType::Input)), |
77 | | mControlOutputPort(NS_LITERAL_STRING("ab8e7fe8-c4de-436a-a960-30898a7c9a3d"), |
78 | | NS_LITERAL_STRING("Test Control MIDI Device Output Port"), |
79 | | NS_LITERAL_STRING("Test Manufacturer"), |
80 | | NS_LITERAL_STRING("1.0.0"), |
81 | | static_cast<uint32_t>(MIDIPortType::Output)), |
82 | | mStateTestInputPort(NS_LITERAL_STRING("a9329677-8588-4460-a091-9d4a7f629a48"), |
83 | | NS_LITERAL_STRING("Test State MIDI Device Input Port"), |
84 | | NS_LITERAL_STRING("Test Manufacturer"), |
85 | | NS_LITERAL_STRING("1.0.0"), |
86 | | static_cast<uint32_t>(MIDIPortType::Input)), |
87 | | mStateTestOutputPort(NS_LITERAL_STRING("478fa225-b5fc-4fa6-a543-d32d9cb651e7"), |
88 | | NS_LITERAL_STRING("Test State MIDI Device Output Port"), |
89 | | NS_LITERAL_STRING("Test Manufacturer"), |
90 | | NS_LITERAL_STRING("1.0.0"), |
91 | | static_cast<uint32_t>(MIDIPortType::Output)), |
92 | | mAlwaysClosedTestOutputPort(NS_LITERAL_STRING("f87d0c76-3c68-49a9-a44f-700f1125c07a"), |
93 | | NS_LITERAL_STRING("Always Closed MIDI Device Output Port"), |
94 | | NS_LITERAL_STRING("Test Manufacturer"), |
95 | | NS_LITERAL_STRING("1.0.0"), |
96 | | static_cast<uint32_t>(MIDIPortType::Output)), |
97 | | mIsInitialized(false) |
98 | 0 | { |
99 | 0 | AssertIsOnBackgroundThread(); |
100 | 0 | } |
101 | | |
102 | | TestMIDIPlatformService::~TestMIDIPlatformService() |
103 | 0 | { |
104 | 0 | AssertIsOnBackgroundThread(); |
105 | 0 | } |
106 | | |
107 | | void |
108 | | TestMIDIPlatformService::Init() |
109 | 0 | { |
110 | 0 | AssertIsOnBackgroundThread(); |
111 | 0 |
|
112 | 0 | if (mIsInitialized) { |
113 | 0 | return; |
114 | 0 | } |
115 | 0 | mIsInitialized = true; |
116 | 0 |
|
117 | 0 | // Treat all of our special ports as always connected. When the service comes |
118 | 0 | // up, prepopulate the port list with them. |
119 | 0 | MIDIPlatformService::Get()->AddPortInfo(mControlInputPort); |
120 | 0 | MIDIPlatformService::Get()->AddPortInfo(mControlOutputPort); |
121 | 0 | MIDIPlatformService::Get()->AddPortInfo(mAlwaysClosedTestOutputPort); |
122 | 0 | nsCOMPtr<nsIRunnable> r(new SendPortListRunnable()); |
123 | 0 |
|
124 | 0 | // Start the IO Thread. |
125 | 0 | NS_DispatchToCurrentThread(r); |
126 | 0 | } |
127 | | |
128 | | void |
129 | | TestMIDIPlatformService::Open(MIDIPortParent* aPort) |
130 | 0 | { |
131 | 0 | MOZ_ASSERT(aPort); |
132 | 0 | MIDIPortConnectionState s = MIDIPortConnectionState::Open; |
133 | 0 | if (aPort->MIDIPortInterface::Id() == mAlwaysClosedTestOutputPort.id()) { |
134 | 0 | // If it's the always closed testing port, act like it's already opened |
135 | 0 | // exclusively elsewhere. |
136 | 0 | s = MIDIPortConnectionState::Closed; |
137 | 0 | } |
138 | 0 | // Connection events are just simulated on the background thread, no need to |
139 | 0 | // push to IO thread. |
140 | 0 | nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(aPort->MIDIPortInterface::Id(), |
141 | 0 | aPort->DeviceState(), |
142 | 0 | s)); |
143 | 0 | NS_DispatchToCurrentThread(r); |
144 | 0 | } |
145 | | |
146 | | void |
147 | | TestMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) |
148 | 0 | { |
149 | 0 | MOZ_ASSERT(aPort); |
150 | 0 | if (aPort->ConnectionState() == MIDIPortConnectionState::Open) { |
151 | 0 | // Connection events are just simulated on the background thread, no need to |
152 | 0 | // push to IO thread. |
153 | 0 | nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(aPort->MIDIPortInterface::Id(), |
154 | 0 | aPort->DeviceState(), |
155 | 0 | MIDIPortConnectionState::Closed)); |
156 | 0 | NS_DispatchToCurrentThread(r); |
157 | 0 | } |
158 | 0 | } |
159 | | |
160 | | void |
161 | | TestMIDIPlatformService::Stop() |
162 | 0 | { |
163 | 0 | AssertIsOnBackgroundThread(); |
164 | 0 | } |
165 | | |
166 | | void TestMIDIPlatformService::ScheduleSend(const nsAString& aPortId) |
167 | 0 | { |
168 | 0 | nsCOMPtr<nsIRunnable> r(new ProcessMessagesRunnable(aPortId)); |
169 | 0 | NS_DispatchToCurrentThread(r); |
170 | 0 | } |
171 | | |
172 | | void TestMIDIPlatformService::ProcessMessages(const nsAString& aPortId) |
173 | 0 | { |
174 | 0 | nsTArray<MIDIMessage> msgs; |
175 | 0 | GetMessagesBefore(aPortId, TimeStamp::Now(), msgs); |
176 | 0 |
|
177 | 0 | for (MIDIMessage msg : msgs) { |
178 | 0 | // receiving message from test control port |
179 | 0 | if (aPortId == mControlOutputPort.id()) { |
180 | 0 | switch (msg.data()[0]) { |
181 | 0 | // Hit a note, get a test! |
182 | 0 | case 0x90: |
183 | 0 | switch (msg.data()[1]) { |
184 | 0 | // Echo data/timestamp back through output port |
185 | 0 | case 0x00: |
186 | 0 | { |
187 | 0 | nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(mControlInputPort.id(), msg)); |
188 | 0 | mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL); |
189 | 0 | break; |
190 | 0 | } |
191 | 0 | // Cause control test ports to connect |
192 | 0 | case 0x01: |
193 | 0 | { |
194 | 0 | nsCOMPtr<nsIRunnable> r1(new AddPortRunnable(mStateTestInputPort)); |
195 | 0 | mBackgroundThread->Dispatch(r1, NS_DISPATCH_NORMAL); |
196 | 0 | break; |
197 | 0 | } |
198 | 0 | // Cause control test ports to disconnect |
199 | 0 | case 0x02: |
200 | 0 | { |
201 | 0 | nsCOMPtr<nsIRunnable> r1(new RemovePortRunnable(mStateTestInputPort)); |
202 | 0 | mBackgroundThread->Dispatch(r1, NS_DISPATCH_NORMAL); |
203 | 0 | break; |
204 | 0 | } |
205 | 0 | // Test for packet timing |
206 | 0 | case 0x03: |
207 | 0 | { |
208 | 0 | // Append a few echo command packets in reverse timing order, should |
209 | 0 | // come out in correct order on other end. |
210 | 0 | nsTArray<MIDIMessage> newMsgs; |
211 | 0 | nsTArray<uint8_t> msg; |
212 | 0 | msg.AppendElement(0x90); |
213 | 0 | msg.AppendElement(0x00); |
214 | 0 | msg.AppendElement(0x00); |
215 | 0 | // PR_Now() returns nanosecods, and we need a double with fractional |
216 | 0 | // milliseconds. |
217 | 0 | TimeStamp currentTime = TimeStamp::Now(); |
218 | 0 | for (int i = 0; i <= 5; ++i) { |
219 | 0 | // Insert messages with timestamps in reverse order, to make sure |
220 | 0 | // we're sorting correctly. |
221 | 0 | newMsgs.AppendElement( |
222 | 0 | MIDIMessage(msg, currentTime - |
223 | 0 | TimeDuration::FromMilliseconds(i * 2))); |
224 | 0 | } |
225 | 0 | nsCOMPtr<nsIRunnable> r(new QueueMessagesRunnable(aPortId, newMsgs)); |
226 | 0 | mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL); |
227 | 0 | break; |
228 | 0 | } |
229 | 0 | default: |
230 | 0 | NS_WARNING("Unknown Test MIDI message received!"); |
231 | 0 | } |
232 | 0 | break; |
233 | 0 | // Sysex tests |
234 | 0 | case 0xF0: |
235 | 0 | switch (msg.data()[1]) { |
236 | 0 | // Echo data/timestamp back through output port |
237 | 0 | case 0x00: |
238 | 0 | { |
239 | 0 | nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(mControlInputPort.id(), msg)); |
240 | 0 | mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL); |
241 | 0 | break; |
242 | 0 | } |
243 | 0 | // Test for system real time messages in the middle of sysex messages. |
244 | 0 | case 0x01: |
245 | 0 | { |
246 | 0 | nsTArray<uint8_t> msgs; |
247 | 0 | const uint8_t msg[] = {0xF0, 0x01, 0xF8, 0x02, 0x03, 0x04, 0xF9, 0x05, 0xF7}; |
248 | 0 | // Can't use AppendElements on an array here, so just do range based loading. |
249 | 0 | for (auto& s : msg) { |
250 | 0 | msgs.AppendElement(s); |
251 | 0 | } |
252 | 0 | nsTArray<MIDIMessage> newMsgs; |
253 | 0 | MIDIUtils::ParseMessages(msgs, TimeStamp::Now(), newMsgs); |
254 | 0 | nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(mControlInputPort.id(), newMsgs)); |
255 | 0 | mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL); |
256 | 0 | break; |
257 | 0 | } |
258 | 0 | default: |
259 | 0 | NS_WARNING("Unknown Test Sysex MIDI message received!"); |
260 | 0 | } |
261 | 0 | break; |
262 | 0 | } |
263 | 0 | } |
264 | 0 | } |
265 | 0 | } |
266 | | |