home *** CD-ROM | disk | FTP | other *** search
/ Audio 4.94 - Over 11,000 Files / audio-11000.iso / mac / soundutl / vmtrs10s.hqx / VUMeters / Sources / VUMeters.C < prev    next >
Text File  |  1993-08-05  |  46KB  |  1,835 lines

  1. /*
  2.  *                  ARTA VUMeters for Macintosh
  3.  *                      Malcolm Slaney
  4.  *                 Advanced Technology Group
  5.  *                Apple Computer, Inc.
  6.  *                 malcolm@apple.com
  7.  *                     1992-1993
  8.  *
  9.  *    Warranty Information
  10.  *    Even though Apple has reviewed this software, Apple makes no warranty
  11.  *    or representation, either express or implied, with respect to this
  12.  *    software, its quality, accuracy, merchantability, or fitness for a 
  13.  *    particular purpose.  As a result, this software is provided "as is,"
  14.  *    and you, its user, are assuming the entire risk as to its quality
  15.  *    and accuracy.
  16.  *
  17.  *    Copyright (c) 1992-1993 by Apple Computer, Inc
  18.  *        All Rights Reserved.
  19.  */
  20.  
  21. /*
  22.  *    This application is actually implemented using three different ThinkC
  23.  *    objects.  
  24.  *
  25.  *    The most primitive is call CWindowPiece.  This is just a piece of a 
  26.  *    window on the screen.  It comes with it's own offscreen GWorld and a
  27.  *    record of what region of the window is dirty.  The idea is that a normal
  28.  *    Macintosh window can be tiled with CWindowPieces to provide all the 
  29.  *    interesting graphics.  This class is not specific to specific to a meter.
  30.  *    It can be used by any object that needs fast graphics on the screen.
  31.  *
  32.  *    The CVUMeter class is a specialized kind of CWindowPiece.  It knows
  33.  *    about all the things that make a VUMeter worthwhile.  As such, it knows
  34.  *    how to draw a scale with ticks and labels, how to draw a pointer, and 
  35.  *    where to get the latest meter reading.  These objects contain off-screen
  36.  *    GWorlds for the scale (and the overall Gworld provided by the CWindowPiece.)
  37.  *
  38.  *    The CPortWindow is a high level object that keeps track of a Macintosh
  39.  *    window and tiles one or two of the CVUMeter objects (depending on whether
  40.  *    we are displaying a mono or stereo VUMeter.).
  41.  *
  42.  *    One helper objects is also defined.
  43.  *
  44.  *    The CPictureMeter class is a special object that uses a PICT in the resource
  45.  *    fork to define the scale.  This is only used by the "About VUMeters" dialog.
  46.  *
  47.  */
  48.     
  49. #include    <math.h>
  50. #include    <stdio.h>
  51. #include    "VUMeters.h"
  52. #include    "VUMeterResources.h"
  53. #include    <DSPManager.h>
  54. #include    <StandardSound.h>
  55. #include    "SoundMonitor.h"
  56.  
  57. RGBColor    rgbBackgroundColor = {65535, 63542, 55792};    /* Dirty Yellow */
  58. RGBColor    rgbTickColor = {0, 0, 0};            /* Black */
  59. RGBColor    rgbOverloadColor = {65535, 0, 0};        /* Bright Red */
  60. RGBColor    rgbBlackColor = {0, 0, 0};            /* Black */
  61. RGBColor    rgbWhiteColor = {65535, 65535, 65535};        /* White */
  62.  
  63. #define    kMeterTimeConstant    .25                /* In Seconds */
  64.  
  65. #define    kDefaultVUWidth        200
  66. #define    kDefaultVUHeight    90
  67.  
  68. #define    MIN_WINDOW_HEIGHT    kDefaultVUWidth
  69. #define    MIN_WINDOW_WIDTH    kDefaultVUHeight
  70.  
  71. #define    DSP_MAXIMUM_VALUE    (float)1.0
  72.  
  73. int    gPatchChannels = kMaxChannel;
  74. int    gPortType = kInputPort;
  75. int    gLevelType = kPeakReading;
  76. int    gSpeedType = kNormalSpeed;
  77. int    gReferenceType = kFullScaleReference;
  78.  
  79. float    gLeftInputGain = 0.0, gRightInputGain = 0.0, gMaxInputGain = 0.0;
  80. float    gLeftOutputGain = 0.0, gRightOutputGain = 0.0, gMaxOutputGain = 0.0;
  81.  
  82.                         /* OK, just a few more globals for
  83.                          * some state information.
  84.                          */
  85. MenuHandle    gAppleMenu, gFileMenu, gEditMenu, gPatchMenu;
  86. Boolean        gDone = false;
  87. WindowPtr    gCurrentWindow = 0;
  88.  
  89.  
  90.                         /* A simple structure to map a small
  91.                          * integer key into a new number and
  92.                          * a Pascal string for output.
  93.                          */
  94. struct tagStruct {
  95.     int        num;                /* Number with this key */
  96.     int        name;                /* Index in STR# Resource */
  97. };
  98.  
  99. struct tagStruct portTypes[] = {
  100.     { SMMonitorInput, kInputPortString},        /* kInputPort */
  101.     { SMMonitorOutput, kOutputPortString}        /* kOutputPort */
  102. };
  103.  
  104.                         /* Mapping between channel type
  105.                          * and the pretty name in the
  106.                          * meter window.
  107.                          */
  108. struct tagStruct channelTypes[] = {
  109.     { 0, kLeftChannelString},            /* kLeftChannel */
  110.     { 0, kRightChannelString},            /* kRightChannel */
  111.     { 0, kMaximumChannelString},            /* kMaxChannel */
  112.     { 0, 0}                        /* kBothChannel */
  113. };
  114.  
  115.                         /* Mapping between level number
  116.                          * and the pretty name in the
  117.                          * meter window.
  118.                          */
  119. struct tagStruct levelNames[] = {
  120.     { kPeakReading, kPeakReadingString},        /* kPeakReading */
  121.     { kPowerReading, kPowerReadingString}        /* kPowerReading */
  122. };
  123.  
  124.                         /* Mapping between level number
  125.                          * and the DSP module name defined
  126.                          * in the DSP assembly language.
  127.                          */
  128. struct tagStruct levelModules[] = {
  129.     { kPeakReading, kPeakModuleString},        /* kPeakReading */
  130.     { kPowerReading, kPowerModuleString}        /* kPowerReading */
  131. };
  132.  
  133. struct tagStruct referenceNames[] = {
  134.     { kFullScaleReference, kFullScaleString},
  135.     { kAnalogLineReference, kAnalogLineString}
  136. };
  137.  
  138. #define    DSPMonitorNum(port, level)    (port + (level * 2))
  139. #define    MAX_DSP_MONITORS        (DSPMonitorNum(kOutputPort, kPowerReading)+1)
  140.  
  141. static DSPMonitor aDSPMonitor[MAX_DSP_MONITORS];
  142.  
  143. int    useMouseForLevels = 0;
  144. float    gMouseLevel[2];
  145.  
  146. static DSPCPUDeviceParamBlkPtr    theDSP = 0;
  147.  
  148. #define    UNIMPL_TRAP_NUM        0x9F
  149. #define    BOM_TRAP_NUM        0xBF5
  150.  
  151. void CloseDSPs(){
  152.     int    i;
  153.     for (i=0;i<MAX_DSP_MONITORS; i++){
  154.         if (aDSPMonitor[i])
  155.             SMDestroyDSPMonitor(aDSPMonitor[i]);
  156.         aDSPMonitor[i] = 0;
  157.     }
  158.     
  159.     if (theDSP)
  160.         SMCloseDSP3210(theDSP);
  161.     theDSP = 0;
  162. }
  163.  
  164. static    SoundStream theSoundStream = 0;
  165.  
  166. float    *GetDSPVolumeAddress(enum portNum portType, enum channelNum channelType,
  167.                 enum levelNum levelType, enum speedNum speedType){
  168.     DSPMonitor    theMonitor;
  169.     Fixed    frameRate, sampleRate;
  170.     float    decayRate;
  171.         
  172.     if (useMouseForLevels)                /* Don't call DSP Manager */
  173.         return gMouseLevel;
  174.     
  175.     if (NGetTrapAddress(BOM_TRAP_NUM, ToolTrap) == NGetTrapAddress(UNIMPL_TRAP_NUM, ToolTrap)){
  176.         DoStopAlert(kConfigurationInstallError);
  177.         ExitToShell();
  178.     }
  179.     
  180.     if (theDSP == 0){
  181.         int    i;
  182.         
  183.         if (!theSoundStream)
  184.             theSoundStream = SMOpenDSPSoundStream();
  185.             
  186.         theDSP = SMOpenDSP3210(theSoundStream);
  187.         if (theDSP == 0){
  188.             DoStopAlert(kConfigurationStartError);
  189.             ExitToShell();
  190.         }
  191.         for (i=0;i<MAX_DSP_MONITORS; i++){
  192.             aDSPMonitor[i] = 0;
  193.         }
  194.     }
  195.     
  196.     theMonitor = aDSPMonitor[DSPMonitorNum(portType, levelType)];
  197.     if ( theMonitor == 0){
  198.         Str255    moduleName;
  199.         
  200.         GetIndString(moduleName, METER_LABEL_RESOURCE, levelModules[levelType].name);
  201.         theMonitor = aDSPMonitor[DSPMonitorNum(portType, levelType)] =
  202.                 SMCreateDSPMonitor(theDSP, theSoundStream,
  203.                         moduleName, portTypes[portType].num);
  204.         if (!theMonitor){
  205.             DoStopAlert(kCreateDSPMonitorError);
  206.             CloseDSPs();
  207.             ExitToShell();
  208.         }
  209.     }
  210.         
  211.     switch(channelType){
  212.     case kLeftChannel:
  213.         SMSetChannel(theMonitor, SMChannelAddClient, SMChannelDontChange);
  214.         break;
  215.     case kRightChannel:
  216.         SMSetChannel(theMonitor, SMChannelDontChange, SMChannelAddClient);
  217.         break;
  218.     case kBothChannel:
  219.     case kMaxChannel:
  220.         SMSetChannel(theMonitor, SMChannelAddClient, SMChannelAddClient);
  221.         break;
  222.     }
  223.     
  224.     StdSndGetSampleAndFrameRate(theSoundStream, &frameRate, &sampleRate);
  225.     decayRate=exp(-1/kMeterTimeConstant/(frameRate/65536.0));
  226.     SMMaxSetDecayRate(theMonitor, decayRate);
  227.     SMMaxSetScaleFactor(theMonitor, 1.0/(frameRate/65536.0));
  228.     
  229.     switch (speedType){
  230.     case kFastSpeed:
  231.         return SMGetFastLevelsPtr(theMonitor);
  232.     default:
  233.         return SMGetSlowLevelsPtr(theMonitor);
  234.     }
  235. }
  236.  
  237. void ReleaseDSPVolumeAddress(enum portNum portType, enum channelNum channelType,
  238.                 enum levelNum levelType){
  239.     DSPMonitor    theMonitor;
  240.     
  241.     if (useMouseForLevels)            /* Short Circuit this routine. */
  242.         return;
  243.  
  244.     theMonitor = aDSPMonitor[DSPMonitorNum(portType, levelType)];
  245.     if ( theMonitor == 0){
  246.         if (!theMonitor){
  247.             DoStopAlert(kReleaseDSPMonitorError);
  248.             CloseDSPs();
  249.             ExitToShell();
  250.         }
  251.     }
  252.     
  253.     switch(channelType){
  254.     case kLeftChannel:
  255.         SMSetChannel(theMonitor, SMChannelRemoveClient, SMChannelDontChange);
  256.         break;
  257.     case kRightChannel:
  258.         SMSetChannel(theMonitor, SMChannelDontChange, SMChannelRemoveClient);
  259.         break;
  260.     case kBothChannel:
  261.     case kMaxChannel:
  262.         SMSetChannel(theMonitor, SMChannelRemoveClient, SMChannelRemoveClient);
  263.         break;
  264.     }
  265. }
  266.  
  267. void GetDSPGains(){
  268.     unsigned long    left, right;
  269.     
  270.     StdSndGetInputGain(theSoundStream, &left, &right);
  271.     gLeftInputGain = -1.5*left;
  272.     gRightInputGain = -1.5*right;
  273.     gMaxInputGain = gLeftInputGain>gRightInputGain? gLeftInputGain : gRightInputGain;
  274.  
  275.     StdSndGetOutputAttenuation(theSoundStream, &left, &right);
  276.     gLeftOutputGain = -1.5*left;
  277.     gRightOutputGain = -1.5*right;
  278.     gMaxOutputGain = gLeftOutputGain>gRightOutputGain? gLeftOutputGain : gRightOutputGain;
  279. }
  280.  
  281. /****************************************** ScaleAngle *********************************/
  282. /*
  283.  * The following scale angles were computed using Mathematica.  Assuming that the
  284.  * scale is logarithmic and has a total range of SCALE_DEGREES.  The most positive
  285.  * excursion (SCALE_DEGREES/2) should occur at the maximum volume level (+5) and the
  286.  * most negative excursion (-SCALE_DEGREES/2) should occur at the minimum volume
  287.  * level.  We solve the following equation:
  288.  *        degrees = a log(dB + 65) + b
  289.  * at the following points
  290.  *        degrees         dB
  291.  *          DEG/2         +5
  292.  *         -DEG/2        -60
  293.  * The solution is
  294.  *    a = d/(x1 - x2)
  295.  *    b = -d/2 (x1+x2)/(x1-x2)
  296.  * where d = SCALE_DEGREES, x1 = log(10 - 5), and x2 = log(10 - -60).
  297.  */
  298. float    ScaleAngle(dB)
  299. float    dB;
  300. {
  301.     register float    a, b, x1, x2;
  302.     const float    mindB = -60, maxdB = 5, offsetdB = 10, d = SCALE_DEGREES;
  303.     
  304.     if (dB < mindB)
  305.         return -d/2;
  306.     else if (dB > maxdB)
  307.         return d/2;
  308.     x1 = log(offsetdB-maxdB);
  309.     x2 = log(offsetdB-mindB);
  310.     a = d/(x1 - x2);
  311.     b = -d/2*(x1 + x2)/(x1 - x2);
  312.     return a * log(offsetdB-dB) + b;
  313. }    
  314.  
  315. /****************************************** ComputeTick *********************************/
  316.  
  317. Rect ComputeTick(vu, center, length, start, stop)
  318. float    vu;
  319. Point    center;
  320. int    length;
  321. float    start, stop;
  322. {
  323.     Rect    r;
  324.     float    angle = ScaleAngle(vu)*3.1415926535/180.0;
  325.     float    cosAngle = cos(angle);
  326.     float    sinAngle = sin(angle);
  327.     
  328.     r.top = center.v - cosAngle*length*stop;
  329.     r.bottom = center.v - cosAngle*length*start;
  330.     r.right = center.h + sinAngle*length*start;
  331.     r.left = center.h + sinAngle*length*stop;
  332.  
  333.     return r;
  334. }
  335.  
  336. /*=================================== CWindowPiece ====================================*/
  337.  
  338. void CWindowPiece::IWindowPiece(){
  339.     fOffScreenWorld = 0;
  340.     SetRect(&this->fWindowRect, 0, 0, 0, 0);
  341.     fWindow = 0;
  342.     fDirtyScreenRegion = NewRgn();
  343.     if (!fDirtyScreenRegion){
  344.         DoStopAlert(kScreenRegionAllocationError);
  345.         CloseDSPs();
  346.         ExitToShell();
  347.     }
  348. }
  349.  
  350. void CWindowPiece::SetWindow(WindowPtr win){
  351.     fWindow = win;
  352. }
  353.  
  354. void CWindowPiece::SetWindowRect(Rect *screenRect, Rect *windowRect){
  355.     QDErr        errorCode;
  356.     GWorldPtr    newWorld = 0;
  357.     
  358.     this->fWindowRect = *windowRect;
  359.     
  360.     if (fOffScreenWorld){            /* Resize Existing GWorld */
  361.         newWorld = fOffScreenWorld;
  362.         errorCode = UpdateGWorld(&newWorld, DEFAULT_SCREEN_DEPTH, screenRect, 
  363.                     NIL_POINTER, NIL_POINTER, NO_FLAGS);
  364.     } else {                /* Allocate a new one */
  365.         
  366.         errorCode = NewGWorld(&newWorld, DEFAULT_SCREEN_DEPTH, screenRect, NIL_POINTER,
  367.                     NIL_POINTER, NO_FLAGS);
  368.     }
  369.     fOffScreenWorld = newWorld;
  370.     
  371.     if (errorCode == memFullErr || !IsMemoryAvailable(0)){
  372.         DoStopAlert(kNoMemoryWindowPieceError);
  373.         CloseDSPs();
  374.         ExitToShell();
  375.     }
  376.             
  377.     if (errorCode != noErr){
  378.         DoStopAlert(kNoOffscreenWorldError);
  379.         CloseDSPs();
  380.         ExitToShell();
  381.     }
  382.     this->Update();
  383. }
  384.  
  385. void CWindowPiece::Erase(){
  386.     GDHandle    oldGD;
  387.     GWorldPtr    oldGW;
  388.     
  389.     GetGWorld(&oldGW, &oldGD);
  390.     
  391.     LockPixels(fOffScreenWorld->portPixMap);
  392.     SetGWorld(fOffScreenWorld, NIL_POINTER);
  393.     RGBForeColor(&rgbBackgroundColor);
  394.     EraseRect(&fWindowRect);    
  395.     SetGWorld(oldGW, oldGD);
  396.     UnlockPixels(fOffScreenWorld->portPixMap);
  397. }
  398.  
  399. void CWindowPiece::CopyToWindow(){
  400.     GDHandle    oldGD;
  401.     GWorldPtr    oldGW;
  402.     
  403.     if (!fWindow){
  404.         DoStopAlert(kNoCopyToWindowError);
  405.         CloseDSPs();
  406.         ExitToShell();
  407.     }
  408.     
  409.     GetGWorld(&oldGW, &oldGD);
  410.     
  411.     LockPixels(fOffScreenWorld->portPixMap);
  412.     SetPort(fWindow);
  413.  
  414.     OffsetRgn(fDirtyScreenRegion, fWindowRect.left, fWindowRect.top);
  415.     RGBBackColor(&rgbWhiteColor);
  416.     RGBForeColor(&rgbBlackColor);
  417.     CopyBits((BitMap *)&fOffScreenWorld->portPixMap, &fWindow->portBits, 
  418.         &fOffScreenWorld->portRect, &fWindowRect, srcCopy, fDirtyScreenRegion);
  419.     OffsetRgn(fDirtyScreenRegion, -fWindowRect.left, -fWindowRect.top);
  420.  
  421.     SetGWorld(oldGW, oldGD);
  422.     UnlockPixels(fOffScreenWorld->portPixMap);
  423. }
  424.  
  425. void CWindowPiece::Dispose(){
  426.     if (fOffScreenWorld)
  427.         DisposeGWorld(fOffScreenWorld);
  428.     fOffScreenWorld = 0;
  429.     if (fDirtyScreenRegion)
  430.         DisposeRgn(fDirtyScreenRegion);
  431.     fDirtyScreenRegion = 0;
  432. /*                        Don't do this here... the CPortWindow
  433.  *                        gets to dispose of this... don't want
  434.  *                        to do this more than once!
  435.  *    if (fWindow)
  436.  *        DisposeWindow(fWindow);
  437.  */
  438.      fWindow = 0;
  439. }
  440.  
  441. void CWindowPiece::Update(){
  442.     RectRgn(fDirtyScreenRegion, &fWindowRect);
  443.     OffsetRgn(fDirtyScreenRegion, -fWindowRect.left, -fWindowRect.top);
  444. }
  445.  
  446. /*=================================== CVUMeter ====================================*/
  447.  
  448. void CVUMeter::IVUMeter(enum portNum portType, enum channelNum channelIndicator, 
  449.             enum levelNum levelType, enum speedNum speedType,
  450.             enum referenceNum referenceType){
  451.     this->IWindowPiece();
  452.     fLastReading = -1000;            /* Arbitrary and unlikely */
  453.     fScaleWorld = 0;
  454.     SetPt(&fMeterCenter, 0, 0);
  455.     fScaleLength = 0;
  456.     fOverload = 0;
  457.     fPortType = portType;
  458.     fChannelIndicator = channelIndicator;
  459.     fLevelType = levelType;
  460.     fSpeedType = speedType;
  461.     fReferenceType = referenceType;
  462.     
  463.     fDirtyScaleRegion = NewRgn();
  464.     if (!fDirtyScaleRegion){
  465.         DoStopAlert(kNoScaleRegionError);
  466.         CloseDSPs();
  467.         ExitToShell();
  468.     }
  469.     
  470.     fCurrentReading = GetDSPVolumeAddress(fPortType, fChannelIndicator, fLevelType, fSpeedType);
  471. }
  472.  
  473. void CVUMeter::GetExternalGain(){
  474.     static float zeroGain = 0.0;
  475.     
  476.     if (fReferenceType == kAnalogLineReference){
  477.         if (fPortType == kInputPort){
  478.             switch (fChannelIndicator){
  479.             case kLeftChannel:
  480.                 fChannelGainPointer = &gLeftInputGain;
  481.                 break;
  482.             case kRightChannel:
  483.                 fChannelGainPointer = &gRightInputGain;
  484.                 break;
  485.             case kMaxChannel:
  486.                 fChannelGainPointer = &gMaxInputGain;
  487.                 break;
  488.             default:
  489.                 fChannelGainPointer = &zeroGain;
  490.             }
  491.         } else {
  492.             switch (fChannelIndicator){
  493.             case kLeftChannel:
  494.                 fChannelGainPointer = &gLeftOutputGain;
  495.                 break;
  496.             case kRightChannel:
  497.                 fChannelGainPointer = &gRightOutputGain;
  498.                 break;
  499.             case kMaxChannel:
  500.                 fChannelGainPointer = &gMaxOutputGain;
  501.                 break;
  502.             default:
  503.                 fChannelGainPointer = &zeroGain;
  504.             }
  505.         }
  506.     } else{
  507.         fChannelGainPointer = &zeroGain;
  508.     }
  509. }
  510.  
  511. struct tagStruct tickList[] = {
  512.     {-60, kMinusSixtyString},
  513.     {-50, 0},
  514.     {-40, kMinusFourtyString},
  515.     {-30, kMinusThirtyString},
  516.     {-25, 0},
  517.     {-20, kMinusTwentyString},
  518.     {-15, 0},
  519.     {-10, kMinusTenString},
  520.     {-5, kMinusFiveString},
  521.     {-4, 0},
  522.     {-3, 0},
  523.     {-2, 0},
  524.     {-1, 0},
  525.     {0, kZeroLevelString},
  526.     {1, 0},
  527.     {2, 0},
  528.     {3, 0},
  529.     {4, 0},
  530.     {5, kPlusFiveString}};
  531.  
  532. void CVUMeter::DrawScale(){
  533.     int        i, len;
  534.     const int    kCenterRadius = 5;
  535.     Rect        rect;
  536.     GDHandle    oldGD;
  537.     GWorldPtr    oldGW;
  538.     Str255        tickString, channelString, levelString, referenceString, minusString;
  539.     
  540.     if (!fScaleWorld)
  541.         return;                    /* Should never see this condition...*/
  542.  
  543.     GetGWorld(&oldGW, &oldGD);
  544.     LockPixels(fScaleWorld->portPixMap);
  545.     SetGWorld(fScaleWorld, NIL_POINTER);
  546.         
  547.     RGBBackColor(&rgbBackgroundColor);        /* Sort of dirty yellow */
  548.     rect = fScaleWorld->portRect;
  549.     EraseRect(&rect);
  550.     
  551.     RGBForeColor(&rgbOverloadColor);        /* Draw Overload Bar */
  552.     SetRect(&rect, fMeterCenter.h - fScaleLength, fMeterCenter.v - fScaleLength,
  553.             fMeterCenter.h + fScaleLength, fMeterCenter.v + fScaleLength);
  554.     i = fScaleLength * (kTickEnd - kTickStart) + 0.5;
  555.     PenSize(i, i);
  556.     FrameArc(&rect, (short)(ScaleAngle(0.0)), 
  557.             (short)(ScaleAngle(5.0)-ScaleAngle(0.0)+0.5));
  558.     
  559.     RGBForeColor(&rgbTickColor);            /* Draw the ticks and legend */
  560.     TextSize(SCALE_FONT_SIZE);
  561.     TextFont(SCALE_FONT_NUMBER);
  562.     PenSize(1,1);
  563.     GetIndString(minusString, TICK_LABEL_RESOURCE, kMinusSignString);
  564.  
  565.     for (i=0;i<sizeof(tickList)/sizeof(struct tagStruct);i++){
  566.         GetIndString(tickString, TICK_LABEL_RESOURCE, tickList[i].name);
  567.  
  568.         if (tickString[0] != 0)
  569.             rect = ComputeTick(tickList[i].num, fMeterCenter, fScaleLength, 
  570.                         kTickStart, kTickEnd);
  571.         else
  572.             rect = ComputeTick(tickList[i].num, fMeterCenter, fScaleLength, 
  573.                         kTickStart, kTickMiddle);
  574.         MoveTo(rect.right, rect.bottom);
  575.         LineTo(rect.left, rect.top);
  576.         if (tickString[0] != 0){
  577.             len = StringWidth(tickString);
  578.                             /* Want to center without worrying
  579.                              * about the initial '-'.  Add in
  580.                              * the width again so it won't
  581.                              * affect the centering calucation
  582.                              */
  583.             if (minusString[0] && minusString[1] == tickString[1])
  584.                 len += StringWidth(minusString);
  585.                 
  586.             Move(-len/2, -3);        /* Center String */
  587.             DrawString(tickString);
  588.         }
  589.     }
  590.  
  591.                             /* Draw Needle Center */
  592.     SetRect(&rect, -kCenterRadius, -kCenterRadius, kCenterRadius, kCenterRadius);
  593.     OffsetRect(&rect, fMeterCenter.h, fMeterCenter.v);
  594.     FrameOval(&rect);
  595.  
  596.     GetIndString(channelString, METER_LABEL_RESOURCE, channelTypes[fChannelIndicator].name);
  597.     GetIndString(levelString, METER_LABEL_RESOURCE, levelNames[fLevelType].name);
  598.     GetIndString(referenceString, METER_LABEL_RESOURCE, referenceNames[fReferenceType].name);
  599.  
  600.     MoveTo(5, SCALE_FONT_SIZE+2);            /* Draw Channel Label */
  601.     DrawString(channelString);
  602.     
  603.     if (levelNames[fLevelType].name){        /* Draw Meter Type */
  604.         len = StringWidth(levelString);
  605.         len += StringWidth(referenceString);
  606.         
  607.         i = fMeterCenter.v + kCenterRadius;
  608.         rect = (**fScaleWorld->portPixMap).bounds;
  609.         if (i > rect.bottom)
  610.             i = rect.bottom;
  611.         i -= (SCALE_FONT_SIZE*3)/2;
  612.         MoveTo(fMeterCenter.h - len/2, i);
  613.         DrawString(levelString);
  614.         DrawString(referenceString);
  615.     }
  616.     
  617.     if (0) {                    /* Add Screen Depth for Debugging */
  618.         Str255    screenDepthString;
  619.         NumToString(-(**((*fOffScreenWorld).portPixMap)).pixelSize, screenDepthString);
  620.         DrawString(screenDepthString);
  621.     }
  622.  
  623.     SetGWorld(oldGW, oldGD);
  624.     UnlockPixels(fScaleWorld->portPixMap);
  625. }
  626.  
  627. void CVUMeter::SetMeterParms(Rect *sizeRect){
  628.     fScaleLength = (sizeRect->right - 3*SCALE_FONT_SIZE)/2;
  629.     SetPt(&fMeterCenter, sizeRect->right/2, fScaleLength+2*SCALE_FONT_SIZE);
  630. }    
  631.  
  632. /*
  633.  *    CVUMeter::SetWindowRect - Set the size of a VUMeter. First update the size of
  634.  *    the private GWorld I use for the scale, then let the inherited method take care
  635.  *    of the offscreen GWorld.
  636.  */
  637. void CVUMeter::SetWindowRect(Rect *screenRect, Rect *windowRect){
  638.     Rect        sizeRect;
  639.     QDErr        errorCode;
  640.     GWorldPtr    newWorld;
  641.     
  642.     sizeRect = *windowRect;
  643.     OffsetRect(&sizeRect, -sizeRect.left, -sizeRect.top);
  644.  
  645.     this->SetMeterParms(&sizeRect);
  646.     
  647.     if (fScaleWorld){
  648.         newWorld = fScaleWorld;
  649.         errorCode = UpdateGWorld(&newWorld, DEFAULT_SCREEN_DEPTH, screenRect, 
  650.                     NIL_POINTER, NIL_POINTER, NO_FLAGS);
  651.     } else {
  652.         
  653.         errorCode = NewGWorld(&newWorld, DEFAULT_SCREEN_DEPTH, screenRect, NIL_POINTER,
  654.                     NIL_POINTER, NO_FLAGS);
  655.     }
  656.     fScaleWorld = newWorld;
  657.     
  658.     if (errorCode != noErr){
  659.         DoCautionAlert(kCantCreateScaleWorldError);
  660.         this->Dispose();
  661.         return;
  662.     }
  663.     RectRgn(fDirtyScaleRegion, &sizeRect);
  664.  
  665.     inherited::SetWindowRect(screenRect, windowRect);
  666.     
  667.     if (!fOffScreenWorld)
  668.         return;
  669.         
  670.     SetRect(&fOverloadRect, 
  671.         fOffScreenWorld->portRect.right - SCALE_OVERLOAD_DELTA - SCALE_OVERLOAD_SIZE,
  672.         fOffScreenWorld->portRect.top + SCALE_OVERLOAD_DELTA - SCALE_OVERLOAD_SIZE,
  673.         fOffScreenWorld->portRect.right - SCALE_OVERLOAD_DELTA + SCALE_OVERLOAD_SIZE,
  674.         fOffScreenWorld->portRect.top + SCALE_OVERLOAD_DELTA + SCALE_OVERLOAD_SIZE);
  675.         
  676.     this->Update();
  677.     this->DrawScale();
  678. }    
  679.  
  680. /*
  681.  * This routine is responsible for combining the meter scale with the new needle's 
  682.  * position and dumping the new face into an offscreen bitmap (which is called 
  683.  * fOffscreenWorld). 
  684.  *
  685.  * Remember, that we've already cached an offscreen bitmap with the meter's face.
  686.  * This routine first copies in the background and the screen, then overlays the 
  687.  * meter's needle and the overload light.  Finally, all the parts that have changed
  688.  * are stored in the fDirtyScreenRegion so we know what needs updating.
  689.  */
  690. void CVUMeter::DrawNeedle(){
  691.     short            currentOverload;
  692.     GDHandle        oldGD;
  693.     GWorldPtr        oldGW;
  694.     Rect            rect;
  695.     static RgnHandle    overloadRegion = 0;    
  696.             
  697.     if (overloadRegion == 0){
  698.         overloadRegion = NewRgn();
  699.     }
  700.         
  701.     
  702.     GetGWorld(&oldGW, &oldGD);
  703.     LockPixels(fOffScreenWorld->portPixMap);
  704.     LockPixels(fScaleWorld->portPixMap);
  705.     SetGWorld(fOffScreenWorld, NIL_POINTER);
  706.  
  707.     RGBBackColor(&rgbWhiteColor);            /* Copy in scale and bgnd. */
  708.     RGBForeColor(&rgbBlackColor);
  709.     CopyBits((BitMap *)&fScaleWorld->portPixMap, (BitMap *)&fOffScreenWorld->portPixMap, 
  710.         &fScaleWorld->portRect, &fOffScreenWorld->portRect, srcCopy, fDirtyScaleRegion);
  711.  
  712.     RGBForeColor(&rgbBlackColor);            /* Be sure we are drawing in black */
  713.     PenSize(2,2);                    /* Now draw the needle */
  714.     rect = ComputeTick(fLastReading, fMeterCenter, fScaleLength, 0.0, kTickStart);
  715.     RGBForeColor(&rgbTickColor);
  716.     MoveTo(rect.right, rect.bottom);
  717.     LineTo(rect.left, rect.top);
  718.     
  719.     if (rect.left > rect.right){
  720.         int    t;
  721.         t = rect.left;
  722.         rect.left = rect.right;
  723.         rect.right = t;
  724.     }
  725.                             /* OK, we need to take the part of the
  726.                              * scale region that is dirty (where we
  727.                              * just drew the needle into) and union
  728.                              * it with the region that has the old
  729.                              * needle in it on the screen.  Be sure 
  730.                              * to add some extra space to it to make
  731.                              * sure we get the full width of the line.
  732.                              */
  733.     InsetRect(&rect, -2, -2);
  734.     RectRgn(fDirtyScreenRegion, &rect);
  735.     UnionRgn(fDirtyScreenRegion, fDirtyScaleRegion, fDirtyScreenRegion);
  736.     RectRgn(fDirtyScaleRegion, &rect);
  737.  
  738.     currentOverload = 0;
  739.     if (fLastReading >= 0){                /* Light the overload indicator */
  740.         RGBForeColor(&rgbOverloadColor);
  741.         FillOval(&fOverloadRect, black);
  742.         currentOverload = 1;
  743.     }
  744.     if (currentOverload != fOverload){
  745.         RectRgn(overloadRegion, &fOverloadRect);
  746.         UnionRgn(fDirtyScaleRegion, overloadRegion, fDirtyScaleRegion);
  747.         UnionRgn(fDirtyScreenRegion, overloadRegion, fDirtyScreenRegion);
  748.     }
  749.     fOverload = currentOverload;
  750.     
  751.     SetGWorld(oldGW, oldGD);
  752.     UnlockPixels(fScaleWorld->portPixMap);
  753.     UnlockPixels(fOffScreenWorld->portPixMap);
  754. }
  755.  
  756. #define    kNormalInputGain    -11.21334612         /* 20*log10(1.1/4) */
  757. #define    kNormalOutputGain    2.278867046        /* 20*log10(1.3/1) */
  758. #define    kNormalPeakPowerGain    10            /* Normal Gain due to Peakiness */
  759.  
  760. void CVUMeter::UpdateLevel(){
  761.     float    dB, level, left, right;
  762.     
  763.     switch (fChannelIndicator){
  764.     case kRightChannel:
  765.     case kLeftChannel:
  766.         level = fCurrentReading[fChannelIndicator];
  767.         break;
  768.     default:                    /* Maximum of two channels */
  769.         right = fCurrentReading[kRightChannel];
  770.         left = fCurrentReading[kLeftChannel];
  771.         if (left > right)
  772.             level = left;
  773.         else
  774.             level = right;
  775.         break;
  776.     }
  777.     
  778.     if (!useMouseForLevels){
  779.         if (fLevelType == kPeakReading)
  780.             dB = 20.*log10(level/DSP_MAXIMUM_VALUE);
  781.         else {
  782.             dB = 10.*log10(level/DSP_MAXIMUM_VALUE/DSP_MAXIMUM_VALUE);
  783.             dB += kNormalPeakPowerGain;
  784.         }
  785.         
  786.         if (fReferenceType == kAnalogLineReference){
  787.             if (fPortType == kInputPort)
  788.                 dB -= kNormalInputGain;
  789.             else
  790.                 dB -= kNormalOutputGain;
  791.         }
  792.         
  793.         dB += *fChannelGainPointer;    /* Add in optional Singer gain */
  794.     } else {
  795.         dB = level;
  796.     }
  797.     
  798.     if (dB != fLastReading){
  799.         fLastReading = dB;
  800.         this->DrawNeedle();
  801.         this->CopyToWindow();
  802.     }
  803. }
  804.  
  805. void CVUMeter::RefreshLevel(){
  806.     this->DrawNeedle();
  807.     this->CopyToWindow();
  808. }
  809.  
  810.  
  811. void CVUMeter::Dispose(){
  812.     ReleaseDSPVolumeAddress(fPortType, fChannelIndicator, fLevelType);
  813.     
  814.     if (fScaleWorld)
  815.         DisposeGWorld(fScaleWorld);
  816.     fScaleWorld = 0;
  817.     
  818.     if (fDirtyScaleRegion)
  819.         DisposeRgn(fDirtyScaleRegion);
  820.     fDirtyScaleRegion = 0;
  821.  
  822.     inherited::Dispose();
  823. }
  824.  
  825. int CVUMeter::AllMemoryAllocated(){
  826.     return fOffScreenWorld && fScaleWorld;
  827. }
  828.  
  829. /*=================================== CPictureMeter ====================================*/
  830.  
  831. void CPictureMeter::IVUMeter(enum portNum portType, enum channelNum channelType, 
  832.                     enum levelNum levelType, enum speedNum speedType,
  833.                     enum referenceNum referenceType){
  834.     PicHandle    thePicture;
  835.     
  836.     CVUMeter::IVUMeter(portType, channelType, levelType, speedType,
  837.                     referenceType);
  838.     thePicture = GetPicture(ABOUT_BOX_PICT_ID);
  839.     fPicture = thePicture;
  840. }
  841.  
  842. void CPictureMeter::DrawScale(){
  843.     GDHandle    oldGD;
  844.     GWorldPtr    oldGW;
  845.     GWorldPtr    newWorld = 0;
  846.     Rect    rect;
  847.     short    wide, high;
  848.     OSErr    errorCode;
  849.  
  850.     if (!fPicture || !fScaleWorld)
  851.         return;
  852.         
  853.     if (!fScaleWorld)
  854.         return;                    /* Should never see this condition...*/
  855.  
  856.     GetGWorld(&oldGW, &oldGD);
  857.     LockPixels(fScaleWorld->portPixMap);
  858.     rect = (*fPicture)->picFrame;
  859.     wide = rect.right - rect.left;            /* calculate width and height */
  860.     high = rect.bottom - rect.top;
  861.     SetRect(&rect, 0, 0, wide,high );
  862.  
  863.     errorCode = NewGWorld(&newWorld, 1, &rect, NIL_POINTER, NIL_POINTER, NO_FLAGS);
  864.  
  865.     if (errorCode == noErr){
  866.         LockPixels(newWorld->portPixMap);
  867.         SetGWorld(newWorld, NIL_POINTER);
  868.         EraseRect(&newWorld->portRect);
  869.         DrawPicture(fPicture, &rect);
  870.         
  871.         SetGWorld(fScaleWorld, NIL_POINTER);
  872.         RGBBackColor(&rgbBackgroundColor);    /* Sort of dirty yellow */
  873.         EraseRect(&newWorld->portRect);
  874.  
  875.         CopyBits((BitMap *)&newWorld->portPixMap, 
  876.              (BitMap *)&fScaleWorld->portPixMap, &rect, &rect, srcOr, 0);
  877.                 
  878.         DisposeGWorld(newWorld);
  879.     } else {
  880.         SetGWorld(fScaleWorld, NIL_POINTER);
  881.  
  882.         RGBBackColor(&rgbBackgroundColor);    /* Sort of dirty yellow */
  883.         rect = newWorld->portRect;
  884.         EraseRect(&rect);
  885.         DrawPicture(fPicture,  &rect );
  886.     }
  887.     SetGWorld(oldGW, oldGD);
  888.     UnlockPixels(fScaleWorld->portPixMap);
  889. }
  890.  
  891. void CPictureMeter::SetMeterParms(Rect *sizeRect){
  892.     Str255    theText;
  893.     long    x, y, len;
  894.     
  895.     GetIndString(theText, ABOUT_BOX_PICT_ID, 1);
  896.     StringToNum(theText, &x);
  897.     GetIndString(theText, ABOUT_BOX_PICT_ID, 2);
  898.     StringToNum(theText, &y);
  899.     GetIndString(theText, ABOUT_BOX_PICT_ID, 3);
  900.     StringToNum(theText, &len);
  901.     
  902.     fScaleLength = len;
  903.     SetPt(&fMeterCenter, x, y);
  904. }    
  905.  
  906. /*=================================== CPortWindow ====================================*/
  907.  
  908. void CPortWindow::IPortWindow(){
  909.     fChannel = gPatchChannels;
  910.     fFirstMeter = fSecondMeter = 0;
  911.     fPortType = gPortType;
  912.     fLevelType = gLevelType;
  913.     fSpeedType = gSpeedType;
  914.     fReferenceType = gReferenceType;
  915.     fWindow = 0;
  916. }
  917.  
  918. void CPortWindow::NewWindow(){
  919.     WindowPtr    cWindow;
  920.     Str255        windowTitle;
  921.     
  922.     if (fChannel == kBothChannel)
  923.         cWindow = GetNewCWindow(BASE_RES_ID+1, NIL_POINTER, (WindowPtr)MOVE_TO_FRONT);
  924.     else
  925.         cWindow = GetNewCWindow(BASE_RES_ID, NIL_POINTER, (WindowPtr)MOVE_TO_FRONT);
  926.  
  927.     if (!cWindow){
  928.         DoCautionAlert(kCantAllocateCWindowError);
  929.         this->Dispose();
  930.         return;
  931.     }
  932.     
  933.     if (!IsMemoryAvailable(MemoryNeededForNewWindow(&cWindow->portRect))){
  934.         DoCautionAlert(kCantAllocateCWindowError);
  935.         this->Dispose();
  936.         DisposeWindow(cWindow);
  937.         return;
  938.     }
  939.  
  940.     SetPort(cWindow);
  941.     DrawControls(cWindow);
  942. #ifdef    SCROLL_BARS
  943.     DrawGrowIcon(cWindow);
  944. #endif
  945.     fWindow = cWindow;
  946.     GetIndString(windowTitle, METER_LABEL_RESOURCE, portTypes[fPortType].name); 
  947.     if (windowTitle[0])
  948.         SetWTitle(cWindow, windowTitle);
  949.     ShowWindow(cWindow);
  950.     this->UpdateWindowSize();
  951.  
  952.     if (fFirstMeter)
  953.         fFirstMeter->GetExternalGain();
  954.     if (fSecondMeter)
  955.         fSecondMeter->GetExternalGain();
  956. }
  957.  
  958. void CPortWindow::UpdateWindowType(){
  959.     int    destroyVUs = 0;
  960.     
  961.     if (fChannel != gPatchChannels){
  962.         if (gPatchChannels == kBothChannel)
  963.             SizeWindow(fWindow, 2*kDefaultVUWidth, kDefaultVUHeight, FALSE);
  964.         else 
  965.             SizeWindow(fWindow, kDefaultVUWidth, kDefaultVUHeight, FALSE);
  966.         fChannel = gPatchChannels;
  967.         destroyVUs = 1;
  968.     }
  969.     
  970.     if (fLevelType != gLevelType){
  971.         destroyVUs = 1;
  972.         fLevelType = gLevelType;
  973.     }
  974.     
  975.     if (fPortType != gPortType){
  976.         destroyVUs = 1;
  977.         fPortType = gPortType;
  978.     }
  979.     
  980.     if (fSpeedType != gSpeedType){
  981.         destroyVUs = 1;
  982.         fSpeedType = gSpeedType;
  983.     }
  984.     
  985.     if (fReferenceType != gReferenceType){
  986.         destroyVUs = 1;
  987.         fReferenceType = gReferenceType;
  988.     }
  989.     
  990.     if (destroyVUs != 0){
  991.         Str255    windowTitle;
  992.         
  993.         if (fFirstMeter){
  994.             fFirstMeter->Dispose();
  995.             delete(fFirstMeter);
  996.         }
  997.         if (fSecondMeter){
  998.             fSecondMeter->Dispose();
  999.             delete(fSecondMeter);
  1000.         }
  1001.         fFirstMeter = fSecondMeter = 0;
  1002.     
  1003.         SetPort(fWindow);
  1004.         DrawControls(fWindow);
  1005. #ifdef    SCROLL_BARS
  1006.         DrawGrowIcon(fWindow);
  1007. #endif
  1008.         this->UpdateWindowSize();
  1009.         GetIndString(windowTitle, METER_LABEL_RESOURCE, portTypes[fPortType].name);
  1010.         if (windowTitle)
  1011.             SetWTitle(fWindow, windowTitle);
  1012.         if (fFirstMeter)
  1013.             fFirstMeter->GetExternalGain();
  1014.         if (fSecondMeter)
  1015.             fSecondMeter->GetExternalGain();
  1016.     }
  1017. }
  1018.  
  1019. void CPortWindow::UpdateWindowSize(){
  1020.     Rect        screenRect, windowRect;
  1021.     CWindowPeek    myWinPeek;
  1022.     
  1023.     myWinPeek = (CWindowPeek)fWindow;
  1024.     screenRect = (*myWinPeek->contRgn)->rgnBBox;
  1025.  
  1026. #ifdef    SCROLL_BARS    
  1027.     screenRect.right -= SCROLL_BAR_SIZE;        /* Actually Grow Bar Control */
  1028.     screenRect.bottom -= SCROLL_BAR_SIZE;
  1029. #endif
  1030.     windowRect = screenRect;
  1031.     OffsetRect(&windowRect, -windowRect.left, -windowRect.top);
  1032.     
  1033.     if (fChannel != kBothChannel){            /* Allocate a single VU Meter */
  1034.         if (!fFirstMeter){
  1035.             fFirstMeter = new(CVUMeter);
  1036.             if (!fFirstMeter){
  1037.                 DoStopAlert(kCantAllocateVUMeterError);
  1038.                 CloseDSPs();
  1039.                 ExitToShell();
  1040.             }
  1041.             fFirstMeter->IVUMeter(fPortType, fChannel, fLevelType, 
  1042.                         fSpeedType, fReferenceType);
  1043.         }
  1044.         fFirstMeter->SetWindowRect(&screenRect, &windowRect);
  1045.         fFirstMeter->SetWindow(fWindow);
  1046.         if (!fFirstMeter->AllMemoryAllocated()){
  1047.             this->Dispose();
  1048.             return;
  1049.         }
  1050.     } else {
  1051.         int    width = screenRect.right - screenRect.left, t;
  1052.         
  1053.                             /* Allocate left VU Meter */
  1054.         screenRect.right = screenRect.left + width/2;
  1055.         windowRect.right = width/2;
  1056.         
  1057.         if (!fFirstMeter){
  1058.             fFirstMeter = new(CVUMeter);
  1059.             if (!fFirstMeter){
  1060.                 DoStopAlert(kCantAllocateVUMeterError);
  1061.                 CloseDSPs();
  1062.                 ExitToShell();
  1063.             }
  1064.             fFirstMeter->IVUMeter(fPortType, kLeftChannel, fLevelType, 
  1065.                         fSpeedType, fReferenceType);
  1066.         }
  1067.         fFirstMeter->SetWindowRect(&screenRect, &windowRect);
  1068.         fFirstMeter->SetWindow(fWindow);
  1069.  
  1070.                              /* Allocate right VU Meter */
  1071.         windowRect.left = width/2;
  1072.         windowRect.right = width;
  1073.         screenRect.right = screenRect.left + width;
  1074.         screenRect.left = screenRect.left + width/2;
  1075.         if (!fSecondMeter){
  1076.             fSecondMeter = new(CVUMeter);
  1077.             if (!fSecondMeter){
  1078.                 DoStopAlert(kCantAllocateVUMeterError);
  1079.                 CloseDSPs();
  1080.                 ExitToShell();
  1081.             }
  1082.             fSecondMeter->IVUMeter(fPortType, kRightChannel, fLevelType, 
  1083.                 fSpeedType, fReferenceType);
  1084.         }
  1085.         fSecondMeter->SetWindowRect(&screenRect, &windowRect);
  1086.         fSecondMeter->SetWindow(fWindow);
  1087.         if (!fSecondMeter->AllMemoryAllocated()){
  1088.             this->Dispose();
  1089.             return;
  1090.         }
  1091.     }
  1092.     this->RefreshWindow();
  1093. }
  1094.  
  1095. void CPortWindow::UpdateLevel(){
  1096.     if (fFirstMeter)
  1097.         fFirstMeter->UpdateLevel();
  1098.     
  1099.     if (fSecondMeter)
  1100.         fSecondMeter->UpdateLevel();
  1101. }
  1102.  
  1103. void CPortWindow::RefreshWindow(){
  1104.     if (fFirstMeter)
  1105.         fFirstMeter->RefreshLevel();
  1106.     
  1107.     if (fSecondMeter)
  1108.         fSecondMeter->RefreshLevel();
  1109. }
  1110.  
  1111. WindowPtr CPortWindow::GetWindow(){ 
  1112.     return fWindow;
  1113. }
  1114.  
  1115. void CPortWindow::RedrawWindow(){
  1116. #ifdef    SCROLL_BARS
  1117.     DrawGrowIcon(fWindow);
  1118. #endif
  1119.     if (fFirstMeter)
  1120.         fFirstMeter->CopyToWindow();
  1121.     if (fSecondMeter)
  1122.         fSecondMeter->CopyToWindow();
  1123. }
  1124.  
  1125. void CPortWindow::SetUpMenus(){
  1126.     CheckItem(gPatchMenu, LEFT_ITEM, fChannel == kLeftChannel);
  1127.     CheckItem(gPatchMenu, RIGHT_ITEM, fChannel == kRightChannel);
  1128.     CheckItem(gPatchMenu, BOTH_ITEM, fChannel == kBothChannel);
  1129.     CheckItem(gPatchMenu, MAXIMUM_ITEM, fChannel == kMaxChannel);
  1130.  
  1131.     CheckItem(gPatchMenu, INPUT_ITEM, fPortType == kInputPort);
  1132.     CheckItem(gPatchMenu, OUTPUT_ITEM, fPortType == kOutputPort);
  1133.  
  1134.     CheckItem(gPatchMenu, POWER_ITEM, fLevelType == kPowerReading);
  1135.     CheckItem(gPatchMenu, PEAK_ITEM, fLevelType == kPeakReading);
  1136.  
  1137.     CheckItem(gPatchMenu, FAST_ITEM, fSpeedType == kFastSpeed);
  1138.     CheckItem(gPatchMenu, NORMAL_ITEM, fSpeedType == kNormalSpeed);
  1139.     
  1140.     CheckItem(gPatchMenu, FULLSCALE_ITEM, fReferenceType == kFullScaleReference);
  1141.     CheckItem(gPatchMenu, ANALOGLINE_ITEM, fReferenceType == kAnalogLineReference);
  1142.     
  1143.     if (gCurrentWindow)
  1144.         EnableItem(gFileMenu, CLOSE_ITEM);
  1145.     else
  1146.         DisableItem(gFileMenu, CLOSE_ITEM);
  1147. }
  1148.     
  1149. void CPortWindow::Update(){
  1150.     if (fFirstMeter)
  1151.         fFirstMeter->Update();
  1152.     if (fSecondMeter)
  1153.         fSecondMeter->Update();
  1154. }
  1155.  
  1156. void CPortWindow::Dispose(){
  1157.     if (fFirstMeter){
  1158.         fFirstMeter->Dispose();
  1159.         delete(fFirstMeter);
  1160.     }
  1161.     if (fSecondMeter){
  1162.         fSecondMeter->Dispose();
  1163.         delete(fSecondMeter);
  1164.     }
  1165.     fFirstMeter = fSecondMeter = 0;
  1166.     
  1167.     if (fWindow)
  1168.         DisposeWindow(fWindow);
  1169.     fWindow = 0;
  1170. }
  1171.  
  1172. int CPortWindow::AllMemoryAllocated(){
  1173.     return fFirstMeter && fWindow;
  1174. }
  1175.  
  1176. /************************************** Entry Library ************************/
  1177.         
  1178. #define    MAX_ENTRIES    100
  1179.  
  1180. struct objectEntry {
  1181.     long key, value;
  1182. } objectList[MAX_ENTRIES];
  1183.  
  1184. Boolean AddEntry(key, value)
  1185. long    key, value;
  1186. {
  1187.     int    i;
  1188.     
  1189.     for (i=0;i<MAX_ENTRIES;i++){
  1190.         if (objectList[i].key == 0){
  1191.             objectList[i].key = key;
  1192.             objectList[i].value = value;
  1193.             return true;
  1194.         }
  1195.     }
  1196.     return false;
  1197. }
  1198.  
  1199. long FindKey(value)
  1200. long    value;
  1201. {
  1202.     int    i;
  1203.     
  1204.     for (i=0;i<MAX_ENTRIES;i++){
  1205.         if (objectList[i].value == value)
  1206.             return objectList[i].key;
  1207.     }
  1208.     return 0;
  1209. }
  1210.  
  1211. long RemoveEntry(key)
  1212. long    key;
  1213. {
  1214.     int    i;
  1215.     
  1216.     for (i=0;i<MAX_ENTRIES;i++){
  1217.         if (objectList[i].key == key){
  1218.             objectList[i].key = 0;
  1219.             return;
  1220.         }
  1221.     }
  1222. }
  1223.  
  1224. static int    entryNumber = 0;
  1225.  
  1226. long    GetNextKey(){
  1227.     int    i = MAX_ENTRIES;
  1228.     
  1229.     while (i-- > 0){
  1230.         if (objectList[entryNumber].key)
  1231.             return objectList[entryNumber++].key;
  1232.         else
  1233.             entryNumber++;
  1234.         if (entryNumber >= MAX_ENTRIES)
  1235.             entryNumber = 0;
  1236.     }
  1237.     return 0;
  1238. }
  1239.  
  1240. /****************************************** main *********************************/
  1241.  
  1242. #define    kCheckGainMask    (0x3f)            /* Must be (power of two) minus 1 */
  1243.  
  1244. main(){
  1245.     KeyMap        theKeys;
  1246.     Boolean     Is32Bit();
  1247.     int        eventCount = 0, i;
  1248.     
  1249.     MaxApplZone();
  1250.     for (i = 0; i<20; i++)                    /* Lots of handles */
  1251.         MoreMasters();
  1252.  
  1253.     ToolBoxInit();
  1254.  
  1255.     MenuBarInit();
  1256.     
  1257.     GetKeys(theKeys);
  1258.     if (theKeys[1] &0x04){
  1259.         useMouseForLevels = 1;
  1260.         SysBeep(10);
  1261.     }
  1262.         
  1263.     if (!Is32Bit()){
  1264.         DoStopAlert(kNeed32BitQuickDrawError);
  1265.     } else {
  1266.         extern void MakeNewWindow();
  1267.         
  1268.         gPatchChannels = kMaxChannel;
  1269.         MakeNewWindow();
  1270.                 
  1271.         while (!gDone){
  1272.             Point        theLoc;
  1273.             CPortWindow    *aPortWindow;
  1274.             
  1275.             if (useMouseForLevels){
  1276.                 GetMouse(&theLoc);
  1277.                 gMouseLevel[0] = theLoc.h;
  1278.                 gMouseLevel[1] = theLoc.v;
  1279.             }
  1280.             aPortWindow = (CPortWindow *)GetNextKey();
  1281.             if (aPortWindow)
  1282.                 aPortWindow->UpdateLevel();
  1283.             HandleEvent();
  1284.             
  1285.             if (!useMouseForLevels && (eventCount++ & kCheckGainMask) == 0)
  1286.                 GetDSPGains();
  1287.         }
  1288.         CloseDSPs();
  1289.     }
  1290. }
  1291.  
  1292. #define    kMemCushion    (80*1024L)                /* How much needed to be safe. */
  1293.  
  1294. IsMemoryAvailable(long memoryRequest){
  1295.     long    spaceAvailable;
  1296.  
  1297.     spaceAvailable = MaxBlock();
  1298.     if (spaceAvailable > kMemCushion+memoryRequest)
  1299.         return 1;
  1300.     return 0;
  1301. }
  1302.  
  1303. #define    kTrialWindowSize    256
  1304. #define    kBytesPerPixel        5/2
  1305.  
  1306. long MemoryNeededForNewWindow(Rect *screenRect){
  1307.     QDErr        errorCode;
  1308.     GDHandle    gDevice;
  1309.     long        bytesNeeded;
  1310.     
  1311.     gDevice = GetMaxDevice(screenRect);
  1312.     bytesNeeded = (*((*gDevice)->gdPMap))->pixelSize;
  1313.     bytesNeeded *= (long)(screenRect->right - screenRect->left) * 
  1314.             (long)(screenRect->bottom - screenRect->top) /
  1315.             8;                    /* Bits per Byte */
  1316.                 
  1317.     return 2 * bytesNeeded;
  1318. }
  1319.  
  1320. void MakeNewWindow(){
  1321.     CPortWindow    *aPortWindow;
  1322.     
  1323.     if (!IsMemoryAvailable(0)){
  1324.         DoCautionAlert(kNeedMoreMemoryError);
  1325.         return;
  1326.     }
  1327.     
  1328.     if (AddEntry(0,0)){
  1329.         aPortWindow = new(CPortWindow);
  1330.         if (!aPortWindow){
  1331.             DoCautionAlert(kCantAllocateCPortWindowError);
  1332.             return;
  1333.         }
  1334.         aPortWindow->IPortWindow();
  1335.         aPortWindow->NewWindow();
  1336.         
  1337.         if (aPortWindow->AllMemoryAllocated()){
  1338.             AddEntry((long)aPortWindow, (long)aPortWindow->GetWindow());
  1339.         }
  1340.     } else {
  1341.         DoCautionAlert(kCantAllocateMoreWindowsError);
  1342.     }
  1343. }
  1344.     
  1345. /****************************************** ToolBoxInit *********************************/
  1346.  
  1347. Rect    gSizeRect;
  1348.  
  1349. ToolBoxInit(){
  1350.     InitGraf(&thePort);
  1351.     InitFonts();
  1352.     FlushEvents(everyEvent, REMOVE_ALL_EVENTS);
  1353.     InitWindows();
  1354.     InitMenus();
  1355.     TEInit();
  1356.     InitDialogs(NIL_POINTER);
  1357.     InitCursor();
  1358.     
  1359.     gSizeRect.top = MIN_WINDOW_HEIGHT;
  1360.     gSizeRect.left = MIN_WINDOW_WIDTH;
  1361.     gSizeRect.bottom = screenBits.bounds.bottom - screenBits.bounds.top;
  1362.     gSizeRect.right = screenBits.bounds.right - screenBits.bounds.left;
  1363. }
  1364.  
  1365. /****************************************** Is32Bit *********************************/
  1366.  
  1367. #define    QD32TRAP    0xAB03
  1368. #define    UNIMPL_TRAP    0xA89F
  1369.  
  1370. Boolean Is32Bit(){
  1371.     SysEnvRec    mySE;
  1372.     
  1373.     SysEnvirons(2,&mySE);
  1374.     
  1375.     if (!mySE.hasColorQD)
  1376.         return FALSE;
  1377.         
  1378.     return (NGetTrapAddress(QD32TRAP, ToolTrap) != 
  1379.             NGetTrapAddress(UNIMPL_TRAP, ToolTrap));
  1380. }
  1381.  
  1382. /****************************************** DoAlert *********************************/
  1383.  
  1384. void DoCautionAlert(short messageNumber)
  1385. {    
  1386.     Str255    string;
  1387.     
  1388.     GetIndString(string, ALERT_STRING_RESOURCE, messageNumber);
  1389.     ParamText(string, NIL_STRING, NIL_STRING, NIL_STRING);
  1390.     CautionAlert(BASE_RES_ID, NIL_POINTER);
  1391. }
  1392.  
  1393. void DoStopAlert(short messageNumber)
  1394. {    
  1395.     Str255    string;
  1396.     
  1397.     GetIndString(string, ALERT_STRING_RESOURCE, messageNumber);
  1398.     ParamText(string, NIL_STRING, NIL_STRING, NIL_STRING);
  1399.     StopAlert(BASE_RES_ID, NIL_POINTER);
  1400. }
  1401.  
  1402. void ShowValue(short messageNumber, long val){
  1403.     Str255    messageString, valString, formatString;
  1404.     
  1405.     GetIndString(messageString, ALERT_STRING_RESOURCE, messageNumber);
  1406.     p2cstr(messageString);
  1407.     
  1408.     GetIndString(formatString, ALERT_STRING_RESOURCE, kShowValueError);
  1409.     p2cstr(formatString);
  1410.     
  1411.     sprintf((char *)valString, (char *)formatString, (char *)messageString, val);
  1412.     c2pstr((char *)valString);
  1413.  
  1414.     ParamText(valString, NIL_STRING, NIL_STRING, NIL_STRING);
  1415.     CautionAlert(BASE_RES_ID, NIL_POINTER);
  1416. }
  1417.  
  1418. /****************************************** MenuBarInit *********************************/
  1419.  
  1420. MenuBarInit(){
  1421.     Handle        myMenuBar;
  1422.     long        size;
  1423.     int        *ip;
  1424.  
  1425.     myMenuBar = GetNewMBar(BASE_RES_ID);
  1426.     if (!myMenuBar){
  1427.         DoStopAlert(kCantFindMenuBarError);
  1428.         CloseDSPs();
  1429.         ExitToShell();
  1430.     }
  1431.     SetMenuBar(myMenuBar);
  1432.     
  1433.     gAppleMenu = GetMHandle(APPLE_MENU);
  1434.     if (!gAppleMenu){
  1435.         DoStopAlert(kCantFindAppleMenuError);
  1436.         CloseDSPs();
  1437.         ExitToShell();
  1438.     }
  1439.     gFileMenu = GetMHandle(FILE_MENU);
  1440.     if (!gAppleMenu){
  1441.         DoStopAlert(kCantFindFileMenuError);
  1442.         CloseDSPs();
  1443.         ExitToShell();
  1444.     }
  1445.     gPatchMenu = GetMHandle(PATCH_MENU);
  1446.     if (!gPatchMenu){
  1447.         DoStopAlert(kCantFindPatchMenuError);
  1448.         CloseDSPs();
  1449.         ExitToShell();
  1450.     }
  1451.         
  1452.     AddResMenu(gAppleMenu, 'DRVR');
  1453.     DrawMenuBar();
  1454. }
  1455.  
  1456. void SetUpMenus(){
  1457.     CPortWindow    *thePortWindow;
  1458.     
  1459.     if (gCurrentWindow){
  1460.         thePortWindow = (CPortWindow *)FindKey((long)gCurrentWindow);
  1461.  
  1462.         if (!thePortWindow)
  1463.             DoCautionAlert(kMenuSetupError);
  1464.         else {
  1465.             thePortWindow->SetUpMenus();
  1466.         }
  1467.         return;
  1468.     }
  1469.     CheckItem(gPatchMenu, LEFT_ITEM, gPatchChannels == kLeftChannel);
  1470.     CheckItem(gPatchMenu, RIGHT_ITEM, gPatchChannels == kRightChannel);
  1471.     CheckItem(gPatchMenu, BOTH_ITEM, gPatchChannels == kBothChannel);
  1472.     CheckItem(gPatchMenu, MAXIMUM_ITEM, gPatchChannels == kMaxChannel);
  1473.  
  1474.     CheckItem(gPatchMenu, INPUT_ITEM, gPortType == kInputPort);
  1475.     CheckItem(gPatchMenu, OUTPUT_ITEM, gPortType == kOutputPort);
  1476.  
  1477.     CheckItem(gPatchMenu, POWER_ITEM, gLevelType == kPowerReading);
  1478.     CheckItem(gPatchMenu, PEAK_ITEM, gLevelType == kPeakReading);
  1479.  
  1480.     CheckItem(gPatchMenu, FULLSCALE_ITEM, gReferenceType == kFullScaleReference);
  1481.     CheckItem(gPatchMenu, ANALOGLINE_ITEM, gReferenceType == kAnalogLineReference);
  1482.  
  1483.     if (gCurrentWindow)
  1484.         EnableItem(gFileMenu, CLOSE_ITEM);
  1485.     else
  1486.         DisableItem(gFileMenu, CLOSE_ITEM);
  1487. }
  1488.     
  1489. /****************************************** HandleEvent *********************************/
  1490.  
  1491. EventRecord    gTheEvent;
  1492.  
  1493. HandleEvent(){
  1494.     char        theChar;
  1495.     CPortWindow    *thePortWindow;
  1496.     
  1497.     WaitNextEvent(everyEvent, &gTheEvent, MIN_SLEEP, NIL_MOUSE_REGION);
  1498.     
  1499.     switch(gTheEvent.what){
  1500.     case nullEvent:
  1501.         break;
  1502.     case mouseDown:
  1503.         HandleMouseDown();
  1504.         break;
  1505.     case keyDown:
  1506.     case autoKey:
  1507.         theChar = gTheEvent.message & charCodeMask;
  1508.         if ((gTheEvent.modifiers & cmdKey) != 0)
  1509.             HandleMenuChoice( MenuKey(theChar));
  1510.         break;
  1511.     case updateEvt:
  1512.         BeginUpdate((WindowPtr)gTheEvent.message);
  1513.         thePortWindow = (CPortWindow *)FindKey((long)gTheEvent.message);
  1514.         if (thePortWindow){
  1515.             thePortWindow->Update();
  1516.             thePortWindow->RedrawWindow();
  1517.         } else {
  1518.             DoCautionAlert(kUpdateEventError);
  1519.         }
  1520.         EndUpdate((WindowPtr)gTheEvent.message);
  1521.         break;
  1522.     case activateEvt:
  1523. #ifdef    SCROLL_BARS
  1524.         DrawGrowIcon((WindowPtr)gTheEvent.message);
  1525. #endif
  1526.         gCurrentWindow = (WindowPtr)gTheEvent.message;
  1527.         break;
  1528.     }
  1529. }
  1530.  
  1531. HandleMouseDown(){
  1532.     WindowPtr    whichWindow;
  1533.     short int    thePart;
  1534.     long int    menuChoice, windSize;
  1535.     CPortWindow    *thePortWindow;
  1536.     
  1537.     thePart = FindWindow(gTheEvent.where, &whichWindow);
  1538.     switch (thePart){
  1539.     case inMenuBar:
  1540.         SetUpMenus();
  1541.         menuChoice = MenuSelect(gTheEvent.where);
  1542.         HandleMenuChoice(menuChoice);
  1543.         break;
  1544.     case inSysWindow:
  1545.         SystemClick(&gTheEvent, whichWindow);
  1546.         break;
  1547.     case inDrag:
  1548.         DragWindow(whichWindow, gTheEvent.where, &screenBits.bounds);
  1549.  
  1550.         thePortWindow = (CPortWindow *)FindKey((long)whichWindow);
  1551.         if (!thePortWindow)
  1552.             DoCautionAlert(kInDragError);
  1553.         else
  1554.             thePortWindow->UpdateWindowSize();
  1555.         break;
  1556.     case inContent:
  1557.         SelectWindow(whichWindow);
  1558.         break;
  1559.     case inGoAway:
  1560.         thePortWindow = (CPortWindow *)FindKey((long)whichWindow);
  1561.  
  1562.         if (!thePortWindow)
  1563.             DoCautionAlert(kGoAwayError);
  1564.         else {
  1565.             RemoveEntry((long)thePortWindow);
  1566.             thePortWindow->Dispose();
  1567.             delete(thePortWindow);
  1568.         }
  1569.  
  1570.         gCurrentWindow = 0;            /* If somebody else is active
  1571.                              * then they'll get an Activate 
  1572.                              */
  1573.         break;
  1574. #ifdef    SCROLL_BARS
  1575.     case inGrow:
  1576.         windSize = GrowWindow(whichWindow, gTheEvent.where, &gSizeRect);
  1577.         if (windSize != 0){
  1578.             GrafPtr    oldPort;
  1579.             
  1580.             GetPort(&oldPort);
  1581.             SetPort(whichWindow);
  1582.             EraseRect(&whichWindow->portRect);
  1583.             SizeWindow(whichWindow, LoWord(windSize), HiWord(windSize), NORMAL_UPDATES);
  1584.             InvalRect(&whichWindow->portRect);
  1585.             SetPort(oldPort);
  1586.         }
  1587.         thePortWindow = (CPortWindow *)FindKey((long)whichWindow);
  1588.  
  1589.         if (!thePortWindow)
  1590.             DoCautionAlert(kInGrowError);
  1591.         else {
  1592.             thePortWindow->UpdateWindowSize();
  1593.             if (!thePortWindow->AllMemoryAllocated()){
  1594.                 RemoveEntry((long)thePortWindow);
  1595.                 thePortWindow->Dispose();
  1596.                 delete(thePortWindow);
  1597.             }
  1598.         }
  1599.         break;
  1600.     case inZoomIn:
  1601.     case inZoomOut:
  1602.         if (TrackBox(whichWindow, gTheEvent.where, thePart)){
  1603.             GrafPtr    oldPort;
  1604.             
  1605.             GetPort(&oldPort);
  1606.             SetPort(whichWindow);
  1607.             EraseRect(&whichWindow->portRect);
  1608.             ZoomWindow(whichWindow, thePart, LEAVE_WHERE_IT_IS);
  1609.             InvalRect(&whichWindow->portRect);
  1610.             SetPort(oldPort);
  1611.         }
  1612.         thePortWindow = (CPortWindow *)FindKey((long)whichWindow);
  1613.  
  1614.         if (!thePortWindow)
  1615.             DoCautionAlert(kInZoomError);
  1616.         else {
  1617.             thePortWindow->UpdateWindowSize();
  1618.             if (!thePortWindow->AllMemoryAllocated()){
  1619.                 RemoveEntry((long)thePortWindow);
  1620.                 thePortWindow->Dispose();
  1621.                 delete(thePortWindow);
  1622.             }
  1623.         }
  1624.         break;
  1625. #endif    /* SCROLL_BARS */
  1626.     }
  1627. }
  1628.  
  1629. HandleMenuChoice(menuChoice)
  1630. long int menuChoice;
  1631. {
  1632.     int    theMenu;
  1633.     int    theItem;
  1634.     
  1635.     if (menuChoice != 0){
  1636.         theMenu = HiWord(menuChoice);
  1637.         theItem = LoWord(menuChoice);
  1638.         
  1639.         switch(theMenu){
  1640.         case APPLE_MENU:
  1641.             HandleAppleChoice(theItem);
  1642.             break;
  1643.         case FILE_MENU:
  1644.             HandleFileChoice(theItem);
  1645.             break;
  1646.         case PATCH_MENU:
  1647.             HandlePatchChoice(theItem);
  1648.             break;
  1649.         default:
  1650. /*            ShowValue(kMenuChoiceError, menuChoice);            */
  1651.             break;
  1652.         }
  1653.         HiliteMenu(0);
  1654.     }
  1655. }
  1656.  
  1657. DisplayAboutBox(){
  1658.     Rect        r;
  1659.     PicHandle    thePicture;
  1660.     short        wide, high;
  1661.     WindowPtr    wind;
  1662.     CPictureMeter    *pictureMeter;
  1663.     short        oldUseMouse = useMouseForLevels, waitForMouse = 1;
  1664.     float        v = 0, eps=.98;
  1665.     Str255        titleString;
  1666.     long        size;
  1667.     
  1668.     thePicture = GetPicture(ABOUT_BOX_PICT_ID);
  1669.     
  1670.     r = (*thePicture)->picFrame;
  1671.     wide = r.right - r.left;            /* calculate width and height */
  1672.     high = r.bottom - r.top;
  1673.     SetRect(&r, 0, 0, wide,high );
  1674.     OffsetRect(&r, 100, 100);
  1675.     
  1676.     if (!IsMemoryAvailable(MemoryNeededForNewWindow(&r))){
  1677.         DoCautionAlert(kCantCreatePictureWindowError);
  1678.         return;
  1679.     }        
  1680.  
  1681.     GetIndString(titleString, ALERT_STRING_RESOURCE, kAboutVUMetersString);
  1682.     wind = NewCWindow(nil, &r, titleString, TRUE, dBoxProc, (WindowPtr)-1, 
  1683.                 FALSE, 0);
  1684.     if (!wind){
  1685.         DoCautionAlert(kCantAllocateVUPictureError);
  1686.         return;
  1687.     }
  1688.     
  1689.     useMouseForLevels = 1;
  1690.     SetPort(wind);
  1691.     DrawControls(wind);
  1692.     ShowWindow(wind);
  1693.     pictureMeter = new(CPictureMeter);
  1694.     if (!pictureMeter){
  1695.         DoCautionAlert(kCantAllocateVUPictureError);
  1696.         return;
  1697.     }
  1698.     pictureMeter->IVUMeter(0, 0, 0, 0, 0);        /* Don't care about meter types */
  1699.     OffsetRect(&r, -r.left, -r.top);
  1700.     pictureMeter->SetWindowRect(&r, &r);        /* Place PictureMeter into window */
  1701.     pictureMeter->SetWindow(wind);
  1702.     
  1703.     while (waitForMouse){
  1704.         v = eps*v + (1-eps)*((Random() & 0x7f)-65);
  1705.         gMouseLevel[0] = v;
  1706.         gMouseLevel[1] = v;            /* Set new random level */
  1707.         pictureMeter->UpdateLevel();        /* Redraw the meter's needle */
  1708.  
  1709.         WaitNextEvent(everyEvent, &gTheEvent, MIN_SLEEP, NIL_MOUSE_REGION);
  1710.     
  1711.         switch(gTheEvent.what){
  1712.         case mouseDown:
  1713.             waitForMouse = 0;        /* Wait for a mouse click */
  1714.             break;
  1715.         default:
  1716.             break;
  1717.         }
  1718.     }
  1719.         
  1720.     pictureMeter->Dispose();            /* Cleanup things */
  1721.     DisposeWindow(wind);
  1722.     useMouseForLevels = oldUseMouse;        /* Go back to normal operation */
  1723. }
  1724.  
  1725. HandleAppleChoice(theItem)
  1726. int    theItem;
  1727. {
  1728.     Str255    accName;
  1729.     int    accNumber;
  1730.     short    itemNumber;
  1731.  
  1732.     switch (theItem){
  1733.     case ABOUT_ITEM:
  1734.         DisplayAboutBox();
  1735.         break;
  1736.     default:
  1737.         GetItem(gAppleMenu, theItem, accName);
  1738.         accNumber = OpenDeskAcc(accName);
  1739.         break;
  1740.     }
  1741. }
  1742.  
  1743. HandleFileChoice(theItem)
  1744. int    theItem;
  1745. {
  1746.     CPortWindow    *thePortWindow;
  1747.     
  1748.     switch (theItem){
  1749.     case NEW_ITEM:
  1750.         MakeNewWindow();
  1751.         break;
  1752.     case CLOSE_ITEM:
  1753.         if (gCurrentWindow){
  1754.             thePortWindow = (CPortWindow *)FindKey((long)gCurrentWindow);
  1755.  
  1756.             if (!thePortWindow)
  1757.                 DoCautionAlert(kCloseMenuError);
  1758.             else {
  1759.                 RemoveEntry((long)thePortWindow);
  1760.                 thePortWindow->Dispose();
  1761.                 delete(thePortWindow);
  1762.             }    
  1763.             gCurrentWindow = 0;
  1764.         }
  1765.         break;
  1766.     case QUIT_ITEM:
  1767.         gDone = true;
  1768.         break;
  1769.     }
  1770. }
  1771.  
  1772. HandlePatchChoice(theItem)
  1773. int    theItem;
  1774. {
  1775.     CPortWindow    *thePortWindow;
  1776.     
  1777.     switch (theItem){
  1778.     case LEFT_ITEM:
  1779.         gPatchChannels = kLeftChannel;
  1780.         break;
  1781.     case RIGHT_ITEM:
  1782.         gPatchChannels = kRightChannel;
  1783.         break;
  1784.     case MAXIMUM_ITEM:
  1785.         gPatchChannels = kMaxChannel;
  1786.         break;
  1787.     case BOTH_ITEM:
  1788.         gPatchChannels = kBothChannel;
  1789.         break;
  1790.     case INPUT_ITEM:
  1791.         gPortType = kInputPort;
  1792.         break;
  1793.     case OUTPUT_ITEM:
  1794.         gPortType = kOutputPort;
  1795.         break;
  1796.     case PEAK_ITEM:
  1797.         gLevelType = kPeakReading;
  1798.         break;
  1799.     case POWER_ITEM:
  1800.         gLevelType = kPowerReading;
  1801.         break;
  1802.     case FAST_ITEM:
  1803.         gSpeedType = kFastSpeed;
  1804.         break;
  1805.     case NORMAL_ITEM:
  1806.         gSpeedType = kNormalSpeed;
  1807.         break;
  1808.     case FULLSCALE_ITEM:
  1809.         gReferenceType = kFullScaleReference;
  1810.         break;
  1811.     case ANALOGLINE_ITEM:
  1812.         gReferenceType = kAnalogLineReference;
  1813.         break;
  1814.     default:
  1815.         return;
  1816.     }
  1817.     
  1818.     if (gCurrentWindow){
  1819.         thePortWindow = (CPortWindow *)FindKey((long)gCurrentWindow);
  1820.  
  1821.         if (!thePortWindow)
  1822.             DoCautionAlert(kPatchMenuError);
  1823.         else {
  1824.             thePortWindow->UpdateWindowType();
  1825.             if (!thePortWindow->AllMemoryAllocated()){
  1826.                 RemoveEntry((long)thePortWindow);
  1827.                 thePortWindow->Dispose();
  1828.                 delete(thePortWindow);
  1829.             }
  1830.         }    
  1831.     }
  1832.  
  1833. }
  1834.  
  1835.