comparison data/midi/rtmidi/RtMidi.cpp @ 1397:b0533d195c83

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