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/ipc/BackgroundParent.h"
15 #include "mozilla/Unused.h"
16 #include "nsIThread.h"
17 #include "mozilla/Logging.h"
20 using namespace mozilla
;
21 using namespace mozilla::dom
;
22 using namespace mozilla::ipc
;
24 static_assert(sizeof(TimeStamp
) == sizeof(GeckoTimeStamp
));
27 * Runnable used for to send messages asynchronously on the I/O thread.
29 class SendRunnable
: public MIDIBackgroundRunnable
{
31 explicit SendRunnable(const nsAString
& aPortID
, const MIDIMessage
& aMessage
)
32 : MIDIBackgroundRunnable("SendRunnable"),
35 ~SendRunnable() = default;
36 virtual void RunInternal() {
37 AssertIsOnBackgroundThread();
38 if (!MIDIPlatformService::IsRunning()) {
39 // Some send operations might outlive the service, bail out and do nothing
42 midirMIDIPlatformService
* srv
=
43 static_cast<midirMIDIPlatformService
*>(MIDIPlatformService::Get());
44 srv
->SendMessage(mPortID
, mMessage
);
53 StaticMutex
midirMIDIPlatformService::gBackgroundThreadMutex
;
56 nsCOMPtr
<nsIThread
> midirMIDIPlatformService::gBackgroundThread
;
58 midirMIDIPlatformService::midirMIDIPlatformService()
59 : mImplementation(nullptr) {
60 StaticMutexAutoLock
lock(gBackgroundThreadMutex
);
61 gBackgroundThread
= NS_GetCurrentThread();
64 midirMIDIPlatformService::~midirMIDIPlatformService() {
65 LOG("midir_impl_shutdown");
66 if (mImplementation
) {
67 midir_impl_shutdown(mImplementation
);
69 StaticMutexAutoLock
lock(gBackgroundThreadMutex
);
70 gBackgroundThread
= nullptr;
74 void midirMIDIPlatformService::AddPort(const nsString
* aId
,
75 const nsString
* aName
, bool aInput
) {
76 MIDIPortType type
= aInput
? MIDIPortType::Input
: MIDIPortType::Output
;
77 MIDIPortInfo
port(*aId
, *aName
, u
""_ns
, u
""_ns
, static_cast<uint32_t>(type
));
78 MIDIPlatformService::Get()->AddPortInfo(port
);
81 void midirMIDIPlatformService::Init() {
82 if (mImplementation
) {
86 mImplementation
= midir_impl_init(AddPort
);
88 if (mImplementation
) {
89 MIDIPlatformService::Get()->SendPortList();
91 LOG("midir_impl_init failure");
96 void midirMIDIPlatformService::CheckAndReceive(const nsString
* aId
,
99 const GeckoTimeStamp
* aTimeStamp
,
101 nsTArray
<uint8_t> data
;
102 for (size_t i
= 0; i
< aLength
; i
++) {
103 data
.AppendElement(aData
[i
]);
105 const TimeStamp
* openTime
= reinterpret_cast<const TimeStamp
*>(aTimeStamp
);
106 TimeStamp timestamp
=
107 *openTime
+ TimeDuration::FromMicroseconds(static_cast<double>(aMicros
));
108 MIDIMessage
message(data
, timestamp
);
109 LogMIDIMessage(message
, *aId
, MIDIPortType::Input
);
110 nsTArray
<MIDIMessage
> messages
;
111 messages
.AppendElement(message
);
113 nsCOMPtr
<nsIRunnable
> r(new ReceiveRunnable(*aId
, messages
));
114 StaticMutexAutoLock
lock(gBackgroundThreadMutex
);
115 if (gBackgroundThread
) {
116 gBackgroundThread
->Dispatch(r
, NS_DISPATCH_NORMAL
);
120 void midirMIDIPlatformService::Open(MIDIPortParent
* aPort
) {
122 nsString id
= aPort
->MIDIPortInterface::Id();
123 TimeStamp openTimeStamp
= TimeStamp::Now();
124 if (midir_impl_open_port(mImplementation
, &id
,
125 reinterpret_cast<GeckoTimeStamp
*>(&openTimeStamp
),
127 LOG("MIDI port open: %s at t=%lf", NS_ConvertUTF16toUTF8(id
).get(),
128 (openTimeStamp
- TimeStamp::ProcessCreation()).ToSeconds());
129 nsCOMPtr
<nsIRunnable
> r(new SetStatusRunnable(
130 aPort
, aPort
->DeviceState(), MIDIPortConnectionState::Open
));
131 NS_DispatchToCurrentThread(r
);
133 LOG("MIDI port open failed: %s", NS_ConvertUTF16toUTF8(id
).get());
137 void midirMIDIPlatformService::Stop() {
138 // Nothing to do here AFAIK
141 void midirMIDIPlatformService::ScheduleSend(const nsAString
& aPortId
) {
142 LOG("MIDI port schedule send %s", NS_ConvertUTF16toUTF8(aPortId
).get());
143 nsTArray
<MIDIMessage
> messages
;
144 GetMessages(aPortId
, messages
);
145 TimeStamp now
= TimeStamp::Now();
146 for (const auto& message
: messages
) {
147 if (message
.timestamp().IsNull()) {
148 SendMessage(aPortId
, message
);
150 double delay
= (message
.timestamp() - now
).ToMilliseconds();
152 SendMessage(aPortId
, message
);
154 nsCOMPtr
<nsIRunnable
> r(new SendRunnable(aPortId
, message
));
155 NS_DelayedDispatchToCurrentThread(r
.forget(),
156 static_cast<uint32_t>(delay
));
162 void midirMIDIPlatformService::ScheduleClose(MIDIPortParent
* aPort
) {
164 nsString id
= aPort
->MIDIPortInterface::Id();
165 LOG("MIDI port schedule close %s", NS_ConvertUTF16toUTF8(id
).get());
166 if (aPort
->ConnectionState() == MIDIPortConnectionState::Open
) {
167 midir_impl_close_port(mImplementation
, &id
);
168 nsCOMPtr
<nsIRunnable
> r(new SetStatusRunnable(
169 aPort
, aPort
->DeviceState(), MIDIPortConnectionState::Closed
));
170 NS_DispatchToCurrentThread(r
);
174 void midirMIDIPlatformService::SendMessage(const nsAString
& aPortId
,
175 const MIDIMessage
& aMessage
) {
176 LOG("MIDI send message on %s", NS_ConvertUTF16toUTF8(aPortId
).get());
177 LogMIDIMessage(aMessage
, aPortId
, MIDIPortType::Output
);
178 midir_impl_send(mImplementation
, &aPortId
, &aMessage
.data());