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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1993 - (hide annotations) (download)
Sun Aug 30 11:27:35 2009 UTC (14 years, 8 months ago) by schoenebeck
File size: 15161 byte(s)
* ALSA MIDI driver supports now "NAME" device parameter, for overriding
  the ALSA sequencer client name

1 schoenebeck 201 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 schoenebeck 1993 * Copyright (C) 2005 - 2009 Christian Schoenebeck *
7 schoenebeck 201 * *
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 schoenebeck 226 #define perm_ok(pinfo,bits) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
28    
29 schoenebeck 201 namespace LinuxSampler {
30    
31 schoenebeck 1993 /// number of currently existing ALSA midi input devices in LinuxSampler
32     static int existingAlsaDevices = 0;
33    
34 schoenebeck 221 // *************** ParameterName ***************
35     // *
36    
37 schoenebeck 880 MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterName::ParameterName(MidiInputPort* pPort) throw (Exception) : MidiInputPort::ParameterName(pPort, "Port " + ToString(pPort->GetPortNumber())) {
38 schoenebeck 221 OnSetValue(ValueAsString()); // initialize port name
39 schoenebeck 201 }
40    
41 schoenebeck 880 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 schoenebeck 221 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 schoenebeck 201 }
50    
51 schoenebeck 221
52    
53     // *************** ParameterAlsaSeqBindings ***************
54     // *
55    
56    
57     MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::ParameterAlsaSeqBindings(MidiInputPortAlsa* pPort) : DeviceRuntimeParameterStrings( std::vector<String>() ) {
58     this->pPort = pPort;
59 schoenebeck 201 }
60    
61 schoenebeck 221 String MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::Description() {
62     return "Bindings to other Alsa sequencer clients";
63 schoenebeck 201 }
64 schoenebeck 221 bool MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::Fix() {
65     return false;
66     }
67 schoenebeck 201
68 schoenebeck 221 std::vector<String> MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::PossibilitiesAsString() {
69 schoenebeck 226 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 schoenebeck 201 }
90    
91 schoenebeck 880 void MidiInputDeviceAlsa::MidiInputPortAlsa::ParameterAlsaSeqBindings::OnSetValue(std::vector<String> vS) throw (Exception) {
92 persson 1951 pPort->UnsubscribeAll();
93 schoenebeck 1344
94 schoenebeck 221 std::vector<String>::iterator iter = vS.begin();
95     for (; iter != vS.end(); iter++) pPort->ConnectToAlsaMidiSource((*iter).c_str());
96 schoenebeck 201 }
97    
98 schoenebeck 221
99    
100 schoenebeck 476 // *************** 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 schoenebeck 1993 // *************** 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 schoenebeck 221 // *************** 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 persson 1248 delete Parameters["NAME"];
183 schoenebeck 221 Parameters["NAME"] = new ParameterName(this);
184     Parameters["ALSA_SEQ_BINDINGS"] = new ParameterAlsaSeqBindings(this);
185 schoenebeck 476 Parameters["ALSA_SEQ_ID"] = new ParameterAlsaSeqId(this);
186 schoenebeck 201 }
187    
188 schoenebeck 221 MidiInputDeviceAlsa::MidiInputPortAlsa::~MidiInputPortAlsa() {
189 persson 1951 UnsubscribeAll();
190     snd_seq_delete_simple_port(pDevice->hAlsaSeq, portNumber);
191 schoenebeck 201 }
192    
193     /**
194 schoenebeck 221 * 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 schoenebeck 201 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 iliev 1296 snd_seq_port_subscribe_malloc(&subs);
211 schoenebeck 201 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 iliev 1296 if (snd_seq_subscribe_port(pDevice->hAlsaSeq, subs) < 0) {
217     snd_seq_port_subscribe_free(subs);
218 schoenebeck 201 throw MidiInputException(String("Unable to connect to Alsa seq client \'") + MidiSource + "\' (" + snd_strerror(errno) + ")");
219 iliev 1296 }
220    
221     subscriptions.push_back(subs);
222 schoenebeck 201 }
223    
224 persson 1951 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 schoenebeck 221
235     // *************** MidiInputDeviceAlsa ***************
236     // *
237    
238 schoenebeck 551 MidiInputDeviceAlsa::MidiInputDeviceAlsa(std::map<String,DeviceCreationParameter*> Parameters, void* pSampler) : MidiInputDevice(Parameters, pSampler), Thread(true, true, 1, -1) {
239 schoenebeck 221 if (snd_seq_open(&hAlsaSeq, "default", SND_SEQ_OPEN_INPUT, 0) < 0) {
240     throw MidiInputException("Error opening ALSA sequencer");
241     }
242 schoenebeck 1993 existingAlsaDevices++;
243 schoenebeck 221 this->hAlsaSeqClient = snd_seq_client_id(hAlsaSeq);
244 schoenebeck 1993 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 schoenebeck 221 }
250    
251     MidiInputDeviceAlsa::~MidiInputDeviceAlsa() {
252 persson 1248 // 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 schoenebeck 1993 existingAlsaDevices--; //FIXME: this is too simple, can lead to multiple clients with the same name
262 schoenebeck 221 }
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 schoenebeck 201 String MidiInputDeviceAlsa::Description() {
285     return "Advanced Linux Sound Architecture";
286     }
287    
288     String MidiInputDeviceAlsa::Version() {
289 schoenebeck 1993 String s = "$Revision: 1.24 $";
290 schoenebeck 201 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 schoenebeck 221 int port = (int) ev->dest.port;
306     MidiInputPort* pMidiInputPort = Ports[port];
307 schoenebeck 201
308     switch (ev->type) {
309     case SND_SEQ_EVENT_CONTROLLER:
310 schoenebeck 947 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 schoenebeck 1715 pMidiInputPort->DispatchControlChange(ev->data.control.param, ev->data.control.value, ev->data.control.channel);
315 schoenebeck 201 break;
316    
317 persson 903 case SND_SEQ_EVENT_CHANPRESS:
318     pMidiInputPort->DispatchControlChange(128, ev->data.control.value, ev->data.control.channel);
319     break;
320    
321 schoenebeck 201 case SND_SEQ_EVENT_PITCHBEND:
322     pMidiInputPort->DispatchPitchbend(ev->data.control.value, ev->data.control.channel);
323     break;
324    
325     case SND_SEQ_EVENT_NOTEON:
326     if (ev->data.note.velocity != 0) {
327     pMidiInputPort->DispatchNoteOn(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
328     }
329     else {
330     pMidiInputPort->DispatchNoteOff(ev->data.note.note, 0, ev->data.control.channel);
331     }
332     break;
333    
334     case SND_SEQ_EVENT_NOTEOFF:
335     pMidiInputPort->DispatchNoteOff(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
336     break;
337 schoenebeck 244
338     case SND_SEQ_EVENT_SYSEX:
339     pMidiInputPort->DispatchSysex(ev->data.ext.ptr, ev->data.ext.len);
340     break;
341 schoenebeck 551
342     case SND_SEQ_EVENT_PGMCHANGE:
343     pMidiInputPort->DispatchProgramChange(ev->data.control.value, ev->data.control.channel);
344     break;
345 schoenebeck 201 }
346     snd_seq_free_event(ev);
347     } while (snd_seq_event_input_pending(hAlsaSeq, 0) > 0);
348     }
349     }
350 schoenebeck 1344 // just to avoid a compiler warning
351     return EXIT_FAILURE;
352 schoenebeck 201 }
353    
354     } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC