Backed out changeset b09d48d2b473 (bug 1655101) for causing mochitest webgl failures...
[gecko.git] / dom / midi / midirMIDIPlatformService.cpp
blob842eb51dcddc2b2950ca1c33e0bb3e283e1415e7
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 "midirMIDIPlatformService.h"
6 #include "mozilla/StaticMutex.h"
7 #include "mozilla/TimeStamp.h"
8 #include "mozilla/dom/MIDIPort.h"
9 #include "mozilla/dom/MIDITypes.h"
10 #include "mozilla/dom/MIDIPortInterface.h"
11 #include "mozilla/dom/MIDIPortParent.h"
12 #include "mozilla/dom/MIDIPlatformRunnables.h"
13 #include "mozilla/dom/MIDIUtils.h"
14 #include "mozilla/dom/midi/midir_impl_ffi_generated.h"
15 #include "mozilla/ipc/BackgroundParent.h"
16 #include "mozilla/Unused.h"
17 #include "nsIThread.h"
18 #include "mozilla/Logging.h"
19 #include "MIDILog.h"
21 using namespace mozilla;
22 using namespace mozilla::dom;
23 using namespace mozilla::ipc;
25 static_assert(sizeof(TimeStamp) == sizeof(GeckoTimeStamp));
27 /**
28 * Runnable used for to send messages asynchronously on the I/O thread.
30 class SendRunnable : public MIDIBackgroundRunnable {
31 public:
32 explicit SendRunnable(const nsAString& aPortID, const MIDIMessage& aMessage)
33 : MIDIBackgroundRunnable("SendRunnable"),
34 mPortID(aPortID),
35 mMessage(aMessage) {}
36 ~SendRunnable() = default;
37 virtual void RunInternal() {
38 MIDIPlatformService::AssertThread();
39 if (!MIDIPlatformService::IsRunning()) {
40 // Some send operations might outlive the service, bail out and do nothing
41 return;
43 midirMIDIPlatformService* srv =
44 static_cast<midirMIDIPlatformService*>(MIDIPlatformService::Get());
45 srv->SendMessage(mPortID, mMessage);
48 private:
49 nsString mPortID;
50 MIDIMessage mMessage;
53 // static
54 StaticMutex midirMIDIPlatformService::gOwnerThreadMutex;
56 // static
57 nsCOMPtr<nsISerialEventTarget> midirMIDIPlatformService::gOwnerThread;
59 midirMIDIPlatformService::midirMIDIPlatformService()
60 : mImplementation(nullptr) {
61 StaticMutexAutoLock lock(gOwnerThreadMutex);
62 gOwnerThread = OwnerThread();
65 midirMIDIPlatformService::~midirMIDIPlatformService() {
66 LOG("midir_impl_shutdown");
67 if (mImplementation) {
68 midir_impl_shutdown(mImplementation);
70 StaticMutexAutoLock lock(gOwnerThreadMutex);
71 gOwnerThread = nullptr;
74 // static
75 void midirMIDIPlatformService::AddPort(const nsString* aId,
76 const nsString* aName, bool aInput) {
77 MIDIPortType type = aInput ? MIDIPortType::Input : MIDIPortType::Output;
78 MIDIPortInfo port(*aId, *aName, u""_ns, u""_ns, static_cast<uint32_t>(type));
79 MIDIPlatformService::Get()->AddPortInfo(port);
82 // static
83 void midirMIDIPlatformService::RemovePort(const nsString* aId,
84 const nsString* aName, bool aInput) {
85 MIDIPortType type = aInput ? MIDIPortType::Input : MIDIPortType::Output;
86 MIDIPortInfo port(*aId, *aName, u""_ns, u""_ns, static_cast<uint32_t>(type));
87 MIDIPlatformService::Get()->RemovePortInfo(port);
90 void midirMIDIPlatformService::Init() {
91 if (mImplementation) {
92 return;
95 mImplementation = midir_impl_init(AddPort);
97 if (mImplementation) {
98 MIDIPlatformService::Get()->SendPortList();
99 } else {
100 LOG("midir_impl_init failure");
104 // static
105 void midirMIDIPlatformService::CheckAndReceive(const nsString* aId,
106 const uint8_t* aData,
107 size_t aLength,
108 const GeckoTimeStamp* aTimeStamp,
109 uint64_t aMicros) {
110 nsTArray<uint8_t> data;
111 data.AppendElements(aData, aLength);
112 const TimeStamp* openTime = reinterpret_cast<const TimeStamp*>(aTimeStamp);
113 TimeStamp timestamp =
114 *openTime + TimeDuration::FromMicroseconds(static_cast<double>(aMicros));
115 MIDIMessage message(data, timestamp);
116 LogMIDIMessage(message, *aId, MIDIPortType::Input);
117 nsTArray<MIDIMessage> messages;
118 messages.AppendElement(message);
120 nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(*aId, messages));
121 StaticMutexAutoLock lock(gOwnerThreadMutex);
122 if (gOwnerThread) {
123 gOwnerThread->Dispatch(r, NS_DISPATCH_NORMAL);
127 void midirMIDIPlatformService::Refresh() {
128 midir_impl_refresh(mImplementation, AddPort, RemovePort);
131 void midirMIDIPlatformService::Open(MIDIPortParent* aPort) {
132 AssertThread();
133 MOZ_ASSERT(aPort);
134 nsString id = aPort->MIDIPortInterface::Id();
135 TimeStamp openTimeStamp = TimeStamp::Now();
136 if (midir_impl_open_port(mImplementation, &id,
137 reinterpret_cast<GeckoTimeStamp*>(&openTimeStamp),
138 CheckAndReceive)) {
139 LOG("MIDI port open: %s at t=%lf", NS_ConvertUTF16toUTF8(id).get(),
140 (openTimeStamp - TimeStamp::ProcessCreation()).ToSeconds());
141 nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
142 aPort, aPort->DeviceState(), MIDIPortConnectionState::Open));
143 OwnerThread()->Dispatch(r.forget());
144 } else {
145 LOG("MIDI port open failed: %s", NS_ConvertUTF16toUTF8(id).get());
149 void midirMIDIPlatformService::Stop() {
150 // Nothing to do here AFAIK
153 void midirMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
154 AssertThread();
155 LOG("MIDI port schedule send %s", NS_ConvertUTF16toUTF8(aPortId).get());
156 nsTArray<MIDIMessage> messages;
157 GetMessages(aPortId, messages);
158 TimeStamp now = TimeStamp::Now();
159 for (const auto& message : messages) {
160 if (message.timestamp().IsNull()) {
161 SendMessage(aPortId, message);
162 } else {
163 double delay = (message.timestamp() - now).ToMilliseconds();
164 if (delay < 1.0) {
165 SendMessage(aPortId, message);
166 } else {
167 nsCOMPtr<nsIRunnable> r(new SendRunnable(aPortId, message));
168 OwnerThread()->DelayedDispatch(r.forget(),
169 static_cast<uint32_t>(delay));
175 void midirMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
176 AssertThread();
177 MOZ_ASSERT(aPort);
178 nsString id = aPort->MIDIPortInterface::Id();
179 LOG("MIDI port schedule close %s", NS_ConvertUTF16toUTF8(id).get());
180 if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
181 midir_impl_close_port(mImplementation, &id);
182 nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
183 aPort, aPort->DeviceState(), MIDIPortConnectionState::Closed));
184 OwnerThread()->Dispatch(r.forget());
188 void midirMIDIPlatformService::SendMessage(const nsAString& aPortId,
189 const MIDIMessage& aMessage) {
190 LOG("MIDI send message on %s", NS_ConvertUTF16toUTF8(aPortId).get());
191 LogMIDIMessage(aMessage, aPortId, MIDIPortType::Output);
192 midir_impl_send(mImplementation, &aPortId, &aMessage.data());