/******************************************************************************
*                                                                             *
*   Name:  MMINK.C                                                            *
*                                                                             *
*   Copyright : COPYRIGHT IBM CORPORATION, 1993                               *
*               LICENSED MATERIAL - PROGRAM PROPERTY OF IBM                   *
*                                                                             *
*   Description: This program loads a predefined bitmap in client window and  *
*                allows user to record the audio synchronized with stroke     *
*                data. Also allows to play it back.                           *
*                                                                             *
*   Execution Instruction: MMINK                                              *
*                                                                             *
*   Hardware Requirement: Audio I/O device.                                   *
*                                                                             *
*   Software Requirement: IBM OS/2 V2.1, Pen for OS/2 V1.0, and MMPM/2 V1.1   *
*                                                                             *
*   Subroutine Names and their function:                                      *
*                                                                             *
*      main : main drive routine                                              *
*                                                                             *
*      ClientWinProc : Client window procedure which has a bitmap in client   *
*                      area and handles the annotation menu item.             *
*                                                                             *
*      AudioDlgProc: Audio dialog procedure which handles the recording and   *
*                    playing back of the recorded voice and strokes.          *
*                                                                             *
*  DISCLAIMER OF WARRANTIES.  The following [enclosed] code is                *
*      sample code created by IBM Corporation. This sample code is not        *
*      part of any standard or IBM product and is provided to you solely      *
*      for  the purpose of assisting you in the development of your           *
*      applications.  The code is provided "AS IS", without                   *
*      warranty of any kind.  IBM shall not be liable for any damages         *
*      arising out of your use of the sample code, even if they have been     *
*      advised of the possibility of such damages.                            *
*                                                                             *
******************************************************************************/

/******************************************************************************
* Note: You may notice slow inking during the recording of audio and ink on   *
* slower machines. This is a result of MMINK being designed as a single       *
* threaded application. Performance should improve by modifying the           *
* application to use a multi-threaded model. The choice of a single thread    *
* was made in order to increase the readability and simplicity the source     *
* code.                                                                       *
******************************************************************************/

#define    INCL_WIN
#define    INCL_ERRORS
#define    INCL_GPI
#define    INCL_DOS
#define    INCL_CIRCULARSLIDER
#include   <os2.h>
#include   <string.h>
#include   <stdlib.h>
#include   <stdio.h>
#include   <os2me.h>
#include   <penpm.h>
#include   <sw.h>
#include   "mmink.h"

/*****************************************************************************
*                                                                            *
*   Subroutine Name : main                                                   *
*                                                                            *
*   Function: Main procedure to create the client window, process the window *
*             messages, and exits the program.                               *
*                                                                            *
*   Parameters: None.                                                        *
*                                                                            *
*****************************************************************************/
VOID main(VOID)
{
   ULONG flFrameFlags = FCF_SIZEBORDER | FCF_MINMAX | FCF_TITLEBAR |
                        FCF_ICON | FCF_MENU | FCF_TASKLIST;
   QMSG         qmsg;
   HMQ          hmq;
   USHORT       i;
   CHAR         achClass[] = "MMINK";

   hab = WinInitialize(0UL);
   hmq = WinCreateMsgQueue(hab, 0);

   WinRegisterClass(hab,
                    achClass,
                    ClientWinProc,
                    CS_SYNCPAINT,
                    0);

   hwndFrame  = WinCreateStdWindow(HWND_DESKTOP,
                                   WS_VISIBLE,
                                   &flFrameFlags,
                                   achClass,
                                   NULL,
                                   0UL,
                                   0UL,
                                   ID_MMINK,
                                   &hwndClient);

   WinSetWindowPos(hwndFrame,
                   HWND_TOP,
                   (WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN) - CLIENTCX) / 2,
                   (WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - CLIENTCY) / 2,
                   CLIENTCX,
                   CLIENTCY,
                   SWP_SIZE | SWP_MOVE | SWP_ACTIVATE | SWP_SHOW);

   /************************************************************************
   *  When the modeless Audio dialog window ends, it makes the WinGetMsg   *
   *  false. So, add a flag which is TRUE when user clicks Exit menu item. *
   ************************************************************************/
   while (!fFinish)
   {
      while (WinGetMsg(hab, &qmsg, 0UL, 0UL, 0UL))
         WinDispatchMsg( hab, &qmsg );
   }

   WinDestroyWindow(hwndFrame);
   WinDestroyMsgQueue( hmq );
   WinTerminate( hab );
   DosExit(1, 0);
}

/*****************************************************************************
*                                                                            *
*   Subroutine Name : ClientWinProc                                          *
*                                                                            *
*   Function: Window procedure of the main client window.                    *
*             Process all messages and menu items in this window.            *
*                                                                            *
*   Parameters: HWND    hwnd                                                 *
*               ULONG   msg                                                  *
*               MAPRAM  mp1                                                  *
*               MPARAM  mp2                                                  *
*                                                                            *
*   Pen message processed: WM_RECO                                           *
*                                                                            *
*****************************************************************************/
MRESULT EXPENTRY ClientWinProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
CHAR                       err[80];
static HDC                 hdc;
static HPS                 hpsMem;
HPS                        hps;
static HBITMAP             hbm;
static BITMAPINFOHEADER2   bmhData;
static SIZEL               sizel;
POINTL                     aptl[4];
RECTL                      rcl;
static LONG                lCx, lCy;

   switch (msg)
   {
      case WM_CREATE:
         /*******************************************************************
         *  . Get the menu handle to enable/disable the ANNOTATE menu item  *
         *  . Get the memory device context, create the presentation space, *
         *    and load predefined bitmap.                                   *
         *  . Get the bitmap information.                                   *
         *******************************************************************/
         hwndMenu = WinWindowFromID(WinQueryWindow(hwnd, QW_PARENT),
                                    FID_MENU);
         hdc = DevOpenDC(hab,
                         OD_MEMORY,
                         "*",         /* No device info */
                         0L,
                         (PDEVOPENDATA) NULL,
                         NULLHANDLE);

         sizel.cx = sizel.cy = 0;
         hpsMem = GpiCreatePS(hab,
                              hdc,
                              &sizel,
                              PU_PELS | GPIT_MICRO | GPIA_ASSOC | GPIF_DEFAULT);

         hbm = GpiLoadBitmap(hpsMem,
                             NULLHANDLE,
                             IDB_MMINK,
                             0L,      /* NO stretch in X and Y direction */
                             0L);
         GpiSetBitmap(hpsMem, hbm);

         /* Get the bitmap information */
         bmhData.cbFix = sizeof(bmhData);
         GpiQueryBitmapInfoHeader(hbm, &bmhData);
         sizel.cx = bmhData.cx;
         sizel.cy = bmhData.cy;
         return FALSE;

      case WM_SIZE:
         if ( hwndAudio )
         {
            WinSendMsg ( hwndAudio, WM_COMMAND, (MPARAM) AUDIOCTL_STOP, (MPARAM) 0 );
         };
         lCx = SHORT1FROMMP(mp2);
         lCy = SHORT2FROMMP(mp2);
         if ( hwndInk )
         {
            WinQueryWindowRect ( hwnd, &rcl );
            WinSetWindowPos ( hwndInk,
                              HWND_TOP,
                              0,
                              0,
                              rcl.xRight,
                              rcl.yTop,
                              SWP_MOVE | SWP_SIZE  );
         }
         else
            WinInvalidateRect ( hwnd, NULL, TRUE );
         return FALSE;

      case WM_PAINT:
      {
         RECTL     rctl;

         hps = WinBeginPaint(hwnd, (ULONG) NULL, (PRECTL) &rctl);

         /* Target coordinate */
         aptl[0].x = rctl.xLeft;
         aptl[0].y = rctl.yBottom;
         aptl[1].x = rctl.xRight;
         aptl[1].y = rctl.yTop;

         /* Source Coordinate */
         aptl[2].x = rctl.xLeft * sizel.cx / lCx;
         aptl[2].y = rctl.yBottom * sizel.cy / lCy;
         aptl[3].x = rctl.xRight * sizel.cx / lCx;
         aptl[3].y = rctl.yTop * sizel.cy / lCy;

         GpiBitBlt(hps,
                   hpsMem,
                   4L,
                   aptl,
                   ROP_SRCCOPY,
                   BBO_IGNORE);

         WinEndPaint(hps);
         return FALSE;
      }

      case WM_RECO:
      {
         RECODATA   rcData;

         /******************************************************************
         *  Process the uppercase 'A' gesture as if the Annotate menu item *
         *  is selected.                                                   *
         ******************************************************************/
         rcData = * (RECODATA *) PVOIDFROMMP(mp2);
         if ((rcData.virtual_id == VIRTUAL_ID) && (rcData.char_code == UPPERA))
         {
            WinPostMsg(hwnd, WM_COMMAND, MPFROMSHORT(IDM_ANNOT), NULL);
            return((MRESULT) RECO_PROCESSED);
         }
         return(FALSE);
      }

      case WM_COMMAND:
         switch(SHORT1FROMMP(mp1))
         {
            case IDM_ANNOT:
               /*************************************************************
               *  Disable this menu item, it will be enabled later when the *
               *  dialog box is closed.                                     *
               *************************************************************/
               WinSendMsg(hwndMenu,
                          MM_SETITEMATTR,
                          MPFROM2SHORT(IDM_ANNOT, TRUE),
                          MPFROM2SHORT(MIA_DISABLED, MIA_DISABLED));

               hwndInk = InitTimedInk(hwnd, &CurrMMTime);
               if ( !hwndInk )
               {
                   SetText(hwnd, err, "Failed to initialize timed Ink.",
                           "Errors", 0,
                           (ULONG) MB_ERROR | MB_OK, TRUE);
                   WinPostMsg(hwnd, WM_CLOSE, NULL, NULL);
               }
               else
                   hwndAudio = WinLoadDlg(HWND_DESKTOP,
                                          hwnd,
                                          AudioDlgProc,
                                          0UL,
                                          IDD_AUDIO,
                                          0UL);
               return FALSE;

            case IDM_EXIT:
               fFinish = TRUE;
               WinPostMsg(hwnd, WM_CLOSE, NULL, NULL);
               return FALSE;
         }
         return FALSE;

      case WM_CLOSE:
         GpiDestroyPS(hpsMem);
         DevCloseDC(hdc);
         GpiDeleteBitmap(hbm);
         break;

      default:
         break;

   } /* end of switch */

   return(WinDefWindowProc(hwnd, msg, mp1, mp2));
}

