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.
4
5#ifndef MEDIA_MIDI_MIDI_MANAGER_H_
6#define MEDIA_MIDI_MIDI_MANAGER_H_
7
8#include <map>
9#include <set>
10#include <vector>
11
12#include "base/basictypes.h"
13#include "base/memory/ref_counted.h"
14#include "base/synchronization/lock.h"
15#include "base/time/time.h"
16#include "media/base/media_export.h"
17#include "media/midi/midi_port_info.h"
18#include "media/midi/midi_result.h"
19
20namespace base {
21class SingleThreadTaskRunner;
22}  // namespace base
23
24namespace media {
25
26// A MidiManagerClient registers with the MidiManager to receive MIDI data.
27// See MidiManager::RequestAccess() and MidiManager::ReleaseAccess()
28// for details.
29class MEDIA_EXPORT MidiManagerClient {
30 public:
31  virtual ~MidiManagerClient() {}
32
33  // CompleteStartSession() is called when platform dependent preparation is
34  // finished.
35  virtual void CompleteStartSession(int client_id, MidiResult result) = 0;
36
37  // ReceiveMidiData() is called when MIDI data has been received from the
38  // MIDI system.
39  // |port_index| represents the specific input port from input_ports().
40  // |data| represents a series of bytes encoding one or more MIDI messages.
41  // |length| is the number of bytes in |data|.
42  // |timestamp| is the time the data was received, in seconds.
43  virtual void ReceiveMidiData(uint32 port_index,
44                               const uint8* data,
45                               size_t length,
46                               double timestamp) = 0;
47
48  // AccumulateMidiBytesSent() is called to acknowledge when bytes have
49  // successfully been sent to the hardware.
50  // This happens as a result of the client having previously called
51  // MidiManager::DispatchSendMidiData().
52  virtual void AccumulateMidiBytesSent(size_t n) = 0;
53};
54
55// Manages access to all MIDI hardware.
56class MEDIA_EXPORT MidiManager {
57 public:
58  static const size_t kMaxPendingClientCount = 128;
59
60  MidiManager();
61  virtual ~MidiManager();
62
63  // The constructor and the destructor will be called on the CrBrowserMain
64  // thread.
65  static MidiManager* Create();
66
67  // A client calls StartSession() to receive and send MIDI data.
68  // If the session is ready to start, the MIDI system is lazily initialized
69  // and the client is registered to receive MIDI data.
70  // CompleteStartSession() is called with MIDI_OK if the session is started.
71  // Otherwise CompleteStartSession() is called with proper MidiResult code.
72  // StartSession() and EndSession() can be called on the Chrome_IOThread.
73  // CompleteStartSession() will be invoked on the same Chrome_IOThread.
74  void StartSession(MidiManagerClient* client, int client_id);
75
76  // A client calls EndSession() to stop receiving MIDI data.
77  void EndSession(MidiManagerClient* client);
78
79  // DispatchSendMidiData() is called when MIDI data should be sent to the MIDI
80  // system.
81  // This method is supposed to return immediately and should not block.
82  // |port_index| represents the specific output port from output_ports().
83  // |data| represents a series of bytes encoding one or more MIDI messages.
84  // |length| is the number of bytes in |data|.
85  // |timestamp| is the time to send the data, in seconds. A value of 0
86  // means send "now" or as soon as possible.
87  // The default implementation is for unsupported platforms.
88  virtual void DispatchSendMidiData(MidiManagerClient* client,
89                                    uint32 port_index,
90                                    const std::vector<uint8>& data,
91                                    double timestamp);
92
93  // input_ports() is a list of MIDI ports for receiving MIDI data.
94  // Each individual port in this list can be identified by its
95  // integer index into this list.
96  const MidiPortInfoList& input_ports() const { return input_ports_; }
97
98  // output_ports() is a list of MIDI ports for sending MIDI data.
99  // Each individual port in this list can be identified by its
100  // integer index into this list.
101  const MidiPortInfoList& output_ports() const { return output_ports_; }
102
103 protected:
104  friend class MidiManagerUsb;
105
106  // Initializes the platform dependent MIDI system. MidiManager class has a
107  // default implementation that synchronously calls CompleteInitialization()
108  // with MIDI_NOT_SUPPORTED on the caller thread. A derived class for a
109  // specific platform should override this method correctly.
110  // This method is called on Chrome_IOThread thread inside StartSession().
111  // Platform dependent initialization can be processed synchronously or
112  // asynchronously. When the initialization is completed,
113  // CompleteInitialization() should be called with |result|.
114  // |result| should be MIDI_OK on success, otherwise a proper MidiResult.
115  virtual void StartInitialization();
116
117  // Called from a platform dependent implementation of StartInitialization().
118  // It invokes CompleteInitializationInternal() on the thread that calls
119  // StartSession() and distributes |result| to MIDIManagerClient objects in
120  // |pending_clients_|.
121  void CompleteInitialization(MidiResult result);
122
123  void AddInputPort(const MidiPortInfo& info);
124  void AddOutputPort(const MidiPortInfo& info);
125
126  // Dispatches to all clients.
127  // TODO(toyoshim): Fix the mac implementation to use
128  // |ReceiveMidiData(..., base::TimeTicks)|.
129  void ReceiveMidiData(uint32 port_index,
130                       const uint8* data,
131                       size_t length,
132                       double timestamp);
133
134  void ReceiveMidiData(uint32 port_index,
135                       const uint8* data,
136                       size_t length,
137                       base::TimeTicks time) {
138    ReceiveMidiData(port_index, data, length,
139                    (time - base::TimeTicks()).InSecondsF());
140  }
141
142  size_t clients_size_for_testing() const { return clients_.size(); }
143  size_t pending_clients_size_for_testing() const {
144    return pending_clients_.size();
145  }
146
147 private:
148  void CompleteInitializationInternal(MidiResult result);
149
150  // Keeps track of all clients who wish to receive MIDI data.
151  typedef std::set<MidiManagerClient*> ClientList;
152  ClientList clients_;
153
154  // Keeps track of all clients who are waiting for CompleteStartSession().
155  typedef std::multimap<MidiManagerClient*, int> PendingClientMap;
156  PendingClientMap pending_clients_;
157
158  // Keeps a SingleThreadTaskRunner of the thread that calls StartSession in
159  // order to invoke CompleteStartSession() on the thread.
160  scoped_refptr<base::SingleThreadTaskRunner> session_thread_runner_;
161
162  // Keeps true if platform dependent initialization is already completed.
163  bool initialized_;
164
165  // Keeps the platform dependent initialization result if initialization is
166  // completed. Otherwise keeps MIDI_NOT_SUPPORTED.
167  MidiResult result_;
168
169  // Protects access to |clients_|, |pending_clients_|, |initialized_|, and
170  // |result_|.
171  base::Lock lock_;
172
173  MidiPortInfoList input_ports_;
174  MidiPortInfoList output_ports_;
175
176  DISALLOW_COPY_AND_ASSIGN(MidiManager);
177};
178
179}  // namespace media
180
181#endif  // MEDIA_MIDI_MIDI_MANAGER_H_
182