Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / dom / midi / TestMIDIPlatformService.cpp
blobcbc11a747462b9c4a5f353abbe3bc61a21a2760e
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/. */
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 #include "nsIThread.h"
16 using namespace mozilla;
17 using namespace mozilla::dom;
18 using namespace mozilla::ipc;
20 /**
21 * Runnable used for making sure ProcessMessages only happens on the IO thread.
24 class ProcessMessagesRunnable : public mozilla::Runnable {
25 public:
26 explicit ProcessMessagesRunnable(const nsAString& aPortID)
27 : Runnable("ProcessMessagesRunnable"), mPortID(aPortID) {}
28 ~ProcessMessagesRunnable() = default;
29 NS_IMETHOD Run() override {
30 // If service is no longer running, just exist without processing.
31 if (!MIDIPlatformService::IsRunning()) {
32 return NS_OK;
34 TestMIDIPlatformService* srv =
35 static_cast<TestMIDIPlatformService*>(MIDIPlatformService::Get());
36 srv->ProcessMessages(mPortID);
37 return NS_OK;
40 private:
41 nsString mPortID;
44 /**
45 * Runnable used for allowing IO thread to queue more messages for processing,
46 * since it can't access the service object directly.
49 class QueueMessagesRunnable : public MIDIBackgroundRunnable {
50 public:
51 QueueMessagesRunnable(const nsAString& aPortID,
52 const nsTArray<MIDIMessage>& aMsgs)
53 : MIDIBackgroundRunnable("QueueMessagesRunnable"),
54 mPortID(aPortID),
55 mMsgs(aMsgs.Clone()) {}
56 ~QueueMessagesRunnable() = default;
57 virtual void RunInternal() {
58 MIDIPlatformService::AssertThread();
59 MIDIPlatformService::Get()->QueueMessages(mPortID, mMsgs);
62 private:
63 nsString mPortID;
64 nsTArray<MIDIMessage> mMsgs;
67 TestMIDIPlatformService::TestMIDIPlatformService()
68 : mControlInputPort(u"b744eebe-f7d8-499b-872b-958f63c8f522"_ns,
69 u"Test Control MIDI Device Input Port"_ns,
70 u"Test Manufacturer"_ns, u"1.0.0"_ns,
71 static_cast<uint32_t>(MIDIPortType::Input)),
72 mControlOutputPort(u"ab8e7fe8-c4de-436a-a960-30898a7c9a3d"_ns,
73 u"Test Control MIDI Device Output Port"_ns,
74 u"Test Manufacturer"_ns, u"1.0.0"_ns,
75 static_cast<uint32_t>(MIDIPortType::Output)),
76 mStateTestInputPort(u"a9329677-8588-4460-a091-9d4a7f629a48"_ns,
77 u"Test State MIDI Device Input Port"_ns,
78 u"Test Manufacturer"_ns, u"1.0.0"_ns,
79 static_cast<uint32_t>(MIDIPortType::Input)),
80 mStateTestOutputPort(u"478fa225-b5fc-4fa6-a543-d32d9cb651e7"_ns,
81 u"Test State MIDI Device Output Port"_ns,
82 u"Test Manufacturer"_ns, u"1.0.0"_ns,
83 static_cast<uint32_t>(MIDIPortType::Output)),
84 mAlwaysClosedTestOutputPort(u"f87d0c76-3c68-49a9-a44f-700f1125c07a"_ns,
85 u"Always Closed MIDI Device Output Port"_ns,
86 u"Test Manufacturer"_ns, u"1.0.0"_ns,
87 static_cast<uint32_t>(MIDIPortType::Output)),
88 mDoRefresh(false),
89 mIsInitialized(false) {
90 MIDIPlatformService::AssertThread();
93 TestMIDIPlatformService::~TestMIDIPlatformService() {
94 MIDIPlatformService::AssertThread();
97 void TestMIDIPlatformService::Init() {
98 MIDIPlatformService::AssertThread();
100 if (mIsInitialized) {
101 return;
103 mIsInitialized = true;
105 // Treat all of our special ports as always connected. When the service comes
106 // up, prepopulate the port list with them.
107 MIDIPlatformService::Get()->AddPortInfo(mControlInputPort);
108 MIDIPlatformService::Get()->AddPortInfo(mControlOutputPort);
109 MIDIPlatformService::Get()->AddPortInfo(mAlwaysClosedTestOutputPort);
110 MIDIPlatformService::Get()->AddPortInfo(mStateTestOutputPort);
111 nsCOMPtr<nsIRunnable> r(new SendPortListRunnable());
113 // Start the IO Thread.
114 OwnerThread()->Dispatch(r.forget());
117 void TestMIDIPlatformService::Refresh() {
118 if (mDoRefresh) {
119 AddPortInfo(mStateTestInputPort);
120 mDoRefresh = false;
124 void TestMIDIPlatformService::Open(MIDIPortParent* aPort) {
125 MOZ_ASSERT(aPort);
126 MIDIPortConnectionState s = MIDIPortConnectionState::Open;
127 if (aPort->MIDIPortInterface::Id() == mAlwaysClosedTestOutputPort.id()) {
128 // If it's the always closed testing port, act like it's already opened
129 // exclusively elsewhere.
130 s = MIDIPortConnectionState::Closed;
132 // Connection events are just simulated on the background thread, no need to
133 // push to IO thread.
134 nsCOMPtr<nsIRunnable> r(
135 new SetStatusRunnable(aPort, aPort->DeviceState(), s));
136 OwnerThread()->Dispatch(r.forget());
139 void TestMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
140 AssertThread();
141 MOZ_ASSERT(aPort);
142 if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
143 // Connection events are just simulated on the background thread, no need to
144 // push to IO thread.
145 nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
146 aPort, aPort->DeviceState(), MIDIPortConnectionState::Closed));
147 OwnerThread()->Dispatch(r.forget());
151 void TestMIDIPlatformService::Stop() { MIDIPlatformService::AssertThread(); }
153 void TestMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
154 AssertThread();
155 nsCOMPtr<nsIRunnable> r(new ProcessMessagesRunnable(aPortId));
156 OwnerThread()->Dispatch(r.forget());
159 void TestMIDIPlatformService::ProcessMessages(const nsAString& aPortId) {
160 nsTArray<MIDIMessage> msgs;
161 GetMessagesBefore(aPortId, TimeStamp::Now(), msgs);
163 for (MIDIMessage msg : msgs) {
164 // receiving message from test control port
165 if (aPortId == mControlOutputPort.id()) {
166 switch (msg.data()[0]) {
167 // Hit a note, get a test!
168 case 0x90:
169 switch (msg.data()[1]) {
170 // Echo data/timestamp back through output port
171 case 0x00: {
172 nsCOMPtr<nsIRunnable> r(
173 new ReceiveRunnable(mControlInputPort.id(), msg));
174 OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
175 break;
177 // Cause control test ports to connect
178 case 0x01: {
179 nsCOMPtr<nsIRunnable> r1(
180 new AddPortRunnable(mStateTestInputPort));
181 OwnerThread()->Dispatch(r1, NS_DISPATCH_NORMAL);
182 break;
184 // Cause control test ports to disconnect
185 case 0x02: {
186 nsCOMPtr<nsIRunnable> r1(
187 new RemovePortRunnable(mStateTestInputPort));
188 OwnerThread()->Dispatch(r1, NS_DISPATCH_NORMAL);
189 break;
191 // Test for packet timing
192 case 0x03: {
193 // Append a few echo command packets in reverse timing order,
194 // should come out in correct order on other end.
195 nsTArray<MIDIMessage> newMsgs;
196 nsTArray<uint8_t> msg;
197 msg.AppendElement(0x90);
198 msg.AppendElement(0x00);
199 msg.AppendElement(0x00);
200 // PR_Now() returns nanosecods, and we need a double with
201 // fractional milliseconds.
202 TimeStamp currentTime = TimeStamp::Now();
203 for (int i = 0; i <= 5; ++i) {
204 // Insert messages with timestamps in reverse order, to make
205 // sure we're sorting correctly.
206 newMsgs.AppendElement(MIDIMessage(
207 msg, currentTime - TimeDuration::FromMilliseconds(i * 2)));
209 nsCOMPtr<nsIRunnable> r(
210 new QueueMessagesRunnable(aPortId, newMsgs));
211 OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
212 break;
214 // Causes the next refresh to add new ports to the list
215 case 0x04: {
216 mDoRefresh = true;
217 break;
219 default:
220 NS_WARNING("Unknown Test MIDI message received!");
222 break;
223 // Sysex tests
224 case 0xF0:
225 switch (msg.data()[1]) {
226 // Echo data/timestamp back through output port
227 case 0x00: {
228 nsCOMPtr<nsIRunnable> r(
229 new ReceiveRunnable(mControlInputPort.id(), msg));
230 OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
231 break;
233 // Test for system real time messages in the middle of sysex
234 // messages.
235 case 0x01: {
236 nsTArray<uint8_t> msgs;
237 const uint8_t msg[] = {0xF0, 0x01, 0xFA, 0x02, 0x03,
238 0x04, 0xF8, 0x05, 0xF7};
239 // Can't use AppendElements on an array here, so just do range
240 // based loading.
241 for (const auto& s : msg) {
242 msgs.AppendElement(s);
244 nsTArray<MIDIMessage> newMsgs;
245 MIDIUtils::ParseMessages(msgs, TimeStamp::Now(), newMsgs);
246 nsCOMPtr<nsIRunnable> r(
247 new ReceiveRunnable(mControlInputPort.id(), newMsgs));
248 OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
249 break;
251 default:
252 NS_WARNING("Unknown Test Sysex MIDI message received!");
254 break;