/[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 1951 - (show annotations) (download)
Wed Jul 29 15:31:09 2009 UTC (14 years, 9 months ago) by persson
File size: 13391 byte(s)
* ASIO fixes: avoid initializing the device twice, avoid throwing
  exception when getting parameters from a disconnected device
* Windows: add the installation directory to the DLL search path when
  loading an editor plugin (solves problems with VST and gigedit on
  systems with other GTK versions installed)
* fixed minor memory leak in ALSA MIDI driver

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2008 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 // *************** ParameterName ***************
32 // *
33
34 MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterName::ParameterName(MidiInputPort* pPort) throw (Exception) : MidiInputPort::ParameterName(pPort, "Port " + ToString(pPort->GetPortNumber())) {
35 OnSetValue(ValueAsString()); // initialize port name
36 }
37
38 void MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterName::OnSetValue(String s) throw (Exception) {
39 if (s.size() > 16) throw Exception("Name too long for ALSA MIDI input port (max. 16 characters)");
40 snd_seq_port_info_t* hInfo;
41 snd_seq_port_info_malloc(&hInfo);
42 snd_seq_get_port_info(((MidiInputDeviceAlsa*)pPort->GetDevice())->hAlsaSeq, pPort->GetPortNumber(), hInfo);
43 snd_seq_port_info_set_name(hInfo, s.c_str());
44 snd_seq_set_port_info(((MidiInputDeviceAlsa*)pPort->GetDevice())->hAlsaSeq, pPort->GetPortNumber(), hInfo);
45 snd_seq_port_info_free(hInfo);
46 }
47
48
49
50 // *************** ParameterAlsaSeqBindings ***************
51 // *
52
53
54 MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::ParameterAlsaSeqBindings(MidiInputPortAlsa* pPort) : DeviceRuntimeParameterStrings( std::vector<String>() ) {
55 this->pPort = pPort;
56 }
57
58 String MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::Description() {
59 return "Bindings to other Alsa sequencer clients";
60 }
61 bool MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::Fix() {
62 return false;
63 }
64
65 std::vector<String> MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::PossibilitiesAsString() {
66 std::vector<String> res;
67 snd_seq_client_info_t* cinfo;
68 snd_seq_port_info_t* pinfo;
69
70 snd_seq_client_info_alloca(&cinfo);
71 snd_seq_port_info_alloca(&pinfo);
72 snd_seq_client_info_set_client(cinfo, -1);
73 while (snd_seq_query_next_client(pPort->pDevice->hAlsaSeq, cinfo) >= 0) {
74 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
75 snd_seq_port_info_set_port(pinfo, -1);
76 while (snd_seq_query_next_port(pPort->pDevice->hAlsaSeq, pinfo) >= 0) {
77 if (perm_ok(pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ)) {
78 String seq_id = ToString(snd_seq_client_info_get_client(cinfo)) + ":" +
79 ToString(snd_seq_port_info_get_port(pinfo));
80 res.push_back(seq_id);
81 }
82 }
83 }
84
85 return res;
86 }
87
88 void MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::OnSetValue(std::vector<String> vS) throw (Exception) {
89 pPort->UnsubscribeAll();
90
91 std::vector<String>::iterator iter = vS.begin();
92 for (; iter != vS.end(); iter++) pPort->ConnectToAlsaMidiSource((*iter).c_str());
93 }
94
95
96
97 // *************** ParameterAlsaSeqId ***************
98 // *
99
100 MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::ParameterAlsaSeqId(MidiInputPortAlsa* pPort)
101 : DeviceRuntimeParameterString(ToString(pPort->pDevice->hAlsaSeqClient) + ":" + ToString(pPort->portNumber)) {
102 }
103
104 String MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::Description() {
105 return "ALSA Sequencer ID";
106 }
107
108 bool MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::Fix() {
109 return true;
110 }
111
112 std::vector<String> MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::PossibilitiesAsString() {
113 return std::vector<String>(); // nothing
114 }
115
116 void MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqId::OnSetValue(String s) {
117 // not possible as parameter is 'fix'
118 }
119
120
121
122 // *************** MidiInputPortAlsa ***************
123 // *
124
125 MidiInputDeviceAlsa::MidiInputPortAlsa::MidiInputPortAlsa(MidiInputDeviceAlsa* pDevice) throw (MidiInputException) : MidiInputPort(pDevice, -1) {
126 this->pDevice = pDevice;
127
128 // create Alsa sequencer port
129 int alsaPort = snd_seq_create_simple_port(pDevice->hAlsaSeq, "unnamed port",
130 SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
131 SND_SEQ_PORT_TYPE_APPLICATION);
132 if (alsaPort < 0) throw MidiInputException("Error creating sequencer port");
133 this->portNumber = alsaPort;
134
135 delete Parameters["NAME"];
136 Parameters["NAME"] = new ParameterName(this);
137 Parameters["ALSA_SEQ_BINDINGS"] = new ParameterAlsaSeqBindings(this);
138 Parameters["ALSA_SEQ_ID"] = new ParameterAlsaSeqId(this);
139 }
140
141 MidiInputDeviceAlsa::MidiInputPortAlsa::~MidiInputPortAlsa() {
142 UnsubscribeAll();
143 snd_seq_delete_simple_port(pDevice->hAlsaSeq, portNumber);
144 }
145
146 /**
147 * Connects this Alsa midi input device with an Alsa MIDI source.
148 *
149 * @param Client - Alsa sequencer client and port ID of a MIDI source
150 * (e.g. "64:0")
151 * @throws MidiInputException if connection cannot be established
152 */
153 void MidiInputDeviceAlsa::MidiInputPortAlsa::ConnectToAlsaMidiSource(const char* MidiSource) {
154 snd_seq_addr_t sender, dest;
155 snd_seq_port_subscribe_t* subs;
156 int hExtClient, hExtPort;
157
158 sscanf(MidiSource, "%d:%d", &hExtClient, &hExtPort);
159 sender.client = (char) hExtClient;
160 sender.port = (char) hExtPort;
161 dest.client = (char) pDevice->hAlsaSeqClient;
162 dest.port = (char) portNumber;
163 snd_seq_port_subscribe_malloc(&subs);
164 snd_seq_port_subscribe_set_sender(subs, &sender);
165 snd_seq_port_subscribe_set_dest(subs, &dest);
166 snd_seq_port_subscribe_set_queue(subs, 1);
167 snd_seq_port_subscribe_set_time_update(subs, 1);
168 snd_seq_port_subscribe_set_time_real(subs, 1);
169 if (snd_seq_subscribe_port(pDevice->hAlsaSeq, subs) < 0) {
170 snd_seq_port_subscribe_free(subs);
171 throw MidiInputException(String("Unable to connect to Alsa seq client \'") + MidiSource + "\' (" + snd_strerror(errno) + ")");
172 }
173
174 subscriptions.push_back(subs);
175 }
176
177 void MidiInputDeviceAlsa::MidiInputPortAlsa::UnsubscribeAll() {
178 for (std::vector<snd_seq_port_subscribe_t*>::iterator it = subscriptions.begin();
179 it != subscriptions.end(); it++) {
180 if (snd_seq_unsubscribe_port(pDevice->hAlsaSeq, *it)) {
181 dmsg(1,("MidiInputPortAlsa::UnsubscribeAll: Can't unsubscribe port connection!.\n"));
182 }
183 snd_seq_port_subscribe_free(*it);
184 }
185 subscriptions.clear();
186 }
187
188 // *************** MidiInputDeviceAlsa ***************
189 // *
190
191 MidiInputDeviceAlsa::MidiInputDeviceAlsa(std::map<String,DeviceCreationParameter*> Parameters, void* pSampler) : MidiInputDevice(Parameters, pSampler), Thread(true, true, 1, -1) {
192 if (snd_seq_open(&hAlsaSeq, "default", SND_SEQ_OPEN_INPUT, 0) < 0) {
193 throw MidiInputException("Error opening ALSA sequencer");
194 }
195 this->hAlsaSeqClient = snd_seq_client_id(hAlsaSeq);
196 snd_seq_set_client_name(hAlsaSeq, "LinuxSampler");
197 AcquirePorts(((DeviceCreationParameterInt*)Parameters["PORTS"])->ValueAsInt());
198 if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) {
199 Listen();
200 }
201 }
202
203 MidiInputDeviceAlsa::~MidiInputDeviceAlsa() {
204 // free the midi ports (we can't let the base class do this,
205 // as the MidiInputPortAlsa destructors need access to
206 // hAlsaSeq)
207 for (std::map<int,MidiInputPort*>::iterator iter = Ports.begin(); iter != Ports.end() ; iter++) {
208 delete static_cast<MidiInputPortAlsa*>(iter->second);
209 }
210 Ports.clear();
211
212 snd_seq_close(hAlsaSeq);
213 }
214
215 MidiInputDeviceAlsa::MidiInputPortAlsa* MidiInputDeviceAlsa::CreateMidiPort() {
216 return new MidiInputPortAlsa(this);
217 }
218
219 String MidiInputDeviceAlsa::Name() {
220 return "ALSA";
221 }
222
223 String MidiInputDeviceAlsa::Driver() {
224 return Name();
225 }
226
227 void MidiInputDeviceAlsa::Listen() {
228 StartThread();
229 }
230
231 void MidiInputDeviceAlsa::StopListen() {
232 StopThread();
233 }
234
235 String MidiInputDeviceAlsa::Description() {
236 return "Advanced Linux Sound Architecture";
237 }
238
239 String MidiInputDeviceAlsa::Version() {
240 String s = "$Revision: 1.23 $";
241 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
242 }
243
244 int MidiInputDeviceAlsa::Main() {
245 int npfd;
246 struct pollfd* pfd;
247 snd_seq_event_t* ev;
248
249 npfd = snd_seq_poll_descriptors_count(hAlsaSeq, POLLIN);
250 pfd = (struct pollfd*) alloca(npfd * sizeof(struct pollfd));
251 snd_seq_poll_descriptors(hAlsaSeq, pfd, npfd, POLLIN);
252 while (true) {
253 if (poll(pfd, npfd, 100000) > 0) {
254 do {
255 snd_seq_event_input(hAlsaSeq, &ev);
256 int port = (int) ev->dest.port;
257 MidiInputPort* pMidiInputPort = Ports[port];
258
259 switch (ev->type) {
260 case SND_SEQ_EVENT_CONTROLLER:
261 if (ev->data.control.param == 0)
262 pMidiInputPort->DispatchBankSelectMsb(ev->data.control.value, ev->data.control.channel);
263 else if (ev->data.control.param == 32)
264 pMidiInputPort->DispatchBankSelectLsb(ev->data.control.value, ev->data.control.channel);
265 pMidiInputPort->DispatchControlChange(ev->data.control.param, ev->data.control.value, ev->data.control.channel);
266 break;
267
268 case SND_SEQ_EVENT_CHANPRESS:
269 pMidiInputPort->DispatchControlChange(128, ev->data.control.value, ev->data.control.channel);
270 break;
271
272 case SND_SEQ_EVENT_PITCHBEND:
273 pMidiInputPort->DispatchPitchbend(ev->data.control.value, ev->data.control.channel);
274 break;
275
276 case SND_SEQ_EVENT_NOTEON:
277 if (ev->data.note.velocity != 0) {
278 pMidiInputPort->DispatchNoteOn(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
279 }
280 else {
281 pMidiInputPort->DispatchNoteOff(ev->data.note.note, 0, ev->data.control.channel);
282 }
283 break;
284
285 case SND_SEQ_EVENT_NOTEOFF:
286 pMidiInputPort->DispatchNoteOff(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
287 break;
288
289 case SND_SEQ_EVENT_SYSEX:
290 pMidiInputPort->DispatchSysex(ev->data.ext.ptr, ev->data.ext.len);
291 break;
292
293 case SND_SEQ_EVENT_PGMCHANGE:
294 pMidiInputPort->DispatchProgramChange(ev->data.control.value, ev->data.control.channel);
295 break;
296 }
297 snd_seq_free_event(ev);
298 } while (snd_seq_event_input_pending(hAlsaSeq, 0) > 0);
299 }
300 }
301 // just to avoid a compiler warning
302 return EXIT_FAILURE;
303 }
304
305 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC