/[svn]/linuxsampler/trunk/src/drivers/midi/MidiInputDeviceAlsa.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/drivers/midi/MidiInputDeviceAlsa.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3766 - (show annotations) (download)
Mon Apr 6 12:41:49 2020 UTC (4 years ago) by schoenebeck
File size: 15913 byte(s)
Fixed deadlocks (e.g. when restarting engines).

* Individual thread implementations (e.g. disk thread, etc.):
  Disable thread cancellation on critical sections, e.g. when holding
  mutex locks, to prevent deadlocks if thread is stopped and/or
  restarted.

* Added TestCancel() calls to thread implementations if missing.

* No need to wrap Thread::TestCancel() calls into
  CONFIG_PTHREAD_TESTCANCEL macro conditions (since TestCancel() is
  already a stub on systems which don't have pthread_testcancel()
  available).

* If compiled for debugging: give each thread a human readable name
  to simplify debugging of multi-threading issues.

* DiskThreadBase: TestCancel() and pthread_testcancel() calls are
  per-se redundant, so only call TestCancel().

* Added missing override keywords to silent compiler warnings.

* Bumped version (2.1.1.svn54).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2020 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #include "MidiInputDeviceAlsa.h"
25 #include "MidiInputDeviceFactory.h"
26
27 #define perm_ok(pinfo,bits) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
28
29 namespace LinuxSampler {
30
31 /// number of currently existing ALSA midi input devices in LinuxSampler
32 static int existingAlsaDevices = 0;
33
34 // *************** ParameterName ***************
35 // *
36
37 MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterName::ParameterName(MidiInputPort* pPort) throw (Exception) : MidiInputPort::ParameterName(pPort, "Port " + ToString(pPort->GetPortNumber())) {
38 OnSetValue(ValueAsString()); // initialize port name
39 }
40
41 void MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterName::OnSetValue(String s) throw (Exception) {
42 if (s.size() > 16) throw Exception("Name too long for ALSA MIDI input port (max. 16 characters)");
43 snd_seq_port_info_t* hInfo;
44 snd_seq_port_info_malloc(&hInfo);
45 snd_seq_get_port_info(((MidiInputDeviceAlsa*)pPort->GetDevice())->hAlsaSeq, pPort->GetPortNumber(), hInfo);
46 snd_seq_port_info_set_name(hInfo, s.c_str());
47 snd_seq_set_port_info(((MidiInputDeviceAlsa*)pPort->GetDevice())->hAlsaSeq, pPort->GetPortNumber(), hInfo);
48 snd_seq_port_info_free(hInfo);
49 }
50
51
52
53 // *************** ParameterAlsaSeqBindings ***************
54 // *
55
56
57 MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::ParameterAlsaSeqBindings(MidiInputPortAlsa* pPort) : DeviceRuntimeParameterStrings( std::vector<String>() ) {
58 this->pPort = pPort;
59 }
60
61 String MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::Description() {
62 return "Bindings to other Alsa sequencer clients";
63 }
64 bool MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::Fix() {
65 return false;
66 }
67
68 std::vector<String> MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::PossibilitiesAsString() {
69 std::vector<String> res;
70 snd_seq_client_info_t* cinfo;
71 snd_seq_port_info_t* pinfo;
72
73 snd_seq_client_info_alloca(&cinfo);
74 snd_seq_port_info_alloca(&pinfo);
75 snd_seq_client_info_set_client(cinfo, -1);
76 while (snd_seq_query_next_client(pPort->pDevice->hAlsaSeq, cinfo) >= 0) {
77 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
78 snd_seq_port_info_set_port(pinfo, -1);
79 while (snd_seq_query_next_port(pPort->pDevice->hAlsaSeq, pinfo) >= 0) {
80 if (perm_ok(pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ)) {
81 String seq_id = ToString(snd_seq_client_info_get_client(cinfo)) + ":" +
82 ToString(snd_seq_port_info_get_port(pinfo));
83 res.push_back(seq_id);
84 }
85 }
86 }
87
88 return res;
89 }
90
91 void MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::OnSetValue(std::vector<String> vS) throw (Exception) {
92 pPort->UnsubscribeAll();
93
94 std::vector<String>::iterator iter = vS.begin();
95 for (; iter != vS.end(); iter++) pPort->ConnectToAlsaMidiSource((*iter).c_str());
96 }
97
98
99
100 // *************** ParameterAlsaSeqId ***************
101 // *
102
103 MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::ParameterAlsaSeqId(MidiInputPortAlsa* pPort)
104 : DeviceRuntimeParameterString(ToString(pPort->pDevice->hAlsaSeqClient) + ":" + ToString(pPort->portNumber)) {
105 }
106
107 String MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::Description() {
108 return "ALSA Sequencer ID";
109 }
110
111 bool MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::Fix() {
112 return true;
113 }
114
115 std::vector<String> MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::PossibilitiesAsString() {
116 return std::vector<String>(); // nothing
117 }
118
119 void MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::OnSetValue(String s) {
120 // not possible as parameter is 'fix'
121 }
122
123
124
125 // *************** MidiInputDeviceAlsa::ParameterName ***************
126 // *
127
128 MidiInputDeviceAlsa::ParameterName::ParameterName() : DeviceCreationParameterString() {
129 InitWithDefault(); // use default name
130 }
131
132 MidiInputDeviceAlsa::ParameterName::ParameterName(String s) : DeviceCreationParameterString(s) {
133 }
134
135 String MidiInputDeviceAlsa::ParameterName::Description() {
136 return "Arbitrary ALSA client name";
137 }
138
139 bool MidiInputDeviceAlsa::ParameterName::Fix() {
140 return true;
141 }
142
143 bool MidiInputDeviceAlsa::ParameterName::Mandatory() {
144 return false;
145 }
146
147 std::map<String,DeviceCreationParameter*> MidiInputDeviceAlsa::ParameterName::DependsAsParameters() {
148 return std::map<String,DeviceCreationParameter*>(); // no dependencies
149 }
150
151 std::vector<String> MidiInputDeviceAlsa::ParameterName::PossibilitiesAsString(std::map<String,String> Parameters) {
152 return std::vector<String>();
153 }
154
155 optional<String> MidiInputDeviceAlsa::ParameterName::DefaultAsString(std::map<String,String> Parameters) {
156 return (existingAlsaDevices) ? "LinuxSampler" + ToString(existingAlsaDevices) : "LinuxSampler";
157 }
158
159 void MidiInputDeviceAlsa::ParameterName::OnSetValue(String s) throw (Exception) {
160 // not possible, as parameter is fix
161 }
162
163 String MidiInputDeviceAlsa::ParameterName::Name() {
164 return "NAME";
165 }
166
167
168
169 // *************** MidiInputPortAlsa ***************
170 // *
171
172 MidiInputDeviceAlsa::MidiInputPortAlsa::MidiInputPortAlsa(MidiInputDeviceAlsa* pDevice) throw (MidiInputException) : MidiInputPort(pDevice, -1) {
173 this->pDevice = pDevice;
174
175 // create Alsa sequencer port
176 int alsaPort = snd_seq_create_simple_port(pDevice->hAlsaSeq, "unnamed port",
177 SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
178 SND_SEQ_PORT_TYPE_APPLICATION);
179 if (alsaPort < 0) throw MidiInputException("Error creating sequencer port");
180 this->portNumber = alsaPort;
181
182 delete Parameters["NAME"];
183 Parameters["NAME"] = new ParameterName(this);
184 Parameters["ALSA_SEQ_BINDINGS"] = new ParameterAlsaSeqBindings(this);
185 Parameters["ALSA_SEQ_ID"] = new ParameterAlsaSeqId(this);
186 }
187
188 MidiInputDeviceAlsa::MidiInputPortAlsa::~MidiInputPortAlsa() {
189 UnsubscribeAll();
190 snd_seq_delete_simple_port(pDevice->hAlsaSeq, portNumber);
191 }
192
193 /**
194 * Connects this Alsa midi input device with an Alsa MIDI source.
195 *
196 * @param Client - Alsa sequencer client and port ID of a MIDI source
197 * (e.g. "64:0")
198 * @throws MidiInputException if connection cannot be established
199 */
200 void MidiInputDeviceAlsa::MidiInputPortAlsa::ConnectToAlsaMidiSource(const char* MidiSource) {
201 snd_seq_addr_t sender, dest;
202 snd_seq_port_subscribe_t* subs;
203 int hExtClient, hExtPort;
204
205 sscanf(MidiSource, "%d:%d", &hExtClient, &hExtPort);
206 sender.client = (char) hExtClient;
207 sender.port = (char) hExtPort;
208 dest.client = (char) pDevice->hAlsaSeqClient;
209 dest.port = (char) portNumber;
210 snd_seq_port_subscribe_malloc(&subs);
211 snd_seq_port_subscribe_set_sender(subs, &sender);
212 snd_seq_port_subscribe_set_dest(subs, &dest);
213 snd_seq_port_subscribe_set_queue(subs, 1);
214 snd_seq_port_subscribe_set_time_update(subs, 1);
215 snd_seq_port_subscribe_set_time_real(subs, 1);
216 if (snd_seq_subscribe_port(pDevice->hAlsaSeq, subs) < 0) {
217 snd_seq_port_subscribe_free(subs);
218 throw MidiInputException(String("Unable to connect to Alsa seq client \'") + MidiSource + "\' (" + snd_strerror(errno) + ")");
219 }
220
221 subscriptions.push_back(subs);
222 }
223
224 void MidiInputDeviceAlsa::MidiInputPortAlsa::UnsubscribeAll() {
225 for (std::vector<snd_seq_port_subscribe_t*>::iterator it = subscriptions.begin();
226 it != subscriptions.end(); it++) {
227 if (snd_seq_unsubscribe_port(pDevice->hAlsaSeq, *it)) {
228 dmsg(1,("MidiInputPortAlsa::UnsubscribeAll: Can't unsubscribe port connection!.\n"));
229 }
230 snd_seq_port_subscribe_free(*it);
231 }
232 subscriptions.clear();
233 }
234
235 // *************** MidiInputDeviceAlsa ***************
236 // *
237
238 MidiInputDeviceAlsa::MidiInputDeviceAlsa(std::map<String,DeviceCreationParameter*> Parameters, void* pSampler) : MidiInputDevice(Parameters, pSampler), Thread(true, true, 1, -1) {
239 if (snd_seq_open(&hAlsaSeq, "default", SND_SEQ_OPEN_INPUT, 0) < 0) {
240 throw MidiInputException("Error opening ALSA sequencer");
241 }
242 existingAlsaDevices++;
243 this->hAlsaSeqClient = snd_seq_client_id(hAlsaSeq);
244 snd_seq_set_client_name(hAlsaSeq, ((DeviceCreationParameterString*)Parameters["NAME"])->ValueAsString().c_str());
245 AcquirePorts(((DeviceCreationParameterInt*)Parameters["PORTS"])->ValueAsInt());
246 if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) {
247 Listen();
248 }
249 }
250
251 MidiInputDeviceAlsa::~MidiInputDeviceAlsa() {
252 // free the midi ports (we can't let the base class do this,
253 // as the MidiInputPortAlsa destructors need access to
254 // hAlsaSeq)
255 for (std::map<int,MidiInputPort*>::iterator iter = Ports.begin(); iter != Ports.end() ; iter++) {
256 delete static_cast<MidiInputPortAlsa*>(iter->second);
257 }
258 Ports.clear();
259
260 snd_seq_close(hAlsaSeq);
261 existingAlsaDevices--; //FIXME: this is too simple, can lead to multiple clients with the same name
262 }
263
264 MidiInputDeviceAlsa::MidiInputPortAlsa* MidiInputDeviceAlsa::CreateMidiPort() {
265 return new MidiInputPortAlsa(this);
266 }
267
268 String MidiInputDeviceAlsa::Name() {
269 return "ALSA";
270 }
271
272 String MidiInputDeviceAlsa::Driver() {
273 return Name();
274 }
275
276 void MidiInputDeviceAlsa::Listen() {
277 StartThread();
278 }
279
280 void MidiInputDeviceAlsa::StopListen() {
281 StopThread();
282 }
283
284 String MidiInputDeviceAlsa::Description() {
285 return "Advanced Linux Sound Architecture";
286 }
287
288 String MidiInputDeviceAlsa::Version() {
289 String s = "$Revision$";
290 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
291 }
292
293 int MidiInputDeviceAlsa::Main() {
294
295 #if DEBUG
296 Thread::setNameOfCaller("AlsaMidi");
297 #endif
298
299 int npfd;
300 struct pollfd* pfd;
301 snd_seq_event_t* ev;
302
303 npfd = snd_seq_poll_descriptors_count(hAlsaSeq, POLLIN);
304 pfd = (struct pollfd*) alloca(npfd * sizeof(struct pollfd));
305 snd_seq_poll_descriptors(hAlsaSeq, pfd, npfd, POLLIN);
306 while (true) {
307 // poll() is defined as thread cancelation point by POSIX
308 if (poll(pfd, npfd, 100000) > 0) {
309 do {
310 TestCancel();
311
312 // prevent thread from being cancelled
313 // (e.g. to prevent deadlocks while holding mutex lock(s))
314 pushCancelable(false);
315
316 snd_seq_event_input(hAlsaSeq, &ev);
317 int port = (int) ev->dest.port;
318 MidiInputPort* pMidiInputPort = Ports[port];
319
320 switch (ev->type) {
321 case SND_SEQ_EVENT_CONTROLLER:
322 if (ev->data.control.param == 0)
323 pMidiInputPort->DispatchBankSelectMsb(ev->data.control.value, ev->data.control.channel);
324 else if (ev->data.control.param == 32)
325 pMidiInputPort->DispatchBankSelectLsb(ev->data.control.value, ev->data.control.channel);
326 pMidiInputPort->DispatchControlChange(ev->data.control.param, ev->data.control.value, ev->data.control.channel);
327 break;
328
329 case SND_SEQ_EVENT_CHANPRESS:
330 pMidiInputPort->DispatchChannelPressure(ev->data.control.value, ev->data.control.channel);
331 break;
332
333 case SND_SEQ_EVENT_KEYPRESS:
334 pMidiInputPort->DispatchPolyphonicKeyPressure(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
335 break;
336
337 case SND_SEQ_EVENT_PITCHBEND:
338 pMidiInputPort->DispatchPitchbend(ev->data.control.value, ev->data.control.channel);
339 break;
340
341 case SND_SEQ_EVENT_NOTEON:
342 if (ev->data.note.velocity != 0) {
343 pMidiInputPort->DispatchNoteOn(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
344 }
345 else {
346 pMidiInputPort->DispatchNoteOff(ev->data.note.note, 0, ev->data.control.channel);
347 }
348 break;
349
350 case SND_SEQ_EVENT_NOTEOFF:
351 pMidiInputPort->DispatchNoteOff(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
352 break;
353
354 case SND_SEQ_EVENT_SYSEX:
355 pMidiInputPort->DispatchSysex(ev->data.ext.ptr, ev->data.ext.len);
356 break;
357
358 case SND_SEQ_EVENT_PGMCHANGE:
359 pMidiInputPort->DispatchProgramChange(ev->data.control.value, ev->data.control.channel);
360 break;
361 }
362 snd_seq_free_event(ev);
363
364 // now allow thread being cancelled again
365 // (since all mutexes are now unlocked)
366 popCancelable();
367
368 } while (snd_seq_event_input_pending(hAlsaSeq, 0) > 0);
369 }
370 }
371 // just to avoid a compiler warning
372 return EXIT_FAILURE;
373 }
374
375 } // namespace LinuxSampler

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC