home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / sound / chaneler.sit / Channelizer.ƒ / MungeMidi.p < prev    next >
Text File  |  1989-09-08  |  10KB  |  343 lines

  1. unit MungeMidi;
  2. { Copyright ⌐ 1989 Chris Muir, Entropy Engineering.        }
  3. { Internet:    (hplabs,pacbell,ucbvax,apple) !well!cbm     }
  4. { Pan:        ZMS                                            }
  5. { US Mail:    360 Elizabeth St. San Francisco, CA 94114    }
  6.  
  7. { ### Channeler. Forces all MIDI input to be on one output channel. ### }
  8.  
  9. { 1.0        ╩August 17, 1989    cbm }
  10.  
  11.  
  12. interface
  13.  
  14.     uses
  15.         Midi;    { QuickDraw, ToolIntf, WindowLayout, }
  16.  
  17.     procedure InitMyMidi;
  18.     procedure UnInitMyMidi;
  19.  
  20.  
  21.     var
  22.         gClockMIDIRefNum: integer;
  23.         gInMIDIRefNum: integer;
  24.         gOutMIDIRefNum: integer;
  25.         gTheOutChannel: integer;
  26.         gErrorPacket: MIDIPacket;
  27.         gMMVersion: longint;
  28.         gMidiOutChanNum: integer;    { MIDI output chan. Needs to be set externally. 0-16 range. }
  29.  
  30.     const
  31.         kDratDialog = 10;
  32.     { Needs a dialog of this number in resource file. }
  33.     { Dialog should have a button as item 1, and 3 static text items (^0,^1,^2) }
  34.         I_Drat = 1;    { Dialog 'cancel' button }
  35.  
  36.  
  37. implementation
  38.  
  39.     var
  40.         pOutPacket: MIDIPacket;
  41.         pOutPacketPtr: MIDIPacketPtr;
  42.  
  43.     const
  44.         kMMPacketHeadLen = 6;    { subtract this from the MM len byte to get # of data bytes. }
  45.         kMMSig = 'mChn';        { our MM Signature (and application signature) }
  46.  
  47.  
  48.  
  49. { *Ñ*Ñ*Ñ*Ñ*Ñ*Ñ*Ñ*Ñ* Some Midi Equates *Ñ*Ñ*Ñ*Ñ*Ñ*Ñ*Ñ*Ñ* }
  50.  
  51. { If a midi status byte (data[0]) is shifted right 4 and masked with 7, }
  52. {the 3 lsbs that are left ╥tell the tale╙. Like this:}
  53. {Ñ    CommandType := BitShift(ByteInQuestion, -4);Ñ}
  54. {Ñ    CommandType := BitAnd(CommandType, $07);Ñ}
  55. {These are the constants for use with a case statement on CommandType (the 3 lsbs above). }
  56.         C_NoteOff = 0;
  57.         C_NoteOn = 1;
  58.         C_PolyKey = 2;
  59.         C_Control = 3;
  60.         C_Prog = 4;
  61.         C_Pressure = 5;
  62.         C_Bend = 6;
  63.         C_Sys = 7;
  64.  
  65.         M_StatMask = $80;        { BitAnd w/ byte in question to determine if a status byte }
  66.         M_ChanMask = $0F;        { BitAnd w/ byte in question to isolate channel nibble }
  67.         M_NoChanMask = $F0;        { BitAnd w/ byte in question to wipe channel nibble }
  68.  
  69.         M_ContSwStart = 64;        { the first switch continous controller }
  70.         M_ContSwStop = 95;        { the last switch continous controller }
  71.         M_ContContStop = 120;    { the last continous controller }
  72.  
  73. { Channel Mode Messages }
  74.         M_ResetControllers = 121;
  75.         M_LocalControl = 122;
  76.         M_AllNotesOff = 123;
  77.         M_OmniOff = 124;
  78.         M_OmniOn = 125;
  79.         M_MonoOn = 126;
  80.         M_PolyOn = 127;
  81.  
  82.         M_SysEx = $F0;            {  }
  83.         M_SongPointer = $F2;    {  }
  84.         M_SongSelect = $F3;        {  }
  85.         M_TuneReq = $F6;        {  }
  86.         M_EOX = $F7;            {  }
  87.         M_Clock = $F8;            {  }
  88.         M_Start = $FA;            {  }
  89.         M_Continue = $FB;        {  }
  90.         M_Stop = $FC;            {  }
  91.         M_ActiveSense = $FE;    {  }
  92.         M_Reset = $FF;            {  }
  93.  
  94.         ForceOff = $10;            { Subtract from a note-on w/ Velo=0 to make a note-off }
  95.         StatToZero = 127;        { subtract this from a status byte to make it zero-based }
  96.         EndNotes = 33;            { the first non-NoteOn / NoteOff byte }
  97.  
  98.  
  99.     function SetA5 (newA5: LONGINT): LONGINT;
  100.     inline        { returns some partition's A5, and sets A5 to newA5 }
  101.         $2F4D, $0004, $2A5F;
  102.  
  103.     procedure YodelMidiErr (Msg1, Msg2, Msg3: str255);
  104.     forward;
  105.  
  106.  
  107.  
  108.  
  109.  
  110. {_____________________    CheckMidiManErr    _____________________}
  111.  
  112. {$D-}
  113.     procedure CheckMidiManErr (myOops: OsErr; theRoutine: str255; BadPacket: MIDIPacketPtr);
  114.         var
  115.             theErrStr: str255;
  116.             ohMy: str255;
  117.     begin
  118.         if myOops <> noErr then
  119.             begin
  120.     { we can't do much in the way of reporting at interrupt time. }
  121.     { this is pretty fragile. }
  122.                 gErrorPacket := BadPacket^;        { put bad packet into a global }
  123.                 DebugStr(theRoutine);            { and crash into MacsBug, TMON, etc. }
  124.             end;
  125.     end;
  126. {$D+}
  127.  
  128.  
  129.  
  130. {_____________________    YodelMidiErr    _____________________}
  131.  
  132.     procedure YodelMidiErr (Msg1, Msg2, Msg3: str255);
  133.         var
  134.             thisDialog: DialogPtr;
  135.             exitDialog: boolean;
  136.             tempRect: Rect;
  137.             dType: Integer;
  138.             dItem: Handle;
  139.             itemHit: Integer;
  140.     begin
  141.         begin
  142.             thisDialog := GetNewDialog(kDratDialog, nil, Pointer(-1));
  143.             paramText(Msg1, Msg2, Msg3, '');                                { Speak }
  144.             ShowWindow(thisDialog);
  145.             SelectWindow(thisDialog);                                        { Let's see it }
  146.             exitDialog := false;
  147.             repeat
  148.                 ModalDialog(nil, itemHit);
  149.                 GetDItem(thisDialog, itemHit, dType, dItem, tempRect);         { what was touched? }
  150.  
  151.                 if (ItemHit = I_Drat) then
  152.                     begin
  153.                         exitDialog := true;
  154.                     end;
  155.  
  156.             until exitDialog;
  157.             DisposDialog(thisDialog);
  158.         end;    { dialog handler }
  159.     end;        { YodelMidiErr }
  160.  
  161.  
  162.  
  163.  
  164.  
  165. {_____________________    MyMidiInput    _____________________}
  166. {$D-}
  167. { This is our Readhook. Called at interrupt time. Be careful.}
  168.  
  169.     function MyMidiInput (myPacket: MIDIPacketPtr; myRefCon: LONGINT): integer;
  170.         var
  171.             packetType: longint;
  172.             oldA5: longint;
  173.             passOut: MIDIPacketPtr;
  174.             theStatusByte: byte;
  175.             theStatusCom: byte;
  176.             theCom: byte;
  177.             theByte: byte;
  178.  
  179.     begin
  180.         oldA5 := SetA5(myRefCon);
  181.         packetType := BAND(myPacket^.flags, midiTypeMask);
  182.         if (packetType = midiMsgType) & (myPacket^.data[0] <> 0) then
  183.             begin        { good packet }
  184.                 pOutPacket := myPacket^;                { copy packet }
  185.                 pOutPacketPtr := @pOutPacket;            { set pointer to copied packet }
  186.                 theStatusByte := pOutPacket.data[0];    { grab the status byte }
  187.                 theCom := BitShift(theStatusByte, -4);    { create the index }
  188.                 theCom := BitAnd(theCom, $07);
  189.  
  190.                 case theCom of
  191.                     C_NoteOff, C_NoteOn, C_PolyKey, C_Prog, C_Pressure, C_Control, C_Bend: 
  192.                         begin
  193.                             theStatusCom := BAND(theStatusByte, M_NoChanMask);    { wipe old chan }
  194.                             theStatusByte := BOR(theStatusCom, gMidiOutChanNum);    { OR in new chan }
  195.                             pOutPacket.data[0] := theStatusByte;                    { and stash it }
  196.                         end;
  197.                     C_Sys: 
  198.                         begin        { just pass system messages }
  199.                         end;
  200.                     otherwise
  201.                         begin        { there really can't be an otherwise, but╔ }
  202.                         end;
  203.                 end;    { case theCom }
  204.  
  205.                 CheckMidiManErr(MIDIWritePacket(gOutMIDIRefNum, pOutPacketPtr), 'MyMidiInput.write', @pOutPacketPtr);
  206.             end        { if (packetType = midiMsgType) & (myPacket^.data[0] <> 0) }
  207.  
  208.         else
  209.  
  210.             begin        { if (packetType <> midiMsgType) | (myPacket^.data[0] = 0) }
  211.                 { we have a bogus packet }
  212.                 CheckMidiManErr(-1, 'MyMidiInput.read', @pOutPacketPtr);
  213.             end;
  214.  
  215.         MyMidiInput := midiMorePacket;        { ask for more }
  216.         oldA5 := SetA5(oldA5);                { restore pre-interrupt A5 }
  217.     end;    { MyMidiInput }
  218. {$D+}
  219.  
  220.  
  221. {_____________________    InitMyMidi    _____________________}
  222.  
  223.     procedure InitMyMidi;
  224.         const
  225.             CurA5 = $904;
  226.         var
  227.             tempRect: rect;
  228.             myOops: OsErr;
  229.             myIcon: Handle;
  230.             myMidiClkInfo: MIDIClkInfo;
  231.             myMidiClockParams: MIDIPortParams;
  232.             myMidiInputParams: MIDIPortParams;
  233.             myMidiOutputParams: MIDIPortParams;
  234.             theErrStr: str255;
  235.     begin
  236.         myIcon := Get1Resource('ICN#', 128);            { our applications ICN# }
  237.         gMMVersion := SndDispVersion(midiToolNum);        { are we here? }
  238.         if gMMVersion = 0 then
  239.             begin    { oops }
  240.                 YodelMidiErr('Midi Manager Not Present', '', '');
  241.             end;
  242.         myOops := MidiSignIn(kMMSig, 0, myIcon, 'Channeler');
  243.  
  244.  
  245.  
  246.         begin     { add the Clock Port, not used by Channeler. Example only. }
  247.             myMidiClkInfo.sync := midiInternalSync;
  248.             myMidiClkInfo.curTime := 0;
  249.             myMidiClkInfo.format := midiFormatMSec;
  250.  
  251.             myMidiClockParams.portID := 'Clk ';                        { it has to have a name. }
  252.             myMidiClockParams.portType := midiPortTypeTimeInv;        { invisable, for an internal time base only }
  253.             myMidiClockParams.timeBase := 0;                            { this IS the time base }
  254.             myMidiClockParams.offsetTime := 0;                        { start at the begining }
  255.             myMidiClockParams.readHook := nil;                        {  }
  256.             myMidiClockParams.refCon := Ord4(handle(CurA5)^); { our A5, so we can find our globals @ Interrupt time }
  257.             myMidiClockParams.initClock := myMidiClkInfo;
  258.             myMidiClockParams.name := '';
  259.             myOops := MIDIAddPort(kMMSig, 2048, gClockMIDIRefNum, @myMidiClockParams);
  260.             if myOops <> noErr then
  261.                 if myOops = midiVConnectMade then
  262.                     begin
  263.                     end
  264.                 else if myOops = midiVConnectErr then
  265.                     begin
  266.                     end
  267.                 else
  268.                     begin
  269.                         NumToString(myOops, theErrStr);
  270.                         YodelMidiErr('Midi Manager Error#', theErrStr, ' while doing MIDIAddPort (time)');
  271.                     end;
  272.             MIDIStartTime(gClockMIDIRefNum);    { this program doesn't use time, but let's start it anyway }
  273.         end;    { add the Clock Port }
  274.  
  275.  
  276.         begin    { add the Input Port }
  277.             myMidiInputParams.portID := 'In  ';
  278.             myMidiInputParams.portType := midiPortTypeInput;
  279.             myMidiInputParams.timeBase := gClockMIDIRefNum;
  280.             myMidiInputParams.offsetTime := 0;
  281.             myMidiInputParams.readHook := @MyMidiInput;
  282.             myMidiInputParams.refCon := Ord4(handle(CurA5)^);
  283.             myMidiInputParams.initClock := myMidiClkInfo;
  284.             myMidiInputParams.name := '';
  285.             myOops := MIDIAddPort(kMMSig, 2048, gInMIDIRefNum, @myMidiInputParams);
  286.             if myOops <> noErr then
  287.                 if myOops = midiVConnectMade then
  288.                     begin
  289.                     end
  290.                 else if myOops = midiVConnectErr then
  291.                     begin
  292.         { the MM had a virtual patch pending. It's not really an error. }
  293.                     end
  294.                 else
  295.                     begin
  296.                         NumToString(myOops, theErrStr);
  297.                         YodelMidiErr('Midi Manager Error#', theErrStr, ' while doing MIDIAddPort (input)');
  298.                     end;
  299.         end;    { add the Input Port }
  300.  
  301.         begin     { add the Output Port }
  302.             myMidiOutputParams.portID := 'Out ';
  303.             myMidiOutputParams.portType := midiPortTypeOutput;
  304.             myMidiOutputParams.timeBase := gClockMIDIRefNum;
  305.             myMidiOutputParams.offsetTime := 0;
  306.             myMidiOutputParams.readHook := nil;
  307.             myMidiOutputParams.refCon := 0;
  308.             myMidiOutputParams.initClock := myMidiClkInfo;
  309.             myMidiOutputParams.name := '';
  310.             myOops := MIDIAddPort(kMMSig, 2048, gOutMIDIRefNum, @myMidiOutputParams);
  311.             if myOops <> noErr then
  312.                 if myOops = midiVConnectMade then
  313.                     begin
  314.                     end
  315.                 else if myOops = midiVConnectErr then
  316.                     begin
  317.                     end
  318.                 else
  319.                     begin
  320.                         NumToString(myOops, theErrStr);
  321.                         YodelMidiErr('Midi Manager Error#', theErrStr, ' while doing MIDIAddPort (output) ');
  322.                     end;
  323.         end;     { add the Output Port }
  324.  
  325.         gMidiOutChanNum := 0;
  326.         new(pOutPacketPtr);
  327.     end;    { InitMyMidi }
  328.  
  329.  
  330.  
  331.  
  332. {_____________________    UnInitMyMidi    _____________________}
  333.  
  334.     procedure UnInitMyMidi;
  335.     begin
  336.         MidiSignOut(kMMSig);
  337.     end;    { UnInitMyMidi }
  338.  
  339.  
  340.  
  341.  
  342.  
  343. end.