1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/modules/webaudio/inspector_web_audio_agent.h"
6 
7 #include <memory>
8 #include "third_party/blink/renderer/core/page/page.h"
9 #include "third_party/blink/renderer/core/frame/local_frame.h"
10 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
11 #include "third_party/blink/renderer/modules/webaudio/audio_context.h"
12 #include "third_party/blink/renderer/modules/webaudio/audio_graph_tracer.h"
13 #include "third_party/blink/renderer/modules/webaudio/audio_listener.h"
14 #include "third_party/blink/renderer/modules/webaudio/audio_node.h"
15 #include "third_party/blink/renderer/modules/webaudio/audio_param.h"
16 
17 namespace blink {
18 
19 namespace {
20 
GetContextTypeEnum(BaseAudioContext * context)21 String GetContextTypeEnum(BaseAudioContext* context) {
22   return context->HasRealtimeConstraint()
23       ? protocol::WebAudio::ContextTypeEnum::Realtime
24       : protocol::WebAudio::ContextTypeEnum::Offline;
25 }
26 
GetContextStateEnum(BaseAudioContext * context)27 String GetContextStateEnum(BaseAudioContext* context) {
28   switch (context->ContextState()) {
29     case BaseAudioContext::AudioContextState::kSuspended:
30       return protocol::WebAudio::ContextStateEnum::Suspended;
31     case BaseAudioContext::AudioContextState::kRunning:
32       return protocol::WebAudio::ContextStateEnum::Running;
33     case BaseAudioContext::AudioContextState::kClosed:
34       return protocol::WebAudio::ContextStateEnum::Closed;
35     default:
36       NOTREACHED();
37       break;
38   }
39 }
40 
41 // Strips "Node" from the node name string. For example, "GainNode" will return
42 // "Gain".
StripNodeSuffix(const String & nodeName)43 String StripNodeSuffix(const String& nodeName) {
44   return nodeName.EndsWith("Node") ? nodeName.Left(nodeName.length() - 4)
45                                    : "Unknown";
46 }
47 
48 // Strips out the prefix and returns the actual parameter name. If the name
49 // does not match |NodeName.ParamName| pattern, returns "Unknown" instead.
StripParamPrefix(const String & paramName)50 String StripParamPrefix(const String& paramName) {
51   Vector<String> name_tokens;
52   paramName.Split('.', name_tokens);
53   return name_tokens.size() == 2 ? name_tokens.at(1) : "Unknown";
54 }
55 
56 }  // namespace
57 
58 using protocol::Response;
59 
InspectorWebAudioAgent(Page * page)60 InspectorWebAudioAgent::InspectorWebAudioAgent(Page* page)
61     : page_(page),
62       enabled_(&agent_state_, /*default_value=*/false) {
63 }
64 
65 InspectorWebAudioAgent::~InspectorWebAudioAgent() = default;
66 
Restore()67 void InspectorWebAudioAgent::Restore() {
68   if (!enabled_.Get())
69     return;
70 
71   AudioGraphTracer* graph_tracer = AudioGraphTracer::FromPage(page_);
72   graph_tracer->SetInspectorAgent(this);
73 }
74 
enable()75 Response InspectorWebAudioAgent::enable() {
76   if (enabled_.Get())
77     return Response::Success();
78   enabled_.Set(true);
79   AudioGraphTracer* graph_tracer = AudioGraphTracer::FromPage(page_);
80   graph_tracer->SetInspectorAgent(this);
81   return Response::Success();
82 }
83 
disable()84 Response InspectorWebAudioAgent::disable() {
85   if (!enabled_.Get())
86     return Response::Success();
87   enabled_.Clear();
88   AudioGraphTracer* graph_tracer = AudioGraphTracer::FromPage(page_);
89   graph_tracer->SetInspectorAgent(nullptr);
90   return Response::Success();
91 }
92 
getRealtimeData(const protocol::WebAudio::GraphObjectId & contextId,std::unique_ptr<ContextRealtimeData> * out_data)93 Response InspectorWebAudioAgent::getRealtimeData(
94     const protocol::WebAudio::GraphObjectId& contextId,
95     std::unique_ptr<ContextRealtimeData>* out_data) {
96   auto* const graph_tracer = AudioGraphTracer::FromPage(page_);
97   if (!enabled_.Get())
98     return Response::ServerError("Enable agent first.");
99 
100   BaseAudioContext* context = graph_tracer->GetContextById(contextId);
101   if (!context)
102     return Response::ServerError("Cannot find BaseAudioContext with such id.");
103 
104   if (!context->HasRealtimeConstraint()) {
105     return Response::ServerError(
106         "ContextRealtimeData is only avaliable for an AudioContext.");
107   }
108 
109   // The realtime metric collection is only for AudioContext.
110   AudioCallbackMetric metric =
111       static_cast<AudioContext*>(context)->GetCallbackMetric();
112   *out_data = ContextRealtimeData::create()
113           .setCurrentTime(context->currentTime())
114           .setRenderCapacity(metric.render_capacity)
115           .setCallbackIntervalMean(metric.mean_callback_interval)
116           .setCallbackIntervalVariance(metric.variance_callback_interval)
117           .build();
118   return Response::Success();
119 }
120 
DidCreateBaseAudioContext(BaseAudioContext * context)121 void InspectorWebAudioAgent::DidCreateBaseAudioContext(
122     BaseAudioContext* context) {
123   GetFrontend()->contextCreated(BuildProtocolContext(context));
124 }
125 
WillDestroyBaseAudioContext(BaseAudioContext * context)126 void InspectorWebAudioAgent::WillDestroyBaseAudioContext(
127     BaseAudioContext* context) {
128   GetFrontend()->contextWillBeDestroyed(context->Uuid());
129 }
130 
DidChangeBaseAudioContext(BaseAudioContext * context)131 void InspectorWebAudioAgent::DidChangeBaseAudioContext(
132     BaseAudioContext* context) {
133   GetFrontend()->contextChanged(BuildProtocolContext(context));
134 }
135 
DidCreateAudioListener(AudioListener * listener)136 void InspectorWebAudioAgent::DidCreateAudioListener(AudioListener* listener) {
137   GetFrontend()->audioListenerCreated(
138       protocol::WebAudio::AudioListener::create()
139           .setListenerId(listener->Uuid())
140           .setContextId(listener->ParentUuid())
141           .build());
142 }
143 
WillDestroyAudioListener(AudioListener * listener)144 void InspectorWebAudioAgent::WillDestroyAudioListener(AudioListener* listener) {
145   GetFrontend()->audioListenerWillBeDestroyed(
146       listener->ParentUuid(), listener->Uuid());
147 }
148 
DidCreateAudioNode(AudioNode * node)149 void InspectorWebAudioAgent::DidCreateAudioNode(AudioNode* node) {
150   GetFrontend()->audioNodeCreated(
151       protocol::WebAudio::AudioNode::create()
152           .setNodeId(node->Uuid())
153           .setNodeType(StripNodeSuffix(node->GetNodeName()))
154           .setNumberOfInputs(node->numberOfInputs())
155           .setNumberOfOutputs(node->numberOfOutputs())
156           .setChannelCount(node->channelCount())
157           .setChannelCountMode(node->channelCountMode())
158           .setChannelInterpretation(node->channelInterpretation())
159           .setContextId(node->ParentUuid())
160           .build());
161 }
162 
WillDestroyAudioNode(AudioNode * node)163 void InspectorWebAudioAgent::WillDestroyAudioNode(AudioNode* node) {
164   GetFrontend()->audioNodeWillBeDestroyed(node->ParentUuid(), node->Uuid());
165 }
166 
DidCreateAudioParam(AudioParam * param)167 void InspectorWebAudioAgent::DidCreateAudioParam(AudioParam* param) {
168   GetFrontend()->audioParamCreated(
169       protocol::WebAudio::AudioParam::create()
170           .setParamId(param->Uuid())
171           .setParamType(StripParamPrefix(param->GetParamName()))
172           .setRate(param->automationRate())
173           .setDefaultValue(param->defaultValue())
174           .setMinValue(param->minValue())
175           .setMaxValue(param->maxValue())
176           .setContextId(param->Context()->Uuid())
177           .setNodeId(param->ParentUuid())
178           .build());
179 }
180 
WillDestroyAudioParam(AudioParam * param)181 void InspectorWebAudioAgent::WillDestroyAudioParam(AudioParam* param) {
182   GetFrontend()->audioParamWillBeDestroyed(
183       param->Context()->Uuid(), param->ParentUuid(), param->Uuid());
184 }
185 
DidConnectNodes(AudioNode * source_node,AudioNode * destination_node,int32_t source_output_index,int32_t destination_input_index)186 void InspectorWebAudioAgent::DidConnectNodes(
187     AudioNode* source_node,
188     AudioNode* destination_node,
189     int32_t source_output_index,
190     int32_t destination_input_index) {
191   GetFrontend()->nodesConnected(
192       source_node->ParentUuid(),
193       source_node->Uuid(),
194       destination_node->Uuid(),
195       source_output_index,
196       destination_input_index);
197 }
198 
DidDisconnectNodes(AudioNode * source_node,AudioNode * destination_node,int32_t source_output_index,int32_t destination_input_index)199 void InspectorWebAudioAgent::DidDisconnectNodes(
200     AudioNode* source_node,
201     AudioNode* destination_node,
202     int32_t source_output_index,
203     int32_t destination_input_index) {
204   GetFrontend()->nodesDisconnected(
205       source_node->ParentUuid(),
206       source_node->Uuid(),
207       destination_node ? destination_node->Uuid() : String(),
208       source_output_index,
209       destination_input_index);
210 }
211 
DidConnectNodeParam(AudioNode * source_node,AudioParam * destination_param,int32_t source_output_index)212 void InspectorWebAudioAgent::DidConnectNodeParam(
213     AudioNode* source_node,
214     AudioParam* destination_param,
215     int32_t source_output_index) {
216   GetFrontend()->nodeParamConnected(
217       source_node->ParentUuid(),
218       source_node->Uuid(),
219       destination_param->Uuid(),
220       source_output_index);
221 }
222 
DidDisconnectNodeParam(AudioNode * source_node,AudioParam * destination_param,int32_t source_output_index)223 void InspectorWebAudioAgent::DidDisconnectNodeParam(
224     AudioNode* source_node,
225     AudioParam* destination_param,
226     int32_t source_output_index) {
227   GetFrontend()->nodeParamDisconnected(
228       source_node->ParentUuid(),
229       source_node->Uuid(),
230       destination_param->Uuid(),
231       source_output_index);
232 }
233 
234 std::unique_ptr<protocol::WebAudio::BaseAudioContext>
BuildProtocolContext(BaseAudioContext * context)235 InspectorWebAudioAgent::BuildProtocolContext(BaseAudioContext* context) {
236   return protocol::WebAudio::BaseAudioContext::create()
237       .setContextId(context->Uuid())
238       .setContextType(GetContextTypeEnum(context))
239       .setContextState(GetContextStateEnum(context))
240       .setCallbackBufferSize(context->CallbackBufferSize())
241       .setMaxOutputChannelCount(context->MaxChannelCount())
242       .setSampleRate(context->sampleRate())
243       .build();
244 }
245 
Trace(Visitor * visitor)246 void InspectorWebAudioAgent::Trace(Visitor* visitor) {
247   visitor->Trace(page_);
248   InspectorBaseAgent::Trace(visitor);
249 }
250 
251 }  // namespace blink
252