summaryrefslogtreecommitdiffstats
path: root/dom/midi/midirMIDIPlatformService.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/midi/midirMIDIPlatformService.cpp193
1 files changed, 193 insertions, 0 deletions
diff --git a/dom/midi/midirMIDIPlatformService.cpp b/dom/midi/midirMIDIPlatformService.cpp
new file mode 100644
index 0000000000..842eb51dcd
--- /dev/null
+++ b/dom/midi/midirMIDIPlatformService.cpp
@@ -0,0 +1,193 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "midirMIDIPlatformService.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/dom/MIDIPort.h"
+#include "mozilla/dom/MIDITypes.h"
+#include "mozilla/dom/MIDIPortInterface.h"
+#include "mozilla/dom/MIDIPortParent.h"
+#include "mozilla/dom/MIDIPlatformRunnables.h"
+#include "mozilla/dom/MIDIUtils.h"
+#include "mozilla/dom/midi/midir_impl_ffi_generated.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/Unused.h"
+#include "nsIThread.h"
+#include "mozilla/Logging.h"
+#include "MIDILog.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+static_assert(sizeof(TimeStamp) == sizeof(GeckoTimeStamp));
+
+/**
+ * Runnable used for to send messages asynchronously on the I/O thread.
+ */
+class SendRunnable : public MIDIBackgroundRunnable {
+ public:
+ explicit SendRunnable(const nsAString& aPortID, const MIDIMessage& aMessage)
+ : MIDIBackgroundRunnable("SendRunnable"),
+ mPortID(aPortID),
+ mMessage(aMessage) {}
+ ~SendRunnable() = default;
+ virtual void RunInternal() {
+ MIDIPlatformService::AssertThread();
+ if (!MIDIPlatformService::IsRunning()) {
+ // Some send operations might outlive the service, bail out and do nothing
+ return;
+ }
+ midirMIDIPlatformService* srv =
+ static_cast<midirMIDIPlatformService*>(MIDIPlatformService::Get());
+ srv->SendMessage(mPortID, mMessage);
+ }
+
+ private:
+ nsString mPortID;
+ MIDIMessage mMessage;
+};
+
+// static
+StaticMutex midirMIDIPlatformService::gOwnerThreadMutex;
+
+// static
+nsCOMPtr<nsISerialEventTarget> midirMIDIPlatformService::gOwnerThread;
+
+midirMIDIPlatformService::midirMIDIPlatformService()
+ : mImplementation(nullptr) {
+ StaticMutexAutoLock lock(gOwnerThreadMutex);
+ gOwnerThread = OwnerThread();
+}
+
+midirMIDIPlatformService::~midirMIDIPlatformService() {
+ LOG("midir_impl_shutdown");
+ if (mImplementation) {
+ midir_impl_shutdown(mImplementation);
+ }
+ StaticMutexAutoLock lock(gOwnerThreadMutex);
+ gOwnerThread = nullptr;
+}
+
+// static
+void midirMIDIPlatformService::AddPort(const nsString* aId,
+ const nsString* aName, bool aInput) {
+ MIDIPortType type = aInput ? MIDIPortType::Input : MIDIPortType::Output;
+ MIDIPortInfo port(*aId, *aName, u""_ns, u""_ns, static_cast<uint32_t>(type));
+ MIDIPlatformService::Get()->AddPortInfo(port);
+}
+
+// static
+void midirMIDIPlatformService::RemovePort(const nsString* aId,
+ const nsString* aName, bool aInput) {
+ MIDIPortType type = aInput ? MIDIPortType::Input : MIDIPortType::Output;
+ MIDIPortInfo port(*aId, *aName, u""_ns, u""_ns, static_cast<uint32_t>(type));
+ MIDIPlatformService::Get()->RemovePortInfo(port);
+}
+
+void midirMIDIPlatformService::Init() {
+ if (mImplementation) {
+ return;
+ }
+
+ mImplementation = midir_impl_init(AddPort);
+
+ if (mImplementation) {
+ MIDIPlatformService::Get()->SendPortList();
+ } else {
+ LOG("midir_impl_init failure");
+ }
+}
+
+// static
+void midirMIDIPlatformService::CheckAndReceive(const nsString* aId,
+ const uint8_t* aData,
+ size_t aLength,
+ const GeckoTimeStamp* aTimeStamp,
+ uint64_t aMicros) {
+ nsTArray<uint8_t> data;
+ data.AppendElements(aData, aLength);
+ const TimeStamp* openTime = reinterpret_cast<const TimeStamp*>(aTimeStamp);
+ TimeStamp timestamp =
+ *openTime + TimeDuration::FromMicroseconds(static_cast<double>(aMicros));
+ MIDIMessage message(data, timestamp);
+ LogMIDIMessage(message, *aId, MIDIPortType::Input);
+ nsTArray<MIDIMessage> messages;
+ messages.AppendElement(message);
+
+ nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(*aId, messages));
+ StaticMutexAutoLock lock(gOwnerThreadMutex);
+ if (gOwnerThread) {
+ gOwnerThread->Dispatch(r, NS_DISPATCH_NORMAL);
+ }
+}
+
+void midirMIDIPlatformService::Refresh() {
+ midir_impl_refresh(mImplementation, AddPort, RemovePort);
+}
+
+void midirMIDIPlatformService::Open(MIDIPortParent* aPort) {
+ AssertThread();
+ MOZ_ASSERT(aPort);
+ nsString id = aPort->MIDIPortInterface::Id();
+ TimeStamp openTimeStamp = TimeStamp::Now();
+ if (midir_impl_open_port(mImplementation, &id,
+ reinterpret_cast<GeckoTimeStamp*>(&openTimeStamp),
+ CheckAndReceive)) {
+ LOG("MIDI port open: %s at t=%lf", NS_ConvertUTF16toUTF8(id).get(),
+ (openTimeStamp - TimeStamp::ProcessCreation()).ToSeconds());
+ nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
+ aPort, aPort->DeviceState(), MIDIPortConnectionState::Open));
+ OwnerThread()->Dispatch(r.forget());
+ } else {
+ LOG("MIDI port open failed: %s", NS_ConvertUTF16toUTF8(id).get());
+ }
+}
+
+void midirMIDIPlatformService::Stop() {
+ // Nothing to do here AFAIK
+}
+
+void midirMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
+ AssertThread();
+ LOG("MIDI port schedule send %s", NS_ConvertUTF16toUTF8(aPortId).get());
+ nsTArray<MIDIMessage> messages;
+ GetMessages(aPortId, messages);
+ TimeStamp now = TimeStamp::Now();
+ for (const auto& message : messages) {
+ if (message.timestamp().IsNull()) {
+ SendMessage(aPortId, message);
+ } else {
+ double delay = (message.timestamp() - now).ToMilliseconds();
+ if (delay < 1.0) {
+ SendMessage(aPortId, message);
+ } else {
+ nsCOMPtr<nsIRunnable> r(new SendRunnable(aPortId, message));
+ OwnerThread()->DelayedDispatch(r.forget(),
+ static_cast<uint32_t>(delay));
+ }
+ }
+ }
+}
+
+void midirMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
+ AssertThread();
+ MOZ_ASSERT(aPort);
+ nsString id = aPort->MIDIPortInterface::Id();
+ LOG("MIDI port schedule close %s", NS_ConvertUTF16toUTF8(id).get());
+ if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
+ midir_impl_close_port(mImplementation, &id);
+ nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
+ aPort, aPort->DeviceState(), MIDIPortConnectionState::Closed));
+ OwnerThread()->Dispatch(r.forget());
+ }
+}
+
+void midirMIDIPlatformService::SendMessage(const nsAString& aPortId,
+ const MIDIMessage& aMessage) {
+ LOG("MIDI send message on %s", NS_ConvertUTF16toUTF8(aPortId).get());
+ LogMIDIMessage(aMessage, aPortId, MIDIPortType::Output);
+ midir_impl_send(mImplementation, &aPortId, &aMessage.data());
+}