diff options
Diffstat (limited to '')
-rw-r--r-- | dom/midi/TestMIDIPlatformService.cpp | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/dom/midi/TestMIDIPlatformService.cpp b/dom/midi/TestMIDIPlatformService.cpp new file mode 100644 index 0000000000..22cd57a2e4 --- /dev/null +++ b/dom/midi/TestMIDIPlatformService.cpp @@ -0,0 +1,244 @@ +/* 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() { + // 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() { + AssertIsOnBackgroundThread(); + MIDIPlatformService::Get()->QueueMessages(mPortID, mMsgs); + } + + private: + nsString mPortID; + nsTArray<MIDIMessage> mMsgs; +}; + +TestMIDIPlatformService::TestMIDIPlatformService() + : mBackgroundThread(NS_GetCurrentThread()), + 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)), + mIsInitialized(false) { + AssertIsOnBackgroundThread(); +} + +TestMIDIPlatformService::~TestMIDIPlatformService() { + AssertIsOnBackgroundThread(); +} + +void TestMIDIPlatformService::Init() { + AssertIsOnBackgroundThread(); + + 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); + nsCOMPtr<nsIRunnable> r(new SendPortListRunnable()); + + // Start the IO Thread. + NS_DispatchToCurrentThread(r); +} + +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->MIDIPortInterface::Id(), + aPort->DeviceState(), s)); + NS_DispatchToCurrentThread(r); +} + +void TestMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) { + 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->MIDIPortInterface::Id(), aPort->DeviceState(), + MIDIPortConnectionState::Closed)); + NS_DispatchToCurrentThread(r); + } +} + +void TestMIDIPlatformService::Stop() { AssertIsOnBackgroundThread(); } + +void TestMIDIPlatformService::ScheduleSend(const nsAString& aPortId) { + nsCOMPtr<nsIRunnable> r(new ProcessMessagesRunnable(aPortId)); + NS_DispatchToCurrentThread(r); +} + +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)); + mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL); + break; + } + // Cause control test ports to connect + case 0x01: { + nsCOMPtr<nsIRunnable> r1( + new AddPortRunnable(mStateTestInputPort)); + mBackgroundThread->Dispatch(r1, NS_DISPATCH_NORMAL); + break; + } + // Cause control test ports to disconnect + case 0x02: { + nsCOMPtr<nsIRunnable> r1( + new RemovePortRunnable(mStateTestInputPort)); + mBackgroundThread->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)); + mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL); + 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)); + mBackgroundThread->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, 0xF8, 0x02, 0x03, + 0x04, 0xF9, 0x05, 0xF7}; + // Can't use AppendElements on an array here, so just do range + // based loading. + for (auto& s : msg) { + msgs.AppendElement(s); + } + nsTArray<MIDIMessage> newMsgs; + MIDIUtils::ParseMessages(msgs, TimeStamp::Now(), newMsgs); + nsCOMPtr<nsIRunnable> r( + new ReceiveRunnable(mControlInputPort.id(), newMsgs)); + mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL); + break; + } + default: + NS_WARNING("Unknown Test Sysex MIDI message received!"); + } + break; + } + } + } +} |