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