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

📄 unadspdlibpipes.pas

📁 Voice Commnucation Components for Delphi
💻 PAS
📖 第 1 页 / 共 2 页
字号:

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

	  unaDspDLibPipes.pas - DSP DLib pipe components
	  Voice Communicator components version 2.5 Pro

	----------------------------------------------
	  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, 2007 Lake of Soft, Ltd
		     All rights reserved
	----------------------------------------------

	  created by:
		Lake, 15 Mar 2007

	  modified by:
		Lake, Mar 2007

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

{$I unaDef.inc}

unit
  unaDspDLibPipes;

interface

uses
  Windows, unaTypes, unaMsAcmApi,
  unaDspLibH, unaDspLibAutomation,
  Classes, unaVcIDE;

type
  {DP:CLASS
    Abstract wave DSP DLib pipe.
  }
  unaDSPDLibWavePipe = class(unavclInOutWavePipe)
  private
    f_localBuf: pointer;
    f_localBufSize: int;
    //
    f_active: bool;	// this property simply stores current active state
    //
    f_automation: unaDSPLibAutomat;
    //
    f_processDataInPlace: bool;
  protected
    {DP:METHOD
      Converts DSP DLib output float buffer(s) to PCM plain buffer, and returns its size in bytes.
    }
    function convertOutFloat2PCM(out buf: pointer): int; virtual;
    //
    {DP:METHOD
      Returns true if component was activated.
    }
    function isActive(): bool; override;
    {DP:METHOD
      "Activates" the component.
    }
    function doOpen(): bool; override;
    {DP:METHOD
      "Deactivates" the component.
    }
    procedure doClose(); override;
    {DP:METHOD
      Applies new format on DSP automation.
    }
    function applyDeviceFormat(const format: WAVEFORMATEX; isSrc: bool = true): bool; override;
    {DP:METHOD
      Processes new chunk with DSP automation.
    }
    function doWrite(data: pointer; len: unsigned; provider: unavclInOutPipe = nil): int; override;
    {DP:METHOD
      Reads last data processed by DSP automation.
      Not yet implemented.
    }
    function doRead(data: pointer; len: unsigned): unsigned; override;
  public
    constructor Create(owner: tComponent); override;
    procedure AfterConstruction(); override;
    procedure BeforeDestruction(); override;
    //
    {DP:MEHTOD
      DSPDLib automation.
    }
    property automation: unaDSPLibAutomat read f_automation;
    //
  published
    {DP:MEHTOD
      Specifies whether data should be processed by conponent "in place", changing the values in provider's buffer.
      When set to false data will be processed using local buffer (a bit slowly, but does not touch provider's data).
      Must be set to false if provider has more than one consumer.
    }
    property processDataInPlace: bool read f_processDataInPlace write f_processDataInPlace default true;
    {DP:METHOD
      DSP components are usually a format providers -- so this property value was changed to be true by default.
    }
    property isFormatProvider default true;
    {DP:METHOD
      Specifies whether the component would perform any data processing.
    }
    property enableDataProcessing;
  end;


const
  //
  c_defNumBands	= 10;


type
  //
  unavcDSPDLib_freqAssignMode = (unafam_manual, unafam_powerOf2);

  //
  // --  --
  //
  unavclDSPDLibMultiBand = class(unaDSPDLibWavePipe)
  private
    f_numBands: uint;
    f_freqAssignMode: unavcDSPDLib_freqAssignMode;
    //
    procedure setNumBands(value: uint);
    function getFreq(band: uint): dspl_float;
    procedure setFreq(band: uint; const value: dspl_float);
    //
    function allocateNBuf(n: uint): pFloatArray;
    procedure checkFreqMode();
  protected
    f_dsplObj: dspl_handle;
    //
    procedure doSetNumBands(value: uint); virtual;
    //
    {DP:MEHTOD
      Usually number of freq equals number of bands, but some classes may override this.
    }
    function getNumFreq(): int; virtual;
    {DP:MEHTOD
      In addition makes sure frequencises are correct.
    }
    function applyDeviceFormat(const format: WAVEFORMATEX; isSrc: bool = true): bool; override;
  public
    procedure AfterConstruction(); override;
    //
    {DP:MEHTOD
      DSPDLib object.
    }
    property dsplObj: dspl_handle read f_dsplObj;
    {DP:MEHTOD
      Frequency value for a specific band.
    }
    property frequency[band: uint]: dspl_float read getFreq write setFreq;
  published
    {DP:MEHTOD
      Number of bands, 32 max.
    }
    property numBands: uint read f_numBands write setNumBands default c_defNumBands;
    {DP:MEHTOD
      Frequences assigment mode.
    }
    property freqAssignMode: unavcDSPDLib_freqAssignMode read f_freqAssignMode write f_freqAssignMode default unafam_powerOf2;
  end;


  {DP:CLASS
    EQ Pipe.
  }
  TunavclDSPDLibEQ = class(unavclDSPDLibMultiBand)
  private
    //
    function getGain(band: uint): dspl_float;
    procedure setGain(band: uint; const value: dspl_float);
  protected
    //
    procedure doSetNumBands(value: uint); override;
  public
    constructor Create(owner: tComponent); override;
    //
    {DP:MEHTOD
      Gain value for a specific band.
    }
    property gain[band: uint]: dspl_float read getGain write setGain;
  end;


  //
  // pointer to array of raw samples for up to 256 bands
  punaDspBandRawSamples = ^unaDspBandRawSamples;
  unaDspBandRawSamples = array[byte] of pFloatArray;

  //
  {DP:METHOD
    Fired when raw multi-band samples are ready.
  }
  unavclRawSamplesAvailable = procedure(sender: unavclInOutPipe; numSamples, numBands, channel: uint; samples: punaDspBandRawSamples) of object;


  {DP:CLASS
    Multi-band Splitter Pipe.
  }
  TunavclDSPDLibMBSP = class(unavclDSPDLibMultiBand)
  private
    f_bandPT: array[byte] of bool;	// up to 256 bands
    //
    f_localRaw: array[byte] of unaDspBandRawSamples;	// up to 256 channels
    f_localFBuf: array[byte] of pFloatArray;		// up to 256 channels
    f_localFBufSize: array[byte] of int;
    //
    f_onRawSamplesAvail: unavclRawSamplesAvailable;
    //
    function getPT(band: uint): bool;
    procedure setPT(band: uint; value: bool);
  protected
    {DP:METHOD
      Converts DSP DLib output float buffer(s) to PCM plain buffer, and returns its size in bytes.
    }
    function convertOutFloat2PCM(out buf: pointer): int; override;
    {DP:METHOD
      Num of freqs is one less than numBands in MBSP, so we override this function
    }
    function getNumFreq(): int; override;
  public
    constructor Create(owner: tComponent); override;
    procedure AfterConstruction(); override;
    procedure BeforeDestruction(); override;
    //
    {DP:MEHTOD
      Pass-through value for a specific band.
    }
    property passThrough[band: uint]: bool read getPT write setPT;
  published
    {DP:MEHTOD
      MBSP splits the audio into bands only when processDataInPlace is false, so we make it default.
    }
    property processDataInPlace default false;
    //
    {DP:METHOD
      Fired when raw multi-band samples are ready.
    }
    property onRawSamplesAvailable: unavclRawSamplesAvailable read f_onRawSamplesAvail write f_onRawSamplesAvail;
  end;


{DP:METHOD
  Registers VC DSP DLib components in Delphi IDE components palette.
}
procedure Register();


implementation


uses
  unaUtils, unaDspDLib;

var
  //
  // global root - Delphi implementation of DSP Lib
  g_root: unaDspLibAbstract;


{ unaDSPDLibWavePipe }

// --  --
procedure unaDSPDLibWavePipe.AfterConstruction();
begin
  inherited;	// creates PCM format 
  //
  processDataInPlace := true;
  isFormatProvider := true;	// by default
end;

// --  --
function unaDSPDLibWavePipe.applyDeviceFormat(const format: WAVEFORMATEX; isSrc: bool): bool;
begin
  // there is no inheried implementation to call
  //
  result := SUCCEEDED(automation.setFormat(format.nSamplesPerSec, format.wBitsPerSample, format.nChannels));
end;

// --  --
procedure unaDSPDLibWavePipe.BeforeDestruction();
begin
  inherited;
  //
  freeAndNil(f_automation);
  //
  f_localBufSize := 0;
  mrealloc(f_localBuf);
end;

// --  --
function unaDSPDLibWavePipe.convertOutFloat2PCM(out buf: pointer): int;
var
  p, c: uint;
  s: uint;
  nSamples: uint;
  outBuf: pdspl_float;
  f: dspl_float;
  //
  pcmBits: uint;	// local copy of pcmFormat.wBitsPerSample
begin
  result := 0;
  //
  // convert float samples to integer values (if we have to)
  if (0 < pcmFormat.nChannels) then begin
    //
    pcmBits := pcmFormat.wBitsPerSample;
    for c := 0 to pcmFormat.nChannels - 1 do begin
      //
      automation.getOutData(c, outBuf, nSamples);
      //
      if (1 > nSamples) then
	continue;	// no out data in this channel?
      //
      result := nSamples * pcmFormat.nChannels * (1 + (pcmBits - 1) shr 3);
      if (f_localBufSize < result) then begin
	//
	mrealloc(f_localBuf, result);
	f_localBufSize := result;
      end;
      //
      p := c;
      for s := 0 to nSamples - 1 do begin
	//
	f := outBuf^;
	if (f > 1.0) then
	  f := 1.0;
	//
	if (f < -1.0) then
	  f := -1.0;
	//
	case (pcmBits) of

	   8: pArray(f_localBuf)[p] 	    := trunc(f * $FF + $80);

	  16: pSmallIntArray(f_localBuf)[p] := trunc(f * $7FFF);

	  24: pInt32Array(f_localBuf)[p]    := trunc(f * $7FFFFF);

	  32: pFloatArray(f_localBuf)[p]    := f;

	end;
	//
	inc(outBuf);
	inc(p, pcmFormat.nChannels);	// go to next sample in same channel
	//
      end;	// for all samples
    end;      // for all channels
    //
    buf := f_localBuf;
    //
  end;	// 0 < channels
end;

// --  --
constructor unaDSPDLibWavePipe.create(owner: tComponent);
begin
  f_automation := unaDSPLibAutomat.create(g_root);
  //
  inherited;
end;

// --  --
procedure unaDSPDLibWavePipe.doClose();
begin
  inherited;
  //
  f_active := false;
end;

// --  --
function unaDSPDLibWavePipe.doOpen(): bool;
begin
  inherited doOpen();	// it will return false since "device" is nil
  result := true;
  //
  f_active := result;
end;

// --  --
function unaDSPDLibWavePipe.doRead(data: pointer; len: unsigned): unsigned;
begin
  // not yet implemeted
  result := 0;
end;

// --  --
function unaDSPDLibWavePipe.doWrite(data: pointer; len: unsigned; provider: unavclInOutPipe): int;
var
  res: HRESULT;
  buf: pointer;
begin
  result := 0;
  //
  if (enableDataProcessing) then begin
    //
    if (enter()) then begin	// we must protect automation and local buffer from MT entering
      //
      try
	// process data, but do not write it into same buffer (if processDataInPlace is false)
	res := automation.processChunk(data, len, -1, -1, processDataInPlace);
	//
	if (SUCCEEDED(res)) then begin
	  //
	  // new data is available immediately, convert it to integers and notify
	  if (not processDataInPlace) then begin
	    //
	    result := convertOutFloat2PCM(buf);
	    onNewData(buf, result, self);
	  end
	  else begin
	    //
	    result := len;
	    onNewData(data, result, self);
	  end;
	end;
	//
      finally
	leave();
      end;
    end;
  end
  else begin
    //

⌨️ 快捷键说明

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