MT32MusicComponent.c

/*
   File:      MT32MusicComponent.c

   Contains:   xxx put contents here xxx

   Written by:   xxx put writers here xxx

   Copyright:   © 1994-1996 by Apple Computer, Inc., all rights reserved.

   Change History (most recent first):

      <14>     4/8/96   dvb      New Interfaces
      <13>    9/13/95   JB      music dumpfile for new build
      <12>    8/21/95   dvb      new musiccomponent.h stuff
      <10>    6/12/95   dvb      
       <9+> fine tune the gaps yet again... NAMIDI had it all wrong.
       <9>    28-4-95   dvb      CanDo implemented wrong for subclassed component
       <8>    14-3-95   dvb      gap directive.
       <7+> delay packet type, set reserved to zero
       <7>     6-2-95   dvb      patch params
       <6+> patch and timbre params as part of instrument
       <6>     5-1-95   dvb      
       <5>   14-11-94   dvb      upp fixes
       <4>   24-10-94   dvb      minor
       <3+>   make it cleaner for copying and reusing
       <3>   11-10-94   dvb      
       <2>    10/3/94   JB      Dispos... ==> Dispose...

*/

/*
   File:      MT32MusicComponent.c

   Written by:   dvb

   Copyright:   © 1993-1994 by Apple Computer, Inc., all rights reserved.

*/

/*-----------------------------------
   Inclusions
-----------------------------------*/

#ifdef MUSICDUMP
   #ifdef DUMP_USEINCLUDE
      #include MUSICDUMP
   #else
      #pragma load MUSICDUMP
   #endif   
#else
   #include "QTEnv.h"
   #include "MusicComponent.h"
#endif



/*-----------------------------------
   pascal ComponentResult Types
-----------------------------------*/
#define kBaseResID 300
#define kSysexTimeGap 15      /* milliseconds midi bus gap after param change */



typedef struct
   {
   MusicComponent super;      /* the component that does everything we cannot */
   MusicComponent self;

   MusicMIDISendProcPtr midiProc;
   long midiProcRefCon;
   } GGlobals;

#define MUSIC_BASENAME()       MT32
#define MUSIC_GLOBALS()       GGlobals *g

#include "MusicComponent.k.h"

/*-----------------------------------
   pascal ComponentResult Prototypes
-----------------------------------*/

static ComponentFunctionUPP FindRoutine(short selektor);

pascal ComponentResult MT32Open (GGlobals *g, ComponentInstance self);
pascal ComponentResult MT32Close (GGlobals *g, ComponentInstance self);
pascal ComponentResult MT32CanDo (GGlobals *g, short selektor);
pascal ComponentResult MT32Version (GGlobals *g);


static ComponentResult MT32WriteMIDI(GGlobals *g,int firstByte,...);
static ComponentResult SetMT32Parameter(GGlobals *g,long p,short v);

/*-----------------------------------
   The Stuff
-----------------------------------*/


#define mt32ParamChannels 0x4000D
#define mt32ParamTimbre 0x10000
#define mt32ParamPatch  0x0C000



pascal ComponentResult MT32MusicComponent( ComponentParameters *params, Handle storage )
   {
   ComponentFunctionUPP gcProc;
   ComponentResult result;
   MusicComponent self;

   gcProc = 0;
   result = 0;
   self = (MusicComponent)params->params[0];

   gcProc = FindRoutine(params->what);

   if (gcProc)
      result = CallComponentFunctionWithStorage(storage, params, gcProc);
   else
      result = DelegateComponentCall(params, ((GGlobals *)storage)->super);

   return result;
   }








#define CCaseSelect(callName) \
      case kComponent##callName##Select: \
         componentProc = (ComponentFunctionUPP) MUSIC_BASENAME()callName; \
         break

#define CaseSelect(callName) \
      case kMusic##callName##Select: \
         componentProc = (ComponentFunctionUPP) MUSIC_BASENAME()callName; \
         break

