drumstick
1.0.2
|
00001 /* 00002 Drumstick RT Backend using the ALSA Sequencer 00003 Copyright (C) 2009-2015 Pedro Lopez-Cabanillas <plcl@users.sf.net> 00004 00005 This program is free software; you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or 00008 (at your option) any later version. 00009 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License along 00016 with this program; if not, write to the Free Software Foundation, Inc., 00017 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include <QMutexLocker> 00021 #include <cmath> 00022 #include "alsaclient.h" 00023 #include "alsaport.h" 00024 #include "alsaevent.h" 00025 #include "alsamidiinput.h" 00026 #include "rtmidioutput.h" 00027 00028 namespace drumstick { 00029 namespace rt { 00030 00031 static QString DEFAULT_PUBLIC_NAME(QLatin1String("MIDI In")); 00032 00033 class ALSAMIDIInput::ALSAMIDIInputPrivate : public SequencerEventHandler 00034 { 00035 public: 00036 ALSAMIDIInput *m_inp; 00037 MIDIOutput *m_out; 00038 MidiClient *m_client; 00039 MidiPort *m_port; 00040 int m_portId; 00041 int m_clientId; 00042 bool m_thruEnabled; 00043 bool m_clientFilter; 00044 int m_runtimeAlsaNum; 00045 QString m_publicName; 00046 QString m_currentInput; 00047 QStringList m_inputDevices; 00048 QStringList m_excludedNames; 00049 QMutex m_openMutex; 00050 00051 ALSAMIDIInputPrivate(ALSAMIDIInput *inp) : 00052 m_inp(inp), 00053 m_out(0), 00054 m_client(0), 00055 m_port(0), 00056 m_portId(-1), 00057 m_clientId(-1), 00058 m_thruEnabled(false), 00059 m_publicName(DEFAULT_PUBLIC_NAME) 00060 { 00061 m_runtimeAlsaNum = getRuntimeALSALibraryNumber(); 00062 m_client = new MidiClient(m_inp); 00063 m_client->open(); 00064 m_client->setClientName(m_publicName); 00065 m_port = m_client->createPort(); 00066 m_port->setPortName("in"); 00067 m_port->setCapability( SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE ); 00068 m_port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC ); 00069 m_clientId = m_client->getClientId(); 00070 m_portId = m_port->getPortId(); 00071 m_port->setTimestamping(false); 00072 m_port->setTimestampReal(false); 00073 m_client->setHandler(this); 00074 } 00075 00076 virtual ~ALSAMIDIInputPrivate() 00077 { 00078 if (m_client != NULL) { 00079 clearSubscription(); 00080 if (m_port != NULL) 00081 m_port->detach(); 00082 m_client->close(); 00083 delete m_client; 00084 } 00085 } 00086 00087 bool clientIsAdvanced(int clientId) 00088 { 00089 // asking for runtime version instead of SND_LIB_VERSION 00090 if (m_runtimeAlsaNum < 0x01000B) 00091 // ALSA <= 1.0.10 00092 return (clientId < 64); 00093 else 00094 // ALSA >= 1.0.11 00095 return (clientId < 16); 00096 } 00097 00098 void reloadDeviceList(bool advanced) 00099 { 00100 m_clientFilter = !advanced; 00101 m_inputDevices.clear(); 00102 QListIterator<PortInfo> it(m_client->getAvailableInputs()); 00103 while(it.hasNext()) { 00104 bool excluded = false; 00105 PortInfo p = it.next(); 00106 QString name = QString("%1:%2").arg(p.getClientName()).arg(p.getPort()); 00107 if (m_clientFilter && clientIsAdvanced(p.getClient())) 00108 continue; 00109 if ( m_clientFilter && name.startsWith(QLatin1String("Virtual Raw MIDI")) ) 00110 continue; 00111 if ( name.startsWith(m_publicName) ) 00112 continue; 00113 foreach(const QString& n, m_excludedNames) { 00114 if (name.startsWith(n)) { 00115 excluded = true; 00116 break; 00117 } 00118 } 00119 if (!excluded) 00120 m_inputDevices << name; 00121 } 00122 if (!m_currentInput.isEmpty() && !m_inputDevices.contains(m_currentInput)) { 00123 m_currentInput.clear(); 00124 } 00125 } 00126 00127 bool setSubscription(const QString &newDevice) 00128 { 00129 //qDebug() << Q_FUNC_INFO << newDevice; 00130 if (m_inputDevices.contains(newDevice)) { 00131 m_currentInput = newDevice; 00132 m_port->unsubscribeAll(); 00133 m_port->subscribeFrom(newDevice); 00134 m_client->startSequencerInput(); 00135 return true; 00136 } 00137 return false; 00138 } 00139 00140 void clearSubscription() 00141 { 00142 if (!m_currentInput.isEmpty()) { 00143 m_client->stopSequencerInput(); 00144 m_port->unsubscribeAll(); 00145 m_currentInput.clear(); 00146 } 00147 } 00148 00149 void setPublicName(QString newName) 00150 { 00151 if (newName != m_publicName) { 00152 m_client->setClientName(newName); 00153 m_publicName = newName; 00154 } 00155 } 00156 00157 void handleSequencerEvent(SequencerEvent* ev) 00158 { 00159 if ( !SequencerEvent::isConnectionChange(ev) ) 00160 switch(ev->getSequencerType()) { 00161 case SND_SEQ_EVENT_NOTEOFF: { 00162 const NoteOffEvent* n = static_cast<const NoteOffEvent*>(ev); 00163 if(m_out != 0 && m_thruEnabled) { 00164 m_out->sendNoteOff(n->getChannel(), n->getKey(), n->getVelocity()); 00165 } 00166 emit m_inp->midiNoteOff(n->getChannel(), n->getKey(), n->getVelocity()); 00167 } 00168 break; 00169 case SND_SEQ_EVENT_NOTEON: { 00170 const NoteOnEvent* n = static_cast<const NoteOnEvent*>(ev); 00171 if(m_out != 0 && m_thruEnabled) { 00172 m_out->sendNoteOn(n->getChannel(), n->getKey(), n->getVelocity()); 00173 } 00174 emit m_inp->midiNoteOn(n->getChannel(), n->getKey(), n->getVelocity()); 00175 } 00176 break; 00177 case SND_SEQ_EVENT_KEYPRESS: { 00178 const KeyPressEvent* n = static_cast<const KeyPressEvent*>(ev); 00179 if(m_out != 0 && m_thruEnabled) { 00180 m_out->sendKeyPressure(n->getChannel(), n->getKey(), n->getVelocity()); 00181 } 00182 emit m_inp->midiKeyPressure(n->getChannel(), n->getKey(), n->getVelocity()); 00183 } 00184 break; 00185 case SND_SEQ_EVENT_CONTROLLER: 00186 case SND_SEQ_EVENT_CONTROL14: { 00187 const ControllerEvent* n = static_cast<const ControllerEvent*>(ev); 00188 if(m_out != 0 && m_thruEnabled) { 00189 m_out->sendController(n->getChannel(), n->getParam(), n->getValue()); 00190 } 00191 emit m_inp->midiController(n->getChannel(), n->getParam(), n->getValue()); 00192 } 00193 break; 00194 case SND_SEQ_EVENT_PGMCHANGE: { 00195 const ProgramChangeEvent* p = static_cast<const ProgramChangeEvent*>(ev); 00196 if(m_out != 0 && m_thruEnabled) { 00197 m_out->sendProgram(p->getChannel(), p->getValue()); 00198 } 00199 emit m_inp->midiProgram(p->getChannel(), p->getValue()); 00200 } 00201 break; 00202 case SND_SEQ_EVENT_CHANPRESS: { 00203 const ChanPressEvent* n = static_cast<const ChanPressEvent*>(ev); 00204 if(m_out != 0 && m_thruEnabled) { 00205 m_out->sendChannelPressure(n->getChannel(), n->getValue()); 00206 } 00207 emit m_inp->midiChannelPressure(n->getChannel(), n->getValue()); 00208 } 00209 break; 00210 case SND_SEQ_EVENT_PITCHBEND: { 00211 const PitchBendEvent* n = static_cast<const PitchBendEvent*>(ev); 00212 if(m_out != 0 && m_thruEnabled) { 00213 m_out->sendPitchBend(n->getChannel(), n->getValue()); 00214 } 00215 emit m_inp->midiPitchBend(n->getChannel(), n->getValue()); 00216 } 00217 break; 00218 case SND_SEQ_EVENT_SYSEX: { 00219 const SysExEvent* n = static_cast<const SysExEvent*>(ev); 00220 QByteArray data(n->getData(), n->getLength()); 00221 if(m_out != 0 && m_thruEnabled) { 00222 m_out->sendSysex(data); 00223 } 00224 emit m_inp->midiSysex(data); 00225 } 00226 break; 00227 case SND_SEQ_EVENT_SYSTEM: { 00228 const SystemEvent* n = static_cast<const SystemEvent*>(ev); 00229 int status = (int) n->getRaw8(0); 00230 if(m_out != 0 && m_thruEnabled) { 00231 m_out->sendSystemMsg(status); 00232 } 00233 if (status < 0xF7) 00234 emit m_inp->midiSystemCommon(status); 00235 else if (status > 0xF7) 00236 emit m_inp->midiSystemRealtime(status); 00237 } 00238 break; 00239 default: 00240 break; 00241 } 00242 delete ev; 00243 } 00244 }; 00245 00246 ALSAMIDIInput::ALSAMIDIInput(QObject *parent) : MIDIInput(parent), 00247 d(new ALSAMIDIInputPrivate(this)) 00248 { } 00249 00250 ALSAMIDIInput::~ALSAMIDIInput() 00251 { 00252 delete d; 00253 } 00254 00255 void ALSAMIDIInput::initialize(QSettings* settings) 00256 { 00257 Q_UNUSED(settings) 00258 } 00259 00260 QString ALSAMIDIInput::backendName() 00261 { 00262 return QLatin1String("ALSA"); 00263 } 00264 00265 QString ALSAMIDIInput::publicName() 00266 { 00267 return d->m_publicName; 00268 } 00269 00270 void ALSAMIDIInput::setPublicName(QString name) 00271 { 00272 d->setPublicName(name); 00273 } 00274 00275 QStringList ALSAMIDIInput::connections(bool advanced) 00276 { 00277 d->reloadDeviceList(advanced); 00278 return d->m_inputDevices; 00279 } 00280 00281 void ALSAMIDIInput::setExcludedConnections(QStringList conns) 00282 { 00283 d->m_excludedNames = conns; 00284 } 00285 00286 void ALSAMIDIInput::open(QString name) 00287 { 00288 d->setSubscription(name); 00289 } 00290 00291 void ALSAMIDIInput::close() 00292 { 00293 d->clearSubscription(); 00294 } 00295 00296 QString ALSAMIDIInput::currentConnection() 00297 { 00298 return d->m_currentInput; 00299 } 00300 00301 void ALSAMIDIInput::setMIDIThruDevice(MIDIOutput *device) 00302 { 00303 d->m_out = device; 00304 } 00305 00306 void ALSAMIDIInput::enableMIDIThru(bool enable) 00307 { 00308 d->m_thruEnabled = enable; 00309 } 00310 00311 bool ALSAMIDIInput::isEnabledMIDIThru() 00312 { 00313 return d->m_thruEnabled && (d->m_out != 0); 00314 } 00315 00316 }}