/[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 2559 - (show annotations) (download)
Sun May 18 17:38:25 2014 UTC (9 years, 11 months ago) by schoenebeck
File size: 15385 byte(s)
* Aftertouch: extended API to explicitly handle channel pressure and
  polyphonic key pressure events (so far polyphonic pressure was not
  supported at all, and channel pressure was rerouted as CC128 but not
  used so far).
* Gig Engine: Fixed support for 'aftertouch' attenuation controller.
* Bumped version (1.0.0.svn39).

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2014 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 int npfd;
295 struct pollfd* pfd;
296 snd_seq_event_t* ev;
297
298 npfd = snd_seq_poll_descriptors_count(hAlsaSeq, POLLIN);
299 pfd = (struct pollfd*) alloca(npfd * sizeof(struct pollfd));
300 snd_seq_poll_descriptors(hAlsaSeq, pfd, npfd, POLLIN);
301 while (true) {
302 if (poll(pfd, npfd, 100000) > 0) {
303 do {
304 snd_seq_event_input(hAlsaSeq, &ev);
305 int port = (int) ev->dest.port;
306 MidiInputPort* pMidiInputPort = Ports[port];
307
308 switch (ev->type) {
309 case SND_SEQ_EVENT_CONTROLLER:
310 if (ev->data.control.param == 0)
311 pMidiInputPort->DispatchBankSelectMsb(ev->data.control.value, ev->data.control.channel);
312 else if (ev->data.control.param == 32)
313 pMidiInputPort->DispatchBankSelectLsb(ev->data.control.value, ev->data.control.channel);
314 pMidiInputPort->DispatchControlChange(ev->data.control.param, ev->data.control.value, ev->data.control.channel);
315 break;
316
317 case SND_SEQ_EVENT_CHANPRESS:
318 pMidiInputPort->DispatchChannelPressure(ev->data.control.value, ev->data.control.channel);
319 break;
320
321 case SND_SEQ_EVENT_KEYPRESS:
322 pMidiInputPort->DispatchPolyphonicKeyPressure(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
323 break;
324
325 case SND_SEQ_EVENT_PITCHBEND:
326 pMidiInputPort->DispatchPitchbend(ev->data.control.value, ev->data.control.channel);
327 break;
328
329 case SND_SEQ_EVENT_NOTEON:
330 if (ev->data.note.velocity != 0) {
331 pMidiInputPort->DispatchNoteOn(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
332 }
333 else {
334 pMidiInputPort->DispatchNoteOff(ev->data.note.note, 0, ev->data.control.channel);
335 }
336 break;
337
338 case SND_SEQ_EVENT_NOTEOFF:
339 pMidiInputPort->DispatchNoteOff(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
340 break;
341
342 case SND_SEQ_EVENT_SYSEX:
343 pMidiInputPort->DispatchSysex(ev->data.ext.ptr, ev->data.ext.len);
344 break;
345
346 case SND_SEQ_EVENT_PGMCHANGE:
347 pMidiInputPort->DispatchProgramChange(ev->data.control.value, ev->data.control.channel);
348 break;
349 }
350 snd_seq_free_event(ev);
351 } while (snd_seq_event_input_pending(hAlsaSeq, 0) > 0);
352 }
353 }
354 // just to avoid a compiler warning
355 return EXIT_FAILURE;
356 }
357
358 } // namespace LinuxSampler

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC