tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestMIDIPlatformService.cpp (9725B)


      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/. */
      4 
      5 #include "TestMIDIPlatformService.h"
      6 
      7 #include "mozilla/dom/MIDIPlatformRunnables.h"
      8 #include "mozilla/dom/MIDIPort.h"
      9 #include "mozilla/dom/MIDIPortInterface.h"
     10 #include "mozilla/dom/MIDIPortParent.h"
     11 #include "mozilla/dom/MIDITypes.h"
     12 #include "mozilla/dom/MIDIUtils.h"
     13 #include "mozilla/ipc/BackgroundParent.h"
     14 #include "nsIThread.h"
     15 
     16 using namespace mozilla;
     17 using namespace mozilla::dom;
     18 using namespace mozilla::ipc;
     19 
     20 /**
     21 * Runnable used for making sure ProcessMessages only happens on the IO thread.
     22 *
     23 */
     24 class ProcessMessagesRunnable : public mozilla::Runnable {
     25 public:
     26  explicit ProcessMessagesRunnable(const nsAString& aPortID)
     27      : Runnable("ProcessMessagesRunnable"), mPortID(aPortID) {}
     28  ~ProcessMessagesRunnable() = default;
     29  NS_IMETHOD Run() override {
     30    // If service is no longer running, just exist without processing.
     31    if (!MIDIPlatformService::IsRunning()) {
     32      return NS_OK;
     33    }
     34    TestMIDIPlatformService* srv =
     35        static_cast<TestMIDIPlatformService*>(MIDIPlatformService::Get());
     36    srv->ProcessMessages(mPortID);
     37    return NS_OK;
     38  }
     39 
     40 private:
     41  nsString mPortID;
     42 };
     43 
     44 /**
     45 * Runnable used for allowing IO thread to queue more messages for processing,
     46 * since it can't access the service object directly.
     47 *
     48 */
     49 class QueueMessagesRunnable : public MIDIBackgroundRunnable {
     50 public:
     51  QueueMessagesRunnable(const nsAString& aPortID,
     52                        const nsTArray<MIDIMessage>& aMsgs)
     53      : MIDIBackgroundRunnable("QueueMessagesRunnable"),
     54        mPortID(aPortID),
     55        mMsgs(aMsgs.Clone()) {}
     56  ~QueueMessagesRunnable() = default;
     57  virtual void RunInternal() {
     58    MIDIPlatformService::AssertThread();
     59    MIDIPlatformService::Get()->QueueMessages(mPortID, mMsgs);
     60  }
     61 
     62 private:
     63  nsString mPortID;
     64  nsTArray<MIDIMessage> mMsgs;
     65 };
     66 
     67 TestMIDIPlatformService::TestMIDIPlatformService()
     68    : mControlInputPort(u"b744eebe-f7d8-499b-872b-958f63c8f522"_ns,
     69                        u"Test Control MIDI Device Input Port"_ns,
     70                        u"Test Manufacturer"_ns, u"1.0.0"_ns,
     71                        static_cast<uint32_t>(MIDIPortType::Input)),
     72      mControlOutputPort(u"ab8e7fe8-c4de-436a-a960-30898a7c9a3d"_ns,
     73                         u"Test Control MIDI Device Output Port"_ns,
     74                         u"Test Manufacturer"_ns, u"1.0.0"_ns,
     75                         static_cast<uint32_t>(MIDIPortType::Output)),
     76      mStateTestInputPort(u"a9329677-8588-4460-a091-9d4a7f629a48"_ns,
     77                          u"Test State MIDI Device Input Port"_ns,
     78                          u"Test Manufacturer"_ns, u"1.0.0"_ns,
     79                          static_cast<uint32_t>(MIDIPortType::Input)),
     80      mStateTestOutputPort(u"478fa225-b5fc-4fa6-a543-d32d9cb651e7"_ns,
     81                           u"Test State MIDI Device Output Port"_ns,
     82                           u"Test Manufacturer"_ns, u"1.0.0"_ns,
     83                           static_cast<uint32_t>(MIDIPortType::Output)),
     84      mAlwaysClosedTestOutputPort(u"f87d0c76-3c68-49a9-a44f-700f1125c07a"_ns,
     85                                  u"Always Closed MIDI Device Output Port"_ns,
     86                                  u"Test Manufacturer"_ns, u"1.0.0"_ns,
     87                                  static_cast<uint32_t>(MIDIPortType::Output)),
     88      mDoRefresh(false),
     89      mIsInitialized(false) {
     90  MIDIPlatformService::AssertThread();
     91 }
     92 
     93 TestMIDIPlatformService::~TestMIDIPlatformService() {
     94  MIDIPlatformService::AssertThread();
     95 }
     96 
     97 void TestMIDIPlatformService::Init() {
     98  MIDIPlatformService::AssertThread();
     99 
    100  if (mIsInitialized) {
    101    return;
    102  }
    103  mIsInitialized = true;
    104 
    105  // Treat all of our special ports as always connected. When the service comes
    106  // up, prepopulate the port list with them.
    107  MIDIPlatformService::Get()->AddPortInfo(mControlInputPort);
    108  MIDIPlatformService::Get()->AddPortInfo(mControlOutputPort);
    109  MIDIPlatformService::Get()->AddPortInfo(mAlwaysClosedTestOutputPort);
    110  MIDIPlatformService::Get()->AddPortInfo(mStateTestOutputPort);
    111  nsCOMPtr<nsIRunnable> r(new SendPortListRunnable());
    112 
    113  // Start the IO Thread.
    114  OwnerThread()->Dispatch(r.forget());
    115 }
    116 
    117 void TestMIDIPlatformService::Refresh() {
    118  if (mDoRefresh) {
    119    AddPortInfo(mStateTestInputPort);
    120    mDoRefresh = false;
    121  }
    122 }
    123 
    124 void TestMIDIPlatformService::Open(MIDIPortParent* aPort) {
    125  MOZ_ASSERT(aPort);
    126  MIDIPortConnectionState s = MIDIPortConnectionState::Open;
    127  if (aPort->MIDIPortInterface::Id() == mAlwaysClosedTestOutputPort.id()) {
    128    // If it's the always closed testing port, act like it's already opened
    129    // exclusively elsewhere.
    130    s = MIDIPortConnectionState::Closed;
    131  }
    132  // Connection events are just simulated on the background thread, no need to
    133  // push to IO thread.
    134  nsCOMPtr<nsIRunnable> r(
    135      new SetStatusRunnable(aPort, aPort->DeviceState(), s));
    136  OwnerThread()->Dispatch(r.forget());
    137 }
    138 
    139 void TestMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
    140  AssertThread();
    141  MOZ_ASSERT(aPort);
    142  if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
    143    // Connection events are just simulated on the background thread, no need to
    144    // push to IO thread.
    145    nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
    146        aPort, aPort->DeviceState(), MIDIPortConnectionState::Closed));
    147    OwnerThread()->Dispatch(r.forget());
    148  }
    149 }
    150 
    151 void TestMIDIPlatformService::Stop() { MIDIPlatformService::AssertThread(); }
    152 
    153 void TestMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
    154  AssertThread();
    155  nsCOMPtr<nsIRunnable> r(new ProcessMessagesRunnable(aPortId));
    156  OwnerThread()->Dispatch(r.forget());
    157 }
    158 
    159 void TestMIDIPlatformService::ProcessMessages(const nsAString& aPortId) {
    160  nsTArray<MIDIMessage> msgs;
    161  GetMessagesBefore(aPortId, TimeStamp::Now(), msgs);
    162 
    163  for (MIDIMessage msg : msgs) {
    164    // receiving message from test control port
    165    if (aPortId == mControlOutputPort.id()) {
    166      switch (msg.data()[0]) {
    167        // Hit a note, get a test!
    168        case 0x90:
    169          switch (msg.data()[1]) {
    170            // Echo data/timestamp back through output port
    171            case 0x00: {
    172              nsCOMPtr<nsIRunnable> r(
    173                  new ReceiveRunnable(mControlInputPort.id(), msg));
    174              OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
    175              break;
    176            }
    177            // Cause control test ports to connect
    178            case 0x01: {
    179              nsCOMPtr<nsIRunnable> r1(
    180                  new AddPortRunnable(mStateTestInputPort));
    181              OwnerThread()->Dispatch(r1, NS_DISPATCH_NORMAL);
    182              break;
    183            }
    184            // Cause control test ports to disconnect
    185            case 0x02: {
    186              nsCOMPtr<nsIRunnable> r1(
    187                  new RemovePortRunnable(mStateTestInputPort));
    188              OwnerThread()->Dispatch(r1, NS_DISPATCH_NORMAL);
    189              break;
    190            }
    191            // Test for packet timing
    192            case 0x03: {
    193              // Append a few echo command packets in reverse timing order,
    194              // should come out in correct order on other end.
    195              nsTArray<MIDIMessage> newMsgs;
    196              nsTArray<uint8_t> msg;
    197              msg.AppendElement(0x90);
    198              msg.AppendElement(0x00);
    199              msg.AppendElement(0x00);
    200              // PR_Now() returns nanosecods, and we need a double with
    201              // fractional milliseconds.
    202              TimeStamp currentTime = TimeStamp::Now();
    203              for (int i = 0; i <= 5; ++i) {
    204                // Insert messages with timestamps in reverse order, to make
    205                // sure we're sorting correctly.
    206                newMsgs.AppendElement(MIDIMessage(
    207                    msg, currentTime - TimeDuration::FromMilliseconds(i * 2)));
    208              }
    209              nsCOMPtr<nsIRunnable> r(
    210                  new QueueMessagesRunnable(aPortId, newMsgs));
    211              OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
    212              break;
    213            }
    214            // Causes the next refresh to add new ports to the list
    215            case 0x04: {
    216              mDoRefresh = true;
    217              break;
    218            }
    219            default:
    220              NS_WARNING("Unknown Test MIDI message received!");
    221          }
    222          break;
    223          // Sysex tests
    224        case 0xF0:
    225          switch (msg.data()[1]) {
    226              // Echo data/timestamp back through output port
    227            case 0x00: {
    228              nsCOMPtr<nsIRunnable> r(
    229                  new ReceiveRunnable(mControlInputPort.id(), msg));
    230              OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
    231              break;
    232            }
    233            // Test for system real time messages in the middle of sysex
    234            // messages.
    235            case 0x01: {
    236              nsTArray<uint8_t> msgs;
    237              const uint8_t msg[] = {0xF0, 0x01, 0xFA, 0x02, 0x03,
    238                                     0x04, 0xF8, 0x05, 0xF7};
    239              // Can't use AppendElements on an array here, so just do range
    240              // based loading.
    241              for (const auto& s : msg) {
    242                msgs.AppendElement(s);
    243              }
    244              nsTArray<MIDIMessage> newMsgs;
    245              MIDIUtils::ParseMessages(msgs, TimeStamp::Now(), newMsgs);
    246              nsCOMPtr<nsIRunnable> r(
    247                  new ReceiveRunnable(mControlInputPort.id(), newMsgs));
    248              OwnerThread()->Dispatch(r, NS_DISPATCH_NORMAL);
    249              break;
    250            }
    251            default:
    252              NS_WARNING("Unknown Test Sysex MIDI message received!");
    253          }
    254          break;
    255      }
    256    }
    257  }
    258 }