Jamoma API  0.6.0.a19
TTMidiInput.cpp
1 /*
2  * Midi Input Object
3  * Copyright © 2011, Timothy Place
4  *
5  * License: This code is licensed under the terms of the "New BSD License"
6  * http://creativecommons.org/licenses/BSD/
7  */
8 
9 #include "TTGraphObjectBase.h"
10 #include "TTMidiInput.h"
11 #ifdef TT_PLATFORM_WIN
12 #include <algorithm>
13 #endif
14 
15 #define thisTTClass TTMidiInput
16 #define thisTTClassName "midi.in"
17 #define thisTTClassTags "midi, input"
18 
19 
20 static const int kMidiBufferSize = 100; // This is arbitrary, is there a more rational value for this?
21 
22 
24  mStream(NULL),
25  mPollingThread(NULL),
26  mRunning(YES),
27  mOwner(NULL)
28 {
29  PmError err = Pm_Initialize();
30 
31  if (err) {
32  logError("err %ld from Pm_Initialize()", err);
33  }
34 
35  addAttribute(Owner, kTypePointer);
37 
38  addMessageWithArguments(getAvailableDeviceNames);
39 
40  setAttributeValue(TT("device"), TT("default"));
41 }
42 
43 
44 TTMidiInput::~TTMidiInput()
45 {
46  // TODO: we are supposed to call Pm_Terminate() when we are done using the library
47  // but we don't currently have a "shutdown" method for classes when the system is torn down
48  mRunning = NO;
49  if (mPollingThread)
50  mPollingThread->wait();
51  delete mPollingThread;
52 }
53 
54 
55 TTErr TTMidiInput::getAvailableDeviceNames(const TTValue&, TTValue& returnedDeviceNames)
56 {
57  const PmDeviceInfo* deviceInfo = NULL;
58  int deviceCount = Pm_CountDevices();
59 
60  returnedDeviceNames.clear();
61 
62  if (deviceCount < 0) {
63  logError("Pa_CountDevices() returned 0x%x\n", deviceCount);
64  return kTTErrGeneric;
65  }
66 
67  for (int i=0; i<deviceCount; i++) {
68  deviceInfo = Pm_GetDeviceInfo(i);
69  if (deviceInfo->input)
70  returnedDeviceNames.append(TT(deviceInfo->name));
71  }
72  return kTTErrNone;
73 }
74 
75 
76 
77 void* TTMidiPoll(TTMidiInput* self)
78 {
79  PmEvent buffer[64];
80  int result;
81 
82  while (self->mRunning) {
83  if (Pm_Poll(self->mStream)) {
84  result = Pm_Read(self->mStream, buffer, 64);
85  if (result < 0) {
86  // result is an error number
87  TTLogError("TTMidiPoll result: %ld\n", result);
88  }
89  else {
90  // result is the number of midi events
91  for (int i=0; i<result; i++) {
93  TTValue v(d);
94 
95  // buffer[i].timestamp;
96  // buffer[i].message;
97 
98  char statusByte = Pm_MessageStatus(buffer[i].message);
99  char dataByte1 = Pm_MessageData1(buffer[i].message);
100  char dataByte2 = Pm_MessageData2(buffer[i].message);
101 
102  // TODO: add some cooked Midi event types too (e.g. for notes or control)
103 
104  d->setSchema(TT("RawMidiEvent"));
105  d->append(TT("status"), statusByte);
106  d->append(TT("data1"), dataByte1);
107  d->append(TT("data2"), dataByte2);
108 
109  //self->mCallbackObservers->iterateObjectsSendingMessage(kTTSym_audioEngineWillProcess, v);
110  if (self->mOwner)
111  self->mOwner->push(*d);
112 
113  // TODO: now need to free the dictionary!
114  }
115  }
116  }
117 
118  self->mPollingThread->sleep(1);
119  }
120  return NULL;
121 }
122 
123 
124 TTErr TTMidiInput::setDevice(TTValue& newDeviceName)
125 {
126  TTSymbol newDevice = newDeviceName;
127  const PmDeviceInfo* deviceInfo;
128  int deviceCount;
129  PmError err = pmNoError;
130 
131  if (newDevice != mDevice) {
132  if (newDevice == TT("default")) {
133  mID = Pm_GetDefaultInputDeviceID();
134  mDeviceInfo = Pm_GetDeviceInfo(mID);
135  }
136  else {
137  int i;
138 
139  deviceCount = Pm_CountDevices();
140  for (i=0; i<deviceCount; i++) {
141  deviceInfo = Pm_GetDeviceInfo(i);
142  if (newDevice == TT(deviceInfo->name)) {
143  mDeviceInfo = deviceInfo;
144  mID = i;
145  break;
146  }
147  }
148  if (i == deviceCount)
149  return kTTErrGeneric;
150  }
151 
152  mDevice = newDevice;
153  mRunning = NO;
154  if (mPollingThread)
155  mPollingThread->wait();
156  delete mPollingThread;
157 
158  if (mStream) {
159  Pm_Close(mStream);
160  mStream = NULL;
161  }
162 
163  err = Pm_OpenInput(&mStream, mID, NULL, kMidiBufferSize, NULL, NULL);
164  if (err) {
165  logError("uh oh\n");
166  }
167 
168  mRunning = YES;
169  mPollingThread = new TTThread(TTThreadCallbackType(TTMidiPoll), this);
170  }
171  return kTTErrNone;
172 }
173 
#define addAttribute(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom getter...
Definition: TTAttribute.h:29
TTErr logError(TTImmutableCString fmtstring,...)
Log errors scoped to this object instance.
TTErr append(const TTSymbol aKey, const TTValue aValue)
Insert an item into the hash table.
Definition: TTDictionary.h:212
Jamoma Asynchronous Object Graph Layer.
Symbol type.
Definition: TTBase.h:282
TTBoolean mRunning
should the thread be running? If NO then the thread will know to abort itself
Definition: TTMidiInput.h:31
A type that represents the key as a C-String and the value as a pointer to the matching TTSymbol obje...
Definition: TTDictionary.h:47
#define TT
This macro is defined as a shortcut for doing a lookup in the symbol table.
Definition: TTSymbol.h:155
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
PortMidiStream * mStream
a descriptor for a MIDI device that is opened when the device is set
Definition: TTMidiInput.h:29
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
const PmDeviceInfo * mDeviceInfo
selected device info struct
Definition: TTMidiInput.h:27
void TTFOUNDATION_EXPORT TTLogError(TTImmutableCString message,...)
Platform and host independent method for posting errors.
Definition: TTBase.cpp:572
#define addMessageWithArguments(name)
A convenience macro to be used by subclasses for registering messages.
Definition: TTMessage.h:27
TTErr setSchema(const TTSymbol aSchemaName)
TODO: Add documentation schemaName TODO: Add documentation.
Definition: TTDictionary.h:172
void clear()
Clear all values from the vector, leaving with size of 0.
Definition: TTValue.h:131
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
Pointer type.
Definition: TTBase.h:284
#define addAttributeWithSetter(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom setter...
Definition: TTAttribute.h:47
No Error.
Definition: TTBase.h:343
PmDeviceID mID
selected device ID number
Definition: TTMidiInput.h:28
TTSymbol mDevice
attr: selected device name
Definition: TTMidiInput.h:26
TT_OBJECT_CONSTRUCTOR
Constructor macro.
TTThreadPtr mPollingThread
our loop that constantly polls for new input
Definition: TTMidiInput.h:30
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
TTMidiInput receives MIDI input from an external device.
Definition: TTMidiInput.h:21