Chris@559
|
1 /**********************************************************************/
|
Chris@559
|
2 /*! \class RtMidi
|
Chris@559
|
3 \brief An abstract base class for realtime MIDI input/output.
|
Chris@559
|
4
|
Chris@559
|
5 This class implements some common functionality for the realtime
|
Chris@559
|
6 MIDI input/output subclasses RtMidiIn and RtMidiOut.
|
Chris@559
|
7
|
Chris@559
|
8 RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
|
Chris@559
|
9
|
Chris@559
|
10 RtMidi: realtime MIDI i/o C++ classes
|
Chris@1397
|
11 Copyright (c) 2003-2016 Gary P. Scavone
|
Chris@559
|
12
|
Chris@559
|
13 Permission is hereby granted, free of charge, to any person
|
Chris@559
|
14 obtaining a copy of this software and associated documentation files
|
Chris@559
|
15 (the "Software"), to deal in the Software without restriction,
|
Chris@559
|
16 including without limitation the rights to use, copy, modify, merge,
|
Chris@559
|
17 publish, distribute, sublicense, and/or sell copies of the Software,
|
Chris@559
|
18 and to permit persons to whom the Software is furnished to do so,
|
Chris@559
|
19 subject to the following conditions:
|
Chris@559
|
20
|
Chris@559
|
21 The above copyright notice and this permission notice shall be
|
Chris@559
|
22 included in all copies or substantial portions of the Software.
|
Chris@559
|
23
|
Chris@559
|
24 Any person wishing to distribute modifications to the Software is
|
Chris@1397
|
25 asked to send the modifications to the original developer so that
|
Chris@1397
|
26 they can be incorporated into the canonical version. This is,
|
Chris@1397
|
27 however, not a binding provision of this license.
|
Chris@559
|
28
|
Chris@559
|
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
Chris@559
|
30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
Chris@559
|
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
Chris@559
|
32 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
Chris@559
|
33 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
Chris@559
|
34 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
Chris@559
|
35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
Chris@559
|
36 */
|
Chris@559
|
37 /**********************************************************************/
|
Chris@559
|
38
|
Chris@559
|
39 #include "RtMidi.h"
|
Chris@559
|
40 #include <sstream>
|
Chris@559
|
41
|
Chris@1402
|
42 // CC be gung-ho about this here, assume upstream has it in hand
|
Chris@1402
|
43 #pragma GCC diagnostic ignored "-Wconversion"
|
Chris@1402
|
44
|
Chris@1397
|
45 #if defined(__MACOSX_CORE__)
|
Chris@1397
|
46 #if TARGET_OS_IPHONE
|
Chris@1397
|
47 #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
|
Chris@1397
|
48 #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos
|
Chris@1397
|
49 #endif
|
Chris@1397
|
50 #endif
|
Chris@843
|
51
|
Chris@559
|
52 //*********************************************************************//
|
Chris@1397
|
53 // RtMidi Definitions
|
Chris@559
|
54 //*********************************************************************//
|
Chris@559
|
55
|
Chris@559
|
56 RtMidi :: RtMidi()
|
Chris@1397
|
57 : rtapi_(0)
|
Chris@559
|
58 {
|
Chris@559
|
59 }
|
Chris@559
|
60
|
Chris@1397
|
61 RtMidi :: ~RtMidi()
|
Chris@559
|
62 {
|
Chris@1397
|
63 if ( rtapi_ )
|
Chris@1397
|
64 delete rtapi_;
|
Chris@1397
|
65 rtapi_ = 0;
|
Chris@1397
|
66 }
|
Chris@1397
|
67
|
Chris@1397
|
68 std::string RtMidi :: getVersion( void ) throw()
|
Chris@1397
|
69 {
|
Chris@1397
|
70 return std::string( RTMIDI_VERSION );
|
Chris@1397
|
71 }
|
Chris@1397
|
72
|
Chris@1397
|
73 void RtMidi :: getCompiledApi( std::vector<RtMidi::Api> &apis ) throw()
|
Chris@1397
|
74 {
|
Chris@1397
|
75 apis.clear();
|
Chris@1397
|
76
|
Chris@1397
|
77 // The order here will control the order of RtMidi's API search in
|
Chris@1397
|
78 // the constructor.
|
Chris@1397
|
79 #if defined(__MACOSX_CORE__)
|
Chris@1397
|
80 apis.push_back( MACOSX_CORE );
|
Chris@1397
|
81 #endif
|
Chris@1397
|
82 #if defined(__LINUX_ALSA__)
|
Chris@1397
|
83 apis.push_back( LINUX_ALSA );
|
Chris@1397
|
84 #endif
|
Chris@1397
|
85 #if defined(__UNIX_JACK__)
|
Chris@1397
|
86 apis.push_back( UNIX_JACK );
|
Chris@1397
|
87 #endif
|
Chris@1397
|
88 #if defined(__WINDOWS_MM__)
|
Chris@1397
|
89 apis.push_back( WINDOWS_MM );
|
Chris@1397
|
90 #endif
|
Chris@1397
|
91 #if defined(__RTMIDI_DUMMY__)
|
Chris@1397
|
92 apis.push_back( RTMIDI_DUMMY );
|
Chris@1397
|
93 #endif
|
Chris@1397
|
94 }
|
Chris@1397
|
95
|
Chris@1397
|
96 //*********************************************************************//
|
Chris@1397
|
97 // RtMidiIn Definitions
|
Chris@1397
|
98 //*********************************************************************//
|
Chris@1397
|
99
|
Chris@1397
|
100 void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit )
|
Chris@1397
|
101 {
|
Chris@1397
|
102 if ( rtapi_ )
|
Chris@1397
|
103 delete rtapi_;
|
Chris@1397
|
104 rtapi_ = 0;
|
Chris@1397
|
105
|
Chris@1397
|
106 #if defined(__UNIX_JACK__)
|
Chris@1397
|
107 if ( api == UNIX_JACK )
|
Chris@1397
|
108 rtapi_ = new MidiInJack( clientName, queueSizeLimit );
|
Chris@1397
|
109 #endif
|
Chris@1397
|
110 #if defined(__LINUX_ALSA__)
|
Chris@1397
|
111 if ( api == LINUX_ALSA )
|
Chris@1397
|
112 rtapi_ = new MidiInAlsa( clientName, queueSizeLimit );
|
Chris@1397
|
113 #endif
|
Chris@1397
|
114 #if defined(__WINDOWS_MM__)
|
Chris@1397
|
115 if ( api == WINDOWS_MM )
|
Chris@1397
|
116 rtapi_ = new MidiInWinMM( clientName, queueSizeLimit );
|
Chris@1397
|
117 #endif
|
Chris@1397
|
118 #if defined(__MACOSX_CORE__)
|
Chris@1397
|
119 if ( api == MACOSX_CORE )
|
Chris@1397
|
120 rtapi_ = new MidiInCore( clientName, queueSizeLimit );
|
Chris@1397
|
121 #endif
|
Chris@1397
|
122 #if defined(__RTMIDI_DUMMY__)
|
Chris@1397
|
123 if ( api == RTMIDI_DUMMY )
|
Chris@1397
|
124 rtapi_ = new MidiInDummy( clientName, queueSizeLimit );
|
Chris@1397
|
125 #endif
|
Chris@1397
|
126 }
|
Chris@1397
|
127
|
Chris@1397
|
128 RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit )
|
Chris@1397
|
129 : RtMidi()
|
Chris@1397
|
130 {
|
Chris@1397
|
131 if ( api != UNSPECIFIED ) {
|
Chris@1397
|
132 // Attempt to open the specified API.
|
Chris@1397
|
133 openMidiApi( api, clientName, queueSizeLimit );
|
Chris@1397
|
134 if ( rtapi_ ) return;
|
Chris@1397
|
135
|
Chris@1397
|
136 // No compiled support for specified API value. Issue a warning
|
Chris@1397
|
137 // and continue as if no API was specified.
|
Chris@1397
|
138 std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl;
|
Chris@559
|
139 }
|
Chris@1397
|
140
|
Chris@1397
|
141 // Iterate through the compiled APIs and return as soon as we find
|
Chris@1397
|
142 // one with at least one port or we reach the end of the list.
|
Chris@1397
|
143 std::vector< RtMidi::Api > apis;
|
Chris@1397
|
144 getCompiledApi( apis );
|
Chris@1397
|
145 for ( unsigned int i=0; i<apis.size(); i++ ) {
|
Chris@1397
|
146 openMidiApi( apis[i], clientName, queueSizeLimit );
|
Chris@1397
|
147 if ( rtapi_->getPortCount() ) break;
|
Chris@1397
|
148 }
|
Chris@1397
|
149
|
Chris@1397
|
150 if ( rtapi_ ) return;
|
Chris@1397
|
151
|
Chris@1397
|
152 // It should not be possible to get here because the preprocessor
|
Chris@1397
|
153 // definition __RTMIDI_DUMMY__ is automatically defined if no
|
Chris@1397
|
154 // API-specific definitions are passed to the compiler. But just in
|
Chris@1397
|
155 // case something weird happens, we'll throw an error.
|
Chris@1397
|
156 std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!";
|
Chris@1397
|
157 throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );
|
Chris@1397
|
158 }
|
Chris@1397
|
159
|
Chris@1397
|
160 RtMidiIn :: ~RtMidiIn() throw()
|
Chris@1397
|
161 {
|
Chris@1397
|
162 }
|
Chris@1397
|
163
|
Chris@1397
|
164
|
Chris@1397
|
165 //*********************************************************************//
|
Chris@1397
|
166 // RtMidiOut Definitions
|
Chris@1397
|
167 //*********************************************************************//
|
Chris@1397
|
168
|
Chris@1397
|
169 void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string clientName )
|
Chris@1397
|
170 {
|
Chris@1397
|
171 if ( rtapi_ )
|
Chris@1397
|
172 delete rtapi_;
|
Chris@1397
|
173 rtapi_ = 0;
|
Chris@1397
|
174
|
Chris@1397
|
175 #if defined(__UNIX_JACK__)
|
Chris@1397
|
176 if ( api == UNIX_JACK )
|
Chris@1397
|
177 rtapi_ = new MidiOutJack( clientName );
|
Chris@1397
|
178 #endif
|
Chris@1397
|
179 #if defined(__LINUX_ALSA__)
|
Chris@1397
|
180 if ( api == LINUX_ALSA )
|
Chris@1397
|
181 rtapi_ = new MidiOutAlsa( clientName );
|
Chris@1397
|
182 #endif
|
Chris@1397
|
183 #if defined(__WINDOWS_MM__)
|
Chris@1397
|
184 if ( api == WINDOWS_MM )
|
Chris@1397
|
185 rtapi_ = new MidiOutWinMM( clientName );
|
Chris@1397
|
186 #endif
|
Chris@1397
|
187 #if defined(__MACOSX_CORE__)
|
Chris@1397
|
188 if ( api == MACOSX_CORE )
|
Chris@1397
|
189 rtapi_ = new MidiOutCore( clientName );
|
Chris@1397
|
190 #endif
|
Chris@1397
|
191 #if defined(__RTMIDI_DUMMY__)
|
Chris@1397
|
192 if ( api == RTMIDI_DUMMY )
|
Chris@1397
|
193 rtapi_ = new MidiOutDummy( clientName );
|
Chris@1397
|
194 #endif
|
Chris@1397
|
195 }
|
Chris@1397
|
196
|
Chris@1397
|
197 RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string clientName )
|
Chris@1397
|
198 {
|
Chris@1397
|
199 if ( api != UNSPECIFIED ) {
|
Chris@1397
|
200 // Attempt to open the specified API.
|
Chris@1397
|
201 openMidiApi( api, clientName );
|
Chris@1397
|
202 if ( rtapi_ ) return;
|
Chris@1397
|
203
|
Chris@1397
|
204 // No compiled support for specified API value. Issue a warning
|
Chris@1397
|
205 // and continue as if no API was specified.
|
Chris@1397
|
206 std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl;
|
Chris@1397
|
207 }
|
Chris@1397
|
208
|
Chris@1397
|
209 // Iterate through the compiled APIs and return as soon as we find
|
Chris@1397
|
210 // one with at least one port or we reach the end of the list.
|
Chris@1397
|
211 std::vector< RtMidi::Api > apis;
|
Chris@1397
|
212 getCompiledApi( apis );
|
Chris@1397
|
213 for ( unsigned int i=0; i<apis.size(); i++ ) {
|
Chris@1397
|
214 openMidiApi( apis[i], clientName );
|
Chris@1397
|
215 if ( rtapi_->getPortCount() ) break;
|
Chris@1397
|
216 }
|
Chris@1397
|
217
|
Chris@1397
|
218 if ( rtapi_ ) return;
|
Chris@1397
|
219
|
Chris@1397
|
220 // It should not be possible to get here because the preprocessor
|
Chris@1397
|
221 // definition __RTMIDI_DUMMY__ is automatically defined if no
|
Chris@1397
|
222 // API-specific definitions are passed to the compiler. But just in
|
Chris@1397
|
223 // case something weird happens, we'll thrown an error.
|
Chris@1397
|
224 std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!";
|
Chris@1397
|
225 throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) );
|
Chris@1397
|
226 }
|
Chris@1397
|
227
|
Chris@1397
|
228 RtMidiOut :: ~RtMidiOut() throw()
|
Chris@1397
|
229 {
|
Chris@1397
|
230 }
|
Chris@1397
|
231
|
Chris@1397
|
232 //*********************************************************************//
|
Chris@1397
|
233 // Common MidiApi Definitions
|
Chris@1397
|
234 //*********************************************************************//
|
Chris@1397
|
235
|
Chris@1397
|
236 MidiApi :: MidiApi( void )
|
Chris@1397
|
237 : apiData_( 0 ), connected_( false ), errorCallback_(0), errorCallbackUserData_(0)
|
Chris@1397
|
238 {
|
Chris@1397
|
239 }
|
Chris@1397
|
240
|
Chris@1397
|
241 MidiApi :: ~MidiApi( void )
|
Chris@1397
|
242 {
|
Chris@1397
|
243 }
|
Chris@1397
|
244
|
Chris@1397
|
245 void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 )
|
Chris@1397
|
246 {
|
Chris@1397
|
247 errorCallback_ = errorCallback;
|
Chris@1397
|
248 errorCallbackUserData_ = userData;
|
Chris@1397
|
249 }
|
Chris@1397
|
250
|
Chris@1397
|
251 void MidiApi :: error( RtMidiError::Type type, std::string errorString )
|
Chris@1397
|
252 {
|
Chris@1397
|
253 if ( errorCallback_ ) {
|
Chris@1397
|
254
|
Chris@1397
|
255 if ( firstErrorOccurred_ )
|
Chris@1397
|
256 return;
|
Chris@1397
|
257
|
Chris@1397
|
258 firstErrorOccurred_ = true;
|
Chris@1397
|
259 const std::string errorMessage = errorString;
|
Chris@1397
|
260
|
Chris@1397
|
261 errorCallback_( type, errorMessage, errorCallbackUserData_);
|
Chris@1397
|
262 firstErrorOccurred_ = false;
|
Chris@1397
|
263 return;
|
Chris@1397
|
264 }
|
Chris@1397
|
265
|
Chris@1397
|
266 if ( type == RtMidiError::WARNING ) {
|
Chris@1397
|
267 std::cerr << '\n' << errorString << "\n\n";
|
Chris@1397
|
268 }
|
Chris@1397
|
269 else if ( type == RtMidiError::DEBUG_WARNING ) {
|
Chris@559
|
270 #if defined(__RTMIDI_DEBUG__)
|
Chris@1397
|
271 std::cerr << '\n' << errorString << "\n\n";
|
Chris@559
|
272 #endif
|
Chris@559
|
273 }
|
Chris@559
|
274 else {
|
Chris@1397
|
275 std::cerr << '\n' << errorString << "\n\n";
|
Chris@1397
|
276 throw RtMidiError( errorString, type );
|
Chris@559
|
277 }
|
Chris@559
|
278 }
|
Chris@559
|
279
|
Chris@559
|
280 //*********************************************************************//
|
Chris@1397
|
281 // Common MidiInApi Definitions
|
Chris@559
|
282 //*********************************************************************//
|
Chris@559
|
283
|
Chris@1397
|
284 MidiInApi :: MidiInApi( unsigned int queueSizeLimit )
|
Chris@1397
|
285 : MidiApi()
|
Chris@559
|
286 {
|
Chris@1397
|
287 // Allocate the MIDI queue.
|
Chris@1397
|
288 inputData_.queue.ringSize = queueSizeLimit;
|
Chris@1397
|
289 if ( inputData_.queue.ringSize > 0 )
|
Chris@1397
|
290 inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ];
|
Chris@559
|
291 }
|
Chris@559
|
292
|
Chris@1397
|
293 MidiInApi :: ~MidiInApi( void )
|
Chris@1397
|
294 {
|
Chris@1397
|
295 // Delete the MIDI queue.
|
Chris@1397
|
296 if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring;
|
Chris@1397
|
297 }
|
Chris@1397
|
298
|
Chris@1397
|
299 void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData )
|
Chris@559
|
300 {
|
Chris@559
|
301 if ( inputData_.usingCallback ) {
|
Chris@1397
|
302 errorString_ = "MidiInApi::setCallback: a callback function is already set!";
|
Chris@1397
|
303 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
304 return;
|
Chris@559
|
305 }
|
Chris@559
|
306
|
Chris@559
|
307 if ( !callback ) {
|
Chris@559
|
308 errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";
|
Chris@1397
|
309 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
310 return;
|
Chris@559
|
311 }
|
Chris@559
|
312
|
Chris@1397
|
313 inputData_.userCallback = callback;
|
Chris@559
|
314 inputData_.userData = userData;
|
Chris@559
|
315 inputData_.usingCallback = true;
|
Chris@559
|
316 }
|
Chris@559
|
317
|
Chris@1397
|
318 void MidiInApi :: cancelCallback()
|
Chris@559
|
319 {
|
Chris@559
|
320 if ( !inputData_.usingCallback ) {
|
Chris@559
|
321 errorString_ = "RtMidiIn::cancelCallback: no callback function was set!";
|
Chris@1397
|
322 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
323 return;
|
Chris@559
|
324 }
|
Chris@559
|
325
|
Chris@559
|
326 inputData_.userCallback = 0;
|
Chris@559
|
327 inputData_.userData = 0;
|
Chris@559
|
328 inputData_.usingCallback = false;
|
Chris@559
|
329 }
|
Chris@559
|
330
|
Chris@1397
|
331 void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense )
|
Chris@559
|
332 {
|
Chris@559
|
333 inputData_.ignoreFlags = 0;
|
Chris@559
|
334 if ( midiSysex ) inputData_.ignoreFlags = 0x01;
|
Chris@559
|
335 if ( midiTime ) inputData_.ignoreFlags |= 0x02;
|
Chris@559
|
336 if ( midiSense ) inputData_.ignoreFlags |= 0x04;
|
Chris@559
|
337 }
|
Chris@559
|
338
|
Chris@1397
|
339 double MidiInApi :: getMessage( std::vector<unsigned char> *message )
|
Chris@559
|
340 {
|
Chris@559
|
341 message->clear();
|
Chris@559
|
342
|
Chris@559
|
343 if ( inputData_.usingCallback ) {
|
Chris@559
|
344 errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port.";
|
Chris@1397
|
345 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
346 return 0.0;
|
Chris@559
|
347 }
|
Chris@559
|
348
|
Chris@1397
|
349 if ( inputData_.queue.size == 0 ) return 0.0;
|
Chris@559
|
350
|
Chris@559
|
351 // Copy queued message to the vector pointer argument and then "pop" it.
|
Chris@1397
|
352 std::vector<unsigned char> *bytes = &(inputData_.queue.ring[inputData_.queue.front].bytes);
|
Chris@559
|
353 message->assign( bytes->begin(), bytes->end() );
|
Chris@1397
|
354 double deltaTime = inputData_.queue.ring[inputData_.queue.front].timeStamp;
|
Chris@1397
|
355 inputData_.queue.size--;
|
Chris@1397
|
356 inputData_.queue.front++;
|
Chris@1397
|
357 if ( inputData_.queue.front == inputData_.queue.ringSize )
|
Chris@1397
|
358 inputData_.queue.front = 0;
|
Chris@559
|
359
|
Chris@559
|
360 return deltaTime;
|
Chris@559
|
361 }
|
Chris@559
|
362
|
Chris@559
|
363 //*********************************************************************//
|
Chris@1397
|
364 // Common MidiOutApi Definitions
|
Chris@559
|
365 //*********************************************************************//
|
Chris@559
|
366
|
Chris@1397
|
367 MidiOutApi :: MidiOutApi( void )
|
Chris@1397
|
368 : MidiApi()
|
Chris@559
|
369 {
|
Chris@559
|
370 }
|
Chris@559
|
371
|
Chris@1397
|
372 MidiOutApi :: ~MidiOutApi( void )
|
Chris@1397
|
373 {
|
Chris@1397
|
374 }
|
Chris@1397
|
375
|
Chris@1397
|
376 // *************************************************** //
|
Chris@1397
|
377 //
|
Chris@1397
|
378 // OS/API-specific methods.
|
Chris@1397
|
379 //
|
Chris@1397
|
380 // *************************************************** //
|
Chris@559
|
381
|
Chris@559
|
382 #if defined(__MACOSX_CORE__)
|
Chris@559
|
383
|
Chris@559
|
384 // The CoreMIDI API is based on the use of a callback function for
|
Chris@559
|
385 // MIDI input. We convert the system specific time stamps to delta
|
Chris@559
|
386 // time values.
|
Chris@559
|
387
|
Chris@559
|
388 // OS-X CoreMIDI header files.
|
Chris@559
|
389 #include <CoreMIDI/CoreMIDI.h>
|
Chris@559
|
390 #include <CoreAudio/HostTime.h>
|
Chris@1397
|
391 #include <CoreServices/CoreServices.h>
|
Chris@559
|
392
|
Chris@559
|
393 // A structure to hold variables related to the CoreMIDI API
|
Chris@559
|
394 // implementation.
|
Chris@559
|
395 struct CoreMidiData {
|
Chris@559
|
396 MIDIClientRef client;
|
Chris@559
|
397 MIDIPortRef port;
|
Chris@559
|
398 MIDIEndpointRef endpoint;
|
Chris@559
|
399 MIDIEndpointRef destinationId;
|
Chris@559
|
400 unsigned long long lastTime;
|
Chris@1397
|
401 MIDISysexSendRequest sysexreq;
|
Chris@559
|
402 };
|
Chris@559
|
403
|
Chris@559
|
404 //*********************************************************************//
|
Chris@559
|
405 // API: OS-X
|
Chris@1397
|
406 // Class Definitions: MidiInCore
|
Chris@559
|
407 //*********************************************************************//
|
Chris@559
|
408
|
Chris@1397
|
409 static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ )
|
Chris@559
|
410 {
|
Chris@1397
|
411 MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (procRef);
|
Chris@559
|
412 CoreMidiData *apiData = static_cast<CoreMidiData *> (data->apiData);
|
Chris@559
|
413
|
Chris@559
|
414 unsigned char status;
|
Chris@559
|
415 unsigned short nBytes, iByte, size;
|
Chris@559
|
416 unsigned long long time;
|
Chris@565
|
417
|
Chris@565
|
418 bool& continueSysex = data->continueSysex;
|
Chris@1397
|
419 MidiInApi::MidiMessage& message = data->message;
|
Chris@565
|
420
|
Chris@559
|
421 const MIDIPacket *packet = &list->packet[0];
|
Chris@559
|
422 for ( unsigned int i=0; i<list->numPackets; ++i ) {
|
Chris@559
|
423
|
Chris@559
|
424 // My interpretation of the CoreMIDI documentation: all message
|
Chris@559
|
425 // types, except sysex, are complete within a packet and there may
|
Chris@559
|
426 // be several of them in a single packet. Sysex messages can be
|
Chris@565
|
427 // broken across multiple packets and PacketLists but are bundled
|
Chris@565
|
428 // alone within each packet (these packets do not contain other
|
Chris@565
|
429 // message types). If sysex messages are split across multiple
|
Chris@565
|
430 // MIDIPacketLists, they must be handled by multiple calls to this
|
Chris@565
|
431 // function.
|
Chris@559
|
432
|
Chris@559
|
433 nBytes = packet->length;
|
Chris@559
|
434 if ( nBytes == 0 ) continue;
|
Chris@559
|
435
|
Chris@559
|
436 // Calculate time stamp.
|
Chris@1397
|
437
|
Chris@1397
|
438 if ( data->firstMessage ) {
|
Chris@1397
|
439 message.timeStamp = 0.0;
|
Chris@559
|
440 data->firstMessage = false;
|
Chris@1397
|
441 }
|
Chris@559
|
442 else {
|
Chris@559
|
443 time = packet->timeStamp;
|
Chris@1397
|
444 if ( time == 0 ) { // this happens when receiving asynchronous sysex messages
|
Chris@1397
|
445 time = AudioGetCurrentHostTime();
|
Chris@1397
|
446 }
|
Chris@559
|
447 time -= apiData->lastTime;
|
Chris@559
|
448 time = AudioConvertHostTimeToNanos( time );
|
Chris@1397
|
449 if ( !continueSysex )
|
Chris@1397
|
450 message.timeStamp = time * 0.000000001;
|
Chris@559
|
451 }
|
Chris@559
|
452 apiData->lastTime = packet->timeStamp;
|
Chris@1397
|
453 if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages
|
Chris@1397
|
454 apiData->lastTime = AudioGetCurrentHostTime();
|
Chris@1397
|
455 }
|
Chris@1397
|
456 //std::cout << "TimeStamp = " << packet->timeStamp << std::endl;
|
Chris@559
|
457
|
Chris@559
|
458 iByte = 0;
|
Chris@559
|
459 if ( continueSysex ) {
|
Chris@559
|
460 // We have a continuing, segmented sysex message.
|
Chris@565
|
461 if ( !( data->ignoreFlags & 0x01 ) ) {
|
Chris@559
|
462 // If we're not ignoring sysex messages, copy the entire packet.
|
Chris@1397
|
463 for ( unsigned int j=0; j<nBytes; ++j )
|
Chris@559
|
464 message.bytes.push_back( packet->data[j] );
|
Chris@559
|
465 }
|
Chris@565
|
466 continueSysex = packet->data[nBytes-1] != 0xF7;
|
Chris@565
|
467
|
Chris@1397
|
468 if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) {
|
Chris@559
|
469 // If not a continuing sysex message, invoke the user callback function or queue the message.
|
Chris@1397
|
470 if ( data->usingCallback ) {
|
Chris@559
|
471 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
|
Chris@559
|
472 callback( message.timeStamp, &message.bytes, data->userData );
|
Chris@559
|
473 }
|
Chris@559
|
474 else {
|
Chris@559
|
475 // As long as we haven't reached our queue size limit, push the message.
|
Chris@1397
|
476 if ( data->queue.size < data->queue.ringSize ) {
|
Chris@1397
|
477 data->queue.ring[data->queue.back++] = message;
|
Chris@1397
|
478 if ( data->queue.back == data->queue.ringSize )
|
Chris@1397
|
479 data->queue.back = 0;
|
Chris@1397
|
480 data->queue.size++;
|
Chris@1397
|
481 }
|
Chris@559
|
482 else
|
Chris@1397
|
483 std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";
|
Chris@559
|
484 }
|
Chris@559
|
485 message.bytes.clear();
|
Chris@559
|
486 }
|
Chris@559
|
487 }
|
Chris@559
|
488 else {
|
Chris@559
|
489 while ( iByte < nBytes ) {
|
Chris@559
|
490 size = 0;
|
Chris@559
|
491 // We are expecting that the next byte in the packet is a status byte.
|
Chris@559
|
492 status = packet->data[iByte];
|
Chris@559
|
493 if ( !(status & 0x80) ) break;
|
Chris@559
|
494 // Determine the number of bytes in the MIDI message.
|
Chris@559
|
495 if ( status < 0xC0 ) size = 3;
|
Chris@559
|
496 else if ( status < 0xE0 ) size = 2;
|
Chris@559
|
497 else if ( status < 0xF0 ) size = 3;
|
Chris@559
|
498 else if ( status == 0xF0 ) {
|
Chris@559
|
499 // A MIDI sysex
|
Chris@559
|
500 if ( data->ignoreFlags & 0x01 ) {
|
Chris@559
|
501 size = 0;
|
Chris@559
|
502 iByte = nBytes;
|
Chris@559
|
503 }
|
Chris@559
|
504 else size = nBytes - iByte;
|
Chris@1397
|
505 continueSysex = packet->data[nBytes-1] != 0xF7;
|
Chris@559
|
506 }
|
Chris@1397
|
507 else if ( status == 0xF1 ) {
|
Chris@1397
|
508 // A MIDI time code message
|
Chris@1397
|
509 if ( data->ignoreFlags & 0x02 ) {
|
Chris@559
|
510 size = 0;
|
Chris@1397
|
511 iByte += 2;
|
Chris@1397
|
512 }
|
Chris@1397
|
513 else size = 2;
|
Chris@559
|
514 }
|
Chris@1397
|
515 else if ( status == 0xF2 ) size = 3;
|
Chris@559
|
516 else if ( status == 0xF3 ) size = 2;
|
Chris@1397
|
517 else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) {
|
Chris@1397
|
518 // A MIDI timing tick message and we're ignoring it.
|
Chris@1397
|
519 size = 0;
|
Chris@1397
|
520 iByte += 1;
|
Chris@559
|
521 }
|
Chris@1397
|
522 else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) {
|
Chris@559
|
523 // A MIDI active sensing message and we're ignoring it.
|
Chris@559
|
524 size = 0;
|
Chris@559
|
525 iByte += 1;
|
Chris@559
|
526 }
|
Chris@559
|
527 else size = 1;
|
Chris@559
|
528
|
Chris@559
|
529 // Copy the MIDI data to our vector.
|
Chris@559
|
530 if ( size ) {
|
Chris@559
|
531 message.bytes.assign( &packet->data[iByte], &packet->data[iByte+size] );
|
Chris@559
|
532 if ( !continueSysex ) {
|
Chris@559
|
533 // If not a continuing sysex message, invoke the user callback function or queue the message.
|
Chris@559
|
534 if ( data->usingCallback ) {
|
Chris@559
|
535 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
|
Chris@559
|
536 callback( message.timeStamp, &message.bytes, data->userData );
|
Chris@559
|
537 }
|
Chris@559
|
538 else {
|
Chris@559
|
539 // As long as we haven't reached our queue size limit, push the message.
|
Chris@1397
|
540 if ( data->queue.size < data->queue.ringSize ) {
|
Chris@1397
|
541 data->queue.ring[data->queue.back++] = message;
|
Chris@1397
|
542 if ( data->queue.back == data->queue.ringSize )
|
Chris@1397
|
543 data->queue.back = 0;
|
Chris@1397
|
544 data->queue.size++;
|
Chris@1397
|
545 }
|
Chris@559
|
546 else
|
Chris@1397
|
547 std::cerr << "\nMidiInCore: message queue limit reached!!\n\n";
|
Chris@559
|
548 }
|
Chris@559
|
549 message.bytes.clear();
|
Chris@559
|
550 }
|
Chris@559
|
551 iByte += size;
|
Chris@559
|
552 }
|
Chris@559
|
553 }
|
Chris@559
|
554 }
|
Chris@559
|
555 packet = MIDIPacketNext(packet);
|
Chris@559
|
556 }
|
Chris@559
|
557 }
|
Chris@559
|
558
|
Chris@1397
|
559 MidiInCore :: MidiInCore( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
|
Chris@1397
|
560 {
|
Chris@1397
|
561 initialize( clientName );
|
Chris@1397
|
562 }
|
Chris@1397
|
563
|
Chris@1397
|
564 MidiInCore :: ~MidiInCore( void )
|
Chris@1397
|
565 {
|
Chris@1397
|
566 // Close a connection if it exists.
|
Chris@1397
|
567 closePort();
|
Chris@1397
|
568
|
Chris@1397
|
569 // Cleanup.
|
Chris@1397
|
570 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
Chris@1397
|
571 MIDIClientDispose( data->client );
|
Chris@1397
|
572 if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
|
Chris@1397
|
573 delete data;
|
Chris@1397
|
574 }
|
Chris@1397
|
575
|
Chris@1397
|
576 void MidiInCore :: initialize( const std::string& clientName )
|
Chris@559
|
577 {
|
Chris@559
|
578 // Set up our client.
|
Chris@559
|
579 MIDIClientRef client;
|
Chris@1397
|
580 CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
|
Chris@1397
|
581 OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
|
Chris@559
|
582 if ( result != noErr ) {
|
Chris@1397
|
583 errorString_ = "MidiInCore::initialize: error creating OS-X MIDI client object.";
|
Chris@1397
|
584 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
585 return;
|
Chris@559
|
586 }
|
Chris@559
|
587
|
Chris@559
|
588 // Save our api-specific connection information.
|
Chris@559
|
589 CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
|
Chris@559
|
590 data->client = client;
|
Chris@559
|
591 data->endpoint = 0;
|
Chris@559
|
592 apiData_ = (void *) data;
|
Chris@559
|
593 inputData_.apiData = (void *) data;
|
Chris@1397
|
594 CFRelease(name);
|
Chris@559
|
595 }
|
Chris@559
|
596
|
Chris@1397
|
597 void MidiInCore :: openPort( unsigned int portNumber, const std::string portName )
|
Chris@559
|
598 {
|
Chris@559
|
599 if ( connected_ ) {
|
Chris@1397
|
600 errorString_ = "MidiInCore::openPort: a valid connection already exists!";
|
Chris@1397
|
601 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
602 return;
|
Chris@559
|
603 }
|
Chris@559
|
604
|
Chris@1397
|
605 CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
|
Chris@559
|
606 unsigned int nSrc = MIDIGetNumberOfSources();
|
Chris@559
|
607 if (nSrc < 1) {
|
Chris@1397
|
608 errorString_ = "MidiInCore::openPort: no MIDI input sources found!";
|
Chris@1397
|
609 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
|
Chris@1397
|
610 return;
|
Chris@559
|
611 }
|
Chris@559
|
612
|
Chris@559
|
613 if ( portNumber >= nSrc ) {
|
Chris@1397
|
614 std::ostringstream ost;
|
Chris@1397
|
615 ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@559
|
616 errorString_ = ost.str();
|
Chris@1397
|
617 error( RtMidiError::INVALID_PARAMETER, errorString_ );
|
Chris@1397
|
618 return;
|
Chris@559
|
619 }
|
Chris@559
|
620
|
Chris@559
|
621 MIDIPortRef port;
|
Chris@559
|
622 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
Chris@565
|
623 OSStatus result = MIDIInputPortCreate( data->client,
|
Chris@565
|
624 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
|
Chris@565
|
625 midiInputCallback, (void *)&inputData_, &port );
|
Chris@559
|
626 if ( result != noErr ) {
|
Chris@559
|
627 MIDIClientDispose( data->client );
|
Chris@1397
|
628 errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port.";
|
Chris@1397
|
629 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
630 return;
|
Chris@559
|
631 }
|
Chris@559
|
632
|
Chris@559
|
633 // Get the desired input source identifier.
|
Chris@559
|
634 MIDIEndpointRef endpoint = MIDIGetSource( portNumber );
|
Chris@1397
|
635 if ( endpoint == 0 ) {
|
Chris@559
|
636 MIDIPortDispose( port );
|
Chris@559
|
637 MIDIClientDispose( data->client );
|
Chris@1397
|
638 errorString_ = "MidiInCore::openPort: error getting MIDI input source reference.";
|
Chris@1397
|
639 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
640 return;
|
Chris@559
|
641 }
|
Chris@559
|
642
|
Chris@559
|
643 // Make the connection.
|
Chris@559
|
644 result = MIDIPortConnectSource( port, endpoint, NULL );
|
Chris@559
|
645 if ( result != noErr ) {
|
Chris@559
|
646 MIDIPortDispose( port );
|
Chris@559
|
647 MIDIClientDispose( data->client );
|
Chris@1397
|
648 errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port.";
|
Chris@1397
|
649 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
650 return;
|
Chris@559
|
651 }
|
Chris@559
|
652
|
Chris@559
|
653 // Save our api-specific port information.
|
Chris@559
|
654 data->port = port;
|
Chris@559
|
655
|
Chris@559
|
656 connected_ = true;
|
Chris@559
|
657 }
|
Chris@559
|
658
|
Chris@1397
|
659 void MidiInCore :: openVirtualPort( const std::string portName )
|
Chris@559
|
660 {
|
Chris@559
|
661 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
Chris@559
|
662
|
Chris@559
|
663 // Create a virtual MIDI input destination.
|
Chris@559
|
664 MIDIEndpointRef endpoint;
|
Chris@559
|
665 OSStatus result = MIDIDestinationCreate( data->client,
|
Chris@559
|
666 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
|
Chris@559
|
667 midiInputCallback, (void *)&inputData_, &endpoint );
|
Chris@559
|
668 if ( result != noErr ) {
|
Chris@1397
|
669 errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination.";
|
Chris@1397
|
670 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
671 return;
|
Chris@559
|
672 }
|
Chris@559
|
673
|
Chris@559
|
674 // Save our api-specific connection information.
|
Chris@559
|
675 data->endpoint = endpoint;
|
Chris@559
|
676 }
|
Chris@559
|
677
|
Chris@1397
|
678 void MidiInCore :: closePort( void )
|
Chris@559
|
679 {
|
Chris@1397
|
680 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
Chris@1397
|
681
|
Chris@1397
|
682 if ( data->endpoint ) {
|
Chris@1397
|
683 MIDIEndpointDispose( data->endpoint );
|
Chris@1397
|
684 }
|
Chris@1397
|
685
|
Chris@1397
|
686 if ( data->port ) {
|
Chris@559
|
687 MIDIPortDispose( data->port );
|
Chris@559
|
688 }
|
Chris@1397
|
689
|
Chris@1397
|
690 connected_ = false;
|
Chris@559
|
691 }
|
Chris@559
|
692
|
Chris@1397
|
693 unsigned int MidiInCore :: getPortCount()
|
Chris@1397
|
694 {
|
Chris@1397
|
695 CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
|
Chris@1397
|
696 return MIDIGetNumberOfSources();
|
Chris@1397
|
697 }
|
Chris@1397
|
698
|
Chris@1397
|
699 // This function was submitted by Douglas Casey Tucker and apparently
|
Chris@1397
|
700 // derived largely from PortMidi.
|
Chris@1397
|
701 CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal )
|
Chris@1397
|
702 {
|
Chris@1397
|
703 CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
|
Chris@1397
|
704 CFStringRef str;
|
Chris@1397
|
705
|
Chris@1397
|
706 // Begin with the endpoint's name.
|
Chris@1397
|
707 str = NULL;
|
Chris@1397
|
708 MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str );
|
Chris@1397
|
709 if ( str != NULL ) {
|
Chris@1397
|
710 CFStringAppend( result, str );
|
Chris@1397
|
711 CFRelease( str );
|
Chris@1397
|
712 }
|
Chris@1397
|
713
|
Chris@1397
|
714 MIDIEntityRef entity = 0;
|
Chris@1397
|
715 MIDIEndpointGetEntity( endpoint, &entity );
|
Chris@1397
|
716 if ( entity == 0 )
|
Chris@1397
|
717 // probably virtual
|
Chris@1397
|
718 return result;
|
Chris@1397
|
719
|
Chris@1397
|
720 if ( CFStringGetLength( result ) == 0 ) {
|
Chris@1397
|
721 // endpoint name has zero length -- try the entity
|
Chris@1397
|
722 str = NULL;
|
Chris@1397
|
723 MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str );
|
Chris@1397
|
724 if ( str != NULL ) {
|
Chris@1397
|
725 CFStringAppend( result, str );
|
Chris@1397
|
726 CFRelease( str );
|
Chris@1397
|
727 }
|
Chris@1397
|
728 }
|
Chris@1397
|
729 // now consider the device's name
|
Chris@1397
|
730 MIDIDeviceRef device = 0;
|
Chris@1397
|
731 MIDIEntityGetDevice( entity, &device );
|
Chris@1397
|
732 if ( device == 0 )
|
Chris@1397
|
733 return result;
|
Chris@1397
|
734
|
Chris@1397
|
735 str = NULL;
|
Chris@1397
|
736 MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str );
|
Chris@1397
|
737 if ( CFStringGetLength( result ) == 0 ) {
|
Chris@1397
|
738 CFRelease( result );
|
Chris@1397
|
739 return str;
|
Chris@1397
|
740 }
|
Chris@1397
|
741 if ( str != NULL ) {
|
Chris@1397
|
742 // if an external device has only one entity, throw away
|
Chris@1397
|
743 // the endpoint name and just use the device name
|
Chris@1397
|
744 if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) {
|
Chris@1397
|
745 CFRelease( result );
|
Chris@1397
|
746 return str;
|
Chris@1397
|
747 } else {
|
Chris@1397
|
748 if ( CFStringGetLength( str ) == 0 ) {
|
Chris@1397
|
749 CFRelease( str );
|
Chris@1397
|
750 return result;
|
Chris@1397
|
751 }
|
Chris@1397
|
752 // does the entity name already start with the device name?
|
Chris@1397
|
753 // (some drivers do this though they shouldn't)
|
Chris@1397
|
754 // if so, do not prepend
|
Chris@1397
|
755 if ( CFStringCompareWithOptions( result, /* endpoint name */
|
Chris@1397
|
756 str /* device name */,
|
Chris@1397
|
757 CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) {
|
Chris@1397
|
758 // prepend the device name to the entity name
|
Chris@1397
|
759 if ( CFStringGetLength( result ) > 0 )
|
Chris@1397
|
760 CFStringInsert( result, 0, CFSTR(" ") );
|
Chris@1397
|
761 CFStringInsert( result, 0, str );
|
Chris@1397
|
762 }
|
Chris@1397
|
763 CFRelease( str );
|
Chris@1397
|
764 }
|
Chris@1397
|
765 }
|
Chris@1397
|
766 return result;
|
Chris@1397
|
767 }
|
Chris@1397
|
768
|
Chris@1397
|
769 // This function was submitted by Douglas Casey Tucker and apparently
|
Chris@1397
|
770 // derived largely from PortMidi.
|
Chris@1397
|
771 static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint )
|
Chris@1397
|
772 {
|
Chris@1397
|
773 CFMutableStringRef result = CFStringCreateMutable( NULL, 0 );
|
Chris@1397
|
774 CFStringRef str;
|
Chris@1397
|
775 OSStatus err;
|
Chris@1397
|
776 int i;
|
Chris@1397
|
777
|
Chris@1397
|
778 // Does the endpoint have connections?
|
Chris@1397
|
779 CFDataRef connections = NULL;
|
Chris@1397
|
780 int nConnected = 0;
|
Chris@1397
|
781 bool anyStrings = false;
|
Chris@1397
|
782 err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections );
|
Chris@1397
|
783 if ( connections != NULL ) {
|
Chris@1397
|
784 // It has connections, follow them
|
Chris@1397
|
785 // Concatenate the names of all connected devices
|
Chris@1397
|
786 nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID);
|
Chris@1397
|
787 if ( nConnected ) {
|
Chris@1397
|
788 const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
|
Chris@1397
|
789 for ( i=0; i<nConnected; ++i, ++pid ) {
|
Chris@1397
|
790 MIDIUniqueID id = EndianS32_BtoN( *pid );
|
Chris@1397
|
791 MIDIObjectRef connObject;
|
Chris@1397
|
792 MIDIObjectType connObjectType;
|
Chris@1397
|
793 err = MIDIObjectFindByUniqueID( id, &connObject, &connObjectType );
|
Chris@1397
|
794 if ( err == noErr ) {
|
Chris@1397
|
795 if ( connObjectType == kMIDIObjectType_ExternalSource ||
|
Chris@1397
|
796 connObjectType == kMIDIObjectType_ExternalDestination ) {
|
Chris@1397
|
797 // Connected to an external device's endpoint (10.3 and later).
|
Chris@1397
|
798 str = EndpointName( (MIDIEndpointRef)(connObject), true );
|
Chris@1397
|
799 } else {
|
Chris@1397
|
800 // Connected to an external device (10.2) (or something else, catch-
|
Chris@1397
|
801 str = NULL;
|
Chris@1397
|
802 MIDIObjectGetStringProperty( connObject, kMIDIPropertyName, &str );
|
Chris@1397
|
803 }
|
Chris@1397
|
804 if ( str != NULL ) {
|
Chris@1397
|
805 if ( anyStrings )
|
Chris@1397
|
806 CFStringAppend( result, CFSTR(", ") );
|
Chris@1397
|
807 else anyStrings = true;
|
Chris@1397
|
808 CFStringAppend( result, str );
|
Chris@1397
|
809 CFRelease( str );
|
Chris@1397
|
810 }
|
Chris@1397
|
811 }
|
Chris@1397
|
812 }
|
Chris@1397
|
813 }
|
Chris@1397
|
814 CFRelease( connections );
|
Chris@1397
|
815 }
|
Chris@1397
|
816 if ( anyStrings )
|
Chris@1397
|
817 return result;
|
Chris@1397
|
818
|
Chris@1397
|
819 CFRelease( result );
|
Chris@1397
|
820
|
Chris@1397
|
821 // Here, either the endpoint had no connections, or we failed to obtain names
|
Chris@1397
|
822 return EndpointName( endpoint, false );
|
Chris@1397
|
823 }
|
Chris@1397
|
824
|
Chris@1397
|
825 std::string MidiInCore :: getPortName( unsigned int portNumber )
|
Chris@1397
|
826 {
|
Chris@1397
|
827 CFStringRef nameRef;
|
Chris@1397
|
828 MIDIEndpointRef portRef;
|
Chris@1397
|
829 char name[128];
|
Chris@1397
|
830
|
Chris@1397
|
831 std::string stringName;
|
Chris@1397
|
832 CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
|
Chris@1397
|
833 if ( portNumber >= MIDIGetNumberOfSources() ) {
|
Chris@1397
|
834 std::ostringstream ost;
|
Chris@1397
|
835 ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@1397
|
836 errorString_ = ost.str();
|
Chris@1397
|
837 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
838 return stringName;
|
Chris@1397
|
839 }
|
Chris@1397
|
840
|
Chris@1397
|
841 portRef = MIDIGetSource( portNumber );
|
Chris@1397
|
842 nameRef = ConnectedEndpointName(portRef);
|
Chris@1397
|
843 CFStringGetCString( nameRef, name, sizeof(name), CFStringGetSystemEncoding());
|
Chris@1397
|
844 CFRelease( nameRef );
|
Chris@1397
|
845
|
Chris@1397
|
846 return stringName = name;
|
Chris@1397
|
847 }
|
Chris@1397
|
848
|
Chris@1397
|
849 //*********************************************************************//
|
Chris@1397
|
850 // API: OS-X
|
Chris@1397
|
851 // Class Definitions: MidiOutCore
|
Chris@1397
|
852 //*********************************************************************//
|
Chris@1397
|
853
|
Chris@1397
|
854 MidiOutCore :: MidiOutCore( const std::string clientName ) : MidiOutApi()
|
Chris@1397
|
855 {
|
Chris@1397
|
856 initialize( clientName );
|
Chris@1397
|
857 }
|
Chris@1397
|
858
|
Chris@1397
|
859 MidiOutCore :: ~MidiOutCore( void )
|
Chris@559
|
860 {
|
Chris@559
|
861 // Close a connection if it exists.
|
Chris@559
|
862 closePort();
|
Chris@559
|
863
|
Chris@559
|
864 // Cleanup.
|
Chris@559
|
865 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
Chris@559
|
866 MIDIClientDispose( data->client );
|
Chris@559
|
867 if ( data->endpoint ) MIDIEndpointDispose( data->endpoint );
|
Chris@559
|
868 delete data;
|
Chris@559
|
869 }
|
Chris@559
|
870
|
Chris@1397
|
871 void MidiOutCore :: initialize( const std::string& clientName )
|
Chris@559
|
872 {
|
Chris@559
|
873 // Set up our client.
|
Chris@559
|
874 MIDIClientRef client;
|
Chris@1397
|
875 CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
|
Chris@1397
|
876 OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
|
Chris@559
|
877 if ( result != noErr ) {
|
Chris@1397
|
878 errorString_ = "MidiOutCore::initialize: error creating OS-X MIDI client object.";
|
Chris@1397
|
879 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
880 return;
|
Chris@559
|
881 }
|
Chris@559
|
882
|
Chris@559
|
883 // Save our api-specific connection information.
|
Chris@559
|
884 CoreMidiData *data = (CoreMidiData *) new CoreMidiData;
|
Chris@559
|
885 data->client = client;
|
Chris@559
|
886 data->endpoint = 0;
|
Chris@559
|
887 apiData_ = (void *) data;
|
Chris@1397
|
888 CFRelease( name );
|
Chris@559
|
889 }
|
Chris@559
|
890
|
Chris@1397
|
891 unsigned int MidiOutCore :: getPortCount()
|
Chris@1397
|
892 {
|
Chris@1397
|
893 CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
|
Chris@1397
|
894 return MIDIGetNumberOfDestinations();
|
Chris@1397
|
895 }
|
Chris@1397
|
896
|
Chris@1397
|
897 std::string MidiOutCore :: getPortName( unsigned int portNumber )
|
Chris@1397
|
898 {
|
Chris@1397
|
899 CFStringRef nameRef;
|
Chris@1397
|
900 MIDIEndpointRef portRef;
|
Chris@1397
|
901 char name[128];
|
Chris@1397
|
902
|
Chris@1397
|
903 std::string stringName;
|
Chris@1397
|
904 CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
|
Chris@1397
|
905 if ( portNumber >= MIDIGetNumberOfDestinations() ) {
|
Chris@1397
|
906 std::ostringstream ost;
|
Chris@1397
|
907 ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@1397
|
908 errorString_ = ost.str();
|
Chris@1397
|
909 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
910 return stringName;
|
Chris@1397
|
911 }
|
Chris@1397
|
912
|
Chris@1397
|
913 portRef = MIDIGetDestination( portNumber );
|
Chris@1397
|
914 nameRef = ConnectedEndpointName(portRef);
|
Chris@1397
|
915 CFStringGetCString( nameRef, name, sizeof(name), CFStringGetSystemEncoding());
|
Chris@1397
|
916 CFRelease( nameRef );
|
Chris@1397
|
917
|
Chris@1397
|
918 return stringName = name;
|
Chris@1397
|
919 }
|
Chris@1397
|
920
|
Chris@1397
|
921 void MidiOutCore :: openPort( unsigned int portNumber, const std::string portName )
|
Chris@559
|
922 {
|
Chris@559
|
923 if ( connected_ ) {
|
Chris@1397
|
924 errorString_ = "MidiOutCore::openPort: a valid connection already exists!";
|
Chris@1397
|
925 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
926 return;
|
Chris@559
|
927 }
|
Chris@559
|
928
|
Chris@1397
|
929 CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false );
|
Chris@559
|
930 unsigned int nDest = MIDIGetNumberOfDestinations();
|
Chris@559
|
931 if (nDest < 1) {
|
Chris@1397
|
932 errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!";
|
Chris@1397
|
933 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
|
Chris@1397
|
934 return;
|
Chris@559
|
935 }
|
Chris@559
|
936
|
Chris@559
|
937 if ( portNumber >= nDest ) {
|
Chris@1397
|
938 std::ostringstream ost;
|
Chris@1397
|
939 ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@559
|
940 errorString_ = ost.str();
|
Chris@1397
|
941 error( RtMidiError::INVALID_PARAMETER, errorString_ );
|
Chris@1397
|
942 return;
|
Chris@559
|
943 }
|
Chris@559
|
944
|
Chris@559
|
945 MIDIPortRef port;
|
Chris@559
|
946 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
Chris@565
|
947 OSStatus result = MIDIOutputPortCreate( data->client,
|
Chris@565
|
948 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
|
Chris@565
|
949 &port );
|
Chris@559
|
950 if ( result != noErr ) {
|
Chris@559
|
951 MIDIClientDispose( data->client );
|
Chris@1397
|
952 errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port.";
|
Chris@1397
|
953 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
954 return;
|
Chris@559
|
955 }
|
Chris@559
|
956
|
Chris@559
|
957 // Get the desired output port identifier.
|
Chris@559
|
958 MIDIEndpointRef destination = MIDIGetDestination( portNumber );
|
Chris@1397
|
959 if ( destination == 0 ) {
|
Chris@559
|
960 MIDIPortDispose( port );
|
Chris@559
|
961 MIDIClientDispose( data->client );
|
Chris@1397
|
962 errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference.";
|
Chris@1397
|
963 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
964 return;
|
Chris@559
|
965 }
|
Chris@559
|
966
|
Chris@559
|
967 // Save our api-specific connection information.
|
Chris@559
|
968 data->port = port;
|
Chris@559
|
969 data->destinationId = destination;
|
Chris@559
|
970 connected_ = true;
|
Chris@559
|
971 }
|
Chris@559
|
972
|
Chris@1397
|
973 void MidiOutCore :: closePort( void )
|
Chris@559
|
974 {
|
Chris@559
|
975 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
Chris@559
|
976
|
Chris@559
|
977 if ( data->endpoint ) {
|
Chris@1397
|
978 MIDIEndpointDispose( data->endpoint );
|
Chris@1397
|
979 }
|
Chris@1397
|
980
|
Chris@1397
|
981 if ( data->port ) {
|
Chris@1397
|
982 MIDIPortDispose( data->port );
|
Chris@1397
|
983 }
|
Chris@1397
|
984
|
Chris@1397
|
985 connected_ = false;
|
Chris@1397
|
986 }
|
Chris@1397
|
987
|
Chris@1397
|
988 void MidiOutCore :: openVirtualPort( std::string portName )
|
Chris@1397
|
989 {
|
Chris@1397
|
990 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
Chris@1397
|
991
|
Chris@1397
|
992 if ( data->endpoint ) {
|
Chris@1397
|
993 errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!";
|
Chris@1397
|
994 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
995 return;
|
Chris@559
|
996 }
|
Chris@559
|
997
|
Chris@559
|
998 // Create a virtual MIDI output source.
|
Chris@559
|
999 MIDIEndpointRef endpoint;
|
Chris@559
|
1000 OSStatus result = MIDISourceCreate( data->client,
|
Chris@559
|
1001 CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
|
Chris@559
|
1002 &endpoint );
|
Chris@559
|
1003 if ( result != noErr ) {
|
Chris@1397
|
1004 errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source.";
|
Chris@1397
|
1005 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1006 return;
|
Chris@559
|
1007 }
|
Chris@559
|
1008
|
Chris@559
|
1009 // Save our api-specific connection information.
|
Chris@559
|
1010 data->endpoint = endpoint;
|
Chris@559
|
1011 }
|
Chris@559
|
1012
|
Chris@1397
|
1013 void MidiOutCore :: sendMessage( std::vector<unsigned char> *message )
|
Chris@559
|
1014 {
|
Chris@1397
|
1015 // We use the MIDISendSysex() function to asynchronously send sysex
|
Chris@1397
|
1016 // messages. Otherwise, we use a single CoreMidi MIDIPacket.
|
Chris@559
|
1017 unsigned int nBytes = message->size();
|
Chris@565
|
1018 if ( nBytes == 0 ) {
|
Chris@1397
|
1019 errorString_ = "MidiOutCore::sendMessage: no data in message argument!";
|
Chris@1397
|
1020 error( RtMidiError::WARNING, errorString_ );
|
Chris@565
|
1021 return;
|
Chris@565
|
1022 }
|
Chris@559
|
1023
|
Chris@1397
|
1024 MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
|
Chris@1397
|
1025 CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
|
Chris@1397
|
1026 OSStatus result;
|
Chris@1397
|
1027
|
Chris@1397
|
1028 if ( message->at(0) != 0xF0 && nBytes > 3 ) {
|
Chris@1397
|
1029 errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
|
Chris@1397
|
1030 error( RtMidiError::WARNING, errorString_ );
|
Chris@565
|
1031 return;
|
Chris@565
|
1032 }
|
Chris@565
|
1033
|
Chris@1397
|
1034 Byte buffer[nBytes+(sizeof(MIDIPacketList))];
|
Chris@1397
|
1035 ByteCount listSize = sizeof(buffer);
|
Chris@1397
|
1036 MIDIPacketList *packetList = (MIDIPacketList*)buffer;
|
Chris@1397
|
1037 MIDIPacket *packet = MIDIPacketListInit( packetList );
|
Chris@1397
|
1038
|
Chris@1397
|
1039 ByteCount remainingBytes = nBytes;
|
Chris@1397
|
1040 while (remainingBytes && packet) {
|
Chris@1397
|
1041 ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket
|
Chris@1397
|
1042 const Byte* dataStartPtr = (const Byte *) &message->at( nBytes - remainingBytes );
|
Chris@1397
|
1043 packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr);
|
Chris@1397
|
1044 remainingBytes -= bytesForPacket;
|
Chris@1397
|
1045 }
|
Chris@1397
|
1046
|
Chris@1397
|
1047 if ( !packet ) {
|
Chris@1397
|
1048 errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
|
Chris@1397
|
1049 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1050 return;
|
Chris@1397
|
1051 }
|
Chris@1397
|
1052
|
Chris@1397
|
1053 // Send to any destinations that may have connected to us.
|
Chris@1397
|
1054 if ( data->endpoint ) {
|
Chris@1397
|
1055 result = MIDIReceived( data->endpoint, packetList );
|
Chris@1397
|
1056 if ( result != noErr ) {
|
Chris@1397
|
1057 errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
|
Chris@1397
|
1058 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
1059 }
|
Chris@1397
|
1060 }
|
Chris@1397
|
1061
|
Chris@1397
|
1062 // And send to an explicit destination port if we're connected.
|
Chris@1397
|
1063 if ( connected_ ) {
|
Chris@1397
|
1064 result = MIDISend( data->port, data->destinationId, packetList );
|
Chris@1397
|
1065 if ( result != noErr ) {
|
Chris@1397
|
1066 errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
|
Chris@1397
|
1067 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
1068 }
|
Chris@559
|
1069 }
|
Chris@559
|
1070 }
|
Chris@559
|
1071
|
Chris@559
|
1072 #endif // __MACOSX_CORE__
|
Chris@559
|
1073
|
Chris@559
|
1074
|
Chris@559
|
1075 //*********************************************************************//
|
Chris@559
|
1076 // API: LINUX ALSA SEQUENCER
|
Chris@559
|
1077 //*********************************************************************//
|
Chris@559
|
1078
|
Chris@559
|
1079 // API information found at:
|
Chris@559
|
1080 // - http://www.alsa-project.org/documentation.php#Library
|
Chris@559
|
1081
|
Chris@1397
|
1082 #if defined(__LINUX_ALSA__)
|
Chris@559
|
1083
|
Chris@559
|
1084 // The ALSA Sequencer API is based on the use of a callback function for
|
Chris@559
|
1085 // MIDI input.
|
Chris@559
|
1086 //
|
Chris@559
|
1087 // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer
|
Chris@559
|
1088 // time stamps and other assorted fixes!!!
|
Chris@559
|
1089
|
Chris@1397
|
1090 // If you don't need timestamping for incoming MIDI events, define the
|
Chris@1397
|
1091 // preprocessor definition AVOID_TIMESTAMPING to save resources
|
Chris@1397
|
1092 // associated with the ALSA sequencer queues.
|
Chris@1397
|
1093
|
Chris@559
|
1094 #include <pthread.h>
|
Chris@559
|
1095 #include <sys/time.h>
|
Chris@559
|
1096
|
Chris@559
|
1097 // ALSA header file.
|
Chris@559
|
1098 #include <alsa/asoundlib.h>
|
Chris@559
|
1099
|
Chris@559
|
1100 // A structure to hold variables related to the ALSA API
|
Chris@559
|
1101 // implementation.
|
Chris@559
|
1102 struct AlsaMidiData {
|
Chris@559
|
1103 snd_seq_t *seq;
|
Chris@1397
|
1104 unsigned int portNum;
|
Chris@559
|
1105 int vport;
|
Chris@559
|
1106 snd_seq_port_subscribe_t *subscription;
|
Chris@559
|
1107 snd_midi_event_t *coder;
|
Chris@559
|
1108 unsigned int bufferSize;
|
Chris@559
|
1109 unsigned char *buffer;
|
Chris@559
|
1110 pthread_t thread;
|
Chris@1397
|
1111 pthread_t dummy_thread_id;
|
Chris@559
|
1112 unsigned long long lastTime;
|
Chris@559
|
1113 int queue_id; // an input queue is needed to get timestamped events
|
Chris@1397
|
1114 int trigger_fds[2];
|
Chris@559
|
1115 };
|
Chris@559
|
1116
|
Chris@559
|
1117 #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits))
|
Chris@559
|
1118
|
Chris@559
|
1119 //*********************************************************************//
|
Chris@559
|
1120 // API: LINUX ALSA
|
Chris@1397
|
1121 // Class Definitions: MidiInAlsa
|
Chris@559
|
1122 //*********************************************************************//
|
Chris@559
|
1123
|
Chris@1397
|
1124 static void *alsaMidiHandler( void *ptr )
|
Chris@559
|
1125 {
|
Chris@1397
|
1126 MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (ptr);
|
Chris@559
|
1127 AlsaMidiData *apiData = static_cast<AlsaMidiData *> (data->apiData);
|
Chris@559
|
1128
|
Chris@559
|
1129 long nBytes;
|
Chris@559
|
1130 unsigned long long time, lastTime;
|
Chris@559
|
1131 bool continueSysex = false;
|
Chris@1397
|
1132 bool doDecode = false;
|
Chris@1397
|
1133 MidiInApi::MidiMessage message;
|
Chris@1397
|
1134 int poll_fd_count;
|
Chris@1397
|
1135 struct pollfd *poll_fds;
|
Chris@559
|
1136
|
Chris@559
|
1137 snd_seq_event_t *ev;
|
Chris@559
|
1138 int result;
|
Chris@559
|
1139 apiData->bufferSize = 32;
|
Chris@559
|
1140 result = snd_midi_event_new( 0, &apiData->coder );
|
Chris@559
|
1141 if ( result < 0 ) {
|
Chris@559
|
1142 data->doInput = false;
|
Chris@1397
|
1143 std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n";
|
Chris@559
|
1144 return 0;
|
Chris@559
|
1145 }
|
Chris@559
|
1146 unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize );
|
Chris@559
|
1147 if ( buffer == NULL ) {
|
Chris@559
|
1148 data->doInput = false;
|
Chris@1397
|
1149 snd_midi_event_free( apiData->coder );
|
Chris@1397
|
1150 apiData->coder = 0;
|
Chris@1397
|
1151 std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n";
|
Chris@559
|
1152 return 0;
|
Chris@559
|
1153 }
|
Chris@559
|
1154 snd_midi_event_init( apiData->coder );
|
Chris@559
|
1155 snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages
|
Chris@559
|
1156
|
Chris@1397
|
1157 poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1;
|
Chris@1397
|
1158 poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd ));
|
Chris@1397
|
1159 snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN );
|
Chris@1397
|
1160 poll_fds[0].fd = apiData->trigger_fds[0];
|
Chris@1397
|
1161 poll_fds[0].events = POLLIN;
|
Chris@1397
|
1162
|
Chris@559
|
1163 while ( data->doInput ) {
|
Chris@559
|
1164
|
Chris@559
|
1165 if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) {
|
Chris@1397
|
1166 // No data pending
|
Chris@1397
|
1167 if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) {
|
Chris@1397
|
1168 if ( poll_fds[0].revents & POLLIN ) {
|
Chris@1397
|
1169 bool dummy;
|
Chris@1397
|
1170 int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) );
|
Chris@1397
|
1171 (void) res;
|
Chris@1397
|
1172 }
|
Chris@1397
|
1173 }
|
Chris@559
|
1174 continue;
|
Chris@559
|
1175 }
|
Chris@559
|
1176
|
Chris@559
|
1177 // If here, there should be data.
|
Chris@559
|
1178 result = snd_seq_event_input( apiData->seq, &ev );
|
Chris@559
|
1179 if ( result == -ENOSPC ) {
|
Chris@1397
|
1180 std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n";
|
Chris@559
|
1181 continue;
|
Chris@559
|
1182 }
|
Chris@559
|
1183 else if ( result <= 0 ) {
|
Chris@1397
|
1184 std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n";
|
Chris@1397
|
1185 perror("System reports");
|
Chris@559
|
1186 continue;
|
Chris@559
|
1187 }
|
Chris@559
|
1188
|
Chris@559
|
1189 // This is a bit weird, but we now have to decode an ALSA MIDI
|
Chris@559
|
1190 // event (back) into MIDI bytes. We'll ignore non-MIDI types.
|
Chris@1397
|
1191 if ( !continueSysex ) message.bytes.clear();
|
Chris@1397
|
1192
|
Chris@1397
|
1193 doDecode = false;
|
Chris@559
|
1194 switch ( ev->type ) {
|
Chris@559
|
1195
|
Chris@1397
|
1196 case SND_SEQ_EVENT_PORT_SUBSCRIBED:
|
Chris@559
|
1197 #if defined(__RTMIDI_DEBUG__)
|
Chris@1397
|
1198 std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n";
|
Chris@559
|
1199 #endif
|
Chris@559
|
1200 break;
|
Chris@559
|
1201
|
Chris@1397
|
1202 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
|
Chris@565
|
1203 #if defined(__RTMIDI_DEBUG__)
|
Chris@1397
|
1204 std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n";
|
Chris@1397
|
1205 std::cout << "sender = " << (int) ev->data.connect.sender.client << ":"
|
Chris@1397
|
1206 << (int) ev->data.connect.sender.port
|
Chris@1397
|
1207 << ", dest = " << (int) ev->data.connect.dest.client << ":"
|
Chris@1397
|
1208 << (int) ev->data.connect.dest.port
|
Chris@1397
|
1209 << std::endl;
|
Chris@565
|
1210 #endif
|
Chris@559
|
1211 break;
|
Chris@559
|
1212
|
Chris@559
|
1213 case SND_SEQ_EVENT_QFRAME: // MIDI time code
|
Chris@1397
|
1214 if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
|
Chris@1397
|
1215 break;
|
Chris@1397
|
1216
|
Chris@1397
|
1217 case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick
|
Chris@1397
|
1218 if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
|
Chris@1397
|
1219 break;
|
Chris@1397
|
1220
|
Chris@1397
|
1221 case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick
|
Chris@1397
|
1222 if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true;
|
Chris@1397
|
1223 break;
|
Chris@559
|
1224
|
Chris@559
|
1225 case SND_SEQ_EVENT_SENSING: // Active sensing
|
Chris@1397
|
1226 if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true;
|
Chris@1397
|
1227 break;
|
Chris@559
|
1228
|
Chris@1426
|
1229 case SND_SEQ_EVENT_SYSEX:
|
Chris@559
|
1230 if ( (data->ignoreFlags & 0x01) ) break;
|
Chris@559
|
1231 if ( ev->data.ext.len > apiData->bufferSize ) {
|
Chris@559
|
1232 apiData->bufferSize = ev->data.ext.len;
|
Chris@559
|
1233 free( buffer );
|
Chris@559
|
1234 buffer = (unsigned char *) malloc( apiData->bufferSize );
|
Chris@559
|
1235 if ( buffer == NULL ) {
|
Chris@559
|
1236 data->doInput = false;
|
Chris@1397
|
1237 std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n";
|
Chris@559
|
1238 break;
|
Chris@559
|
1239 }
|
Chris@559
|
1240 }
|
Chris@1426
|
1241 doDecode = true;
|
Chris@1426
|
1242 break;
|
Chris@559
|
1243
|
Chris@559
|
1244 default:
|
Chris@1397
|
1245 doDecode = true;
|
Chris@1397
|
1246 }
|
Chris@1397
|
1247
|
Chris@1397
|
1248 if ( doDecode ) {
|
Chris@1397
|
1249
|
Chris@559
|
1250 nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev );
|
Chris@1397
|
1251 if ( nBytes > 0 ) {
|
Chris@1397
|
1252 // The ALSA sequencer has a maximum buffer size for MIDI sysex
|
Chris@1397
|
1253 // events of 256 bytes. If a device sends sysex messages larger
|
Chris@1397
|
1254 // than this, they are segmented into 256 byte chunks. So,
|
Chris@1397
|
1255 // we'll watch for this and concatenate sysex chunks into a
|
Chris@1397
|
1256 // single sysex message if necessary.
|
Chris@1397
|
1257 if ( !continueSysex )
|
Chris@1397
|
1258 message.bytes.assign( buffer, &buffer[nBytes] );
|
Chris@1397
|
1259 else
|
Chris@1397
|
1260 message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] );
|
Chris@1397
|
1261
|
Chris@1397
|
1262 continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) );
|
Chris@1397
|
1263 if ( !continueSysex ) {
|
Chris@1397
|
1264
|
Chris@1397
|
1265 // Calculate the time stamp:
|
Chris@1397
|
1266 message.timeStamp = 0.0;
|
Chris@1397
|
1267
|
Chris@1397
|
1268 // Method 1: Use the system time.
|
Chris@1397
|
1269 //(void)gettimeofday(&tv, (struct timezone *)NULL);
|
Chris@1397
|
1270 //time = (tv.tv_sec * 1000000) + tv.tv_usec;
|
Chris@1397
|
1271
|
Chris@1397
|
1272 // Method 2: Use the ALSA sequencer event time data.
|
Chris@1397
|
1273 // (thanks to Pedro Lopez-Cabanillas!).
|
Chris@1397
|
1274 time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 );
|
Chris@1397
|
1275 lastTime = time;
|
Chris@1397
|
1276 time -= apiData->lastTime;
|
Chris@1397
|
1277 apiData->lastTime = lastTime;
|
Chris@1397
|
1278 if ( data->firstMessage == true )
|
Chris@1397
|
1279 data->firstMessage = false;
|
Chris@1397
|
1280 else
|
Chris@1397
|
1281 message.timeStamp = time * 0.000001;
|
Chris@1397
|
1282 }
|
Chris@1397
|
1283 else {
|
Chris@559
|
1284 #if defined(__RTMIDI_DEBUG__)
|
Chris@1397
|
1285 std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n";
|
Chris@559
|
1286 #endif
|
Chris@1397
|
1287 }
|
Chris@559
|
1288 }
|
Chris@559
|
1289 }
|
Chris@559
|
1290
|
Chris@1397
|
1291 snd_seq_free_event( ev );
|
Chris@1397
|
1292 if ( message.bytes.size() == 0 || continueSysex ) continue;
|
Chris@1397
|
1293
|
Chris@1397
|
1294 if ( data->usingCallback ) {
|
Chris@559
|
1295 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
|
Chris@559
|
1296 callback( message.timeStamp, &message.bytes, data->userData );
|
Chris@559
|
1297 }
|
Chris@559
|
1298 else {
|
Chris@559
|
1299 // As long as we haven't reached our queue size limit, push the message.
|
Chris@1397
|
1300 if ( data->queue.size < data->queue.ringSize ) {
|
Chris@1397
|
1301 data->queue.ring[data->queue.back++] = message;
|
Chris@1397
|
1302 if ( data->queue.back == data->queue.ringSize )
|
Chris@1397
|
1303 data->queue.back = 0;
|
Chris@1397
|
1304 data->queue.size++;
|
Chris@1397
|
1305 }
|
Chris@559
|
1306 else
|
Chris@1397
|
1307 std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n";
|
Chris@559
|
1308 }
|
Chris@559
|
1309 }
|
Chris@559
|
1310
|
Chris@559
|
1311 if ( buffer ) free( buffer );
|
Chris@559
|
1312 snd_midi_event_free( apiData->coder );
|
Chris@559
|
1313 apiData->coder = 0;
|
Chris@1397
|
1314 apiData->thread = apiData->dummy_thread_id;
|
Chris@559
|
1315 return 0;
|
Chris@559
|
1316 }
|
Chris@559
|
1317
|
Chris@1397
|
1318 MidiInAlsa :: MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
|
Chris@1397
|
1319 {
|
Chris@1397
|
1320 initialize( clientName );
|
Chris@1397
|
1321 }
|
Chris@1397
|
1322
|
Chris@1397
|
1323 MidiInAlsa :: ~MidiInAlsa()
|
Chris@1397
|
1324 {
|
Chris@1397
|
1325 // Close a connection if it exists.
|
Chris@1397
|
1326 closePort();
|
Chris@1397
|
1327
|
Chris@1397
|
1328 // Shutdown the input thread.
|
Chris@1397
|
1329 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@1397
|
1330 if ( inputData_.doInput ) {
|
Chris@1397
|
1331 inputData_.doInput = false;
|
Chris@1397
|
1332 int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) );
|
Chris@1397
|
1333 (void) res;
|
Chris@1397
|
1334 if ( !pthread_equal(data->thread, data->dummy_thread_id) )
|
Chris@1397
|
1335 pthread_join( data->thread, NULL );
|
Chris@1397
|
1336 }
|
Chris@1397
|
1337
|
Chris@1397
|
1338 // Cleanup.
|
Chris@1397
|
1339 close ( data->trigger_fds[0] );
|
Chris@1397
|
1340 close ( data->trigger_fds[1] );
|
Chris@1397
|
1341 if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
|
Chris@1397
|
1342 #ifndef AVOID_TIMESTAMPING
|
Chris@1397
|
1343 snd_seq_free_queue( data->seq, data->queue_id );
|
Chris@1397
|
1344 #endif
|
Chris@1397
|
1345 snd_seq_close( data->seq );
|
Chris@1397
|
1346 delete data;
|
Chris@1397
|
1347 }
|
Chris@1397
|
1348
|
Chris@1397
|
1349 void MidiInAlsa :: initialize( const std::string& clientName )
|
Chris@559
|
1350 {
|
Chris@559
|
1351 // Set up the ALSA sequencer client.
|
Chris@565
|
1352 snd_seq_t *seq;
|
Chris@559
|
1353 int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
|
Chris@559
|
1354 if ( result < 0 ) {
|
Chris@1397
|
1355 errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object.";
|
Chris@1397
|
1356 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1357 return;
|
Chris@1397
|
1358 }
|
Chris@559
|
1359
|
Chris@559
|
1360 // Set client name.
|
Chris@565
|
1361 snd_seq_set_client_name( seq, clientName.c_str() );
|
Chris@559
|
1362
|
Chris@559
|
1363 // Save our api-specific connection information.
|
Chris@559
|
1364 AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
|
Chris@559
|
1365 data->seq = seq;
|
Chris@1397
|
1366 data->portNum = -1;
|
Chris@559
|
1367 data->vport = -1;
|
Chris@1397
|
1368 data->subscription = 0;
|
Chris@1397
|
1369 data->dummy_thread_id = pthread_self();
|
Chris@1397
|
1370 data->thread = data->dummy_thread_id;
|
Chris@1397
|
1371 data->trigger_fds[0] = -1;
|
Chris@1397
|
1372 data->trigger_fds[1] = -1;
|
Chris@559
|
1373 apiData_ = (void *) data;
|
Chris@559
|
1374 inputData_.apiData = (void *) data;
|
Chris@559
|
1375
|
Chris@1397
|
1376 if ( pipe(data->trigger_fds) == -1 ) {
|
Chris@1397
|
1377 errorString_ = "MidiInAlsa::initialize: error creating pipe objects.";
|
Chris@1397
|
1378 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1379 return;
|
Chris@1397
|
1380 }
|
Chris@1397
|
1381
|
Chris@559
|
1382 // Create the input queue
|
Chris@1397
|
1383 #ifndef AVOID_TIMESTAMPING
|
Chris@559
|
1384 data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue");
|
Chris@559
|
1385 // Set arbitrary tempo (mm=100) and resolution (240)
|
Chris@559
|
1386 snd_seq_queue_tempo_t *qtempo;
|
Chris@559
|
1387 snd_seq_queue_tempo_alloca(&qtempo);
|
Chris@559
|
1388 snd_seq_queue_tempo_set_tempo(qtempo, 600000);
|
Chris@559
|
1389 snd_seq_queue_tempo_set_ppq(qtempo, 240);
|
Chris@559
|
1390 snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo);
|
Chris@559
|
1391 snd_seq_drain_output(data->seq);
|
Chris@1397
|
1392 #endif
|
Chris@559
|
1393 }
|
Chris@559
|
1394
|
Chris@559
|
1395 // This function is used to count or get the pinfo structure for a given port number.
|
Chris@559
|
1396 unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber )
|
Chris@559
|
1397 {
|
Chris@1397
|
1398 snd_seq_client_info_t *cinfo;
|
Chris@559
|
1399 int client;
|
Chris@559
|
1400 int count = 0;
|
Chris@1397
|
1401 snd_seq_client_info_alloca( &cinfo );
|
Chris@1397
|
1402
|
Chris@1397
|
1403 snd_seq_client_info_set_client( cinfo, -1 );
|
Chris@1397
|
1404 while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) {
|
Chris@559
|
1405 client = snd_seq_client_info_get_client( cinfo );
|
Chris@559
|
1406 if ( client == 0 ) continue;
|
Chris@1397
|
1407 // Reset query info
|
Chris@1397
|
1408 snd_seq_port_info_set_client( pinfo, client );
|
Chris@1397
|
1409 snd_seq_port_info_set_port( pinfo, -1 );
|
Chris@1397
|
1410 while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
|
Chris@565
|
1411 unsigned int atyp = snd_seq_port_info_get_type( pinfo );
|
Chris@1397
|
1412 if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) &&
|
Chris@1397
|
1413 ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) ) continue;
|
Chris@565
|
1414 unsigned int caps = snd_seq_port_info_get_capability( pinfo );
|
Chris@565
|
1415 if ( ( caps & type ) != type ) continue;
|
Chris@559
|
1416 if ( count == portNumber ) return 1;
|
Chris@1397
|
1417 ++count;
|
Chris@1397
|
1418 }
|
Chris@1397
|
1419 }
|
Chris@559
|
1420
|
Chris@559
|
1421 // If a negative portNumber was used, return the port count.
|
Chris@559
|
1422 if ( portNumber < 0 ) return count;
|
Chris@559
|
1423 return 0;
|
Chris@559
|
1424 }
|
Chris@559
|
1425
|
Chris@1397
|
1426 unsigned int MidiInAlsa :: getPortCount()
|
Chris@1397
|
1427 {
|
Chris@1397
|
1428 snd_seq_port_info_t *pinfo;
|
Chris@1397
|
1429 snd_seq_port_info_alloca( &pinfo );
|
Chris@1397
|
1430
|
Chris@1397
|
1431 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@1397
|
1432 return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );
|
Chris@1397
|
1433 }
|
Chris@1397
|
1434
|
Chris@1397
|
1435 std::string MidiInAlsa :: getPortName( unsigned int portNumber )
|
Chris@1397
|
1436 {
|
Chris@1397
|
1437 snd_seq_client_info_t *cinfo;
|
Chris@1397
|
1438 snd_seq_port_info_t *pinfo;
|
Chris@1397
|
1439 snd_seq_client_info_alloca( &cinfo );
|
Chris@1397
|
1440 snd_seq_port_info_alloca( &pinfo );
|
Chris@1397
|
1441
|
Chris@1397
|
1442 std::string stringName;
|
Chris@1397
|
1443 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@1397
|
1444 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) {
|
Chris@1397
|
1445 int cnum = snd_seq_port_info_get_client( pinfo );
|
Chris@1397
|
1446 snd_seq_get_any_client_info( data->seq, cnum, cinfo );
|
Chris@1397
|
1447 std::ostringstream os;
|
Chris@1397
|
1448 os << snd_seq_client_info_get_name( cinfo );
|
Chris@1397
|
1449 os << " "; // These lines added to make sure devices are listed
|
Chris@1397
|
1450 os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names
|
Chris@1397
|
1451 os << ":";
|
Chris@1397
|
1452 os << snd_seq_port_info_get_port( pinfo );
|
Chris@1397
|
1453 stringName = os.str();
|
Chris@1397
|
1454 return stringName;
|
Chris@1397
|
1455 }
|
Chris@1397
|
1456
|
Chris@1397
|
1457 // If we get here, we didn't find a match.
|
Chris@1397
|
1458 errorString_ = "MidiInAlsa::getPortName: error looking for port name!";
|
Chris@1397
|
1459 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
1460 return stringName;
|
Chris@1397
|
1461 }
|
Chris@1397
|
1462
|
Chris@1397
|
1463 void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName )
|
Chris@559
|
1464 {
|
Chris@559
|
1465 if ( connected_ ) {
|
Chris@1397
|
1466 errorString_ = "MidiInAlsa::openPort: a valid connection already exists!";
|
Chris@1397
|
1467 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
1468 return;
|
Chris@559
|
1469 }
|
Chris@559
|
1470
|
Chris@559
|
1471 unsigned int nSrc = this->getPortCount();
|
Chris@1397
|
1472 if ( nSrc < 1 ) {
|
Chris@1397
|
1473 errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!";
|
Chris@1397
|
1474 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
|
Chris@1397
|
1475 return;
|
Chris@559
|
1476 }
|
Chris@559
|
1477
|
Chris@1397
|
1478 snd_seq_port_info_t *src_pinfo;
|
Chris@1397
|
1479 snd_seq_port_info_alloca( &src_pinfo );
|
Chris@559
|
1480 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@1397
|
1481 if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) {
|
Chris@1397
|
1482 std::ostringstream ost;
|
Chris@1397
|
1483 ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@559
|
1484 errorString_ = ost.str();
|
Chris@1397
|
1485 error( RtMidiError::INVALID_PARAMETER, errorString_ );
|
Chris@1397
|
1486 return;
|
Chris@559
|
1487 }
|
Chris@559
|
1488
|
Chris@559
|
1489 snd_seq_addr_t sender, receiver;
|
Chris@1397
|
1490 sender.client = snd_seq_port_info_get_client( src_pinfo );
|
Chris@1397
|
1491 sender.port = snd_seq_port_info_get_port( src_pinfo );
|
Chris@1397
|
1492 receiver.client = snd_seq_client_id( data->seq );
|
Chris@1397
|
1493
|
Chris@1397
|
1494 snd_seq_port_info_t *pinfo;
|
Chris@1397
|
1495 snd_seq_port_info_alloca( &pinfo );
|
Chris@559
|
1496 if ( data->vport < 0 ) {
|
Chris@559
|
1497 snd_seq_port_info_set_client( pinfo, 0 );
|
Chris@559
|
1498 snd_seq_port_info_set_port( pinfo, 0 );
|
Chris@559
|
1499 snd_seq_port_info_set_capability( pinfo,
|
Chris@559
|
1500 SND_SEQ_PORT_CAP_WRITE |
|
Chris@559
|
1501 SND_SEQ_PORT_CAP_SUBS_WRITE );
|
Chris@559
|
1502 snd_seq_port_info_set_type( pinfo,
|
Chris@559
|
1503 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
Chris@559
|
1504 SND_SEQ_PORT_TYPE_APPLICATION );
|
Chris@559
|
1505 snd_seq_port_info_set_midi_channels(pinfo, 16);
|
Chris@1397
|
1506 #ifndef AVOID_TIMESTAMPING
|
Chris@559
|
1507 snd_seq_port_info_set_timestamping(pinfo, 1);
|
Chris@559
|
1508 snd_seq_port_info_set_timestamp_real(pinfo, 1);
|
Chris@559
|
1509 snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
|
Chris@1397
|
1510 #endif
|
Chris@565
|
1511 snd_seq_port_info_set_name(pinfo, portName.c_str() );
|
Chris@559
|
1512 data->vport = snd_seq_create_port(data->seq, pinfo);
|
Chris@559
|
1513
|
Chris@559
|
1514 if ( data->vport < 0 ) {
|
Chris@1397
|
1515 errorString_ = "MidiInAlsa::openPort: ALSA error creating input port.";
|
Chris@1397
|
1516 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1517 return;
|
Chris@1397
|
1518 }
|
Chris@1397
|
1519 data->vport = snd_seq_port_info_get_port(pinfo);
|
Chris@1397
|
1520 }
|
Chris@1397
|
1521
|
Chris@1397
|
1522 receiver.port = data->vport;
|
Chris@1397
|
1523
|
Chris@1397
|
1524 if ( !data->subscription ) {
|
Chris@1397
|
1525 // Make subscription
|
Chris@1397
|
1526 if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) {
|
Chris@1397
|
1527 errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription.";
|
Chris@1397
|
1528 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1529 return;
|
Chris@1397
|
1530 }
|
Chris@1397
|
1531 snd_seq_port_subscribe_set_sender(data->subscription, &sender);
|
Chris@1397
|
1532 snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
|
Chris@1397
|
1533 if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
|
Chris@1397
|
1534 snd_seq_port_subscribe_free( data->subscription );
|
Chris@1397
|
1535 data->subscription = 0;
|
Chris@1397
|
1536 errorString_ = "MidiInAlsa::openPort: ALSA error making port connection.";
|
Chris@1397
|
1537 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1538 return;
|
Chris@559
|
1539 }
|
Chris@559
|
1540 }
|
Chris@559
|
1541
|
Chris@559
|
1542 if ( inputData_.doInput == false ) {
|
Chris@559
|
1543 // Start the input queue
|
Chris@1397
|
1544 #ifndef AVOID_TIMESTAMPING
|
Chris@559
|
1545 snd_seq_start_queue( data->seq, data->queue_id, NULL );
|
Chris@559
|
1546 snd_seq_drain_output( data->seq );
|
Chris@1397
|
1547 #endif
|
Chris@559
|
1548 // Start our MIDI input thread.
|
Chris@559
|
1549 pthread_attr_t attr;
|
Chris@559
|
1550 pthread_attr_init(&attr);
|
Chris@559
|
1551 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
Chris@559
|
1552 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
|
Chris@559
|
1553
|
Chris@559
|
1554 inputData_.doInput = true;
|
Chris@559
|
1555 int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
|
Chris@559
|
1556 pthread_attr_destroy(&attr);
|
Chris@1397
|
1557 if ( err ) {
|
Chris@559
|
1558 snd_seq_unsubscribe_port( data->seq, data->subscription );
|
Chris@559
|
1559 snd_seq_port_subscribe_free( data->subscription );
|
Chris@1397
|
1560 data->subscription = 0;
|
Chris@559
|
1561 inputData_.doInput = false;
|
Chris@1397
|
1562 errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";
|
Chris@1397
|
1563 error( RtMidiError::THREAD_ERROR, errorString_ );
|
Chris@1397
|
1564 return;
|
Chris@559
|
1565 }
|
Chris@559
|
1566 }
|
Chris@559
|
1567
|
Chris@559
|
1568 connected_ = true;
|
Chris@559
|
1569 }
|
Chris@559
|
1570
|
Chris@1397
|
1571 void MidiInAlsa :: openVirtualPort( std::string portName )
|
Chris@559
|
1572 {
|
Chris@559
|
1573 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@559
|
1574 if ( data->vport < 0 ) {
|
Chris@559
|
1575 snd_seq_port_info_t *pinfo;
|
Chris@559
|
1576 snd_seq_port_info_alloca( &pinfo );
|
Chris@559
|
1577 snd_seq_port_info_set_capability( pinfo,
|
Chris@559
|
1578 SND_SEQ_PORT_CAP_WRITE |
|
Chris@559
|
1579 SND_SEQ_PORT_CAP_SUBS_WRITE );
|
Chris@559
|
1580 snd_seq_port_info_set_type( pinfo,
|
Chris@559
|
1581 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
Chris@559
|
1582 SND_SEQ_PORT_TYPE_APPLICATION );
|
Chris@559
|
1583 snd_seq_port_info_set_midi_channels(pinfo, 16);
|
Chris@1397
|
1584 #ifndef AVOID_TIMESTAMPING
|
Chris@559
|
1585 snd_seq_port_info_set_timestamping(pinfo, 1);
|
Chris@559
|
1586 snd_seq_port_info_set_timestamp_real(pinfo, 1);
|
Chris@559
|
1587 snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id);
|
Chris@1397
|
1588 #endif
|
Chris@559
|
1589 snd_seq_port_info_set_name(pinfo, portName.c_str());
|
Chris@559
|
1590 data->vport = snd_seq_create_port(data->seq, pinfo);
|
Chris@559
|
1591
|
Chris@559
|
1592 if ( data->vport < 0 ) {
|
Chris@1397
|
1593 errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port.";
|
Chris@1397
|
1594 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1595 return;
|
Chris@559
|
1596 }
|
Chris@1397
|
1597 data->vport = snd_seq_port_info_get_port(pinfo);
|
Chris@559
|
1598 }
|
Chris@559
|
1599
|
Chris@559
|
1600 if ( inputData_.doInput == false ) {
|
Chris@1397
|
1601 // Wait for old thread to stop, if still running
|
Chris@1397
|
1602 if ( !pthread_equal(data->thread, data->dummy_thread_id) )
|
Chris@1397
|
1603 pthread_join( data->thread, NULL );
|
Chris@1397
|
1604
|
Chris@559
|
1605 // Start the input queue
|
Chris@1397
|
1606 #ifndef AVOID_TIMESTAMPING
|
Chris@559
|
1607 snd_seq_start_queue( data->seq, data->queue_id, NULL );
|
Chris@559
|
1608 snd_seq_drain_output( data->seq );
|
Chris@1397
|
1609 #endif
|
Chris@559
|
1610 // Start our MIDI input thread.
|
Chris@559
|
1611 pthread_attr_t attr;
|
Chris@559
|
1612 pthread_attr_init(&attr);
|
Chris@559
|
1613 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
Chris@559
|
1614 pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
|
Chris@559
|
1615
|
Chris@559
|
1616 inputData_.doInput = true;
|
Chris@559
|
1617 int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_);
|
Chris@559
|
1618 pthread_attr_destroy(&attr);
|
Chris@1397
|
1619 if ( err ) {
|
Chris@1397
|
1620 if ( data->subscription ) {
|
Chris@1397
|
1621 snd_seq_unsubscribe_port( data->seq, data->subscription );
|
Chris@1397
|
1622 snd_seq_port_subscribe_free( data->subscription );
|
Chris@1397
|
1623 data->subscription = 0;
|
Chris@1397
|
1624 }
|
Chris@559
|
1625 inputData_.doInput = false;
|
Chris@1397
|
1626 errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!";
|
Chris@1397
|
1627 error( RtMidiError::THREAD_ERROR, errorString_ );
|
Chris@1397
|
1628 return;
|
Chris@559
|
1629 }
|
Chris@559
|
1630 }
|
Chris@559
|
1631 }
|
Chris@559
|
1632
|
Chris@1397
|
1633 void MidiInAlsa :: closePort( void )
|
Chris@559
|
1634 {
|
Chris@1397
|
1635 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@1397
|
1636
|
Chris@559
|
1637 if ( connected_ ) {
|
Chris@1397
|
1638 if ( data->subscription ) {
|
Chris@1397
|
1639 snd_seq_unsubscribe_port( data->seq, data->subscription );
|
Chris@1397
|
1640 snd_seq_port_subscribe_free( data->subscription );
|
Chris@1397
|
1641 data->subscription = 0;
|
Chris@1397
|
1642 }
|
Chris@559
|
1643 // Stop the input queue
|
Chris@1397
|
1644 #ifndef AVOID_TIMESTAMPING
|
Chris@559
|
1645 snd_seq_stop_queue( data->seq, data->queue_id, NULL );
|
Chris@559
|
1646 snd_seq_drain_output( data->seq );
|
Chris@1397
|
1647 #endif
|
Chris@559
|
1648 connected_ = false;
|
Chris@559
|
1649 }
|
Chris@1397
|
1650
|
Chris@1397
|
1651 // Stop thread to avoid triggering the callback, while the port is intended to be closed
|
Chris@1397
|
1652 if ( inputData_.doInput ) {
|
Chris@1397
|
1653 inputData_.doInput = false;
|
Chris@1397
|
1654 int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) );
|
Chris@1397
|
1655 (void) res;
|
Chris@1397
|
1656 if ( !pthread_equal(data->thread, data->dummy_thread_id) )
|
Chris@1397
|
1657 pthread_join( data->thread, NULL );
|
Chris@1397
|
1658 }
|
Chris@559
|
1659 }
|
Chris@559
|
1660
|
Chris@1397
|
1661 //*********************************************************************//
|
Chris@1397
|
1662 // API: LINUX ALSA
|
Chris@1397
|
1663 // Class Definitions: MidiOutAlsa
|
Chris@1397
|
1664 //*********************************************************************//
|
Chris@1397
|
1665
|
Chris@1397
|
1666 MidiOutAlsa :: MidiOutAlsa( const std::string clientName ) : MidiOutApi()
|
Chris@1397
|
1667 {
|
Chris@1397
|
1668 initialize( clientName );
|
Chris@1397
|
1669 }
|
Chris@1397
|
1670
|
Chris@1397
|
1671 MidiOutAlsa :: ~MidiOutAlsa()
|
Chris@559
|
1672 {
|
Chris@559
|
1673 // Close a connection if it exists.
|
Chris@559
|
1674 closePort();
|
Chris@559
|
1675
|
Chris@1397
|
1676 // Cleanup.
|
Chris@559
|
1677 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@559
|
1678 if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport );
|
Chris@1397
|
1679 if ( data->coder ) snd_midi_event_free( data->coder );
|
Chris@1397
|
1680 if ( data->buffer ) free( data->buffer );
|
Chris@559
|
1681 snd_seq_close( data->seq );
|
Chris@559
|
1682 delete data;
|
Chris@559
|
1683 }
|
Chris@559
|
1684
|
Chris@1397
|
1685 void MidiOutAlsa :: initialize( const std::string& clientName )
|
Chris@1397
|
1686 {
|
Chris@1397
|
1687 // Set up the ALSA sequencer client.
|
Chris@1397
|
1688 snd_seq_t *seq;
|
Chris@1397
|
1689 int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK );
|
Chris@1397
|
1690 if ( result1 < 0 ) {
|
Chris@1397
|
1691 errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object.";
|
Chris@1397
|
1692 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1693 return;
|
Chris@1397
|
1694 }
|
Chris@1397
|
1695
|
Chris@1397
|
1696 // Set client name.
|
Chris@1397
|
1697 snd_seq_set_client_name( seq, clientName.c_str() );
|
Chris@1397
|
1698
|
Chris@1397
|
1699 // Save our api-specific connection information.
|
Chris@1397
|
1700 AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData;
|
Chris@1397
|
1701 data->seq = seq;
|
Chris@1397
|
1702 data->portNum = -1;
|
Chris@1397
|
1703 data->vport = -1;
|
Chris@1397
|
1704 data->bufferSize = 32;
|
Chris@1397
|
1705 data->coder = 0;
|
Chris@1397
|
1706 data->buffer = 0;
|
Chris@1397
|
1707 int result = snd_midi_event_new( data->bufferSize, &data->coder );
|
Chris@1397
|
1708 if ( result < 0 ) {
|
Chris@1397
|
1709 delete data;
|
Chris@1397
|
1710 errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n";
|
Chris@1397
|
1711 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1712 return;
|
Chris@1397
|
1713 }
|
Chris@1397
|
1714 data->buffer = (unsigned char *) malloc( data->bufferSize );
|
Chris@1397
|
1715 if ( data->buffer == NULL ) {
|
Chris@1397
|
1716 delete data;
|
Chris@1397
|
1717 errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";
|
Chris@1397
|
1718 error( RtMidiError::MEMORY_ERROR, errorString_ );
|
Chris@1397
|
1719 return;
|
Chris@1397
|
1720 }
|
Chris@1397
|
1721 snd_midi_event_init( data->coder );
|
Chris@1397
|
1722 apiData_ = (void *) data;
|
Chris@1397
|
1723 }
|
Chris@1397
|
1724
|
Chris@1397
|
1725 unsigned int MidiOutAlsa :: getPortCount()
|
Chris@559
|
1726 {
|
Chris@559
|
1727 snd_seq_port_info_t *pinfo;
|
Chris@559
|
1728 snd_seq_port_info_alloca( &pinfo );
|
Chris@559
|
1729
|
Chris@559
|
1730 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@1397
|
1731 return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 );
|
Chris@559
|
1732 }
|
Chris@559
|
1733
|
Chris@1397
|
1734 std::string MidiOutAlsa :: getPortName( unsigned int portNumber )
|
Chris@559
|
1735 {
|
Chris@565
|
1736 snd_seq_client_info_t *cinfo;
|
Chris@565
|
1737 snd_seq_port_info_t *pinfo;
|
Chris@565
|
1738 snd_seq_client_info_alloca( &cinfo );
|
Chris@565
|
1739 snd_seq_port_info_alloca( &pinfo );
|
Chris@559
|
1740
|
Chris@1397
|
1741 std::string stringName;
|
Chris@559
|
1742 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@559
|
1743 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) {
|
Chris@565
|
1744 int cnum = snd_seq_port_info_get_client(pinfo);
|
Chris@565
|
1745 snd_seq_get_any_client_info( data->seq, cnum, cinfo );
|
Chris@565
|
1746 std::ostringstream os;
|
Chris@565
|
1747 os << snd_seq_client_info_get_name(cinfo);
|
Chris@1397
|
1748 os << " "; // These lines added to make sure devices are listed
|
Chris@1397
|
1749 os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names
|
Chris@565
|
1750 os << ":";
|
Chris@565
|
1751 os << snd_seq_port_info_get_port(pinfo);
|
Chris@1397
|
1752 stringName = os.str();
|
Chris@559
|
1753 return stringName;
|
Chris@559
|
1754 }
|
Chris@559
|
1755
|
Chris@559
|
1756 // If we get here, we didn't find a match.
|
Chris@1397
|
1757 errorString_ = "MidiOutAlsa::getPortName: error looking for port name!";
|
Chris@1397
|
1758 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
1759 return stringName;
|
Chris@559
|
1760 }
|
Chris@559
|
1761
|
Chris@1397
|
1762 void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string portName )
|
Chris@559
|
1763 {
|
Chris@559
|
1764 if ( connected_ ) {
|
Chris@1397
|
1765 errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!";
|
Chris@1397
|
1766 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
1767 return;
|
Chris@559
|
1768 }
|
Chris@559
|
1769
|
Chris@559
|
1770 unsigned int nSrc = this->getPortCount();
|
Chris@559
|
1771 if (nSrc < 1) {
|
Chris@1397
|
1772 errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!";
|
Chris@1397
|
1773 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
|
Chris@1397
|
1774 return;
|
Chris@559
|
1775 }
|
Chris@559
|
1776
|
Chris@565
|
1777 snd_seq_port_info_t *pinfo;
|
Chris@565
|
1778 snd_seq_port_info_alloca( &pinfo );
|
Chris@559
|
1779 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@559
|
1780 if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) {
|
Chris@1397
|
1781 std::ostringstream ost;
|
Chris@1397
|
1782 ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@559
|
1783 errorString_ = ost.str();
|
Chris@1397
|
1784 error( RtMidiError::INVALID_PARAMETER, errorString_ );
|
Chris@1397
|
1785 return;
|
Chris@559
|
1786 }
|
Chris@559
|
1787
|
Chris@559
|
1788 snd_seq_addr_t sender, receiver;
|
Chris@1397
|
1789 receiver.client = snd_seq_port_info_get_client( pinfo );
|
Chris@1397
|
1790 receiver.port = snd_seq_port_info_get_port( pinfo );
|
Chris@1397
|
1791 sender.client = snd_seq_client_id( data->seq );
|
Chris@559
|
1792
|
Chris@559
|
1793 if ( data->vport < 0 ) {
|
Chris@565
|
1794 data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
|
Chris@559
|
1795 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
|
Chris@1397
|
1796 SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
|
Chris@559
|
1797 if ( data->vport < 0 ) {
|
Chris@1397
|
1798 errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port.";
|
Chris@1397
|
1799 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1800 return;
|
Chris@559
|
1801 }
|
Chris@559
|
1802 }
|
Chris@559
|
1803
|
Chris@1397
|
1804 sender.port = data->vport;
|
Chris@559
|
1805
|
Chris@559
|
1806 // Make subscription
|
Chris@1397
|
1807 if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) {
|
Chris@1397
|
1808 snd_seq_port_subscribe_free( data->subscription );
|
Chris@1397
|
1809 errorString_ = "MidiOutAlsa::openPort: error allocating port subscription.";
|
Chris@1397
|
1810 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1811 return;
|
Chris@1397
|
1812 }
|
Chris@559
|
1813 snd_seq_port_subscribe_set_sender(data->subscription, &sender);
|
Chris@559
|
1814 snd_seq_port_subscribe_set_dest(data->subscription, &receiver);
|
Chris@559
|
1815 snd_seq_port_subscribe_set_time_update(data->subscription, 1);
|
Chris@559
|
1816 snd_seq_port_subscribe_set_time_real(data->subscription, 1);
|
Chris@559
|
1817 if ( snd_seq_subscribe_port(data->seq, data->subscription) ) {
|
Chris@1397
|
1818 snd_seq_port_subscribe_free( data->subscription );
|
Chris@1397
|
1819 errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection.";
|
Chris@1397
|
1820 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1821 return;
|
Chris@559
|
1822 }
|
Chris@559
|
1823
|
Chris@559
|
1824 connected_ = true;
|
Chris@559
|
1825 }
|
Chris@559
|
1826
|
Chris@1397
|
1827 void MidiOutAlsa :: closePort( void )
|
Chris@559
|
1828 {
|
Chris@559
|
1829 if ( connected_ ) {
|
Chris@559
|
1830 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@559
|
1831 snd_seq_unsubscribe_port( data->seq, data->subscription );
|
Chris@559
|
1832 snd_seq_port_subscribe_free( data->subscription );
|
Chris@559
|
1833 connected_ = false;
|
Chris@559
|
1834 }
|
Chris@559
|
1835 }
|
Chris@559
|
1836
|
Chris@1397
|
1837 void MidiOutAlsa :: openVirtualPort( std::string portName )
|
Chris@559
|
1838 {
|
Chris@559
|
1839 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@559
|
1840 if ( data->vport < 0 ) {
|
Chris@559
|
1841 data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(),
|
Chris@559
|
1842 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
|
Chris@1397
|
1843 SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION );
|
Chris@559
|
1844
|
Chris@559
|
1845 if ( data->vport < 0 ) {
|
Chris@1397
|
1846 errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port.";
|
Chris@1397
|
1847 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@559
|
1848 }
|
Chris@559
|
1849 }
|
Chris@559
|
1850 }
|
Chris@559
|
1851
|
Chris@1397
|
1852 void MidiOutAlsa :: sendMessage( std::vector<unsigned char> *message )
|
Chris@559
|
1853 {
|
Chris@559
|
1854 int result;
|
Chris@559
|
1855 AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_);
|
Chris@1397
|
1856 unsigned int nBytes = message->size();
|
Chris@559
|
1857 if ( nBytes > data->bufferSize ) {
|
Chris@559
|
1858 data->bufferSize = nBytes;
|
Chris@559
|
1859 result = snd_midi_event_resize_buffer ( data->coder, nBytes);
|
Chris@559
|
1860 if ( result != 0 ) {
|
Chris@1397
|
1861 errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer.";
|
Chris@1397
|
1862 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
1863 return;
|
Chris@559
|
1864 }
|
Chris@559
|
1865 free (data->buffer);
|
Chris@559
|
1866 data->buffer = (unsigned char *) malloc( data->bufferSize );
|
Chris@559
|
1867 if ( data->buffer == NULL ) {
|
Chris@1397
|
1868 errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n";
|
Chris@1397
|
1869 error( RtMidiError::MEMORY_ERROR, errorString_ );
|
Chris@1397
|
1870 return;
|
Chris@559
|
1871 }
|
Chris@559
|
1872 }
|
Chris@559
|
1873
|
Chris@559
|
1874 snd_seq_event_t ev;
|
Chris@559
|
1875 snd_seq_ev_clear(&ev);
|
Chris@1397
|
1876 snd_seq_ev_set_source(&ev, data->vport);
|
Chris@559
|
1877 snd_seq_ev_set_subs(&ev);
|
Chris@559
|
1878 snd_seq_ev_set_direct(&ev);
|
Chris@1397
|
1879 for ( unsigned int i=0; i<nBytes; ++i ) data->buffer[i] = message->at(i);
|
Chris@1397
|
1880 result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev );
|
Chris@559
|
1881 if ( result < (int)nBytes ) {
|
Chris@1397
|
1882 errorString_ = "MidiOutAlsa::sendMessage: event parsing error!";
|
Chris@1397
|
1883 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
1884 return;
|
Chris@559
|
1885 }
|
Chris@559
|
1886
|
Chris@559
|
1887 // Send the event.
|
Chris@559
|
1888 result = snd_seq_event_output(data->seq, &ev);
|
Chris@559
|
1889 if ( result < 0 ) {
|
Chris@1397
|
1890 errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port.";
|
Chris@1397
|
1891 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
1892 return;
|
Chris@559
|
1893 }
|
Chris@559
|
1894 snd_seq_drain_output(data->seq);
|
Chris@559
|
1895 }
|
Chris@559
|
1896
|
Chris@559
|
1897 #endif // __LINUX_ALSA__
|
Chris@559
|
1898
|
Chris@559
|
1899
|
Chris@559
|
1900 //*********************************************************************//
|
Chris@559
|
1901 // API: Windows Multimedia Library (MM)
|
Chris@559
|
1902 //*********************************************************************//
|
Chris@559
|
1903
|
Chris@559
|
1904 // API information deciphered from:
|
Chris@559
|
1905 // - http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_midi_reference.asp
|
Chris@559
|
1906
|
Chris@559
|
1907 // Thanks to Jean-Baptiste Berruchon for the sysex code.
|
Chris@559
|
1908
|
Chris@559
|
1909 #if defined(__WINDOWS_MM__)
|
Chris@559
|
1910
|
Chris@559
|
1911 // The Windows MM API is based on the use of a callback function for
|
Chris@559
|
1912 // MIDI input. We convert the system specific time stamps to delta
|
Chris@559
|
1913 // time values.
|
Chris@559
|
1914
|
Chris@559
|
1915 // Windows MM MIDI header files.
|
Chris@559
|
1916 #include <windows.h>
|
Chris@559
|
1917 #include <mmsystem.h>
|
Chris@559
|
1918
|
Chris@1397
|
1919 #define RT_SYSEX_BUFFER_SIZE 1024
|
Chris@1397
|
1920 #define RT_SYSEX_BUFFER_COUNT 4
|
Chris@1397
|
1921
|
Chris@559
|
1922 // A structure to hold variables related to the CoreMIDI API
|
Chris@559
|
1923 // implementation.
|
Chris@559
|
1924 struct WinMidiData {
|
Chris@559
|
1925 HMIDIIN inHandle; // Handle to Midi Input Device
|
Chris@559
|
1926 HMIDIOUT outHandle; // Handle to Midi Output Device
|
Chris@559
|
1927 DWORD lastTime;
|
Chris@1397
|
1928 MidiInApi::MidiMessage message;
|
Chris@1397
|
1929 LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT];
|
Chris@1397
|
1930 CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo
|
Chris@559
|
1931 };
|
Chris@559
|
1932
|
Chris@559
|
1933 //*********************************************************************//
|
Chris@559
|
1934 // API: Windows MM
|
Chris@1397
|
1935 // Class Definitions: MidiInWinMM
|
Chris@559
|
1936 //*********************************************************************//
|
Chris@559
|
1937
|
Chris@1397
|
1938 static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/,
|
Chris@559
|
1939 UINT inputStatus,
|
Chris@1397
|
1940 DWORD_PTR instancePtr,
|
Chris@1397
|
1941 DWORD_PTR midiMessage,
|
Chris@559
|
1942 DWORD timestamp )
|
Chris@559
|
1943 {
|
Chris@1397
|
1944 if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return;
|
Chris@1397
|
1945
|
Chris@1397
|
1946 //MidiInApi::RtMidiInData *data = static_cast<MidiInApi::RtMidiInData *> (instancePtr);
|
Chris@1397
|
1947 MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr;
|
Chris@559
|
1948 WinMidiData *apiData = static_cast<WinMidiData *> (data->apiData);
|
Chris@559
|
1949
|
Chris@559
|
1950 // Calculate time stamp.
|
Chris@1397
|
1951 if ( data->firstMessage == true ) {
|
Chris@1397
|
1952 apiData->message.timeStamp = 0.0;
|
Chris@1397
|
1953 data->firstMessage = false;
|
Chris@1397
|
1954 }
|
Chris@559
|
1955 else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001;
|
Chris@559
|
1956 apiData->lastTime = timestamp;
|
Chris@559
|
1957
|
Chris@559
|
1958 if ( inputStatus == MIM_DATA ) { // Channel or system message
|
Chris@559
|
1959
|
Chris@559
|
1960 // Make sure the first byte is a status byte.
|
Chris@559
|
1961 unsigned char status = (unsigned char) (midiMessage & 0x000000FF);
|
Chris@559
|
1962 if ( !(status & 0x80) ) return;
|
Chris@559
|
1963
|
Chris@559
|
1964 // Determine the number of bytes in the MIDI message.
|
Chris@559
|
1965 unsigned short nBytes = 1;
|
Chris@559
|
1966 if ( status < 0xC0 ) nBytes = 3;
|
Chris@559
|
1967 else if ( status < 0xE0 ) nBytes = 2;
|
Chris@559
|
1968 else if ( status < 0xF0 ) nBytes = 3;
|
Chris@1397
|
1969 else if ( status == 0xF1 ) {
|
Chris@1397
|
1970 if ( data->ignoreFlags & 0x02 ) return;
|
Chris@1397
|
1971 else nBytes = 2;
|
Chris@559
|
1972 }
|
Chris@1397
|
1973 else if ( status == 0xF2 ) nBytes = 3;
|
Chris@559
|
1974 else if ( status == 0xF3 ) nBytes = 2;
|
Chris@559
|
1975 else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) {
|
Chris@559
|
1976 // A MIDI timing tick message and we're ignoring it.
|
Chris@559
|
1977 return;
|
Chris@559
|
1978 }
|
Chris@559
|
1979 else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) {
|
Chris@559
|
1980 // A MIDI active sensing message and we're ignoring it.
|
Chris@559
|
1981 return;
|
Chris@559
|
1982 }
|
Chris@559
|
1983
|
Chris@559
|
1984 // Copy bytes to our MIDI message.
|
Chris@559
|
1985 unsigned char *ptr = (unsigned char *) &midiMessage;
|
Chris@1397
|
1986 for ( int i=0; i<nBytes; ++i ) apiData->message.bytes.push_back( *ptr++ );
|
Chris@559
|
1987 }
|
Chris@1397
|
1988 else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR )
|
Chris@565
|
1989 MIDIHDR *sysex = ( MIDIHDR *) midiMessage;
|
Chris@1397
|
1990 if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) {
|
Chris@565
|
1991 // Sysex message and we're not ignoring it
|
Chris@1397
|
1992 for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i )
|
Chris@565
|
1993 apiData->message.bytes.push_back( sysex->lpData[i] );
|
Chris@565
|
1994 }
|
Chris@559
|
1995
|
Chris@565
|
1996 // The WinMM API requires that the sysex buffer be requeued after
|
Chris@565
|
1997 // input of each sysex message. Even if we are ignoring sysex
|
Chris@565
|
1998 // messages, we still need to requeue the buffer in case the user
|
Chris@565
|
1999 // decides to not ignore sysex messages in the future. However,
|
Chris@565
|
2000 // it seems that WinMM calls this function with an empty sysex
|
Chris@565
|
2001 // buffer when an application closes and in this case, we should
|
Chris@565
|
2002 // avoid requeueing it, else the computer suddenly reboots after
|
Chris@565
|
2003 // one or two minutes.
|
Chris@1397
|
2004 if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) {
|
Chris@565
|
2005 //if ( sysex->dwBytesRecorded > 0 ) {
|
Chris@1397
|
2006 EnterCriticalSection( &(apiData->_mutex) );
|
Chris@1397
|
2007 MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) );
|
Chris@1397
|
2008 LeaveCriticalSection( &(apiData->_mutex) );
|
Chris@559
|
2009 if ( result != MMSYSERR_NOERROR )
|
Chris@1397
|
2010 std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n";
|
Chris@565
|
2011
|
Chris@565
|
2012 if ( data->ignoreFlags & 0x01 ) return;
|
Chris@559
|
2013 }
|
Chris@565
|
2014 else return;
|
Chris@559
|
2015 }
|
Chris@559
|
2016
|
Chris@559
|
2017 if ( data->usingCallback ) {
|
Chris@559
|
2018 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback;
|
Chris@559
|
2019 callback( apiData->message.timeStamp, &apiData->message.bytes, data->userData );
|
Chris@559
|
2020 }
|
Chris@559
|
2021 else {
|
Chris@559
|
2022 // As long as we haven't reached our queue size limit, push the message.
|
Chris@1397
|
2023 if ( data->queue.size < data->queue.ringSize ) {
|
Chris@1397
|
2024 data->queue.ring[data->queue.back++] = apiData->message;
|
Chris@1397
|
2025 if ( data->queue.back == data->queue.ringSize )
|
Chris@1397
|
2026 data->queue.back = 0;
|
Chris@1397
|
2027 data->queue.size++;
|
Chris@1397
|
2028 }
|
Chris@559
|
2029 else
|
Chris@1397
|
2030 std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n";
|
Chris@559
|
2031 }
|
Chris@559
|
2032
|
Chris@565
|
2033 // Clear the vector for the next input message.
|
Chris@559
|
2034 apiData->message.bytes.clear();
|
Chris@559
|
2035 }
|
Chris@559
|
2036
|
Chris@1397
|
2037 MidiInWinMM :: MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
|
Chris@1397
|
2038 {
|
Chris@1397
|
2039 initialize( clientName );
|
Chris@1397
|
2040 }
|
Chris@1397
|
2041
|
Chris@1397
|
2042 MidiInWinMM :: ~MidiInWinMM()
|
Chris@1397
|
2043 {
|
Chris@1397
|
2044 // Close a connection if it exists.
|
Chris@1397
|
2045 closePort();
|
Chris@1397
|
2046
|
Chris@1397
|
2047 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
|
Chris@1397
|
2048 DeleteCriticalSection( &(data->_mutex) );
|
Chris@1397
|
2049
|
Chris@1397
|
2050 // Cleanup.
|
Chris@1397
|
2051 delete data;
|
Chris@1397
|
2052 }
|
Chris@1397
|
2053
|
Chris@1397
|
2054 void MidiInWinMM :: initialize( const std::string& /*clientName*/ )
|
Chris@559
|
2055 {
|
Chris@559
|
2056 // We'll issue a warning here if no devices are available but not
|
Chris@559
|
2057 // throw an error since the user can plugin something later.
|
Chris@559
|
2058 unsigned int nDevices = midiInGetNumDevs();
|
Chris@559
|
2059 if ( nDevices == 0 ) {
|
Chris@1397
|
2060 errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available.";
|
Chris@1397
|
2061 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
2062 }
|
Chris@559
|
2063
|
Chris@559
|
2064 // Save our api-specific connection information.
|
Chris@559
|
2065 WinMidiData *data = (WinMidiData *) new WinMidiData;
|
Chris@559
|
2066 apiData_ = (void *) data;
|
Chris@559
|
2067 inputData_.apiData = (void *) data;
|
Chris@559
|
2068 data->message.bytes.clear(); // needs to be empty for first input message
|
Chris@1397
|
2069
|
Chris@1397
|
2070 if ( !InitializeCriticalSectionAndSpinCount(&(data->_mutex), 0x00000400) ) {
|
Chris@1397
|
2071 errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed.";
|
Chris@1397
|
2072 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
2073 }
|
Chris@559
|
2074 }
|
Chris@559
|
2075
|
Chris@1397
|
2076 void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ )
|
Chris@559
|
2077 {
|
Chris@559
|
2078 if ( connected_ ) {
|
Chris@1397
|
2079 errorString_ = "MidiInWinMM::openPort: a valid connection already exists!";
|
Chris@1397
|
2080 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
2081 return;
|
Chris@559
|
2082 }
|
Chris@559
|
2083
|
Chris@559
|
2084 unsigned int nDevices = midiInGetNumDevs();
|
Chris@559
|
2085 if (nDevices == 0) {
|
Chris@1397
|
2086 errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!";
|
Chris@1397
|
2087 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
|
Chris@1397
|
2088 return;
|
Chris@559
|
2089 }
|
Chris@559
|
2090
|
Chris@559
|
2091 if ( portNumber >= nDevices ) {
|
Chris@1397
|
2092 std::ostringstream ost;
|
Chris@1397
|
2093 ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@559
|
2094 errorString_ = ost.str();
|
Chris@1397
|
2095 error( RtMidiError::INVALID_PARAMETER, errorString_ );
|
Chris@1397
|
2096 return;
|
Chris@559
|
2097 }
|
Chris@559
|
2098
|
Chris@559
|
2099 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
|
Chris@559
|
2100 MMRESULT result = midiInOpen( &data->inHandle,
|
Chris@559
|
2101 portNumber,
|
Chris@1397
|
2102 (DWORD_PTR)&midiInputCallback,
|
Chris@1397
|
2103 (DWORD_PTR)&inputData_,
|
Chris@559
|
2104 CALLBACK_FUNCTION );
|
Chris@559
|
2105 if ( result != MMSYSERR_NOERROR ) {
|
Chris@1397
|
2106 errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port.";
|
Chris@1397
|
2107 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2108 return;
|
Chris@559
|
2109 }
|
Chris@559
|
2110
|
Chris@1397
|
2111 // Allocate and init the sysex buffers.
|
Chris@1397
|
2112 for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
|
Chris@1397
|
2113 data->sysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ];
|
Chris@1397
|
2114 data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ];
|
Chris@1397
|
2115 data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE;
|
Chris@1397
|
2116 data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator
|
Chris@1397
|
2117 data->sysexBuffer[i]->dwFlags = 0;
|
Chris@1397
|
2118
|
Chris@1397
|
2119 result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
|
Chris@1397
|
2120 if ( result != MMSYSERR_NOERROR ) {
|
Chris@1397
|
2121 midiInClose( data->inHandle );
|
Chris@1397
|
2122 errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader).";
|
Chris@1397
|
2123 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2124 return;
|
Chris@1397
|
2125 }
|
Chris@1397
|
2126
|
Chris@1397
|
2127 // Register the buffer.
|
Chris@1397
|
2128 result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) );
|
Chris@1397
|
2129 if ( result != MMSYSERR_NOERROR ) {
|
Chris@1397
|
2130 midiInClose( data->inHandle );
|
Chris@1397
|
2131 errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer).";
|
Chris@1397
|
2132 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2133 return;
|
Chris@1397
|
2134 }
|
Chris@559
|
2135 }
|
Chris@559
|
2136
|
Chris@559
|
2137 result = midiInStart( data->inHandle );
|
Chris@559
|
2138 if ( result != MMSYSERR_NOERROR ) {
|
Chris@559
|
2139 midiInClose( data->inHandle );
|
Chris@1397
|
2140 errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port.";
|
Chris@1397
|
2141 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2142 return;
|
Chris@559
|
2143 }
|
Chris@559
|
2144
|
Chris@559
|
2145 connected_ = true;
|
Chris@559
|
2146 }
|
Chris@559
|
2147
|
Chris@1397
|
2148 void MidiInWinMM :: openVirtualPort( std::string /*portName*/ )
|
Chris@559
|
2149 {
|
Chris@559
|
2150 // This function cannot be implemented for the Windows MM MIDI API.
|
Chris@1397
|
2151 errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
|
Chris@1397
|
2152 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
2153 }
|
Chris@559
|
2154
|
Chris@1397
|
2155 void MidiInWinMM :: closePort( void )
|
Chris@559
|
2156 {
|
Chris@559
|
2157 if ( connected_ ) {
|
Chris@559
|
2158 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
|
Chris@1397
|
2159 EnterCriticalSection( &(data->_mutex) );
|
Chris@559
|
2160 midiInReset( data->inHandle );
|
Chris@559
|
2161 midiInStop( data->inHandle );
|
Chris@559
|
2162
|
Chris@1397
|
2163 for ( int i=0; i<RT_SYSEX_BUFFER_COUNT; ++i ) {
|
Chris@1397
|
2164 int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR));
|
Chris@1397
|
2165 delete [] data->sysexBuffer[i]->lpData;
|
Chris@1397
|
2166 delete [] data->sysexBuffer[i];
|
Chris@1397
|
2167 if ( result != MMSYSERR_NOERROR ) {
|
Chris@1397
|
2168 midiInClose( data->inHandle );
|
Chris@1397
|
2169 errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader).";
|
Chris@1397
|
2170 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2171 return;
|
Chris@1397
|
2172 }
|
Chris@559
|
2173 }
|
Chris@559
|
2174
|
Chris@559
|
2175 midiInClose( data->inHandle );
|
Chris@559
|
2176 connected_ = false;
|
Chris@1397
|
2177 LeaveCriticalSection( &(data->_mutex) );
|
Chris@559
|
2178 }
|
Chris@559
|
2179 }
|
Chris@559
|
2180
|
Chris@1397
|
2181 unsigned int MidiInWinMM :: getPortCount()
|
Chris@1397
|
2182 {
|
Chris@1397
|
2183 return midiInGetNumDevs();
|
Chris@1397
|
2184 }
|
Chris@1397
|
2185
|
Chris@1397
|
2186 std::string MidiInWinMM :: getPortName( unsigned int portNumber )
|
Chris@1397
|
2187 {
|
Chris@1397
|
2188 std::string stringName;
|
Chris@1397
|
2189 unsigned int nDevices = midiInGetNumDevs();
|
Chris@1397
|
2190 if ( portNumber >= nDevices ) {
|
Chris@1397
|
2191 std::ostringstream ost;
|
Chris@1397
|
2192 ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@1397
|
2193 errorString_ = ost.str();
|
Chris@1397
|
2194 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
2195 return stringName;
|
Chris@1397
|
2196 }
|
Chris@1397
|
2197
|
Chris@1397
|
2198 MIDIINCAPS deviceCaps;
|
Chris@1397
|
2199 midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS));
|
Chris@1397
|
2200
|
Chris@1397
|
2201 #if defined( UNICODE ) || defined( _UNICODE )
|
Chris@1397
|
2202 int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1;
|
Chris@1397
|
2203 stringName.assign( length, 0 );
|
Chris@1397
|
2204 length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast<int>(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL);
|
Chris@1397
|
2205 #else
|
Chris@1397
|
2206 stringName = std::string( deviceCaps.szPname );
|
Chris@1397
|
2207 #endif
|
Chris@1397
|
2208
|
Chris@1397
|
2209 // Next lines added to add the portNumber to the name so that
|
Chris@1397
|
2210 // the device's names are sure to be listed with individual names
|
Chris@1397
|
2211 // even when they have the same brand name
|
Chris@1397
|
2212 std::ostringstream os;
|
Chris@1397
|
2213 os << " ";
|
Chris@1397
|
2214 os << portNumber;
|
Chris@1397
|
2215 stringName += os.str();
|
Chris@1397
|
2216
|
Chris@1397
|
2217 return stringName;
|
Chris@1397
|
2218 }
|
Chris@1397
|
2219
|
Chris@1397
|
2220 //*********************************************************************//
|
Chris@1397
|
2221 // API: Windows MM
|
Chris@1397
|
2222 // Class Definitions: MidiOutWinMM
|
Chris@1397
|
2223 //*********************************************************************//
|
Chris@1397
|
2224
|
Chris@1397
|
2225 MidiOutWinMM :: MidiOutWinMM( const std::string clientName ) : MidiOutApi()
|
Chris@1397
|
2226 {
|
Chris@1397
|
2227 initialize( clientName );
|
Chris@1397
|
2228 }
|
Chris@1397
|
2229
|
Chris@1397
|
2230 MidiOutWinMM :: ~MidiOutWinMM()
|
Chris@559
|
2231 {
|
Chris@559
|
2232 // Close a connection if it exists.
|
Chris@559
|
2233 closePort();
|
Chris@559
|
2234
|
Chris@559
|
2235 // Cleanup.
|
Chris@559
|
2236 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
|
Chris@559
|
2237 delete data;
|
Chris@559
|
2238 }
|
Chris@559
|
2239
|
Chris@1397
|
2240 void MidiOutWinMM :: initialize( const std::string& /*clientName*/ )
|
Chris@559
|
2241 {
|
Chris@559
|
2242 // We'll issue a warning here if no devices are available but not
|
Chris@559
|
2243 // throw an error since the user can plug something in later.
|
Chris@559
|
2244 unsigned int nDevices = midiOutGetNumDevs();
|
Chris@559
|
2245 if ( nDevices == 0 ) {
|
Chris@1397
|
2246 errorString_ = "MidiOutWinMM::initialize: no MIDI output devices currently available.";
|
Chris@1397
|
2247 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
2248 }
|
Chris@559
|
2249
|
Chris@559
|
2250 // Save our api-specific connection information.
|
Chris@559
|
2251 WinMidiData *data = (WinMidiData *) new WinMidiData;
|
Chris@559
|
2252 apiData_ = (void *) data;
|
Chris@559
|
2253 }
|
Chris@559
|
2254
|
Chris@1397
|
2255 unsigned int MidiOutWinMM :: getPortCount()
|
Chris@1397
|
2256 {
|
Chris@1397
|
2257 return midiOutGetNumDevs();
|
Chris@1397
|
2258 }
|
Chris@1397
|
2259
|
Chris@1397
|
2260 std::string MidiOutWinMM :: getPortName( unsigned int portNumber )
|
Chris@1397
|
2261 {
|
Chris@1397
|
2262 std::string stringName;
|
Chris@1397
|
2263 unsigned int nDevices = midiOutGetNumDevs();
|
Chris@1397
|
2264 if ( portNumber >= nDevices ) {
|
Chris@1397
|
2265 std::ostringstream ost;
|
Chris@1397
|
2266 ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@1397
|
2267 errorString_ = ost.str();
|
Chris@1397
|
2268 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
2269 return stringName;
|
Chris@1397
|
2270 }
|
Chris@1397
|
2271
|
Chris@1397
|
2272 MIDIOUTCAPS deviceCaps;
|
Chris@1397
|
2273 midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS));
|
Chris@1397
|
2274
|
Chris@1397
|
2275 #if defined( UNICODE ) || defined( _UNICODE )
|
Chris@1397
|
2276 int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1;
|
Chris@1397
|
2277 stringName.assign( length, 0 );
|
Chris@1397
|
2278 length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast<int>(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL);
|
Chris@1397
|
2279 #else
|
Chris@1397
|
2280 stringName = std::string( deviceCaps.szPname );
|
Chris@1397
|
2281 #endif
|
Chris@1397
|
2282
|
Chris@1397
|
2283 // Next lines added to add the portNumber to the name so that
|
Chris@1397
|
2284 // the device's names are sure to be listed with individual names
|
Chris@1397
|
2285 // even when they have the same brand name
|
Chris@1397
|
2286 std::ostringstream os;
|
Chris@1397
|
2287 os << " ";
|
Chris@1397
|
2288 os << portNumber;
|
Chris@1397
|
2289 stringName += os.str();
|
Chris@1397
|
2290
|
Chris@1397
|
2291 return stringName;
|
Chris@1397
|
2292 }
|
Chris@1397
|
2293
|
Chris@1397
|
2294 void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ )
|
Chris@559
|
2295 {
|
Chris@559
|
2296 if ( connected_ ) {
|
Chris@1397
|
2297 errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!";
|
Chris@1397
|
2298 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
2299 return;
|
Chris@559
|
2300 }
|
Chris@559
|
2301
|
Chris@559
|
2302 unsigned int nDevices = midiOutGetNumDevs();
|
Chris@559
|
2303 if (nDevices < 1) {
|
Chris@1397
|
2304 errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!";
|
Chris@1397
|
2305 error( RtMidiError::NO_DEVICES_FOUND, errorString_ );
|
Chris@1397
|
2306 return;
|
Chris@559
|
2307 }
|
Chris@559
|
2308
|
Chris@559
|
2309 if ( portNumber >= nDevices ) {
|
Chris@1397
|
2310 std::ostringstream ost;
|
Chris@1397
|
2311 ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@559
|
2312 errorString_ = ost.str();
|
Chris@1397
|
2313 error( RtMidiError::INVALID_PARAMETER, errorString_ );
|
Chris@1397
|
2314 return;
|
Chris@559
|
2315 }
|
Chris@559
|
2316
|
Chris@559
|
2317 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
|
Chris@559
|
2318 MMRESULT result = midiOutOpen( &data->outHandle,
|
Chris@559
|
2319 portNumber,
|
Chris@559
|
2320 (DWORD)NULL,
|
Chris@559
|
2321 (DWORD)NULL,
|
Chris@559
|
2322 CALLBACK_NULL );
|
Chris@559
|
2323 if ( result != MMSYSERR_NOERROR ) {
|
Chris@1397
|
2324 errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port.";
|
Chris@1397
|
2325 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2326 return;
|
Chris@559
|
2327 }
|
Chris@559
|
2328
|
Chris@559
|
2329 connected_ = true;
|
Chris@559
|
2330 }
|
Chris@559
|
2331
|
Chris@1397
|
2332 void MidiOutWinMM :: closePort( void )
|
Chris@559
|
2333 {
|
Chris@559
|
2334 if ( connected_ ) {
|
Chris@559
|
2335 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
|
Chris@559
|
2336 midiOutReset( data->outHandle );
|
Chris@559
|
2337 midiOutClose( data->outHandle );
|
Chris@559
|
2338 connected_ = false;
|
Chris@559
|
2339 }
|
Chris@559
|
2340 }
|
Chris@559
|
2341
|
Chris@1397
|
2342 void MidiOutWinMM :: openVirtualPort( std::string /*portName*/ )
|
Chris@559
|
2343 {
|
Chris@559
|
2344 // This function cannot be implemented for the Windows MM MIDI API.
|
Chris@1397
|
2345 errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!";
|
Chris@1397
|
2346 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
2347 }
|
Chris@559
|
2348
|
Chris@1397
|
2349 void MidiOutWinMM :: sendMessage( std::vector<unsigned char> *message )
|
Chris@559
|
2350 {
|
Chris@1397
|
2351 if ( !connected_ ) return;
|
Chris@1397
|
2352
|
Chris@1397
|
2353 unsigned int nBytes = static_cast<unsigned int>(message->size());
|
Chris@559
|
2354 if ( nBytes == 0 ) {
|
Chris@1397
|
2355 errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!";
|
Chris@1397
|
2356 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
2357 return;
|
Chris@559
|
2358 }
|
Chris@559
|
2359
|
Chris@559
|
2360 MMRESULT result;
|
Chris@559
|
2361 WinMidiData *data = static_cast<WinMidiData *> (apiData_);
|
Chris@559
|
2362 if ( message->at(0) == 0xF0 ) { // Sysex message
|
Chris@559
|
2363
|
Chris@559
|
2364 // Allocate buffer for sysex data.
|
Chris@559
|
2365 char *buffer = (char *) malloc( nBytes );
|
Chris@559
|
2366 if ( buffer == NULL ) {
|
Chris@1397
|
2367 errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!";
|
Chris@1397
|
2368 error( RtMidiError::MEMORY_ERROR, errorString_ );
|
Chris@1397
|
2369 return;
|
Chris@559
|
2370 }
|
Chris@559
|
2371
|
Chris@559
|
2372 // Copy data to buffer.
|
Chris@1397
|
2373 for ( unsigned int i=0; i<nBytes; ++i ) buffer[i] = message->at(i);
|
Chris@559
|
2374
|
Chris@559
|
2375 // Create and prepare MIDIHDR structure.
|
Chris@559
|
2376 MIDIHDR sysex;
|
Chris@559
|
2377 sysex.lpData = (LPSTR) buffer;
|
Chris@559
|
2378 sysex.dwBufferLength = nBytes;
|
Chris@559
|
2379 sysex.dwFlags = 0;
|
Chris@559
|
2380 result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) );
|
Chris@559
|
2381 if ( result != MMSYSERR_NOERROR ) {
|
Chris@559
|
2382 free( buffer );
|
Chris@1397
|
2383 errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header.";
|
Chris@1397
|
2384 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2385 return;
|
Chris@559
|
2386 }
|
Chris@559
|
2387
|
Chris@559
|
2388 // Send the message.
|
Chris@559
|
2389 result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) );
|
Chris@559
|
2390 if ( result != MMSYSERR_NOERROR ) {
|
Chris@559
|
2391 free( buffer );
|
Chris@1397
|
2392 errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message.";
|
Chris@1397
|
2393 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2394 return;
|
Chris@559
|
2395 }
|
Chris@559
|
2396
|
Chris@559
|
2397 // Unprepare the buffer and MIDIHDR.
|
Chris@559
|
2398 while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 );
|
Chris@559
|
2399 free( buffer );
|
Chris@559
|
2400 }
|
Chris@559
|
2401 else { // Channel or system message.
|
Chris@559
|
2402
|
Chris@559
|
2403 // Make sure the message size isn't too big.
|
Chris@559
|
2404 if ( nBytes > 3 ) {
|
Chris@1397
|
2405 errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!";
|
Chris@1397
|
2406 error( RtMidiError::WARNING, errorString_ );
|
Chris@559
|
2407 return;
|
Chris@559
|
2408 }
|
Chris@559
|
2409
|
Chris@559
|
2410 // Pack MIDI bytes into double word.
|
Chris@559
|
2411 DWORD packet;
|
Chris@559
|
2412 unsigned char *ptr = (unsigned char *) &packet;
|
Chris@1397
|
2413 for ( unsigned int i=0; i<nBytes; ++i ) {
|
Chris@559
|
2414 *ptr = message->at(i);
|
Chris@1397
|
2415 ++ptr;
|
Chris@559
|
2416 }
|
Chris@559
|
2417
|
Chris@559
|
2418 // Send the message immediately.
|
Chris@559
|
2419 result = midiOutShortMsg( data->outHandle, packet );
|
Chris@559
|
2420 if ( result != MMSYSERR_NOERROR ) {
|
Chris@1397
|
2421 errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message.";
|
Chris@1397
|
2422 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@559
|
2423 }
|
Chris@559
|
2424 }
|
Chris@559
|
2425 }
|
Chris@559
|
2426
|
Chris@559
|
2427 #endif // __WINDOWS_MM__
|
Chris@609
|
2428
|
Chris@1397
|
2429
|
Chris@1397
|
2430 //*********************************************************************//
|
Chris@1397
|
2431 // API: UNIX JACK
|
Chris@1397
|
2432 //
|
Chris@1397
|
2433 // Written primarily by Alexander Svetalkin, with updates for delta
|
Chris@1397
|
2434 // time by Gary Scavone, April 2011.
|
Chris@1397
|
2435 //
|
Chris@1397
|
2436 // *********************************************************************//
|
Chris@1397
|
2437
|
Chris@1397
|
2438 #if defined(__UNIX_JACK__)
|
Chris@1397
|
2439
|
Chris@1397
|
2440 // JACK header files
|
Chris@1397
|
2441 #include <jack/jack.h>
|
Chris@1397
|
2442 #include <jack/midiport.h>
|
Chris@1397
|
2443 #include <jack/ringbuffer.h>
|
Chris@1397
|
2444
|
Chris@1397
|
2445 #define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer
|
Chris@1397
|
2446
|
Chris@1397
|
2447 struct JackMidiData {
|
Chris@1397
|
2448 jack_client_t *client;
|
Chris@1397
|
2449 jack_port_t *port;
|
Chris@1397
|
2450 jack_ringbuffer_t *buffSize;
|
Chris@1397
|
2451 jack_ringbuffer_t *buffMessage;
|
Chris@1397
|
2452 jack_time_t lastTime;
|
Chris@1397
|
2453 MidiInApi :: RtMidiInData *rtMidiIn;
|
Chris@1397
|
2454 };
|
Chris@1397
|
2455
|
Chris@1397
|
2456 //*********************************************************************//
|
Chris@1397
|
2457 // API: JACK
|
Chris@1397
|
2458 // Class Definitions: MidiInJack
|
Chris@1397
|
2459 //*********************************************************************//
|
Chris@1397
|
2460
|
Chris@1397
|
2461 static int jackProcessIn( jack_nframes_t nframes, void *arg )
|
Chris@609
|
2462 {
|
Chris@1397
|
2463 JackMidiData *jData = (JackMidiData *) arg;
|
Chris@1397
|
2464 MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn;
|
Chris@1397
|
2465 jack_midi_event_t event;
|
Chris@1397
|
2466 jack_time_t time;
|
Chris@1397
|
2467
|
Chris@1397
|
2468 // Is port created?
|
Chris@1397
|
2469 if ( jData->port == NULL ) return 0;
|
Chris@1397
|
2470 void *buff = jack_port_get_buffer( jData->port, nframes );
|
Chris@1397
|
2471
|
Chris@1397
|
2472 // We have midi events in buffer
|
Chris@1397
|
2473 int evCount = jack_midi_get_event_count( buff );
|
Chris@1397
|
2474 for (int j = 0; j < evCount; j++) {
|
Chris@1397
|
2475 MidiInApi::MidiMessage message;
|
Chris@1397
|
2476 message.bytes.clear();
|
Chris@1397
|
2477
|
Chris@1397
|
2478 jack_midi_event_get( &event, buff, j );
|
Chris@1397
|
2479
|
Chris@1397
|
2480 for ( unsigned int i = 0; i < event.size; i++ )
|
Chris@1397
|
2481 message.bytes.push_back( event.buffer[i] );
|
Chris@1397
|
2482
|
Chris@1397
|
2483 // Compute the delta time.
|
Chris@1397
|
2484 time = jack_get_time();
|
Chris@1397
|
2485 if ( rtData->firstMessage == true )
|
Chris@1397
|
2486 rtData->firstMessage = false;
|
Chris@1397
|
2487 else
|
Chris@1397
|
2488 message.timeStamp = ( time - jData->lastTime ) * 0.000001;
|
Chris@1397
|
2489
|
Chris@1397
|
2490 jData->lastTime = time;
|
Chris@1397
|
2491
|
Chris@1397
|
2492 if ( !rtData->continueSysex ) {
|
Chris@1397
|
2493 if ( rtData->usingCallback ) {
|
Chris@1397
|
2494 RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback;
|
Chris@1397
|
2495 callback( message.timeStamp, &message.bytes, rtData->userData );
|
Chris@1397
|
2496 }
|
Chris@1397
|
2497 else {
|
Chris@1397
|
2498 // As long as we haven't reached our queue size limit, push the message.
|
Chris@1397
|
2499 if ( rtData->queue.size < rtData->queue.ringSize ) {
|
Chris@1397
|
2500 rtData->queue.ring[rtData->queue.back++] = message;
|
Chris@1397
|
2501 if ( rtData->queue.back == rtData->queue.ringSize )
|
Chris@1397
|
2502 rtData->queue.back = 0;
|
Chris@1397
|
2503 rtData->queue.size++;
|
Chris@1397
|
2504 }
|
Chris@1397
|
2505 else
|
Chris@1397
|
2506 std::cerr << "\nMidiInJack: message queue limit reached!!\n\n";
|
Chris@1397
|
2507 }
|
Chris@1397
|
2508 }
|
Chris@1397
|
2509 }
|
Chris@1397
|
2510
|
Chris@1397
|
2511 return 0;
|
Chris@609
|
2512 }
|
Chris@609
|
2513
|
Chris@1397
|
2514 MidiInJack :: MidiInJack( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit )
|
Chris@609
|
2515 {
|
Chris@1397
|
2516 initialize( clientName );
|
Chris@609
|
2517 }
|
Chris@609
|
2518
|
Chris@1397
|
2519 void MidiInJack :: initialize( const std::string& clientName )
|
Chris@609
|
2520 {
|
Chris@1397
|
2521 JackMidiData *data = new JackMidiData;
|
Chris@1397
|
2522 apiData_ = (void *) data;
|
Chris@1397
|
2523
|
Chris@1397
|
2524 data->rtMidiIn = &inputData_;
|
Chris@1397
|
2525 data->port = NULL;
|
Chris@1397
|
2526 data->client = NULL;
|
Chris@1397
|
2527 this->clientName = clientName;
|
Chris@1397
|
2528
|
Chris@1397
|
2529 connect();
|
Chris@609
|
2530 }
|
Chris@609
|
2531
|
Chris@1397
|
2532 void MidiInJack :: connect()
|
Chris@609
|
2533 {
|
Chris@1397
|
2534 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2535 if ( data->client )
|
Chris@1397
|
2536 return;
|
Chris@1397
|
2537
|
Chris@1397
|
2538 // Initialize JACK client
|
Chris@1397
|
2539 if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
|
Chris@1397
|
2540 errorString_ = "MidiInJack::initialize: JACK server not running?";
|
Chris@1397
|
2541 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
2542 return;
|
Chris@1397
|
2543 }
|
Chris@1397
|
2544
|
Chris@1397
|
2545 jack_set_process_callback( data->client, jackProcessIn, data );
|
Chris@1397
|
2546 jack_activate( data->client );
|
Chris@609
|
2547 }
|
Chris@609
|
2548
|
Chris@1397
|
2549 MidiInJack :: ~MidiInJack()
|
Chris@609
|
2550 {
|
Chris@1397
|
2551 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2552 closePort();
|
Chris@1397
|
2553
|
Chris@1397
|
2554 if ( data->client )
|
Chris@1397
|
2555 jack_client_close( data->client );
|
Chris@1397
|
2556 delete data;
|
Chris@609
|
2557 }
|
Chris@609
|
2558
|
Chris@1397
|
2559 void MidiInJack :: openPort( unsigned int portNumber, const std::string portName )
|
Chris@609
|
2560 {
|
Chris@1397
|
2561 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2562
|
Chris@1397
|
2563 connect();
|
Chris@1397
|
2564
|
Chris@1397
|
2565 // Creating new port
|
Chris@1397
|
2566 if ( data->port == NULL)
|
Chris@1397
|
2567 data->port = jack_port_register( data->client, portName.c_str(),
|
Chris@1397
|
2568 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
|
Chris@1397
|
2569
|
Chris@1397
|
2570 if ( data->port == NULL) {
|
Chris@1397
|
2571 errorString_ = "MidiInJack::openPort: JACK error creating port";
|
Chris@1397
|
2572 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2573 return;
|
Chris@1397
|
2574 }
|
Chris@1397
|
2575
|
Chris@1397
|
2576 // Connecting to the output
|
Chris@1397
|
2577 std::string name = getPortName( portNumber );
|
Chris@1397
|
2578 jack_connect( data->client, name.c_str(), jack_port_name( data->port ) );
|
Chris@1397
|
2579 }
|
Chris@1397
|
2580
|
Chris@1397
|
2581 void MidiInJack :: openVirtualPort( const std::string portName )
|
Chris@1397
|
2582 {
|
Chris@1397
|
2583 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2584
|
Chris@1397
|
2585 connect();
|
Chris@1397
|
2586 if ( data->port == NULL )
|
Chris@1397
|
2587 data->port = jack_port_register( data->client, portName.c_str(),
|
Chris@1397
|
2588 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
|
Chris@1397
|
2589
|
Chris@1397
|
2590 if ( data->port == NULL ) {
|
Chris@1397
|
2591 errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port";
|
Chris@1397
|
2592 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2593 }
|
Chris@1397
|
2594 }
|
Chris@1397
|
2595
|
Chris@1397
|
2596 unsigned int MidiInJack :: getPortCount()
|
Chris@1397
|
2597 {
|
Chris@1397
|
2598 int count = 0;
|
Chris@1397
|
2599 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2600 connect();
|
Chris@1397
|
2601 if ( !data->client )
|
Chris@609
|
2602 return 0;
|
Chris@1397
|
2603
|
Chris@1397
|
2604 // List of available ports
|
Chris@1397
|
2605 const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
|
Chris@1397
|
2606
|
Chris@1397
|
2607 if ( ports == NULL ) return 0;
|
Chris@1397
|
2608 while ( ports[count] != NULL )
|
Chris@1397
|
2609 count++;
|
Chris@1397
|
2610
|
Chris@1397
|
2611 free( ports );
|
Chris@1397
|
2612
|
Chris@1397
|
2613 return count;
|
Chris@609
|
2614 }
|
Chris@609
|
2615
|
Chris@1397
|
2616 std::string MidiInJack :: getPortName( unsigned int portNumber )
|
Chris@609
|
2617 {
|
Chris@1397
|
2618 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2619 std::string retStr("");
|
Chris@1397
|
2620
|
Chris@1397
|
2621 connect();
|
Chris@1397
|
2622
|
Chris@1397
|
2623 // List of available ports
|
Chris@1397
|
2624 const char **ports = jack_get_ports( data->client, NULL,
|
Chris@1397
|
2625 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput );
|
Chris@1397
|
2626
|
Chris@1397
|
2627 // Check port validity
|
Chris@1397
|
2628 if ( ports == NULL ) {
|
Chris@1397
|
2629 errorString_ = "MidiInJack::getPortName: no ports available!";
|
Chris@1397
|
2630 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
2631 return retStr;
|
Chris@1397
|
2632 }
|
Chris@1397
|
2633
|
Chris@1397
|
2634 if ( ports[portNumber] == NULL ) {
|
Chris@1397
|
2635 std::ostringstream ost;
|
Chris@1397
|
2636 ost << "MidiInJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@1397
|
2637 errorString_ = ost.str();
|
Chris@1397
|
2638 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
2639 }
|
Chris@1397
|
2640 else retStr.assign( ports[portNumber] );
|
Chris@1397
|
2641
|
Chris@1397
|
2642 free( ports );
|
Chris@1397
|
2643 return retStr;
|
Chris@609
|
2644 }
|
Chris@609
|
2645
|
Chris@1397
|
2646 void MidiInJack :: closePort()
|
Chris@609
|
2647 {
|
Chris@1397
|
2648 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2649
|
Chris@1397
|
2650 if ( data->port == NULL ) return;
|
Chris@1397
|
2651 jack_port_unregister( data->client, data->port );
|
Chris@1397
|
2652 data->port = NULL;
|
Chris@1397
|
2653 }
|
Chris@1397
|
2654
|
Chris@1397
|
2655 //*********************************************************************//
|
Chris@1397
|
2656 // API: JACK
|
Chris@1397
|
2657 // Class Definitions: MidiOutJack
|
Chris@1397
|
2658 //*********************************************************************//
|
Chris@1397
|
2659
|
Chris@1397
|
2660 // Jack process callback
|
Chris@1397
|
2661 static int jackProcessOut( jack_nframes_t nframes, void *arg )
|
Chris@1397
|
2662 {
|
Chris@1397
|
2663 JackMidiData *data = (JackMidiData *) arg;
|
Chris@1397
|
2664 jack_midi_data_t *midiData;
|
Chris@1397
|
2665 int space;
|
Chris@1397
|
2666
|
Chris@1397
|
2667 // Is port created?
|
Chris@1397
|
2668 if ( data->port == NULL ) return 0;
|
Chris@1397
|
2669
|
Chris@1397
|
2670 void *buff = jack_port_get_buffer( data->port, nframes );
|
Chris@1397
|
2671 jack_midi_clear_buffer( buff );
|
Chris@1397
|
2672
|
Chris@1397
|
2673 while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) {
|
Chris@1397
|
2674 jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof(space) );
|
Chris@1397
|
2675 midiData = jack_midi_event_reserve( buff, 0, space );
|
Chris@1397
|
2676
|
Chris@1397
|
2677 jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space );
|
Chris@1397
|
2678 }
|
Chris@1397
|
2679
|
Chris@1397
|
2680 return 0;
|
Chris@1397
|
2681 }
|
Chris@1397
|
2682
|
Chris@1397
|
2683 MidiOutJack :: MidiOutJack( const std::string clientName ) : MidiOutApi()
|
Chris@1397
|
2684 {
|
Chris@1397
|
2685 initialize( clientName );
|
Chris@1397
|
2686 }
|
Chris@1397
|
2687
|
Chris@1397
|
2688 void MidiOutJack :: initialize( const std::string& clientName )
|
Chris@1397
|
2689 {
|
Chris@1397
|
2690 JackMidiData *data = new JackMidiData;
|
Chris@1397
|
2691 apiData_ = (void *) data;
|
Chris@1397
|
2692
|
Chris@1397
|
2693 data->port = NULL;
|
Chris@1397
|
2694 data->client = NULL;
|
Chris@1397
|
2695 this->clientName = clientName;
|
Chris@1397
|
2696
|
Chris@1397
|
2697 connect();
|
Chris@1397
|
2698 }
|
Chris@1397
|
2699
|
Chris@1397
|
2700 void MidiOutJack :: connect()
|
Chris@1397
|
2701 {
|
Chris@1397
|
2702 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2703 if ( data->client )
|
Chris@1397
|
2704 return;
|
Chris@1397
|
2705
|
Chris@1397
|
2706 // Initialize output ringbuffers
|
Chris@1397
|
2707 data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
|
Chris@1397
|
2708 data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
|
Chris@1397
|
2709
|
Chris@1397
|
2710 // Initialize JACK client
|
Chris@1397
|
2711 if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
|
Chris@1397
|
2712 errorString_ = "MidiOutJack::initialize: JACK server not running?";
|
Chris@1397
|
2713 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
2714 return;
|
Chris@1397
|
2715 }
|
Chris@1397
|
2716
|
Chris@1397
|
2717 jack_set_process_callback( data->client, jackProcessOut, data );
|
Chris@1397
|
2718 jack_activate( data->client );
|
Chris@1397
|
2719 }
|
Chris@1397
|
2720
|
Chris@1397
|
2721 MidiOutJack :: ~MidiOutJack()
|
Chris@1397
|
2722 {
|
Chris@1397
|
2723 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2724 closePort();
|
Chris@1397
|
2725
|
Chris@1397
|
2726 // Cleanup
|
Chris@1397
|
2727 jack_ringbuffer_free( data->buffSize );
|
Chris@1397
|
2728 jack_ringbuffer_free( data->buffMessage );
|
Chris@1397
|
2729 if ( data->client ) {
|
Chris@1397
|
2730 jack_client_close( data->client );
|
Chris@1397
|
2731 }
|
Chris@1397
|
2732
|
Chris@1397
|
2733 delete data;
|
Chris@1397
|
2734 }
|
Chris@1397
|
2735
|
Chris@1397
|
2736 void MidiOutJack :: openPort( unsigned int portNumber, const std::string portName )
|
Chris@1397
|
2737 {
|
Chris@1397
|
2738 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2739
|
Chris@1397
|
2740 connect();
|
Chris@1397
|
2741
|
Chris@1397
|
2742 // Creating new port
|
Chris@1397
|
2743 if ( data->port == NULL )
|
Chris@1397
|
2744 data->port = jack_port_register( data->client, portName.c_str(),
|
Chris@1397
|
2745 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
|
Chris@1397
|
2746
|
Chris@1397
|
2747 if ( data->port == NULL ) {
|
Chris@1397
|
2748 errorString_ = "MidiOutJack::openPort: JACK error creating port";
|
Chris@1397
|
2749 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2750 return;
|
Chris@1397
|
2751 }
|
Chris@1397
|
2752
|
Chris@1397
|
2753 // Connecting to the output
|
Chris@1397
|
2754 std::string name = getPortName( portNumber );
|
Chris@1397
|
2755 jack_connect( data->client, jack_port_name( data->port ), name.c_str() );
|
Chris@1397
|
2756 }
|
Chris@1397
|
2757
|
Chris@1397
|
2758 void MidiOutJack :: openVirtualPort( const std::string portName )
|
Chris@1397
|
2759 {
|
Chris@1397
|
2760 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2761
|
Chris@1397
|
2762 connect();
|
Chris@1397
|
2763 if ( data->port == NULL )
|
Chris@1397
|
2764 data->port = jack_port_register( data->client, portName.c_str(),
|
Chris@1397
|
2765 JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 );
|
Chris@1397
|
2766
|
Chris@1397
|
2767 if ( data->port == NULL ) {
|
Chris@1397
|
2768 errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port";
|
Chris@1397
|
2769 error( RtMidiError::DRIVER_ERROR, errorString_ );
|
Chris@1397
|
2770 }
|
Chris@1397
|
2771 }
|
Chris@1397
|
2772
|
Chris@1397
|
2773 unsigned int MidiOutJack :: getPortCount()
|
Chris@1397
|
2774 {
|
Chris@1397
|
2775 int count = 0;
|
Chris@1397
|
2776 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2777 connect();
|
Chris@1397
|
2778 if ( !data->client )
|
Chris@609
|
2779 return 0;
|
Chris@1397
|
2780
|
Chris@1397
|
2781 // List of available ports
|
Chris@1397
|
2782 const char **ports = jack_get_ports( data->client, NULL,
|
Chris@1397
|
2783 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
|
Chris@1397
|
2784
|
Chris@1397
|
2785 if ( ports == NULL ) return 0;
|
Chris@1397
|
2786 while ( ports[count] != NULL )
|
Chris@1397
|
2787 count++;
|
Chris@1397
|
2788
|
Chris@1397
|
2789 free( ports );
|
Chris@1397
|
2790
|
Chris@1397
|
2791 return count;
|
Chris@609
|
2792 }
|
Chris@609
|
2793
|
Chris@1397
|
2794 std::string MidiOutJack :: getPortName( unsigned int portNumber )
|
Chris@609
|
2795 {
|
Chris@1397
|
2796 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2797 std::string retStr("");
|
Chris@1397
|
2798
|
Chris@1397
|
2799 connect();
|
Chris@1397
|
2800
|
Chris@1397
|
2801 // List of available ports
|
Chris@1397
|
2802 const char **ports = jack_get_ports( data->client, NULL,
|
Chris@1397
|
2803 JACK_DEFAULT_MIDI_TYPE, JackPortIsInput );
|
Chris@1397
|
2804
|
Chris@1397
|
2805 // Check port validity
|
Chris@1397
|
2806 if ( ports == NULL) {
|
Chris@1397
|
2807 errorString_ = "MidiOutJack::getPortName: no ports available!";
|
Chris@1397
|
2808 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
2809 return retStr;
|
Chris@1397
|
2810 }
|
Chris@1397
|
2811
|
Chris@1397
|
2812 if ( ports[portNumber] == NULL) {
|
Chris@1397
|
2813 std::ostringstream ost;
|
Chris@1397
|
2814 ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid.";
|
Chris@1397
|
2815 errorString_ = ost.str();
|
Chris@1397
|
2816 error( RtMidiError::WARNING, errorString_ );
|
Chris@1397
|
2817 }
|
Chris@1397
|
2818 else retStr.assign( ports[portNumber] );
|
Chris@1397
|
2819
|
Chris@1397
|
2820 free( ports );
|
Chris@1397
|
2821 return retStr;
|
Chris@609
|
2822 }
|
Chris@609
|
2823
|
Chris@1397
|
2824 void MidiOutJack :: closePort()
|
Chris@609
|
2825 {
|
Chris@1397
|
2826 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2827
|
Chris@1397
|
2828 if ( data->port == NULL ) return;
|
Chris@1397
|
2829 jack_port_unregister( data->client, data->port );
|
Chris@1397
|
2830 data->port = NULL;
|
Chris@609
|
2831 }
|
Chris@609
|
2832
|
Chris@1397
|
2833 void MidiOutJack :: sendMessage( std::vector<unsigned char> *message )
|
Chris@609
|
2834 {
|
Chris@1397
|
2835 int nBytes = message->size();
|
Chris@1397
|
2836 JackMidiData *data = static_cast<JackMidiData *> (apiData_);
|
Chris@1397
|
2837
|
Chris@1397
|
2838 // Write full message to buffer
|
Chris@1397
|
2839 jack_ringbuffer_write( data->buffMessage, ( const char * ) &( *message )[0],
|
Chris@1397
|
2840 message->size() );
|
Chris@1397
|
2841 jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) );
|
Chris@609
|
2842 }
|
Chris@609
|
2843
|
Chris@1397
|
2844 #endif // __UNIX_JACK__
|