Jamoma API  0.6.0.a19
MIDI.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup modularMIDI
4  *
5  * @brief the MIDI protocol for Jamoma Modular
6  *
7  * @details
8  *
9  * @author Theo Delahogue
10  *
11  * @copyright © 2014, GMEA (http://www.gmea.net) @n
12  * This code is licensed under the terms of the "New BSD License" @n
13  * http://creativecommons.org/licenses/BSD/
14  */
15 
16 #include "TTProtocol.h"
17 #include "MIDI.h"
18 
19 #define thisTTClass MIDI
20 #define thisTTClassName "MIDI"
21 #define thisTTClassTags "network, protocol, MIDI"
22 
23 #define thisProtocolVersion "0.1"
24 #define thisProtocolAuthor "Theo de la Hogue"
25 #define thisProtocolGet NO
26 #define thisProtocolSet YES
27 #define thisProtocolListen NO
28 #define thisProtocolDiscover NO
29 #define thisProtocolDiscoverAll NO
30 
31 extern "C" TT_EXTENSION_EXPORT TTErr TTLoadJamomaExtension_MIDI(void)
32 {
33  TTFoundationInit();
34  MIDI::registerClass();
35  return kTTErrNone;
36 }
37 
39 {
41 
44 
45  PmError err = Pm_Initialize();
46 
47  if (err) {
48  mRunning = NO;
49  TTLogError("MIDI : error %ld from Pm_Initialize()", err);
50  }
51  else
52  mRunning = YES;
53 
54  // instantiate the client handle
55 #if !defined(TT_PLATFORM_MAC)
56  ; // TODO
57 #else
58  OSStatus status = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &mClient);
59  if (status != noErr) {
60  TTLogError("Could not initialize PortMidi client : %d\n", (int)status);
61  return;
62  }
63 #endif
64 }
65 
66 MIDI::~MIDI()
67 {
68  TTValue applications, v;
69  TTSymbol applicationName;
70  MIDIInputPtr midiInput;
71  MIDIOutputPtr midiOutput;
72 
73  // clear mInputs
74  mInputs.getKeys(applications);
75  for (TTUInt32 i = 0; i < applications.size(); i++) {
76 
77  applicationName = applications[i];
78  mInputs.lookup(applicationName, v);
79  midiInput = MIDIInputPtr(TTPtr(v[0]));
80  delete midiInput;
81  }
82  mInputs.clear();
83 
84  // clear mOutputs
85  mOutputs.getKeys(applications);
86  for (TTUInt32 i = 0; i < applications.size(); i++) {
87 
88  applicationName = applications[i];
89  mOutputs.lookup(applicationName, v);
90  midiOutput = MIDIOutputPtr(TTPtr(v[0]));
91  delete midiOutput;
92  }
93  mOutputs.clear();
94 
95  // release the client handle
96 #if !defined(TT_PLATFORM_MAC)
97  ; // TODO
98 #else
99  MIDIClientDispose(mClient);
100 #endif
101 
102  Pm_Terminate();
103 }
104 
105 TTErr MIDI::sendMessage(TTSymbol& applicationName, TTAddress& address, const TTValue& value)
106 {
107  TTValue v;
108 
109  if (!mOutputs.lookup(applicationName, v)) {
110 
111  MIDIOutputPtr midiOutput = MIDIOutputPtr(TTPtr(v[0]));
112 
113  if (!midiOutput->sendMessage(address, value)) {
114 
115  if (mActivity) {
116 
117  TTValue message = value;
118  message.prepend(address);
119  ActivityOutMessage(message);
120  }
121 
122  return kTTErrNone;
123  }
124  }
125 
126  return kTTErrGeneric;
127 }
128 
129 TTErr MIDI::receivedMessage(TTSymbol& applicationName, TTAddress& address, const TTValue& value)
130 {
131  if (mActivity) {
132 
133  TTValue message = value;
134  message.prepend(address);
135  ActivityInMessage(message);
136  }
137 
138  // to local application
139  if (applicationName == mLocalApplicationName)
140  return ReceiveSetRequest(kTTSymEmpty, address, value);
141 
142  // from distant application
143  else
144  return ReceiveListenAnswer(applicationName, address, value);
145 }
146 
147 TTErr MIDI::Scan(const TTValue& inputValue, TTValue& outputValue)
148 {
149  const PmDeviceInfo* deviceInfo = NULL;
150  int deviceCount = Pm_CountDevices();
151  TTSymbol deviceType;
152 
153  if (inputValue.size() == 1) {
154 
155  if (inputValue[0].type() == kTypeSymbol) {
156 
157  deviceType = inputValue[0];
158 
159  if (deviceCount < 0) {
160 
161  logError("Pm_CountDevices() returned 0x%x\n", deviceCount);
162  return kTTErrGeneric;
163  }
164 
165  for (TTInt32 i = 0; i < deviceCount; i++) {
166 
167  deviceInfo = Pm_GetDeviceInfo(i);
168 
169  if (deviceType == TTSymbol("inputs") && deviceInfo->input)
170  outputValue.append(TTSymbol(deviceInfo->name));
171 
172  else if (deviceType == TTSymbol("outputs") && deviceInfo->output)
173  outputValue.append(TTSymbol(deviceInfo->name));
174 
175  }
176 
177  return kTTErrNone;
178  }
179  }
180 
181  return kTTErrGeneric;
182 }
183 
184 /*!
185  * Run the reception thread
186  *
187  */
188 TTErr MIDI::Run(const TTValue& inputValue, TTValue& outputValue)
189 {
190  // start one application at time
191  if (inputValue.size() == 1) {
192 
193  if (inputValue[0].type() == kTypeSymbol) {
194 
195  TTSymbol applicationName = inputValue[0];
196  TTValue v;
197 
198  // select the given application to get its "input" and "output" device parameter
199  ApplicationSelect(applicationName, v);
200 
201  // enable MIDI message reception for the application (if it have an "input" parameter)
202  if (applicationName == mLocalApplicationName)
203  runDestination(applicationName);
204  else
205  runInput(applicationName);
206 
207  // enable MIDI message sending for the application (if it have an "output" parameter)
208  if (applicationName == mLocalApplicationName)
209  runSource(applicationName);
210  else
211  runOutput(applicationName);
212 
213  return kTTErrNone;
214  }
215  }
216 
217  // or start all registered applications
218  TTValue applications, none;
219 
220  mApplicationParameters.getKeys(applications);
221  for (TTUInt32 i = 0; i < applications.size(); i++)
222  Run(applications[i], none);
223 
224  return kTTErrNone;
225 }
226 
227 TTErr MIDI::runDestination(TTSymbol& applicationName)
228 {
229  TTObject MIDIProtocol(this);
230  MIDIDestinationPtr midiDestination = NULL;
231  TTValue v;
232  TTErr err;
233 
234  // if the application is still not registered as input
235  if (mInputs.lookup(applicationName, v)) {
236 
237  // get the input device name parameter
238  MIDIProtocol.get("input", v);
239 
240  if (v.size()) {
241 
242  TTSymbol inputName = v[0];
243 
244  // create a specific-plateform MIDI destination object
245 #if !defined(TT_PLATFORM_MAC)
246  ; // TODO
247 #else
248  midiDestination = new MIDIDestination(this, applicationName, mClient);
249 #endif
250  err = midiDestination->setName(inputName);
251 
252  // if the name is already used
253  if (err) {
254  delete midiDestination;
255  midiDestination = NULL;
256  }
257  // register the MIDI source as input
258  else
259  mInputs.append(applicationName, TTPtr(midiDestination));
260  }
261  }
262  // if the application is already registered as input
263  else {
264 
265  midiDestination = MIDIDestinationPtr(TTPtr(v[0]));
266 
267  // if the application is not registered anymore
268  if (mApplicationParameters.lookup(applicationName, v)) {
269 
270  // clear MIDI destination
271  delete midiDestination;
272  midiDestination = NULL;
273  mInputs.remove(applicationName);
274  }
275  }
276 
277  // run the MIDI destination if there is one
278  if (midiDestination) {
279 
280  midiDestination->setRunning(YES);
281  return kTTErrNone;
282  }
283 
284  return kTTErrGeneric;
285 }
286 
287 TTErr MIDI::runInput(TTSymbol& applicationName)
288 {
289  TTObject MIDIProtocol(this);
290  MIDIInputPtr midiInput = NULL;
291  TTValue v;
292  TTErr err;
293 
294  // if the application is still not registered as input
295  if (mInputs.lookup(applicationName, v)) {
296 
297  // get the input device name parameter
298  MIDIProtocol.get("input", v);
299 
300  if (v.size()) {
301 
302  TTSymbol inputName = v[0];
303 
304  // create a MIDI input object
305  midiInput = new MIDIInput(this, applicationName);
306  err = midiInput->setName(inputName);
307 
308  // if the device doesn't exist
309  if (err) {
310  delete midiInput;
311  midiInput = NULL;
312  }
313  // register the MIDI input
314  else
315  mInputs.append(applicationName, TTPtr(midiInput));
316  }
317  }
318  // if the application is already registered as input
319  else {
320 
321  midiInput = MIDIInputPtr(TTPtr(v[0]));
322 
323  // if the application is not registered anymore
324  if (mApplicationParameters.lookup(applicationName, v)) {
325 
326  // clear the MIDI input
327  delete midiInput;
328  midiInput = NULL;
329  mInputs.remove(applicationName);
330  }
331  }
332 
333  // run the MIDI input if there is one
334  if (midiInput) {
335 
336  midiInput->setRunning(YES);
337  return kTTErrNone;
338  }
339 
340  return kTTErrGeneric;
341 }
342 
343 TTErr MIDI::runOutput(TTSymbol& applicationName)
344 {
345  TTObject MIDIProtocol(this);
346  MIDIOutputPtr midiOutput = NULL;
347  TTValue v;
348  TTErr err;
349 
350  // if the application is still not registered as output
351  if (mOutputs.lookup(applicationName, v)) {
352 
353  // get the output device name parameter
354  MIDIProtocol.get("output", v);
355 
356  if (v.size()) {
357 
358  TTSymbol outputName = v[0];
359 
360  // create a MIDI output object
361  midiOutput = new MIDIOutput(this, applicationName);
362  err = midiOutput->setName(outputName);
363 
364  // if the device doesn't exist
365  if (err) {
366  delete midiOutput;
367  midiOutput = NULL;
368  }
369  // register the MIDI output
370  else
371  mOutputs.append(applicationName, TTPtr(midiOutput));
372  }
373  }
374  // if the application is already registered as output
375  else {
376 
377  midiOutput = MIDIOutputPtr(TTPtr(v[0]));
378 
379  // if the application is not registered anymore
380  if (mApplicationParameters.lookup(applicationName, v)) {
381 
382  // clear the MIDI output
383  delete midiOutput;
384  midiOutput = NULL;
385  mOutputs.remove(applicationName);
386  }
387  }
388 
389  // run the MIDI output if there is one
390  if (midiOutput) {
391 
392  midiOutput->setRunning(YES);
393  return kTTErrNone;
394  }
395 
396  return kTTErrGeneric;
397 }
398 
399 TTErr MIDI::runSource(TTSymbol& applicationName)
400 {
401  TTObject MIDIProtocol(this);
402  MIDISourcePtr midiSource = NULL;
403  TTValue v;
404  TTErr err;
405 
406  // if the application is still not registered as output
407  if (mOutputs.lookup(applicationName, v)) {
408 
409  // get the output device name parameter
410  MIDIProtocol.get("output", v);
411 
412  if (v.size()) {
413 
414  TTSymbol outputName = v[0];
415 
416  // create a specific-plateform MIDI source object
417 #if !defined(TT_PLATFORM_MAC)
418  ; // TODO
419 #else
420  midiSource = new MIDISource(this, applicationName, mClient);
421 #endif
422  err = midiSource->setName(outputName);
423 
424  // if the name is already used
425  if (err) {
426  delete midiSource;
427  midiSource = NULL;
428  }
429  // register the MIDI source as input
430  else
431  mOutputs.append(applicationName, TTPtr(midiSource));
432  }
433  }
434  // if the application is already registered as output
435  else {
436  midiSource = MIDISourcePtr(TTPtr(v[0]));
437 
438  // if the local application is not registered anymore
439  if (mApplicationParameters.lookup(applicationName, v)) {
440 
441  // clear the MIDI source
442  delete midiSource;
443  midiSource = NULL;
444  mInputs.remove(applicationName);
445  }
446  }
447 
448  // run the MIDI source if there is one
449  if (midiSource) {
450 
451  midiSource->setRunning(YES);
452  return kTTErrNone;
453  }
454 
455  return kTTErrGeneric;
456 }
457 
458 /*!
459  * Stop the reception thread
460  *
461  */
462 TTErr MIDI::Stop(const TTValue& inputValue, TTValue& outputValue)
463 {
464  // stop one MIDI device at time
465  if (inputValue.size() == 1) {
466 
467  if (inputValue[0].type() == kTypeSymbol) {
468 
469  TTSymbol applicationName = inputValue[0];
470  TTValue v;
471 
472  // select the given application to get its "input" and "output" device parameter
473  ApplicationSelect(applicationName, v);
474 
475  // disable MIDI message reception for the application (if it have an "input" parameter)
476  if (applicationName == mLocalApplicationName)
477  stopDestination(applicationName);
478  else
479  stopInput(applicationName);
480 
481  // disable MIDI message sending for the application (if it have an "output" parameter)
482  if (applicationName == mLocalApplicationName)
483  stopSource(applicationName);
484  else
485  stopOutput(applicationName);
486 
487  return kTTErrNone;
488  }
489  }
490 
491  // or stop all registered applications
492  TTValue applications, none;
493 
494  mApplicationParameters.getKeys(applications);
495  for (TTUInt32 i = 0; i < applications.size(); i++)
496  Stop(applications[i], none);
497 
498  return kTTErrNone;
499 }
500 
501 TTErr MIDI::stopDestination(TTSymbol& applicationName)
502 {
503  TTObject MIDIProtocol(this);
504  MIDIDestinationPtr midiDestination = NULL;
505  TTValue v;
506 
507  // if the application is already registered as input
508  if (!mInputs.lookup(applicationName, v)) {
509 
510  midiDestination = MIDIDestinationPtr(TTPtr(v[0]));
511 
512  // if the application is not registered anymore
513  if (mApplicationParameters.lookup(applicationName, v)) {
514 
515  // clear the MIDI destination
516  delete midiDestination;
517  midiDestination = NULL;
518  mInputs.remove(applicationName);
519  }
520 
521  // stop the MIDI destination
522  if (midiDestination) {
523 
524  midiDestination->setRunning(NO);
525  return kTTErrNone;
526  }
527  }
528 
529  return kTTErrGeneric;
530 }
531 
532 TTErr MIDI::stopInput(TTSymbol& applicationName)
533 {
534  TTObject MIDIProtocol(this);
535  MIDIInputPtr midiInput = NULL;
536  TTValue v;
537 
538  // if the application is already registered as input
539  if (!mInputs.lookup(applicationName, v)) {
540 
541  midiInput = MIDIInputPtr(TTPtr(v[0]));
542 
543  // if the application is not registered anymore
544  if (mApplicationParameters.lookup(applicationName, v)) {
545 
546  // clear the MIDI input
547  delete midiInput;
548  midiInput = NULL;
549  mInputs.remove(applicationName);
550  }
551 
552  // stop the MIDI input
553  if (midiInput) {
554 
555  midiInput->setRunning(NO);
556  return kTTErrNone;
557  }
558  }
559 
560  return kTTErrGeneric;
561 }
562 
563 TTErr MIDI::stopOutput(TTSymbol& applicationName)
564 {
565  TTObject MIDIProtocol(this);
566  MIDIOutputPtr midiOutput = NULL;
567  TTValue v;
568 
569  // if the application is already registered as output
570  if (!mOutputs.lookup(applicationName, v)) {
571 
572  midiOutput = MIDIOutputPtr(TTPtr(v[0]));
573 
574  // if the application is not registered anymore
575  if (mApplicationParameters.lookup(applicationName, v)) {
576 
577  // clear the MIDI output
578  delete midiOutput;
579  midiOutput = NULL;
580  mOutputs.remove(applicationName);
581  }
582 
583  // stop the MIDI output
584  if (midiOutput) {
585 
586  midiOutput->setRunning(NO);
587  return kTTErrNone;
588  }
589  }
590 
591  return kTTErrGeneric;
592 }
593 
594 TTErr MIDI::stopSource(TTSymbol& applicationName)
595 {
596  TTObject MIDIProtocol(this);
597  MIDISourcePtr midiSource = NULL;
598  TTValue v;
599 
600  // if the application is already registered as output
601  if (!mOutputs.lookup(applicationName, v)) {
602 
603  midiSource = MIDISourcePtr(TTPtr(v[0]));
604 
605  // if the application is not registered anymore
606  if (mApplicationParameters.lookup(applicationName, v)) {
607 
608  // clear the MIDI source
609  delete midiSource;
610  midiSource = NULL;
611  mOutputs.remove(applicationName);
612  }
613 
614  // stop the MIDI source
615  if (midiSource) {
616 
617  midiSource->setRunning(NO);
618  return kTTErrNone;
619  }
620  }
621 
622  return kTTErrGeneric;
623 }
624 
625 /**************************************************************************************************************************
626  *
627  * SEND REQUEST METHODS
628  *
629  **************************************************************************************************************************/
630 
631 /*!
632  * Send a discover request to an application to get a part of the namespace at the given address
633  *
634  @param to : the application where to discover
635  @param address : the address to discover
636  @param returnedType : the type of the node at the address (default is none which means no type)
637  @param returnedChildren : all names of nodes below the address
638  @param returnedAttributes : all attributes the node at the address
639  @return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
640  else it returns kTTErrGeneric if no answer or timeout
641  */
642 TTErr MIDI::SendDiscoverRequest(TTSymbol to, TTAddress address,
643  TTSymbol& returnedType,
644  TTValue& returnedChildren,
645  TTValue& returnedAttributes,
646  TTUInt8 tryCount)
647 {
648  return kTTErrGeneric;
649 }
650 
651 /*!
652  * Send a discover all request to an application to fill all the directory under this address
653  *
654  @param to : the application where to discover
655  @param address : the address to discover
656  @param node : the node for this address
657  @param tryCount : number of try for this request
658  @return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
659  else it returns kTTErrGeneric if no answer or timeout
660  */
661 TTErr MIDI::SendDiscoverAllRequest(TTSymbol to, TTAddress address, TTNodePtr node, TTUInt8 tryCount)
662 {
663  return kTTErrGeneric;
664 }
665 
666 /*!
667  * Send a get request to an application to get a value at the given address
668  *
669  @param to : the application where to get
670  @param address : the address to get
671  @param returnedValue : the value which is going to be filled
672  @return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
673  else it returns kTTErrGeneric if no answer or timeout
674  */
675 TTErr MIDI::SendGetRequest(TTSymbol to, TTAddress address,
676  TTValue& returnedValue,
677  TTUInt8 tryCount)
678 {
679  return kTTErrGeneric;
680 }
681 
682 /*!
683  * Send a set request to set a value of a specific application
684  *
685  @param to : the application where to set
686  @param address : the address to set
687  @param value : anything to send
688  @return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
689  */
690 TTErr MIDI::SendSetRequest(TTSymbol to, TTAddress address,
691  TTValue& value,
692  TTUInt8 tryCount)
693 {
694  return sendMessage(to, address, value);
695 }
696 
697 /*!
698  * Send a listen request to a specific application
699  *
700  @param to : the application where to listen
701  @param address : the address to listen
702  @param enable : enable/disable the listening
703  @return errorcode : kTTErrNone means the answer has been received, kTTErrValueNotFound means something is bad in the request
704  */
705 TTErr MIDI::SendListenRequest(TTSymbol to, TTAddress address,
706  TTBoolean enable,
707  TTUInt8 tryCount)
708 {
709  return kTTErrGeneric;
710 }
711 
712 
713 /**************************************************************************************************************************
714  *
715  * SEND ANSWER METHODS
716  *
717  **************************************************************************************************************************/
718 
719 /*!
720  * Send a disover answer to a application which ask for.
721  *
722  @param to : the application where to send answer
723  @param address : the address where comes from the description
724  @param returnedType : the type of the node at the address (default is none which means no type)
725  @param returnedChildren : all names of nodes below the address
726  @param returnedAttributes : all attributes the node at the address
727  */
728 TTErr MIDI::SendDiscoverAnswer(TTSymbol to, TTAddress address,
729  TTSymbol& returnedType,
730  TTValue& returnedChildren,
731  TTValue& returnedAttributes,
732  TTErr err)
733 {
734  return kTTErrGeneric;
735 }
736 
737 /*!
738  * Send a disover answer to a application which ask for.
739  *
740  @param to : the application where to send answer
741  @param address : the address where comes from the description
742  @param node : the node for this address
743  */
744 TTErr MIDI::SendDiscoverAllAnswer(TTSymbol to, TTAddress address, TTNodePtr node, TTErr err)
745 {
746  return kTTErrGeneric;
747 }
748 
749 /*!
750  * Send a get answer to an application which ask for.
751  *
752  @param to : the application where to send answer
753  @param address : the address where comes from the value
754  @param returnedValue : the value of the attribute at the address
755  */
756 TTErr MIDI::SendGetAnswer(TTSymbol to, TTAddress address,
757  const TTValue& returnedValue,
758  TTErr err)
759 {
760  return kTTErrGeneric;
761 }
762 
763 /*!
764  * Send a listen answer to an application which ask for.
765  *
766  @param to : the application where to send answer
767  @param address : the address where comes from the value
768  @param returnedValue : the value of the attribute at the address
769  */
770 TTErr MIDI::SendListenAnswer(TTSymbol to, TTAddress address,
771  const TTValue& returnedValue,
772  TTErr err)
773 {
774  return kTTErrGeneric;
775 }
TTErr sendMessage(const TTSymbol name)
TODO: Document this function.
bool TTBoolean
Boolean flag, same as Boolean on the Mac.
Definition: TTBase.h:167
#define TT_PROTOCOL_INITIALIZE
Declares all members needed by any protocol class.
Definition: TTProtocol.h:31
We build a directory of TTNodes, and you can request a pointer for any TTNode, or add an observer to ...
Definition: TTNode.h:59
The TTAddress class is used to represent a string and efficiently pass and compare that string...
Definition: TTAddress.h:29
Create and use Jamoma object instances.
Definition: TTObject.h:29
size_type size() const noexcept
Return the number of elements.
Symbol type.
Definition: TTBase.h:282
void prepend(const TTValue &aValueToPrepend)
Insert another TTValue before the first element.
Definition: TTValue.h:162
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
void * TTPtr
A generic pointer.
Definition: TTBase.h:248
A Protocol interface.
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
void TTFOUNDATION_EXPORT TTLogError(TTImmutableCString message,...)
Platform and host independent method for posting errors.
Definition: TTBase.cpp:572
void get(const TTUInt16 index, T &returnedElementValue) const
DEPRECATED.
Definition: TTValue.h:591
std::int32_t TTInt32
32 bit signed integer
Definition: TTBase.h:177
the MIDI protocol for Jamoma Modular
Something went wrong, but what exactly is not known. Typically used for context-specific problems...
Definition: TTBase.h:344
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
No Error.
Definition: TTBase.h:343
#define TT_PROTOCOL_CONSTRUCTOR
Declares instantiation and registration methods to add the protocol class as any TTObject class...
Definition: TTProtocol.h:23
#define addAttributeAsProtocolParameter(name, type)
Add a protocol parameter as an attribute of the class.
Definition: TTProtocol.h:48
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
unsigned char TTUInt8
8 bit unsigned integer (char)
Definition: TTBase.h:174