comparison data/midi/rtmidi/RtMidi.cpp @ 1527:710e6250a401 zoom

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