CLAM-Development  1.4.0
MIDIInControl.cxx
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG)
3  * UNIVERSITAT POMPEU FABRA
4  *
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include "MIDIInControl.hxx"
23 #include "OutControl.hxx"
24 #include <string>
25 #include "ProcessingFactory.hxx"
26 
27 namespace CLAM
28 {
29 
30 namespace Hidden
31 {
32  static const char * metadata[] = {
33  "key", "MIDIInControl",
34  // "category", "MIDI",
35  // "description", "MIDIInControl",
36  0
37  };
38  static FactoryRegistrator<ProcessingFactory, MIDIInControl> reg = metadata;
39 }
40 
42 {
43  mpDevice = 0;
44  mMessageSize = mControllingBytes = 0;
45  mMsgByteIdToControlId = 0;
47 }
48 
50 {
51  mpDevice = 0;
52  mMessageSize = mControllingBytes = 0;
53  mMsgByteIdToControlId = 0;
54  Configure(c);
55 }
56 
57 
59  throw(ErrProcessingObj)
60 {
61  bool ret = MIDIIn::ConcreteConfigure(c);
62  if (ret==false) return false;
63 
64  MIDI::Message m = MIDI::Message(mConfig.GetMessage());
65 
66  int mMessageSize = MIDI::GetMessageInfo(m).length;
67 
68  /* the amount of controlled bytes is the lenght of the midi message,
69  * but...: */
70  mControllingBytes = mMessageSize;
71  /* ... one less if we predefine the channel ... */
72  if (mConfig.GetChannel()!=0) mControllingBytes--;
73  /* ... and one less if we predefine the first data byte,
74  * which is particularly useful for control change messages */
75  if (mConfig.GetFirstData()!=128) mControllingBytes--;
76 
77  if (mMsgByteIdToControlId) delete mMsgByteIdToControlId;
78  mMsgByteIdToControlId = new unsigned char[mControllingBytes];
79 
80  int ctrlid = 0;
81 
82  bool singlePitchBendValue = false;
83 
84  /* create the InControls */
85  for (int i=0;i<mMessageSize;i++)
86  {
87  const char* fieldname = 0;
88  /* if in this switch we set the fieldname, the control
89  * will be added */
90  switch (i)
91  {
92  case 0:
93  if (mConfig.GetMessage()==MIDI::eSystem)
94  {
95  if (mConfig.GetChannel()==0)
96  fprintf(stderr,"ERROR: sysex in not yet implemented\n");
97  else
98  /* channel is not predefined */
99  fieldname = MIDI::GetMessageInfo(m).field[i];
100  }else{
101  if (mConfig.GetChannel()==0)
102  /* channel is not predefined */
103  fieldname = MIDI::GetMessageInfo(m).field[i];
104  }
105  break;
106  case 1:
107  if (mConfig.GetFirstData()==128)
108  {
109  /* first data byte is not predefined */
110  fieldname = MIDI::GetMessageInfo(m).field[i];
111  }
112  /* we make an exception for pitchbend: instead of putting
113  * out to values (LSB, MSB), we prefer 1 14bit value.
114  * see also the code in Handle
115  */
116  /* nb: does a FirstData make sense with pitchbend??
117  * I don't think so, so we'll just ignore it.
118  * (silently...)
119  */
120  if (mConfig.GetMessage()==MIDI::ePitchbend)
121  {
122  fieldname = "Value";
123  singlePitchBendValue = true;
124  }
125 
126  break;
127  default:
128  /* all other fields will be controlled */
129  if (!singlePitchBendValue)
130  {
131  fieldname = MIDI::GetMessageInfo(m).field[i];
132  }
133  break;
134  }
135  if (fieldname)
136  {
137  std::string tmp = std::string() + MIDI::GetMessageInfo(m).name + ":" + fieldname;
138  /* add the InControl, and remember which message byte it will
139  * control */
140  mMsgByteIdToControlId[i] = ctrlid++;
141  mMyOutControls.AddElem(new FloatOutControl(tmp.c_str(),this));
142  }else{
143  mMsgByteIdToControlId[i] = 0xFF;
144  }
145  }
146 
147  return true;
148 }
149 
150 void MIDIInControl::Handle(unsigned char* msg,int size)
151 {
152  /* The device has passed the message to this MIDIInControl.
153  * We now need dispatch the message to the resp. OutControls
154  */
155  for (int i=size-1;i>=0;i--)
156  {
157  if (i==0 && (msg[0]&0xF0) == 0xF0) // system message
158  {
159  /* TODO: this now only handles correctly system realtime
160  * messages, where SetChannel is used to specify the
161  * type of message. Maybe this can be done more elegantly? */
162  SendFloatToOutControl(*this,0,1);
163  }
164  else
165  {
166  if (mMsgByteIdToControlId[i] == 0xFF) continue;
167 
168  if (i==1 && (msg[0]&0xF0)==0xE0)
169  {
170  /* we make an exception for pitchbend: instead of putting
171  * out to values (LSB, MSB), we prefer 1 14bit value.
172  * see also the code in ConcreteConfigure
173  */
174  SendFloatToOutControl(*this,mMsgByteIdToControlId[1],msg[1] + (msg[2]<<7));
175  }
176  else
177  if (i==0)
178  {
179  SendFloatToOutControl(*this,mMsgByteIdToControlId[0],(msg[0]&0x0F)+1);
180  }
181  else
182  {
183  SendFloatToOutControl(*this,mMsgByteIdToControlId[i],msg[i]);
184  }
185  }
186  }
187 }
188 
189 
190 } // namespace CLAM
191