home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / wps / mmpm2 / tracker / mmpm_aud.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-19  |  17.6 KB  |  676 lines

  1. /* mmpm_audio.c */
  2.  
  3. /* modified by Raymond Garcia for Multimedia OS/2 11/93, 12/93 */
  4. /* note that much of the code used is plucked and pruned from CLOCK from IBM */
  5.  
  6.  
  7. /* based on soundblaster_audio.c */
  8.  
  9. /* modified by David Nichols for this PM MOD player */
  10.  
  11. /* MODIFIED BY Michael Fulbright (MSF) to work with os/2 device driver */
  12.  
  13. /* $Author: steve $
  14.  * $Id: soundblaster_audio.c,v 1.1 1992/06/24 06:24:17 steve Exp steve $
  15.  * $Revision: 1.1 $
  16.  * $Log: soundblaster_audio.c,v $
  17.  * Revision 1.1  1992/06/24  06:24:17  steve
  18.  * Initial revision
  19.  */
  20.  
  21. #define INCL_DOS
  22.  
  23. #include <os2.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26.  
  27. #define  INCL_OS2MM                      /* Required for MCI & MMIO headers   */
  28. #include "os2me.h"
  29.  
  30. #include "defs.h"
  31.  
  32.  
  33. typedef struct pls
  34. {
  35.    ULONG ulCommand;
  36.    ULONG ulOperandOne;
  37.    ULONG ulOperandTwo;
  38.    ULONG ulOperandThree;
  39. } PLAY_LIST_STRUCTURE_T;
  40.  
  41. #define MCI_ERROR_STRING_LENGTH                                 128
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48. #define NUMBER_OF_BUFFERS 4
  49.  
  50. #define MSG_NOTIFICATION 1130
  51. #define MSG_BUFFERS_COMPLETE 1140
  52. #define MSG_BUFFER0_EMPTIED 1131
  53. #define MSG_BUFFER1_EMPTIED 1132
  54. #define MSG_BUFFER2_EMPTIED 1133
  55. #define MSG_BUFFER3_EMPTIED 1134
  56.  
  57.  
  58. HWND host;              /* this is the window graceful enough to accept messages
  59.                            for us and pass them our way.  (dialog box) */
  60.  
  61. int playing = 0;
  62.  
  63.  
  64. MCI_OPEN_PARMS     mciOpenParameters;                /* Open structure.       */
  65.  
  66.  
  67. unsigned char *Buffer[ NUMBER_OF_BUFFERS ];
  68. unsigned long BufferSize;
  69.  
  70.  
  71.  
  72. unsigned current_buffer = 0;
  73. unsigned full_buffers = 0x00;   /* NUMBER_OF_BUFFERS bits flagging full */
  74.                                 /* 1 << buffer_number */
  75.                                 /* these are manipulated by message handler (clear) */
  76.                                 /* and by flush_buffer (set) */
  77. #define ALL_BUFFERS_FULL 0x0F   /* 00001111 */
  78.  
  79. unsigned char *buffer;  /* points to Buffer[current_buffer] */
  80. unsigned buf_index = 0;  /* index within that buffer to add data */
  81.  
  82.  
  83. HEV aBufferHasBeenFreed;         /* event triggered via message to dialog */
  84.  
  85.  
  86.  
  87.  
  88. #define NUMBER_OF_COMMANDS 12
  89.  
  90. /* the following playlist uses the NUMBER_OF_BUFFERS buffers as a circular buffer */
  91.  
  92. PLAY_LIST_STRUCTURE_T apltPlayList[ 1 ][ NUMBER_OF_COMMANDS ] =
  93. {
  94.         {
  95.                 /* 0  */  { DATA_OPERATION, 0,0,0 },  /* buffer 0 */
  96.                 /* 1  */  { MESSAGE_OPERATION, 0,MSG_BUFFER2_EMPTIED,0 },
  97.                 /* 2  */  { DATA_OPERATION, 0,0,0 },
  98.                 /* 3  */  { MESSAGE_OPERATION, 0,MSG_BUFFER3_EMPTIED,0 },
  99.                 /* 4  */  { DATA_OPERATION, 0,0,0 },
  100.                 /* 5  */  { MESSAGE_OPERATION, 0,MSG_BUFFER0_EMPTIED,0 },
  101.                 /* 6  */  { DATA_OPERATION, 0,0,0 },
  102.                 /* 7  */  { MESSAGE_OPERATION, 0,MSG_BUFFER1_EMPTIED,0 },
  103.                 /* 8  */  { BRANCH_OPERATION, 0, 0, 0 },  /* branch back to loop */
  104.                 /* 9  */  { MESSAGE_OPERATION, 0,MSG_BUFFERS_COMPLETE,0 },
  105.                 /* 10 */  { EXIT_OPERATION, 0,0,0 }
  106.         }
  107. };
  108.  
  109. #define PlayListData(bufnum) (apltPlayList[0][(bufnum)*2])
  110.  
  111.  
  112.  
  113.  
  114.  
  115. void ReportMMError()
  116. {
  117.       ULONG mciRC;
  118.       static CHAR  acErrorStringBuffer[ MCI_ERROR_STRING_LENGTH ];
  119.  
  120.  
  121.       mciRC =
  122.          mciGetErrorString(
  123.             mciRC,
  124.             (PSZ)&acErrorStringBuffer[ 0 ],
  125.             (USHORT) MCI_ERROR_STRING_LENGTH );
  126.  
  127.       /*
  128.        * Make sure that the retrieving of the error string was successfull.
  129.        */
  130.       if ( mciRC != MCIERR_SUCCESS )
  131.         strcpy( acErrorStringBuffer, "unknown error");
  132.  
  133.       /*
  134.        * Show the error string that was retrieved by the mciGetErrorString
  135.        * MMPM/2 API.
  136.        */
  137.       WinMessageBox(
  138.          HWND_DESKTOP,                  /* Parent handle.                     */
  139.          HWND_DESKTOP,                  /* Owner handle of the message box.   */
  140.          acErrorStringBuffer,           /* String to show in the message box. */
  141.          (PSZ)"Open Waveform Audio Device Error",
  142.          911,                           /* Message Box Id.                    */
  143.          MB_OK | MB_INFORMATION );      /* The style of the message box.      */
  144.  
  145. }  
  146.  
  147.  
  148.  
  149.  
  150.  
  151. /* this function is exported to the main dialog window procedure,
  152.    such that the user dialog passes multimedia (MM_) messages to us */
  153.  
  154. MRESULT resetBuffer( int n )
  155. {
  156.         ULONG scratch;
  157.                         /* erase buffer full flag */
  158.         full_buffers &= ~( 1 << n );
  159.                         /* signal event */
  160.         DosPostEventSem( aBufferHasBeenFreed );   /* start the other process */
  161.         DosResetEventSem( aBufferHasBeenFreed, &scratch ); /* and reset the semaphore */
  162.         return 0;
  163. }
  164.  
  165.  
  166.  
  167. /* the following function has to be called by the host dialog window
  168.     when an MM_MCINOTIFY message appears in the queue */
  169. MRESULT EXPENTRY MM_MCINOTIFY_Handle( USHORT msg, MPARAM mp1, MPARAM mp2 )
  170. {
  171.  
  172.         switch( (long)mp2 )
  173.         {
  174.                 case MSG_BUFFER0_EMPTIED :
  175.                         return resetBuffer( 0 );
  176.  
  177.                 case MSG_BUFFER1_EMPTIED :
  178.                         return resetBuffer( 1 );
  179.  
  180.                 case MSG_BUFFER2_EMPTIED :
  181.                         return resetBuffer( 2 );
  182.  
  183.                 case MSG_BUFFER3_EMPTIED :
  184.                         return resetBuffer( 3 );
  185.         }
  186.  
  187.         return 0;
  188. }
  189.  
  190.  
  191.  
  192.  
  193. void restoreparams()
  194. {
  195.         /* restore audio device to original state */
  196. }
  197.  
  198.  
  199.  
  200.  
  201.  
  202. /* 256th of primary/secondary source for that side. */
  203. static int primary, secondary;
  204.  
  205. void set_mix (int percent)
  206. {
  207.   percent *= 256;
  208.   percent /= 100;
  209.   primary = percent;
  210.   secondary = 512 - percent;
  211. }
  212.  
  213.  
  214. void next_buffer();
  215.  
  216. void output_samples (int left, int right)
  217. {
  218.   if (pref.stereo)
  219.     {
  220.       buffer[buf_index++] = (((left * primary + right * secondary) / 256) + (1 << 15)) >> 8;
  221.       buffer[buf_index++] = (((right * primary + left * secondary) / 256) + (1 << 15)) >> 8;
  222.     }
  223.   else buffer[buf_index++] = (left + right + (1 << 15)) >> 8;
  224.  
  225.   if (buf_index>=BufferSize) next_buffer();
  226. }
  227.  
  228.  
  229.  
  230. void startPlaying()
  231. {
  232.         ULONG mciRC;
  233.  
  234.  
  235.         mciRC = mciSendCommand(
  236.             mciOpenParameters.usDeviceID, /* Device to play the chimes.    */
  237.             MCI_PLAY,                     /* MCI message.                  */
  238.             MCI_NOTIFY,                   /* Flags for the MCI message.    */
  239.             (PVOID) &mciOpenParameters,   /* Parameters for the message.   */
  240.             MSG_NOTIFICATION );           /* Parameter for notify message. */
  241.  
  242.         if (mciRC) ReportMMError();
  243.  
  244.         /* DosBeep( 880, 10 );  /* ############# */
  245.  
  246.         playing = 1;
  247. }
  248.  
  249.  
  250.  
  251. void stopPlaying()
  252. {
  253.         ULONG mciRC;
  254.         mciRC = 
  255.         mciSendCommand(
  256.             mciOpenParameters.usDeviceID, /* Device to play the chimes.    */
  257.             MCI_STOP,                     /* MCI message.                  */
  258.             MCI_NOTIFY,                   /* Flags for the MCI message.    */
  259.             (PVOID) &mciOpenParameters,   /* Parameters for the message.   */
  260.             MSG_NOTIFICATION );           /* Parameter for notify message. */
  261.  
  262.         if (mciRC) ReportMMError();
  263.  
  264.         playing = 0;
  265. }
  266.  
  267.  
  268. void flush_buffer()
  269. {
  270.         /* output buffer to card - done automatically by playlist */
  271.  
  272. }
  273.  
  274.  
  275. void next_buffer (void)
  276. {
  277.  
  278.       /* set correspondent length in playlist to match buf_index */
  279.         PlayListData( current_buffer ).ulOperandTwo =
  280.                 (ULONG)buf_index;
  281.         PlayListData( current_buffer ).ulOperandThree = 0;
  282.                 /* none of buffer has been played */
  283.  
  284.         /* reset the loop counter in the playlist for infinite looping */
  285.         /* USING BRANCH NOT LOOP */
  286.         /* apltPlayList[0][0].ulOperandThree = 0; */
  287.  
  288.  
  289.       /* set current buffer as busy so we don't overwrite it */
  290.         full_buffers |= (1 << current_buffer);
  291.  
  292.  
  293.       /* increment to next buffer */
  294.         current_buffer = (current_buffer + 1) % NUMBER_OF_BUFFERS;
  295.         buffer = Buffer[current_buffer];
  296.         buf_index = 0;
  297.  
  298.         if (!playing && full_buffers==ALL_BUFFERS_FULL) startPlaying();
  299.  
  300.       /* what?  the next buffer isn't empty?
  301.                 go to a wait state on the event semaphore */
  302.         while ( (1<<current_buffer) & full_buffers ) 
  303.         {
  304.                 DosWaitEventSem( aBufferHasBeenFreed, 5000L ); /* SEM_INDEFINITE_WAIT ); */
  305.                 /* DosBeep( 440, 10 );                /* limit wait to 5 second increments */
  306.         }
  307.  
  308.         /* DosBeep( 880, 2 ); */
  309.         PlayListData( current_buffer ).ulOperandThree = 0;
  310.                 /* none of this buffer has been played either */
  311.  
  312.         full_buffers |= (1 << current_buffer);
  313.         
  314. }
  315.  
  316.  
  317.  
  318.  
  319.  
  320. void openDevice()
  321. {
  322.    ULONG ulOpenFlags = MCI_WAIT | MCI_OPEN_PLAYLIST | MCI_OPEN_TYPE_ID;
  323.                        /* MCI_OPEN_SHAREABLE; */
  324.    ULONG mciRC;                 /* MCI generic return code variable.          */
  325.  
  326.  
  327.    /*
  328.     * Open the correct waveform device for the chimes with the MCI_OPEN
  329.     * message to MCI.
  330.     */
  331.    mciOpenParameters.pszDeviceType    = (PSZ)
  332.       MAKEULONG ( MCI_DEVTYPE_WAVEFORM_AUDIO, 1 );
  333.  
  334.    /*
  335.     * The address of the buffer containing the chime waveform file.
  336.     */
  337.    mciOpenParameters.pszElementName =
  338.                (PSZ)&apltPlayList[ 0 ][ 0 ];
  339.  
  340.    /*
  341.     * Initialize the MCI_OPEN_PARMS data structure with host
  342.     * as callback handle for MM_MCIPASSDEVICE, then issue the MCI_OPEN
  343.     * command with the mciSendCommand function.  No alias is used.
  344.     */
  345.    mciOpenParameters.hwndCallback  = host;
  346.    mciOpenParameters.pszAlias      = (CHAR) NULL;
  347.  
  348.    /*
  349.     * Open the waveform file in the playlist mode.
  350.     */
  351.    mciRC =
  352.       mciSendCommand(
  353.          0,                           /* We don't know the device yet.        */
  354.          MCI_OPEN,                    /* MCI message.                         */
  355.          ulOpenFlags,                 /* Flags for the MCI message.           */
  356.          (PVOID) &mciOpenParameters,  /* Parameters for the message.          */
  357.          0 );                         /* Parameter for notify message.        */
  358.  
  359.    if ( mciRC != 0 )
  360.    {
  361.         ReportMMError();
  362.    }
  363.  
  364. }
  365.  
  366.  
  367.  
  368. void flush_DMA_buffers();
  369.  
  370. void setupPlayList( unsigned short DMAbuffersize )
  371. {
  372.         int i=0;
  373. /*         DMAbuffersize = 32;     /* 931219 */
  374.  
  375.         BufferSize = DMAbuffersize * 1024L;
  376.  
  377.         for( ; i<NUMBER_OF_BUFFERS; i++ )
  378.         {
  379.                 Buffer[i] = malloc( BufferSize );
  380.                 PlayListData( i ).ulOperandOne = (ULONG)Buffer[i];
  381.                 PlayListData( i ).ulOperandTwo = (ULONG)BufferSize;
  382.                 PlayListData( i ).ulOperandThree = 0;
  383.         }
  384.  
  385.         flush_DMA_buffers();
  386. }
  387.  
  388.  
  389. int setupSampleRate( int frequency )
  390. {
  391.    MCI_WAVE_SET_PARMS mwspWaveFormParameters;   /* Waveform parameters.       */
  392.    ULONG              ulRC;                     /* Return code.               */
  393.  
  394.    /*
  395.     * Fill the structure with zeros.
  396.     */
  397.    memset( &mwspWaveFormParameters,            /* Object to fill with zeros.  */
  398.            0,                                  /* Value to place in object.   */
  399.            sizeof( mwspWaveFormParameters ) ); /* How many zeros's to use.    */
  400.  
  401.    /*
  402.     * Set structure values for the MCI_SET.
  403.     */
  404.    mwspWaveFormParameters.ulSamplesPerSec =
  405.       (ULONG)frequency;
  406.    mwspWaveFormParameters.usBitsPerSample =
  407.       8;                /*  for now.  16 later.  then who knows. */
  408.    mwspWaveFormParameters.usChannels     =
  409.       pref.stereo? 2 : 1;
  410.    mwspWaveFormParameters.ulAudio        =
  411.       MCI_SET_AUDIO_ALL;
  412.  
  413.    /*
  414.     * Set the number of channels.
  415.     */
  416.    ulRC =
  417.       mciSendCommand(
  418.          mciOpenParameters.usDeviceID,
  419.          MCI_SET,
  420.          MCI_WAIT | MCI_WAVE_SET_CHANNELS,
  421.          (PVOID) &mwspWaveFormParameters,
  422.          0 );
  423.  
  424.    if (ulRC) ReportMMError();
  425.  
  426.    /*
  427.     * Set the samples per second for the waveform file.
  428.     */
  429.    ulRC =
  430.       mciSendCommand(
  431.          mciOpenParameters.usDeviceID,
  432.          MCI_SET,
  433.          MCI_WAIT | MCI_WAVE_SET_SAMPLESPERSEC,
  434.          (PVOID) &mwspWaveFormParameters,
  435.          0 );
  436.  
  437.    if (ulRC) ReportMMError();
  438.  
  439.    /*
  440.     * Set the bits per second for the waveform file.
  441.     */
  442.    ulRC =
  443.       mciSendCommand(
  444.          mciOpenParameters.usDeviceID,
  445.          MCI_SET,
  446.          MCI_WAIT | MCI_WAVE_SET_BITSPERSAMPLE,
  447.          (PVOID) &mwspWaveFormParameters,
  448.          0 );
  449.  
  450.    if (ulRC) ReportMMError();
  451.  
  452.    return frequency;
  453. }
  454.  
  455.  
  456. void createSemaphore()
  457. {
  458.    DosCreateEventSem(
  459.       (PSZ) NULL,                         /* No semaphore Name. Do not share. */
  460.       &aBufferHasBeenFreed,               /* Semaphore Handle.                */
  461.       (ULONG) NULL,                       /* Creation attributes.             */
  462.       (BOOL32) FALSE );                   /* State of semaphore.              */
  463.  
  464.    if (!aBufferHasBeenFreed) 
  465.       WinMessageBox(
  466.          HWND_DESKTOP,                  /* Parent handle.                     */
  467.          HWND_DESKTOP,                  /* Owner handle of the message box.   */
  468.          (PSZ)"unable to open event semaphore",           /* String to show in the message box. */
  469.          (PSZ)"Error",
  470.          911,                           /* Message Box Id.                    */
  471.          MB_OK | MB_INFORMATION );      /* The style of the message box.      */
  472. }
  473.  
  474.  
  475. int open_audio(int frequency, unsigned short DMAbuffersize)
  476. {
  477.   /* DMAbuffersize is in kilobytes */
  478.  
  479.    createSemaphore();
  480.    setupPlayList( DMAbuffersize );
  481.    openDevice( );
  482.    return setupSampleRate( frequency );
  483. }  
  484.  
  485.  
  486.  
  487. void waitForFinished()
  488. {
  489.         if (!playing) return;
  490.  
  491.         if (buf_index) {        /* throw out any ending sound */
  492.                 next_buffer();
  493.                 full_buffers &= ~(1 << current_buffer); /* leave this empty */
  494.         }
  495.  
  496.         while ( full_buffers )
  497.         {
  498.                 DosWaitEventSem( aBufferHasBeenFreed, 5000L ); /* SEM_INDEFINITE_WAIT ); */
  499.                 /* DosBeep( 440, 10 );                /* limit wait to 5 second increments */
  500.         }
  501. }
  502.  
  503. void flush_DMA_buffers()
  504. {
  505.    /* now tell device driver to flush out internal buffers, abort-style */
  506.  
  507.    /* in other words, stop and reset the playlist */
  508.         if (playing) waitForFinished(), stopPlaying();
  509.  
  510.         current_buffer = 0;
  511.         buffer = Buffer[0];
  512.         full_buffers = 1;       /* set this one so we don't overwrite */
  513.         buf_index = 0;
  514. }
  515.  
  516.  
  517. void close_audio (void)
  518. {
  519.         ULONG ulRC;
  520.         if (playing) stopPlaying();
  521.  
  522.         ulRC = mciSendCommand(
  523.                mciOpenParameters.usDeviceID, /* Device to play the chimes.    */
  524.                MCI_CLOSE,                    /* MCI message.                  */
  525.                MCI_WAIT,                     /* Flags for the MCI message.    */
  526.                (ULONG) NULL,                 /* Parameters for the message.   */
  527.                (ULONG) NULL );               /* Parameter for notify message. */
  528.         if (ulRC) ReportMMError();
  529. }
  530.  
  531.  
  532.  
  533.  
  534.  
  535.  
  536.  
  537.  
  538. /* the following function has to be spliced into WM_INITDLG handling : */
  539. void MM_Set_Host( HWND hwnd )
  540. {
  541.         host = hwnd;
  542. }
  543.  
  544.  
  545.  
  546.  
  547.  
  548.  
  549.  
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556.  
  557.  
  558.  
  559.  
  560.  
  561.  
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568.  
  569.  
  570. #if 0
  571.  
  572. int fixparams = 0;
  573. int filterout;
  574. int filterin;
  575. int filterhi;
  576.  
  577. /* 256th of primary/secondary source for that side. */
  578. static int primary, secondary;
  579.  
  580. void restoreparams()
  581. {
  582.  
  583.         /* restore audio device to original state */
  584.  
  585. }
  586.  
  587. void set_mix (int percent)
  588. {
  589.   percent *= 256;
  590.   percent /= 100;
  591.   primary = percent;
  592.   secondary = 512 - percent;
  593. }
  594.  
  595. int open_audio(int frequency, unsigned short DMAbuffersize)
  596. {
  597.   /* DMAbuffersize is in kilobytes */
  598.  
  599.   trackerror(1, "Error opening audio device SBDSP$", FATAL_ERROR);
  600.  
  601.   /* pref.stereo? can we open stereo? */
  602.  
  603.   if (status !=0) pref.stereo=FALSE;
  604.  
  605.  
  606.  
  607.   if (pref.stereo) frequency *= 2;  /* XXX Stereo takes twice the speed */
  608.                 /* do we need twice frequency for MMOS2? */
  609.  
  610.   if (frequency == 0) frequency = -1;  /* read current frequency from driver */
  611.  
  612. /*  buffer = malloc (sizeof (SAMPLE) * frequency);    /* Stereo makes x2 */
  613. /*  buf_index = 0; */
  614.  
  615.   if (pref.stereo) return (frequency / 2);
  616.   else return (frequency);
  617. }
  618.  
  619. void output_samples (int left, int right)
  620. {
  621.   if (pref.stereo)
  622.     {
  623.       buffer[buf_index++] = (((left * primary + right * secondary) / 256) + (1 << 15)) >> 8;
  624.       buffer[buf_index++] = (((right * primary + left * secondary) / 256) + (1 << 15)) >> 8;
  625.     }
  626.   else buffer[buf_index++] = (left + right + (1 << 15)) >> 8;
  627. }
  628.  
  629. void flush_buffer (void)
  630. {
  631.   ULONG numread, status;
  632.  
  633.   status = DosWrite(hAudio, buffer, buf_index, &numread);
  634.    if (status != 0)
  635.    {
  636.       char buf[80];
  637.  
  638.       sprintf(buf, "Error writing to audio device: %d, tried to write: %d, wrote: %d", status, buf_index, numread);
  639.       trackerror (1, buf, FATAL_ERROR);
  640.    }
  641.   if (numread != buf_index)
  642.    {
  643.       char buf[80];
  644.  
  645.       sprintf(buf, "DosWrite mismatch, buf_index: %d, numread: %d", buf_index, numread);
  646.       trackerror(1, buf, NONFATAL_ERROR);
  647.    }      
  648.   buf_index = 0;
  649. }
  650.  
  651. void flush_DMA_buffers()
  652. {
  653.   ULONG status, datlen, parlen;
  654.  
  655.   /* now tell device driver to flush out internal buffers */
  656.   parlen=0;
  657.   datlen=0;
  658.   status=DosDevIOCtl(hAudio, DSP_CAT, DSP_IOCTL_FLUSH,
  659.                     NULL, 0, &parlen, NULL, 0, &datlen);
  660.    if (status != 0)
  661.    {
  662.       char buf[80];
  663.  
  664.       sprintf(buf, "Error flushing DMA buffers: %d", status);
  665.       trackerror(1, buf, NONFATAL_ERROR);
  666.    }
  667. }
  668.  
  669. void close_audio (void)
  670. {
  671.    DosClose(hAudio);
  672. }
  673.  
  674.  
  675. #endif
  676.