All instances of addresses as void* are changed to uintptr_t in
[chromium-blink-merge.git] / media / midi / midi_manager.cc
blob6e8c1a266fa86502407d929db218eb1e85afc1f5
1 // Copyright (c) 2013 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.
5 #include "media/midi/midi_manager.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/trace_event/trace_event.h"
12 namespace media {
13 namespace midi {
15 namespace {
17 using Sample = base::HistogramBase::Sample;
19 // If many users have more devices, this number will be increased.
20 // But the number is expected to be big enough for now.
21 const Sample kMaxUmaDevices = 31;
23 // Used to count events for usage histogram.
24 enum class Usage {
25 CREATED,
26 CREATED_ON_UNSUPPORTED_PLATFORMS,
27 SESSION_STARTED,
28 SESSION_ENDED,
29 INITIALIZED,
30 INPUT_PORT_ADDED,
31 OUTPUT_PORT_ADDED,
33 // New items should be inserted here, and |MAX| should point the last item.
34 MAX = INITIALIZED,
37 void ReportUsage(Usage usage) {
38 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage",
39 static_cast<Sample>(usage),
40 static_cast<Sample>(Usage::MAX) + 1);
43 } // namespace
45 MidiManager::MidiManager()
46 : initialized_(false), result_(Result::NOT_INITIALIZED) {
47 ReportUsage(Usage::CREATED);
50 MidiManager::~MidiManager() {
51 UMA_HISTOGRAM_ENUMERATION("Media.Midi.ResultOnShutdown",
52 static_cast<Sample>(result_),
53 static_cast<Sample>(Result::MAX) + 1);
56 #if !defined(OS_MACOSX) && !defined(OS_WIN) && \
57 !(defined(USE_ALSA) && defined(USE_UDEV)) && !defined(OS_ANDROID)
58 MidiManager* MidiManager::Create() {
59 ReportUsage(Usage::CREATED_ON_UNSUPPORTED_PLATFORMS);
60 return new MidiManager;
62 #endif
64 void MidiManager::StartSession(MidiManagerClient* client) {
65 ReportUsage(Usage::SESSION_STARTED);
67 bool session_is_ready;
68 bool session_needs_initialization = false;
69 bool too_many_pending_clients_exist = false;
72 base::AutoLock auto_lock(lock_);
73 session_is_ready = initialized_;
74 if (clients_.find(client) != clients_.end() ||
75 pending_clients_.find(client) != pending_clients_.end()) {
76 // Should not happen. But just in case the renderer is compromised.
77 NOTREACHED();
78 return;
80 if (!session_is_ready) {
81 // Do not accept a new request if the pending client list contains too
82 // many clients.
83 too_many_pending_clients_exist =
84 pending_clients_.size() >= kMaxPendingClientCount;
86 if (!too_many_pending_clients_exist) {
87 // Call StartInitialization() only for the first request.
88 session_needs_initialization = pending_clients_.empty();
89 pending_clients_.insert(client);
94 // Lazily initialize the MIDI back-end.
95 if (!session_is_ready) {
96 if (session_needs_initialization) {
97 TRACE_EVENT0("midi", "MidiManager::StartInitialization");
98 session_thread_runner_ =
99 base::MessageLoop::current()->task_runner();
100 StartInitialization();
102 if (too_many_pending_clients_exist) {
103 // Return an error immediately if there are too many requests.
104 client->CompleteStartSession(Result::INITIALIZATION_ERROR);
105 return;
107 // CompleteInitialization() will be called asynchronously when platform
108 // dependent initialization is finished.
109 return;
112 // Platform dependent initialization was already finished for previously
113 // initialized clients.
114 Result result;
116 base::AutoLock auto_lock(lock_);
117 if (result_ == Result::OK) {
118 AddInitialPorts(client);
119 clients_.insert(client);
121 result = result_;
123 client->CompleteStartSession(result);
126 void MidiManager::EndSession(MidiManagerClient* client) {
127 ReportUsage(Usage::SESSION_ENDED);
129 // At this point, |client| can be in the destruction process, and calling
130 // any method of |client| is dangerous.
131 base::AutoLock auto_lock(lock_);
132 clients_.erase(client);
133 pending_clients_.erase(client);
136 void MidiManager::AccumulateMidiBytesSent(MidiManagerClient* client, size_t n) {
138 base::AutoLock auto_lock(lock_);
139 if (clients_.find(client) == clients_.end())
140 return;
142 client->AccumulateMidiBytesSent(n);
145 void MidiManager::DispatchSendMidiData(MidiManagerClient* client,
146 uint32 port_index,
147 const std::vector<uint8>& data,
148 double timestamp) {
149 NOTREACHED();
152 void MidiManager::StartInitialization() {
153 CompleteInitialization(Result::NOT_SUPPORTED);
156 void MidiManager::CompleteInitialization(Result result) {
157 DCHECK(session_thread_runner_.get());
158 // It is safe to post a task to the IO thread from here because the IO thread
159 // should have stopped if the MidiManager is going to be destructed.
160 session_thread_runner_->PostTask(
161 FROM_HERE,
162 base::Bind(&MidiManager::CompleteInitializationInternal,
163 base::Unretained(this),
164 result));
167 void MidiManager::AddInputPort(const MidiPortInfo& info) {
168 ReportUsage(Usage::INPUT_PORT_ADDED);
169 base::AutoLock auto_lock(lock_);
170 input_ports_.push_back(info);
171 for (auto client : clients_)
172 client->AddInputPort(info);
175 void MidiManager::AddOutputPort(const MidiPortInfo& info) {
176 ReportUsage(Usage::OUTPUT_PORT_ADDED);
177 base::AutoLock auto_lock(lock_);
178 output_ports_.push_back(info);
179 for (auto client : clients_)
180 client->AddOutputPort(info);
183 void MidiManager::SetInputPortState(uint32 port_index, MidiPortState state) {
184 base::AutoLock auto_lock(lock_);
185 DCHECK_LT(port_index, input_ports_.size());
186 input_ports_[port_index].state = state;
187 for (auto client : clients_)
188 client->SetInputPortState(port_index, state);
191 void MidiManager::SetOutputPortState(uint32 port_index, MidiPortState state) {
192 base::AutoLock auto_lock(lock_);
193 DCHECK_LT(port_index, output_ports_.size());
194 output_ports_[port_index].state = state;
195 for (auto client : clients_)
196 client->SetOutputPortState(port_index, state);
199 void MidiManager::ReceiveMidiData(
200 uint32 port_index,
201 const uint8* data,
202 size_t length,
203 double timestamp) {
204 base::AutoLock auto_lock(lock_);
206 for (auto client : clients_)
207 client->ReceiveMidiData(port_index, data, length, timestamp);
210 void MidiManager::CompleteInitializationInternal(Result result) {
211 TRACE_EVENT0("midi", "MidiManager::CompleteInitialization");
212 ReportUsage(Usage::INITIALIZED);
213 UMA_HISTOGRAM_ENUMERATION("Media.Midi.InputPorts",
214 static_cast<Sample>(input_ports_.size()),
215 kMaxUmaDevices + 1);
216 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
217 static_cast<Sample>(output_ports_.size()),
218 kMaxUmaDevices + 1);
220 base::AutoLock auto_lock(lock_);
221 DCHECK(clients_.empty());
222 DCHECK(!initialized_);
223 initialized_ = true;
224 result_ = result;
226 for (auto client : pending_clients_) {
227 if (result_ == Result::OK) {
228 AddInitialPorts(client);
229 clients_.insert(client);
231 client->CompleteStartSession(result_);
233 pending_clients_.clear();
236 void MidiManager::AddInitialPorts(MidiManagerClient* client) {
237 lock_.AssertAcquired();
239 for (const auto& info : input_ports_)
240 client->AddInputPort(info);
241 for (const auto& info : output_ports_)
242 client->AddOutputPort(info);
245 } // namespace midi
246 } // namespace media