-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathseq_handler.h
166 lines (134 loc) · 6.5 KB
/
seq_handler.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright 2017 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MIDIS_SEQ_HANDLER_H_
#define MIDIS_SEQ_HANDLER_H_
#include <memory>
#include <base/files/file_descriptor_watcher_posix.h>
#include <base/memory/weak_ptr.h>
#include <gtest/gtest_prod.h>
#include "midis/device.h"
#include "midis/device_tracker.h"
namespace midis {
class DeviceTracker;
// Class to handle all interactions with the ALSA sequencer interface.
// NOTE: The term "input" refers to data received *from* the MIDI H/W and
// external clients that are registered to the ALSA sequencer interfance. The
// term "output" refers to data that a client of midis write *to* MIDI H/W and
// external clients.
class SeqHandler {
public:
using AddDeviceCallback =
base::RepeatingCallback<void(std::unique_ptr<Device>)>;
using RemoveDeviceCallback =
base::RepeatingCallback<void(uint32_t, uint32_t)>;
using HandleReceiveDataCallback = base::RepeatingCallback<void(
uint32_t, uint32_t, uint32_t, const char*, size_t)>;
using IsDevicePresentCallback =
base::RepeatingCallback<bool(uint32_t, uint32_t)>;
using IsPortPresentCallback =
base::RepeatingCallback<bool(uint32_t, uint32_t, uint32_t)>;
struct SeqDeleter {
void operator()(snd_seq_t* seq) const { snd_seq_close(seq); }
};
struct MidiEventDeleter {
void operator()(snd_midi_event_t* coder) const {
snd_midi_event_free(coder);
}
};
using ScopedMidiEventPtr =
std::unique_ptr<snd_midi_event_t, MidiEventDeleter>;
using ScopedSeqPtr = std::unique_ptr<snd_seq_t, SeqDeleter>;
SeqHandler(AddDeviceCallback add_device_cb,
RemoveDeviceCallback remove_device_cb,
HandleReceiveDataCallback handle_rx_data_cb,
IsDevicePresentCallback is_device_present_cb,
IsPortPresentCallback is_port_present_cb);
virtual ~SeqHandler() = default;
// Initializes the ALSA seq interface. Creates client handles for input and
// output, as well create an input port to receive messages (announce as
// well as MIDI data) from ALSA seq. Also starts off the file watcher which
// watches for events on the input port.
bool InitSeq();
void ProcessAlsaClientFd();
// Creates a Device object and runs the necessary callback to register that
// object with DeviceTracker, stored in |add_device_cb_|
virtual void AddSeqDevice(uint32_t device_id);
// At present, we don't support hotplugging of individual ports in devices.
// so, we enumerate all the available ports in AddAlsaDevice().
// This function is here merely to handle MIDI events associated with any
// port being added or removed later (and to print an error message, since
// we don't support it yet)
virtual void AddSeqPort(uint32_t device_id, uint32_t port_id);
// Runs the relevant callback, stored in |remove_device_cb_|, when a MIDI H/W
// device or external client is removed from the ALSA sequencer interface.
virtual void RemoveSeqDevice(uint32_t device_id);
virtual void RemoveSeqPort(uint32_t device_id, uint32_t port_id);
// Callback to run when starting an input port (establishes a subscription,
// and creates a relevant port on the server side, in necessary).
// Returns true on success, false otherwise.
bool SubscribeInPort(uint32_t device_id, uint32_t port_id);
// Callback to run when starting an input port (establishes a subscription,
// and creates a relevant port on the server side, in necessary).
// Returns created seq port id success, -1 otherwise.
int SubscribeOutPort(uint32_t device_id, uint32_t port_id);
// The following two functions undo the work of the Subscribe*Port() function,
// for input and output ports respectively.
void UnsubscribeInPort(uint32_t device_id, uint32_t port_id);
void UnsubscribeOutPort(int out_port_id);
// Encodes the bytes in a MIDI buffer into the provided |encoder|.
bool EncodeMidiBytes(int out_port_id,
snd_seq_t* out_client,
const uint8_t* buffer,
size_t buffer_len,
snd_midi_event_t* encoder);
// Callback to send MIDI data to the H/W. This callback is generally called by
// a Device handler which receives MIDI data from a client (e.g ARC++). The
// Device handler will in turn be called by a Client handler which is
// listening for data from it's client.
void SendMidiData(int out_port_id, const uint8_t* buffer, size_t buf_len);
// This function processes the MIDI data received from H/W or an external
// client, and invokes the callback |handle_rx_data_cb_| which handles the
// data accordingly.
virtual void ProcessMidiEvent(snd_seq_event_t* event);
// Wrappers for functions that interact with the ALSA Sequencer interface.
// These are kept separately, because the intention is to mock these functions
// in unit tests.
virtual int SndSeqEventOutputDirect(snd_seq_t* out_client,
snd_seq_event_t* event);
virtual int SndSeqEventInput(snd_seq_t* in_client, snd_seq_event_t** ev);
virtual int SndSeqEventInputPending(snd_seq_t* in_client,
int fetch_sequencer);
protected:
// For testing purposes.
SeqHandler();
SeqHandler(const SeqHandler&) = delete;
SeqHandler& operator=(const SeqHandler&) = delete;
private:
friend class SeqHandlerTest;
friend class SeqHandlerFuzzer;
FRIEND_TEST(SeqHandlerTest, TestEncodeBytes);
FRIEND_TEST(SeqHandlerTest, TestProcessAlsaClientFdPositive);
FRIEND_TEST(SeqHandlerTest, TestProcessMidiEventsPositive);
FRIEND_TEST(SeqHandlerTest, TestProcessMidiEventsNegative);
// Enumerates all clients which are already connected to the ALSA Sequencer.
void EnumerateExistingDevices();
// Creates a MIDI event wrapped in a ScopedMidiEventPtr.
static ScopedMidiEventPtr CreateMidiEvent(size_t buf_size);
std::unique_ptr<snd_seq_t, SeqDeleter> in_client_;
std::unique_ptr<snd_seq_t, SeqDeleter> out_client_;
std::unique_ptr<snd_midi_event_t, MidiEventDeleter> decoder_;
int in_client_id_;
int out_client_id_;
int in_port_id_;
std::unique_ptr<pollfd> pfd_;
std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_;
AddDeviceCallback add_device_cb_;
RemoveDeviceCallback remove_device_cb_;
HandleReceiveDataCallback handle_rx_data_cb_;
IsDevicePresentCallback is_device_present_cb_;
IsPortPresentCallback is_port_present_cb_;
base::WeakPtrFactory<SeqHandler> weak_factory_;
};
} // namespace midis
#endif // MIDIS_SEQ_HANDLER_H_