⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unaaudiofeedback.pas

📁 Voice Commnucation Components for Delphi
💻 PAS
字号:

(*
	----------------------------------------------

	  unaAudioFeedback.pas
	  Voice Communicator components version 2.5
	  Audio Feedabck routines

	----------------------------------------------
	  This source code cannot be used without
	  proper permission granted to you as a private
	  person or an entity by the Lake of Soft, Ltd

	  Visit http://lakeofsoft.com/ for details.

	  Copyright (c) 2001, 2005 Lake of Soft, Ltd
		     All rights reserved
	----------------------------------------------

	  created by:
		Lake, 11 Aug 2005

	  modified by:
		Lake, Aug 2005
		Lake, Nov 2005

	----------------------------------------------
*)

{$I unaDef.inc}

unit
  unaAudioFeedback;

interface

uses
  Windows, unaTypes, unaClasses, MMSystem, unaMsAcmClasses;


const
  // feedback class status
  c_stat_afStopped	 = 0;	// feedback was stopped or not started
  c_stat_afActive	 = 1;	// feedback is running OK
  c_stat_afErrorIn	 = 2;	// some error occured with waveIn device, see errorCode for delails
  c_stat_afErrorOut	 = 3;	// some error occured with waveOut device, see errorCode for delails


  //
  c_recordWaveCmd_start	= 1;	// starts recording into a file
  c_recordWaveCmd_stop	= 2;	// stops recording



type
  // --  --
  myWaveOutDevice = class(unaWaveOutDevice)
  private
    f_inProgress: int;
  protected
    function onHeaderDone(header: unaWaveHeader; wakeUpByHeaderDone: bool): bool; override;
  end;


  // --  --
  myWaveHeader = class(unaWaveHeader)
    // this class is only needed to get access to protected methods of unaWaveHeader class
  end;


  //
  // --  audio feedback class --
  //
  unaAudioFeedbackClass = class(unaThread)
  private
    f_errorCode: int;
    f_status: int;
    f_delay: unsigned;
    f_delayAsCount: int;
    //
    f_waveFile: unaRiffStream;
    //
    f_waveIn: unaWaveInDevice;
    f_waveOut: myWaveOutDevice;
    //
    f_waveOutHeaders: array[byte] of myWaveHeader;
    f_silence: pointer;
    //
    f_waveChunksThrownAway: int64;
    f_waveOutLastHeaderIndex: int;
    f_onDA: unaWaveDataEvent;
    //
    procedure setDelay(delay: unsigned);
    function getWaveFormat(): pWAVEFORMATEX;
    function getWaveOut(): unaWaveOutDevice;
    //
    procedure onWaveDataAvailable(sender: tObject; data: pointer; len: unsigned);
  protected
    procedure startIn(); override;
    procedure startOut(); override;
    function execute(threadId: unsigned): int; override;
  public
    procedure AfterConstruction(); override;
    procedure BeforeDestruction(); override;
    //
    function setup(delay: unsigned = 40; inDeviceId: int = int(WAVE_MAPPER); outDeviceId: int = int(WAVE_MAPPER); format: pWAVEFORMATEX = nil): HRESULT;
    function recordWaveCmd(cmd: int; const fileName: wideString = ''): HRESULT;
    //
    property status: int read f_status;
    property errorCode: int read f_errorCode;
    property delay: unsigned read f_delay write setDelay;
    //
    property waveFormat: pWAVEFORMATEX read getWaveFormat;
    //
    property waveIn: unaWaveInDevice read f_waveIn;
    property waveOut: unaWaveOutDevice read getWaveOut; // stupid Delphi compiler canot directly cast f_waveOut as unaWaveOutDevice.
    //
    property onDataAvailable: unaWaveDataEvent read f_onDA write f_onDA;
  end;


implementation


uses
  unaUtils;

{ myWaveOutDevice }

// --  --
function myWaveOutDevice.onHeaderDone(header: unaWaveHeader; wakeUpByHeaderDone: bool): bool;
begin
  if (wakeUpByHeaderDone) then
    dec(f_inProgress);
  //
  result := true;	// no need to call overitten method
end;

{ unaAudioFeedbackClass }

// --  --
procedure unaAudioFeedbackClass.afterConstruction();
begin
  f_status := c_stat_afStopped;
  f_errorCode := 0;
  //
  f_waveIn := unaWaveInDevice.create();
  f_waveOut := myWaveOutDevice.create();
  //
  f_waveIn.assignStream(false, nil);	// remove outgoing stream
  //
  waveIn.onDataAvailable := onWaveDataAvailable;
  //
  inherited;
  //
  setup();	// assign default values
end;

// --  --
procedure unaAudioFeedbackClass.beforeDestruction();
begin
  inherited;	// should stop thread and close devices
  //
  freeAndNil(f_waveIn);
  freeAndNil(f_waveOut);
end;

// --  --
function unaAudioFeedbackClass.execute(threadId: unsigned): int;
begin
  if (0 = f_errorCode) then
    f_status := c_stat_afActive;
    //
  while (not shouldStop) do begin
    //
    sleep(100);
  end;
  //
  result := 0;
end;

// --  --
function unaAudioFeedbackClass.getWaveFormat(): pWAVEFORMATEX;
begin
  result := waveIn.dstFormat;
end;

