summaryrefslogtreecommitdiffstats
path: root/dom/midi/TestMIDIPlatformService.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /dom/midi/TestMIDIPlatformService.cpp
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/midi/TestMIDIPlatformService.cpp')
-rw-r--r--dom/midi/TestMIDIPlatformService.cpp258
1 files changed, 258 insertions, 0 deletions
diff --git a/dom/midi/TestMIDIPlatformService.cpp b/dom/midi/TestMIDIPlatformService.cpp
new file mode 100644
index 0000000000..cbc11a7474
--- /dev/null
+++ b/dom/midi/TestMIDIPlatformService.cpp
@@ -0,0 +1,258 @@
+/* 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 "TestMIDIPlatformService.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/ipc/BackgroundParent.h"
+#include "mozilla/Unused.h"
+#include "nsIThread.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+/**
+ * Runnable used for making sure ProcessMessages only happens on the IO thread.
+ *
+ */
+class ProcessMessagesRunnable : public mozilla::Runnable {
+ public:
+ explicit ProcessMessagesRunnable(const nsAString& aPortID)
+ : Runnable("ProcessMessagesRunnable"), mPortID(aPortID) {}
+ ~ProcessMessagesRunnable() = default;
+ NS_IMETHOD Run() override {
+ // If service is no longer running, just exist without processing.
+ if (!MIDIPlatformService::IsRunning()) {
+ return NS_OK;
+ }
+ TestMIDIPlatformService* srv =
+ static_cast<TestMIDIPlatformService*>(MIDIPlatformService::Get());
+ srv->ProcessMessages(mPortID);
+ return NS_OK;
+ }
+
+ private:
+ nsString mPortID;
+};
+
+/**
+ * Runnable used for allowing IO thread to queue more messages for processing,
+ * since it can't access the service object directly.
+ *
+ */
+class QueueMessagesRunnable : public MIDIBackgroundRunnable {
+ public:
+ QueueMessagesRunnable(const nsAString& aPortID,
+ const nsTArray<MIDIMessage>& aMsgs)
+ : MIDIBackgroundRunnable("QueueMessagesRunnable"),
+ mPortID(aPortID),
+ mMsgs(aMsgs.Clone()) {}
+ ~QueueMessagesRunnable() = default;
+ virtual void RunInternal() {
+ MIDIPlatformService::AssertThread();
+ MIDIPlatformService::Get()->QueueMessages(mPortID, mMsgs);
+ }
+
+ private:
+ nsString mPortID;
+ nsTArray<MIDIMessage> mMsgs;
+};
+
+TestMIDIPlatformService::TestMIDIPlatformService()
+ : mControlInputPort(u"b744eebe-f7d8-499b-872b-958f63c8f522"_ns,
+ u"Test Control MIDI Device Input Port"_ns,
+ u"Test Manufacturer"_ns, u"1.0.0"_ns,
+ static_cast<uint32_t>(MIDIPortType::Input)),
+ mControlOutputPort(u"ab8e7fe8-c4de-436a-a960-30898a7c9a3d"_ns,
+ u"Test Control MIDI Device Output Port"_ns,
+ u"Test Manufacturer"_ns, u"1.0.0"_ns,
+ static_cast<uint32_t>(MIDIPortType::Output)),
+ mStateTestInputPort(u"a9329677-8588-4460-a091-9d4a7f629a48"_ns,
+ u"Test State MIDI Device Input Port"_ns,
+ u"Test Manufacturer"_ns, u"1.0.0"_ns,
+ static_cast<uint32_t>(MIDIPortType::Input)),
+ mStateTestOutputPort(u"478fa225-b5fc-4fa6-a543-d32d9cb651e7"_ns,
+ u"Test State MIDI Device Output Port"_ns,
+ u"Test Manufacturer"_ns, u"1.0.0"_ns,
+ static_cast<uint32_t>(MIDIPortType::Output)),
+ mAlwaysClosedTestOutputPort(u"f87d0c76-3c68-49a9-a44f-700f1125c07a"_ns,
+ u"Always Closed MIDI Device Output Port"_ns,
+ u"Test Manufacturer"_ns, u"1.0.0"_ns,
+ static_cast<uint32_t>(MIDIPortType::Output)),
+ mDoRefresh(false),
+ mIsInitialized(false) {
+ MIDIPlatformService::AssertThread();
+}
+
+TestMIDIPlatformService::~TestMIDIPlatformService() {
+ MIDIPlatformService::AssertThread();
+}
+
+void TestMIDIPlatformService::Init() {
+ MIDIPlatformService::AssertThread();
+
+ if (mIsInitialized) {
+ return;
+ }
+ mIsInitialized = true;
+
+ // Treat all of our special ports as always connected. When the service comes
+ // up, prepopulate the port list with them.
+ MIDIPlatformService::Get()->AddPortInfo(mControlInputPort);
+ MIDIPlatformService::Get()->AddPortInfo(mControlOutputPort);
+ MIDIPlatformService::Get()->AddPortInfo(mAlwaysClosedTestOutputPort);
+ MIDIPlatformService::Get()->AddPortInfo(mStateTestOutputPort);
+ nsCOMPtr<nsIRunnable> r(new SendPortListRunnable());
+
+ // Start the IO Thread.
+ OwnerThread()->Dispatch(r.forget());
+}
+
+void TestMIDIPlatformService::Refresh() {
+ if (mDoRefresh) {
+ AddPortInfo(mStateTestInputPort);
+ mDoRefresh = false;
+ }
+}
+
+void TestMIDIPlatformService::Open(MIDIPortParent* aPort) {
+ MOZ_ASSERT(aPort);
+ MIDIPortConnectionState s = MIDIPortConnectionState::Open;
+ if (aPort->MIDIPortInterface::Id() == mAlwaysClosedTestOutputPort.id()) {
+ // If it's the always closed testing port, act like it's already opened
+ // exclusively elsewhere.
+ s = MIDIPortConnectionState::Closed;
+ }
+ // Connection events are just simulated on the background thread, no need to
+ // push to IO thread.
+ nsCOMPtr<nsIRunnable> r(
+ new SetStatusRunnable(aPort, aPort->DeviceState(), s));
+ OwnerThread()->Dispatch(r.forget());
+}
+
+void TestMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
+ AssertThread();
+ MOZ_ASSERT(aPort);
+ if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
+ // Connection events are just simulated on the background thread, no need to
+ // push to IO thread.
+ nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
+ aPort, aPort->DeviceState(), MIDIPortConnectionState::Closed));
+ OwnerThread()->Dispatch(r.forget());
+ }
+}
+
+void TestMIDIPlatformService::Stop() { MIDIPlatformService::AssertThread(); }
+
+void TestMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
+ AssertThread();
+ nsCOMPtr<nsIRunnable> r(new ProcessMessagesRunnable(aPortId));
+ OwnerThread()->Dispatch(r.forget());
+}
+
+void TestMIDIPlatformService::ProcessMessages(const nsAString& aPortId) {
+ nsTArray<MIDIMessage> msgs;
+ GetMessagesBefore(aPortId, TimeStamp::Now(), msgs);
+
+ for (MIDIMessage msg : msgs) {
+ // receiving message from test control port
+ if (aPortId == mControlOutputPort.id()) {
+ switch (msg.data()[0]) {
+ // Hit a note, get a test!
+ case 0x90:
+ switch (msg.data()[1]) {
+ // Echo data/timestamp back through output port
+ case 0x00: {
+ nsCOMPtr<nsIRunnable> r(
+ new ReceiveRunnable(mControlInputPort.id(), msg));
+ OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
+ break;
+ }
+ // Cause control test ports to connect
+ case 0x01: {
+ nsCOMPtr<nsIRunnable> r1(
+ new AddPortRunnable(mStateTestInputPort));
+ OwnerThread()->Dispatch(r1, NS_DISPATCH_NORMAL);
+ break;
+ }
+ // Cause control test ports to disconnect
+ case 0x02: {
+ nsCOMPtr<nsIRunnable> r1(
+ new RemovePortRunnable(mStateTestInputPort));
+ OwnerThread()->Dispatch(r1, NS_DISPATCH_NORMAL);
+ break;
+ }
+ // Test for packet timing
+ case 0x03: {
+ // Append a few echo command packets in reverse timing order,
+ // should come out in correct order on other end.
+ nsTArray<MIDIMessage> newMsgs;
+ nsTArray<uint8_t> msg;
+ msg.AppendElement(0x90);
+ msg.AppendElement(0x00);
+ msg.AppendElement(0x00);
+ // PR_Now() returns nanosecods, and we need a double with
+ // fractional milliseconds.
+ TimeStamp currentTime = TimeStamp::Now();
+ for (int i = 0; i <= 5; ++i) {
+ // Insert messages with timestamps in reverse order, to make
+ // sure we're sorting correctly.
+ newMsgs.AppendElement(MIDIMessage(
+ msg, currentTime - TimeDuration::FromMilliseconds(i * 2)));
+ }
+ nsCOMPtr<nsIRunnable> r(
+ new QueueMessagesRunnable(aPortId, newMsgs));
+ OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
+ break;
+ }
+ // Causes the next refresh to add new ports to the list
+ case 0x04: {
+ mDoRefresh = true;
+ break;
+ }
+ default:
+ NS_WARNING("Unknown Test MIDI message received!");
+ }
+ break;
+ // Sysex tests
+ case 0xF0:
+ switch (msg.data()[1]) {
+ // Echo data/timestamp back through output port
+ case 0x00: {
+ nsCOMPtr<nsIRunnable> r(
+ new ReceiveRunnable(mControlInputPort.id(), msg));
+ OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
+ break;
+ }
+ // Test for system real time messages in the middle of sysex
+ // messages.
+ case 0x01: {
+ nsTArray<uint8_t> msgs;
+ const uint8_t msg[] = {0xF0, 0x01, 0xFA, 0x02, 0x03,
+ 0x04, 0xF8, 0x05, 0xF7};
+ // Can't use AppendElements on an array here, so just do range
+ // based loading.
+ for (const auto& s : msg) {
+ msgs.AppendElement(s);
+ }
+ nsTArray<MIDIMessage> newMsgs;
+ MIDIUtils::ParseMessages(msgs, TimeStamp::Now(), newMsgs);
+ nsCOMPtr<nsIRunnable> r(
+ new ReceiveRunnable(mControlInputPort.id(), newMsgs));
+ OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
+ break;
+ }
+ default:
+ NS_WARNING("Unknown Test Sysex MIDI message received!");
+ }
+ break;
+ }
+ }
+ }
+}