home *** CD-ROM | disk | FTP | other *** search
-
- /**********************************************************
- SBScope.C
-
- A tutorial MMPM/2 program written by Mike Thompson
- of Graduate Software. This program demonstrates
- the record, message, and branch functions provided
- by the memory playlist feature in MMPM/2.
-
- Although this code provides very few features, it
- provides a great way to learn about memory playlists.
- Here are some features which could be added to this
- program to enhance it:
-
- Select sampling rate
- Select refresh interval
- Select input source
- Make window sizeable
- Draw Grid lines
- Save trace
- Cursor mode
- Trigger level
-
- A really neat feature would be a wave generator
- mode. I envision a Corel Draw like Bezier Spline
- edit mode to generate a wave shape. A wave buffer
- could be built and played at any frequency using
- the memory playlist function. I have fooled a
- little with the playback, and it works o.k. I
- don't have time to get into the waveform editor,
- though.
-
- This code is released into the public domain,
- however, it would be a courtesy to reference the
- author if this code is used in whole or in
- part in another program. Thanks.
-
- This code was generated using the Clock program
- in the MMPM/2 toolkit as a starting point. By
- the way, did anyone notice the copyright date
- on the clock source? October, 1991. Has IBM
- really been working on this since then?
-
- Mike Thompson
- Graduate Software
- CIS: 76500,2037
-
- **********************************************************/
-
- #define INCL_PM
- #define INCL_GPI
- #define INCL_MMIO
- #define INCL_DOS
- #define INCL_DOSERRORS
-
- #include <OS2.H>
- #include <OS2ME.H>
- #include <Math.H>
- #include <StdIO.H>
- #include <StdLib.H>
- #include <String.H>
-
- /**********************************************************
- Define the structure for the playlist commands.
- I'm not sure why this structure is not defined in
- the MMPM/2 headers. It probably should be.
- **********************************************************/
- typedef struct {
- ULONG Opcode;
- ULONG Operand1;
- ULONG Operand2;
- ULONG Operand3;
- } PLAYLIST;
-
- /**********************************************************
- Define variables which need to be accessed by
- both message procedures. The LastWaveBuffer is the
- pointer to the wave buffer last filled by the
- playlist record function. The WaveBufferSize is
- the number of bytes recorded by the last playlist
- command.
- **********************************************************/
- static BYTE *LastWaveBuffer=NULL;
- static LONG WaveBufferSize=0;
-
- /* ------------------------------------------------------------------------- */
-
- MRESULT EXPENTRY ProcessScope(HWND Window,ULONG Message,MPARAM Param1,MPARAM Param2)
- {
- static BYTE *Buffer=NULL;
- HPS PresentationSpace;
- RECTL Rectangle;
- LONG YOffset;
- LONG XOffset;
- LONG YRange;
- LONG XRange;
- LONG i;
- POINTL Point;
-
- /**********************************************************
- Whenever a timer message is received, a check is
- made to determine if the playlist has recorded a
- new wave buffer. If it has, the scope display
- window is invalidated, forcing a repaint.
- **********************************************************/
- if(Message==WM_TIMER) {
- if(LastWaveBuffer!=Buffer)
- WinInvalidateRect(Window,NULL,TRUE);
- }
-
- /**********************************************************
- A paint message is handled by filling the scope
- window to erase the last display, then drawing
- every fourth point from the wave buffer. Every
- fourth point is used since display resolution and
- speed isn't good enough to warrant displaying every
- point. If you have ever heard of a man named
- Nyquist, you will know that you should expect to see
- signal aliasing when the signal contains frequencies
- above a certain critical frequency. The critical
- frequency is one half of the sampling frequency,
- which in my case, is the default sampling frequency
- for the SBPro divided by four since I am taking
- every fourth point. I haven't taken time to
- determine exactly what that is, but I just wanted
- you to be aware of this fact in case you use this
- for some high frequency monitoring. I used integer
- arithmetic to perform the graphics transformations
- so this should run equally well without a math
- coprocessor.
- **********************************************************/
- if(Message==WM_PAINT) {
- PresentationSpace=WinBeginPaint(Window,NULLHANDLE,&Rectangle);
- WinQueryWindowRect(Window,&Rectangle);
- WinFillRect(PresentationSpace,&Rectangle,CLR_WHITE);
- if(LastWaveBuffer!=NULL) {
- Buffer=LastWaveBuffer;
- XRange=Rectangle.xRight-Rectangle.xLeft;
- YRange=Rectangle.yTop-Rectangle.yBottom;
- XOffset=Rectangle.xLeft;
- YOffset=(Rectangle.yTop+Rectangle.yBottom)/2;
- Point.x=XOffset;
- Point.y=YRange*(Buffer[0]-128)/256+YOffset;
- GpiSetCurrentPosition(PresentationSpace,&Point);
- for(i=0;i<WaveBufferSize;i+=4) {
- Point.x=XRange*i/WaveBufferSize+XOffset;
- Point.y=YRange*(Buffer[i]-128)/256+YOffset;
- GpiLine(PresentationSpace,&Point);
- }
- }
- WinEndPaint(PresentationSpace);
- return (MRESULT)FALSE;
- }
-
- /**********************************************************
- Handle all other messages using default message
- procedure.
- **********************************************************/
- return WinDefWindowProc(Window,Message,Param1,Param2);
- }
-
- /* ------------------------------------------------------------------------- */
-
- MRESULT EXPENTRY ProcessDialog(HWND Window,ULONG Message,MPARAM Param1,MPARAM Param2)
- {
- static CHAR *DeviceName="Digital Audio";
- static BYTE *WaveBuffer=NULL;
- static PLAYLIST PlayList[21];
- static MCI_OPEN_PARMS MCIOpen;
- ULONG Error;
- ULONG i;
-
- switch(Message) {
-
- case WM_INITDLG:
-
- /**********************************************************
- Subclass the scope display window to do the scope
- refresh whenever the timer elapses. The timer is
- setup to trigger a refresh as often as possible. By
- forcing scope updates using the timer message, better
- user interface response is gained.
- **********************************************************/
- WinSubclassWindow(WinWindowFromID(Window,9001),ProcessScope);
- WinStartTimer(WinQueryAnchorBlock(HWND_DESKTOP),WinWindowFromID(Window,9001),0,0);
-
- /**********************************************************
- Allocate memory for 10 wave buffers. Each buffer
- is 2000 bytes long. The playlist will be setup to
- fill each of the 10 wave buffers in order before
- looping back to the start. After it fills a wave
- buffer, it will post a message to the dialog window
- with the pointer to the start of the wave buffer
- just recorded. The dialog window can then notify the
- scope display window that a new buffer is available.
- **********************************************************/
- WaveBufferSize=2000;
- WaveBuffer=calloc((ULONG)(10*WaveBufferSize),sizeof(BYTE));
- if(WaveBuffer==NULL) {
- DosBeep(1000,200);
- printf("Unable to allocate memory for the wave buffer.\n",DeviceName);
- WinPostMsg(Window,WM_CLOSE,0L,0L);
- break;
- }
-
- /**********************************************************
- Build the playlist. The first 20 commands are
- setup to fill the ten wave buffers and post a message
- after each is filled. The last command is a branch
- back to the start.
- **********************************************************/
- for(i=0;i<10;i++) {
- PlayList[i*2].Opcode=DATA_OPERATION;
- PlayList[i*2].Operand1=(ULONG)(WaveBuffer+i*WaveBufferSize);
- PlayList[i*2].Operand2=(ULONG)WaveBufferSize;
- PlayList[i*2].Operand3=0;
- PlayList[i*2+1].Opcode=MESSAGE_OPERATION;
- PlayList[i*2+1].Operand2=(ULONG)(WaveBuffer+i*WaveBufferSize);
- }
- PlayList[i*2].Opcode=BRANCH_OPERATION;
- PlayList[i*2].Operand2=0;
-
- /**********************************************************
- Open the audio device. I have only tested this
- with the SBPro, but I'm sure it should work with
- other audio devices with (at most) some minor work.
- For example, my SBPro defaults to 8-bit audio, which
- is what I wrote this program to expect. I don't
- know how other boards default, but it shouldn't be
- too hard to get them working. In addition, I have
- not made any options for selecting alternate input
- sources, gains, or filters. I will leave these
- options to someone who might have more time to
- experiment. It doesn't look as though they should
- be too tough, though.
- **********************************************************/
- MCIOpen.dwCallback=Window;
- MCIOpen.wDeviceID=0;
- MCIOpen.lpstrDeviceType=(VOID *)DeviceName;
- MCIOpen.lpstrElementName=(VOID *)PlayList;
- MCIOpen.lpstrAlias=NULL;
- Error=mciSendCommand(0,
- MCI_OPEN,
- MCI_WAIT |
- MCI_OPEN_PLAYLIST |
- MCI_OPEN_SHAREABLE,
- (DWORD)&MCIOpen,
- 0);
- if(Error!=MCIERR_SUCCESS) {
- DosBeep(1000,200);
- printf("Unable to open the audio device '%s'.\n",DeviceName);
- WinPostMsg(Window,WM_CLOSE,0L,0L);
- break;
- }
-
- /**********************************************************
- Attempt to start the playlist. It should run
- continuously until the program is ended.
- **********************************************************/
- Error=mciSendCommand(MCIOpen.wDeviceID,
- MCI_RECORD,
- MCI_NOTIFY,
- (DWORD)&MCIOpen,
- 0);
- if(Error!=MCIERR_SUCCESS) {
- DosBeep(1000,200);
- printf("Unable to perform the playlist for device '%s'.\n",DeviceName);
- WinPostMsg(Window,WM_CLOSE,0L,0L);
- break;
- }
-
- break;
-
- /**********************************************************
- Save the pointer to the last filled wave buffer.
- This is the parameter sent by the playlist message
- command after a record operation is complete.
- The scope display window will use the most recent
- wave buffer on its next update.
- **********************************************************/
- case MM_MCIPLAYLISTMESSAGE:
- LastWaveBuffer=(BYTE *)Param2;
- break;
-
- /**********************************************************
- Close the audio device before ending the program.
- This seems to be important, since an exit() without
- a WM_CLOSE seems to cause SYS3175 trap. Seems like
- it is a bug in the MCI libraries.
- **********************************************************/
- case WM_CLOSE:
- mciSendCommand(MCIOpen.wDeviceID,
- MCI_CLOSE,
- MCI_WAIT,
- (DWORD)&MCIOpen,
- 0);
- break;
-
- }
-
- /**********************************************************
- Handle all other dialog messages using default
- procedure.
- **********************************************************/
- return WinDefDlgProc(Window,Message,Param1,Param2);
- }
-
- /* ------------------------------------------------------------------------- */
-
- INT main(USHORT ArgumentCount,CHAR *ArgumentList[])
- {
- HAB AnchorBlock;
- HMQ MessageQueue;
- ULONG DialogID;
-
- /**********************************************************
- Initialize PM application.
- **********************************************************/
- AnchorBlock=WinInitialize(0);
- MessageQueue=WinCreateMsgQueue(AnchorBlock,0);
-
- /**********************************************************
- Run the dialog window until it is closed.
- **********************************************************/
- DialogID=9000;
- if(WinDlgBox(HWND_DESKTOP,HWND_DESKTOP,ProcessDialog,NULLHANDLE,DialogID,NULL)==DID_ERROR) {
- printf("\n\aUnable to load dialog %ld",DialogID);
- exit(1);
- }
-
- /**********************************************************
- Terminate PM application.
- **********************************************************/
- WinDestroyMsgQueue(MessageQueue);
- WinTerminate(AnchorBlock);
- return 0;
- }
-
-
-