Commit 97a5174c authored by Gabriele Svelto's avatar Gabriele Svelto
Browse files

Bug 1201598 - Add a midir-based implementation for WebMIDI r=padenot

parent 2793748f
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -2015,6 +2015,7 @@ dependencies = [
 "mapped_hyph",
 "mdns_service",
 "midir",
 "midir_impl",
 "mozurl",
 "mp4parse_capi",
 "neqo_glue",
@@ -3074,6 +3075,16 @@ dependencies = [
 "winapi",
]

[[package]]
name = "midir_impl"
version = "0.1.0"
dependencies = [
 "midir",
 "nsstring",
 "thin-vec",
 "uuid",
]

[[package]]
name = "mime"
version = "0.3.16"
+1 −0
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ chardetng = { git = "https://github.com/hsivonen/chardetng", rev="302c995f91f44c
chardetng_c = { git = "https://github.com/hsivonen/chardetng_c", rev="ed8a4c6f900a90d4dbc1d64b856e61490a1c3570" }
libudev-sys = { path = "dom/webauthn/libudev-sys" }
packed_simd = { git = "https://github.com/hsivonen/packed_simd", rev="8b4bd7d8229660a749dbe419a57ea01df9de5453" }
midir = { git = "https://github.com/mozilla/midir.git", rev = "dc87afbd4361ae5ec192e1fab0a6409dd13d4011" }
minidump_writer_linux = { git = "https://github.com/msirringhaus/minidump_writer_linux.git", rev = "029ac0d54b237f27dc7d8d4e51bc0fb076e5e852" }

# Patch mio 0.6 to use winapi 0.3 and miow 0.3, getting rid of winapi 0.2.
+11 −4
Original line number Diff line number Diff line
@@ -7,6 +7,9 @@
#include "MIDIPlatformService.h"
#include "MIDIMessageQueue.h"
#include "TestMIDIPlatformService.h"
#ifndef MOZ_WIDGET_ANDROID
#  include "midirMIDIPlatformService.h"
#endif  // MOZ_WIDGET_ANDROID
#include "mozilla/ErrorResult.h"
#include "mozilla/StaticPrefs_midi.h"
#include "mozilla/StaticPtr.h"
@@ -179,10 +182,14 @@ MIDIPlatformService* MIDIPlatformService::Get() {
  MOZ_ASSERT(XRE_IsParentProcess());
  ::mozilla::ipc::AssertIsOnBackgroundThread();
  if (!IsRunning()) {
    // Uncomment once we have an actual platform library to test.
    //
    // if (StaticPrefs::midi_testing()) {
    if (StaticPrefs::midi_testing()) {
      gMIDIPlatformService = new TestMIDIPlatformService();
    }
#ifndef MOZ_WIDGET_ANDROID
    else {
      gMIDIPlatformService = new midirMIDIPlatformService();
    }
#endif  // MOZ_WIDGET_ANDROID
    gMIDIPlatformService->Init();
  }
  return gMIDIPlatformService;
+152 −0
Original line number Diff line number Diff line
/* 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/ipc/BackgroundParent.h"
#include "mozilla/Unused.h"
#include "nsIThread.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)
      : MIDIBackgroundRunnable("SendRunnable"), mPortID(aPortID) {}
  ~SendRunnable() = default;
  virtual void RunInternal() {
    AssertIsOnBackgroundThread();
    midirMIDIPlatformService* srv =
        static_cast<midirMIDIPlatformService*>(MIDIPlatformService::Get());
    srv->SendMessages(mPortID);
  }

 private:
  nsString mPortID;
  nsTArray<MIDIMessage> mMsgs;
};

// static
StaticMutex midirMIDIPlatformService::gBackgroundThreadMutex;

// static
nsCOMPtr<nsIThread> midirMIDIPlatformService::gBackgroundThread;

midirMIDIPlatformService::midirMIDIPlatformService()
    : mIsInitialized(false), mImplementation(nullptr) {
  StaticMutexAutoLock lock(gBackgroundThreadMutex);
  gBackgroundThread = NS_GetCurrentThread();
}

midirMIDIPlatformService::~midirMIDIPlatformService() {
  midir_impl_shutdown(mImplementation);
  StaticMutexAutoLock lock(gBackgroundThreadMutex);
  gBackgroundThread = 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);
}

void midirMIDIPlatformService::Init() {
  if (mIsInitialized) {
    return;
  }

  mImplementation = midir_impl_init();

  if (mImplementation) {
    mIsInitialized = true;
    midir_impl_enum_ports(mImplementation, AddPort);
    MIDIPlatformService::Get()->SendPortList();
  }
}

// static
void midirMIDIPlatformService::CheckAndReceive(const nsString* aId,
                                               const uint8_t* aData,
                                               size_t aLength,
                                               const GeckoTimeStamp* aTimeStamp,
                                               uint64_t aMicros) {
  nsTArray<uint8_t> data;
  for (size_t i = 0; i < aLength; i++) {
    data.AppendElement(aData[i]);
  }
  const TimeStamp* openTime = reinterpret_cast<const TimeStamp*>(aTimeStamp);
  TimeStamp timestamp =
      *openTime + TimeDuration::FromMicroseconds(static_cast<double>(aMicros));
  MIDIMessage message(data, timestamp);
  nsTArray<MIDIMessage> messages;
  messages.AppendElement(message);

  nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(*aId, messages));
  StaticMutexAutoLock lock(gBackgroundThreadMutex);
  if (gBackgroundThread) {
    gBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
  }
}

void midirMIDIPlatformService::Open(MIDIPortParent* aPort) {
  MOZ_ASSERT(aPort);
  nsString id = aPort->MIDIPortInterface::Id();
  TimeStamp openTimeStamp = TimeStamp::Now();
  if (midir_impl_open_port(mImplementation, &id,
                           reinterpret_cast<GeckoTimeStamp*>(&openTimeStamp),
                           CheckAndReceive)) {
    nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
        aPort->MIDIPortInterface::Id(), aPort->DeviceState(),
        MIDIPortConnectionState::Open));
    NS_DispatchToCurrentThread(r);
  }
}

void midirMIDIPlatformService::Stop() {
  // Nothing to do here AFAIK
}

void midirMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
  nsCOMPtr<nsIRunnable> r(new SendRunnable(aPortId));
  StaticMutexAutoLock lock(gBackgroundThreadMutex);
  if (gBackgroundThread) {
    gBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
  }
}

void midirMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
  MOZ_ASSERT(aPort);
  nsString id = aPort->MIDIPortInterface::Id();
  if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
    midir_impl_close_port(mImplementation, &id);
    nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
        aPort->MIDIPortInterface::Id(), aPort->DeviceState(),
        MIDIPortConnectionState::Closed));
    NS_DispatchToCurrentThread(r);
  }
}

void midirMIDIPlatformService::SendMessages(const nsAString& aPortId) {
  nsTArray<MIDIMessage> messages;
  GetMessages(aPortId, messages);
  for (const auto& message : messages) {
    midir_impl_send(mImplementation, &aPortId, &message.data());
  }
}
+59 −0
Original line number Diff line number Diff line
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */

#ifndef mozilla_dom_midirMIDIPlatformService_h
#define mozilla_dom_midirMIDIPlatformService_h

#include "mozilla/StaticMutex.h"
#include "mozilla/dom/MIDIPlatformService.h"
#include "mozilla/dom/MIDITypes.h"
#include "mozilla/dom/midi/midir_impl_ffi_generated.h"

class nsIThread;
struct MidirWrapper;

namespace mozilla::dom {

class MIDIPortInterface;

/**
 * Platform service implementation using the midir crate.
 */
class midirMIDIPlatformService : public MIDIPlatformService {
 public:
  midirMIDIPlatformService();
  virtual void Init() override;
  virtual void Open(MIDIPortParent* aPort) override;
  virtual void Stop() override;
  virtual void ScheduleSend(const nsAString& aPort) override;
  virtual void ScheduleClose(MIDIPortParent* aPort) override;

  void SendMessages(const nsAString& aPort);

 private:
  virtual ~midirMIDIPlatformService();

  static void AddPort(const nsString* aId, const nsString* aName, bool aInput);
  static void CheckAndReceive(const nsString* aId, const uint8_t* aData,
                              size_t aLength, const GeckoTimeStamp* aTimeStamp,
                              uint64_t aMicros);

  // True if server has been brought up already.
  bool mIsInitialized;

  // Wrapper around the midir Rust implementation.
  MidirWrapper* mImplementation;

  // midir has its own internal threads and we can't execute jobs directly on
  // them, instead we forward them to the background thread the service was
  // created in.
  static StaticMutex gBackgroundThreadMutex;
  static nsCOMPtr<nsIThread> gBackgroundThread;
};

}  // namespace mozilla::dom

#endif  // mozilla_dom_midirMIDIPlatformService_h
Loading