/[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 1715 - (hide annotations) (download)
Tue Mar 11 15:20:46 2008 UTC (16 years, 1 month ago) by schoenebeck
File size: 13283 byte(s)
* dispatch bank select as ordinary CC as well, the user might seriously
  want to (mis)use it for some purpose ("fixed" in all current MIDI
  input drivers: ALSA, CoreMIDI, JACK, MidiShare, MME)
* minor fix: only mark FX sends as being modified if really the
  respective FX send MIDI controller was used

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

  ViewVC Help
Powered by ViewVC