CLAM-Development  1.1
MIDIOutControl.cxx
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG)
00003  *                         UNIVERSITAT POMPEU FABRA
00004  *
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 #include "InControl.hxx"
00023 #include "MIDIOutControl.hxx"
00024 #include <string>
00025 #include <cstdio>
00026 
00027 namespace CLAM {
00028 
00029 MIDIOutControl::MIDIOutControl():MIDIOut(false)
00030 { 
00031         mpDevice = 0;
00032         InitMembers();
00033         Configure(MIDIIOConfig());
00034 }
00035 
00036 MIDIOutControl::MIDIOutControl(const MIDIIOConfig &c):MIDIOut(false)
00037 {
00038         mpDevice = 0;
00039         InitMembers();
00040         Configure(c);
00041 }
00042 
00043 void MIDIOutControl::InitMembers(void)
00044 {
00045         mUniqId = 0;
00046 
00047         mMessage = 0;
00048         mReceivedUniqId = 0;
00049         mControlIdToMsgByteId = 0;
00050 
00051         mMessageSize = 0;
00052 
00053         mControlledBytes = 0;
00054         mControlsReceived = 0;
00055 }
00056 
00057 bool MIDIOutControl::ConcreteConfigure(const ProcessingConfig& c)
00058         throw(ErrProcessingObj)
00059 {
00060         bool ret = MIDIOut::ConcreteConfigure(c);
00061         
00062         if (ret==false) return false;
00063 
00064         MIDI::Message m = MIDI::Message(mConfig.GetMessage());
00065 
00066         mMessageSize = MIDI::GetMessageInfo(m).length;
00067 
00068         /* the amount of controlled bytes is the lenght of the midi message,
00069          * but...: */
00070         mControlledBytes = MIDI::GetMessageInfo(m).length;
00071         /* ... one less if we predefine the channel ... */
00072         if (mConfig.GetChannel()!=0) mControlledBytes--;
00073         /* ... and one less if we predefine the first data byte,
00074          * which is particularly useful for control change messages */
00075         if (mConfig.GetFirstData()!=128) mControlledBytes--;
00076 
00077         mControlsReceived = 0;
00078 
00079         /* allocate arrays */
00080         if (mMessage) delete [] mMessage;
00081         mMessage = new unsigned char[mMessageSize];
00082 
00083         if (mReceivedUniqId) delete [] mReceivedUniqId;
00084         mReceivedUniqId = new unsigned char[mControlledBytes];
00085 
00086         if (mControlIdToMsgByteId) delete mControlIdToMsgByteId;
00087         mControlIdToMsgByteId = new unsigned char[mControlledBytes];
00088 
00089         /* init uniq-id-per-received-value array */
00090         for (int i = 0; i < mControlledBytes ; i++ )
00091         {
00092                 mReceivedUniqId[i] = mUniqId;
00093         }
00094         
00095         if (m==MIDI::eNoteOnOff) m = MIDI::eNoteOn;
00096         /* init first message byte, based on message type */
00097         mStatusByte = 0x80|(int(m)<<4);
00098 
00099         int ctrlid = 0;
00100         
00101         /* create the InControls */
00102         for (int i=0;i<MIDI::GetMessageInfo(m).length;i++)
00103         {
00104                 const char* fieldname = 0;
00105                 /* if in this switch we set the fieldname, the control
00106                  * will be added */
00107                 switch (i)
00108                 {
00109                         case 0:
00110                                 if (mConfig.GetChannel()==0)
00111                                         /* channel is not predefined */
00112                                         fieldname = "Channel";
00113                                 else
00114                                         /* channel _is_ predefined, so modify status byte
00115                                          * to contain channel */
00116                                         mStatusByte |= (mConfig.GetChannel()+1);
00117                                 break;
00118                         case 1:
00119                                 if (mConfig.GetFirstData()==128)
00120                                         /* first data byte is not predefined */
00121                                         fieldname = MIDI::GetMessageInfo(m).field[i-1];
00122                                 else
00123                                         /* first data byte _is_ predefined, so modify message */        
00124                                         mMessage[1] = mConfig.GetFirstData();
00125                                 break;
00126                         default:
00127                                 /* all other fields will be controlled */
00128                                 fieldname = MIDI::GetMessageInfo(m).field[i-1];
00129                                 break;
00130                 }
00131                 if (fieldname)
00132                 {
00133                         std::string tmp = std::string() + MIDI::GetMessageInfo(m).name + ":" + fieldname;
00134                         /* add the InControl, and remember which message byte it will
00135                          * control */
00136                         mControlIdToMsgByteId[ctrlid] = i;
00137                         mMyInControls.AddElem(new InControlTmpl<MIDIOutControl>(ctrlid++,tmp.c_str(),this,&MIDIOutControl::DoControl));
00138                 }
00139         }
00140 
00141         return true;
00142 }
00143 
00144 int MIDIOutControl::DoControl(int id,TControlData val)
00145 {
00146         /* we keep a uniq id to check if each message has been fully 
00147          * constructed */
00148         if (mReceivedUniqId[id]==mUniqId)
00149         {
00150                 /* ok, we still needed this byte */
00151                 int i = mControlIdToMsgByteId[id];
00152                 
00153                 if (i==0)
00154                 {
00155                         /* for the first byte, we need to keep the status, and
00156                          * modify the channel */
00157                         mStatusByte = (mStatusByte&0xF0) | ((unsigned char)(val)-1);
00158                 }else{
00159                         mMessage[i] = (unsigned char) val;
00160                 }
00161                 mReceivedUniqId[id]++;
00162                 mControlsReceived++;
00163                 if (mControlsReceived==mControlledBytes)
00164                 {
00165                         /* we got all controlled bytes, so we increment the mUniqId
00166                          * for the next message, reset mControlsReceived, and 
00167                          * call Handle to send the message to the device */
00168                         mUniqId++;
00169                         mControlsReceived = 0;
00170                         mMessage[0]=mStatusByte;
00171                         Handle(mMessage,mMessageSize);
00172                 }
00173         }else{
00174                 /* receiving a byte when the prev message was not fully
00175                  * constructed yet... TODO: should we throw or assert? */
00176                 fprintf(stderr,"ERROR!!!! receiving a byte when the prev message was not fully constructed yet... TODO: should we throw or assert?\n");
00177         }
00178         return 1;
00179 }
00180 
00181 void MIDIOutControl::Handle(unsigned char* msg,int size)
00182 {
00183         /* write the message to the device */
00184         CLAM_ASSERT(mpDevice,"MIDIOutControl used without a valid device");
00185         if ((msg[0]&0xF0)==0x90 && msg[2]==0) msg[0] &=0x8F;
00186         mpDevice->Write(msg,size);
00187 }
00188 
00189 } // namespace CLAM
00190