AudioParam.cpp (5159B)
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 "AudioParam.h" 8 9 #include "AudioContext.h" 10 #include "AudioNodeEngine.h" 11 #include "AudioNodeTrack.h" 12 #include "mozilla/dom/AudioParamBinding.h" 13 14 namespace mozilla::dom { 15 16 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(AudioParam) 17 18 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioParam) 19 tmp->DisconnectFromGraphAndDestroyTrack(); 20 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNode) 21 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 22 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 23 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioParam) 24 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode) 25 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 26 27 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(AudioParam) 28 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(AudioParam) 29 30 AudioParam::AudioParam(AudioNode* aNode, uint32_t aIndex, 31 const nsAString& aName, float aDefaultValue, 32 float aMinValue, float aMaxValue) 33 : AudioParamTimeline(aDefaultValue), 34 mNode(aNode), 35 mName(aName), 36 mIndex(aIndex), 37 mDefaultValue(aDefaultValue), 38 mMinValue(aMinValue), 39 mMaxValue(aMaxValue) {} 40 41 AudioParam::~AudioParam() { DisconnectFromGraphAndDestroyTrack(); } 42 43 JSObject* AudioParam::WrapObject(JSContext* aCx, 44 JS::Handle<JSObject*> aGivenProto) { 45 return AudioParam_Binding::Wrap(aCx, this, aGivenProto); 46 } 47 48 void AudioParam::DisconnectFromGraphAndDestroyTrack() { 49 MOZ_ASSERT(mRefCnt.get() > mInputNodes.Length(), 50 "Caller should be holding a reference or have called " 51 "mRefCnt.stabilizeForDeletion()"); 52 53 while (!mInputNodes.IsEmpty()) { 54 RefPtr<AudioNode> input = mInputNodes.PopLastElement().mInputNode; 55 input->RemoveOutputParam(this); 56 } 57 58 if (mNodeTrackPort) { 59 mNodeTrackPort->Destroy(); 60 mNodeTrackPort = nullptr; 61 } 62 63 if (mTrack) { 64 mTrack->Destroy(); 65 mTrack = nullptr; 66 } 67 } 68 69 mozilla::MediaTrack* AudioParam::GetTrack() const { return mTrack; } 70 71 mozilla::MediaTrack* AudioParam::Track() { 72 if (mTrack) { 73 return mTrack; 74 } 75 76 AudioNodeEngine* engine = new AudioNodeEngine(nullptr); 77 mTrack = AudioNodeTrack::Create(mNode->Context(), engine, 78 AudioNodeTrack::NO_TRACK_FLAGS, 79 mNode->Context()->Graph()); 80 81 // Force the input to have only one channel, and make it down-mix using 82 // the speaker rules if needed. 83 mTrack->SetChannelMixingParametersImpl(1, ChannelCountMode::Explicit, 84 ChannelInterpretation::Speakers); 85 // Mark as an AudioParam helper track 86 mTrack->SetAudioParamHelperTrack(); 87 88 // Setup the AudioParam's track as an input to the owner AudioNode's track 89 AudioNodeTrack* nodeTrack = mNode->GetTrack(); 90 if (nodeTrack) { 91 mNodeTrackPort = nodeTrack->AllocateInputPort(mTrack); 92 } 93 94 // Send the track to the timeline on the MTG side. 95 AudioParamEvent event(mTrack); 96 SendEventToEngine(event); 97 98 return mTrack; 99 } 100 101 void AudioParam::SendEventToEngine(const AudioParamEvent& aEvent) { 102 if (WEB_AUDIO_API_LOG_TEST()) { 103 nsAutoCString params; 104 if (aEvent.mType == AudioTimelineEvent::SetValueCurve) { 105 params.AppendFmt("length={} time={:f} duration={:f}", 106 aEvent.CurveLength(), aEvent.Time<double>(), 107 aEvent.Duration()); 108 } else { 109 params.AppendFmt("value={} time={:f}", aEvent.NominalValue(), 110 aEvent.Time<double>()); 111 if (aEvent.mType == AudioTimelineEvent::SetTarget) { 112 params.AppendFmt(" constant={}", aEvent.TimeConstant()); 113 } 114 } 115 WEB_AUDIO_API_LOG("{:f}: {} for {} {} {}", GetParentObject()->CurrentTime(), 116 NS_ConvertUTF16toUTF8(mName).get(), ParentNodeId(), 117 AudioTimelineEvent::EnumValueToString(aEvent.mType), 118 params.get()); 119 } 120 AudioNodeTrack* track = mNode->GetTrack(); 121 if (track) { 122 track->SendTimelineEvent(mIndex, aEvent); 123 } 124 } 125 126 void AudioParam::CleanupOldEvents() { 127 MOZ_ASSERT(NS_IsMainThread()); 128 double currentTime = mNode->Context()->CurrentTime(); 129 130 CleanupEventsOlderThan(currentTime); 131 } 132 133 float AudioParamTimeline::AudioNodeInputValue(size_t aCounter) const { 134 MOZ_ASSERT(mTrack); 135 136 // If we have a chunk produced by the AudioNode inputs to the AudioParam, 137 // get its value now. We use aCounter to tell us which frame of the last 138 // AudioChunk to look at. 139 float audioNodeInputValue = 0.0f; 140 const AudioBlock& lastAudioNodeChunk = mTrack->LastChunks()[0]; 141 if (!lastAudioNodeChunk.IsNull()) { 142 MOZ_ASSERT(lastAudioNodeChunk.GetDuration() == WEBAUDIO_BLOCK_SIZE); 143 audioNodeInputValue = 144 static_cast<const float*>(lastAudioNodeChunk.mChannelData[0])[aCounter]; 145 audioNodeInputValue *= lastAudioNodeChunk.mVolume; 146 } 147 148 return audioNodeInputValue; 149 } 150 151 } // namespace mozilla::dom