tor-browser

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

midirMIDIPlatformService.cpp (6700B)


      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 "midirMIDIPlatformService.h"
      6 
      7 #include "MIDILog.h"
      8 #include "mozilla/Logging.h"
      9 #include "mozilla/StaticMutex.h"
     10 #include "mozilla/TimeStamp.h"
     11 #include "mozilla/dom/MIDIPlatformRunnables.h"
     12 #include "mozilla/dom/MIDIPort.h"
     13 #include "mozilla/dom/MIDIPortInterface.h"
     14 #include "mozilla/dom/MIDIPortParent.h"
     15 #include "mozilla/dom/MIDITypes.h"
     16 #include "mozilla/dom/MIDIUtils.h"
     17 #include "mozilla/dom/midi/midir_impl_ffi_generated.h"
     18 #include "mozilla/ipc/BackgroundParent.h"
     19 #include "nsIThread.h"
     20 
     21 using namespace mozilla;
     22 using namespace mozilla::dom;
     23 using namespace mozilla::ipc;
     24 
     25 static_assert(sizeof(TimeStamp) == sizeof(GeckoTimeStamp));
     26 
     27 /**
     28 * Runnable used for to send messages asynchronously on the I/O thread.
     29 */
     30 class SendRunnable : public MIDIBackgroundRunnable {
     31 public:
     32  explicit SendRunnable(const nsAString& aPortID, const MIDIMessage& aMessage)
     33      : MIDIBackgroundRunnable("SendRunnable"),
     34        mPortID(aPortID),
     35        mMessage(aMessage) {}
     36  ~SendRunnable() = default;
     37  virtual void RunInternal() {
     38    MIDIPlatformService::AssertThread();
     39    if (!MIDIPlatformService::IsRunning()) {
     40      // Some send operations might outlive the service, bail out and do nothing
     41      return;
     42    }
     43    midirMIDIPlatformService* srv =
     44        static_cast<midirMIDIPlatformService*>(MIDIPlatformService::Get());
     45    srv->SendMessage(mPortID, mMessage);
     46  }
     47 
     48 private:
     49  nsString mPortID;
     50  MIDIMessage mMessage;
     51 };
     52 
     53 // static
     54 StaticMutex midirMIDIPlatformService::gOwnerThreadMutex;
     55 
     56 // static
     57 MOZ_RUNINIT nsCOMPtr<nsISerialEventTarget>
     58    midirMIDIPlatformService::gOwnerThread;
     59 
     60 midirMIDIPlatformService::midirMIDIPlatformService()
     61    : mImplementation(nullptr) {
     62  StaticMutexAutoLock lock(gOwnerThreadMutex);
     63  gOwnerThread = OwnerThread();
     64 }
     65 
     66 midirMIDIPlatformService::~midirMIDIPlatformService() {
     67  LOG("midir_impl_shutdown");
     68  if (mImplementation) {
     69    midir_impl_shutdown(mImplementation);
     70  }
     71  StaticMutexAutoLock lock(gOwnerThreadMutex);
     72  gOwnerThread = nullptr;
     73 }
     74 
     75 // static
     76 void midirMIDIPlatformService::AddPort(const nsString* aId,
     77                                       const nsString* aName, bool aInput) {
     78  MIDIPortType type = aInput ? MIDIPortType::Input : MIDIPortType::Output;
     79  MIDIPortInfo port(*aId, *aName, u""_ns, u""_ns, static_cast<uint32_t>(type));
     80  MIDIPlatformService::Get()->AddPortInfo(port);
     81 }
     82 
     83 // static
     84 void midirMIDIPlatformService::RemovePort(const nsString* aId,
     85                                          const nsString* aName, bool aInput) {
     86  MIDIPortType type = aInput ? MIDIPortType::Input : MIDIPortType::Output;
     87  MIDIPortInfo port(*aId, *aName, u""_ns, u""_ns, static_cast<uint32_t>(type));
     88  MIDIPlatformService::Get()->RemovePortInfo(port);
     89 }
     90 
     91 void midirMIDIPlatformService::Init() {
     92  if (mImplementation) {
     93    return;
     94  }
     95 
     96  mImplementation = midir_impl_init(AddPort);
     97 
     98  if (mImplementation) {
     99    MIDIPlatformService::Get()->SendPortList();
    100  } else {
    101    LOG("midir_impl_init failure");
    102  }
    103 }
    104 
    105 // static
    106 void midirMIDIPlatformService::CheckAndReceive(const nsString* aId,
    107                                               const uint8_t* aData,
    108                                               size_t aLength,
    109                                               const GeckoTimeStamp* aTimeStamp,
    110                                               uint64_t aMicros) {
    111  nsTArray<uint8_t> data;
    112  data.AppendElements(aData, aLength);
    113  const TimeStamp* openTime = reinterpret_cast<const TimeStamp*>(aTimeStamp);
    114  TimeStamp timestamp =
    115      *openTime + TimeDuration::FromMicroseconds(static_cast<double>(aMicros));
    116  MIDIMessage message(data, timestamp);
    117  LogMIDIMessage(message, *aId, MIDIPortType::Input);
    118  nsTArray<MIDIMessage> messages;
    119  messages.AppendElement(message);
    120 
    121  nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(*aId, messages));
    122  StaticMutexAutoLock lock(gOwnerThreadMutex);
    123  if (gOwnerThread) {
    124    gOwnerThread->Dispatch(r, NS_DISPATCH_NORMAL);
    125  }
    126 }
    127 
    128 void midirMIDIPlatformService::Refresh() {
    129  midir_impl_refresh(mImplementation, AddPort, RemovePort);
    130 }
    131 
    132 void midirMIDIPlatformService::Open(MIDIPortParent* aPort) {
    133  AssertThread();
    134  MOZ_ASSERT(aPort);
    135  nsString id = aPort->MIDIPortInterface::Id();
    136  TimeStamp openTimeStamp = TimeStamp::Now();
    137  if (midir_impl_open_port(mImplementation, &id,
    138                           reinterpret_cast<GeckoTimeStamp*>(&openTimeStamp),
    139                           CheckAndReceive)) {
    140    LOG("MIDI port open: %s at t=%lf", NS_ConvertUTF16toUTF8(id).get(),
    141        (openTimeStamp - TimeStamp::ProcessCreation()).ToSeconds());
    142    nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
    143        aPort, aPort->DeviceState(), MIDIPortConnectionState::Open));
    144    OwnerThread()->Dispatch(r.forget());
    145  } else {
    146    LOG("MIDI port open failed: %s", NS_ConvertUTF16toUTF8(id).get());
    147  }
    148 }
    149 
    150 void midirMIDIPlatformService::Stop() {
    151  // Nothing to do here AFAIK
    152 }
    153 
    154 void midirMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
    155  AssertThread();
    156  LOG("MIDI port schedule send %s", NS_ConvertUTF16toUTF8(aPortId).get());
    157  nsTArray<MIDIMessage> messages;
    158  GetMessages(aPortId, messages);
    159  TimeStamp now = TimeStamp::Now();
    160  for (const auto& message : messages) {
    161    if (message.timestamp().IsNull()) {
    162      SendMessage(aPortId, message);
    163    } else {
    164      double delay = (message.timestamp() - now).ToMilliseconds();
    165      if (delay < 1.0) {
    166        SendMessage(aPortId, message);
    167      } else {
    168        nsCOMPtr<nsIRunnable> r(new SendRunnable(aPortId, message));
    169        OwnerThread()->DelayedDispatch(r.forget(),
    170                                       static_cast<uint32_t>(delay));
    171      }
    172    }
    173  }
    174 }
    175 
    176 void midirMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
    177  AssertThread();
    178  MOZ_ASSERT(aPort);
    179  nsString id = aPort->MIDIPortInterface::Id();
    180  LOG("MIDI port schedule close %s", NS_ConvertUTF16toUTF8(id).get());
    181  if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
    182    midir_impl_close_port(mImplementation, &id);
    183    nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
    184        aPort, aPort->DeviceState(), MIDIPortConnectionState::Closed));
    185    OwnerThread()->Dispatch(r.forget());
    186  }
    187 }
    188 
    189 void midirMIDIPlatformService::SendMessage(const nsAString& aPortId,
    190                                           const MIDIMessage& aMessage) {
    191  LOG("MIDI send message on %s", NS_ConvertUTF16toUTF8(aPortId).get());
    192  LogMIDIMessage(aMessage, aPortId, MIDIPortType::Output);
    193  midir_impl_send(mImplementation, &aPortId, &aMessage.data());
    194 }