forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			193 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* 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/dom/midi/midir_impl_ffi_generated.h"
 | |
| #include "mozilla/ipc/BackgroundParent.h"
 | |
| #include "mozilla/Unused.h"
 | |
| #include "nsIThread.h"
 | |
| #include "mozilla/Logging.h"
 | |
| #include "MIDILog.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, const MIDIMessage& aMessage)
 | |
|       : MIDIBackgroundRunnable("SendRunnable"),
 | |
|         mPortID(aPortID),
 | |
|         mMessage(aMessage) {}
 | |
|   ~SendRunnable() = default;
 | |
|   virtual void RunInternal() {
 | |
|     MIDIPlatformService::AssertThread();
 | |
|     if (!MIDIPlatformService::IsRunning()) {
 | |
|       // Some send operations might outlive the service, bail out and do nothing
 | |
|       return;
 | |
|     }
 | |
|     midirMIDIPlatformService* srv =
 | |
|         static_cast<midirMIDIPlatformService*>(MIDIPlatformService::Get());
 | |
|     srv->SendMessage(mPortID, mMessage);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   nsString mPortID;
 | |
|   MIDIMessage mMessage;
 | |
| };
 | |
| 
 | |
| // static
 | |
| StaticMutex midirMIDIPlatformService::gOwnerThreadMutex;
 | |
| 
 | |
| // static
 | |
| nsCOMPtr<nsISerialEventTarget> midirMIDIPlatformService::gOwnerThread;
 | |
| 
 | |
| midirMIDIPlatformService::midirMIDIPlatformService()
 | |
|     : mImplementation(nullptr) {
 | |
|   StaticMutexAutoLock lock(gOwnerThreadMutex);
 | |
|   gOwnerThread = OwnerThread();
 | |
| }
 | |
| 
 | |
| midirMIDIPlatformService::~midirMIDIPlatformService() {
 | |
|   LOG("midir_impl_shutdown");
 | |
|   if (mImplementation) {
 | |
|     midir_impl_shutdown(mImplementation);
 | |
|   }
 | |
|   StaticMutexAutoLock lock(gOwnerThreadMutex);
 | |
|   gOwnerThread = 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);
 | |
| }
 | |
| 
 | |
| // static
 | |
| void midirMIDIPlatformService::RemovePort(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()->RemovePortInfo(port);
 | |
| }
 | |
| 
 | |
| void midirMIDIPlatformService::Init() {
 | |
|   if (mImplementation) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mImplementation = midir_impl_init(AddPort);
 | |
| 
 | |
|   if (mImplementation) {
 | |
|     MIDIPlatformService::Get()->SendPortList();
 | |
|   } else {
 | |
|     LOG("midir_impl_init failure");
 | |
|   }
 | |
| }
 | |
| 
 | |
| // static
 | |
| void midirMIDIPlatformService::CheckAndReceive(const nsString* aId,
 | |
|                                                const uint8_t* aData,
 | |
|                                                size_t aLength,
 | |
|                                                const GeckoTimeStamp* aTimeStamp,
 | |
|                                                uint64_t aMicros) {
 | |
|   nsTArray<uint8_t> data;
 | |
|   data.AppendElements(aData, aLength);
 | |
|   const TimeStamp* openTime = reinterpret_cast<const TimeStamp*>(aTimeStamp);
 | |
|   TimeStamp timestamp =
 | |
|       *openTime + TimeDuration::FromMicroseconds(static_cast<double>(aMicros));
 | |
|   MIDIMessage message(data, timestamp);
 | |
|   LogMIDIMessage(message, *aId, MIDIPortType::Input);
 | |
|   nsTArray<MIDIMessage> messages;
 | |
|   messages.AppendElement(message);
 | |
| 
 | |
|   nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(*aId, messages));
 | |
|   StaticMutexAutoLock lock(gOwnerThreadMutex);
 | |
|   if (gOwnerThread) {
 | |
|     gOwnerThread->Dispatch(r, NS_DISPATCH_NORMAL);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void midirMIDIPlatformService::Refresh() {
 | |
|   midir_impl_refresh(mImplementation, AddPort, RemovePort);
 | |
| }
 | |
| 
 | |
| void midirMIDIPlatformService::Open(MIDIPortParent* aPort) {
 | |
|   AssertThread();
 | |
|   MOZ_ASSERT(aPort);
 | |
|   nsString id = aPort->MIDIPortInterface::Id();
 | |
|   TimeStamp openTimeStamp = TimeStamp::Now();
 | |
|   if (midir_impl_open_port(mImplementation, &id,
 | |
|                            reinterpret_cast<GeckoTimeStamp*>(&openTimeStamp),
 | |
|                            CheckAndReceive)) {
 | |
|     LOG("MIDI port open: %s at t=%lf", NS_ConvertUTF16toUTF8(id).get(),
 | |
|         (openTimeStamp - TimeStamp::ProcessCreation()).ToSeconds());
 | |
|     nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
 | |
|         aPort, aPort->DeviceState(), MIDIPortConnectionState::Open));
 | |
|     OwnerThread()->Dispatch(r.forget());
 | |
|   } else {
 | |
|     LOG("MIDI port open failed: %s", NS_ConvertUTF16toUTF8(id).get());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void midirMIDIPlatformService::Stop() {
 | |
|   // Nothing to do here AFAIK
 | |
| }
 | |
| 
 | |
| void midirMIDIPlatformService::ScheduleSend(const nsAString& aPortId) {
 | |
|   AssertThread();
 | |
|   LOG("MIDI port schedule send %s", NS_ConvertUTF16toUTF8(aPortId).get());
 | |
|   nsTArray<MIDIMessage> messages;
 | |
|   GetMessages(aPortId, messages);
 | |
|   TimeStamp now = TimeStamp::Now();
 | |
|   for (const auto& message : messages) {
 | |
|     if (message.timestamp().IsNull()) {
 | |
|       SendMessage(aPortId, message);
 | |
|     } else {
 | |
|       double delay = (message.timestamp() - now).ToMilliseconds();
 | |
|       if (delay < 1.0) {
 | |
|         SendMessage(aPortId, message);
 | |
|       } else {
 | |
|         nsCOMPtr<nsIRunnable> r(new SendRunnable(aPortId, message));
 | |
|         OwnerThread()->DelayedDispatch(r.forget(),
 | |
|                                        static_cast<uint32_t>(delay));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void midirMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort) {
 | |
|   AssertThread();
 | |
|   MOZ_ASSERT(aPort);
 | |
|   nsString id = aPort->MIDIPortInterface::Id();
 | |
|   LOG("MIDI port schedule close %s", NS_ConvertUTF16toUTF8(id).get());
 | |
|   if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
 | |
|     midir_impl_close_port(mImplementation, &id);
 | |
|     nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(
 | |
|         aPort, aPort->DeviceState(), MIDIPortConnectionState::Closed));
 | |
|     OwnerThread()->Dispatch(r.forget());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void midirMIDIPlatformService::SendMessage(const nsAString& aPortId,
 | |
|                                            const MIDIMessage& aMessage) {
 | |
|   LOG("MIDI send message on %s", NS_ConvertUTF16toUTF8(aPortId).get());
 | |
|   LogMIDIMessage(aMessage, aPortId, MIDIPortType::Output);
 | |
|   midir_impl_send(mImplementation, &aPortId, &aMessage.data());
 | |
| }
 | 