#define DCaseSelect(callName) \
      case kMusicDerived##callName##Select: \
         componentProc = (ComponentFunctionUPP) MUSIC_BASENAME()Derived##callName; \
         break

static ComponentFunctionUPP FindRoutine(short selektor)
   {
   ComponentFunctionUPP componentProc;

   switch (selektor)
      {
      CCaseSelect(Open);
      CCaseSelect(Close);
      CCaseSelect(CanDo);
      CCaseSelect(Version);

      CaseSelect(SetMIDIProc);
      DCaseSelect(SetKnob);
      DCaseSelect(SetPart);
      DCaseSelect(SetInstrument);

      default:
         componentProc = 0;
         break;
      }
   return componentProc;
   }


pascal ComponentResult MT32CanDo (GGlobals *g, short selektor)
   {
   ComponentResult result;
   result = FindRoutine(selektor) != 0;

   if(!result)
      result = ComponentFunctionImplemented(g->super,selektor);

   return result;
   }


pascal ComponentResult MT32Version (GGlobals *g)
   {
   return 0x00010001;
   }


pascal ComponentResult MT32Open (GGlobals *g, ComponentInstance self)
   {
   ComponentResult result;

   g = (GGlobals *)NewPtrClear(sizeof(GGlobals));

   if (result = MemError())
      goto goHome;

   g->self = self;
   SetComponentInstanceStorage(self, (Handle)g);

   g->super = OpenDefaultComponent(kMusicComponentType,kGenericMusicComponentSubtype);
   if(!g->super)
      {
      result = -1;         /* cant open synth err */
fail:
      SetComponentInstanceStorage(self, nil);
      DisposePtr((Ptr)g);
      goto goHome;
      }

   result = ComponentSetTarget(g->super,g->self);
   if(result)
      goto fail;

   result = MusicGenericConfigure(g->super,0,
         kGenericMusicDoMIDI
         + kGenericMusicCallKnobs
         + kGenericMusicCallParts
         + kGenericMusicCallInstrument,
         kBaseResID);      /* tell it our resource ID */
   if(result)
      goto fail;

goHome:
   return result;
   }


pascal ComponentResult MT32Close (GGlobals *g, ComponentInstance self)
   {
   ComponentResult result;

   SetMT32Parameter(g,0x1FFFFF,0);      /* reset all parameters */

   result = CloseComponent(g->super);
   DisposePtr((Ptr)g);
   SetComponentInstanceStorage(self,0);

   return result;
   }


/* -------------------------------- -------------------------------- */

pascal ComponentResult MT32SetMIDIProc(GGlobals *g,
      MusicMIDISendProcPtr midiProc, long refCon)
   {
   ComponentResult result;
   short i;

   g->midiProc = midiProc;
   g->midiProcRefCon = refCon;

   result = MusicSetMIDIProc(g->super,midiProc,refCon);

   /*
    * Turn off the 6 PCM voices above, if
    * it happens to be a CM500.
    * This assumes we chose mode B for our unit.
    */
   MT32WriteMIDI(g,0xF0,0x41,0x10,0x16,0x12,
         0x52,0x00,0x0A,
         0x10,0x10,0x10,
         0x10,0x10,0x10,
         0   ,0xF7,-3);

   /*
    * Turn off all 9 parts of the CM32
    */
   MT32WriteMIDI(g,0xF0,0x41,0x10,0x16,0x12,
         0x10,0x00,0x0D,
         0x10,0x10,0x10,
         0x10,0x10,0x10,
         0x10,0x10,0x10,
         0   ,0xF7,-3);

   return result;
   }