/*****************************************************************************
*                                                                            *
*   Subroutine Name : SetText                                                *
*                                                                            *
*   Function: Error message is given in a message box if fMessage is TRUE.   *
*             This function put a text and number in ASCII in an array       *
*             so that the sprintf C function is not used.                    *
*                                                                            *
*   Parameters: HWND    hwnd                                                 *
*               PSZ     pszArray:arrary which will have the error message    *
*               PSZ     pszStr: error message                                *
*               PSZ     pszTitle: title of the message box                   *
*               ULONG   ulRc: error code                                     *
*               ULONG   ulFlags: flags for the message box                   *
*               BOOL    fMessage: give message box or not                    *
*                                                                            *
*               All parameters are input.                                    *
*                                                                            *
*****************************************************************************/
VOID SetText(HWND hwnd, PSZ pszArray, PSZ pszStr, PSZ pszTitle, ULONG ulRc,
             ULONG ulFlags, BOOL fMessage)
{
PSZ  pszTemp;

    strcpy(pszArray, pszStr);
    pszTemp = pszArray + strlen(pszArray);
    _itoa(ulRc, pszTemp, 10);
    *(pszTemp + 3) = '\0';

    if (fMessage)
    {
       WinMessageBox(HWND_DESKTOP,
                     hwnd,
                     pszArray,
                     pszTitle,
                     0,
                     ulFlags);
    }

}

