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"
8 #include "base/message_loop/message_loop.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/trace_event/trace_event.h"
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.
26 CREATED_ON_UNSUPPORTED_PLATFORMS
,
33 // New items should be inserted here, and |MAX| should point the last item.
37 void ReportUsage(Usage usage
) {
38 UMA_HISTOGRAM_ENUMERATION("Media.Midi.Usage",
39 static_cast<Sample
>(usage
),
40 static_cast<Sample
>(Usage::MAX
) + 1);
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
;
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.
80 if (!session_is_ready
) {
81 // Do not accept a new request if the pending client list contains too
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
);
107 // CompleteInitialization() will be called asynchronously when platform
108 // dependent initialization is finished.
112 // Platform dependent initialization was already finished for previously
113 // initialized clients.
116 base::AutoLock
auto_lock(lock_
);
117 if (result_
== Result::OK
) {
118 AddInitialPorts(client
);
119 clients_
.insert(client
);
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())
142 client
->AccumulateMidiBytesSent(n
);
145 void MidiManager::DispatchSendMidiData(MidiManagerClient
* client
,
147 const std::vector
<uint8
>& data
,
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(
162 base::Bind(&MidiManager::CompleteInitializationInternal
,
163 base::Unretained(this),
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(
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()),
216 UMA_HISTOGRAM_ENUMERATION("Media.Midi.OutputPorts",
217 static_cast<Sample
>(output_ports_
.size()),
220 base::AutoLock
auto_lock(lock_
);
221 DCHECK(clients_
.empty());
222 DCHECK(!initialized_
);
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
);