зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1201598 - Add a midir-based implementation for WebMIDI r=padenot
Differential Revision: https://phabricator.services.mozilla.com/D131351
This commit is contained in:
Родитель
2793748f11
Коммит
97a5174c27
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()) {
|
||||
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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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"] }
|
|
@ -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"
|
|
@ -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",
|
||||
]
|
|
@ -0,0 +1,334 @@
|
|||
extern crate thin_vec;
|
||||
|
||||
use midir::{
|
||||
InitError, MidiInput, MidiInputConnection, MidiInputPort, MidiOutput, MidiOutputConnection,
|
||||
MidiOutputPort,
|
||||
};
|
||||
use nsstring::{nsAString, nsString};
|
||||
use std::boxed::Box;
|
||||
use std::ptr;
|
||||
use thin_vec::ThinVec;
|
||||
use uuid::Uuid;
|
||||
|
||||
/* 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/. */
|
||||
extern crate midir;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GeckoTimeStamp {
|
||||
gtc: u64,
|
||||
qpc: u64,
|
||||
|
||||
is_null: u8,
|
||||
has_qpc: u8,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GeckoTimeStamp {
|
||||
value: u64,
|
||||
}
|
||||
|
||||
enum MidiConnection {
|
||||
Input(MidiInputConnection<CallbackData>),
|
||||
Output(MidiOutputConnection),
|
||||
}
|
||||
|
||||
struct MidiConnectionWrapper {
|
||||
id: String,
|
||||
connection: MidiConnection,
|
||||
}
|
||||
|
||||
enum MidiPort {
|
||||
Input(MidiInputPort),
|
||||
Output(MidiOutputPort),
|
||||
}
|
||||
|
||||
struct MidiPortWrapper {
|
||||
id: String,
|
||||
name: String,
|
||||
port: MidiPort,
|
||||
}
|
||||
|
||||
pub struct MidirWrapper {
|
||||
ports: Vec<MidiPortWrapper>,
|
||||
connections: Vec<MidiConnectionWrapper>,
|
||||
}
|
||||
|
||||
struct CallbackData {
|
||||
nsid: nsString,
|
||||
open_timestamp: GeckoTimeStamp,
|
||||
}
|
||||
|
||||
impl MidirWrapper {
|
||||
fn open_port(
|
||||
self: &mut MidirWrapper,
|
||||
nsid: &nsString,
|
||||
timestamp: GeckoTimeStamp,
|
||||
callback: unsafe extern "C" fn(
|
||||
id: &nsString,
|
||||
data: *const u8,
|
||||
length: usize,
|
||||
timestamp: &GeckoTimeStamp,
|
||||
micros: u64,
|
||||
),
|
||||
) -> Result<(), ()> {
|
||||
let id = nsid.to_string();
|
||||
let ports = &self.ports;
|
||||
let connections = &mut self.connections;
|
||||
let port = ports.iter().find(|e| e.id.eq(&id));
|
||||
if let Some(port) = port {
|
||||
match &port.port {
|
||||
MidiPort::Input(port) => {
|
||||
let input = MidiInput::new("WebMIDI input").map_err(|_err| ())?;
|
||||
let data = CallbackData {
|
||||
nsid: nsid.clone(),
|
||||
open_timestamp: timestamp,
|
||||
};
|
||||
let connection = input
|
||||
.connect(
|
||||
port,
|
||||
"Input connection",
|
||||
move |stamp, message, data| unsafe {
|
||||
callback(
|
||||
&data.nsid,
|
||||
message.as_ptr(),
|
||||
message.len(),
|
||||
&data.open_timestamp,
|
||||
stamp,
|
||||
);
|
||||
},
|
||||
data,
|
||||
)
|
||||
.map_err(|_err| ())?;
|
||||
let connection_wrapper = MidiConnectionWrapper {
|
||||
id: id.clone(),
|
||||
connection: MidiConnection::Input(connection),
|
||||
};
|
||||
connections.push(connection_wrapper);
|
||||
return Ok(());
|
||||
}
|
||||
MidiPort::Output(port) => {
|
||||
let output = MidiOutput::new("WebMIDI output").map_err(|_err| ())?;
|
||||
let connection = output
|
||||
.connect(port, "Output connection")
|
||||
.map_err(|_err| ())?;
|
||||
let connection_wrapper = MidiConnectionWrapper {
|
||||
connection: MidiConnection::Output(connection),
|
||||
id: id.clone(),
|
||||
};
|
||||
connections.push(connection_wrapper);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(())
|
||||
}
|
||||
|
||||
fn close_port(self: &mut MidirWrapper, id: &str) {
|
||||
let connections = &mut self.connections;
|
||||
let index = connections.iter().position(|e| e.id.eq(id)).unwrap();
|
||||
let connection_wrapper = connections.remove(index);
|
||||
|
||||
match connection_wrapper.connection {
|
||||
MidiConnection::Input(connection) => {
|
||||
connection.close();
|
||||
}
|
||||
MidiConnection::Output(connection) => {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send(self: &mut MidirWrapper, id: &str, data: &[u8]) -> Result<(), ()> {
|
||||
let connections = &mut self.connections;
|
||||
let index = connections.iter().position(|e| e.id.eq(id)).unwrap();
|
||||
let connection_wrapper = connections.get_mut(index).unwrap();
|
||||
|
||||
match &mut connection_wrapper.connection {
|
||||
MidiConnection::Output(connection) => {
|
||||
connection.send(data).map_err(|_err| ())?;
|
||||
}
|
||||
_ => {
|
||||
panic!("Sending on an input port!");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MidirWrapper {
|
||||
fn new() -> Result<MidirWrapper, InitError> {
|
||||
let input = MidiInput::new("WebMIDI input")?;
|
||||
let output = MidiOutput::new("WebMIDI output")?;
|
||||
let mut ports: Vec<MidiPortWrapper> = Vec::new();
|
||||
collect_input_ports(&input, &mut ports);
|
||||
collect_output_ports(&output, &mut ports);
|
||||
let connections: Vec<MidiConnectionWrapper> = Vec::new();
|
||||
Ok(MidirWrapper { ports, connections })
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the C++ wrapper that will be used to talk with midir.
|
||||
///
|
||||
/// This function will be exposed to C++
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function deliberately leaks the wrapper because ownership is
|
||||
/// transfered to the C++ code. Use [midir_impl_shutdown()] to free it.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn midir_impl_init() -> *mut MidirWrapper {
|
||||
if let Ok(midir_impl) = MidirWrapper::new() {
|
||||
let midir_box = Box::new(midir_impl);
|
||||
// Leak the object as it will be owned by the C++ code from now on
|
||||
Box::leak(midir_box) as *mut _
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Shutdown midir and free the C++ wrapper.
|
||||
///
|
||||
/// This function will be exposed to C++
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `wrapper` must be the pointer returned by [midir_impl_init()]. After this
|
||||
/// has been called the wrapper object will be destoyed and cannot be accessed
|
||||
/// anymore.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn midir_impl_shutdown(wrapper: *mut MidirWrapper) {
|
||||
// The MidirImpl object will be automatically destroyed when the contents
|
||||
// of this box are automatically dropped at the end of the function
|
||||
let _midir_box = Box::from_raw(wrapper);
|
||||
}
|
||||
|
||||
/// Enumerate the available MIDI ports.
|
||||
///
|
||||
/// This function will be exposed to C++
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `wrapper` must be the pointer returned by [midir_impl_init()].
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn midir_impl_enum_ports(
|
||||
wrapper: *mut MidirWrapper,
|
||||
callback: unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool),
|
||||
) {
|
||||
iterate_ports(&(*wrapper).ports, callback);
|
||||
}
|
||||
|
||||
/// Open a MIDI port.
|
||||
///
|
||||
/// This function will be exposed to C++
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `wrapper` must be the pointer returned by [midir_impl_init()].
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn midir_impl_open_port(
|
||||
wrapper: *mut MidirWrapper,
|
||||
nsid: *mut nsString,
|
||||
timestamp: *mut GeckoTimeStamp,
|
||||
callback: unsafe extern "C" fn(
|
||||
id: &nsString,
|
||||
data: *const u8,
|
||||
length: usize,
|
||||
timestamp: &GeckoTimeStamp,
|
||||
micros: u64,
|
||||
),
|
||||
) -> bool {
|
||||
(*wrapper)
|
||||
.open_port(nsid.as_ref().unwrap(), *timestamp, callback)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Close a MIDI port.
|
||||
///
|
||||
/// This function will be exposed to C++
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `wrapper` must be the pointer returned by [midir_impl_init()].
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn midir_impl_close_port(wrapper: *mut MidirWrapper, id: *mut nsString) {
|
||||
(*wrapper).close_port(&(*id).to_string());
|
||||
}
|
||||
|
||||
/// Send a message over a MIDI output port.
|
||||
///
|
||||
/// This function will be exposed to C++
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `wrapper` must be the pointer returned by [midir_impl_init()].
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn midir_impl_send(
|
||||
wrapper: *mut MidirWrapper,
|
||||
id: *const nsAString,
|
||||
data: *const ThinVec<u8>,
|
||||
) -> bool {
|
||||
(*wrapper)
|
||||
.send(&(*id).to_string(), (*data).as_slice())
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
fn collect_input_ports(input: &MidiInput, wrappers: &mut Vec<MidiPortWrapper>) {
|
||||
let ports = input.ports();
|
||||
for port in ports {
|
||||
let id = Uuid::new_v4()
|
||||
.to_hyphenated()
|
||||
.encode_lower(&mut Uuid::encode_buffer())
|
||||
.to_owned();
|
||||
let name = input
|
||||
.port_name(&port)
|
||||
.unwrap_or_else(|_| "unknown input port".to_string());
|
||||
let port = MidiPortWrapper {
|
||||
id,
|
||||
name,
|
||||
port: MidiPort::Input(port),
|
||||
};
|
||||
wrappers.push(port);
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_output_ports(output: &MidiOutput, wrappers: &mut Vec<MidiPortWrapper>) {
|
||||
let ports = output.ports();
|
||||
for port in ports {
|
||||
let id = Uuid::new_v4()
|
||||
.to_hyphenated()
|
||||
.encode_lower(&mut Uuid::encode_buffer())
|
||||
.to_owned();
|
||||
let name = output
|
||||
.port_name(&port)
|
||||
.unwrap_or_else(|_| "unknown input port".to_string());
|
||||
let port = MidiPortWrapper {
|
||||
id,
|
||||
name,
|
||||
port: MidiPort::Output(port),
|
||||
};
|
||||
wrappers.push(port);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn iterate_ports(
|
||||
ports: &[MidiPortWrapper],
|
||||
callback: unsafe extern "C" fn(id: &nsString, name: &nsString, input: bool),
|
||||
) {
|
||||
for port in ports {
|
||||
let id = nsString::from(&port.id);
|
||||
let name = nsString::from(&port.name);
|
||||
let input = match port.port {
|
||||
MidiPort::Input(_) => true,
|
||||
MidiPort::Output(_) => false,
|
||||
};
|
||||
callback(&id, &name, input);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,9 @@
|
|||
# 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["OS_TARGET"] != "Android":
|
||||
DIRS += ["midir_impl"]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
"MIDITypes.ipdlh",
|
||||
"PMIDIManager.ipdl",
|
||||
|
@ -31,7 +34,7 @@ EXPORTS.mozilla.dom += [
|
|||
"MIDIUtils.h",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES = [
|
||||
UNIFIED_SOURCES += [
|
||||
"MIDIAccess.cpp",
|
||||
"MIDIAccessManager.cpp",
|
||||
"MIDIInput.cpp",
|
||||
|
@ -56,8 +59,15 @@ UNIFIED_SOURCES = [
|
|||
include("/ipc/chromium/chromium-config.mozbuild")
|
||||
|
||||
if CONFIG["OS_TARGET"] == "Linux":
|
||||
OS_LIBS += ["asound"] # Required by midir
|
||||
UNIFIED_SOURCES += ["AlsaCompatibility.cpp"]
|
||||
|
||||
|
||||
if CONFIG["OS_TARGET"] != "Android":
|
||||
UNIFIED_SOURCES += [
|
||||
"midirMIDIPlatformService.cpp",
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
LOCAL_INCLUDES += [
|
||||
"/dom/base",
|
||||
|
|
|
@ -1270,7 +1270,7 @@ cocoa)
|
|||
LDFLAGS="$LDFLAGS -framework Cocoa"
|
||||
# Use -Wl as a trick to avoid -framework and framework names from
|
||||
# being separated by AC_SUBST_LIST.
|
||||
TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreLocation -Wl,-framework,QuartzCore -Wl,-framework,Carbon -Wl,-framework,CoreAudio -Wl,-framework,CoreVideo -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,AddressBook -Wl,-framework,OpenGL -Wl,-framework,Security -Wl,-framework,ServiceManagement -Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -Wl,-framework,AppKit'
|
||||
TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreLocation -Wl,-framework,QuartzCore -Wl,-framework,Carbon -Wl,-framework,CoreAudio -Wl,-framework,CoreVideo -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,AddressBook -Wl,-framework,OpenGL -Wl,-framework,Security -Wl,-framework,ServiceManagement -Wl,-framework,CoreServices -Wl,-framework,ApplicationServices -Wl,-framework,AppKit -Wl,-framework,CoreMIDI'
|
||||
TK_CFLAGS=""
|
||||
CFLAGS="$CFLAGS $TK_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $TK_CFLAGS"
|
||||
|
|
|
@ -73,6 +73,7 @@ qcms = { path = "../../../../gfx/qcms", features = ["c_bindings", "neon"], defau
|
|||
viaduct = { git = "https://github.com/mozilla/application-services", rev = "c51b63595a27a6ef45161012323e0261475c10c9"}
|
||||
webext_storage_bridge = { path = "../../../components/extensions/storage/webext_storage_bridge" }
|
||||
midir = { git = "https://github.com/mozilla/midir.git", rev = "dc87afbd4361ae5ec192e1fab0a6409dd13d4011" }
|
||||
midir_impl = { path = "../../../../dom/midi/midir_impl" }
|
||||
|
||||
# https://github.com/mozilla/audioipc-2/commits/master
|
||||
[target.'cfg(not(target_os = "macos"))'.dependencies]
|
||||
|
|
|
@ -82,6 +82,9 @@ extern crate viaduct;
|
|||
#[cfg(not(target_os = "android"))]
|
||||
extern crate midir;
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
extern crate midir_impl;
|
||||
|
||||
extern crate gecko_logger;
|
||||
|
||||
#[cfg(feature = "oxidized_breakpad")]
|
||||
|
|
|
@ -3,6 +3,7 @@ clippy:
|
|||
description: Lint rust
|
||||
include:
|
||||
- build/workspace-hack/
|
||||
- dom/midi/midir_impl/
|
||||
- dom/media/gtest/
|
||||
- dom/webauthn/libudev-sys/
|
||||
- gfx/webrender_bindings/
|
||||
|
|
Загрузка…
Ссылка в новой задаче