MIDIOutput.cpp (3443B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/MIDIOutput.h" 8 9 #include "mozilla/ErrorResult.h" 10 #include "mozilla/TimeStamp.h" 11 #include "mozilla/dom/Document.h" 12 #include "mozilla/dom/MIDIOutputBinding.h" 13 #include "mozilla/dom/MIDIPortChild.h" 14 #include "mozilla/dom/MIDITypes.h" 15 #include "mozilla/dom/MIDIUtils.h" 16 #include "mozilla/dom/Performance.h" 17 #include "nsDOMNavigationTiming.h" 18 #include "nsGlobalWindowInner.h" 19 20 namespace mozilla::dom { 21 22 MIDIOutput::MIDIOutput(nsPIDOMWindowInner* aWindow) : MIDIPort(aWindow) {} 23 24 // static 25 RefPtr<MIDIOutput> MIDIOutput::Create(nsPIDOMWindowInner* aWindow, 26 MIDIAccess* aMIDIAccessParent, 27 const MIDIPortInfo& aPortInfo, 28 const bool aSysexEnabled) { 29 MOZ_ASSERT(static_cast<MIDIPortType>(aPortInfo.type()) == 30 MIDIPortType::Output); 31 RefPtr<MIDIOutput> port = new MIDIOutput(aWindow); 32 if (NS_WARN_IF( 33 !port->Initialize(aPortInfo, aSysexEnabled, aMIDIAccessParent))) { 34 return nullptr; 35 } 36 return port; 37 } 38 39 JSObject* MIDIOutput::WrapObject(JSContext* aCx, 40 JS::Handle<JSObject*> aGivenProto) { 41 return MIDIOutput_Binding::Wrap(aCx, this, aGivenProto); 42 } 43 44 void MIDIOutput::Send(const Sequence<uint8_t>& aData, 45 const Optional<double>& aTimestamp, ErrorResult& aRv) { 46 if (Port()->DeviceState() == MIDIPortDeviceState::Disconnected) { 47 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 48 return; 49 } 50 // The timestamp passed to us is a DOMHighResTimestamp, which is in relation 51 // to the start of navigation timing. This needs to be turned into a 52 // TimeStamp before it hits the platform specific MIDI service. 53 // 54 // If timestamp is either not set or zero, set timestamp to now and send the 55 // message ASAP. 56 TimeStamp timestamp; 57 if (aTimestamp.WasPassed() && aTimestamp.Value() != 0) { 58 nsCOMPtr<Document> doc = GetOwnerWindow()->GetDoc(); 59 if (!doc) { 60 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 61 return; 62 } 63 TimeDuration ts_diff = TimeDuration::FromMilliseconds(aTimestamp.Value()); 64 timestamp = GetOwnerWindow() 65 ->GetPerformance() 66 ->GetDOMTiming() 67 ->GetNavigationStartTimeStamp() + 68 ts_diff; 69 } else { 70 timestamp = TimeStamp::Now(); 71 } 72 73 nsTArray<MIDIMessage> msgArray; 74 bool ret = MIDIUtils::ParseMessages(aData, timestamp, msgArray); 75 if (!ret) { 76 aRv.ThrowTypeError("Invalid MIDI message"); 77 return; 78 } 79 80 if (msgArray.IsEmpty()) { 81 aRv.ThrowTypeError("Empty message array"); 82 return; 83 } 84 85 // TODO Move this check back to parse message so we don't have to iterate 86 // twice. 87 if (!SysexEnabled()) { 88 for (auto& msg : msgArray) { 89 if (MIDIUtils::IsSysexMessage(msg)) { 90 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); 91 return; 92 } 93 } 94 } 95 Port()->SendSend(msgArray); 96 } 97 98 void MIDIOutput::Clear() { 99 if (Port()->ConnectionState() == MIDIPortConnectionState::Closed) { 100 return; 101 } 102 Port()->SendClear(); 103 } 104 105 } // namespace mozilla::dom