1 /*
2 * MidiClient.cpp - base-class for MIDI-clients like ALSA-sequencer-client
3 *
4 * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5 * This file partly contains code from Fluidsynth, Peter Hanappe
6 *
7 * This file is part of LMMS - https://lmms.io
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this program (see COPYING); if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301 USA.
23 *
24 */
25
26 #include "MidiClient.h"
27 #include "MidiPort.h"
28 #include "Note.h"
29
30
MidiClient()31 MidiClient::MidiClient()
32 {
33 }
34
35
36
37
~MidiClient()38 MidiClient::~MidiClient()
39 {
40 //TODO: noteOffAll(); / clear all ports
41 for (MidiPort* port : m_midiPorts)
42 {
43 port->invalidateCilent();
44 }
45 }
46
47
48
49
applyPortMode(MidiPort *)50 void MidiClient::applyPortMode( MidiPort* )
51 {
52 }
53
54
55
56
applyPortName(MidiPort *)57 void MidiClient::applyPortName( MidiPort* )
58 {
59 }
60
61
62
63
addPort(MidiPort * port)64 void MidiClient::addPort( MidiPort* port )
65 {
66 m_midiPorts.push_back( port );
67 }
68
69
70
71
removePort(MidiPort * port)72 void MidiClient::removePort( MidiPort* port )
73 {
74 if( ! port )
75 {
76 return;
77 }
78
79 QVector<MidiPort *>::Iterator it =
80 qFind( m_midiPorts.begin(), m_midiPorts.end(), port );
81 if( it != m_midiPorts.end() )
82 {
83 m_midiPorts.erase( it );
84 }
85 }
86
87
88
89
subscribeReadablePort(MidiPort *,const QString &,bool)90 void MidiClient::subscribeReadablePort( MidiPort*, const QString& , bool )
91 {
92 }
93
94
95
96
subscribeWritablePort(MidiPort *,const QString &,bool)97 void MidiClient::subscribeWritablePort( MidiPort* , const QString& , bool )
98 {
99 }
100
101
102
103
104
105
106
MidiClientRaw()107 MidiClientRaw::MidiClientRaw()
108 {
109 }
110
111
112
113
~MidiClientRaw()114 MidiClientRaw::~MidiClientRaw()
115 {
116 }
117
118
119
120
parseData(const unsigned char c)121 void MidiClientRaw::parseData( const unsigned char c )
122 {
123 /*********************************************************************/
124 /* 'Process' system real-time messages */
125 /*********************************************************************/
126 /* There are not too many real-time messages that are of interest here.
127 * They can occur anywhere, even in the middle of a noteon message!
128 * Real-time range: 0xF8 .. 0xFF
129 * Note: Real-time does not affect (running) status.
130 */
131 if( c >= 0xF8 )
132 {
133 if( c == MidiSystemReset )
134 {
135 m_midiParseData.m_midiEvent.setType( MidiSystemReset );
136 m_midiParseData.m_status = 0;
137 processParsedEvent();
138 }
139 return;
140 }
141
142 /*********************************************************************/
143 /* 'Process' system common messages (again, just skip them) */
144 /*********************************************************************/
145 /* There are no system common messages that are of interest here.
146 * System common range: 0xF0 .. 0xF7
147 */
148 if( c > 0xF0 )
149 {
150 /* MIDI spec say: To ignore a non-real-time message, just discard all
151 * data up to the next status byte. And our parser will ignore data
152 * that is received without a valid status.
153 * Note: system common cancels running status. */
154 m_midiParseData.m_status = 0;
155 return;
156 }
157
158 /*********************************************************************/
159 /* Process voice category messages: */
160 /*********************************************************************/
161 /* Now that we have handled realtime and system common messages, only
162 * voice messages are left.
163 * Only a status byte has bit # 7 set.
164 * So no matter the status of the parser (in case we have lost sync),
165 * as soon as a byte >= 0x80 comes in, we are dealing with a status byte
166 * and start a new event.
167 */
168 if( c & 0x80 )
169 {
170 m_midiParseData.m_channel = c & 0x0F;
171 m_midiParseData.m_status = c & 0xF0;
172 /* The event consumes x bytes of data...
173 (subtract 1 for the status byte) */
174 m_midiParseData.m_bytesTotal = eventLength( m_midiParseData.m_status ) - 1;
175 /* of which we have read 0 at this time. */
176 m_midiParseData.m_bytes = 0;
177 return;
178 }
179
180 /*********************************************************************/
181 /* Process data */
182 /*********************************************************************/
183 /* If we made it this far, then the received char belongs to the data
184 * of the last event. */
185 if( m_midiParseData.m_status == 0 )
186 {
187 /* We are not interested in the event currently received.
188 Discard the data. */
189 return;
190 }
191
192 /* Store the first couple of bytes */
193 if( m_midiParseData.m_bytes < RAW_MIDI_PARSE_BUF_SIZE )
194 {
195 m_midiParseData.m_buffer[m_midiParseData.m_bytes] = c;
196 }
197 ++m_midiParseData.m_bytes;
198
199 /* Do we still need more data to get this event complete? */
200 if( m_midiParseData.m_bytes < m_midiParseData.m_bytesTotal )
201 {
202 return;
203 }
204
205 /*********************************************************************/
206 /* Send the event */
207 /*********************************************************************/
208 /* The event is ready-to-go. About 'running status':
209 *
210 * The MIDI protocol has a built-in compression mechanism. If several
211 * similar events are sent in-a-row, for example note-ons, then the
212 * event type is only sent once. For this case, the last event type
213 * (status) is remembered.
214 * We simply keep the status as it is, just reset the parameter counter.
215 * If another status byte comes in, it will overwrite the status.
216 */
217 m_midiParseData.m_midiEvent.setType( static_cast<MidiEventTypes>( m_midiParseData.m_status ) );
218 m_midiParseData.m_midiEvent.setChannel( m_midiParseData.m_channel );
219 m_midiParseData.m_bytes = 0; /* Related to running status! */
220 switch( m_midiParseData.m_midiEvent.type() )
221 {
222 case MidiNoteOff:
223 case MidiNoteOn:
224 case MidiKeyPressure:
225 case MidiChannelPressure:
226 m_midiParseData.m_midiEvent.setKey( m_midiParseData.m_buffer[0] - KeysPerOctave );
227 m_midiParseData.m_midiEvent.setVelocity( m_midiParseData.m_buffer[1] );
228 break;
229
230 case MidiProgramChange:
231 m_midiParseData.m_midiEvent.setKey( m_midiParseData.m_buffer[0] );
232 m_midiParseData.m_midiEvent.setVelocity( m_midiParseData.m_buffer[1] );
233 break;
234
235 case MidiControlChange:
236 m_midiParseData.m_midiEvent.setControllerNumber( m_midiParseData.m_buffer[0] );
237 m_midiParseData.m_midiEvent.setControllerValue( m_midiParseData.m_buffer[1] );
238 break;
239
240 case MidiPitchBend:
241 // Pitch-bend is transmitted with 14-bit precision.
242 // Note: '|' does here the same as '+' (no common bits),
243 // but might be faster
244 m_midiParseData.m_midiEvent.setPitchBend( ( m_midiParseData.m_buffer[1] * 128 ) | m_midiParseData.m_buffer[0] );
245 break;
246
247 default:
248 // Unlikely
249 return;
250 }
251
252 processParsedEvent();
253 }
254
255
256
257
processParsedEvent()258 void MidiClientRaw::processParsedEvent()
259 {
260 for( int i = 0; i < m_midiPorts.size(); ++i )
261 {
262 m_midiPorts[i]->processInEvent( m_midiParseData.m_midiEvent );
263 }
264 }
265
266
267
268
processOutEvent(const MidiEvent & event,const MidiTime &,const MidiPort * port)269 void MidiClientRaw::processOutEvent( const MidiEvent& event, const MidiTime & , const MidiPort* port )
270 {
271 // TODO: also evaluate _time and queue event if necessary
272 switch( event.type() )
273 {
274 case MidiNoteOn:
275 case MidiNoteOff:
276 case MidiKeyPressure:
277 sendByte( event.type() | event.channel() );
278 sendByte( event.key() + KeysPerOctave );
279 sendByte( event.velocity() );
280 break;
281
282 default:
283 qWarning( "MidiClientRaw: unhandled MIDI-event %d\n",
284 (int) event.type() );
285 break;
286 }
287 }
288
289
290
291
292
293
294 // Taken from Nagano Daisuke's USB-MIDI driver
295 static const unsigned char REMAINS_F0F6[] =
296 {
297 0, /* 0xF0 */
298 2, /* 0XF1 */
299 3, /* 0XF2 */
300 2, /* 0XF3 */
301 2, /* 0XF4 (Undefined by MIDI Spec, and subject to change) */
302 2, /* 0XF5 (Undefined by MIDI Spec, and subject to change) */
303 1 /* 0XF6 */
304 } ;
305
306 static const unsigned char REMAINS_80E0[] =
307 {
308 3, /* 0x8X Note Off */
309 3, /* 0x9X Note On */
310 3, /* 0xAX Poly-key pressure */
311 3, /* 0xBX Control Change */
312 2, /* 0xCX Program Change */
313 2, /* 0xDX Channel pressure */
314 3 /* 0xEX PitchBend Change */
315 } ;
316
317
318
319 // Returns the length of the MIDI message starting with _event.
320 // Taken from Nagano Daisuke's USB-MIDI driver
eventLength(const unsigned char event)321 int MidiClientRaw::eventLength( const unsigned char event )
322 {
323 if ( event < 0xF0 )
324 {
325 return REMAINS_80E0[( ( event - 0x80 ) >> 4 ) & 0x0F];
326 }
327 else if ( event < 0xF7 )
328 {
329 return REMAINS_F0F6[event - 0xF0];
330 }
331 return 1;
332 }
333
334
335