/*****************************************************************************
*                                                                            *
*   Subroutine Name : AudioDlgProc                                           *
*                                                                            *
*   Function: Window procedure of the audio dialog window.                   *
*             Process all messages and button messages in this window.       *
*                                                                            *
*   Parameters: HWND    hwnd                                                 *
*               ULONG   msg                                                  *
*               MAPRAM  mp1                                                  *
*               MPARAM  mp2                                                  *
*                                                                            *
*****************************************************************************/
MRESULT EXPENTRY AudioDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
  static SHORT      sAudioLevel = 100;
  static USHORT     usTotTime = 0;
  static SBCDATA    sbc =  { 14, 0, 0, 100, 50, 1, 100 };
  static BTNCDATA   bBut;
  SHORT             rc;
  CHAR              err[80];
  HPS               hps;
  static SHORT      currentMode = IDLE_MODE;
  ULONG             ulReturn, i;
  static USHORT     usPos = 0;
  USHORT            usNotifyCode;          /* notification message code            */
  SHORT             sWmControlID;          /* WM_CONTROL id                        */
  USHORT            usTime;     /* Duration and Elapsed Time in second */

  switch (msg)
  {
     case WM_COMMAND:
     {
        switch (SHORT1FROMMP(mp1))
        {
           case AUDIOCTL_PLAY:
           /******************************************************************
           *  User clicks the PLAY button after recording.                   *
           *  If any old stroke in window, erase it.                         *
           *  Acquire the audio device.                                      *
           *  Play the recorded audio data.                                  *
           *  For stroke data, convert sensor resolution to screen resolution*
           *  Enable STOP buttons and disable the other buttons.             *
           ******************************************************************/
           {
              PSTROKEDATA pStroke;

              if (ulReturn = mmsrvAcquire())
              {
                 SetText(hwnd, err, "Error during Acquire, rc = ",
                         "MMPM/2 Error", ulReturn,
                         (ULONG) MB_ERROR | MB_OK, TRUE);
                 return  FALSE;
              }

              if (ulReturn = mmsrvPlay(hwnd))
              {
                 SetText(hwnd, err, "Error during Playback, rc = ",
                         "Audio Error", ulReturn,
                         (ULONG) MB_ERROR | MB_OK, TRUE);
                 return  FALSE;
              }

              usPos = 0;
              currentMode = PLAYING_MODE;

              PlayTimedInk ( hwndInk );
              WinSetDlgItemText(hwnd,
                                AUDIOCTL_INFO,
                                "Playing voice & handwriting.");

              /***********************************************************
              *  Disable the PLAY and RECORD buttons but enable STOP but *
              ***********************************************************/
              WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_PLAY), FALSE);
              WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_REC), FALSE);
              WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_STOP), TRUE);
              break;
           }  /* of case AUDIOCTL_PLAY                                */

           case AUDIOCTL_REC:
           /******************************************************************
           *  User clicks the RECORD button.                                 *
           *  If any old stroke in window, erase it.                         *
           *  Acquire the audio device.                                      *
           *  Record the audio data.                                         *
           *  Enable STOP buttons and disable the other buttons.             *
           ******************************************************************/
           {
              if (ulReturn = mmsrvAcquire())
              {
                 SetText(hwnd, err, "Error during Acquire, rc = ",
                         "MMPM/2 Error", ulReturn,
                         (ULONG) MB_ERROR | MB_OK, TRUE);
                 return  FALSE;
              }

              if (ulReturn = mmsrvRecord(hwnd))
              {
                 SetText(hwnd, err, "Error during Recording, rc = ",
                         "Audio Error", ulReturn,
                         (ULONG) MB_ERROR | MB_OK, TRUE);
                 return  FALSE;
              }
              usPos = 0;
              currentMode = RECORDING_MODE;
              WinSetDlgItemText(hwnd,
                                AUDIOCTL_INFO,
                                "Recording voice & handwriting.");

              RecordTimedInk(hwndInk);

              /************************************************************
              *  Enable the STOP button, but disables the PLAY and RECORD *
              *  buttons.                                                 *
              ************************************************************/
              WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_STOP), TRUE);
              WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_REC), FALSE);
              WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_PLAY), FALSE);
              break;
           }  /* of case AUDIOCTL_REC                                 */

           case AUDIOCTL_STOP:
           /******************************************************************
           *  User clicks the STOP button while recording or playing.        *
           *  Depending upon the mode (playing or recording), set correct    *
           *  text in INFO field of dialog box.                              *
           *  Stop recording/playing auiod data.                             *
           ******************************************************************/
           {
              StopTimedInk(hwndInk);
              switch (currentMode)
              {
                 case RECORDING_MODE:
                 {
                    WinSetDlgItemText(hwnd,
                                      AUDIOCTL_INFO,
                                      "Stopping voice/pen recording.");
                    break;
                 }  /* of case RECORDING_MODE                         */

                 case  PLAYING_MODE:
                 {
                    WinSetDlgItemText(hwnd,
                                      AUDIOCTL_INFO,
                                      "Stopping voice/pen playback.");
                    break;
                 }  /* of case PLAYING_MODE                           */
              }  /*  of switch(currentMode)                           */

              (void) mmsrvStop();
              currentMode = IDLE_MODE;
              break;

           }  /* of case AUDIOCTL_STOP                                */
        }  /* of switch(SHORT1FROMMP(mp1))                            */
        return  FALSE;
     }  /* of case WM_COMMAND                                         */

     case MM_MCINOTIFY:
     /*********************************************************************
     *  This message is sent from MMPM/2 system when the MMPM/2 device    *
     *  successfully completes the action indicated by media message or   *
     *  when an error occurs.                                             *             *
     *********************************************************************/
     {
         USHORT usNotifyCode;
         USHORT usReceivedMessage;

         StopTimedInk ( hwndInk );

         usNotifyCode = SHORT1FROMMP(mp1);
         usReceivedMessage = SHORT2FROMMP(mp2);
         WinSetDlgItemText(hwnd,
                           AUDIOCTL_ELAP,
                           "Elapsed: 0");
         WinSetDlgItemText(hwnd,
                           AUDIOCTL_INFO,
                           "");
         if (usNotifyCode == MCI_NOTIFY_SUCCESSFUL
             || usNotifyCode == MCI_NOTIFY_SUPERSEDED
             || usNotifyCode == MCI_NOTIFY_ABORTED)
         {
            if (ulReturn = mmsrvRelease())
            {
               SetText(hwnd, err, "Error during Release, rc = ",
                       "MMPM/2 Error", ulReturn,
                       (ULONG) MB_ERROR | MB_OK, TRUE);
               return  FALSE;
            };
            if (usReceivedMessage == MCI_RECORD)
            {
               if (usPos == 0)
                  WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_PLAY),
                                  FALSE);
               else
               {
                  usTime = usPos / 10;
                  SetText(hwnd, err, "Duration: ",
                          NULL, usTime,
                          (ULONG) NULL, FALSE);
                  WinSetDlgItemText(hwnd, AUDIOCTL_DUR, err);
               };
            };
         }
         else
         {
            SetText(hwnd, err, "Play/Record could not succeed",
                    "Audio Error", 0,
                    (ULONG) MB_ERROR | MB_OK, TRUE);
         };  /* of else                       */

         for (i = AUDIOCTL_PLAY; i < AUDIOCTL_STOP; i++)
            WinEnableWindow(WinWindowFromID(hwnd, i),
                            TRUE);

         WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_STOP),
                         FALSE);
         return  FALSE;
     }   /* of case MM_MCINOTIFY          */

     /**********************************************************************
     *  This message is sent from multi media device for every one tenth   *
     *  second while audio data is played or recorded. This one tenth of   *
     *  time interval was done in mmsrvOpen().                             *
     **********************************************************************/
     case MM_MCIPOSITIONCHANGE:
     {
         usPos++;
         usTime = usPos / 10;

         switch (currentMode)
         {
            case RECORDING_MODE:
            {
               SetText(hwnd, err, "Duration: ",
                       NULL, usTime,
                       (ULONG) NULL, FALSE);
               WinSetDlgItemText(hwnd, AUDIOCTL_DUR, err);

               SetText(hwnd, err, "Elapsed: ",
                       NULL, usTime,
                       (ULONG) NULL, FALSE);
               WinSetDlgItemText(hwnd, AUDIOCTL_ELAP, err);
               CurrMMTime = (MMTIME) mp2;
               break;
            }

            case PLAYING_MODE:
            {
               SetText(hwnd, err, "Duration: ",
                       NULL, usTime,
                       (ULONG) NULL, FALSE);
               WinSetDlgItemText(hwnd, AUDIOCTL_DUR, err);
               SetText(hwnd, err, "Elapsed: ",
                       NULL, usTime,
                       (ULONG) NULL, FALSE);
               WinSetDlgItemText(hwnd, AUDIOCTL_ELAP, err);

               CurrMMTime = (MMTIME) mp2;
               InkTimedInk ( hwndInk, (MMTIME) mp2 );

               break;
            }
         }  /* end of switch(currentMode)                               */
         return FALSE;
     }   /* of case MM_MCIPOSITIONCHANGE                                */

     case WM_HSCROLL:
     {
        switch (SHORT2FROMMP(mp2))
        {
           case SB_LINELEFT:
              sAudioLevel = max(0, sAudioLevel - 1);
              break;

           case SB_LINERIGHT:
              sAudioLevel = min(100, sAudioLevel + 1);
              break;

           case SB_PAGELEFT:
              sAudioLevel = max(0, sAudioLevel - 10);
              break;

           case SB_PAGERIGHT:
              sAudioLevel = min(100, sAudioLevel + 10);
              break;

           case SB_SLIDERTRACK:
              sAudioLevel = SHORT1FROMMP(mp2);
              break;

           case SB_ENDSCROLL:
           case SB_SLIDERPOSITION:
              mmsrvSetVolume((USHORT) sAudioLevel);
              return  FALSE;
        }  /* of switch(SHORT2FROMMP...       */

        WinSendDlgItemMsg(hwnd,
                          AUDIOCTL_SCRL,
                          SBM_SETPOS,
                          MPFROMSHORT(sAudioLevel),
                          NULL);

        SetText(hwnd, err, "Volume: ",
                NULL, sAudioLevel,
                (ULONG) NULL, FALSE);
        WinSetDlgItemText(hwnd, AUDIOCTL_VOL, err);
        return  FALSE;

     }  /* of case WM_HSCROLL */


     case WM_INITDLG:
     {
        HWND    hwndVol;

        fAudioAvail = TRUE;

        /* volume control scroll bar
        WinCreateWindow(hwnd,
                        WC_SCROLLBAR,
                        NULL,
                        WS_VISIBLE | WS_SYNCPAINT | SBS_HORZ,
                        (ANWINSIZE / 2),
                        38,
                        (ANWINSIZE - 20) / 2,
                        18,
                        hwnd,
                        HWND_TOP,
                        AUDIOCTL_SCRL,
                        &sbc,
                        NULL);         */

        /* give slider position */
        WinSendDlgItemMsg(hwnd,
                          AUDIOCTL_SCRL,
                          SBM_SETPOS,
                          MPFROMSHORT(sAudioLevel),
                          NULL);

        /* Initial setup for recording */
        currentMode = IDLE_MODE;
        usTotTime = 0;

        mmsrvOpen(hwnd);

        WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_PLAY), FALSE);
        WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_REC), TRUE);
        WinEnableWindow(WinWindowFromID(hwnd, AUDIOCTL_STOP), FALSE);
        return (FALSE);
     }  /* of case WM_CREATE */

     case WM_CLOSE:
     {
        mmsrvClose();
        DestroyTimedInk ( hwndInk );
        fAudioActive = FALSE;
        WinSetFocus(HWND_DESKTOP, hwndClient);

        /*******************************************************************
        *  Enable the Voice & Ink menu item.                               *
        *******************************************************************/
        WinSendMsg(hwndMenu,
                   MM_SETITEMATTR,
                   MPFROM2SHORT(IDM_ANNOT, TRUE),
                   MPFROM2SHORT(MIA_DISABLED, FALSE));

        WinDestroyWindow(hwnd);
        return(FALSE);
     }  /* of case WM_CLOSE */
  }  /* of switch(msg)        */

  return (WinDefDlgProc(hwnd, msg, mp1, mp2));
}

