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"
21 using namespace mozilla
;
22 using namespace mozilla::dom
;
23 using namespace mozilla::ipc
;
25 static_assert(sizeof(TimeStamp
) == sizeof(GeckoTimeStamp
));
28 * Runnable used for to send messages asynchronously on the I/O thread.
30 class SendRunnable
: public MIDIBackgroundRunnable
{
32 explicit SendRunnable(const nsAString
& aPortID
, const MIDIMessage
& aMessage
)
33 : MIDIBackgroundRunnable("SendRunnable"),
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
43 midirMIDIPlatformService
* srv
=
44 static_cast<midirMIDIPlatformService
*>(MIDIPlatformService::Get());
45 srv
->SendMessage(mPortID
, mMessage
);
54 StaticMutex
midirMIDIPlatformService::gOwnerThreadMutex
;
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;
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
);
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
) {
95 mImplementation
= midir_impl_init(AddPort
);
97 if (mImplementation
) {
98 MIDIPlatformService::Get()->SendPortList();
100 LOG("midir_impl_init failure");
105 void midirMIDIPlatformService::CheckAndReceive(const nsString
* aId
,
106 const uint8_t* aData
,
108 const GeckoTimeStamp
* aTimeStamp
,
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
);
123 gOwnerThread
->Dispatch(r
, NS_DISPATCH_NORMAL
);
127 void midirMIDIPlatformService::Refresh() {
128 midir_impl_refresh(mImplementation
, AddPort
, RemovePort
);
131 void midirMIDIPlatformService::Open(MIDIPortParent
* 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
),
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());
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
) {
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
);
163 double delay
= (message
.timestamp() - now
).ToMilliseconds();
165 SendMessage(aPortId
, message
);
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
) {
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());