/[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 2559 - (hide annotations) (download)
Sun May 18 17:38:25 2014 UTC (10 years 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 schoenebeck 201 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 schoenebeck 2559 * Copyright (C) 2005 - 2014 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 2494 String s = "$Revision$";
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 schoenebeck 2559 pMidiInputPort->DispatchChannelPressure(ev->data.control.value, ev->data.control.channel);
319 persson 903 break;
320    
321 schoenebeck 2559 case SND_SEQ_EVENT_KEYPRESS:
322     pMidiInputPort->DispatchPolyphonicKeyPressure(ev->data.note.note, ev->data.note.velocity, ev->data.control.channel);
323     break;
324    
325 schoenebeck 201 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 schoenebeck 244
342     case SND_SEQ_EVENT_SYSEX:
343     pMidiInputPort->DispatchSysex(ev->data.ext.ptr, ev->data.ext.len);
344     break;
345 schoenebeck 551
346     case SND_SEQ_EVENT_PGMCHANGE:
347     pMidiInputPort->DispatchProgramChange(ev->data.control.value, ev->data.control.channel);
348     break;
349 schoenebeck 201 }
350     snd_seq_free_event(ev);
351     } while (snd_seq_event_input_pending(hAlsaSeq, 0) > 0);
352     }
353     }
354 schoenebeck 1344 // just to avoid a compiler warning
355     return EXIT_FAILURE;
356 schoenebeck 201 }
357    
358     } // namespace LinuxSampler

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC