#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntddbeep.h>
#include <windows.h>
#include "insignia.h"
#include "host_def.h"
/*
 * VPC-XT Revision 2.0
 *
 * Title	: sg_sound.c
 *
 * Description	: This module provides functions to control sound.  The 
 *		  functions are defined:
 *		
 *		  host_ring_bell(duration)
 *		  host_alarm(duration)
 *		  host_timer2_waveform(delay,lowclocks,hiclocks,lohi,repeat)
 *		  host_enable_timer2_sound()
 *		  host_disable_timer2_sound()
 *
 * Author	: 
 *
 * Notes	: 
 */

#include "xt.h"
#include "config.h"
#include "debug.h"
#include "error.h"
#include <stdio.h>
#include "trace.h"
#include "video.h"
#include "debug.h"


IMPORT ULONG GetPerfCounter(VOID);


HANDLE hBeepDevice = 0;
ULONG FreqT2    = 0;
BOOL  PpiState  = FALSE;
BOOL  T2State   = FALSE;
ULONG LastPpi   = 0;
ULONG FreqPpi   = 0;
ULONG ET2TicCount=0;
ULONG PpiCounting  = 0;




// human frequency audible sound range
#define AUDIBLE_MIN 10
#define AUDIBLE_MAX 20000
#define CLICK       100

VOID LazyBeep(ULONG Freq, ULONG Duration);
void PulsePpi(void);

/*============================================================================

	host_alarm - ring bell irrespective of configuration (used on keyboard
	buffer overflow for example).

=============================================================================*/

void host_alarm(duration)
long int duration;
{
MessageBeep(MB_OK);
}

/*========================================================================

	host_ring_bell - ring bell if configured (used by video on output
	of ^G, for example).

=========================================================================*/

void host_ring_bell(duration)
long int duration;
{
if( host_runtime_inquire(C_SOUND_ON))
   {
   host_alarm(duration);
   }
}



VOID InitSound( BOOL bInit)
{
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING NameString;
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatus;


    if (!bInit) {
        LazyBeep(0L, 0L);
        if (hBeepDevice)
            CloseHandle(hBeepDevice);
        return;
        }

    RtlInitUnicodeString( &NameString, DD_BEEP_DEVICE_NAME_U );
    InitializeObjectAttributes( &ObjectAttributes,
                                &NameString,
                                0,
                                NULL,
                                NULL );

    Status = NtCreateFile( &hBeepDevice,
                           FILE_READ_DATA | FILE_WRITE_DATA,
                           &ObjectAttributes,
                           &IoStatus,
                           NULL,
                           0,
                           FILE_SHARE_READ | FILE_SHARE_WRITE,
                           FILE_OPEN_IF,
                           0,
                           (PVOID) NULL,
                           0 );

      if (!NT_SUCCESS( Status )) {
#ifndef PROD
          printf("NTVDM: OpenBeepDevice Status=%lx\n",Status);
#endif
          hBeepDevice = 0;
          }

}


/*
 * LazyBeep -
 * Calls Beep Device Driver asynchronously
 *
 * Acceptable parameters are:
 *   (Freq,Dur)            Action
 *   (0,0)               - stop sound
 *   (nonzero, INFINITE) - play a freq
 *
 * not multithreaded safe
 *
 */
VOID LazyBeep(ULONG Freq, ULONG Duration)
{
  static ULONG LastFreq = 0;
  static ULONG LastDuration = 0;

  IO_STATUS_BLOCK     IoStatus;
  BEEP_SET_PARAMETERS bps;

  if ((Freq != LastFreq || Duration != LastDuration)
      && hBeepDevice)
     {
      bps.Frequency = Freq;
      bps.Duration  = Duration;

         //
         // If the duration is < 10 ms, then we assume sound is being
         // off so remember the state as 0,0 so that we won't turn it
         // off again.
         //
      if (Duration < 10) {
         LastFreq = 0;
         LastDuration = 0;
         }
      else {
         LastFreq      = Freq;
         LastDuration  = Duration;
         }

      // should this be synchronous\asynch file io ????
      NtDeviceIoControlFile( hBeepDevice,
                             NULL,
                             NULL,
                             NULL,
                             &IoStatus,
                             IOCTL_BEEP_SET,
                             &bps,
                             sizeof(bps),
                             NULL,
                             0 );
      }
}


/*
 *  PlaySound
 */
void PlaySound(BOOL bPulsedPpi)
{
  if (PpiState && T2State && FreqT2) {
      LazyBeep(FreqT2, INFINITE);
      }
  else if (FreqPpi > AUDIBLE_MIN) {
      LazyBeep(FreqPpi,INFINITE);
      }
  else if (bPulsedPpi && PpiCounting) {
      LazyBeep(CLICK,1);
      }
  else {
      LazyBeep(0,0);
      }
}




/*
 *  host_timer2_waveform - output specified sound waveform
 *
 */
void host_timer2_waveform(int delay,
                          ULONG loclocks,
                          ULONG hiclocks,
                          int lohi,
                          int repeat)
{
    ULONG ul;

    if (loclocks == INFINITE || hiclocks == INFINITE) {
        FreqT2 = 0;
        }
    else {
        ul  = loclocks + hiclocks;
        if (!ul)
            ul++;
        FreqT2 = 1193180/ul;

        if (FreqT2 >= AUDIBLE_MAX) {
            hiclocks = INFINITE;
            FreqT2 = 0;
            }
        else if (FreqT2 <= AUDIBLE_MIN) {
            loclocks = INFINITE;
            FreqT2 = 0;
            }
        }

    PlaySound(FALSE);
}



/*
 *  Updates the hosts Ppi sound state
 */
void HostPpiState(BYTE PortValue)
{
   BOOL   bPpi;


   T2State = PortValue & 1 ? TRUE: FALSE;
   bPpi = PortValue & 2 ? TRUE: FALSE;

   if (bPpi != PpiState) {
       PpiState = bPpi;
       if (PpiState) {
          PulsePpi();
          }

       PlaySound(PpiState);
       }
}




void PulsePpi(void)
{
    static ULONG PpiTicStart=0;
    static ULONG PpiCycles  =0;
    ULONG  ul,Elapsed;
    ULONG  PrevTicCount;


    PrevTicCount = ET2TicCount;
    ET2TicCount = GetTickCount();
    Elapsed = ET2TicCount > PrevTicCount
                  ? ET2TicCount - PrevTicCount
                  : 0xFFFFFFFF - ET2TicCount + PrevTicCount;

    if (Elapsed > 200) {
        if (PpiCounting) {
            PpiCounting = 0;
            LastPpi     = 0;
            FreqPpi     = 0;
            }
        return;
        }


    if (!PpiCounting) {
        PpiCounting  = GetPerfCounter();
        PpiCycles    = 0;
        LastPpi      = 0;
        FreqPpi      = 0;
        PpiTicStart  = ET2TicCount;
        return;
        }

    if (PpiTicStart + 200 >= ET2TicCount) {
        PpiCycles++;
        return;
        }


    ul = GetPerfCounter();
    Elapsed = ul >= PpiCounting
               ? ul - PpiCounting
               : 0xFFFFFFFF - PpiCounting + ul;
    if (!Elapsed)   // insurance!
        Elapsed++;
    PpiCounting = ul;
    PpiTicStart = ET2TicCount;

    /*
     *  Calculate the new avergae Ppi, rounding off to keep
     *  signal from wavering.
     */
    ul = (10000 * PpiCycles)/Elapsed;
    if ((ul & 0x0f) > 7)
        ul += 0x10;
    ul &= ~0x0f;
    ul += 0x10;   // fudge factor

    if (!LastPpi)
        LastPpi = ul;
    if (!FreqPpi)
        FreqPpi = LastPpi;

    /*
     * New Average Ppi is derived from previous AveragePpi,
     * plus last Ppi sample count plus current Ppi sample
     * count to get a frequency which has minimal variation
     * and at the same time responsive to change in the
     * apps pulse rate.
     */
    FreqPpi = ((FreqPpi << 2) + LastPpi + ul)/6;
    if ((FreqPpi & 0x0f) > 7)
        FreqPpi += 0x10;
    FreqPpi &= ~0x0f;

    LastPpi = ul;
    PpiCycles = 0;

}



/*============================================================

Function:	PlayContinuousTone()
Called by:      The SoftPC timer.

==============================================================*/

void PlayContinuousTone(void)
{
   ULONG Elapsed;

   if (PpiCounting) {
       Elapsed = GetTickCount();
       Elapsed = Elapsed > ET2TicCount ? Elapsed - ET2TicCount
                             : 0xFFFFFFFF - ET2TicCount + Elapsed;
       if (Elapsed > 200) {
           PpiCounting = 0;
           LastPpi     = 0;
           FreqPpi     = 0;
           }
       }

   PlaySound(FALSE);
}