// --  --
function unaAudioFeedbackClass.getWaveOut(): unaWaveOutDevice;
begin
  result := f_waveOut;
end;

// --  --
procedure unaAudioFeedbackClass.onWaveDataAvailable(sender: tObject; data: pointer; len: unsigned);
var
  header: myWaveHeader;
  res: MMRESULT;
begin
  // check if we can feed outbuffer now
  if (f_waveOut.f_inProgress <= f_delayAsCount) then begin
    //
    // locate unused header
    inc(f_waveOutLastHeaderIndex);
    if (f_waveOutLastHeaderIndex > high(f_waveOutHeaders)) then
      f_waveOutLastHeaderIndex := low(f_waveOutHeaders);	// dirty hack, assuming we will never run out of headers
    //
    header := f_waveOutHeaders[f_waveOutLastHeaderIndex];
    //
    // send audio chunk to waveOut device
    header.rePrepare();
    header.setData(data, len);
    //
    res := f_waveOut.addHeader(header);
    if (mmNoError(res)) then
      inc(f_waveOut.f_inProgress);
    //
  end
  else
    inc(f_waveChunksThrownAway);
  //
  // check if we need to increase the delay
  while (not shouldStop and (f_waveOut.f_inProgress < f_delayAsCount - 2)) do begin
    //
    // send silence to waveOut
    onWaveDataAvailable(sender, f_silence, waveIn.chunkSize);
  end;
  //
  if (enter(20)) then begin
    //
    try
      if (nil <> f_waveFile) then
	f_waveFile.write(data, len);
      //
    finally
      leave();
    end;
  end;
  //
  if assigned(f_onDA) then
    f_onDA(self, data, len);
end;

// --  --
function unaAudioFeedbackClass.recordWaveCmd(cmd: int; const fileName: wideString): HRESULT;
begin
  result := HRESULT(-2);
  //
  if (enter(100)) then begin
    //
    try
      //
      case (cmd) of

	
	c_recordWaveCmd_start: begin
	  //
	  if ((nil <> f_waveFile) and sameStringW(f_waveFile.fileName, fileName)) then begin
	    //
	    result := S_OK;	// already recording into this file
	  end
	  else begin
	    //
	    freeAndNil(f_waveFile);
	    f_waveFile := unaRiffStream.createNew(fileName, waveIn.dstFormat^);
	    //
	    f_waveFile.open();
	    //
	    result := S_OK;
	  end;
	end;


	c_recordWaveCmd_stop: begin
	  //
	  freeAndNil(f_waveFile);
	  //
	  result := S_OK;
	end;

	else
	  result := HRESULT(-1);

      end;
      //
    finally
      leave();
    end;
  end;
end;

// --  --
procedure unaAudioFeedbackClass.setDelay(delay: unsigned);
begin
  f_delay := delay;
  f_delayAsCount := delay div (1000 div c_defChunksPerSecond);
end;

// --  --
function unaAudioFeedbackClass.setup(delay: unsigned; inDeviceId, outDeviceId: int; format: pWAVEFORMATEX): HRESULT;
begin
  stop();	 // just in case
  //
  self.delay := delay;
  waveIn.deviceId := unsigned(inDeviceId);
  waveOut.deviceId := unsigned(outDeviceId);
  //
  if (nil <> format) then
    waveIn.setSampling(format^)
  else
    waveIn.setSampling(22050, 16, 1);
  //
  waveOut.setSampling(waveIn.dstFormat^);
  //
  result := 0;
end;

// --  --
procedure unaAudioFeedbackClass.startIn();
var
  i: int;
  res: MMRESULT;
begin
  // try opening waveIn/Out devices
  res := waveOut.open();
  if (mmNoError(res)) then begin
    //
    // create waveOut headers (need to do that _before_ activating the waveIn
    for i := low(f_waveOutHeaders) to high(f_waveOutHeaders) do begin
      //
      f_waveOutHeaders[i] := myWaveHeader.create(waveOut, waveOut.chunkSize);
      f_waveOutHeaders[i].prepare();
    end;
    //
    f_silence := malloc(waveIn.chunkSize, true, choice(8 = waveIn.dstFormat.wBitsPerSample, $80, int(0)));
    //
    f_waveChunksThrownAway := 0;
    //
    res := waveIn.open();
    if (mmNoError(res)) then begin
      //
      // looks like all devices were activated normally
      f_errorCode := 0;
    end
    else begin
      //
      f_status := c_stat_afErrorIn;
      f_errorCode := res;
    end;
  end
  else begin
    //
    f_status := c_stat_afErrorOut;
    f_errorCode := res;
  end;
  //
  inherited;
end;

// --  --
procedure unaAudioFeedbackClass.startOut();
var
  i: int;
begin
  // close all devices
  waveIn.close();
  //
  // release waveOut headers (need to do that _before_ activating the waveIn
  for i := low(f_waveOutHeaders) to high(f_waveOutHeaders) do begin
    //
    f_waveOutHeaders[i].unprepare();
    freeAndNil(f_waveOutHeaders[i]);
  end;
  //
  waveOut.close();
  //
  mrealloc(f_silence);
  f_waveOut.f_inProgress := 0;
  //
  f_status := c_stat_afStopped;
  //
  recordWaveCmd(c_recordWaveCmd_stop);
  //
  inherited;
end;


end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -