{*******************************************************}
{                                                       }
{       nrCommLib Communication Library for Delphi      }
{       TnrCustomWave class (VCL)                       }
{       Copyright (c) 1997-2011 Roman Novgorodov        }
{       v9.27                                           }
{       last update: 18 Dec 2006                        }
{                                                       }
{       e-mail:noro@deepsoftware.com                    }
{       DeepSoftware                                    }
{       http://www.deepsoftware.com/                    }
{                                                       }
{*******************************************************}
{$I nrcommlib.inc}
unit nrwavebase;
interface
uses  nrclasses,nrcommtapi,nrwin32, windows,mmsystem, messages,
      classes,sysutils;

Const
  //type of error codes for TnrWave

  ntecVoiceOut          = 10;
  ntecVoiceIn           = 11;
  ntecLineNotOpened     = 12;

  ntecWavOutOpen        = 14;
  ntecWavFileOpen       = 15;
  ntecWavFileBad        = 16;
  ntecWavWhileBusy      = 17;
  ntecWavInOpen         = 18;
  ntecWavTAPIAssign     = 13;
  ntecWavPlayHandle     = 19;
  ntecWavRecHandle      = 20;

  nteVoiceOut           = 'nrWave Error: Device does not support Voice output';
  nteVoiceIn            = 'nrWave Error: Device does not support Voice input';
  nteLineNotOpened      = 'nrWave Error: Voice failes because line was not open';
  nteWavOutOpen         = 'nrWave Error: Device does not open wav output';
  nteWavFileOpen        = 'nrWave Error: Open WAV file failed';
  nteWavFileBad         = 'nrWave Error: Bad format of WAV file';
  nteWavWhileBusy       = 'nrWave Error: Wav output device is busy already';
  nteWavInOpen          = 'nrWave Error: Wav input device not opened';
  nteWavTAPIAssign      = 'nrWave Error: TnrCommTAPI not attached.';
  nteWavPlayHandle      = 'nrWave Error: Error in OnBufferPlay event.';
  nteWavRecHandle       = 'nrWave Error: Error in OnBufferRecord event.';

Type

  TWaveBufferEvent     = procedure (Sender:TObject; Buffer:pointer; var Size:integer; var Handled:boolean) of object;

  TWavThread = class(TThread)
  private
    Param:cardinal;
    Finished:boolean;
    OnPulse:TNotifyEvent;
    procedure DoPulse;
  protected
    procedure Execute; override;
    procedure Terminate;
  end;

  TWaveOptions=set of (woSynchro,woMapping,woEventLast);

  TnrCustomWave = class(TnrCustomTAPIProcessor)
  private
    fRecOptions: TWaveOptions;
    fPlayOptions: TWaveOptions;
    wavInHdr1:TWaveHdr;
    wavInHdr2:TWaveHdr;
    //wavInHdrCurrent: PWaveHdr;
    thrdIn:TWavThread;
    fAfterPlay: TNotifyEvent;
    fAfterRecord: TNotifyEvent;
//    flStopping:boolean;
    fRecMaxSec: cardinal;
    fRecHz: cardinal;
    FRecDSecBlock: cardinal;
    fRecBits: cardinal;
    fRecChannels: cardinal;
    fPlaySecBlock: cardinal;
    fBlockOutSize:cardinal;
    fBlockRecSize:cardinal;
    fMaxRecSize:cardinal;
//    ptrCacheRec:pointer;
//    fBytesInCacheRec:cardinal;
    fSaveTAPIState:boolean;
    fOnBufferRecord: TWaveBufferEvent;
    fOnBufferPlay: TWaveBufferEvent;
    function GetPlayVolume: integer;
    function GetRecVolume: integer;
    procedure SetPlayVolume(const Value: integer);
    procedure SetRecVolume(const Value: integer);
    function GetAfterPlay: TNotifyEvent;
    function GetAfterRecord: TNotifyEvent;
    function GetPlayOptions: TWaveOptions;
    function GetPlaySecBlock: cardinal;
    function GetRecBits: cardinal;
    function GetRecChannels: cardinal;
    function GetRecDSecBlock: cardinal;
    function GetRecHz: cardinal;
    function GetRecMaxSec: cardinal;
    function GetRecOptions: TWaveOptions;
    procedure SetAfterPlay(const Value: TNotifyEvent);
    procedure SetAfterRecord(const Value: TNotifyEvent);
    procedure SetPlayOptions(const Value: TWaveOptions);
    procedure SetPlaySecBlock(const Value: cardinal);
    procedure SetRecBits(const Value: cardinal);
    procedure SetRecChannels(const Value: cardinal);
    procedure SetRecDSecBlock(const Value: cardinal);
    procedure SetRecHz(const Value: cardinal);
    procedure SetRecMaxSec(const Value: cardinal);
    procedure SetRecOptions(const Value: TWaveOptions);
    function GetHandleIn: integer;
    function GetHandleOut: integer;
    function GetIsPlaying: boolean;
    function GetIsRecording: boolean;

  protected
    thrdOut:TWavThread;
    csPlay:TRTLCriticalSection;//RTL_CRITICAL_SECTION;
    csRecord:TRTLCriticalSection;//RTL_CRITICAL_SECTION;
    fHandleIn: integer;
    fHandleOut: integer;
    fIsPlaying: boolean;
    fIsRecording: boolean;
    wavOutFmt:TWAVEFORMATEX;
    wavInFmt:TWAVEFORMATEX;
    fTotalOutSize:cardinal;
    fPlayedSize:cardinal;
    fTotalRecSize:cardinal;
    wavOutHdr:TWaveHdr;
    wavOutHdr2:TWaveHdr;

    procedure DoMediumClosed; override;
    procedure DoMediumOpened; override;

    procedure PlayStart; virtual;
    procedure PlayStop; virtual;
    function OpenPlaySrc:boolean; virtual; abstract;
    procedure ClosePlaySrc; virtual;

    procedure BlockPlay(Sender:TObject); virtual;
    function BlockRead(P:pointer;MaxSz:integer):integer; virtual;
    function BlockPlayOut(pW:PWaveHdr):integer; virtual;

    procedure DoAfterPlay(Sender:TObject); virtual;
    procedure ClosePlay; virtual;
    procedure AllocPlayBuffers; virtual;
    procedure FreePlayBuffers;  virtual;

    procedure RecordStart; virtual;
    procedure RecordStop; virtual;
    function OpenRecDest:boolean; virtual;
    procedure CloseRecDest; virtual;
    procedure BlockRec(Sender:TObject); virtual;
    procedure BlockWrite(P:pointer;Sz:integer); virtual;
    procedure DoAfterRec(Sender:TObject); virtual;
    procedure CloseRec; virtual;
    procedure AllocRecBuffers; virtual;
    procedure FreeRecBuffers;  virtual;

    function GetPlayDeviceId:integer;
    function GetRecDeviceId:integer;

    procedure SaveTAPIState;
    procedure RestoreTAPIState;

    function IsPlayHandled(Buffer:pointer; var Sz:integer):boolean;
    function IsRecordHandled(Buffer:pointer; var Sz:integer):boolean;

    property HandleOut:integer read GetHandleOut;
    property HandleIn:integer read GetHandleIn;
    property PlayOptions:TWaveOptions read GetPlayOptions write SetPlayOptions;
    property RecOptions:TWaveOptions read GetRecOptions write SetRecOptions;
    property IsPlaying:boolean read GetIsPlaying;
    property IsRecording:boolean read GetIsRecording;
    property AfterPlay:TNotifyEvent read GetAfterPlay write SetAfterPlay;
    property AfterRecord:TNotifyEvent read GetAfterRecord write SetAfterRecord;
    property RecVolume:integer read GetRecVolume write SetRecVolume;
    property PlayVolume:integer read GetPlayVolume write SetPlayVolume;

    property RecChannels:cardinal read GetRecChannels write SetRecChannels;
    property RecBits:cardinal read GetRecBits write SetRecBits;
    property RecHz:cardinal read GetRecHz write SetRecHz;
    property RecDSecBlock:cardinal read GetRecDSecBlock write SetRecDSecBlock;
    property RecSecMax:cardinal read GetRecMaxSec write SetRecMaxSec;
    property PlaySecBlock:cardinal read GetPlaySecBlock write SetPlaySecBlock;
    property OnBufferPlay:TWaveBufferEvent read fOnBufferPlay write fOnBufferPlay;
    property OnBufferRecord:TWaveBufferEvent read fOnBufferRecord write fOnBufferRecord;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

implementation

function GetWaveErrorString(const Code:integer; flOut:boolean):string;
var p:PChar;
begin
  p:=Pointer(LocalAlloc(LPTR,MAXERRORLENGTH));
  if flOut
   then waveOutGetErrorText(Code,p,MAXERRORLENGTH-1)
   else waveInGetErrorText(Code,p,MAXERRORLENGTH-1);
  Result:=String(p);
  LocalFree(DWORD(P))
end;

{ TnrCustomWave }

constructor TnrCustomWave.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  InitializeCriticalSection(csPlay);
  InitializeCriticalSection(csRecord);
  fRecHz:=8000;
  fRecBits:=16;
  fRecChannels:=1; // mono
  fRecDSecBlock:=1; // buffer time for recording in seconds
  fPlaySecBlock:=1;
  fRecMaxSec:=20;  // max seconds for record
  fRecOptions:=[woMapping];
  fPlayOptions:=[woMapping];
end;

destructor TnrCustomWave.Destroy;
begin
  fAfterPlay:=nil;
  fAfterRecord:=nil;
  PlayStop;
  RecordStop;
  DeleteCriticalSection(csRecord);
  DeleteCriticalSection(csPlay);
  inherited Destroy;
end;

procedure TnrCustomWave.DoMediumClosed;
begin
  inherited;
  Logging(dlDetail,'Medium CLOSED!!!');
  PlayStop;
  RecordStop
end;

procedure TnrCustomWave.DoMediumOpened;
begin
  inherited;
  Logging(dlDetail,'Medium OPENED!!!');
end;

// Playing ....

function TnrCustomWave.GetPlayDeviceId: integer;
begin
  Result:=0;
  if Assigned(nrCommTAPI) then begin
    if nrCommTAPI.DeviceModem.VoiceOut
       then Result:=nrCommTAPI.DeviceModem.VoiceOutId;
  end else begin

  end;
end;

procedure TnrCustomWave.AllocPlayBuffers;
begin
//  fBlockOutSize:=(wavOutFmt.nAvgBytesPerSec * fPlaySecBlock ) div 10;
  fBlockOutSize := wavOutFmt.nAvgBytesPerSec * fPlaySecBlock;
  wavOutHdr.lpData:=Pointer(LocalAlloc(LPTR,fBlockOutSize));
  wavOutHdr.dwBufferLength:=0;
  wavOutHdr.dwFlags:=0;
  wavOutHdr.dwLoops:=0;
  wavOutHdr2.lpData:=Pointer(LocalAlloc(LPTR,fBlockOutSize));
  wavOutHdr2.dwBufferLength:=0;
  wavOutHdr2.dwFlags:=0;
  wavOutHdr2.dwLoops:=0;

  fPlayedSize:=0;
end;

procedure TnrCustomWave.FreePlayBuffers;
begin
  LocalFree(Cardinal(wavOutHdr.lpData));
  LocalFree(Cardinal(wavOutHdr2.lpData));
  wavOutHdr.lpData:=nil;
  wavOutHdr2.lpData:=nil;
  fBlockOutSize:=0;
  fTotalOutSize:=0;
end;

procedure TnrCustomWave.ClosePlay;
begin
  thrdOut.OnPulse:=nil;
  EnterCriticalSection(csPlay);
  waveOutReset(fHandleOut);
  waveOutUnprepareHeader(fHandleOut,@wavOutHdr,SizeOf(TWaveHdr));
  waveOutUnprepareHeader(fHandleOut,@wavOutHdr2,SizeOf(TWaveHdr));
  waveOutClose(fHandleOut);
  fHandleOut:=0;
  ClosePlaySrc;
  FreePlayBuffers;
  LeaveCriticalSection(csPlay);
  fIsPlaying:=False;
  RestoreTAPIState;
end;

procedure TnrCustomWave.PlayStop;
begin
  if fIsPlaying then begin
    thrdOut.OnPulse:=nil;
    thrdOut.FreeOnTerminate:=False;
    ClosePlay;
    thrdOut.WaitFor;
    thrdOut.Free; // wait for end ...
    fIsPlaying:=False;
  end;
end;

procedure TnrCustomWave.DoAfterPlay(Sender: TObject);
begin
  if Assigned(fAfterPlay) then fAfterPlay(Self);
end;

procedure TnrCustomWave.PlayStart;
var di,iR:integer;
begin
  if fIsPlaying then PlayStop;
  fIsPlaying:=False;
  di:=GetPlayDeviceId;
  if not OpenPlaySrc then exit;
  AllocPlayBuffers;

  //preparing WAV output device ...
  if woMapping in fPlayOptions then iR:=WAVE_MAPPED
                               else iR:=0;
  SaveTAPIState;                             
  thrdOut:=TWavThread.Create(True);
  thrdOut.OnPulse:=BlockPlay;
  thrdOut.OnTerminate:=DoAfterPlay;
  fIsPlaying:=True;
  thrdOut.Resume;
  iR:=waveOutOpen(@fHandleOut,di,@wavOutFmt,
                 thrdOut.ThreadID,0,CALLBACK_THREAD or iR);
  if iR<>MMSYSERR_NOERROR then begin
    thrdOut.Terminate;
    ClosePlaySrc;
    FreePlayBuffers;
    DoFatalErrors(ECC_NRWAVE,ntecWavOutOpen,nteWavOutOpen+#13+intToStr(iR)+#13+GetWaveErrorString(iR,True));
    exit;
  end;
//  waveOutPrepareHeader(fHandleOut,@wavOutHdr,SizeOf(TWaveHdr));
  BlockPlay(TObject(@wavOutHdr));
//  waveOutPrepareHeader(fHandleOut,@wavOutHdr2,SizeOf(TWaveHdr));
//  BlockPlay(TObject(@wavOutHdr2));
  if woSynchro in PlayOptions then begin
    thrdOut.WaitFor;
    thrdOut.Free;
  end else thrdOut.FreeOnTerminate:=True;
end;

procedure TnrCustomWave.BlockPlay(Sender: TObject);
label exit_leave;
var wavResult:integer;
    pWh,pWh2:PWaveHdr;
begin
//  EnterCriticalSection(csPlay);
  pWh:=Pointer(Sender);

  if pWh<>nil then begin
    if pWh^.lpData=wavOutHdr.lpData
      then pWh2:=@wavOutHdr2
      else pWh2:=@wavOutHdr;
  end else goto exit_leave;

  if pWh^.dwBufferLength<>0 then
    waveOutUnPrepareHeader(fHandleOut,pWh,SizeOf(TWaveHdr));

  pWh^.dwBufferLength:=BlockRead(pWh^.lpData,fBlockOutSize);

  fPlayedSize:=fPlayedSize+pWh^.dwBufferLength;

  if pWh^.dwBufferLength=0 then begin
    if pWh2^.dwBufferLength=0 then ClosePlay;
    goto exit_leave;
  end;

//  waveOutPrepareHeader(fHandleOut,pWh,SizeOf(TWaveHdr));
//  wavResult:=waveOutWrite(fHandleOut,pWh,SizeOf(TWaveHdr));

  wavResult:=BlockPlayOut(pWh);
  if wavResult<>MMSYSERR_NOERROR then begin
    ClosePlay;
    DoFatalErrors(ECC_NRWAVE,ntecWavOutOpen,nteWavOutOpen+#13+intToStr(wavResult)+#13+GetWaveErrorString(wavResult,True));
    goto exit_leave;
  end;
exit_leave:
//  LeaveCriticalSection(csPlay);
end;

function TnrCustomWave.BlockPlayOut(pW: PWaveHdr): integer;
begin
  pW^.dwFlags:=0;
  pW^.dwLoops:=0;
  waveOutPrepareHeader(fHandleOut,pW,SizeOf(TWaveHdr));
  Result:=waveOutWrite(fHandleOut,pW,SizeOf(TWaveHdr));
end;

//=========================================================
// Recording ....

procedure TnrCustomWave.CloseRec;
begin
  //EnterCriticalSection(csRecord);
  fIsRecording:=False;
  thrdIn.OnPulse:=nil;
  waveInReset(HandleIn);
  waveInClose(HandleIn);
  //sleep(50);
  //sleep(50);


  //LeaveCriticalSection(csRecord);
//  if wavInHdrCurrent^.dwBytesRecorded>0
//    then BlockWrite(wavInHdrCurrent^.lpData,wavInHdrCurrent^.dwBytesRecorded);
  if wavInHdr1.dwBytesRecorded>0
    then BlockWrite(wavInHdr1.lpData,wavInHdr1.dwBytesRecorded)
  else
  if wavInHdr2.dwBytesRecorded>0
    then BlockWrite(wavInHdr2.lpData,wavInHdr2.dwBytesRecorded);

  waveInUnprepareHeader(HandleIn,@wavInHdr1,SizeOf(TWaveHdr));//=MMSYSERR_NOERROR
  waveInUnprepareHeader(HandleIn,@wavInHdr2,SizeOf(TWaveHdr));//=MMSYSERR_NOERROR

  CloseRecDest;
  FreeRecBuffers;
  fHandleIn:=0;
  RestoreTAPIState;
end;

procedure TnrCustomWave.DoAfterRec(Sender: TObject);
begin
  if Assigned(fAfterRecord) then fAfterRecord(Self);
end;

procedure TnrCustomWave.RecordStop;
begin
  if fIsRecording then begin
    //OutPutDebugString('STOP !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
//    thrdIn.OnPulse:=nil;
    thrdIn.FreeOnTerminate:=False;
    //waveInMessage(HandleIn,WIM_CLOSE,0,0);
//    thrdIn.Terminate;
    //OutPutDebugString('terminate thread');
    CloseRec;
    //OutPutDebugString('close WIm ');
    try
    thrdIn.WaitFor;
    //OutPutDebugString('wait for thread');

    thrdIn.Free;
    //OutPutDebugString('free thread');

    except
    end;
    //OutputDebugString('... END REC STOP.');
  end;
end;

procedure TnrCustomWave.AllocRecBuffers;
begin
  fTotalRecSize:=0;
  fBlockRecSize:=(wavInFmt.nAvgBytesPerSec div 10)*RecDSecBlock;
  fMaxRecSize:=wavInFmt.nAvgBytesPerSec*RecSecMax;
  wavInHdr1.lpData:=//Pointer(LocalAlloc(LPTR,fBlockRecSize));

  Pointer(GlobalAlloc(GMEM_FIXED or GMEM_NOCOMPACT or GMEM_NODISCARD,
              fBlockRecSize));
  wavInHdr1.dwBufferLength:=fBlockRecSize;
  wavInHdr1.dwBytesRecorded:=0;
  wavInHdr1.dwFlags:=0;

  wavInHdr2.lpData:=//Pointer(LocalAlloc(LPTR,fBlockRecSize));

  Pointer(GlobalAlloc(GMEM_FIXED or GMEM_NOCOMPACT or GMEM_NODISCARD,
              fBlockRecSize));
  wavInHdr2.dwBufferLength:=fBlockRecSize;
  wavInHdr2.dwBytesRecorded:=0;
  wavInHdr2.dwFlags:=0;

end;

procedure TnrCustomWave.FreeRecBuffers;
begin
  GlobalFree(Cardinal(wavInHdr1.lpData));
  GlobalFree(Cardinal(wavInHdr2.lpData));
  wavInHdr1.lpData:=nil;
  wavInHdr2.lpData:=nil;
  fBlockRecSize:=0;
end;

function TnrCustomWave.GetRecDeviceId: integer;
begin
  Result:=0;
  if Assigned(nrCommTAPI) then
    if nrCommTAPI.DeviceModem.VoiceIn
       then Result:=nrCommTAPI.DeviceModem.VoiceInId;
end;

procedure TnrCustomWave.RecordStart;
var di,iR:integer;
begin
  if fIsRecording then RecordStop;
  fIsRecording:=False;
  di:=GetRecDeviceId;
  if not OpenRecDest then exit;
  AllocRecBuffers;
  //preparing WAV input device ...
  if woMapping in fRecOptions then iR:=WAVE_MAPPED
                              else iR:=0;
  SaveTAPIState;
  thrdIn:=TWavThread.Create(True);
  //thrdIn.Log:=Log;
  thrdIn.OnPulse:=BlockRec;
  thrdIn.OnTerminate:=DoAfterRec;
  thrdIn.Resume;
  iR:=waveInOpen(@fHandleIn,di,@wavInFmt,
                 thrdIn.ThreadID,0,CALLBACK_THREAD or iR);
  if iR<>MMSYSERR_NOERROR then begin
    thrdIn.Terminate;
    CloseRecDest;
    FreeRecBuffers;
    DoFatalErrors(ECC_NRWAVE,ntecWavInOpen,nteWavInOpen+#13+intToStr(iR)+#13+GetWaveErrorString(iR,False));
    exit;
  end;

  waveInPrepareHeader(fHandleIn,@wavInHdr1,sizeof(TWaveHdr));
  waveInPrepareHeader(fHandleIn,@wavInHdr2,sizeof(TWaveHdr));
  waveInAddBuffer(fHandleIn,@wavInHdr1,SizeOf(TWaveHdr));
  waveInAddBuffer(fHandleIn,@wavInHdr2,SizeOf(TWaveHdr));
  fIsRecording:=True;
  waveInStart(fHandleIn);
  if woSynchro in RecOptions then begin
    thrdIn.WaitFor;
    thrdIn.Free;
  end else thrdIn.FreeOnTerminate:=True;
end;

procedure TnrCustomWave.BlockRec(Sender: TObject);
var p:PWaveHdr;
begin
  if Sender=nil then exit;
  p:=PWAVEHDR(Pointer(Sender));
  if (p^.dwBytesRecorded+fTotalRecSize>=fMaxRecSize) or (p^.dwBytesRecorded=0)
    then begin
      CloseRec;
    end
    else begin
//      OutputDebugString(PChar('begin write  ...'+IntToStr(P^.dwBytesRecorded)));
      BlockWrite(P^.lpData,P^.dwBytesRecorded);
      Inc(fTotalRecSize,P^.dwBytesRecorded);
      waveInAddBuffer(fHandleIn,p,SizeOf(TWaveHdr));
    end;
end;

procedure TnrCustomWave.ClosePlaySrc;
begin
  //
end;

procedure TnrCustomWave.CloseRecDest;
begin
  //
end;

function TnrCustomWave.GetPlayVolume: integer;
begin
  Result:=0;
end;

function TnrCustomWave.GetRecVolume: integer;
begin
  Result:=0;
end;

procedure TnrCustomWave.SetPlayVolume(const Value: integer);
begin
  //
end;

procedure TnrCustomWave.SetRecVolume(const Value: integer);
begin
  //
end;

function TnrCustomWave.BlockRead(P: pointer; MaxSz: integer): integer;
begin
  Result:=0;
end;

procedure TnrCustomWave.BlockWrite(P: pointer; Sz: integer);
begin
//
end;

function TnrCustomWave.OpenRecDest: boolean;
begin
  Result:=False;
end;

procedure TnrCustomWave.RestoreTAPIState;
begin
  // close direct com TAPI handle ...
  if Assigned(nrCommTAPI) then begin
    nrCommTAPI.Active:=fSaveTAPIState;
  end;
end;

procedure TnrCustomWave.SaveTAPIState;
begin
  // close direct com TAPI handle ...
  if Assigned(nrCommTAPI) then begin
    fSaveTAPIState:=nrCommTAPI.Active;
    nrCommTAPI.Active:=False;
  end;
end;

function TnrCustomWave.IsPlayHandled(Buffer:pointer; var Sz:integer): boolean;
begin
  Result := False;
  if Assigned(fOnBufferPlay) then
  try
    fOnBufferPlay(Self, Buffer, Sz, Result);
  except
    on e:Exception do
    DoFatalErrors(ECC_NRWAVE,ntecWavPlayHandle,nteWavPlayHandle + e.Message);
  end;
end;

function TnrCustomWave.IsRecordHandled(Buffer:pointer; var Sz:integer): boolean;
begin
  Result := False;
  if Assigned(fOnBufferRecord) then
  try
    fOnBufferRecord(Self, Buffer, Sz, Result);
  except
    on e:Exception do
      DoFatalErrors(ECC_NRWAVE,ntecWavRecHandle,nteWavRecHandle + e.Message);
  end;
end;

function TnrCustomWave.GetHandleIn: integer;
begin
  Result := fHandleIn;
end;

function TnrCustomWave.GetHandleOut: integer;
begin
  Result := fHandleOut;
end;

function TnrCustomWave.GetIsPlaying: boolean;
begin
  Result := fIsPlaying;
end;

function TnrCustomWave.GetIsRecording: boolean;
begin
  Result := fIsRecording;
end;

{ TWavThread }

procedure TWavThread.DoPulse;
begin
  //OutPutDebugString('+++++++++++++++++++++++++++++++++++');
  if Assigned(OnPulse) then OnPulse(TObject(Param));//DoPulse;
  //OutPutDebugString('------------------------------------');
end;

procedure TWavThread.Execute;
var msg:TMsg;
begin
//  PeekMessage(msg,0,WM_USER,WM_USER,PM_NOREMOVE);

  Finished:=False;
  while not Terminated do begin
    if GetMessage(msg,0,0,0) then begin
      param:=msg.lParam;
      case msg.message of
        WOM_DONE,WIM_DATA{,WIM_OPEN}://WOM_OPEN
        if Assigned(OnPulse) then DoPulse;//Synchronize(DoPulse);//}//OnPulse(TObject(msg.lParam));//DoPulse;
        WOM_CLOSE,WIM_CLOSE:begin
          //outputdebugstring('wav_close');
          break; //Synchronize(DoPulse);
        end;
      end;
//      sleep(1000);
    end;
  end;

{  while not Terminated do begin
    if GetMessage(msg,0,0,0) then
      if msg.message = MM_WOM_DONE then begin
        param := msg.lParam;
        DoPulse;
      end;
//    sleep(5);
  end;
 }
  //OutPutDebugString('End thread');
  Finished := True;
end;

procedure TWavThread.Terminate;
begin
  inherited Terminate;
  PostThreadMessage(ThreadId,WOM_CLOSE,0,0);
  //OutPutDebugString('Terminate');
end;

function TnrCustomWave.GetAfterPlay: TNotifyEvent;
begin
  Result := fAfterPlay;
end;

function TnrCustomWave.GetAfterRecord: TNotifyEvent;
begin
  Result := fAfterRecord;
end;

function TnrCustomWave.GetPlayOptions: TWaveOptions;
begin
  Result := fPlayOptions;
end;

function TnrCustomWave.GetPlaySecBlock: cardinal;
begin
  Result := fPlaySecBlock;
end;

function TnrCustomWave.GetRecBits: cardinal;
begin
  Result := fRecBits;
end;

function TnrCustomWave.GetRecChannels: cardinal;
begin
  Result := fRecChannels;
end;

function TnrCustomWave.GetRecDSecBlock: cardinal;
begin
  Result := fRecDSecBlock;
end;

function TnrCustomWave.GetRecHz: cardinal;
begin
  Result := fRecHz;
end;

function TnrCustomWave.GetRecMaxSec: cardinal;
begin
  Result := fRecMaxSec;
end;

function TnrCustomWave.GetRecOptions: TWaveOptions;
begin
  Result := fRecOptions;
end;

procedure TnrCustomWave.SetAfterPlay(const Value: TNotifyEvent);
begin
  fAfterPlay:=Value;
end;

procedure TnrCustomWave.SetAfterRecord(const Value: TNotifyEvent);
begin
  fAfterRecord:=Value;
end;

procedure TnrCustomWave.SetPlayOptions(const Value: TWaveOptions);
begin
  fPlayOptions:=Value;
end;

procedure TnrCustomWave.SetPlaySecBlock(const Value: cardinal);
begin
  fPlaySecBlock:=Value;
end;

procedure TnrCustomWave.SetRecBits(const Value: cardinal);
begin
  fRecBits:=Value;
end;

procedure TnrCustomWave.SetRecChannels(const Value: cardinal);
begin
  fRecChannels:=Value;
end;

procedure TnrCustomWave.SetRecDSecBlock(const Value: cardinal);
begin
  fRecDSecBlock:=Value;
end;

procedure TnrCustomWave.SetRecHz(const Value: cardinal);
begin
  fRecHz:=Value;
end;

procedure TnrCustomWave.SetRecMaxSec(const Value: cardinal);
begin
  fRecMaxSec:=Value;
end;

procedure TnrCustomWave.SetRecOptions(const Value: TWaveOptions);
begin
  fRecOptions:=Value;
end;

end.
