home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / mmpm2 / sbscope / sbscope.c < prev    next >
C/C++ Source or Header  |  1993-01-16  |  15KB  |  340 lines

  1.  
  2. /**********************************************************
  3.     SBScope.C
  4.     
  5.     A tutorial MMPM/2 program written by Mike Thompson
  6.     of Graduate Software.  This program demonstrates
  7.     the record, message, and branch functions provided 
  8.     by the memory playlist feature in MMPM/2.
  9.     
  10.     Although this code provides very few features, it 
  11.     provides a great way to learn about memory playlists.
  12.     Here are some features which could be added to this 
  13.     program to enhance it:
  14.     
  15.         Select sampling rate
  16.         Select refresh interval
  17.         Select input source
  18.         Make window sizeable
  19.         Draw Grid lines
  20.         Save trace
  21.         Cursor mode
  22.         Trigger level
  23.     
  24.     A really neat feature would be a wave generator
  25.     mode.  I envision a Corel Draw like Bezier Spline
  26.     edit mode to generate a wave shape.  A wave buffer
  27.     could be built and played at any frequency using
  28.     the memory playlist function.  I have fooled a
  29.     little with the playback, and it works o.k.  I
  30.     don't have time to get into the waveform editor,
  31.     though.
  32.     
  33.     This code is released into the public domain,
  34.     however, it would be a courtesy to reference the
  35.     author if this code is used in whole or in
  36.     part in another program.  Thanks.
  37.     
  38.     This code was generated using the Clock program
  39.     in the MMPM/2 toolkit as a starting point.  By
  40.     the way, did anyone notice the copyright date
  41.     on the clock source?  October, 1991.  Has IBM
  42.     really been working on this since then?
  43.     
  44.     Mike Thompson
  45.     Graduate Software
  46.     CIS:  76500,2037
  47.  
  48. **********************************************************/
  49.  
  50. #define INCL_PM
  51. #define INCL_GPI
  52. #define INCL_MMIO
  53. #define INCL_DOS
  54. #define INCL_DOSERRORS
  55.  
  56. #include <OS2.H>
  57. #include <OS2ME.H>
  58. #include <Math.H>
  59. #include <StdIO.H>
  60. #include <StdLib.H>
  61. #include <String.H>
  62.  
  63. /**********************************************************
  64.     Define the structure for the playlist commands.
  65.     I'm not sure why this structure is not defined in
  66.     the MMPM/2 headers.  It probably should be.
  67. **********************************************************/
  68.     typedef struct {
  69.         ULONG Opcode;
  70.         ULONG Operand1;
  71.         ULONG Operand2;
  72.         ULONG Operand3;
  73.     } PLAYLIST;
  74.  
  75. /**********************************************************
  76.     Define variables which need to be accessed by 
  77.     both message procedures.  The LastWaveBuffer is the
  78.     pointer to the wave buffer last filled by the 
  79.     playlist record function.  The WaveBufferSize is
  80.     the number of bytes recorded by the last playlist
  81.     command.
  82. **********************************************************/
  83.     static BYTE *LastWaveBuffer=NULL;
  84.     static LONG WaveBufferSize=0;
  85.  
  86. /* ------------------------------------------------------------------------- */
  87.  
  88. MRESULT EXPENTRY ProcessScope(HWND Window,ULONG Message,MPARAM Param1,MPARAM Param2)
  89.     {
  90.     static BYTE *Buffer=NULL;
  91.     HPS PresentationSpace;
  92.     RECTL Rectangle;
  93.     LONG YOffset;
  94.     LONG XOffset;
  95.     LONG YRange;
  96.     LONG XRange;
  97.     LONG i;
  98.     POINTL Point;
  99.     
  100.     /**********************************************************
  101.         Whenever a timer message is received, a check is
  102.         made to determine if the playlist has recorded a
  103.         new wave buffer.  If it has, the scope display
  104.         window is invalidated, forcing a repaint.
  105.     **********************************************************/
  106.         if(Message==WM_TIMER) {
  107.             if(LastWaveBuffer!=Buffer)
  108.                 WinInvalidateRect(Window,NULL,TRUE);
  109.         }  
  110.         
  111.     /**********************************************************
  112.         A paint message is handled by filling the scope
  113.         window to erase the last display, then drawing
  114.         every fourth point from the wave buffer.  Every
  115.         fourth point is used since display resolution and
  116.         speed isn't good enough to warrant displaying every
  117.         point.  If you have ever heard of a man named 
  118.         Nyquist, you will know that you should expect to see
  119.         signal aliasing when the signal contains frequencies
  120.         above a certain critical frequency.  The critical 
  121.         frequency is one half of the sampling frequency, 
  122.         which in my case, is the default sampling frequency
  123.         for the SBPro divided by four since I am taking 
  124.         every fourth point.  I haven't taken time to
  125.         determine exactly what that is, but I just wanted
  126.         you to be aware of this fact in case you use this
  127.         for some high frequency monitoring.  I used integer
  128.         arithmetic to perform the graphics transformations
  129.         so this should run equally well without a math
  130.         coprocessor.
  131.     **********************************************************/
  132.         if(Message==WM_PAINT) {
  133.             PresentationSpace=WinBeginPaint(Window,NULLHANDLE,&Rectangle);
  134.             WinQueryWindowRect(Window,&Rectangle);
  135.             WinFillRect(PresentationSpace,&Rectangle,CLR_WHITE);
  136.             if(LastWaveBuffer!=NULL) {
  137.                 Buffer=LastWaveBuffer;
  138.                 XRange=Rectangle.xRight-Rectangle.xLeft;
  139.                 YRange=Rectangle.yTop-Rectangle.yBottom;
  140.                 XOffset=Rectangle.xLeft;
  141.                 YOffset=(Rectangle.yTop+Rectangle.yBottom)/2;
  142.                 Point.x=XOffset;
  143.                 Point.y=YRange*(Buffer[0]-128)/256+YOffset;
  144.                 GpiSetCurrentPosition(PresentationSpace,&Point);
  145.                 for(i=0;i<WaveBufferSize;i+=4) {
  146.                     Point.x=XRange*i/WaveBufferSize+XOffset;
  147.                     Point.y=YRange*(Buffer[i]-128)/256+YOffset;
  148.                     GpiLine(PresentationSpace,&Point);
  149.                 }
  150.             }
  151.             WinEndPaint(PresentationSpace);
  152.             return (MRESULT)FALSE;
  153.         }
  154.         
  155.     /**********************************************************
  156.         Handle all other messages using default message
  157.         procedure.
  158.     **********************************************************/
  159.         return WinDefWindowProc(Window,Message,Param1,Param2);
  160.     }
  161.  
  162. /* ------------------------------------------------------------------------- */
  163.  
  164. MRESULT EXPENTRY ProcessDialog(HWND Window,ULONG Message,MPARAM Param1,MPARAM Param2)
  165.     {
  166.     static CHAR *DeviceName="Digital Audio";
  167.     static BYTE *WaveBuffer=NULL;
  168.     static PLAYLIST PlayList[21];
  169.     static MCI_OPEN_PARMS MCIOpen;
  170.     ULONG Error;
  171.     ULONG i;
  172.  
  173.         switch(Message) {
  174.  
  175.             case WM_INITDLG:
  176.  
  177.             /**********************************************************
  178.                 Subclass the scope display window to do the scope 
  179.                 refresh whenever the timer elapses.  The timer is 
  180.                 setup to trigger a refresh as often as possible.  By 
  181.                 forcing scope updates using the timer message, better 
  182.                 user interface response is gained.  
  183.             **********************************************************/
  184.                 WinSubclassWindow(WinWindowFromID(Window,9001),ProcessScope);
  185.                 WinStartTimer(WinQueryAnchorBlock(HWND_DESKTOP),WinWindowFromID(Window,9001),0,0);
  186.  
  187.             /**********************************************************
  188.                 Allocate memory for 10 wave buffers.  Each buffer
  189.                 is 2000 bytes long.  The playlist will be setup to 
  190.                 fill each of the 10 wave buffers in order before 
  191.                 looping back to the start.  After it fills a wave
  192.                 buffer, it will post a message to the dialog window 
  193.                 with the pointer to the start of the wave buffer
  194.                 just recorded.  The dialog window can then notify the
  195.                 scope display window that a new buffer is available.
  196.             **********************************************************/
  197.                 WaveBufferSize=2000;
  198.                 WaveBuffer=calloc((ULONG)(10*WaveBufferSize),sizeof(BYTE));
  199.                 if(WaveBuffer==NULL) {
  200.                     DosBeep(1000,200);
  201.                     printf("Unable to allocate memory for the wave buffer.\n",DeviceName);
  202.                     WinPostMsg(Window,WM_CLOSE,0L,0L);
  203.                     break;
  204.                 }
  205.                 
  206.             /**********************************************************
  207.                 Build the playlist.  The first 20 commands are
  208.                 setup to fill the ten wave buffers and post a message
  209.                 after each is filled.  The last command is a branch
  210.                 back to the start.
  211.             **********************************************************/
  212.                 for(i=0;i<10;i++) {
  213.                     PlayList[i*2].Opcode=DATA_OPERATION;
  214.                     PlayList[i*2].Operand1=(ULONG)(WaveBuffer+i*WaveBufferSize);
  215.                     PlayList[i*2].Operand2=(ULONG)WaveBufferSize;
  216.                     PlayList[i*2].Operand3=0;
  217.                     PlayList[i*2+1].Opcode=MESSAGE_OPERATION;
  218.                     PlayList[i*2+1].Operand2=(ULONG)(WaveBuffer+i*WaveBufferSize);
  219.                 }
  220.                 PlayList[i*2].Opcode=BRANCH_OPERATION;
  221.                 PlayList[i*2].Operand2=0;
  222.  
  223.             /**********************************************************
  224.                 Open the audio device.  I have only tested this
  225.                 with the SBPro, but I'm sure it should work with
  226.                 other audio devices with (at most) some minor work.
  227.                 For example, my SBPro defaults to 8-bit audio, which
  228.                 is what I wrote this program to expect.  I don't
  229.                 know how other boards default, but it shouldn't be
  230.                 too hard to get them working.  In addition, I have
  231.                 not made any options for selecting alternate input
  232.                 sources, gains, or filters.  I will leave these
  233.                 options to someone who might have more time to 
  234.                 experiment.  It doesn't look as though they should
  235.                 be too tough, though.
  236.             **********************************************************/
  237.                 MCIOpen.dwCallback=Window;
  238.                 MCIOpen.wDeviceID=0;
  239.                 MCIOpen.lpstrDeviceType=(VOID *)DeviceName;
  240.                 MCIOpen.lpstrElementName=(VOID *)PlayList;
  241.                 MCIOpen.lpstrAlias=NULL;
  242.                 Error=mciSendCommand(0,
  243.                                      MCI_OPEN,
  244.                                      MCI_WAIT |
  245.                                      MCI_OPEN_PLAYLIST | 
  246.                                      MCI_OPEN_SHAREABLE,
  247.                                      (DWORD)&MCIOpen,
  248.                                      0);
  249.                 if(Error!=MCIERR_SUCCESS) {
  250.                     DosBeep(1000,200);
  251.                     printf("Unable to open the audio device '%s'.\n",DeviceName);
  252.                     WinPostMsg(Window,WM_CLOSE,0L,0L);
  253.                     break;
  254.                 }
  255.  
  256.             /**********************************************************
  257.                 Attempt to start the playlist.  It should run
  258.                 continuously until the program is ended.
  259.             **********************************************************/
  260.                 Error=mciSendCommand(MCIOpen.wDeviceID,
  261.                                      MCI_RECORD,
  262.                                      MCI_NOTIFY,
  263.                                      (DWORD)&MCIOpen,
  264.                                      0);
  265.                 if(Error!=MCIERR_SUCCESS) {
  266.                     DosBeep(1000,200);
  267.                     printf("Unable to perform the playlist for device '%s'.\n",DeviceName);
  268.                     WinPostMsg(Window,WM_CLOSE,0L,0L);
  269.                     break;
  270.                 }
  271.  
  272.                 break;
  273.                 
  274.         /**********************************************************
  275.             Save the pointer to the last filled wave buffer.
  276.             This is the parameter sent by the playlist message
  277.             command after a record operation is complete.
  278.             The scope display window will use the most recent
  279.             wave buffer on its next update.
  280.         **********************************************************/
  281.             case MM_MCIPLAYLISTMESSAGE:
  282.                 LastWaveBuffer=(BYTE *)Param2;
  283.                 break;
  284.                 
  285.         /**********************************************************
  286.             Close the audio device before ending the program.
  287.             This seems to be important, since an exit() without
  288.             a WM_CLOSE seems to cause SYS3175 trap.  Seems like
  289.             it is a bug in the MCI libraries.
  290.         **********************************************************/
  291.             case WM_CLOSE:
  292.                 mciSendCommand(MCIOpen.wDeviceID,
  293.                                MCI_CLOSE,
  294.                                MCI_WAIT,
  295.                                (DWORD)&MCIOpen,
  296.                                0);
  297.                 break;
  298.                 
  299.         }
  300.  
  301.     /**********************************************************
  302.         Handle all other dialog messages using default
  303.         procedure.
  304.     **********************************************************/
  305.         return WinDefDlgProc(Window,Message,Param1,Param2);
  306.     }
  307.  
  308. /* ------------------------------------------------------------------------- */
  309.  
  310. INT main(USHORT ArgumentCount,CHAR *ArgumentList[])
  311.     {
  312.     HAB AnchorBlock;
  313.     HMQ MessageQueue;
  314.     ULONG DialogID;
  315.  
  316.     /**********************************************************
  317.         Initialize PM application.
  318.     **********************************************************/
  319.         AnchorBlock=WinInitialize(0);
  320.         MessageQueue=WinCreateMsgQueue(AnchorBlock,0);
  321.  
  322.     /**********************************************************
  323.         Run the dialog window until it is closed.
  324.     **********************************************************/
  325.         DialogID=9000;
  326.         if(WinDlgBox(HWND_DESKTOP,HWND_DESKTOP,ProcessDialog,NULLHANDLE,DialogID,NULL)==DID_ERROR) {
  327.             printf("\n\aUnable to load dialog %ld",DialogID);
  328.             exit(1);
  329.         }
  330.             
  331.     /**********************************************************
  332.         Terminate PM application.
  333.     **********************************************************/
  334.         WinDestroyMsgQueue(MessageQueue);
  335.         WinTerminate(AnchorBlock);
  336.         return 0;
  337.     }
  338.     
  339.     
  340.