Skip to content

Commit

Permalink
Bug 1201598 - Add a midir-based implementation for WebMIDI r=padenot
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielesvelto committed Dec 21, 2021
1 parent 2793748 commit 97a5174
Show file tree
Hide file tree
Showing 14 changed files with 633 additions and 6 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
15 changes: 11 additions & 4 deletions dom/midi/MIDIPlatformService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()) {
gMIDIPlatformService = new TestMIDIPlatformService();
if (StaticPrefs::midi_testing()) {
gMIDIPlatformService = new TestMIDIPlatformService();
}
#ifndef MOZ_WIDGET_ANDROID
else {
gMIDIPlatformService = new midirMIDIPlatformService();
}
#endif // MOZ_WIDGET_ANDROID
gMIDIPlatformService->Init();
}
return gMIDIPlatformService;
Expand Down
152 changes: 152 additions & 0 deletions dom/midi/midirMIDIPlatformService.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/* 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 changes: 59 additions & 0 deletions dom/midi/midirMIDIPlatformService.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* -*- 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
13 changes: 13 additions & 0 deletions dom/midi/midir_impl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "midir_impl"
version = "0.1.0"
authors = ["Gabriele Svelto"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
midir = "0.7.0"
nsstring = { path = "../../../xpcom/rust/nsstring/" }
uuid = { version = "0.8", features = ["v4"] }
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
18 changes: 18 additions & 0 deletions dom/midi/midir_impl/cbindgen.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
header = """/* 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/. */"""
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */
"""
include_version = true
braces = "SameLine"
line_length = 100
tab_width = 2
language = "C++"
include_guard = "midir_impl_ffi_generated_h"
includes = ["nsStringFwd.h", "nsTArrayForwardDeclare.h"]

[defines]
"target_os = windows" = "XP_WIN"

[export.rename]
"ThinVec" = "nsTArray"
17 changes: 17 additions & 0 deletions dom/midi/midir_impl/moz.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.

if CONFIG["COMPILE_ENVIRONMENT"]:
# This tells mach to run cbindgen and that this header-file should be created
CbindgenHeader(
"midir_impl_ffi_generated.h",
inputs=["/dom/midi/midir_impl"],
)

# This tells mach to copy that generated file to obj/dist/includes/mozilla/dom/midi
EXPORTS.mozilla.dom.midi += [
"!midir_impl_ffi_generated.h",
]
Loading

0 comments on commit 97a5174

Please sign in to comment.