static ComponentResult MT32WriteMIDI(GGlobals *g,int firstByte,...)
   {
   MusicMIDIPacket somePacket;
   register unsigned char *w;
   register int *source;
   register short x;
   OSErr result;

   if(!g->midiProc)
      {
      result = cantSendToSynthesizerErr;
      goto goHome;
      }







   w = somePacket.data;
   source = &firstByte;

   do
      {
      x = *source++;
      if(x >= 0)
         *w++ = x;
      } while (x >= 0);

   if(x == -2 || x == -3)      /* we're asked to compute checksum */
      {
      unsigned char *w1,*w2;
      short sum;

      w1 = somePacket.data + 5;
      w2 = w - 2;
      sum = 0;
      while(w1 < w2)
         sum -= *w1++;
      *w2 = sum & 0x7F;
      }

   if(x == -3)      // -3 is checksum and time gap before & after
      {
      MusicMIDIPacket gapPacket;

      gapPacket.length = 1;
      gapPacket.reserved = kMusicPacketTimeGap;
      gapPacket.data[0] = kSysexTimeGap;      /* pause in milliseconds */
      result = (*g->midiProc)(g->self,g->midiProcRefCon,&gapPacket;);

      somePacket.length = w - somePacket.data + 1;
      *w = kSysexTimeGap;
      somePacket.reserved = kMusicPacketTimeGap;
      }
   else
      {
      somePacket.length = w - somePacket.data;
      somePacket.reserved = 0;
      }

   result = (*g->midiProc)(g->self,g->midiProcRefCon,&somePacket;);
goHome:
   return result;
   }


pascal ComponentResult MT32DerivedSetKnob(GGlobals *g,
      long knobType,long knobNumber,long knobValue,
      long partNumber,GCPart *p,
      GenericKnobDescription *gkd)
   {
   ComponentResult result;
   short effectSetting;

   if(knobType == kGenericMusicKnob)
      result = SetMT32Parameter(g,gkd->hw1,knobValue);
   else if(knobType == kGenericMusicInstrumentKnob)
      {
      long param;

      partNumber--;

      if(gkd->hw1 & 0x80000000)
         param = mt32ParamPatch + partNumber * 0x10 + (gkd->hw1 & 0xFF);
      else
         param = mt32ParamTimbre + partNumber * 0xF6 + gkd->hw1;
      knobValue = knobValue + gkd->hw3 - gkd->kd.lowValue;
      result = SetMT32Parameter(g,param,knobValue);
      }

   return result;
   }

ComponentResult SetMT32Parameter(GGlobals *g,long p,short v)
   {
   unsigned short ah,am,al;
   ComponentResult result;

   ah = (p>>14) & 0x7F;
   am = (p>>7) & 0x7F;
   al = p & 0x7F;
   
   result = MT32WriteMIDI(g,
         (int)0xF0,
         (int)0x41,
         (int)0x10,
         (int)0x16,
         (int)0x12,
         (int)ah,
         (int)am,
         (int)al,
         (int)v,
         (int)0,      // checksum computed by writemidi
         (int)0xF7,
         (int)-3);

#if 0
lame, lame, attach to packet
   if(g->midiProc)
      {
      MusicMIDIPacket somePacket;

      somePacket.length = 1;
      somePacket.reserved = kMusicPacketTimeGap;
      somePacket.data[0] = kSysexTimeGap;      /* pause in milliseconds */
      result = (*g->midiProc)(g->self,g->midiProcRefCon,&somePacket;);
      }
#endif
   return result;
   }

pascal ComponentResult MUSIC_BASENAME()DerivedSetPart(GGlobals *g,
      long partNumber,GCPart *p)
   {
   long param;
   short v;
   ComponentResult result;

   result = noErr;

   v = p->midiChannel;

   param = mt32ParamChannels + partNumber - 1;
   if(v == 0)
      v = 16;
   else
      v--;

   result = SetMT32Parameter(g,param,v);

   return result;
   }
   

pascal ComponentResult MUSIC_BASENAME()DerivedSetInstrument(GGlobals *g,
      long partNumber,GCPart *p)
/*
 * Set the knobs one by one. It would be cooler
 * if we could set them all at once. !!!
 */
   {
   short i;
   ComponentResult result;

   result = noErr;

   for(i = 0; i < p->id.knobCount; i++)
      MusicSetPartKnob(g->self,partNumber,i+1,p->id.knob[i]);

   return result;
   }